diff options
author | Dale Rahn <drahn@cvs.openbsd.org> | 2004-01-11 16:32:16 +0000 |
---|---|---|
committer | Dale Rahn <drahn@cvs.openbsd.org> | 2004-01-11 16:32:16 +0000 |
commit | f34e88b371d3c880a99019af33321d8e15121d41 (patch) | |
tree | 1b1cebf3430bfb0b96275e23e42ec046c65e272d /sys/arch/macppc | |
parent | b7e3701a995b5be8086b192557f42c47dfa09723 (diff) |
Audio support for some newer macppc machines, any 'snapper' based systems.
Driver ported from netbsd with bus_dma addition/vtophys deletion.
This driver does not work on the first generation 'Digital audio' systems
those were 'tumbler' nor the latest version. ok dhartmei@
Diffstat (limited to 'sys/arch/macppc')
-rw-r--r-- | sys/arch/macppc/dev/ki2c.c | 372 | ||||
-rw-r--r-- | sys/arch/macppc/dev/snapper.c | 1528 |
2 files changed, 1900 insertions, 0 deletions
diff --git a/sys/arch/macppc/dev/ki2c.c b/sys/arch/macppc/dev/ki2c.c new file mode 100644 index 00000000000..df4ef8625d5 --- /dev/null +++ b/sys/arch/macppc/dev/ki2c.c @@ -0,0 +1,372 @@ +/* $NetBSD: ki2c.c,v 1.1 2003/12/27 02:19:34 grant Exp $ */ +/* Id: ki2c.c,v 1.7 2002/10/05 09:56:05 tsubai Exp */ + +/*- + * Copyright (c) 2001 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. + */ + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/systm.h> + +#include <dev/ofw/openfirm.h> +#include <uvm/uvm_extern.h> +#include <machine/autoconf.h> + +/* Keywest I2C Register offsets */ +#define MODE 0 +#define CONTROL 1 +#define STATUS 2 +#define ISR 3 +#define IER 4 +#define ADDR 5 +#define SUBADDR 6 +#define DATA 7 + +/* MODE */ +#define I2C_SPEED 0x03 /* Speed mask */ +#define I2C_100kHz 0x00 +#define I2C_50kHz 0x01 +#define I2C_25kHz 0x02 +#define I2C_MODE 0x0c /* Mode mask */ +#define I2C_DUMBMODE 0x00 /* Dumb mode */ +#define I2C_STDMODE 0x04 /* Standard mode */ +#define I2C_STDSUBMODE 0x08 /* Standard mode + sub address */ +#define I2C_COMBMODE 0x0c /* Combined mode */ +#define I2C_PORT 0xf0 /* Port mask */ + +/* CONTROL */ +#define I2C_CT_AAK 0x01 /* Send AAK */ +#define I2C_CT_ADDR 0x02 /* Send address(es) */ +#define I2C_CT_STOP 0x04 /* Send STOP */ +#define I2C_CT_START 0x08 /* Send START */ + +/* STATUS */ +#define I2C_ST_BUSY 0x01 /* Busy */ +#define I2C_ST_LASTAAK 0x02 /* Last AAK */ +#define I2C_ST_LASTRW 0x04 /* Last R/W */ +#define I2C_ST_SDA 0x08 /* SDA */ +#define I2C_ST_SCL 0x10 /* SCL */ + +/* ISR/IER */ +#define I2C_INT_DATA 0x01 /* Data byte sent/received */ +#define I2C_INT_ADDR 0x02 /* Address sent */ +#define I2C_INT_STOP 0x04 /* STOP condition sent */ +#define I2C_INT_START 0x08 /* START condition sent */ + +/* I2C flags */ +#define I2C_BUSY 0x01 +#define I2C_READING 0x02 +#define I2C_ERROR 0x04 + +struct ki2c_softc { + struct device sc_dev; + paddr_t sc_paddr; + u_char *sc_reg; + int sc_regstep; + + int sc_flags; + u_char *sc_data; + int sc_resid; +}; + +int ki2c_match(struct device *, void *, void *); +void ki2c_attach(struct device *, struct device *, void *); +inline u_int ki2c_readreg(struct ki2c_softc *, int); +inline void ki2c_writereg(struct ki2c_softc *, int, u_int); +u_int ki2c_getmode(struct ki2c_softc *); +void ki2c_setmode(struct ki2c_softc *, u_int); +u_int ki2c_getspeed(struct ki2c_softc *); +void ki2c_setspeed(struct ki2c_softc *, u_int); +int ki2c_intr(struct ki2c_softc *); +int ki2c_poll(struct ki2c_softc *, int); +int ki2c_start(struct ki2c_softc *, int, int, void *, int); +int ki2c_read(struct ki2c_softc *, int, int, void *, int); +int ki2c_write(struct ki2c_softc *, int, int, const void *, int); + +struct cfattach ki2c_ca = { + sizeof(struct ki2c_softc), ki2c_match, ki2c_attach +}; +struct cfdriver ki2c_cd = { + NULL, "ki2c", DV_DULL +}; + +int +ki2c_match(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct confargs *ca = aux; + + if (strcmp(ca->ca_name, "i2c") == 0) + return 1; + + return 0; +} + +void +ki2c_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct ki2c_softc *sc = (struct ki2c_softc *)self; + struct confargs *ca = aux; + int node = ca->ca_node; + int rate; + + ca->ca_reg[0] += ca->ca_baseaddr; + + if (OF_getprop(node, "AAPL,i2c-rate", &rate, 4) != 4) { + printf(": cannot get i2c-rate\n"); + return; + } + if (OF_getprop(node, "AAPL,address", &sc->sc_paddr, 4) != 4) { + printf(": unable to find i2c address\n"); + return; + } + if (OF_getprop(node, "AAPL,address-step", &sc->sc_regstep, 4) != 4) { + printf(": unable to find i2c address step\n"); + return; + } + sc->sc_reg = mapiodev(sc->sc_paddr, (DATA+1)*sc->sc_regstep); + + printf("\n"); + + ki2c_writereg(sc, STATUS, 0); + ki2c_writereg(sc, ISR, 0); + ki2c_writereg(sc, IER, 0); + + ki2c_setmode(sc, I2C_STDSUBMODE); + ki2c_setspeed(sc, I2C_100kHz); /* XXX rate */ +} + +u_int +ki2c_readreg(sc, reg) + struct ki2c_softc *sc; + int reg; +{ + u_char *addr = sc->sc_reg + sc->sc_regstep * reg; + + return *addr; +} + +void +ki2c_writereg(sc, reg, val) + struct ki2c_softc *sc; + int reg; + u_int val; +{ + u_char *addr = sc->sc_reg + sc->sc_regstep * reg; + + *addr = val; + asm volatile ("eieio"); + delay(10); +} + +u_int +ki2c_getmode(sc) + struct ki2c_softc *sc; +{ + return ki2c_readreg(sc, MODE) & I2C_MODE; +} + +void +ki2c_setmode(sc, mode) + struct ki2c_softc *sc; + u_int mode; +{ + u_int x; + + KASSERT((mode & ~I2C_MODE) == 0); + x = ki2c_readreg(sc, MODE); + x &= ~I2C_MODE; + x |= mode; + ki2c_writereg(sc, MODE, x); +} + +u_int +ki2c_getspeed(sc) + struct ki2c_softc *sc; +{ + return ki2c_readreg(sc, MODE) & I2C_SPEED; +} + +void +ki2c_setspeed(sc, speed) + struct ki2c_softc *sc; + u_int speed; +{ + u_int x; + + KASSERT((speed & ~I2C_SPEED) == 0); + x = ki2c_readreg(sc, MODE); + x &= ~I2C_SPEED; + x |= speed; + ki2c_writereg(sc, MODE, x); +} + +int +ki2c_intr(sc) + struct ki2c_softc *sc; +{ + u_int isr, x; + + isr = ki2c_readreg(sc, ISR); + + if (isr & I2C_INT_ADDR) { +#if 0 + if ((ki2c_readreg(sc, STATUS) & I2C_ST_LASTAAK) == 0) { + /* No slave responded. */ + sc->sc_flags |= I2C_ERROR; + goto out; + } +#endif + + if (sc->sc_flags & I2C_READING) { + if (sc->sc_resid > 1) { + x = ki2c_readreg(sc, CONTROL); + x |= I2C_CT_AAK; + ki2c_writereg(sc, CONTROL, x); + } + } else { + ki2c_writereg(sc, DATA, *sc->sc_data++); + sc->sc_resid--; + } + } + + if (isr & I2C_INT_DATA) { + if (sc->sc_flags & I2C_READING) { + *sc->sc_data++ = ki2c_readreg(sc, DATA); + sc->sc_resid--; + + if (sc->sc_resid == 0) { /* Completed */ + ki2c_writereg(sc, CONTROL, 0); + goto out; + } + } else { +#if 0 + if ((ki2c_readreg(sc, STATUS) & I2C_ST_LASTAAK) == 0) { + /* No slave responded. */ + sc->sc_flags |= I2C_ERROR; + goto out; + } +#endif + + if (sc->sc_resid == 0) { + x = ki2c_readreg(sc, CONTROL) | I2C_CT_STOP; + ki2c_writereg(sc, CONTROL, x); + } else { + ki2c_writereg(sc, DATA, *sc->sc_data++); + sc->sc_resid--; + } + } + } + +out: + if (isr & I2C_INT_STOP) { + ki2c_writereg(sc, CONTROL, 0); + sc->sc_flags &= ~I2C_BUSY; + } + + ki2c_writereg(sc, ISR, isr); + + return 1; +} + +int +ki2c_poll(sc, timo) + struct ki2c_softc *sc; + int timo; +{ + while (sc->sc_flags & I2C_BUSY) { + if (ki2c_readreg(sc, ISR)) + ki2c_intr(sc); + timo -= 100; + if (timo < 0) { + printf("i2c_poll: timeout\n"); + return -1; + } + delay(100); + } + return 0; +} + +int +ki2c_start(sc, addr, subaddr, data, len) + struct ki2c_softc *sc; + int addr, subaddr, len; + void *data; +{ + int rw = (sc->sc_flags & I2C_READING) ? 1 : 0; + int timo, x; + + KASSERT((addr & 1) == 0); + + sc->sc_data = data; + sc->sc_resid = len; + sc->sc_flags |= I2C_BUSY; + + timo = 1000 + len * 200; + + /* XXX TAS3001 sometimes takes 50ms to finish writing registers. */ + /* if (addr == 0x68) */ + timo += 100000; + + ki2c_writereg(sc, ADDR, addr | rw); + ki2c_writereg(sc, SUBADDR, subaddr); + + x = ki2c_readreg(sc, CONTROL) | I2C_CT_ADDR; + ki2c_writereg(sc, CONTROL, x); + + if (ki2c_poll(sc, timo)) + return -1; + if (sc->sc_flags & I2C_ERROR) { + printf("I2C_ERROR\n"); + return -1; + } + return 0; +} + +int +ki2c_read(sc, addr, subaddr, data, len) + struct ki2c_softc *sc; + int addr, subaddr, len; + void *data; +{ + sc->sc_flags = I2C_READING; + return ki2c_start(sc, addr, subaddr, data, len); +} + +int +ki2c_write(sc, addr, subaddr, data, len) + struct ki2c_softc *sc; + int addr, subaddr, len; + const void *data; +{ + sc->sc_flags = 0; + return ki2c_start(sc, addr, subaddr, (void *)data, len); +} diff --git a/sys/arch/macppc/dev/snapper.c b/sys/arch/macppc/dev/snapper.c new file mode 100644 index 00000000000..ed014b02cda --- /dev/null +++ b/sys/arch/macppc/dev/snapper.c @@ -0,0 +1,1528 @@ +/* $NetBSD: snapper.c,v 1.1 2003/12/27 02:19:34 grant Exp $ */ +/* Id: snapper.c,v 1.11 2002/10/31 17:42:13 tsubai Exp */ + +/*- + * Copyright (c) 2002 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. + */ + +/* + * Datasheet is available from + * http://www.ti.com/sc/docs/products/analog/tas3004.html + */ + +#include <sys/param.h> +#include <sys/audioio.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/systm.h> + +#include <dev/auconv.h> +#include <dev/audio_if.h> +#include <dev/mulaw.h> +#include <dev/ofw/openfirm.h> +#include <macppc/dev/dbdma.h> + +#include <uvm/uvm_extern.h> + +#include <machine/autoconf.h> +#include <machine/pio.h> + +#ifdef SNAPPER_DEBUG +# define DPRINTF(x) printf x +#else +# define DPRINTF(x) +#endif + +#define SNAPPER_DMALIST_MAX 32 +#define SNAPPER_DMASEG_MAX NBPG + +struct snapper_dma { + bus_dmamap_t map; + caddr_t addr; + bus_dma_segment_t segs[SNAPPER_DMALIST_MAX]; + int nsegs; + size_t size; + struct snapper_dma *next; +}; + +struct snapper_softc { + struct device sc_dev; + int sc_flags; + int sc_node; + + void (*sc_ointr)(void *); /* dma completion intr handler */ + void *sc_oarg; /* arg for sc_ointr() */ + int sc_opages; /* # of output pages */ + + void (*sc_iintr)(void *); /* dma completion intr handler */ + void *sc_iarg; /* arg for sc_iintr() */ + + u_int sc_record_source; /* recording source mask */ + u_int sc_output_mask; /* output source mask */ + + u_char *sc_reg; + struct device *sc_i2c; + + u_int sc_vol_l; + u_int sc_vol_r; + + bus_dma_tag_t sc_dmat; + dbdma_regmap_t *sc_odma; + dbdma_regmap_t *sc_idma; + struct dbdma_command *sc_odmacmd, *sc_odmap; + struct dbdma_command *sc_idmacmd, *sc_idmap; + dbdma_t sc_odbdma, sc_idbdma; + + struct snapper_dma *sc_dmas; +}; + +int snapper_match(struct device *, void *, void *); +void snapper_attach(struct device *, struct device *, void *); +void snapper_defer(struct device *); +int snapper_intr(void *); +int snapper_open(void *, int); +void snapper_close(void *); +int snapper_query_encoding(void *, struct audio_encoding *); +int snapper_set_params(void *, int, int, struct audio_params *, + struct audio_params *); +int snapper_round_blocksize(void *, int); +int snapper_halt_output(void *); +int snapper_halt_input(void *); +int snapper_getdev(void *, struct audio_device *); +int snapper_set_port(void *, mixer_ctrl_t *); +int snapper_get_port(void *, mixer_ctrl_t *); +int snapper_query_devinfo(void *, mixer_devinfo_t *); +size_t snapper_round_buffersize(void *, int, size_t); +paddr_t snapper_mappage(void *, void *, off_t, int); +int snapper_get_props(void *); +int snapper_trigger_output(void *, void *, void *, int, void (*)(void *), + void *, struct audio_params *); +int snapper_trigger_input(void *, void *, void *, int, void (*)(void *), + void *, struct audio_params *); +void snapper_set_volume(struct snapper_softc *, int, int); +int snapper_set_rate(struct snapper_softc *, int); +void snapper_config(struct snapper_softc *sc, int node, struct device *parent); + +int tas3004_write(struct snapper_softc *, u_int, const void *); +static int gpio_read(char *); +static void gpio_write(char *, int); +void snapper_mute_speaker(struct snapper_softc *, int); +void snapper_mute_headphone(struct snapper_softc *, int); +int snapper_cint(void *); +int tas3004_init(struct snapper_softc *); +void snapper_init(struct snapper_softc *, int); +void *snapper_allocm(void *h, int dir, size_t size, int type, int flags); + +static void mono16_to_stereo16(void *, u_char *, int); +static void swap_bytes_mono16_to_stereo16(void *, u_char *, int); + +/* XXX */ +int ki2c_setmode(struct device *, int); +int ki2c_write(struct device *, int, int, const void *, int); +void ki2c_writereg(struct device *, int, u_int); + + +struct cfattach snapper_ca = { + sizeof(struct snapper_softc), snapper_match, snapper_attach +}; +struct cfdriver snapper_cd = { + NULL, "snapper", DV_DULL +}; + +struct audio_hw_if snapper_hw_if = { + snapper_open, + snapper_close, + NULL, + snapper_query_encoding, + snapper_set_params, + snapper_round_blocksize, + NULL, + NULL, + NULL, + NULL, + NULL, + snapper_halt_output, + snapper_halt_input, + NULL, + snapper_getdev, + NULL, + snapper_set_port, + snapper_get_port, + snapper_query_devinfo, + snapper_allocm, /* allocm */ + NULL, + snapper_round_buffersize, + snapper_mappage, + snapper_get_props, + snapper_trigger_output, + snapper_trigger_input, +}; + +struct audio_device snapper_device = { + "SNAPPER", + "", + "snapper" +}; + +static u_char *amp_mute; +static u_char *headphone_mute; +static u_char *audio_hw_reset; +static u_char *headphone_detect; +static int headphone_detect_active; + + +/* I2S registers */ +#define I2S_INT 0x00 +#define I2S_FORMAT 0x10 +#define I2S_FRAMECOUNT 0x40 +#define I2S_FRAMEMATCH 0x50 +#define I2S_WORDSIZE 0x60 + +/* TAS3004 registers */ +#define DEQ_MCR1 0x01 /* Main control register 1 (1byte) */ +#define DEQ_DRC 0x02 /* Dynamic range compression (6bytes?) */ +#define DEQ_VOLUME 0x04 /* Volume (6bytes) */ +#define DEQ_TREBLE 0x05 /* Treble control (1byte) */ +#define DEQ_BASS 0x06 /* Bass control (1byte) */ +#define DEQ_MIXER_L 0x07 /* Mixer left gain (9bytes) */ +#define DEQ_MIXER_R 0x08 /* Mixer right gain (9bytes) */ +#define DEQ_LB0 0x0a /* Left biquad 0 (15bytes) */ +#define DEQ_LB1 0x0b /* Left biquad 1 (15bytes) */ +#define DEQ_LB2 0x0c /* Left biquad 2 (15bytes) */ +#define DEQ_LB3 0x0d /* Left biquad 3 (15bytes) */ +#define DEQ_LB4 0x0e /* Left biquad 4 (15bytes) */ +#define DEQ_LB5 0x0f /* Left biquad 5 (15bytes) */ +#define DEQ_LB6 0x10 /* Left biquad 6 (15bytes) */ +#define DEQ_RB0 0x13 /* Right biquad 0 (15bytes) */ +#define DEQ_RB1 0x14 /* Right biquad 1 (15bytes) */ +#define DEQ_RB2 0x15 /* Right biquad 2 (15bytes) */ +#define DEQ_RB3 0x16 /* Right biquad 3 (15bytes) */ +#define DEQ_RB4 0x17 /* Right biquad 4 (15bytes) */ +#define DEQ_RB5 0x18 /* Right biquad 5 (15bytes) */ +#define DEQ_RB6 0x19 /* Right biquad 6 (15bytes) */ +#define DEQ_LLB 0x21 /* Left loudness biquad (15bytes) */ +#define DEQ_RLB 0x22 /* Right loudness biquad (15bytes) */ +#define DEQ_LLB_GAIN 0x23 /* Left loudness biquad gain (3bytes) */ +#define DEQ_RLB_GAIN 0x24 /* Right loudness biquad gain (3bytes) */ +#define DEQ_ACR 0x40 /* Analog control register (1byte) */ +#define DEQ_MCR2 0x43 /* Main control register 2 (1byte) */ + +#define DEQ_MCR1_FL 0x80 /* Fast load */ +#define DEQ_MCR1_SC 0x40 /* SCLK frequency */ +#define DEQ_MCR1_SC_32 0x00 /* 32fs */ +#define DEQ_MCR1_SC_64 0x40 /* 64fs */ +#define DEQ_MCR1_SM 0x30 /* Output serial port mode */ +#define DEQ_MCR1_SM_L 0x00 /* Left justified */ +#define DEQ_MCR1_SM_R 0x10 /* Right justified */ +#define DEQ_MCR1_SM_I2S 0x20 /* I2S */ +#define DEQ_MCR1_W 0x03 /* Serial port word length */ +#define DEQ_MCR1_W_16 0x00 /* 16 bit */ +#define DEQ_MCR1_W_18 0x01 /* 18 bit */ +#define DEQ_MCR1_W_20 0x02 /* 20 bit */ + +#define DEQ_MCR2_DL 0x80 /* Download */ +#define DEQ_MCR2_AP 0x02 /* All pass mode */ + +#define DEQ_ACR_ADM 0x80 /* ADC output mode */ +#define DEQ_ACR_LRB 0x40 /* Select B input */ +#define DEQ_ACR_DM 0x0c /* De-emphasis control */ +#define DEQ_ACR_DM_OFF 0x00 /* off */ +#define DEQ_ACR_DM_48 0x04 /* fs = 48kHz */ +#define DEQ_ACR_DM_44 0x08 /* fs = 44.1kHz */ +#define DEQ_ACR_INP 0x02 /* Analog input select */ +#define DEQ_ACR_INP_A 0x00 /* A */ +#define DEQ_ACR_INP_B 0x02 /* B */ +#define DEQ_ACR_APD 0x01 /* Analog power down */ + +struct tas3004_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]; +}; + +#define GPIO_OUTSEL 0xf0 /* Output select */ + /* 0x00 GPIO bit0 is output + 0x10 media-bay power + 0x20 reserved + 0x30 MPIC */ + +#define GPIO_ALTOE 0x08 /* Alternate output enable */ + /* 0x00 Use DDR + 0x08 Use output select */ + +#define GPIO_DDR 0x04 /* Data direction */ +#define GPIO_DDR_OUTPUT 0x04 /* Output */ +#define GPIO_DDR_INPUT 0x00 /* Input */ + +#define GPIO_LEVEL 0x02 /* Pin level (RO) */ + +#define GPIO_DATA 0x01 /* Data */ + +int +snapper_match(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct confargs *ca = aux; + int soundbus, soundchip; + char compat[32]; + + if (strcmp(ca->ca_name, "i2s") != 0) + return 0; + + if ((soundbus = OF_child(ca->ca_node)) == 0 || + (soundchip = OF_child(soundbus)) == 0) + return 0; + + bzero(compat, sizeof compat); + OF_getprop(soundchip, "compatible", compat, sizeof compat); + + if (strcmp(compat, "snapper") != 0) + return 0; + + return 1; +} + +void +snapper_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct snapper_softc *sc = (struct snapper_softc *)self; + struct confargs *ca = aux; + int cirq, oirq, iirq, cirq_type, oirq_type, iirq_type; + int soundbus, intr[6]; + + ca->ca_reg[0] += ca->ca_baseaddr; + ca->ca_reg[2] += ca->ca_baseaddr; + ca->ca_reg[4] += ca->ca_baseaddr; + + sc->sc_reg = mapiodev(ca->ca_reg[0], ca->ca_reg[1]); + + sc->sc_node = ca->ca_node; + + sc->sc_dmat = ca->ca_dmat; + sc->sc_odma = mapiodev(ca->ca_reg[2], ca->ca_reg[3]); /* out */ + sc->sc_idma = mapiodev(ca->ca_reg[4], ca->ca_reg[5]); /* in */ + sc->sc_odbdma = dbdma_alloc(sc->sc_dmat, SNAPPER_DMALIST_MAX); + sc->sc_odmacmd = sc->sc_odbdma->d_addr; + sc->sc_idbdma = dbdma_alloc(sc->sc_dmat, SNAPPER_DMALIST_MAX); + sc->sc_idmacmd = sc->sc_idbdma->d_addr; + + soundbus = OF_child(ca->ca_node); + OF_getprop(soundbus, "interrupts", intr, sizeof intr); + cirq = intr[0]; + oirq = intr[2]; + iirq = intr[4]; + cirq_type = intr[1] ? IST_LEVEL : IST_EDGE; + oirq_type = intr[3] ? IST_LEVEL : IST_EDGE; + iirq_type = intr[5] ? IST_LEVEL : IST_EDGE; + + /* intr_establish(cirq, cirq_type, IPL_AUDIO, snapper_intr, sc); */ + mac_intr_establish(parent, oirq, oirq_type, IPL_AUDIO, snapper_intr, + sc, "snapper"); + /* intr_establish(iirq, iirq_type, IPL_AUDIO, snapper_intr, sc); */ + + printf(": irq %d,%d,%d\n", sc->sc_dev.dv_xname, cirq, oirq, iirq); + + snapper_config(sc, sc->sc_node, parent); +#if 0 + config_interrupts(self, snapper_defer); +#else + config_defer(self, snapper_defer); +#endif +} + +void +snapper_defer(struct device *dev) +{ + struct snapper_softc *sc = (struct snapper_softc *)dev; + struct device *dv; + + for (dv = alldevs.tqh_first; dv; dv=dv->dv_list.tqe_next) + if (strncmp(dv->dv_xname, "ki2c", 4) == 0 && + strncmp(dv->dv_parent->dv_xname, "macobio", 4) == 0) + sc->sc_i2c = dv; + if (sc->sc_i2c == NULL) { + printf("%s: unable to find i2c\n", sc->sc_dev.dv_xname); + return; + } + + /* XXX If i2c was failed to attach, what should we do? */ + + audio_attach_mi(&snapper_hw_if, sc, &sc->sc_dev); + + /* ki2c_setmode(sc->sc_i2c, I2C_STDSUBMODE); */ + snapper_init(sc, sc->sc_node); +} + +int +snapper_intr(v) + void *v; +{ + struct snapper_softc *sc = v; + struct dbdma_command *cmd = sc->sc_odmap; +#ifndef __OpenBSD__ + int count = sc->sc_opages; + int status; +#else + u_int16_t c, status; +#endif + + /* if not set we are not running */ + if (!cmd) + return (0); + DPRINTF(("snapper_intr: cmd %x\n", cmd)); + +#ifndef __OpenBSD__ + /* Fill used buffer(s). */ + while (count-- > 0) { + if ((dbdma_ld16(&cmd->d_command) & 0x30) == 0x30) { + status = dbdma_ld16(&cmd->d_status); + cmd->d_status = 0; + if (status) /* status == 0x8400 */ + if (sc->sc_ointr) + (*sc->sc_ointr)(sc->sc_oarg); + } + cmd++; + } +#else + c = in16rb(&cmd->d_command); + status = in16rb(&cmd->d_status); + + if (c >> 12 == DBDMA_CMD_OUT_LAST) + sc->sc_odmap = sc->sc_odmacmd; + else + sc->sc_odmap++; + + if (c & (DBDMA_INT_ALWAYS << 4)) { + cmd->d_status = 0; + if (status) /* status == 0x8400 */ + if (sc->sc_ointr) + (*sc->sc_ointr)(sc->sc_oarg); + } +#endif + + return 1; +} + +int +snapper_open(h, flags) + void *h; + int flags; +{ + return 0; +} + +/* + * Close function is called at splaudio(). + */ +void +snapper_close(h) + void *h; +{ + struct snapper_softc *sc = h; + + snapper_halt_output(sc); + snapper_halt_input(sc); + + sc->sc_ointr = 0; + sc->sc_iintr = 0; +} + +int +snapper_query_encoding(h, ae) + void *h; + struct audio_encoding *ae; +{ + ae->flags = AUDIO_ENCODINGFLAG_EMULATED; + + switch (ae->index) { + case 0: + strlcpy(ae->name, AudioEslinear, sizeof(ae->name)); + ae->encoding = AUDIO_ENCODING_SLINEAR; + ae->precision = 16; + ae->flags = 0; + return 0; + case 1: + strlcpy(ae->name, AudioEslinear_be, sizeof(ae->name)); + ae->encoding = AUDIO_ENCODING_SLINEAR_BE; + ae->precision = 16; + ae->flags = 0; + return 0; + case 2: + strlcpy(ae->name, AudioEslinear_le, sizeof(ae->name)); + ae->encoding = AUDIO_ENCODING_SLINEAR_LE; + ae->precision = 16; + return 0; + case 3: + strlcpy(ae->name, AudioEslinear_be, sizeof(ae->name)); + ae->encoding = AUDIO_ENCODING_ULINEAR_BE; + ae->precision = 16; + return 0; + case 4: + strlcpy(ae->name, AudioEslinear_le, sizeof(ae->name)); + ae->encoding = AUDIO_ENCODING_ULINEAR_LE; + ae->precision = 16; + return 0; + case 5: + strlcpy(ae->name, AudioEmulaw, sizeof(ae->name)); + ae->encoding = AUDIO_ENCODING_ULAW; + ae->precision = 8; + return 0; + case 6: + strlcpy(ae->name, AudioEmulaw, sizeof(ae->name)); + ae->encoding = AUDIO_ENCODING_ALAW; + ae->precision = 8; + return 0; + default: + return EINVAL; + } +} + +static void +mono16_to_stereo16(v, p, cc) + void *v; + u_char *p; + int cc; +{ + int x; + int16_t *src, *dst; + + src = (void *)(p + cc); + dst = (void *)(p + cc * 2); + while (cc > 0) { + x = *--src; + *--dst = x; + *--dst = x; + cc -= 2; + } +} + +static void +swap_bytes_mono16_to_stereo16(v, p, cc) + void *v; + u_char *p; + int cc; +{ + swap_bytes(v, p, cc); + mono16_to_stereo16(v, p, cc); +} + +int +snapper_set_params(h, setmode, usemode, play, rec) + void *h; + int setmode, usemode; + struct audio_params *play, *rec; +{ + struct snapper_softc *sc = h; + struct audio_params *p; + int mode, rate; + + p = NULL; + + /* + * This device only has one clock, so make the sample rates match. + */ + if (play->sample_rate != rec->sample_rate && + usemode == (AUMODE_PLAY | AUMODE_RECORD)) { + if (setmode == AUMODE_PLAY) { + rec->sample_rate = play->sample_rate; + setmode |= AUMODE_RECORD; + } else if (setmode == AUMODE_RECORD) { + play->sample_rate = rec->sample_rate; + setmode |= AUMODE_PLAY; + } else + return EINVAL; + } + + for (mode = AUMODE_RECORD; mode != -1; + mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) { + if ((setmode & mode) == 0) + continue; + + p = mode == AUMODE_PLAY ? play : rec; + + if (p->sample_rate < 4000 || p->sample_rate > 50000 || + (p->precision != 8 && p->precision != 16) || + (p->channels != 1 && p->channels != 2)) + return EINVAL; + + p->factor = 1; + p->sw_code = 0; + + switch (p->encoding) { + + case AUDIO_ENCODING_SLINEAR_LE: + if (p->channels == 2 && p->precision == 16) { + p->sw_code = swap_bytes; + break; + } + if (p->channels == 1 && p->precision == 16) { + p->factor = 2; + p->sw_code = swap_bytes_mono16_to_stereo16; + break; + } + return EINVAL; + case AUDIO_ENCODING_SLINEAR_BE: + if (p->channels == 1 && p->precision == 16) { + p->factor = 2; + p->sw_code = mono16_to_stereo16; + break; + } + if (p->channels == 2 && p->precision == 16) + break; + + return EINVAL; + + case AUDIO_ENCODING_ULINEAR_LE: + if (p->channels == 2 && p->precision == 16) { + p->sw_code = swap_bytes_change_sign16_be; + break; + } + return EINVAL; + + case AUDIO_ENCODING_ULINEAR_BE: + if (p->channels == 2 && p->precision == 16) { + p->sw_code = change_sign16_be; + break; + } + return EINVAL; + + case AUDIO_ENCODING_ULAW: + if (mode == AUMODE_PLAY) { + p->factor = 2; + p->sw_code = mulaw_to_slinear16_be; + break; + } else + break; /* XXX */ + + return EINVAL; + + case AUDIO_ENCODING_ALAW: + if (mode == AUMODE_PLAY) { + p->factor = 2; + p->sw_code = alaw_to_slinear16_be; + break; + } + return EINVAL; + + default: + return EINVAL; + } + } + + /* Set the speed */ + rate = p->sample_rate; + + if (snapper_set_rate(sc, rate)) + return EINVAL; + + return 0; +} + +int +snapper_round_blocksize(h, size) + void *h; + int size; +{ + if (size < NBPG) + size = NBPG; + return size & ~PGOFSET; +} + +int +snapper_halt_output(h) + void *h; +{ + struct snapper_softc *sc = h; + + dbdma_stop(sc->sc_odma); + dbdma_reset(sc->sc_odma); + return 0; +} + +int +snapper_halt_input(h) + void *h; +{ + struct snapper_softc *sc = h; + + dbdma_stop(sc->sc_idma); + dbdma_reset(sc->sc_idma); + return 0; +} + +int +snapper_getdev(h, retp) + void *h; + struct audio_device *retp; +{ + *retp = snapper_device; + return 0; +} + +enum { + SNAPPER_MONITOR_CLASS, + SNAPPER_OUTPUT_CLASS, + SNAPPER_RECORD_CLASS, + SNAPPER_OUTPUT_SELECT, + SNAPPER_VOL_OUTPUT, + SNAPPER_INPUT_SELECT, + SNAPPER_VOL_INPUT, + SNAPPER_ENUM_LAST +}; + +int +snapper_set_port(h, mc) + void *h; + mixer_ctrl_t *mc; +{ + struct snapper_softc *sc = h; + int l, r; + + DPRINTF(("snapper_set_port dev = %d, type = %d\n", mc->dev, mc->type)); + + l = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; + r = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; + + switch (mc->dev) { + case SNAPPER_OUTPUT_SELECT: + /* No change necessary? */ + if (mc->un.mask == sc->sc_output_mask) + return 0; + + snapper_mute_speaker(sc, 1); + snapper_mute_headphone(sc, 1); + if (mc->un.mask & 1 << 0) + snapper_mute_speaker(sc, 0); + if (mc->un.mask & 1 << 1) + snapper_mute_headphone(sc, 0); + + sc->sc_output_mask = mc->un.mask; + return 0; + + case SNAPPER_VOL_OUTPUT: + snapper_set_volume(sc, l, r); + return 0; + + case SNAPPER_INPUT_SELECT: + /* no change necessary? */ + if (mc->un.mask == sc->sc_record_source) + return 0; + switch (mc->un.mask) { + case 1 << 0: /* CD */ + case 1 << 1: /* microphone */ + case 1 << 2: /* line in */ + /* XXX TO BE DONE */ + break; + default: /* invalid argument */ + return EINVAL; + } + sc->sc_record_source = mc->un.mask; + return 0; + + case SNAPPER_VOL_INPUT: + /* XXX TO BE DONE */ + return 0; + } + + return ENXIO; +} + +int +snapper_get_port(h, mc) + void *h; + mixer_ctrl_t *mc; +{ + struct snapper_softc *sc = h; + + DPRINTF(("snapper_get_port dev = %d, type = %d\n", mc->dev, mc->type)); + + switch (mc->dev) { + case SNAPPER_OUTPUT_SELECT: + mc->un.mask = sc->sc_output_mask; + return 0; + + case SNAPPER_VOL_OUTPUT: + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->sc_vol_l; + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = sc->sc_vol_r; + return 0; + + case SNAPPER_INPUT_SELECT: + mc->un.mask = sc->sc_record_source; + return 0; + + case SNAPPER_VOL_INPUT: + /* XXX TO BE DONE */ + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 0; + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 0; + return 0; + + default: + return ENXIO; + } + + return 0; +} + +int +snapper_query_devinfo(h, dip) + void *h; + mixer_devinfo_t *dip; +{ + switch (dip->index) { + + case SNAPPER_OUTPUT_SELECT: + dip->mixer_class = SNAPPER_MONITOR_CLASS; + strlcpy(dip->label.name, AudioNoutput, sizeof(dip->label.name)); + dip->type = AUDIO_MIXER_SET; + dip->prev = dip->next = AUDIO_MIXER_LAST; + dip->un.s.num_mem = 2; + strlcpy(dip->un.s.member[0].label.name, AudioNspeaker, + strlen(dip->un.s.member[0].label.name)); + dip->un.s.member[0].mask = 1 << 0; + strlcpy(dip->un.s.member[1].label.name, AudioNheadphone, + strlen(dip->un.s.member[1].label.name)); + dip->un.s.member[1].mask = 1 << 1; + return 0; + + case SNAPPER_VOL_OUTPUT: + dip->mixer_class = SNAPPER_MONITOR_CLASS; + strlcpy(dip->label.name, AudioNmaster, sizeof(dip->label.name)); + dip->type = AUDIO_MIXER_VALUE; + dip->prev = dip->next = AUDIO_MIXER_LAST; + dip->un.v.num_channels = 2; + strlcpy(dip->un.v.units.name, AudioNvolume, + strlen(dip->un.v.units.name)); + return 0; + + case SNAPPER_INPUT_SELECT: + dip->mixer_class = SNAPPER_RECORD_CLASS; + strlcpy(dip->label.name, AudioNsource, sizeof(dip->label.name)); + dip->type = AUDIO_MIXER_SET; + dip->prev = dip->next = AUDIO_MIXER_LAST; + dip->un.s.num_mem = 3; + strlcpy(dip->un.s.member[0].label.name, AudioNcd, + sizeof(dip->un.s.member[0].label.name)); + dip->un.s.member[0].mask = 1 << 0; + strlcpy(dip->un.s.member[1].label.name, AudioNmicrophone, + sizeof(dip->un.s.member[1].label.name)); + dip->un.s.member[1].mask = 1 << 1; + strlcpy(dip->un.s.member[2].label.name, AudioNline, + sizeof(dip->un.s.member[2].label.name)); + dip->un.s.member[2].mask = 1 << 2; + return 0; + + case SNAPPER_VOL_INPUT: + dip->mixer_class = SNAPPER_RECORD_CLASS; + strlcpy(dip->label.name, AudioNrecord, sizeof(dip->label.name)); + dip->type = AUDIO_MIXER_VALUE; + dip->prev = dip->next = AUDIO_MIXER_LAST; + dip->un.v.num_channels = 2; + strlcpy(dip->un.v.units.name, AudioNvolume, + sizeof(dip->un.v.units.name)); + return 0; + + case SNAPPER_MONITOR_CLASS: + dip->mixer_class = SNAPPER_MONITOR_CLASS; + strlcpy(dip->label.name, AudioCmonitor, sizeof(dip->label.name)); + dip->type = AUDIO_MIXER_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + return 0; + + case SNAPPER_OUTPUT_CLASS: + dip->mixer_class = SNAPPER_OUTPUT_CLASS; + strlcpy(dip->label.name, AudioCoutputs, + sizeof(dip->label.name)); + dip->type = AUDIO_MIXER_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + return 0; + + case SNAPPER_RECORD_CLASS: + dip->mixer_class = SNAPPER_RECORD_CLASS; + strlcpy(dip->label.name, AudioCrecord, sizeof(dip->label.name)); + dip->type = AUDIO_MIXER_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + return 0; + } + + return ENXIO; +} + +size_t +snapper_round_buffersize(h, dir, size) + void *h; + int dir; + size_t size; +{ + if (size > 65536) + size = 65536; + return size; +} + +paddr_t +snapper_mappage(h, mem, off, prot) + void *h; + void *mem; + off_t off; + int prot; +{ + if (off < 0) + return -1; + return -1; /* XXX */ +} + +int +snapper_get_props(h) + void *h; +{ + return AUDIO_PROP_FULLDUPLEX /* | AUDIO_PROP_MMAP */; +} + +int +snapper_trigger_output(h, start, end, bsize, intr, arg, param) + void *h; + void *start, *end; + int bsize; + void (*intr)(void *); + void *arg; + struct audio_params *param; +{ + struct snapper_softc *sc = h; + struct snapper_dma *p; + struct dbdma_command *cmd = sc->sc_odmacmd; + vaddr_t spa, pa, epa; + int c; + + DPRINTF(("trigger_output %p %p 0x%x\n", start, end, bsize)); + + for (p = sc->sc_dmas; p && p->addr != start; p = p->next); + if (!p) + return -1; + + sc->sc_ointr = intr; + sc->sc_oarg = arg; + sc->sc_odmap = sc->sc_odmacmd; + + spa = p->segs[0].ds_addr; + c = DBDMA_CMD_OUT_MORE; + for (pa = spa, epa = spa + (end - start); + pa < epa; pa += bsize, cmd++) { + + if (pa + bsize == epa) + c = DBDMA_CMD_OUT_LAST; + + DBDMA_BUILD(cmd, c, 0, bsize, pa, DBDMA_INT_ALWAYS, + DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + } + + DBDMA_BUILD(cmd, DBDMA_CMD_NOP, 0, 0, 0, + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_ALWAYS); + dbdma_st32(&cmd->d_cmddep, sc->sc_odbdma->d_paddr); + + dbdma_start(sc->sc_odma, sc->sc_odbdma); + + return 0; +} + +int +snapper_trigger_input(h, start, end, bsize, intr, arg, param) + void *h; + void *start, *end; + int bsize; + void (*intr)(void *); + void *arg; + struct audio_params *param; +{ + printf("snapper_trigger_input called\n"); + + return 1; +} + +void +snapper_set_volume(sc, left, right) + struct snapper_softc *sc; + int left, right; +{ + u_char vol[6]; + + sc->sc_vol_l = left; + sc->sc_vol_r = right; + + left <<= 8; /* XXX for now */ + right <<= 8; + + vol[0] = left >> 16; + vol[1] = left >> 8; + vol[2] = left; + vol[3] = right >> 16; + vol[4] = right >> 8; + vol[5] = right; + + tas3004_write(sc, DEQ_VOLUME, vol); +} + +#define CLKSRC_49MHz 0x80000000 /* Use 49152000Hz Osc. */ +#define CLKSRC_45MHz 0x40000000 /* Use 45158400Hz Osc. */ +#define CLKSRC_18MHz 0x00000000 /* Use 18432000Hz Osc. */ +#define MCLK_DIV 0x1f000000 /* MCLK = SRC / DIV */ +#define MCLK_DIV1 0x14000000 /* MCLK = SRC */ +#define MCLK_DIV3 0x13000000 /* MCLK = SRC / 3 */ +#define MCLK_DIV5 0x12000000 /* MCLK = SRC / 5 */ +#define SCLK_DIV 0x00f00000 /* SCLK = MCLK / DIV */ +#define SCLK_DIV1 0x00800000 +#define SCLK_DIV3 0x00900000 +#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 + +// rate = fs = LRCLK +// SCLK = 64*LRCLK (I2S) +// MCLK = 256fs (typ. -- changeable) + +// MCLK = clksrc / mdiv +// SCLK = MCLK / sdiv +// rate = SCLK / 64 ( = LRCLK = fs) + +int +snapper_set_rate(sc, rate) + struct snapper_softc *sc; + int rate; +{ + u_int reg = 0; + int MCLK; + int clksrc, mdiv, sdiv; + int mclk_fs; + + switch (rate) { + case 8000: + clksrc = 18432000; /* 18MHz */ + reg = CLKSRC_18MHz; + mclk_fs = 256; + break; + + case 44100: + clksrc = 45158400; /* 45MHz */ + reg = CLKSRC_45MHz; + mclk_fs = 256; + break; + + case 48000: + clksrc = 49152000; /* 49MHz */ + reg = CLKSRC_49MHz; + mclk_fs = 256; + break; + + default: + return EINVAL; + } + + MCLK = rate * mclk_fs; + mdiv = clksrc / MCLK; // 4 + sdiv = mclk_fs / 64; // 4 + + switch (mdiv) { + case 1: + reg |= MCLK_DIV1; + break; + case 3: + reg |= MCLK_DIV3; + break; + case 5: + reg |= MCLK_DIV5; + break; + default: + reg |= ((mdiv / 2 - 1) << 24) & 0x1f000000; + break; + } + + switch (sdiv) { + case 1: + reg |= SCLK_DIV1; + break; + case 3: + reg |= SCLK_DIV3; + break; + default: + reg |= ((sdiv / 2 - 1) << 20) & 0x00f00000; + break; + } + + reg |= SCLK_MASTER; /* XXX master mode */ + + reg |= SERIAL_64x; + + /* stereo input and output */ + DPRINTF(("I2SSetDataWordSizeReg 0x%08x -> 0x%08x\n", + in32rb(sc->sc_reg + I2S_WORDSIZE), 0x02000200)); + out32rb(sc->sc_reg + I2S_WORDSIZE, 0x02000200); + + DPRINTF(("I2SSetSerialFormatReg 0x%x -> 0x%x\n", + in32rb(sc->sc_reg + I2S_FORMAT), reg)); + out32rb(sc->sc_reg + I2S_FORMAT, reg); + + return 0; +} + +#define DEQaddr 0x6a + +const struct tas3004_reg tas3004_initdata = { + { DEQ_MCR1_SC_64 | DEQ_MCR1_SM_I2S | DEQ_MCR1_W_20 }, /* 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 */ + { 0 }, /* ACR */ + { 0 } /* MCR2 */ +}; + +const char tas3004_regsize[] = { + 0, /* 0x00 */ + sizeof tas3004_initdata.MCR1, /* 0x01 */ + sizeof tas3004_initdata.DRC, /* 0x02 */ + 0, /* 0x03 */ + sizeof tas3004_initdata.VOLUME, /* 0x04 */ + sizeof tas3004_initdata.TREBLE, /* 0x05 */ + sizeof tas3004_initdata.BASS, /* 0x06 */ + sizeof tas3004_initdata.MIXER_L, /* 0x07 */ + sizeof tas3004_initdata.MIXER_R, /* 0x08 */ + 0, /* 0x09 */ + sizeof tas3004_initdata.LB0, /* 0x0a */ + sizeof tas3004_initdata.LB1, /* 0x0b */ + sizeof tas3004_initdata.LB2, /* 0x0c */ + sizeof tas3004_initdata.LB3, /* 0x0d */ + sizeof tas3004_initdata.LB4, /* 0x0e */ + sizeof tas3004_initdata.LB5, /* 0x0f */ + sizeof tas3004_initdata.LB6, /* 0x10 */ + 0, /* 0x11 */ + 0, /* 0x12 */ + sizeof tas3004_initdata.RB0, /* 0x13 */ + sizeof tas3004_initdata.RB1, /* 0x14 */ + sizeof tas3004_initdata.RB2, /* 0x15 */ + sizeof tas3004_initdata.RB3, /* 0x16 */ + sizeof tas3004_initdata.RB4, /* 0x17 */ + sizeof tas3004_initdata.RB5, /* 0x18 */ + sizeof tas3004_initdata.RB6, /* 0x19 */ + 0,0,0,0, 0,0, + 0, /* 0x20 */ + sizeof tas3004_initdata.LLB, /* 0x21 */ + sizeof tas3004_initdata.RLB, /* 0x22 */ + sizeof tas3004_initdata.LLB_GAIN, /* 0x23 */ + sizeof tas3004_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 tas3004_initdata.ACR, /* 0x40 */ + 0, /* 0x41 */ + 0, /* 0x42 */ + sizeof tas3004_initdata.MCR2 /* 0x43 */ +}; + +int +tas3004_write(sc, reg, data) + struct snapper_softc *sc; + u_int reg; + const void *data; +{ + int size; + + KASSERT(reg < sizeof tas3004_regsize); + size = tas3004_regsize[reg]; + KASSERT(size > 0); + + if (ki2c_write(sc->sc_i2c, DEQaddr, reg, data, size)) + return -1; + + return 0; +} + +int +gpio_read(addr) + char *addr; +{ + if (*addr & GPIO_DATA) + return 1; + return 0; +} + +void +gpio_write(addr, val) + char *addr; + int val; +{ + u_int data = GPIO_DDR_OUTPUT; + + if (val) + data |= GPIO_DATA; + *addr = data; + asm volatile ("eieio"); +} + +#define headphone_active 0 /* XXX OF */ +#define amp_active 0 /* XXX OF */ + +void +snapper_mute_speaker(sc, mute) + struct snapper_softc *sc; + int mute; +{ + u_int x; + + DPRINTF(("ampmute %d --> ", gpio_read(amp_mute))); + + if (mute) + x = amp_active; /* mute */ + else + x = !amp_active; /* unmute */ + if (x != gpio_read(amp_mute)) + gpio_write(amp_mute, x); + + DPRINTF(("%d\n", gpio_read(amp_mute))); +} + +void +snapper_mute_headphone(sc, mute) + struct snapper_softc *sc; + int mute; +{ + u_int x; + + DPRINTF(("headphonemute %d --> ", gpio_read(headphone_mute))); + + if (mute) + x = headphone_active; /* mute */ + else + x = !headphone_active; /* unmute */ + if (x != gpio_read(headphone_mute)) + gpio_write(headphone_mute, x); + + DPRINTF(("%d\n", gpio_read(headphone_mute))); +} + +int +snapper_cint(v) + void *v; +{ + struct snapper_softc *sc = v; + u_int sense; + + sense = *headphone_detect; + DPRINTF(("headphone detect = 0x%x\n", sense)); + + if (((sense & 0x02) >> 1) == headphone_detect_active) { + DPRINTF(("headphone is inserted\n")); + snapper_mute_speaker(sc, 1); + snapper_mute_headphone(sc, 0); + sc->sc_output_mask = 1 << 1; + } else { + DPRINTF(("headphone is NOT inserted\n")); + snapper_mute_speaker(sc, 0); + snapper_mute_headphone(sc, 1); + sc->sc_output_mask = 1 << 0; + } + + return 1; +} + +#define reset_active 0 /* XXX OF */ + +#define DEQ_WRITE(sc, reg, addr) \ + if (tas3004_write(sc, reg, addr)) goto err + +int +tas3004_init(sc) + struct snapper_softc *sc; +{ + + /* No reset port. Nothing to do. */ + if (audio_hw_reset == NULL) + goto noreset; + + /* Reset TAS3004. */ + gpio_write(audio_hw_reset, !reset_active); /* Negate RESET */ + delay(100000); /* XXX Really needed? */ + + gpio_write(audio_hw_reset, reset_active); /* Assert RESET */ + delay(1); + + gpio_write(audio_hw_reset, !reset_active); /* Negate RESET */ + delay(10000); + +noreset: + DEQ_WRITE(sc, DEQ_LB0, tas3004_initdata.LB0); + DEQ_WRITE(sc, DEQ_LB1, tas3004_initdata.LB1); + DEQ_WRITE(sc, DEQ_LB2, tas3004_initdata.LB2); + DEQ_WRITE(sc, DEQ_LB3, tas3004_initdata.LB3); + DEQ_WRITE(sc, DEQ_LB4, tas3004_initdata.LB4); + DEQ_WRITE(sc, DEQ_LB5, tas3004_initdata.LB5); + DEQ_WRITE(sc, DEQ_LB6, tas3004_initdata.LB6); + DEQ_WRITE(sc, DEQ_RB0, tas3004_initdata.RB0); + DEQ_WRITE(sc, DEQ_RB1, tas3004_initdata.RB1); + DEQ_WRITE(sc, DEQ_RB1, tas3004_initdata.RB1); + DEQ_WRITE(sc, DEQ_RB2, tas3004_initdata.RB2); + DEQ_WRITE(sc, DEQ_RB3, tas3004_initdata.RB3); + DEQ_WRITE(sc, DEQ_RB4, tas3004_initdata.RB4); + DEQ_WRITE(sc, DEQ_RB5, tas3004_initdata.RB5); + DEQ_WRITE(sc, DEQ_MCR1, tas3004_initdata.MCR1); + DEQ_WRITE(sc, DEQ_MCR2, tas3004_initdata.MCR2); + DEQ_WRITE(sc, DEQ_DRC, tas3004_initdata.DRC); + DEQ_WRITE(sc, DEQ_VOLUME, tas3004_initdata.VOLUME); + DEQ_WRITE(sc, DEQ_TREBLE, tas3004_initdata.TREBLE); + DEQ_WRITE(sc, DEQ_BASS, tas3004_initdata.BASS); + DEQ_WRITE(sc, DEQ_MIXER_L, tas3004_initdata.MIXER_L); + DEQ_WRITE(sc, DEQ_MIXER_R, tas3004_initdata.MIXER_R); + DEQ_WRITE(sc, DEQ_LLB, tas3004_initdata.LLB); + DEQ_WRITE(sc, DEQ_RLB, tas3004_initdata.RLB); + DEQ_WRITE(sc, DEQ_LLB_GAIN, tas3004_initdata.LLB_GAIN); + DEQ_WRITE(sc, DEQ_RLB_GAIN, tas3004_initdata.RLB_GAIN); + DEQ_WRITE(sc, DEQ_ACR, tas3004_initdata.ACR); + + return 0; +err: + printf("tas3004_init: error\n"); + return -1; +} + +/* FCR(0x3c) bits */ +#define I2S0CLKEN 0x1000 +#define I2S0EN 0x2000 +#define I2S1CLKEN 0x080000 +#define I2S1EN 0x100000 + +#define FCR3C_BITMASK "\020\25I2S1EN\24I2S1CLKEN\16I2S0EN\15I2S0CLKEN" + + +void +snapper_config(sc, node, parent) + struct snapper_softc *sc; + int node; + struct device *parent; +{ + int gpio; + int headphone_detect_intr = -1, headphone_detect_intrtype; + +#if 0 +#ifdef SNAPPER_DEBUG + char fcr[32]; + + bitmask_snprintf(in32rb(0x8000003c), FCR3C_BITMASK, fcr, sizeof fcr); + printf("FCR(0x3c) 0x%s\n", fcr); +#endif +#endif + + gpio = OF_getnodebyname(OF_parent(node), "gpio"); + DPRINTF((" /gpio 0x%x\n", gpio)); + gpio = OF_child(gpio); + while (gpio) { + char name[64], audio_gpio[64]; + int intr[2]; + paddr_t addr; + + bzero(name, sizeof name); + bzero(audio_gpio, sizeof audio_gpio); + addr = 0; + OF_getprop(gpio, "name", name, sizeof name); + OF_getprop(gpio, "audio-gpio", audio_gpio, sizeof audio_gpio); + OF_getprop(gpio, "AAPL,address", &addr, sizeof addr); + /* printf("0x%x %s %s\n", gpio, name, audio_gpio); */ + + /* gpio5 */ + if (strcmp(audio_gpio, "headphone-mute") == 0) + headphone_mute = mapiodev(addr,1); + /* gpio6 */ + if (strcmp(audio_gpio, "amp-mute") == 0) + amp_mute = mapiodev(addr,1); + /* extint-gpio15 */ + if (strcmp(audio_gpio, "headphone-detect") == 0) { + headphone_detect = mapiodev(addr,1); + OF_getprop(gpio, "audio-gpio-active-state", + &headphone_detect_active, 4); + OF_getprop(gpio, "interrupts", intr, 8); + headphone_detect_intr = intr[0]; + headphone_detect_intrtype = intr[1]; + } + /* gpio11 (keywest-11) */ + if (strcmp(audio_gpio, "audio-hw-reset") == 0) + audio_hw_reset = mapiodev(addr,1); + gpio = OF_peer(gpio); + } + DPRINTF((" headphone-mute %p\n", headphone_mute)); + DPRINTF((" amp-mute %p\n", amp_mute)); + DPRINTF((" headphone-detect %p\n", headphone_detect)); + DPRINTF((" headphone-detect active %x\n", headphone_detect_active)); + DPRINTF((" headphone-detect intr %x\n", headphone_detect_intr)); + DPRINTF((" audio-hw-reset %p\n", audio_hw_reset)); + + if (headphone_detect_intr != -1) + mac_intr_establish(parent, headphone_detect_intr, IST_EDGE, + IPL_AUDIO, snapper_cint, sc, "snapper_h"); +} +void +snapper_init(sc, node) + struct snapper_softc *sc; + int node; +{ + + /* "sample-rates" (44100, 48000) */ + snapper_set_rate(sc, 44100); + + /* Enable headphone interrupt? */ + *headphone_detect |= 0x80; + asm volatile ("eieio"); + + /* i2c_set_port(port); */ + +#if 1 + /* Enable I2C interrupts. */ +#define IER 4 +#define I2C_INT_DATA 0x01 +#define I2C_INT_ADDR 0x02 +#define I2C_INT_STOP 0x04 + ki2c_writereg(sc->sc_i2c, IER,I2C_INT_DATA|I2C_INT_ADDR|I2C_INT_STOP); +#endif + + if (tas3004_init(sc)) + return; + + /* Update headphone status. */ + snapper_cint(sc); + + snapper_set_volume(sc, 80, 80); +} + +void * +snapper_allocm(void *h, int dir, size_t size, int type, int flags) +{ + struct snapper_softc *sc = h; + struct snapper_dma *p; + int error; + + if (size > SNAPPER_DMALIST_MAX * SNAPPER_DMASEG_MAX) + return (NULL); + + p = malloc(sizeof(*p), type, flags); + if (!p) + return (NULL); + bzero(p, sizeof(*p)); + + /* convert to the bus.h style, not used otherwise */ + if (flags & M_NOWAIT) + flags = BUS_DMA_NOWAIT; + + p->size = size; + if ((error = bus_dmamem_alloc(sc->sc_dmat, p->size, NBPG, 0, p->segs, + 1, &p->nsegs, flags)) != 0) { + printf("%s: unable to allocate dma, error = %d\n", + sc->sc_dev.dv_xname, error); + free(p, type); + return NULL; + } + + if ((error = bus_dmamem_map(sc->sc_dmat, p->segs, p->nsegs, p->size, + &p->addr, flags | BUS_DMA_COHERENT)) != 0) { + printf("%s: unable to map dma, error = %d\n", + sc->sc_dev.dv_xname, error); + bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs); + free(p, type); + return NULL; + } + + if ((error = bus_dmamap_create(sc->sc_dmat, p->size, 1, + p->size, 0, flags, &p->map)) != 0) { + printf("%s: unable to create dma map, error = %d\n", + sc->sc_dev.dv_xname, error); + bus_dmamem_unmap(sc->sc_dmat, p->addr, size); + bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs); + free(p, type); + return NULL; + } + + if ((error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, p->size, + NULL, flags)) != 0) { + printf("%s: unable to load dma map, error = %d\n", + sc->sc_dev.dv_xname, error); + bus_dmamap_destroy(sc->sc_dmat, p->map); + bus_dmamem_unmap(sc->sc_dmat, p->addr, size); + bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs); + free(p, type); + return NULL; + } + + p->next = sc->sc_dmas; + sc->sc_dmas = p; + + return p->addr; +} |