Add support for the I2S and davbus audio controllers found in Apple PowerPC

hardware.

Submitted by:	Marco Trillo
This commit is contained in:
nwhitehorn 2009-01-25 18:20:15 +00:00
parent d8a0e03d76
commit 2cc6f5c8ec
16 changed files with 3174 additions and 8 deletions

View File

@ -2,7 +2,9 @@
MAN= bm.4 \
pmu.4 \
powermac_nvram.4
powermac_nvram.4 \
snd_ai2s.4 \
snd_davbus
MANSUBDIR=/powerpc

View 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 .

View 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 .

View File

@ -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
View 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
View 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 */

View 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);
}

View 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
View 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);
}

View 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);
}

View 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));
}

View File

@ -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>

View 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>

View 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>

View File

@ -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

View File

@ -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
#####################################################################