Add support for the I2S and davbus audio controllers found in Apple PowerPC
hardware. Submitted by: Marco Trillo
This commit is contained in:
parent
d8a0e03d76
commit
2cc6f5c8ec
@ -2,7 +2,9 @@
|
||||
|
||||
MAN= bm.4 \
|
||||
pmu.4 \
|
||||
powermac_nvram.4
|
||||
powermac_nvram.4 \
|
||||
snd_ai2s.4 \
|
||||
snd_davbus
|
||||
|
||||
MANSUBDIR=/powerpc
|
||||
|
||||
|
90
share/man/man4/man4.powerpc/snd_ai2s.4
Normal file
90
share/man/man4/man4.powerpc/snd_ai2s.4
Normal file
@ -0,0 +1,90 @@
|
||||
.\"-
|
||||
.\" Copyright (c) 2009 Nathan Whitehorn <nwhitehorn@FreeBSD.org>
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
.\" POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd January 20, 2009
|
||||
.Dt SND_AI2S 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm snd_ai2s
|
||||
.Nd "Apple I2S audio device driver"
|
||||
.Sh SYNOPSIS
|
||||
To compile this driver into the kernel,
|
||||
place the following lines in your
|
||||
kernel configuration file:
|
||||
.Bd -ragged -offset indent
|
||||
.Cd "device sound"
|
||||
.Cd "device snd_ai2s"
|
||||
.Ed
|
||||
.Pp
|
||||
Alternatively, to load the driver as a
|
||||
module at boot time, place the following line in
|
||||
.Xr loader.conf 5 :
|
||||
.Bd -literal -offset indent
|
||||
snd_ai2s_load="YES"
|
||||
.Ed
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
driver provides support for the Apple I2S audio controllers found
|
||||
predominantly in G4 and G5 machines, along with the snapper and tumbler
|
||||
codecs. Some machines (e.g. the Mac Mini) do not have configurable
|
||||
codecs and so lack hardware volume control.
|
||||
.Sh HARDWARE
|
||||
.Pp
|
||||
Chips supported by the
|
||||
.Nm
|
||||
driver include:
|
||||
.Pp
|
||||
.Bl -bullet -compact
|
||||
.It
|
||||
Apple Tumbler Audio
|
||||
.It
|
||||
Apple Snapper Audio
|
||||
.El
|
||||
.Pp
|
||||
.Sh BUGS
|
||||
Recording and operation with non-44.1 Khz audio are not currently supported.
|
||||
.Sh SEE ALSO
|
||||
.Xr sound 4 ,
|
||||
.Xr snd_davbus 4
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
device driver appeared in
|
||||
.Nx 2.0
|
||||
and then in
|
||||
.Fx 8.0 .
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The
|
||||
.Nm
|
||||
driver was written by
|
||||
.An Tsubai Masanari
|
||||
.Aq tsubai@netbsd.org ,
|
||||
and ported to FreeBSD by
|
||||
.An Marco Trillo
|
||||
.Aq marcotrillo@gmail.com .
|
83
share/man/man4/man4.powerpc/snd_davbus.4
Normal file
83
share/man/man4/man4.powerpc/snd_davbus.4
Normal file
@ -0,0 +1,83 @@
|
||||
.\"-
|
||||
.\" Copyright (c) 2009 Nathan Whitehorn <nwhitehorn@FreeBSD.org>
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
.\" POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd January 20, 2009
|
||||
.Dt SND_DAVBUS 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm snd_davbus
|
||||
.Nd "Apple Davbus audio device driver"
|
||||
.Sh SYNOPSIS
|
||||
To compile this driver into the kernel,
|
||||
place the following lines in your
|
||||
kernel configuration file:
|
||||
.Bd -ragged -offset indent
|
||||
.Cd "device sound"
|
||||
.Cd "device snd_davbus"
|
||||
.Ed
|
||||
.Pp
|
||||
Alternatively, to load the driver as a
|
||||
module at boot time, place the following line in
|
||||
.Xr loader.conf 5 :
|
||||
.Bd -literal -offset indent
|
||||
snd_davbus_load="YES"
|
||||
.Ed
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
driver provides support for the Apple Davbus audio controllers found in
|
||||
many G3-era Apple machines.
|
||||
.Sh HARDWARE
|
||||
.Pp
|
||||
Chips supported by the
|
||||
.Nm
|
||||
driver include:
|
||||
.Pp
|
||||
.Bl -bullet -compact
|
||||
.It
|
||||
Apple Burgundy Audio
|
||||
.It
|
||||
Apple Screamer Audio
|
||||
.El
|
||||
.Pp
|
||||
.Sh BUGS
|
||||
Recording is not currently supported.
|
||||
.Sh SEE ALSO
|
||||
.Xr sound 4 ,
|
||||
.Xr snd_ai2s 4
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
device driver appeared in
|
||||
.Fx 8.0 .
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The
|
||||
.Nm
|
||||
driver was written by
|
||||
.An Marco Trillo
|
||||
.Aq marcotrillo@gmail.com .
|
@ -39,6 +39,11 @@ dev/ofw/ofw_standard.c optional aim
|
||||
dev/powermac_nvram/powermac_nvram.c optional powermac_nvram powermac
|
||||
dev/quicc/quicc_bfe_ocp.c optional quicc mpc85xx
|
||||
dev/scc/scc_bfe_macio.c optional scc powermac
|
||||
dev/sound/macio/aoa.c optional snd_davbus | snd_ai2s powermac
|
||||
dev/sound/macio/davbus.c optional snd_davbus powermac
|
||||
dev/sound/macio/i2s.c optional snd_ai2s powermac
|
||||
dev/sound/macio/snapper.c optional snd_ai2s iicbus powermac
|
||||
dev/sound/macio/tumbler.c optional snd_ai2s iicbus powermac
|
||||
dev/syscons/scgfbrndr.c optional sc
|
||||
dev/syscons/scterm-teken.c optional sc
|
||||
dev/syscons/scvtb.c optional sc
|
||||
|
379
sys/dev/sound/macio/aoa.c
Normal file
379
sys/dev/sound/macio/aoa.c
Normal file
@ -0,0 +1,379 @@
|
||||
/*-
|
||||
* Copyright 2008 by Marco Trillo. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/*
|
||||
* Apple Onboard Audio (AOA).
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <machine/dbdma.h>
|
||||
#include <machine/resource.h>
|
||||
#include <machine/bus.h>
|
||||
#include <sys/rman.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/macio/aoa.h>
|
||||
#include "mixer_if.h"
|
||||
|
||||
struct aoa_dma {
|
||||
struct mtx mutex;
|
||||
struct resource *reg; /* DBDMA registers */
|
||||
dbdma_channel_t *channel; /* DBDMA channel */
|
||||
bus_dma_tag_t tag; /* bus_dma tag */
|
||||
struct pcm_channel *pcm; /* PCM channel */
|
||||
struct snd_dbuf *buf; /* PCM buffer */
|
||||
u_int slots; /* # of slots */
|
||||
u_int slot; /* current slot */
|
||||
u_int bufsz; /* buffer size */
|
||||
u_int blksz; /* block size */
|
||||
int running;
|
||||
};
|
||||
|
||||
static void
|
||||
aoa_dma_set_program(struct aoa_dma *dma)
|
||||
{
|
||||
u_int32_t addr;
|
||||
int i;
|
||||
|
||||
addr = (u_int32_t) sndbuf_getbufaddr(dma->buf);
|
||||
KASSERT(dma->bufsz == sndbuf_getsize(dma->buf), ("bad size"));
|
||||
|
||||
dma->slots = dma->bufsz / dma->blksz;
|
||||
|
||||
for (i = 0; i < dma->slots; ++i) {
|
||||
dbdma_insert_command(dma->channel,
|
||||
i, /* slot */
|
||||
DBDMA_OUTPUT_MORE, /* command */
|
||||
0, /* stream */
|
||||
addr, /* data */
|
||||
dma->blksz, /* count */
|
||||
DBDMA_ALWAYS, /* interrupt */
|
||||
DBDMA_COND_TRUE, /* branch */
|
||||
DBDMA_NEVER, /* wait */
|
||||
dma->slots + 1 /* branch_slot */
|
||||
);
|
||||
|
||||
addr += dma->blksz;
|
||||
}
|
||||
|
||||
/* Branch back to beginning. */
|
||||
dbdma_insert_branch(dma->channel, dma->slots, 0);
|
||||
|
||||
/* STOP command to branch when S0 is asserted. */
|
||||
dbdma_insert_stop(dma->channel, dma->slots + 1);
|
||||
|
||||
/* Set S0 as the condition to branch to STOP. */
|
||||
dbdma_set_branch_selector(dma->channel, 1 << 0, 1 << 0);
|
||||
dbdma_set_device_status(dma->channel, 1 << 0, 0);
|
||||
|
||||
dbdma_sync_commands(dma->channel, BUS_DMASYNC_PREWRITE);
|
||||
}
|
||||
|
||||
#define AOA_BUFFER_SIZE 65536
|
||||
|
||||
static struct aoa_dma *
|
||||
aoa_dma_create(device_t self)
|
||||
{
|
||||
struct aoa_softc *sc = device_get_softc(self);
|
||||
struct aoa_dma *dma;
|
||||
bus_dma_tag_t tag;
|
||||
int err;
|
||||
|
||||
err = bus_dma_tag_create(bus_get_dma_tag(self),
|
||||
4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
|
||||
AOA_BUFFER_SIZE, 1, AOA_BUFFER_SIZE, 0, NULL, NULL, &tag);
|
||||
if (err != 0)
|
||||
return (NULL);
|
||||
|
||||
dma = malloc(sizeof(*dma), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
dma->tag = tag;
|
||||
dma->bufsz = AOA_BUFFER_SIZE;
|
||||
dma->blksz = PAGE_SIZE; /* initial blocksize */
|
||||
|
||||
mtx_init(&dma->mutex, "AOA", NULL, MTX_DEF);
|
||||
|
||||
sc->sc_intrp = dma;
|
||||
|
||||
return (dma);
|
||||
}
|
||||
|
||||
static void
|
||||
aoa_dma_delete(struct aoa_dma *dma)
|
||||
{
|
||||
bus_dma_tag_destroy(dma->tag);
|
||||
mtx_destroy(&dma->mutex);
|
||||
free(dma, M_DEVBUF);
|
||||
}
|
||||
|
||||
static int
|
||||
aoa_chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksz)
|
||||
{
|
||||
struct aoa_dma *dma = data;
|
||||
int err, lz;
|
||||
|
||||
DPRINTF(("aoa_chan_setblocksize: blocksz = %u, dma->blksz = %u\n",
|
||||
blocksz, dma->blksz));
|
||||
KASSERT(!dma->running, ("dma is running"));
|
||||
KASSERT(blocksz > 0, ("bad blocksz"));
|
||||
|
||||
/* Round blocksz down to a power of two... */
|
||||
__asm volatile ("cntlzw %0,%1" : "=r"(lz) : "r"(blocksz));
|
||||
blocksz = 1 << (31 - lz);
|
||||
DPRINTF(("blocksz = %u\n", blocksz));
|
||||
|
||||
/* ...but no more than the buffer. */
|
||||
if (blocksz > dma->bufsz)
|
||||
blocksz = dma->bufsz;
|
||||
|
||||
err = sndbuf_resize(dma->buf, dma->bufsz / blocksz, blocksz);
|
||||
if (err != 0) {
|
||||
DPRINTF(("sndbuf_resize returned %d\n", err));
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (blocksz == dma->blksz)
|
||||
return (dma->blksz);
|
||||
|
||||
/* One slot per block plus branch to 0 plus STOP. */
|
||||
err = dbdma_resize_channel(dma->channel, 2 + dma->bufsz / blocksz);
|
||||
if (err != 0) {
|
||||
DPRINTF(("dbdma_resize_channel returned %d\n", err));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Set the new blocksize. */
|
||||
dma->blksz = blocksz;
|
||||
aoa_dma_set_program(dma);
|
||||
|
||||
return (dma->blksz);
|
||||
}
|
||||
|
||||
static int
|
||||
aoa_chan_setformat(kobj_t obj, void *data, u_int32_t format)
|
||||
{
|
||||
DPRINTF(("aoa_chan_setformat: format = %u\n", format));
|
||||
|
||||
if (format != (AFMT_STEREO | AFMT_S16_BE))
|
||||
return (EINVAL);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
aoa_chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
|
||||
{
|
||||
DPRINTF(("aoa_chan_setspeed: speed = %u\n", speed));
|
||||
|
||||
return (44100);
|
||||
}
|
||||
|
||||
static int
|
||||
aoa_chan_getptr(kobj_t obj, void *data)
|
||||
{
|
||||
struct aoa_dma *dma = data;
|
||||
|
||||
if (!dma->running)
|
||||
return (0);
|
||||
|
||||
return (dma->slot * dma->blksz);
|
||||
}
|
||||
|
||||
static void *
|
||||
aoa_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
|
||||
struct pcm_channel *c, int dir)
|
||||
{
|
||||
device_t self = devinfo;
|
||||
struct aoa_softc *sc = device_get_softc(self);
|
||||
struct aoa_dma *dma;
|
||||
int max_slots, err;
|
||||
|
||||
KASSERT(dir == PCMDIR_PLAY, ("bad dir"));
|
||||
|
||||
dma = aoa_dma_create(self);
|
||||
if (!dma)
|
||||
return (NULL);
|
||||
dma->pcm = c;
|
||||
dma->buf = b;
|
||||
dma->reg = sc->sc_odma;
|
||||
|
||||
/* One slot per block, plus branch to 0 plus STOP. */
|
||||
max_slots = 2 + dma->bufsz / dma->blksz;
|
||||
err = dbdma_allocate_channel(dma->reg, 0, bus_get_dma_tag(self),
|
||||
max_slots, &dma->channel );
|
||||
if (err != 0) {
|
||||
aoa_dma_delete(dma);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (sndbuf_alloc(dma->buf, dma->tag, 0, dma->bufsz) != 0) {
|
||||
dbdma_free_channel(dma->channel);
|
||||
aoa_dma_delete(dma);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
aoa_dma_set_program(dma);
|
||||
|
||||
return (dma);
|
||||
}
|
||||
|
||||
static int
|
||||
aoa_chan_trigger(kobj_t obj, void *data, int go)
|
||||
{
|
||||
struct aoa_dma *dma = data;
|
||||
int i;
|
||||
|
||||
switch (go) {
|
||||
case PCMTRIG_START:
|
||||
|
||||
/* Start the DMA. */
|
||||
dma->running = 1;
|
||||
|
||||
dma->slot = 0;
|
||||
dbdma_set_current_cmd(dma->channel, dma->slot);
|
||||
|
||||
dbdma_run(dma->channel);
|
||||
|
||||
return (0);
|
||||
|
||||
case PCMTRIG_STOP:
|
||||
case PCMTRIG_ABORT:
|
||||
|
||||
mtx_lock(&dma->mutex);
|
||||
|
||||
dma->running = 0;
|
||||
|
||||
/* Make it branch to the STOP command. */
|
||||
dbdma_set_device_status(dma->channel, 1 << 0, 1 << 0);
|
||||
|
||||
/* XXX should wait for DBDMA_ACTIVE to clear. */
|
||||
DELAY(40000);
|
||||
|
||||
/* Reset the DMA. */
|
||||
dbdma_stop(dma->channel);
|
||||
dbdma_set_device_status(dma->channel, 1 << 0, 0);
|
||||
|
||||
for (i = 0; i < dma->slots; ++i)
|
||||
dbdma_clear_cmd_status(dma->channel, i);
|
||||
|
||||
mtx_unlock(&dma->mutex);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
aoa_chan_free(kobj_t obj, void *data)
|
||||
{
|
||||
struct aoa_dma *dma = data;
|
||||
|
||||
sndbuf_free(dma->buf);
|
||||
dbdma_free_channel(dma->channel);
|
||||
aoa_dma_delete(dma);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
aoa_interrupt(void *arg)
|
||||
{
|
||||
struct aoa_softc *sc = arg;
|
||||
struct aoa_dma *dma;
|
||||
|
||||
if (!(dma = sc->sc_intrp) || !dma->running)
|
||||
return;
|
||||
|
||||
mtx_lock(&dma->mutex);
|
||||
|
||||
while (dbdma_get_cmd_status(dma->channel, dma->slot)) {
|
||||
|
||||
dbdma_clear_cmd_status(dma->channel, dma->slot);
|
||||
dma->slot = (dma->slot + 1) % dma->slots;
|
||||
|
||||
mtx_unlock(&dma->mutex);
|
||||
chn_intr(dma->pcm);
|
||||
mtx_lock(&dma->mutex);
|
||||
}
|
||||
|
||||
mtx_unlock(&dma->mutex);
|
||||
}
|
||||
|
||||
static u_int32_t sc_fmt[] = {
|
||||
AFMT_S16_BE | AFMT_STEREO,
|
||||
0
|
||||
};
|
||||
static struct pcmchan_caps aoa_caps = {44100, 44100, sc_fmt, 0};
|
||||
|
||||
static struct pcmchan_caps *
|
||||
aoa_chan_getcaps(kobj_t obj, void *data)
|
||||
{
|
||||
return (&aoa_caps);
|
||||
}
|
||||
|
||||
static kobj_method_t aoa_chan_methods[] = {
|
||||
KOBJMETHOD(channel_init, aoa_chan_init),
|
||||
KOBJMETHOD(channel_free, aoa_chan_free),
|
||||
KOBJMETHOD(channel_setformat, aoa_chan_setformat),
|
||||
KOBJMETHOD(channel_setspeed, aoa_chan_setspeed),
|
||||
KOBJMETHOD(channel_setblocksize,aoa_chan_setblocksize),
|
||||
KOBJMETHOD(channel_trigger, aoa_chan_trigger),
|
||||
KOBJMETHOD(channel_getptr, aoa_chan_getptr),
|
||||
KOBJMETHOD(channel_getcaps, aoa_chan_getcaps),
|
||||
{ 0, 0 }
|
||||
};
|
||||
CHANNEL_DECLARE(aoa_chan);
|
||||
|
||||
int
|
||||
aoa_attach(device_t self)
|
||||
{
|
||||
char status[SND_STATUSLEN];
|
||||
int err;
|
||||
|
||||
if (pcm_register(self, self, 1, 0))
|
||||
return (ENXIO);
|
||||
|
||||
err = pcm_getbuffersize(self, AOA_BUFFER_SIZE, AOA_BUFFER_SIZE,
|
||||
AOA_BUFFER_SIZE);
|
||||
DPRINTF(("pcm_getbuffersize returned %d\n", err));
|
||||
|
||||
pcm_addchan(self, PCMDIR_PLAY, &aoa_chan_class, self);
|
||||
|
||||
snprintf(status, sizeof(status), "at %s", ofw_bus_get_name(self));
|
||||
pcm_setstatus(self, status);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
44
sys/dev/sound/macio/aoa.h
Normal file
44
sys/dev/sound/macio/aoa.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*-
|
||||
* Copyright 2008 by Marco Trillo. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef SOUND_AOA_H
|
||||
#define SOUND_AOA_H
|
||||
|
||||
#define DPRINTF(x) /* nothing */
|
||||
/* #define DPRINTF(x) printf x */
|
||||
|
||||
struct aoa_softc {
|
||||
u_int8_t sc_super[PCM_SOFTC_SIZE];
|
||||
void *sc_intrp;
|
||||
struct resource *sc_odma;
|
||||
};
|
||||
|
||||
void aoa_interrupt(void *);
|
||||
int aoa_attach(device_t);
|
||||
|
||||
#endif /* SOUND_AOA_H */
|
||||
|
600
sys/dev/sound/macio/davbus.c
Normal file
600
sys/dev/sound/macio/davbus.c
Normal file
@ -0,0 +1,600 @@
|
||||
/*-
|
||||
* Copyright 2008 by Marco Trillo. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/*
|
||||
* Apple DAVbus audio controller.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/rman.h>
|
||||
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/macio/aoa.h>
|
||||
#include <dev/sound/macio/davbusreg.h>
|
||||
|
||||
#include <machine/intr_machdep.h>
|
||||
#include <machine/resource.h>
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include "mixer_if.h"
|
||||
|
||||
struct davbus_softc {
|
||||
struct aoa_softc aoa;
|
||||
device_t dev;
|
||||
phandle_t node;
|
||||
phandle_t soundnode;
|
||||
struct resource *reg;
|
||||
struct mtx mutex;
|
||||
int device_id;
|
||||
u_int output_mask;
|
||||
u_int (*read_status)(struct davbus_softc *, u_int);
|
||||
void (*set_outputs)(struct davbus_softc *, u_int);
|
||||
};
|
||||
|
||||
static int davbus_probe(device_t);
|
||||
static int davbus_attach(device_t);
|
||||
static void davbus_cint(void *);
|
||||
|
||||
static device_method_t pcm_davbus_methods[] = {
|
||||
/* Device interface. */
|
||||
DEVMETHOD(device_probe, davbus_probe),
|
||||
DEVMETHOD(device_attach, davbus_attach),
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static driver_t pcm_davbus_driver = {
|
||||
"pcm",
|
||||
pcm_davbus_methods,
|
||||
sizeof(struct davbus_softc)
|
||||
};
|
||||
|
||||
DRIVER_MODULE(pcm_davbus, macio, pcm_davbus_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(pcm_davbus, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
|
||||
|
||||
/*****************************************************************************
|
||||
Probe and attachment routines.
|
||||
*****************************************************************************/
|
||||
static int
|
||||
davbus_probe(device_t self)
|
||||
{
|
||||
const char *name;
|
||||
struct davbus_softc *sc;
|
||||
|
||||
name = ofw_bus_get_name(self);
|
||||
if (!name)
|
||||
return (ENXIO);
|
||||
|
||||
if (strcmp(name, "davbus") != 0)
|
||||
return (ENXIO);
|
||||
|
||||
sc = device_get_softc(self);
|
||||
if (!sc)
|
||||
return (ENOMEM);
|
||||
bzero(sc, sizeof(*sc));
|
||||
|
||||
device_set_desc(self, "Apple DAVBus Audio Controller");
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Burgundy codec control
|
||||
*/
|
||||
|
||||
static int burgundy_init(struct snd_mixer *m);
|
||||
static int burgundy_uninit(struct snd_mixer *m);
|
||||
static int burgundy_reinit(struct snd_mixer *m);
|
||||
static void burgundy_write_locked(struct davbus_softc *, u_int, u_int);
|
||||
static void burgundy_set_outputs(struct davbus_softc *d, u_int mask);
|
||||
static u_int burgundy_read_status(struct davbus_softc *d, u_int status);
|
||||
static int burgundy_set(struct snd_mixer *m, unsigned dev, unsigned left,
|
||||
unsigned right);
|
||||
static int burgundy_setrecsrc(struct snd_mixer *m, u_int32_t src);
|
||||
|
||||
static kobj_method_t burgundy_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, burgundy_init),
|
||||
KOBJMETHOD(mixer_uninit, burgundy_uninit),
|
||||
KOBJMETHOD(mixer_reinit, burgundy_reinit),
|
||||
KOBJMETHOD(mixer_set, burgundy_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, burgundy_setrecsrc),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
MIXER_DECLARE(burgundy_mixer);
|
||||
|
||||
static int
|
||||
burgundy_init(struct snd_mixer *m)
|
||||
{
|
||||
struct davbus_softc *d;
|
||||
|
||||
d = mix_getdevinfo(m);
|
||||
|
||||
d->read_status = burgundy_read_status;
|
||||
d->set_outputs = burgundy_set_outputs;
|
||||
|
||||
/*
|
||||
* We configure the Burgundy codec as follows:
|
||||
*
|
||||
* o Input subframe 0 is connected to input digital
|
||||
* stream A (ISA).
|
||||
* o Stream A (ISA) is mixed in mixer 2 (MIX2).
|
||||
* o Output of mixer 2 (MIX2) is routed to output sources
|
||||
* OS0 and OS1 which can be converted to analog.
|
||||
*
|
||||
*/
|
||||
mtx_lock(&d->mutex);
|
||||
|
||||
burgundy_write_locked(d, 0x16700, 0x40);
|
||||
|
||||
burgundy_write_locked(d, BURGUNDY_MIX0_REG, 0);
|
||||
burgundy_write_locked(d, BURGUNDY_MIX1_REG, 0);
|
||||
burgundy_write_locked(d, BURGUNDY_MIX2_REG, BURGUNDY_MIX_ISA);
|
||||
burgundy_write_locked(d, BURGUNDY_MIX3_REG, 0);
|
||||
|
||||
burgundy_write_locked(d, BURGUNDY_OS_REG, BURGUNDY_OS0_MIX2 |
|
||||
BURGUNDY_OS1_MIX2);
|
||||
|
||||
burgundy_write_locked(d, BURGUNDY_SDIN_REG, BURGUNDY_ISA_SF0);
|
||||
|
||||
/* Set several digital scalers to unity gain. */
|
||||
burgundy_write_locked(d, BURGUNDY_MXS2L_REG, BURGUNDY_MXS_UNITY);
|
||||
burgundy_write_locked(d, BURGUNDY_MXS2R_REG, BURGUNDY_MXS_UNITY);
|
||||
burgundy_write_locked(d, BURGUNDY_OSS0L_REG, BURGUNDY_OSS_UNITY);
|
||||
burgundy_write_locked(d, BURGUNDY_OSS0R_REG, BURGUNDY_OSS_UNITY);
|
||||
burgundy_write_locked(d, BURGUNDY_OSS1L_REG, BURGUNDY_OSS_UNITY);
|
||||
burgundy_write_locked(d, BURGUNDY_OSS1R_REG, BURGUNDY_OSS_UNITY);
|
||||
burgundy_write_locked(d, BURGUNDY_ISSAL_REG, BURGUNDY_ISS_UNITY);
|
||||
burgundy_write_locked(d, BURGUNDY_ISSAR_REG, BURGUNDY_ISS_UNITY);
|
||||
|
||||
burgundy_set_outputs(d, burgundy_read_status(d,
|
||||
bus_read_4(d->reg, DAVBUS_CODEC_STATUS)));
|
||||
|
||||
mtx_unlock(&d->mutex);
|
||||
|
||||
mix_setdevs(m, SOUND_MASK_VOLUME);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
burgundy_uninit(struct snd_mixer *m)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
burgundy_reinit(struct snd_mixer *m)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
burgundy_write_locked(struct davbus_softc *d, u_int reg, u_int val)
|
||||
{
|
||||
u_int size, addr, offset, data, i;
|
||||
|
||||
size = (reg & 0x00FF0000) >> 16;
|
||||
addr = (reg & 0x0000FF00) >> 8;
|
||||
offset = reg & 0xFF;
|
||||
|
||||
for (i = offset; i < offset + size; ++i) {
|
||||
data = BURGUNDY_CTRL_WRITE | (addr << 12) |
|
||||
((size + offset - 1) << 10) | (i << 8) | (val & 0xFF);
|
||||
if (i == offset)
|
||||
data |= BURGUNDY_CTRL_RESET;
|
||||
|
||||
bus_write_4(d->reg, DAVBUS_CODEC_CTRL, data);
|
||||
|
||||
while (bus_read_4(d->reg, DAVBUS_CODEC_CTRL) &
|
||||
DAVBUS_CODEC_BUSY)
|
||||
DELAY(1);
|
||||
|
||||
val >>= 8; /* next byte. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Must be called with d->mutex held. */
|
||||
static void
|
||||
burgundy_set_outputs(struct davbus_softc *d, u_int mask)
|
||||
{
|
||||
u_int x = 0;
|
||||
|
||||
if (mask == d->output_mask)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Bordeaux card wirings:
|
||||
* Port 15: RCA out
|
||||
* Port 16: Minijack out
|
||||
* Port 17: Internal speaker
|
||||
*
|
||||
* B&W G3 wirings:
|
||||
* Port 14: Minijack out
|
||||
* Port 17: Internal speaker
|
||||
*/
|
||||
|
||||
DPRINTF(("Enabled outputs:"));
|
||||
if (mask & (1 << 0)) {
|
||||
DPRINTF((" SPEAKER"));
|
||||
x |= BURGUNDY_P17M_EN;
|
||||
}
|
||||
if (mask & (1 << 1)) {
|
||||
DPRINTF((" HEADPHONES"));
|
||||
x |= BURGUNDY_P14L_EN | BURGUNDY_P14R_EN;
|
||||
}
|
||||
DPRINTF(("\n"));
|
||||
|
||||
burgundy_write_locked(d, BURGUNDY_MUTE_REG, x);
|
||||
d->output_mask = mask;
|
||||
}
|
||||
|
||||
static u_int
|
||||
burgundy_read_status(struct davbus_softc *d, u_int status)
|
||||
{
|
||||
if (status & 0x4)
|
||||
return (1 << 1);
|
||||
else
|
||||
return (1 << 0);
|
||||
}
|
||||
|
||||
static int
|
||||
burgundy_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
{
|
||||
struct davbus_softc *d;
|
||||
int lval, rval;
|
||||
|
||||
lval = ((100 - left) * 15 / 100) & 0xf;
|
||||
rval = ((100 - right) * 15 / 100) & 0xf;
|
||||
DPRINTF(("volume %d %d\n", lval, rval));
|
||||
|
||||
d = mix_getdevinfo(m);
|
||||
|
||||
switch (dev) {
|
||||
case SOUND_MIXER_VOLUME:
|
||||
mtx_lock(&d->mutex);
|
||||
|
||||
burgundy_write_locked(d, BURGUNDY_OL13_REG, lval);
|
||||
burgundy_write_locked(d, BURGUNDY_OL14_REG, (rval << 4) | lval);
|
||||
burgundy_write_locked(d, BURGUNDY_OL15_REG, (rval << 4) | lval);
|
||||
burgundy_write_locked(d, BURGUNDY_OL16_REG, (rval << 4) | lval);
|
||||
burgundy_write_locked(d, BURGUNDY_OL17_REG, lval);
|
||||
|
||||
mtx_unlock(&d->mutex);
|
||||
|
||||
return (left | (right << 8));
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
burgundy_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Screamer Codec Control
|
||||
*/
|
||||
|
||||
static int screamer_init(struct snd_mixer *m);
|
||||
static int screamer_uninit(struct snd_mixer *m);
|
||||
static int screamer_reinit(struct snd_mixer *m);
|
||||
static void screamer_write_locked(struct davbus_softc *, u_int, u_int);
|
||||
static void screamer_set_outputs(struct davbus_softc *d, u_int mask);
|
||||
static u_int screamer_read_status(struct davbus_softc *d, u_int status);
|
||||
static int screamer_set(struct snd_mixer *m, unsigned dev, unsigned left,
|
||||
unsigned right);
|
||||
static int screamer_setrecsrc(struct snd_mixer *m, u_int32_t src);
|
||||
|
||||
static kobj_method_t screamer_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, screamer_init),
|
||||
KOBJMETHOD(mixer_uninit, screamer_uninit),
|
||||
KOBJMETHOD(mixer_reinit, screamer_reinit),
|
||||
KOBJMETHOD(mixer_set, screamer_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, screamer_setrecsrc),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
MIXER_DECLARE(screamer_mixer);
|
||||
|
||||
static int
|
||||
screamer_init(struct snd_mixer *m)
|
||||
{
|
||||
struct davbus_softc *d;
|
||||
|
||||
d = mix_getdevinfo(m);
|
||||
|
||||
d->read_status = screamer_read_status;
|
||||
d->set_outputs = screamer_set_outputs;
|
||||
|
||||
mtx_lock(&d->mutex);
|
||||
|
||||
screamer_write_locked(d, SCREAMER_CODEC_ADDR0, SCREAMER_INPUT_CD |
|
||||
SCREAMER_DEFAULT_CD_GAIN);
|
||||
|
||||
screamer_set_outputs(d, screamer_read_status(d,
|
||||
bus_read_4(d->reg, DAVBUS_CODEC_STATUS)));
|
||||
|
||||
screamer_write_locked(d, SCREAMER_CODEC_ADDR2, 0);
|
||||
screamer_write_locked(d, SCREAMER_CODEC_ADDR4, 0);
|
||||
screamer_write_locked(d, SCREAMER_CODEC_ADDR5, 0);
|
||||
screamer_write_locked(d, SCREAMER_CODEC_ADDR6, 0);
|
||||
|
||||
mtx_unlock(&d->mutex);
|
||||
|
||||
mix_setdevs(m, SOUND_MASK_VOLUME);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
screamer_uninit(struct snd_mixer *m)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
screamer_reinit(struct snd_mixer *m)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
screamer_write_locked(struct davbus_softc *d, u_int reg, u_int val)
|
||||
{
|
||||
u_int x;
|
||||
|
||||
KASSERT(val == (val & 0xfff), ("bad val"));
|
||||
|
||||
while (bus_read_4(d->reg, DAVBUS_CODEC_CTRL) & DAVBUS_CODEC_BUSY)
|
||||
DELAY(100);
|
||||
|
||||
x = reg;
|
||||
x |= SCREAMER_CODEC_EMSEL0;
|
||||
x |= val;
|
||||
bus_write_4(d->reg, DAVBUS_CODEC_CTRL, x);
|
||||
|
||||
while (bus_read_4(d->reg, DAVBUS_CODEC_CTRL) & DAVBUS_CODEC_BUSY)
|
||||
DELAY(100);
|
||||
}
|
||||
|
||||
/* Must be called with d->mutex held. */
|
||||
static void
|
||||
screamer_set_outputs(struct davbus_softc *d, u_int mask)
|
||||
{
|
||||
u_int x;
|
||||
|
||||
if (mask == d->output_mask) {
|
||||
return;
|
||||
}
|
||||
|
||||
x = SCREAMER_MUTE_SPEAKER | SCREAMER_MUTE_HEADPHONES;
|
||||
|
||||
DPRINTF(("Enabled outputs: "));
|
||||
|
||||
if (mask & (1 << 0)) {
|
||||
DPRINTF(("SPEAKER "));
|
||||
x &= ~SCREAMER_MUTE_SPEAKER;
|
||||
}
|
||||
if (mask & (1 << 1)) {
|
||||
DPRINTF(("HEADPHONES "));
|
||||
x &= ~SCREAMER_MUTE_HEADPHONES;
|
||||
}
|
||||
|
||||
DPRINTF(("\n"));
|
||||
|
||||
if (d->device_id == 5 || d->device_id == 11) {
|
||||
DPRINTF(("Enabling programmable output.\n"));
|
||||
x |= SCREAMER_PROG_OUTPUT0;
|
||||
}
|
||||
if (d->device_id == 8 || d->device_id == 11) {
|
||||
x &= ~SCREAMER_MUTE_SPEAKER;
|
||||
|
||||
if (mask & (1 << 0))
|
||||
x |= SCREAMER_PROG_OUTPUT1; /* enable speaker. */
|
||||
}
|
||||
|
||||
screamer_write_locked(d, SCREAMER_CODEC_ADDR1, x);
|
||||
d->output_mask = mask;
|
||||
}
|
||||
|
||||
static u_int
|
||||
screamer_read_status(struct davbus_softc *d, u_int status)
|
||||
{
|
||||
int headphones;
|
||||
|
||||
switch (d->device_id) {
|
||||
case 5: /* Sawtooth */
|
||||
headphones = (status & 0x4);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
case 11: /* iMac DV */
|
||||
/* The iMac DV has 2 headphone outputs. */
|
||||
headphones = (status & 0x7);
|
||||
break;
|
||||
|
||||
default:
|
||||
headphones = (status & 0x8);
|
||||
}
|
||||
|
||||
if (headphones)
|
||||
return (1 << 1);
|
||||
else
|
||||
return (1 << 0);
|
||||
}
|
||||
|
||||
static int
|
||||
screamer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
{
|
||||
struct davbus_softc *d;
|
||||
int lval, rval;
|
||||
|
||||
lval = ((100 - left) * 15 / 100) & 0xf;
|
||||
rval = ((100 - right) * 15 / 100) & 0xf;
|
||||
DPRINTF(("volume %d %d\n", lval, rval));
|
||||
|
||||
d = mix_getdevinfo(m);
|
||||
|
||||
switch (dev) {
|
||||
case SOUND_MIXER_VOLUME:
|
||||
mtx_lock(&d->mutex);
|
||||
screamer_write_locked(d, SCREAMER_CODEC_ADDR2, (lval << 6) |
|
||||
rval);
|
||||
screamer_write_locked(d, SCREAMER_CODEC_ADDR4, (lval << 6) |
|
||||
rval);
|
||||
mtx_unlock(&d->mutex);
|
||||
|
||||
return (left | (right << 8));
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
screamer_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
davbus_attach(device_t self)
|
||||
{
|
||||
struct davbus_softc *sc = device_get_softc(self);
|
||||
struct resource *dbdma_irq, *cintr;
|
||||
void *cookie;
|
||||
char compat[64];
|
||||
int rid, oirq, err;
|
||||
|
||||
sc->dev = self;
|
||||
sc->node = ofw_bus_get_node(self);
|
||||
sc->soundnode = OF_child(sc->node);
|
||||
|
||||
/* Map the controller register space. */
|
||||
rid = 0;
|
||||
sc->reg = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE);
|
||||
if (sc->reg == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
/* Map the DBDMA channel register space. */
|
||||
rid = 1;
|
||||
sc->aoa.sc_odma = bus_alloc_resource_any(self, SYS_RES_MEMORY,
|
||||
&rid, RF_ACTIVE);
|
||||
if (sc->aoa.sc_odma == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
/* Establish the DBDMA channel edge-triggered interrupt. */
|
||||
rid = 1;
|
||||
dbdma_irq = bus_alloc_resource_any(self, SYS_RES_IRQ,
|
||||
&rid, RF_SHAREABLE | RF_ACTIVE);
|
||||
if (dbdma_irq == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
oirq = rman_get_start(dbdma_irq);
|
||||
|
||||
DPRINTF(("interrupting at irq %d\n", oirq));
|
||||
|
||||
err = powerpc_config_intr(oirq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
|
||||
bus_setup_intr(self, dbdma_irq, INTR_TYPE_AV | INTR_MPSAFE,
|
||||
NULL, aoa_interrupt, sc, &cookie);
|
||||
|
||||
/* Now initialize the controller. */
|
||||
|
||||
bzero(compat, sizeof(compat));
|
||||
OF_getprop(sc->soundnode, "compatible", compat, sizeof(compat));
|
||||
OF_getprop(sc->soundnode, "device-id", &sc->device_id, sizeof(u_int));
|
||||
|
||||
mtx_init(&sc->mutex, "DAVbus", NULL, MTX_DEF);
|
||||
|
||||
device_printf(self, "codec: <%s>\n", compat);
|
||||
|
||||
/* Setup the control interrupt. */
|
||||
rid = 0;
|
||||
cintr = bus_alloc_resource_any(self, SYS_RES_IRQ,
|
||||
&rid, RF_SHAREABLE | RF_ACTIVE);
|
||||
if (cintr != NULL)
|
||||
bus_setup_intr(self, cintr, INTR_TYPE_MISC | INTR_MPSAFE,
|
||||
NULL, davbus_cint, sc, &cookie);
|
||||
|
||||
/* Initialize controller registers. */
|
||||
bus_write_4(sc->reg, DAVBUS_SOUND_CTRL, DAVBUS_INPUT_SUBFRAME0 |
|
||||
DAVBUS_OUTPUT_SUBFRAME0 | DAVBUS_RATE_44100 | DAVBUS_INTR_PORTCHG);
|
||||
|
||||
/* Attach DBDMA engine and PCM layer */
|
||||
err = aoa_attach(self);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
/* Install codec module */
|
||||
if (strcmp(compat, "screamer") == 0)
|
||||
mixer_init(self, &screamer_mixer_class, sc);
|
||||
else if (strcmp(compat, "burgundy") == 0)
|
||||
mixer_init(self, &burgundy_mixer_class, sc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
davbus_cint(void *ptr)
|
||||
{
|
||||
struct davbus_softc *d = ptr;
|
||||
u_int reg, status, mask;
|
||||
|
||||
mtx_lock(&d->mutex);
|
||||
|
||||
reg = bus_read_4(d->reg, DAVBUS_SOUND_CTRL);
|
||||
if (reg & DAVBUS_PORTCHG) {
|
||||
|
||||
status = bus_read_4(d->reg, DAVBUS_CODEC_STATUS);
|
||||
|
||||
if (d->read_status && d->set_outputs) {
|
||||
|
||||
mask = (*d->read_status)(d, status);
|
||||
(*d->set_outputs)(d, mask);
|
||||
}
|
||||
|
||||
/* Clear the interrupt. */
|
||||
bus_write_4(d->reg, DAVBUS_SOUND_CTRL, reg);
|
||||
}
|
||||
|
||||
mtx_unlock(&d->mutex);
|
||||
}
|
||||
|
285
sys/dev/sound/macio/davbusreg.h
Normal file
285
sys/dev/sound/macio/davbusreg.h
Normal file
@ -0,0 +1,285 @@
|
||||
/*-
|
||||
* Copyright 2008 by Marco Trillo. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/*
|
||||
* Apple DAVbus audio controller.
|
||||
*/
|
||||
|
||||
#ifndef _SOUND_DAVBUS_H
|
||||
#define _SOUND_DAVBUS_H
|
||||
|
||||
/* DAVbus controller registers. */
|
||||
#define DAVBUS_SOUND_CTRL 0x00
|
||||
#define DAVBUS_CODEC_CTRL 0x10
|
||||
#define DAVBUS_CODEC_STATUS 0x20
|
||||
#define DAVBUS_CLIP_COUNT 0x30
|
||||
#define DAVBUS_BYTE_SWAP 0x40
|
||||
|
||||
/*
|
||||
* The DAVbus uses a serial bus time multiplexed in four subframes,
|
||||
* but the controller itself uses subframe 0 to communicate with the codec.
|
||||
* In some machines, the other subframes may be used by external devices
|
||||
* thorugh the DAV interface.
|
||||
*/
|
||||
/* DAVBUS_SOUND_CTRL bit definitions. */
|
||||
#define DAVBUS_INPUT_SUBFRAME0 0x00000001
|
||||
#define DAVBUS_INPUT_SUBFRAME1 0x00000002
|
||||
#define DAVBUS_INPUT_SUBFRAME2 0x00000004
|
||||
#define DAVBUS_INPUT_SUBFRAME3 0x00000008
|
||||
|
||||
#define DAVBUS_OUTPUT_SUBFRAME0 0x00000010
|
||||
#define DAVBUS_OUTPUT_SUBFRAME1 0x00000020
|
||||
#define DAVBUS_OUTPUT_SUBFRAME2 0x00000040
|
||||
#define DAVBUS_OUTPUT_SUBFRAME3 0x00000080
|
||||
|
||||
#define DAVBUS_RATE_44100 0x00000000
|
||||
#define DAVBUS_RATE_29400 0x00000100
|
||||
#define DAVBUS_RATE_22050 0x00000200
|
||||
#define DAVBUS_RATE_17640 0x00000300
|
||||
#define DAVBUS_RATE_14700 0x00000400
|
||||
#define DAVBUS_RATE_11025 0x00000500
|
||||
#define DAVBUS_RATE_8820 0x00000600
|
||||
#define DAVBUS_RATE_7350 0x00000700
|
||||
#define DAVBUS_RATE_MASK 0x00000700
|
||||
|
||||
#define DAVBUS_ERROR 0x00000800
|
||||
#define DAVBUS_PORTCHG 0x00001000
|
||||
#define DAVBUS_INTR_ERROR 0x00002000 /* interrupt on error */
|
||||
#define DAVBUS_INTR_PORTCHG 0x00004000 /* interrupt on port change */
|
||||
|
||||
#define DAVBUS_STATUS_SUBFRAME 0x00018000 /* mask */
|
||||
|
||||
/* DAVBUS_CODEC_CTRL bit definitions. */
|
||||
#define DAVBUS_CODEC_BUSY 0x01000000
|
||||
|
||||
|
||||
/*
|
||||
* Burgundy Codec Control Bits
|
||||
*/
|
||||
|
||||
/* Burgundy transaction bits. */
|
||||
#define BURGUNDY_CTRL_RESET 0x00100000
|
||||
#define BURGUNDY_CTRL_WRITE 0x00200000
|
||||
|
||||
/* Mute control for each analog output port. */
|
||||
#define BURGUNDY_MUTE_REG 0x16000
|
||||
#define BURGUNDY_P13M_EN 0x01
|
||||
#define BURGUNDY_P14L_EN 0x02
|
||||
#define BURGUNDY_P14R_EN 0x04
|
||||
#define BURGUNDY_P15L_EN 0x08
|
||||
#define BURGUNDY_P15R_EN 0x10
|
||||
#define BURGUNDY_P16L_EN 0x20
|
||||
#define BURGUNDY_P16R_EN 0x40
|
||||
#define BURGUNDY_P17M_EN 0x80
|
||||
|
||||
/* Attenuation of each analog output port. */
|
||||
#define BURGUNDY_OL13_REG 0x16100
|
||||
#define BURGUNDY_OL14_REG 0x16200
|
||||
#define BURGUNDY_OL15_REG 0x16300
|
||||
#define BURGUNDY_OL16_REG 0x16400
|
||||
#define BURGUNDY_OL17_REG 0x16500
|
||||
|
||||
/* Inputs of four digital mixers. */
|
||||
#define BURGUNDY_MIX0_REG 0x42900
|
||||
#define BURGUNDY_MIX1_REG 0x42A00
|
||||
#define BURGUNDY_MIX2_REG 0x42B00
|
||||
#define BURGUNDY_MIX3_REG 0x42C00
|
||||
#define BURGUNDY_MIX_IS0 0x00010001
|
||||
#define BURGUNDY_MIX_IS1 0x00020002
|
||||
#define BURGUNDY_MIX_IS2 0x00040004
|
||||
#define BURGUNDY_MIX_IS3 0x00080008
|
||||
#define BURGUNDY_MIX_IS4 0x00100010
|
||||
#define BURGUNDY_MIX_ISA 0x01000100 /* Digital stream ISA. */
|
||||
#define BURGUNDY_MIX_ISB 0x02000200 /* Digital stream ISB. */
|
||||
#define BURGUNDY_MIX_ISC 0x04000400 /* Digital stream ISC. */
|
||||
#define BURGUNDY_MIX_ISD 0x08000800 /* Digital stream ISD. */
|
||||
#define BURGUNDY_MIX_ISE 0x10001000 /* Digital stream ISE. */
|
||||
#define BURGUNDY_MIX_ISF 0x20002000 /* Digital stream ISF. */
|
||||
#define BURGUNDY_MIX_ISG 0x40004000 /* Digital stream ISG. */
|
||||
#define BURGUNDY_MIX_ISH 0x80008000 /* Digital stream ISH. */
|
||||
|
||||
/* A digital scalar at the output of each mixer. */
|
||||
#define BURGUNDY_MXS0L_REG 0x12D00
|
||||
#define BURGUNDY_MXS0R_REG 0x12D01
|
||||
#define BURGUNDY_MXS1L_REG 0x12D02
|
||||
#define BURGUNDY_MXS1R_REG 0x12D03
|
||||
#define BURGUNDY_MXS2L_REG 0x12E00
|
||||
#define BURGUNDY_MXS2R_REG 0x12E01
|
||||
#define BURGUNDY_MXS3L_REG 0x12E02
|
||||
#define BURGUNDY_MXS3R_REG 0x12E03
|
||||
#define BURGUNDY_MXS_UNITY 0xDF
|
||||
|
||||
/* Demultiplexer. Routes the mixer 0-3 (see above) to output sources.
|
||||
Output sources 0-2 can be converted to analog. */
|
||||
#define BURGUNDY_OS_REG 0x42F00
|
||||
#define BURGUNDY_OS0_MIX0 0x00000000
|
||||
#define BURGUNDY_OS0_MIX1 0x00000001
|
||||
#define BURGUNDY_OS0_MIX2 0x00000002
|
||||
#define BURGUNDY_OS0_MIX3 0x00000003
|
||||
#define BURGUNDY_OS1_MIX0 0x00000000
|
||||
#define BURGUNDY_OS1_MIX1 0x00000004
|
||||
#define BURGUNDY_OS1_MIX2 0x00000008
|
||||
#define BURGUNDY_OS1_MIX3 0x0000000C
|
||||
#define BURGUNDY_OS2_MIX0 0x00000000
|
||||
#define BURGUNDY_OS2_MIX1 0x00000010
|
||||
#define BURGUNDY_OS2_MIX2 0x00000020
|
||||
#define BURGUNDY_OS2_MIX3 0x00000030
|
||||
#define BURGUNDY_OS3_MIX0 0x00000000
|
||||
#define BURGUNDY_OS3_MIX1 0x00000040
|
||||
#define BURGUNDY_OS3_MIX2 0x00000080
|
||||
#define BURGUNDY_OS3_MIX3 0x000000C0
|
||||
#define BURGUNDY_OSA_MIX0 0x00000000
|
||||
#define BURGUNDY_OSA_MIX1 0x00010000
|
||||
#define BURGUNDY_OSA_MIX2 0x00020000
|
||||
#define BURGUNDY_OSA_MIX3 0x00030000
|
||||
#define BURGUNDY_OSB_MIX0 0x00000000
|
||||
#define BURGUNDY_OSB_MIX1 0x00040000
|
||||
#define BURGUNDY_OSB_MIX2 0x00080000
|
||||
#define BURGUNDY_OSB_MIX3 0x000C0000
|
||||
#define BURGUNDY_OSC_MIX0 0x00000000
|
||||
#define BURGUNDY_OSC_MIX1 0x00100000
|
||||
#define BURGUNDY_OSC_MIX2 0x00200000
|
||||
#define BURGUNDY_OSC_MIX3 0x00300000
|
||||
#define BURGUNDY_OSD_MIX0 0x00000000
|
||||
#define BURGUNDY_OSD_MIX1 0x00400000
|
||||
#define BURGUNDY_OSD_MIX2 0x00800000
|
||||
#define BURGUNDY_OSD_MIX3 0x00C00000
|
||||
#define BURGUNDY_OSE_MIX0 0x00000000
|
||||
#define BURGUNDY_OSE_MIX1 0x01000000
|
||||
#define BURGUNDY_OSE_MIX2 0x02000000
|
||||
#define BURGUNDY_OSE_MIX3 0x03000000
|
||||
#define BURGUNDY_OSF_MIX0 0x00000000
|
||||
#define BURGUNDY_OSF_MIX1 0x04000000
|
||||
#define BURGUNDY_OSF_MIX2 0x08000000
|
||||
#define BURGUNDY_OSF_MIX3 0x0C000000
|
||||
#define BURGUNDY_OSG_MIX0 0x00000000
|
||||
#define BURGUNDY_OSG_MIX1 0x10000000
|
||||
#define BURGUNDY_OSG_MIX2 0x20000000
|
||||
#define BURGUNDY_OSG_MIX3 0x30000000
|
||||
#define BURGUNDY_OSH_MIX0 0x00000000
|
||||
#define BURGUNDY_OSH_MIX1 0x40000000
|
||||
#define BURGUNDY_OSH_MIX2 0x80000000
|
||||
#define BURGUNDY_OSH_MIX3 0xC0000000
|
||||
|
||||
/* A digital scalar for output sources 0 to 3. */
|
||||
#define BURGUNDY_OSS0L_REG 0x13000
|
||||
#define BURGUNDY_OSS0R_REG 0x13001
|
||||
#define BURGUNDY_OSS1L_REG 0x13002
|
||||
#define BURGUNDY_OSS1R_REG 0x13003
|
||||
#define BURGUNDY_OSS2L_REG 0x13100
|
||||
#define BURGUNDY_OSS2R_REG 0x13101
|
||||
#define BURGUNDY_OSS3L_REG 0x13102
|
||||
#define BURGUNDY_OSS3R_REG 0x13103
|
||||
#define BURGUNDY_OSS_UNITY 0xDF
|
||||
|
||||
/* Digital input streams ISA-ISC. A stream may be derived from data coming
|
||||
from the controller in subframes 0 to 3 as well as from internal
|
||||
output sources OSA-OSD. */
|
||||
#define BURGUNDY_SDIN_REG 0x17800
|
||||
#define BURGUNDY_ISA_SF0 0x00
|
||||
#define BURGUNDY_ISA_OSA 0x02
|
||||
#define BURGUNDY_ISB_SF1 0x00
|
||||
#define BURGUNDY_ISB_OSB 0x08
|
||||
#define BURGUNDY_ISC_SF2 0x00
|
||||
#define BURGUNDY_ISC_OSC 0x20
|
||||
#define BURGUNDY_ISD_SF3 0x00
|
||||
#define BURGUNDY_ISD_OSD 0x80
|
||||
|
||||
/* A digital scaler for input streams 0-4 A-H. */
|
||||
#define BURGUNDY_ISSAL_REG 0x12500
|
||||
#define BURGUNDY_ISSAR_REG 0x12501
|
||||
#define BURGUNDY_ISS_UNITY 0xDF
|
||||
|
||||
/*
|
||||
* Screamer codec control bits
|
||||
* This codec has the following 12-bit control registers:
|
||||
* cc0 cc1 cc2 cc4 cc5 cc6 cc7
|
||||
*/
|
||||
|
||||
/* screamer transaction bits. */
|
||||
#define SCREAMER_CODEC_ADDR0 0x00000000
|
||||
#define SCREAMER_CODEC_ADDR1 0x00001000
|
||||
#define SCREAMER_CODEC_ADDR2 0x00002000
|
||||
#define SCREAMER_CODEC_ADDR4 0x00004000
|
||||
#define SCREAMER_CODEC_ADDR5 0x00005000
|
||||
#define SCREAMER_CODEC_ADDR6 0x00006000
|
||||
#define SCREAMER_CODEC_ADDR7 0x00007000
|
||||
#define SCREAMER_CODEC_EMSEL0 0x00000000
|
||||
#define SCREAMER_CODEC_EMSEL1 0x00400000
|
||||
#define SCREAMER_CODEC_EMSEL2 0x00800000
|
||||
#define SCREAMER_CODEC_EMSEL4 0x00c00000
|
||||
|
||||
|
||||
/* cc0 */
|
||||
/*
|
||||
* Bits 7-4 specify the left ADC input gain;
|
||||
* bits 3-0 specify the right ADC input gain.
|
||||
*
|
||||
* The gain is a 4-bit value expressed in units of 1.5 dB,
|
||||
* ranging from 0 dB (0) to +22.5 dB (15).
|
||||
*/
|
||||
#define SCREAMER_DEFAULT_CD_GAIN 0x000000bb /* +16.5 dB */
|
||||
#define SCREAMER_INPUT_CD 0x00000200
|
||||
#define SCREAMER_INPUT_LINE 0x00000400
|
||||
#define SCREAMER_INPUT_MICROPHONE 0x00000800
|
||||
#define SCREAMER_INPUT_MASK 0x00000e00
|
||||
|
||||
/* cc1 */
|
||||
#define SCREAMER_LOOP_THROUGH 0x00000040
|
||||
#define SCREAMER_MUTE_SPEAKER 0x00000080
|
||||
#define SCREAMER_MUTE_HEADPHONES 0x00000200
|
||||
#define SCREAMER_PARALLEL_OUTPUT 0x00000c00
|
||||
#define SCREAMER_PROG_OUTPUT0 0x00000400
|
||||
#define SCREAMER_PROG_OUTPUT1 0x00000800
|
||||
|
||||
/* cc2: headphones/external port attenuation */
|
||||
/* cc4: internal speaker attenuation */
|
||||
/*
|
||||
* Bits 9-6 specify left DAC output attenuation.
|
||||
* Bits 3-0 specify right DAC output attenuation.
|
||||
*
|
||||
* The attenuation is a 4-bit value expressed in units of -1.5 dB,
|
||||
* ranging from 0 dB (0) to -22.5 dB (15).
|
||||
*/
|
||||
|
||||
/* screamer codec status bits. */
|
||||
#define SCREAMER_STATUS_MASK 0x00FFFFFF
|
||||
#define SCREAMER_STATUS_SENSEMASK 0x0000000F
|
||||
#define SCREAMER_STATUS_SENSE0 0x00000008
|
||||
#define SCREAMER_STATUS_SENSE1 0x00000004
|
||||
#define SCREAMER_STATUS_SENSE2 0x00000002
|
||||
#define SCREAMER_STATUS_SENSE3 0x00000001
|
||||
#define SCREAMER_STATUS_PARTMASK 0x00000300
|
||||
#define SCREAMER_STATUS_PARTSHFT 8
|
||||
#define SCREAMER_PART_CRYSTAL 0x00000100
|
||||
#define SCREAMER_PART_NATIONAL 0x00000200
|
||||
#define SCREAMER_PART_TI 0x00000300
|
||||
#define SCREAMER_STATUS_REVMASK 0x0000F000
|
||||
#define SCREAMER_STATUS_REVSHFT 12
|
||||
|
||||
#endif /* _SOUND_DAVBUS_H */
|
||||
|
754
sys/dev/sound/macio/i2s.c
Normal file
754
sys/dev/sound/macio/i2s.c
Normal file
@ -0,0 +1,754 @@
|
||||
/*-
|
||||
* Copyright 2008 by Marco Trillo. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
/*-
|
||||
* Copyright (c) 2002, 2003 Tsubai Masanari. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* NetBSD: snapper.c,v 1.28 2008/05/16 03:49:54 macallan Exp
|
||||
* Id: snapper.c,v 1.11 2002/10/31 17:42:13 tsubai Exp
|
||||
*/
|
||||
|
||||
/*
|
||||
* Apple I2S audio controller.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <machine/dbdma.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#include <machine/resource.h>
|
||||
#include <machine/bus.h>
|
||||
#include <machine/pio.h>
|
||||
#include <sys/rman.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/macio/aoa.h>
|
||||
#include <powerpc/powermac/macgpiovar.h>
|
||||
|
||||
struct i2s_softc {
|
||||
struct aoa_softc aoa;
|
||||
device_t dev;
|
||||
phandle_t node;
|
||||
phandle_t soundnode;
|
||||
struct resource *reg;
|
||||
u_int output_mask;
|
||||
struct mtx port_mtx;
|
||||
};
|
||||
|
||||
static int i2s_probe(device_t);
|
||||
static int i2s_attach(device_t);
|
||||
static void i2s_postattach(void *);
|
||||
static int i2s_setup(struct i2s_softc *, u_int, u_int, u_int);
|
||||
static void i2s_mute_headphone (struct i2s_softc *, int);
|
||||
static void i2s_mute_lineout (struct i2s_softc *, int);
|
||||
static void i2s_mute_speaker (struct i2s_softc *, int);
|
||||
static void i2s_set_outputs(void *, u_int);
|
||||
|
||||
static struct intr_config_hook *i2s_delayed_attach = NULL;
|
||||
|
||||
kobj_class_t i2s_mixer_class = NULL;
|
||||
device_t i2s_mixer = NULL;
|
||||
|
||||
static device_method_t pcm_i2s_methods[] = {
|
||||
/* Device interface. */
|
||||
DEVMETHOD(device_probe, i2s_probe),
|
||||
DEVMETHOD(device_attach, i2s_attach),
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static driver_t pcm_i2s_driver = {
|
||||
"pcm",
|
||||
pcm_i2s_methods,
|
||||
sizeof(struct i2s_softc)
|
||||
};
|
||||
|
||||
DRIVER_MODULE(pcm_i2s, macio, pcm_i2s_driver, pcm_devclass, 0, 0);
|
||||
MODULE_DEPEND(pcm_i2s, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
|
||||
|
||||
static int aoagpio_probe(device_t);
|
||||
static int aoagpio_attach(device_t);
|
||||
|
||||
static device_method_t aoagpio_methods[] = {
|
||||
/* Device interface. */
|
||||
DEVMETHOD(device_probe, aoagpio_probe),
|
||||
DEVMETHOD(device_attach, aoagpio_attach),
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
struct aoagpio_softc {
|
||||
device_t dev;
|
||||
int ctrl;
|
||||
int detect_active; /* for extint-gpio */
|
||||
int level; /* for extint-gpio */
|
||||
struct i2s_softc *i2s; /* for extint-gpio */
|
||||
};
|
||||
|
||||
static driver_t aoagpio_driver = {
|
||||
"aoagpio",
|
||||
aoagpio_methods,
|
||||
sizeof(struct aoagpio_softc)
|
||||
};
|
||||
static devclass_t aoagpio_devclass;
|
||||
|
||||
DRIVER_MODULE(aoagpio, macgpio, aoagpio_driver, aoagpio_devclass, 0, 0);
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
Probe and attachment routines.
|
||||
*****************************************************************************/
|
||||
static int
|
||||
i2s_probe(device_t self)
|
||||
{
|
||||
const char *name;
|
||||
struct i2s_softc *sc;
|
||||
|
||||
name = ofw_bus_get_name(self);
|
||||
if (!name)
|
||||
return (ENXIO);
|
||||
|
||||
if (strcmp(name, "i2s") != 0)
|
||||
return (ENXIO);
|
||||
|
||||
sc = device_get_softc(self);
|
||||
if (!sc)
|
||||
return (ENOMEM);
|
||||
bzero(sc, sizeof(*sc));
|
||||
|
||||
device_set_desc(self, "Apple I2S Audio Controller");
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static phandle_t of_find_firstchild_byname(phandle_t, const char *);
|
||||
|
||||
static int
|
||||
i2s_attach(device_t self)
|
||||
{
|
||||
struct i2s_softc *sc = device_get_softc(self);
|
||||
struct resource *dbdma_irq;
|
||||
void *dbdma_ih;
|
||||
int rid, oirq, err;
|
||||
phandle_t port;
|
||||
|
||||
sc->dev = self;
|
||||
sc->node = ofw_bus_get_node(self);
|
||||
|
||||
port = of_find_firstchild_byname(sc->node, "i2s-a");
|
||||
if (port == -1)
|
||||
return (ENXIO);
|
||||
sc->soundnode = of_find_firstchild_byname(port, "sound");
|
||||
if (sc->soundnode == -1)
|
||||
return (ENXIO);
|
||||
|
||||
mtx_init(&sc->port_mtx, "port_mtx", NULL, MTX_DEF);
|
||||
|
||||
/* Map the controller register space. */
|
||||
rid = 0;
|
||||
sc->reg = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE);
|
||||
if (sc->reg == NULL)
|
||||
return ENXIO;
|
||||
|
||||
/* Map the DBDMA channel register space. */
|
||||
rid = 1;
|
||||
sc->aoa.sc_odma = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
|
||||
RF_ACTIVE);
|
||||
if (sc->aoa.sc_odma == NULL)
|
||||
return ENXIO;
|
||||
|
||||
/* Establish the DBDMA channel edge-triggered interrupt. */
|
||||
rid = 1;
|
||||
dbdma_irq = bus_alloc_resource_any(self, SYS_RES_IRQ,
|
||||
&rid, RF_SHAREABLE | RF_ACTIVE);
|
||||
if (dbdma_irq == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
/* Now initialize the controller. */
|
||||
err = i2s_setup(sc, 44100, 16, 64);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
|
||||
bus_setup_intr(self, dbdma_irq, INTR_TYPE_AV | INTR_MPSAFE, NULL,
|
||||
aoa_interrupt, sc, &dbdma_ih);
|
||||
|
||||
oirq = rman_get_start(dbdma_irq);
|
||||
err = powerpc_config_intr(oirq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
|
||||
/*
|
||||
* Register a hook for delayed attach in order to allow
|
||||
* the I2C controller to attach.
|
||||
*/
|
||||
if ((i2s_delayed_attach = malloc(sizeof(struct intr_config_hook),
|
||||
M_TEMP, M_WAITOK | M_ZERO)) == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
i2s_delayed_attach->ich_func = i2s_postattach;
|
||||
i2s_delayed_attach->ich_arg = self;
|
||||
|
||||
if (config_intrhook_establish(i2s_delayed_attach) != 0)
|
||||
return (ENOMEM);
|
||||
|
||||
return (aoa_attach(self));
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
GPIO routines.
|
||||
*****************************************************************************/
|
||||
|
||||
enum gpio_ctrl {
|
||||
AMP_MUTE,
|
||||
HEADPHONE_MUTE,
|
||||
LINEOUT_MUTE,
|
||||
AUDIO_HW_RESET,
|
||||
HEADPHONE_DETECT,
|
||||
LINEOUT_DETECT,
|
||||
GPIO_CTRL_NUM
|
||||
};
|
||||
|
||||
#define GPIO_CTRL_EXTINT_SET \
|
||||
((1 << HEADPHONE_DETECT) | \
|
||||
(1 << LINEOUT_DETECT))
|
||||
|
||||
static struct aoagpio_softc *gpio_ctrls[GPIO_CTRL_NUM] =
|
||||
{NULL, NULL, NULL, NULL, NULL, NULL};
|
||||
|
||||
static struct gpio_match {
|
||||
const char *name;
|
||||
enum gpio_ctrl ctrl;
|
||||
} gpio_controls[] = {
|
||||
{"headphone-mute", HEADPHONE_MUTE},
|
||||
{"lineout-mute", LINEOUT_MUTE},
|
||||
{"amp-mute", AMP_MUTE},
|
||||
{"headphone-detect", HEADPHONE_DETECT},
|
||||
{"lineout-detect", LINEOUT_DETECT},
|
||||
{"line-output-detect", LINEOUT_DETECT},
|
||||
{"audio-hw-reset", AUDIO_HW_RESET},
|
||||
{"hw-reset", AUDIO_HW_RESET},
|
||||
{NULL, GPIO_CTRL_NUM}
|
||||
};
|
||||
|
||||
static void i2s_cint(struct i2s_softc *);
|
||||
|
||||
static void
|
||||
aoagpio_int(void *cookie)
|
||||
{
|
||||
device_t self = cookie;
|
||||
struct aoagpio_softc *sc;
|
||||
|
||||
sc = device_get_softc(self);
|
||||
|
||||
if (macgpio_read(self) & GPIO_LEVEL_RO)
|
||||
sc->level = sc->detect_active;
|
||||
else
|
||||
sc->level = !(sc->detect_active);
|
||||
|
||||
if (sc->i2s)
|
||||
i2s_cint(sc->i2s);
|
||||
}
|
||||
|
||||
static int
|
||||
aoagpio_probe(device_t gpio)
|
||||
{
|
||||
phandle_t node;
|
||||
char bname[32];
|
||||
const char *name;
|
||||
struct gpio_match *m;
|
||||
struct aoagpio_softc *sc;
|
||||
|
||||
node = ofw_bus_get_node(gpio);
|
||||
if (node == 0 || node == -1)
|
||||
return (EINVAL);
|
||||
|
||||
bzero(bname, sizeof(bname));
|
||||
if (OF_getprop(node, "audio-gpio", bname, sizeof(bname)) > 2)
|
||||
name = bname;
|
||||
else
|
||||
name = ofw_bus_get_name(gpio);
|
||||
|
||||
/* Try to find a match. */
|
||||
for (m = gpio_controls; m->name != NULL; m++) {
|
||||
if (strcmp(name, m->name) == 0) {
|
||||
|
||||
sc = device_get_softc(gpio);
|
||||
gpio_ctrls[m->ctrl] = sc;
|
||||
sc->dev = gpio;
|
||||
sc->ctrl = m->ctrl;
|
||||
sc->level = 0;
|
||||
sc->detect_active = 0;
|
||||
sc->i2s = NULL;
|
||||
|
||||
OF_getprop(node, "audio-gpio-active-state",
|
||||
&sc->detect_active, sizeof(sc->detect_active));
|
||||
|
||||
if ((1 << m->ctrl) & GPIO_CTRL_EXTINT_SET)
|
||||
aoagpio_int(gpio);
|
||||
|
||||
device_set_desc(gpio, m->name);
|
||||
device_quiet(gpio);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
static int
|
||||
aoagpio_attach(device_t gpio)
|
||||
{
|
||||
struct aoagpio_softc *sc;
|
||||
struct resource *r;
|
||||
void *cookie;
|
||||
int irq, rid = 0;
|
||||
|
||||
sc = device_get_softc(gpio);
|
||||
|
||||
if ((1 << sc->ctrl) & GPIO_CTRL_EXTINT_SET) {
|
||||
r = bus_alloc_resource_any(gpio, SYS_RES_IRQ, &rid, RF_ACTIVE);
|
||||
if (r == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
irq = rman_get_start(r);
|
||||
DPRINTF(("interrupting at irq %d\n", irq));
|
||||
|
||||
if (powerpc_config_intr(irq, INTR_TRIGGER_EDGE,
|
||||
INTR_POLARITY_LOW) != 0)
|
||||
return (ENXIO);
|
||||
|
||||
bus_setup_intr(gpio, r, INTR_TYPE_MISC | INTR_MPSAFE |
|
||||
INTR_ENTROPY, NULL, aoagpio_int, gpio, &cookie);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* I2S module registers
|
||||
*/
|
||||
#define I2S_INT 0x00
|
||||
#define I2S_FORMAT 0x10
|
||||
#define I2S_FRAMECOUNT 0x40
|
||||
#define I2S_FRAMEMATCH 0x50
|
||||
#define I2S_WORDSIZE 0x60
|
||||
|
||||
/* I2S_INT register definitions */
|
||||
#define I2S_INT_CLKSTOPPEND 0x01000000 /* clock-stop interrupt pending */
|
||||
|
||||
/* I2S_FORMAT register definitions */
|
||||
#define CLKSRC_49MHz 0x80000000 /* Use 49152000Hz Osc. */
|
||||
#define CLKSRC_45MHz 0x40000000 /* Use 45158400Hz Osc. */
|
||||
#define CLKSRC_18MHz 0x00000000 /* Use 18432000Hz Osc. */
|
||||
#define MCLK_DIV_MASK 0x1f000000 /* MCLK = SRC / DIV */
|
||||
#define SCLK_DIV_MASK 0x00f00000 /* SCLK = MCLK / DIV */
|
||||
#define SCLK_MASTER 0x00080000 /* Master mode */
|
||||
#define SCLK_SLAVE 0x00000000 /* Slave mode */
|
||||
#define SERIAL_FORMAT 0x00070000
|
||||
#define SERIAL_SONY 0x00000000
|
||||
#define SERIAL_64x 0x00010000
|
||||
#define SERIAL_32x 0x00020000
|
||||
#define SERIAL_DAV 0x00040000
|
||||
#define SERIAL_SILICON 0x00050000
|
||||
|
||||
/* I2S_WORDSIZE register definitions */
|
||||
#define INPUT_STEREO (2 << 24)
|
||||
#define INPUT_MONO (1 << 24)
|
||||
#define INPUT_16BIT (0 << 16)
|
||||
#define INPUT_24BIT (3 << 16)
|
||||
#define OUTPUT_STEREO (2 << 8)
|
||||
#define OUTPUT_MONO (1 << 8)
|
||||
#define OUTPUT_16BIT (0 << 0)
|
||||
#define OUTPUT_24BIT (3 << 0)
|
||||
|
||||
/* Master clock, needed by some codecs. We hardcode this
|
||||
to 256 * fs as this is valid for most codecs. */
|
||||
#define MCLK_FS 256
|
||||
|
||||
/* Number of clock sources we can use. */
|
||||
#define NCLKS 3
|
||||
static const struct i2s_clksrc {
|
||||
u_int cs_clock;
|
||||
u_int cs_reg;
|
||||
} clksrc[NCLKS] = {
|
||||
{49152000, CLKSRC_49MHz},
|
||||
{45158400, CLKSRC_45MHz},
|
||||
{18432000, CLKSRC_18MHz}
|
||||
};
|
||||
|
||||
/* Configure the I2S controller for the required settings.
|
||||
'rate' is the frame rate.
|
||||
'wordsize' is the sample size (usually 16 bits).
|
||||
'sclk_fs' is the SCLK/framerate ratio, which needs to be equal
|
||||
or greater to the number of bits per frame. */
|
||||
|
||||
static int
|
||||
i2s_setup(struct i2s_softc *sc, u_int rate, u_int wordsize, u_int sclk_fs)
|
||||
{
|
||||
u_int mclk, mdiv, sdiv;
|
||||
u_int reg = 0, x, wordformat;
|
||||
u_int i;
|
||||
|
||||
/* Make sure the settings are consistent... */
|
||||
if ((wordsize * 2) > sclk_fs)
|
||||
return (EINVAL);
|
||||
|
||||
if (sclk_fs != 32 && sclk_fs != 64)
|
||||
return (EINVAL);
|
||||
|
||||
/*
|
||||
* Find a clock source to derive the master clock (MCLK)
|
||||
* and the I2S bit block (SCLK) and set the divisors as
|
||||
* appropriate.
|
||||
*/
|
||||
mclk = rate * MCLK_FS;
|
||||
sdiv = MCLK_FS / sclk_fs;
|
||||
|
||||
for (i = 0; i < NCLKS; ++i) {
|
||||
if ((clksrc[i].cs_clock % mclk) == 0) {
|
||||
reg = clksrc[i].cs_reg;
|
||||
mdiv = clksrc[i].cs_clock / mclk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (reg == 0)
|
||||
return (EINVAL);
|
||||
|
||||
switch (mdiv) {
|
||||
/* exception cases */
|
||||
case 1:
|
||||
x = 14;
|
||||
break;
|
||||
case 3:
|
||||
x = 13;
|
||||
break;
|
||||
case 5:
|
||||
x = 12;
|
||||
break;
|
||||
default:
|
||||
x = (mdiv / 2) - 1;
|
||||
break;
|
||||
}
|
||||
reg |= (x << 24) & MCLK_DIV_MASK;
|
||||
|
||||
switch (sdiv) {
|
||||
case 1:
|
||||
x = 8;
|
||||
break;
|
||||
case 3:
|
||||
x = 9;
|
||||
break;
|
||||
default:
|
||||
x = (sdiv / 2) - 1;
|
||||
break;
|
||||
}
|
||||
reg |= (x << 20) & SCLK_DIV_MASK;
|
||||
|
||||
/*
|
||||
* XXX use master mode for now. This needs to be
|
||||
* revisited if we want to add recording from SPDIF some day.
|
||||
*/
|
||||
reg |= SCLK_MASTER;
|
||||
|
||||
switch (sclk_fs) {
|
||||
case 64:
|
||||
reg |= SERIAL_64x;
|
||||
break;
|
||||
case 32:
|
||||
reg |= SERIAL_32x;
|
||||
break;
|
||||
}
|
||||
|
||||
/* stereo input and output */
|
||||
wordformat = INPUT_STEREO | OUTPUT_STEREO;
|
||||
|
||||
switch (wordsize) {
|
||||
case 16:
|
||||
wordformat |= INPUT_16BIT | OUTPUT_16BIT;
|
||||
break;
|
||||
case 24:
|
||||
wordformat |= INPUT_24BIT | OUTPUT_24BIT;
|
||||
break;
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
x = bus_read_4(sc->reg, I2S_WORDSIZE);
|
||||
if (x != wordformat)
|
||||
bus_write_4(sc->reg, I2S_WORDSIZE, wordformat);
|
||||
|
||||
x = bus_read_4(sc->reg, I2S_FORMAT);
|
||||
if (x != reg) {
|
||||
/*
|
||||
* XXX to change the format we need to stop the clock
|
||||
* via the FCR registers. For now, rely on the firmware
|
||||
* to set sane defaults (44100).
|
||||
*/
|
||||
printf("i2s_setup: changing format not supported yet.\n");
|
||||
return (EOPNOTSUPP);
|
||||
|
||||
#ifdef notyet
|
||||
if (obio_fcr_isset(OBIO_FCR1, I2S0CLKEN)) {
|
||||
|
||||
bus_space_write_4(sc->sc_tag, sc->sc_bsh, I2S_INT,
|
||||
I2S_INT_CLKSTOPPEND);
|
||||
|
||||
obio_fcr_clear(OBIO_FCR1, I2S0CLKEN);
|
||||
|
||||
for (timo = 1000; timo > 0; timo--) {
|
||||
if (bus_space_read_4(sc->sc_tag, sc->sc_bsh,
|
||||
I2S_INT) & I2S_INT_CLKSTOPPEND)
|
||||
break;
|
||||
|
||||
DELAY(10);
|
||||
}
|
||||
|
||||
if (timo == 0)
|
||||
printf("%s: timeout waiting for clock to stop\n",
|
||||
sc->sc_dev.dv_xname);
|
||||
}
|
||||
|
||||
bus_space_write_4(sc->sc_tag, sc->sc_bsh, I2S_FORMAT, reg);
|
||||
|
||||
obio_fcr_set(OBIO_FCR1, I2S0CLKEN);
|
||||
#endif
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/* XXX this does not belong here. */
|
||||
static phandle_t
|
||||
of_find_firstchild_byname(phandle_t node, const char *req_name)
|
||||
{
|
||||
char name[32]; /* max name len per OF spec. */
|
||||
phandle_t n;
|
||||
|
||||
for (n = OF_child(node); n != -1; n = OF_peer(n)) {
|
||||
bzero(name, sizeof(name));
|
||||
OF_getprop(n, "name", name, sizeof(name));
|
||||
|
||||
if (strcmp(name, req_name) == 0)
|
||||
return (n);
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
||||
static u_int
|
||||
gpio_read(enum gpio_ctrl ctrl)
|
||||
{
|
||||
struct aoagpio_softc *sc;
|
||||
|
||||
if ((sc = gpio_ctrls[ctrl]) == NULL)
|
||||
return (0);
|
||||
|
||||
return (macgpio_read(sc->dev) & GPIO_DATA);
|
||||
}
|
||||
|
||||
static void
|
||||
gpio_write(enum gpio_ctrl ctrl, u_int x)
|
||||
{
|
||||
struct aoagpio_softc *sc;
|
||||
u_int reg;
|
||||
|
||||
if ((sc = gpio_ctrls[ctrl]) == NULL)
|
||||
return;
|
||||
|
||||
reg = GPIO_DDR_OUTPUT;
|
||||
if (x)
|
||||
reg |= GPIO_DATA;
|
||||
|
||||
macgpio_write(sc->dev, reg);
|
||||
}
|
||||
|
||||
static void
|
||||
i2s_cint(struct i2s_softc *sc)
|
||||
{
|
||||
u_int mask = 0;
|
||||
|
||||
if (gpio_ctrls[HEADPHONE_DETECT] &&
|
||||
gpio_ctrls[HEADPHONE_DETECT]->level)
|
||||
mask |= 1 << 1;
|
||||
|
||||
if (gpio_ctrls[LINEOUT_DETECT] &&
|
||||
gpio_ctrls[LINEOUT_DETECT]->level)
|
||||
mask |= 1 << 2;
|
||||
|
||||
if (mask == 0)
|
||||
mask = 1 << 0; /* fall back to speakers. */
|
||||
|
||||
i2s_set_outputs(sc, mask);
|
||||
}
|
||||
|
||||
#define reset_active 0
|
||||
|
||||
/* these values are in microseconds */
|
||||
#define RESET_SETUP_TIME 5000
|
||||
#define RESET_HOLD_TIME 20000
|
||||
#define RESET_RELEASE_TIME 10000
|
||||
|
||||
static void
|
||||
i2s_audio_hw_reset(struct i2s_softc *sc)
|
||||
{
|
||||
if (gpio_ctrls[AUDIO_HW_RESET]) {
|
||||
DPRINTF(("resetting codec\n"));
|
||||
|
||||
gpio_write(AUDIO_HW_RESET, !reset_active); /* Negate RESET */
|
||||
DELAY(RESET_SETUP_TIME);
|
||||
|
||||
gpio_write(AUDIO_HW_RESET, reset_active); /* Assert RESET */
|
||||
DELAY(RESET_HOLD_TIME);
|
||||
|
||||
gpio_write(AUDIO_HW_RESET, !reset_active); /* Negate RESET */
|
||||
DELAY(RESET_RELEASE_TIME);
|
||||
|
||||
} else {
|
||||
DPRINTF(("no audio_hw_reset\n"));
|
||||
}
|
||||
}
|
||||
|
||||
#define AMP_ACTIVE 0 /* XXX OF */
|
||||
#define HEADPHONE_ACTIVE 0 /* XXX OF */
|
||||
#define LINEOUT_ACTIVE 0 /* XXX OF */
|
||||
|
||||
#define MUTE_CONTROL(xxx, yyy) \
|
||||
static void \
|
||||
i2s_mute_##xxx(struct i2s_softc *sc, int mute) \
|
||||
{ \
|
||||
int x; \
|
||||
\
|
||||
if (gpio_ctrls[yyy##_MUTE] == NULL) \
|
||||
return; \
|
||||
if (mute) \
|
||||
x = yyy##_ACTIVE; \
|
||||
else \
|
||||
x = ! yyy##_ACTIVE; \
|
||||
\
|
||||
if (x != gpio_read(yyy##_MUTE)) \
|
||||
gpio_write(yyy##_MUTE, x); \
|
||||
}
|
||||
|
||||
MUTE_CONTROL(speaker, AMP)
|
||||
MUTE_CONTROL(headphone, HEADPHONE)
|
||||
MUTE_CONTROL(lineout, LINEOUT)
|
||||
|
||||
static void
|
||||
i2s_set_outputs(void *ptr, u_int mask)
|
||||
{
|
||||
struct i2s_softc *sc = ptr;
|
||||
|
||||
if (mask == sc->output_mask)
|
||||
return;
|
||||
|
||||
mtx_lock(&sc->port_mtx);
|
||||
|
||||
i2s_mute_speaker(sc, 1);
|
||||
i2s_mute_headphone(sc, 1);
|
||||
i2s_mute_lineout(sc, 1);
|
||||
|
||||
DPRINTF(("enabled outputs: "));
|
||||
|
||||
if (mask & (1 << 0)) {
|
||||
DPRINTF(("SPEAKER "));
|
||||
i2s_mute_speaker(sc, 0);
|
||||
}
|
||||
if (mask & (1 << 1)) {
|
||||
DPRINTF(("HEADPHONE "));
|
||||
i2s_mute_headphone(sc, 0);
|
||||
}
|
||||
if (mask & (1 << 2)) {
|
||||
DPRINTF(("LINEOUT "));
|
||||
i2s_mute_lineout(sc, 0);
|
||||
}
|
||||
|
||||
DPRINTF(("\n"));
|
||||
sc->output_mask = mask;
|
||||
|
||||
mtx_unlock(&sc->port_mtx);
|
||||
}
|
||||
|
||||
static void
|
||||
i2s_postattach(void *arg)
|
||||
{
|
||||
device_t self = arg;
|
||||
struct i2s_softc *sc;
|
||||
int i;
|
||||
|
||||
KASSERT(self != NULL, ("bad arg"));
|
||||
KASSERT(i2s_delayed_attach != NULL, ("bogus call"));
|
||||
|
||||
sc = device_get_softc(self);
|
||||
|
||||
/* Reset the codec. */
|
||||
i2s_audio_hw_reset(sc);
|
||||
|
||||
/* If we have a codec, initialize it. */
|
||||
if (i2s_mixer)
|
||||
mixer_init(self, i2s_mixer_class, i2s_mixer);
|
||||
|
||||
/* Read initial port status. */
|
||||
i2s_cint(sc);
|
||||
|
||||
/* Enable GPIO interrupt callback. */
|
||||
for (i = 0; i < GPIO_CTRL_NUM; i++)
|
||||
if (gpio_ctrls[i])
|
||||
gpio_ctrls[i]->i2s = sc;
|
||||
|
||||
config_intrhook_disestablish(i2s_delayed_attach);
|
||||
free(i2s_delayed_attach, M_TEMP);
|
||||
}
|
||||
|
468
sys/dev/sound/macio/snapper.c
Normal file
468
sys/dev/sound/macio/snapper.c
Normal file
@ -0,0 +1,468 @@
|
||||
/*-
|
||||
* Copyright 2008 by Marco Trillo. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
/*-
|
||||
* Copyright (c) 2002, 2003 Tsubai Masanari. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* NetBSD: snapper.c,v 1.28 2008/05/16 03:49:54 macallan Exp
|
||||
* Id: snapper.c,v 1.11 2002/10/31 17:42:13 tsubai Exp
|
||||
*/
|
||||
|
||||
/*
|
||||
* Apple Snapper audio.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <machine/dbdma.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#include <machine/resource.h>
|
||||
#include <machine/bus.h>
|
||||
#include <machine/pio.h>
|
||||
#include <sys/rman.h>
|
||||
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include "mixer_if.h"
|
||||
|
||||
extern kobj_class_t i2s_mixer_class;
|
||||
extern device_t i2s_mixer;
|
||||
|
||||
struct snapper_softc
|
||||
{
|
||||
device_t sc_dev;
|
||||
uint32_t sc_addr;
|
||||
};
|
||||
|
||||
static int snapper_probe(device_t);
|
||||
static int snapper_attach(device_t);
|
||||
static int snapper_init(struct snd_mixer *m);
|
||||
static void snapper_uninit(struct snd_mixer *m);
|
||||
static int snapper_reinit(struct snd_mixer *m);
|
||||
static int snapper_set(struct snd_mixer *m, unsigned dev, unsigned left,
|
||||
unsigned right);
|
||||
static int snapper_setrecsrc(struct snd_mixer *m, u_int32_t src);
|
||||
|
||||
static device_method_t snapper_methods[] = {
|
||||
/* Device interface. */
|
||||
DEVMETHOD(device_probe, snapper_probe),
|
||||
DEVMETHOD(device_attach, snapper_attach),
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static driver_t snapper_driver = {
|
||||
"snapper",
|
||||
snapper_methods,
|
||||
sizeof(struct snapper_softc)
|
||||
};
|
||||
static devclass_t snapper_devclass;
|
||||
|
||||
DRIVER_MODULE(snapper, iicbus, snapper_driver, snapper_devclass, 0, 0);
|
||||
MODULE_VERSION(snapper, 1);
|
||||
MODULE_DEPEND(snapper, iicbus, 1, 1, 1);
|
||||
|
||||
static kobj_method_t snapper_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, snapper_init),
|
||||
KOBJMETHOD(mixer_uninit, snapper_uninit),
|
||||
KOBJMETHOD(mixer_reinit, snapper_reinit),
|
||||
KOBJMETHOD(mixer_set, snapper_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, snapper_setrecsrc),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
MIXER_DECLARE(snapper_mixer);
|
||||
|
||||
#define SNAPPER_IICADDR 0x6a /* Hard-coded I2C slave addr */
|
||||
|
||||
/* Snapper (Texas Instruments TAS3004) registers. */
|
||||
#define SNAPPER_MCR1 0x01 /* Main control register 1 (1byte) */
|
||||
#define SNAPPER_DRC 0x02 /* Dynamic range compression (6bytes) */
|
||||
#define SNAPPER_VOLUME 0x04 /* Volume (6bytes) */
|
||||
#define SNAPPER_TREBLE 0x05 /* Treble control (1byte) */
|
||||
#define SNAPPER_BASS 0x06 /* Bass control (1byte) */
|
||||
#define SNAPPER_MIXER_L 0x07 /* Mixer left gain (9bytes) */
|
||||
#define SNAPPER_MIXER_R 0x08 /* Mixer right gain (9bytes) */
|
||||
#define SNAPPER_LB0 0x0a /* Left biquad 0 (15bytes) */
|
||||
#define SNAPPER_LB1 0x0b /* Left biquad 1 (15bytes) */
|
||||
#define SNAPPER_LB2 0x0c /* Left biquad 2 (15bytes) */
|
||||
#define SNAPPER_LB3 0x0d /* Left biquad 3 (15bytes) */
|
||||
#define SNAPPER_LB4 0x0e /* Left biquad 4 (15bytes) */
|
||||
#define SNAPPER_LB5 0x0f /* Left biquad 5 (15bytes) */
|
||||
#define SNAPPER_LB6 0x10 /* Left biquad 6 (15bytes) */
|
||||
#define SNAPPER_RB0 0x13 /* Right biquad 0 (15bytes) */
|
||||
#define SNAPPER_RB1 0x14 /* Right biquad 1 (15bytes) */
|
||||
#define SNAPPER_RB2 0x15 /* Right biquad 2 (15bytes) */
|
||||
#define SNAPPER_RB3 0x16 /* Right biquad 3 (15bytes) */
|
||||
#define SNAPPER_RB4 0x17 /* Right biquad 4 (15bytes) */
|
||||
#define SNAPPER_RB5 0x18 /* Right biquad 5 (15bytes) */
|
||||
#define SNAPPER_RB6 0x19 /* Right biquad 6 (15bytes) */
|
||||
#define SNAPPER_LLB 0x21 /* Left loudness biquad (15bytes) */
|
||||
#define SNAPPER_RLB 0x22 /* Right loudness biquad (15bytes) */
|
||||
#define SNAPPER_LLB_GAIN 0x23 /* Left loudness biquad gain (3bytes) */
|
||||
#define SNAPPER_RLB_GAIN 0x24 /* Right loudness biquad gain (3bytes) */
|
||||
#define SNAPPER_ACR 0x40 /* Analog control register (1byte) */
|
||||
#define SNAPPER_MCR2 0x43 /* Main control register 2 (1byte) */
|
||||
#define SNAPPER_MCR1_FL 0x80 /* Fast load */
|
||||
#define SNAPPER_MCR1_SC 0x40 /* SCLK frequency */
|
||||
#define SNAPPER_MCR1_SC_32 0x00 /* 32fs */
|
||||
#define SNAPPER_MCR1_SC_64 0x40 /* 64fs */
|
||||
#define SNAPPER_MCR1_SM 0x30 /* Output serial port mode */
|
||||
#define SNAPPER_MCR1_SM_L 0x00 /* Left justified */
|
||||
#define SNAPPER_MCR1_SM_R 0x10 /* Right justified */
|
||||
#define SNAPPER_MCR1_SM_I2S 0x20 /* I2S */
|
||||
#define SNAPPER_MCR1_W 0x03 /* Serial port word length */
|
||||
#define SNAPPER_MCR1_W_16 0x00 /* 16 bit */
|
||||
#define SNAPPER_MCR1_W_18 0x01 /* 18 bit */
|
||||
#define SNAPPER_MCR1_W_20 0x02 /* 20 bit */
|
||||
#define SNAPPER_MCR1_W_24 0x03 /* 20 bit */
|
||||
#define SNAPPER_MCR2_DL 0x80 /* Download */
|
||||
#define SNAPPER_MCR2_AP 0x02 /* All pass mode */
|
||||
#define SNAPPER_ACR_ADM 0x80 /* ADC output mode */
|
||||
#define SNAPPER_ACR_LRB 0x40 /* Select B input */
|
||||
#define SNAPPER_ACR_DM 0x0c /* De-emphasis control */
|
||||
#define SNAPPER_ACR_DM_OFF 0x00 /* off */
|
||||
#define SNAPPER_ACR_DM_48 0x04 /* fs = 48kHz */
|
||||
#define SNAPPER_ACR_DM_44 0x08 /* fs = 44.1kHz */
|
||||
#define SNAPPER_ACR_INP 0x02 /* Analog input select */
|
||||
#define SNAPPER_ACR_INP_A 0x00 /* A */
|
||||
#define SNAPPER_ACR_INP_B 0x02 /* B */
|
||||
#define SNAPPER_ACR_APD 0x01 /* Analog power down */
|
||||
|
||||
|
||||
struct snapper_reg {
|
||||
u_char MCR1[1];
|
||||
u_char DRC[6];
|
||||
u_char VOLUME[6];
|
||||
u_char TREBLE[1];
|
||||
u_char BASS[1];
|
||||
u_char MIXER_L[9];
|
||||
u_char MIXER_R[9];
|
||||
u_char LB0[15];
|
||||
u_char LB1[15];
|
||||
u_char LB2[15];
|
||||
u_char LB3[15];
|
||||
u_char LB4[15];
|
||||
u_char LB5[15];
|
||||
u_char LB6[15];
|
||||
u_char RB0[15];
|
||||
u_char RB1[15];
|
||||
u_char RB2[15];
|
||||
u_char RB3[15];
|
||||
u_char RB4[15];
|
||||
u_char RB5[15];
|
||||
u_char RB6[15];
|
||||
u_char LLB[15];
|
||||
u_char RLB[15];
|
||||
u_char LLB_GAIN[3];
|
||||
u_char RLB_GAIN[3];
|
||||
u_char ACR[1];
|
||||
u_char MCR2[1];
|
||||
};
|
||||
|
||||
static const struct snapper_reg snapper_initdata = {
|
||||
{ SNAPPER_MCR1_SC_64 | SNAPPER_MCR1_SM_I2S |
|
||||
SNAPPER_MCR1_W_16 }, /* MCR1 */
|
||||
{ 1, 0, 0, 0, 0, 0 }, /* DRC */
|
||||
{ 0, 0, 0, 0, 0, 0 }, /* VOLUME */
|
||||
{ 0x72 }, /* TREBLE */
|
||||
{ 0x72 }, /* BASS */
|
||||
{ 0x10, 0x00, 0x00, 0, 0, 0, 0, 0, 0 }, /* MIXER_L */
|
||||
{ 0x10, 0x00, 0x00, 0, 0, 0, 0, 0, 0 }, /* MIXER_R */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0, 0, 0 }, /* LLB_GAIN */
|
||||
{ 0, 0, 0 }, /* RLB_GAIN */
|
||||
{ SNAPPER_ACR_ADM | SNAPPER_ACR_LRB | SNAPPER_ACR_INP_B },/* ACR */
|
||||
{ SNAPPER_MCR2_AP } /* MCR2 */
|
||||
};
|
||||
|
||||
static const char snapper_regsize[] = {
|
||||
0, /* 0x00 */
|
||||
sizeof snapper_initdata.MCR1, /* 0x01 */
|
||||
sizeof snapper_initdata.DRC, /* 0x02 */
|
||||
0, /* 0x03 */
|
||||
sizeof snapper_initdata.VOLUME, /* 0x04 */
|
||||
sizeof snapper_initdata.TREBLE, /* 0x05 */
|
||||
sizeof snapper_initdata.BASS, /* 0x06 */
|
||||
sizeof snapper_initdata.MIXER_L, /* 0x07 */
|
||||
sizeof snapper_initdata.MIXER_R, /* 0x08 */
|
||||
0, /* 0x09 */
|
||||
sizeof snapper_initdata.LB0, /* 0x0a */
|
||||
sizeof snapper_initdata.LB1, /* 0x0b */
|
||||
sizeof snapper_initdata.LB2, /* 0x0c */
|
||||
sizeof snapper_initdata.LB3, /* 0x0d */
|
||||
sizeof snapper_initdata.LB4, /* 0x0e */
|
||||
sizeof snapper_initdata.LB5, /* 0x0f */
|
||||
sizeof snapper_initdata.LB6, /* 0x10 */
|
||||
0, /* 0x11 */
|
||||
0, /* 0x12 */
|
||||
sizeof snapper_initdata.RB0, /* 0x13 */
|
||||
sizeof snapper_initdata.RB1, /* 0x14 */
|
||||
sizeof snapper_initdata.RB2, /* 0x15 */
|
||||
sizeof snapper_initdata.RB3, /* 0x16 */
|
||||
sizeof snapper_initdata.RB4, /* 0x17 */
|
||||
sizeof snapper_initdata.RB5, /* 0x18 */
|
||||
sizeof snapper_initdata.RB6, /* 0x19 */
|
||||
0,0,0,0, 0,0,
|
||||
0, /* 0x20 */
|
||||
sizeof snapper_initdata.LLB, /* 0x21 */
|
||||
sizeof snapper_initdata.RLB, /* 0x22 */
|
||||
sizeof snapper_initdata.LLB_GAIN, /* 0x23 */
|
||||
sizeof snapper_initdata.RLB_GAIN, /* 0x24 */
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
||||
sizeof snapper_initdata.ACR, /* 0x40 */
|
||||
0, /* 0x41 */
|
||||
0, /* 0x42 */
|
||||
sizeof snapper_initdata.MCR2 /* 0x43 */
|
||||
};
|
||||
|
||||
/* dB = 20 * log (x) table. */
|
||||
static u_int snapper_volume_table[100] = {
|
||||
0x00000148, 0x0000015C, 0x00000171, 0x00000186, // -46.0, -45.5, -45.0, -44.5,
|
||||
0x0000019E, 0x000001B6, 0x000001D0, 0x000001EB, // -44.0, -43.5, -43.0, -42.5,
|
||||
0x00000209, 0x00000227, 0x00000248, 0x0000026B, // -42.0, -41.5, -41.0, -40.5,
|
||||
0x0000028F, 0x000002B6, 0x000002DF, 0x0000030B, // -40.0, -39.5, -39.0, -38.5,
|
||||
0x00000339, 0x0000036A, 0x0000039E, 0x000003D5, // -38.0, -37.5, -37.0, -36.5,
|
||||
0x0000040F, 0x0000044C, 0x0000048D, 0x000004D2, // -36.0, -35.5, -35.0, -34.5,
|
||||
0x0000051C, 0x00000569, 0x000005BB, 0x00000612, // -34.0, -33.5, -33.0, -32.5,
|
||||
0x0000066E, 0x000006D0, 0x00000737, 0x000007A5, // -32.0, -31.5, -31.0, -30.5,
|
||||
0x00000818, 0x00000893, 0x00000915, 0x0000099F, // -30.0, -29.5, -29.0, -28.5,
|
||||
0x00000A31, 0x00000ACC, 0x00000B6F, 0x00000C1D, // -28.0, -27.5, -27.0, -26.5,
|
||||
0x00000CD5, 0x00000D97, 0x00000E65, 0x00000F40, // -26.0, -25.5, -25.0, -24.5,
|
||||
0x00001027, 0x0000111C, 0x00001220, 0x00001333, // -24.0, -23.5, -23.0, -22.5,
|
||||
0x00001456, 0x0000158A, 0x000016D1, 0x0000182B, // -22.0, -21.5, -21.0, -20.5,
|
||||
0x0000199A, 0x00001B1E, 0x00001CB9, 0x00001E6D, // -20.0, -19.5, -19.0, -18.5,
|
||||
0x0000203A, 0x00002223, 0x00002429, 0x0000264E, // -18.0, -17.5, -17.0, -16.5,
|
||||
0x00002893, 0x00002AFA, 0x00002D86, 0x00003039, // -16.0, -15.5, -15.0, -14.5,
|
||||
0x00003314, 0x0000361B, 0x00003950, 0x00003CB5, // -14.0, -13.5, -13.0, -12.5,
|
||||
0x0000404E, 0x0000441D, 0x00004827, 0x00004C6D, // -12.0, -11.5, -11.0, -10.5,
|
||||
0x000050F4, 0x000055C0, 0x00005AD5, 0x00006037, // -10.0, -9.5, -9.0, -8.5,
|
||||
0x000065EA, 0x00006BF4, 0x0000725A, 0x00007920, // -8.0, -7.5, -7.0, -6.5,
|
||||
0x0000804E, 0x000087EF, 0x00008FF6, 0x0000987D, // -6.0, -5.5, -5.0, -4.5,
|
||||
0x0000A186, 0x0000AB19, 0x0000B53C, 0x0000BFF9, // -4.0, -3.5, -3.0, -2.5,
|
||||
0x0000CB59, 0x0000D766, 0x0000E429, 0x0000F1AE, // -2.0, -1.5, -1.0, -0.5,
|
||||
0x00010000, 0x00010F2B, 0x00011F3D, 0x00013042, // 0.0, +0.5, +1.0, +1.5,
|
||||
0x00014249, 0x00015562, 0x0001699C, 0x00017F09 // 2.0, +2.5, +3.0, +3.5,
|
||||
};
|
||||
|
||||
static int
|
||||
snapper_write(struct snapper_softc *sc, uint8_t reg, const void *data)
|
||||
{
|
||||
u_int size;
|
||||
uint8_t buf[16];
|
||||
|
||||
struct iic_msg msg[] = {
|
||||
{ sc->sc_addr, IIC_M_WR, 0, buf }
|
||||
};
|
||||
|
||||
KASSERT(reg < sizeof(snapper_regsize), ("bad reg"));
|
||||
size = snapper_regsize[reg];
|
||||
msg[0].len = size + 1;
|
||||
buf[0] = reg;
|
||||
memcpy(&buf[1], data, size);
|
||||
|
||||
iicbus_transfer(sc->sc_dev, msg, 1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
snapper_probe(device_t dev)
|
||||
{
|
||||
const char *name, *compat;
|
||||
|
||||
name = ofw_bus_get_name(dev);
|
||||
if (name == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
if (strcmp(name, "deq") == 0) {
|
||||
if (iicbus_get_addr(dev) != SNAPPER_IICADDR)
|
||||
return (ENXIO);
|
||||
} else if (strcmp(name, "codec") == 0) {
|
||||
compat = ofw_bus_get_compat(dev);
|
||||
if (compat == NULL || strcmp(compat, "tas3004") != 0)
|
||||
return (ENXIO);
|
||||
} else {
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
device_set_desc(dev, "Texas Instruments TAS3004 Audio Codec");
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
snapper_attach(device_t dev)
|
||||
{
|
||||
struct snapper_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->sc_dev = dev;
|
||||
sc->sc_addr = iicbus_get_addr(dev);
|
||||
|
||||
i2s_mixer_class = &snapper_mixer_class;
|
||||
i2s_mixer = dev;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
snapper_init(struct snd_mixer *m)
|
||||
{
|
||||
struct snapper_softc *sc;
|
||||
u_int x = 0;
|
||||
|
||||
sc = device_get_softc(mix_getdevinfo(m));
|
||||
|
||||
snapper_write(sc, SNAPPER_LB0, snapper_initdata.LB0);
|
||||
snapper_write(sc, SNAPPER_LB1, snapper_initdata.LB1);
|
||||
snapper_write(sc, SNAPPER_LB2, snapper_initdata.LB2);
|
||||
snapper_write(sc, SNAPPER_LB3, snapper_initdata.LB3);
|
||||
snapper_write(sc, SNAPPER_LB4, snapper_initdata.LB4);
|
||||
snapper_write(sc, SNAPPER_LB5, snapper_initdata.LB5);
|
||||
snapper_write(sc, SNAPPER_LB6, snapper_initdata.LB6);
|
||||
snapper_write(sc, SNAPPER_RB0, snapper_initdata.RB0);
|
||||
snapper_write(sc, SNAPPER_RB1, snapper_initdata.RB1);
|
||||
snapper_write(sc, SNAPPER_RB1, snapper_initdata.RB1);
|
||||
snapper_write(sc, SNAPPER_RB2, snapper_initdata.RB2);
|
||||
snapper_write(sc, SNAPPER_RB3, snapper_initdata.RB3);
|
||||
snapper_write(sc, SNAPPER_RB4, snapper_initdata.RB4);
|
||||
snapper_write(sc, SNAPPER_RB5, snapper_initdata.RB5);
|
||||
snapper_write(sc, SNAPPER_RB6, snapper_initdata.RB6);
|
||||
snapper_write(sc, SNAPPER_MCR1, snapper_initdata.MCR1);
|
||||
snapper_write(sc, SNAPPER_MCR2, snapper_initdata.MCR2);
|
||||
snapper_write(sc, SNAPPER_DRC, snapper_initdata.DRC);
|
||||
snapper_write(sc, SNAPPER_VOLUME, snapper_initdata.VOLUME);
|
||||
snapper_write(sc, SNAPPER_TREBLE, snapper_initdata.TREBLE);
|
||||
snapper_write(sc, SNAPPER_BASS, snapper_initdata.BASS);
|
||||
snapper_write(sc, SNAPPER_MIXER_L, snapper_initdata.MIXER_L);
|
||||
snapper_write(sc, SNAPPER_MIXER_R, snapper_initdata.MIXER_R);
|
||||
snapper_write(sc, SNAPPER_LLB, snapper_initdata.LLB);
|
||||
snapper_write(sc, SNAPPER_RLB, snapper_initdata.RLB);
|
||||
snapper_write(sc, SNAPPER_LLB_GAIN, snapper_initdata.LLB_GAIN);
|
||||
snapper_write(sc, SNAPPER_RLB_GAIN, snapper_initdata.RLB_GAIN);
|
||||
snapper_write(sc, SNAPPER_ACR, snapper_initdata.ACR);
|
||||
|
||||
x |= SOUND_MASK_VOLUME;
|
||||
mix_setdevs(m, x);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
snapper_uninit(struct snd_mixer *m)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
snapper_reinit(struct snd_mixer *m)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
snapper_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
{
|
||||
struct snapper_softc *sc;
|
||||
u_int l, r;
|
||||
u_char reg[6];
|
||||
|
||||
sc = device_get_softc(mix_getdevinfo(m));
|
||||
|
||||
if (left > 100 || right > 100)
|
||||
return (0);
|
||||
|
||||
l = (left == 0) ? 0 : snapper_volume_table[left - 1];
|
||||
r = (right == 0) ? 0 : snapper_volume_table[right - 1];
|
||||
|
||||
switch (dev) {
|
||||
case SOUND_MIXER_VOLUME:
|
||||
reg[0] = (l & 0xff0000) >> 16;
|
||||
reg[1] = (l & 0x00ff00) >> 8;
|
||||
reg[2] = l & 0x0000ff;
|
||||
reg[3] = (r & 0xff0000) >> 16;
|
||||
reg[4] = (r & 0x00ff00) >> 8;
|
||||
reg[5] = r & 0x0000ff;
|
||||
snapper_write(sc, SNAPPER_VOLUME, reg);
|
||||
|
||||
return (left | (right << 8));
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
snapper_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
423
sys/dev/sound/macio/tumbler.c
Normal file
423
sys/dev/sound/macio/tumbler.c
Normal file
@ -0,0 +1,423 @@
|
||||
/*-
|
||||
* Copyright 2008 by Marco Trillo. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
/*-
|
||||
* Copyright (c) 2002, 2003 Tsubai Masanari. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* NetBSD: tumbler.c,v 1.28 2008/05/16 03:49:54 macallan Exp
|
||||
* Id: tumbler.c,v 1.11 2002/10/31 17:42:13 tsubai Exp
|
||||
*/
|
||||
|
||||
/*
|
||||
* Apple I2S audio controller.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <machine/dbdma.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#include <machine/resource.h>
|
||||
#include <machine/bus.h>
|
||||
#include <machine/pio.h>
|
||||
#include <sys/rman.h>
|
||||
|
||||
#include <dev/iicbus/iicbus.h>
|
||||
#include <dev/iicbus/iiconf.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include "mixer_if.h"
|
||||
|
||||
extern kobj_class_t i2s_mixer_class;
|
||||
extern device_t i2s_mixer;
|
||||
|
||||
struct tumbler_softc
|
||||
{
|
||||
device_t sc_dev;
|
||||
uint32_t sc_addr;
|
||||
};
|
||||
|
||||
static int tumbler_probe(device_t);
|
||||
static int tumbler_attach(device_t);
|
||||
static int tumbler_init(struct snd_mixer *m);
|
||||
static void tumbler_uninit(struct snd_mixer *m);
|
||||
static int tumbler_reinit(struct snd_mixer *m);
|
||||
static int tumbler_set(struct snd_mixer *m, unsigned dev, unsigned left,
|
||||
unsigned right);
|
||||
static int tumbler_setrecsrc(struct snd_mixer *m, u_int32_t src);
|
||||
static int tumbler_set_volume(struct tumbler_softc *sc, int left,
|
||||
int right);
|
||||
|
||||
static device_method_t tumbler_methods[] = {
|
||||
/* Device interface. */
|
||||
DEVMETHOD(device_probe, tumbler_probe),
|
||||
DEVMETHOD(device_attach, tumbler_attach),
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static driver_t tumbler_driver = {
|
||||
"tumbler",
|
||||
tumbler_methods,
|
||||
sizeof(struct tumbler_softc)
|
||||
};
|
||||
static devclass_t tumbler_devclass;
|
||||
|
||||
DRIVER_MODULE(tumbler, iicbus, tumbler_driver, tumbler_devclass, 0, 0);
|
||||
MODULE_VERSION(tumbler, 1);
|
||||
MODULE_DEPEND(tumbler, iicbus, 1, 1, 1);
|
||||
|
||||
static kobj_method_t tumbler_mixer_methods[] = {
|
||||
KOBJMETHOD(mixer_init, tumbler_init),
|
||||
KOBJMETHOD(mixer_uninit, tumbler_uninit),
|
||||
KOBJMETHOD(mixer_reinit, tumbler_reinit),
|
||||
KOBJMETHOD(mixer_set, tumbler_set),
|
||||
KOBJMETHOD(mixer_setrecsrc, tumbler_setrecsrc),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
MIXER_DECLARE(tumbler_mixer);
|
||||
|
||||
#define TUMBLER_IICADDR 0x68 /* Tumbler I2C slave address */
|
||||
|
||||
/* Tumbler (Texas Instruments TAS3001) registers. */
|
||||
#define TUMBLER_MCR 0x01 /* Main control register (1byte) */
|
||||
#define TUMBLER_DRC 0x02 /* Dynamic Range Compression (2bytes) */
|
||||
#define TUMBLER_VOLUME 0x04 /* Volume (6bytes) */
|
||||
#define TUMBLER_TREBLE 0x05 /* Treble control (1byte) */
|
||||
#define TUMBLER_BASS 0x06 /* Bass control (1byte) */
|
||||
#define TUMBLER_MIXER1 0x07 /* Mixer1 (3bytes) */
|
||||
#define TUMBLER_MIXER2 0x08 /* Mixer2 (3bytes) */
|
||||
#define TUMBLER_LB0 0x0a /* Left biquad 0 (15bytes) */
|
||||
#define TUMBLER_LB1 0x0b /* Left biquad 1 (15bytes) */
|
||||
#define TUMBLER_LB2 0x0c /* Left biquad 2 (15bytes) */
|
||||
#define TUMBLER_LB3 0x0d /* Left biquad 3 (15bytes) */
|
||||
#define TUMBLER_LB4 0x0e /* Left biquad 4 (15bytes) */
|
||||
#define TUMBLER_LB5 0x0f /* Left biquad 5 (15bytes) */
|
||||
#define TUMBLER_RB0 0x13 /* Right biquad 0 (15bytes) */
|
||||
#define TUMBLER_RB1 0x14 /* Right biquad 1 (15bytes) */
|
||||
#define TUMBLER_RB2 0x15 /* Right biquad 2 (15bytes) */
|
||||
#define TUMBLER_RB3 0x16 /* Right biquad 3 (15bytes) */
|
||||
#define TUMBLER_RB4 0x17 /* Right biquad 4 (15bytes) */
|
||||
#define TUMBLER_RB5 0x18 /* Right biquad 5 (15bytes) */
|
||||
#define TUMBLER_MCR_FL 0x80 /* Fast load */
|
||||
#define TUMBLER_MCR_SC 0x40 /* SCLK frequency */
|
||||
#define TUMBLER_MCR_SC_32 0x00 /* 32fs */
|
||||
#define TUMBLER_MCR_SC_64 0x40 /* 64fs */
|
||||
#define TUMBLER_MCR_SM 0x30 /* Output serial port mode */
|
||||
#define TUMBLER_MCR_SM_L 0x00 /* Left justified */
|
||||
#define TUMBLER_MCR_SM_R 0x10 /* Right justified */
|
||||
#define TUMBLER_MCR_SM_I2S 0x20 /* I2S */
|
||||
#define TUMBLER_MCR_ISM 0x0C /* Input serial mode */
|
||||
#define TUMBLER_MCR_ISM_L 0x00
|
||||
#define TUMBLER_MCR_ISM_R 0x04
|
||||
#define TUMBLER_MCR_ISM_I2S 0x08
|
||||
#define TUMBLER_MCR_W 0x03 /* Serial port word length */
|
||||
#define TUMBLER_MCR_W_16 0x00 /* 16 bit */
|
||||
#define TUMBLER_MCR_W_18 0x01 /* 18 bit */
|
||||
#define TUMBLER_MCR_W_20 0x02 /* 20 bit */
|
||||
#define TUMBLER_DRC_COMP_31 0xc0 /* 3:1 compression */
|
||||
#define TUMBLER_DRC_ENABLE 0x01 /* enable DRC */
|
||||
#define TUMBLER_DRC_DEFL_TH 0xa0 /* default compression threshold */
|
||||
|
||||
/*
|
||||
* Tumbler codec.
|
||||
*/
|
||||
|
||||
struct tumbler_reg {
|
||||
u_char MCR[1];
|
||||
u_char DRC[2];
|
||||
u_char VOLUME[6];
|
||||
u_char TREBLE[1];
|
||||
u_char BASS[1];
|
||||
u_char MIXER1[3];
|
||||
u_char MIXER2[3];
|
||||
u_char LB0[15];
|
||||
u_char LB1[15];
|
||||
u_char LB2[15];
|
||||
u_char LB3[15];
|
||||
u_char LB4[15];
|
||||
u_char LB5[15];
|
||||
u_char RB0[15];
|
||||
u_char RB1[15];
|
||||
u_char RB2[15];
|
||||
u_char RB3[15];
|
||||
u_char RB4[15];
|
||||
u_char RB5[15];
|
||||
};
|
||||
|
||||
const struct tumbler_reg tumbler_initdata = {
|
||||
{ TUMBLER_MCR_SC_64 | TUMBLER_MCR_SM_I2S |
|
||||
TUMBLER_MCR_ISM_I2S | TUMBLER_MCR_W_16 }, /* MCR */
|
||||
{ TUMBLER_DRC_COMP_31, TUMBLER_DRC_DEFL_TH }, /* DRC */
|
||||
{ 0, 0, 0, 0, 0, 0 }, /* VOLUME */
|
||||
{ 0x72 }, /* TREBLE */
|
||||
{ 0x3e }, /* BASS */
|
||||
{ 0x10, 0x00, 0x00 }, /* MIXER1 */
|
||||
{ 0x00, 0x00, 0x00 }, /* MIXER2 */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* BIQUAD */
|
||||
{ 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } /* BIQUAD */
|
||||
};
|
||||
|
||||
const char tumbler_regsize[] = {
|
||||
0, /* 0x00 */
|
||||
sizeof tumbler_initdata.MCR, /* 0x01 */
|
||||
sizeof tumbler_initdata.DRC, /* 0x02 */
|
||||
0, /* 0x03 */
|
||||
sizeof tumbler_initdata.VOLUME, /* 0x04 */
|
||||
sizeof tumbler_initdata.TREBLE, /* 0x05 */
|
||||
sizeof tumbler_initdata.BASS, /* 0x06 */
|
||||
sizeof tumbler_initdata.MIXER1, /* 0x07 */
|
||||
sizeof tumbler_initdata.MIXER2, /* 0x08 */
|
||||
0, /* 0x09 */
|
||||
sizeof tumbler_initdata.LB0, /* 0x0a */
|
||||
sizeof tumbler_initdata.LB1, /* 0x0b */
|
||||
sizeof tumbler_initdata.LB2, /* 0x0c */
|
||||
sizeof tumbler_initdata.LB3, /* 0x0d */
|
||||
sizeof tumbler_initdata.LB4, /* 0x0e */
|
||||
sizeof tumbler_initdata.LB5, /* 0x0f */
|
||||
0, /* 0x10 */
|
||||
0, /* 0x11 */
|
||||
0, /* 0x12 */
|
||||
sizeof tumbler_initdata.RB0, /* 0x13 */
|
||||
sizeof tumbler_initdata.RB1, /* 0x14 */
|
||||
sizeof tumbler_initdata.RB2, /* 0x15 */
|
||||
sizeof tumbler_initdata.RB3, /* 0x16 */
|
||||
sizeof tumbler_initdata.RB4, /* 0x17 */
|
||||
sizeof tumbler_initdata.RB5 /* 0x18 */
|
||||
};
|
||||
|
||||
/* dB = 20 * log (x) table. */
|
||||
static u_int tumbler_volume_table[100] = {
|
||||
0x00000148, 0x0000015C, 0x00000171, 0x00000186, // -46.0, -45.5, -45.0, -44.5,
|
||||
0x0000019E, 0x000001B6, 0x000001D0, 0x000001EB, // -44.0, -43.5, -43.0, -42.5,
|
||||
0x00000209, 0x00000227, 0x00000248, 0x0000026B, // -42.0, -41.5, -41.0, -40.5,
|
||||
0x0000028F, 0x000002B6, 0x000002DF, 0x0000030B, // -40.0, -39.5, -39.0, -38.5,
|
||||
0x00000339, 0x0000036A, 0x0000039E, 0x000003D5, // -38.0, -37.5, -37.0, -36.5,
|
||||
0x0000040F, 0x0000044C, 0x0000048D, 0x000004D2, // -36.0, -35.5, -35.0, -34.5,
|
||||
0x0000051C, 0x00000569, 0x000005BB, 0x00000612, // -34.0, -33.5, -33.0, -32.5,
|
||||
0x0000066E, 0x000006D0, 0x00000737, 0x000007A5, // -32.0, -31.5, -31.0, -30.5,
|
||||
0x00000818, 0x00000893, 0x00000915, 0x0000099F, // -30.0, -29.5, -29.0, -28.5,
|
||||
0x00000A31, 0x00000ACC, 0x00000B6F, 0x00000C1D, // -28.0, -27.5, -27.0, -26.5,
|
||||
0x00000CD5, 0x00000D97, 0x00000E65, 0x00000F40, // -26.0, -25.5, -25.0, -24.5,
|
||||
0x00001027, 0x0000111C, 0x00001220, 0x00001333, // -24.0, -23.5, -23.0, -22.5,
|
||||
0x00001456, 0x0000158A, 0x000016D1, 0x0000182B, // -22.0, -21.5, -21.0, -20.5,
|
||||
0x0000199A, 0x00001B1E, 0x00001CB9, 0x00001E6D, // -20.0, -19.5, -19.0, -18.5,
|
||||
0x0000203A, 0x00002223, 0x00002429, 0x0000264E, // -18.0, -17.5, -17.0, -16.5,
|
||||
0x00002893, 0x00002AFA, 0x00002D86, 0x00003039, // -16.0, -15.5, -15.0, -14.5,
|
||||
0x00003314, 0x0000361B, 0x00003950, 0x00003CB5, // -14.0, -13.5, -13.0, -12.5,
|
||||
0x0000404E, 0x0000441D, 0x00004827, 0x00004C6D, // -12.0, -11.5, -11.0, -10.5,
|
||||
0x000050F4, 0x000055C0, 0x00005AD5, 0x00006037, // -10.0, -9.5, -9.0, -8.5,
|
||||
0x000065EA, 0x00006BF4, 0x0000725A, 0x00007920, // -8.0, -7.5, -7.0, -6.5,
|
||||
0x0000804E, 0x000087EF, 0x00008FF6, 0x0000987D, // -6.0, -5.5, -5.0, -4.5,
|
||||
0x0000A186, 0x0000AB19, 0x0000B53C, 0x0000BFF9, // -4.0, -3.5, -3.0, -2.5,
|
||||
0x0000CB59, 0x0000D766, 0x0000E429, 0x0000F1AE, // -2.0, -1.5, -1.0, -0.5,
|
||||
0x00010000, 0x00010F2B, 0x00011F3D, 0x00013042, // 0.0, +0.5, +1.0, +1.5,
|
||||
0x00014249, 0x00015562, 0x0001699C, 0x00017F09 // 2.0, +2.5, +3.0, +3.5,
|
||||
};
|
||||
|
||||
static int
|
||||
tumbler_write(struct tumbler_softc *sc, uint8_t reg, const void *data)
|
||||
{
|
||||
u_int size;
|
||||
uint8_t buf[16];
|
||||
|
||||
struct iic_msg msg[] = {
|
||||
{ sc->sc_addr, IIC_M_WR, 0, buf }
|
||||
};
|
||||
|
||||
KASSERT(reg < sizeof(tumbler_regsize), ("bad reg"));
|
||||
size = tumbler_regsize[reg];
|
||||
msg[0].len = size + 1;
|
||||
buf[0] = reg;
|
||||
memcpy(&buf[1], data, size);
|
||||
|
||||
iicbus_transfer(sc->sc_dev, msg, 1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tumbler_probe(device_t dev)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
name = ofw_bus_get_name(dev);
|
||||
if (name == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
if (strcmp(name, "deq") == 0 && iicbus_get_addr(dev) ==
|
||||
TUMBLER_IICADDR) {
|
||||
device_set_desc(dev, "Texas Instruments TAS3001 Audio Codec");
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
static int
|
||||
tumbler_attach(device_t dev)
|
||||
{
|
||||
struct tumbler_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->sc_dev = dev;
|
||||
sc->sc_addr = iicbus_get_addr(dev);
|
||||
|
||||
i2s_mixer_class = &tumbler_mixer_class;
|
||||
i2s_mixer = dev;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tumbler_init(struct snd_mixer *m)
|
||||
{
|
||||
struct tumbler_softc *sc;
|
||||
u_int x = 0;
|
||||
|
||||
sc = device_get_softc(mix_getdevinfo(m));
|
||||
|
||||
tumbler_write(sc, TUMBLER_LB0, tumbler_initdata.LB0);
|
||||
tumbler_write(sc, TUMBLER_LB1, tumbler_initdata.LB1);
|
||||
tumbler_write(sc, TUMBLER_LB2, tumbler_initdata.LB2);
|
||||
tumbler_write(sc, TUMBLER_LB3, tumbler_initdata.LB3);
|
||||
tumbler_write(sc, TUMBLER_LB4, tumbler_initdata.LB4);
|
||||
tumbler_write(sc, TUMBLER_LB5, tumbler_initdata.LB5);
|
||||
tumbler_write(sc, TUMBLER_RB0, tumbler_initdata.RB0);
|
||||
tumbler_write(sc, TUMBLER_RB1, tumbler_initdata.RB1);
|
||||
tumbler_write(sc, TUMBLER_RB1, tumbler_initdata.RB1);
|
||||
tumbler_write(sc, TUMBLER_RB2, tumbler_initdata.RB2);
|
||||
tumbler_write(sc, TUMBLER_RB3, tumbler_initdata.RB3);
|
||||
tumbler_write(sc, TUMBLER_RB4, tumbler_initdata.RB4);
|
||||
tumbler_write(sc, TUMBLER_RB5, tumbler_initdata.RB5);
|
||||
tumbler_write(sc, TUMBLER_MCR, tumbler_initdata.MCR);
|
||||
tumbler_write(sc, TUMBLER_DRC, tumbler_initdata.DRC);
|
||||
tumbler_write(sc, TUMBLER_VOLUME, tumbler_initdata.VOLUME);
|
||||
tumbler_write(sc, TUMBLER_TREBLE, tumbler_initdata.TREBLE);
|
||||
tumbler_write(sc, TUMBLER_BASS, tumbler_initdata.BASS);
|
||||
tumbler_write(sc, TUMBLER_MIXER1, tumbler_initdata.MIXER1);
|
||||
tumbler_write(sc, TUMBLER_MIXER2, tumbler_initdata.MIXER2);
|
||||
|
||||
x |= SOUND_MASK_VOLUME;
|
||||
mix_setdevs(m, x);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
tumbler_uninit(struct snd_mixer *m)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
tumbler_reinit(struct snd_mixer *m)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tumbler_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
|
||||
{
|
||||
struct tumbler_softc *sc;
|
||||
|
||||
sc = device_get_softc(mix_getdevinfo(m));
|
||||
|
||||
switch (dev) {
|
||||
case SOUND_MIXER_VOLUME:
|
||||
return (tumbler_set_volume(sc, left, right));
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tumbler_setrecsrc(struct snd_mixer *m, u_int32_t src)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tumbler_set_volume(struct tumbler_softc *sc, int left, int right)
|
||||
{
|
||||
u_int l, r;
|
||||
u_char reg[6];
|
||||
|
||||
if (left > 100 || right > 100)
|
||||
return (0);
|
||||
|
||||
l = (left == 0 ? 0 : tumbler_volume_table[left - 1]);
|
||||
r = (right == 0 ? 0 : tumbler_volume_table[right - 1]);
|
||||
|
||||
reg[0] = (l & 0xff0000) >> 16;
|
||||
reg[1] = (l & 0x00ff00) >> 8;
|
||||
reg[2] = l & 0x0000ff;
|
||||
reg[3] = (r & 0xff0000) >> 16;
|
||||
reg[4] = (r & 0x00ff00) >> 8;
|
||||
reg[5] = r & 0x0000ff;
|
||||
tumbler_write(sc, TUMBLER_VOLUME, reg);
|
||||
|
||||
return (left | (right << 8));
|
||||
}
|
||||
|
@ -1,15 +1,22 @@
|
||||
# $FreeBSD$
|
||||
|
||||
SUBDIR= ad1816 als4000 atiixp cs4281 csa ds1 emu10k1 emu10kx
|
||||
SUBDIR+= envy24 envy24ht es137x ess fm801 hda ich maestro maestro3
|
||||
SUBDIR+= neomagic sb16 sb8 sbc solo spicds t4dwave via8233
|
||||
SUBDIR+= via82c686 vibes driver uaudio
|
||||
|
||||
.if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "amd64"
|
||||
SUBDIR+= cmi mss
|
||||
.endif
|
||||
|
||||
.if ${MACHINE_ARCH} == "sparc64"
|
||||
.if ${MACHINE} == "sparc64"
|
||||
audiocs= audiocs
|
||||
SUBDIR+= audiocs
|
||||
.endif
|
||||
SUBDIR= ${audiocs} es137x
|
||||
.else
|
||||
SUBDIR= ad1816 als4000 atiixp cmi cs4281 csa ds1 emu10k1 emu10kx
|
||||
SUBDIR+= envy24 envy24ht es137x ess fm801 hda ich maestro maestro3
|
||||
SUBDIR+= mss neomagic sb16 sb8 sbc solo spicds t4dwave via8233
|
||||
SUBDIR+= via82c686 vibes driver uaudio
|
||||
.endif
|
||||
|
||||
.if ${MACHINE_ARCH} == "powerpc"
|
||||
SUBDIR+= ai2s davbus
|
||||
.endif
|
||||
|
||||
.include <bsd.subdir.mk>
|
||||
|
10
sys/modules/sound/driver/ai2s/Makefile
Normal file
10
sys/modules/sound/driver/ai2s/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${.CURDIR}/../../../../dev/sound/macio
|
||||
|
||||
KMOD= snd_ai2s
|
||||
SRCS= device_if.h bus_if.h ofw_bus_if.h
|
||||
SRCS+= channel_if.h feeder_if.h mixer_if.h
|
||||
SRCS+= snapper.c tumbler.c aoa.c i2s.c
|
||||
|
||||
.include <bsd.kmod.mk>
|
10
sys/modules/sound/driver/davbus/Makefile
Normal file
10
sys/modules/sound/driver/davbus/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${.CURDIR}/../../../../dev/sound/macio
|
||||
|
||||
KMOD= snd_davbus
|
||||
SRCS= device_if.h bus_if.h ofw_bus_if.h
|
||||
SRCS+= channel_if.h feeder_if.h mixer_if.h
|
||||
SRCS+= aoa.c davbus.c
|
||||
|
||||
.include <bsd.kmod.mk>
|
@ -162,6 +162,10 @@ device adb
|
||||
device cuda
|
||||
device pmu
|
||||
|
||||
# Powermac I2C support
|
||||
device iicbus # I2C bus code
|
||||
device kiic # Keywest I2C
|
||||
|
||||
options KTR
|
||||
options KTR_COMPILE=0xffffffff
|
||||
#options KTR_MASK=KTR_SIG
|
||||
|
@ -30,6 +30,8 @@ device ofwd # Open Firmware disks
|
||||
device adb # Apple Desktop Bus
|
||||
device cuda # VIA-CUDA ADB interface
|
||||
device pmu # Apple Power Management Unit
|
||||
device snd_ai2s # Apple I2S Audio
|
||||
device snd_davbus # Apple Davbus Audio
|
||||
|
||||
|
||||
#####################################################################
|
||||
|
Loading…
x
Reference in New Issue
Block a user