diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2005-01-02 00:25:42 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2005-01-02 00:25:42 +0000 |
commit | 294f89dcd89487b1e381a215a0c963b43e4a26f1 (patch) | |
tree | 0194e649c6cd95656f8c0755a562de5d81c46d53 | |
parent | 21a6c595cf47808e2eb97228220a07b2312fa9e2 (diff) |
Moosehead A/V Board audio driver.
-rw-r--r-- | sys/arch/sgi/dev/mavb.c | 977 |
1 files changed, 977 insertions, 0 deletions
diff --git a/sys/arch/sgi/dev/mavb.c b/sys/arch/sgi/dev/mavb.c new file mode 100644 index 00000000000..8756964f719 --- /dev/null +++ b/sys/arch/sgi/dev/mavb.c @@ -0,0 +1,977 @@ +/* + * Copyright (c) 2004 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/timeout.h> + +#include <machine/bus.h> +#include <machine/intr.h> +#include <machine/autoconf.h> + +#include <sys/audioio.h> +#include <dev/audio_if.h> + +#include <mips64/archtype.h> + +#include <sgi/localbus/macebus.h> + +#undef MAVB_DEBUG + +#ifdef MAVB_DEBUG +#define DPRINTF(l,x) do { if (mavb_debug & (l)) printf x; } while (0) +#define MAVB_DEBUG_INTR 0x0100 +int mavb_debug = ~MAVB_DEBUG_INTR; +#else +#define DPRINTF(l,x) /* nothing */ +#endif + +/* Repeat delays for volume buttons. */ +#define MAVB_VOLUME_BUTTON_REPEAT_DEL1 400 /* 400ms to start repeating */ +#define MAVB_VOLUME_BUTTON_REPEAT_DELN 100 /* 100ms between repeats */ + +/* + * MACE registers used by the Moosehead A/V Board. + */ + +#define MAVB_CONTROL 0x00 +#define MAVB_CONTROL_RESET 0x0000000000000001 +#define MAVB_CONTROL_CODEC_PRESENT 0x0000000000000002 +#define MAVB_CONTROL_VOLUME_BUTTON_UP 0x0000000001000000 +#define MAVB_CONTROL_VOLUME_BUTTON_DOWN 0x0000000000800000 + +#define MAVB_CODEC_CONTROL 0x08 +#define MAVB_CODEC_READ 0x0000000000010000 +#define MAVB_CODEC_WORD_SHIFT 0 +#define MAVB_CODEC_WORD_MASK 0x000000000000ffff +#define MAVB_CODEC_ADDRESS_SHIFT 17 +#define MAVB_CODEC_ADDRESS_MASK 0x000000000000001f + +#define MAVB_CODEC_STATUS 0x18 +#define MAVB_CHANNEL1_CONTROL 0x20 +#define MAVB_CHANNEL2_CONTROL 0x40 +#define MAVB_CHANNEL3_CONTROL 0x60 +#define MAVB_CHANNEL_RESET 0x0000000000000400 +#define MAVB_CHANNEL_DMA_ENABLE 0x0000000000000200 +#define MAVB_CHANNEL_INT_DISABLED 0x0000000000000000 +#define MAVB_CHANNEL_INT_25 0x0000000000000020 +#define MAVB_CHANNEL_INT_50 0x0000000000000040 +#define MAVB_CHANNEL_INT_75 0x0000000000000060 +#define MAVB_CHANNEL_INT_EMPTY 0x0000000000000080 +#define MAVB_CHANNEL_INT_NOT_EMPTY 0x00000000000000a0 +#define MAVB_CHANNEL_INT_FULL 0x00000000000000c0 +#define MAVB_CHANNEL_INT_NOT_FULL 0x00000000000000e0 + +#define MAVB_CHANNEL1_READ_PTR 0x28 +#define MAVB_CHANNEL1_WRITE_PTR 0x30 +#define MAVB_CHANNEL1_DEPTH 0x38 +#define MAVB_CHANNEL2_READ_PTR 0x48 +#define MAVB_CHANNEL2_WRITE_PTR 0x50 +#define MAVB_CHANNEL2_DEPTH 0x58 + +#define MAVB_NREGS 0x80 + +/* XXX We need access to some of the MACE ISA registers. */ +#define MAVB_ISA_NREGS 0x20 + +/* + * AD1843 Codec registers & defenitions. + */ + +#define AD1843_CODEC_STATUS 0 +#define AD1843_INIT 0x8000 +#define AD1843_PDNO 0x4000 +#define AD1843_REVISION_MASK 0x000f + +#define AD1843_MISC_SETTINGS 8 +#define AD1843_MPOM 0x0040 +#define AD1843_HPOM 0x0020 +#define AD1843_HSOM 0x0010 + +#define AD1843_DAC1_ANALOG_GAIN 9 +#define AD1843_LDA1GM 0x8000 +#define AD1843_RDA1GM 0x0080 +#define AD1843_LDA1G_MASK 0x3f00 +#define AD1843_LDA1G_SHIFT 8 +#define AD1843_RDA1G_MASK 0x003f +#define AD1843_RDA1G_SHIFT 0 + +#define AD1843_DAC1_DIGITAL_GAIN 11 +#define AD1843_LDA1AM 0x8000 +#define AD1843_RDA1AM 0x0080 + +#define AD1843_CHANNEL_SAMPLE_RATE 15 +#define AD1843_DA1C_SHIFT 8 + +#define AD1843_CLOCK1_SAMPLE_RATE 17 +#define AD1843_CLOCK2_SAMPLE_RATE 20 +#define AD1843_CLOCK3_SAMPLE_RATE 13 + +#define AD1843_SERIAL_INTERFACE 26 +#define AD1843_DA2F_MASK 0x0c00 +#define AD1843_DA2F_SHIFT 10 +#define AD1843_DA1F_MASK 0x0300 +#define AD1843_DA1F_SHIFT 8 +#define AD1843_PCM8 0 +#define AD1843_PCM16 1 +#define AD1843_ULAW 2 +#define AD1843_ALAW 3 +#define AD1843_SCF 0x0080 + +#define AD1843_CHANNEL_POWER_DOWN 27 +#define AD1843_DA2EN 0x0200 +#define AD1843_DA1EN 0x0100 + +#define AD1843_FUNDAMENTAL_SETTINGS 28 +#define AD1843_PDNI 0x8000 +#define AD1843_ACEN 0x4000 +#define AD1843_C3EN 0x2000 +#define AD1843_C2EN 0x1000 +#define AD1843_C1EN 0x0800 + +#define AD1843_NREGS 32 + +typedef u_long ad1843_addr_t; + +/* + * AD1843 Mixer. + */ + +#define AD1843_OUTPUT_CLASS 0 +#define AD1843_MASTER_LVL 1 /* DAC1 Analog Gain/Attenuation */ + + +struct mavb_softc { + struct device sc_dev; + bus_space_tag_t sc_st; + bus_space_handle_t sc_sh; + bus_dma_tag_t sc_dmat; + bus_dmamap_t sc_dmamap; + + /* XXX We need access to some of the MACE ISA registers. */ + bus_space_handle_t sc_isash; + +#define MAVB_ISA_RING_SIZE 0x1000 + caddr_t sc_ring; + + caddr_t sc_start, sc_end; + int sc_blksize; + void (*sc_intr)(void *); + void *sc_intrarg; + + caddr_t sc_get; + int sc_count; + + u_long sc_play_rate; + u_int sc_play_format; + + struct timeout sc_volume_button_to; +}; + +int mavb_match(struct device *, void *, void *); +void mavb_attach(struct device *, struct device *, void *); + +struct cfattach mavb_ca = { + sizeof(struct mavb_softc), mavb_match, mavb_attach +}; + +struct cfdriver mavb_cd = { + NULL, "mavb", DV_DULL +}; + +u_int16_t ad1843_reg_read(struct mavb_softc *, ad1843_addr_t); +u_int16_t ad1843_reg_write(struct mavb_softc *, ad1843_addr_t, u_int16_t); +void ad1843_dump_regs(struct mavb_softc *); + +int mavb_open(void *, int); +void mavb_close(void *); +int mavb_query_encoding(void *, struct audio_encoding *); +int mavb_set_params(void *, int, int, struct audio_params *, + struct audio_params *); +int mavb_round_blocksize(void *hdl, int bs); +int mavb_halt_output(void *); +int mavb_halt_input(void *); +int mavb_getdev(void *, struct audio_device *); +int mavb_set_port(void *, struct mixer_ctrl *); +int mavb_get_port(void *, struct mixer_ctrl *); +int mavb_query_devinfo(void *, struct mixer_devinfo *); +int mavb_get_props(void *); +int mavb_trigger_output(void *, void *, void *, int, void (*)(void *), + void *, struct audio_params *); +int mavb_trigger_input(void *, void *, void *, int, void (*)(void *), + void *, struct audio_params *); + +struct audio_hw_if mavb_sa_hw_if = { + mavb_open, + mavb_close, + 0, + mavb_query_encoding, + mavb_set_params, + mavb_round_blocksize, + 0, + 0, + 0, + 0, + 0, + mavb_halt_output, + mavb_halt_input, + 0, + mavb_getdev, + 0, + mavb_set_port, + mavb_get_port, + mavb_query_devinfo, + 0, + 0, + 0, + 0, + mavb_get_props, + mavb_trigger_output, + mavb_trigger_input +}; + +struct audio_device mavb_device = { + "A3", + "", + "mavb" +}; + +int +mavb_open(void *hdl, int flags) +{ + return (0); +} + +void +mavb_close(void *hdl) +{ +} + +int +mavb_query_encoding(void *hdl, struct audio_encoding *ae) +{ + switch (ae->index) { + case 0: + /* 8-bit Unsigned Linear PCM. */ + strlcpy(ae->name, AudioEulinear, sizeof ae->name); + ae->encoding = AUDIO_ENCODING_ULINEAR; + ae->precision = 8; + ae->flags = 0; + break; + case 1: + /* 16-bit Signed Linear PCM. */ + strlcpy(ae->name, AudioEslinear_be, sizeof ae->name); + ae->encoding = AUDIO_ENCODING_SLINEAR_BE; + ae->precision = 16; + ae->flags = 0; + break; + case 2: + /* 8-bit mu-Law Companded. */ + strlcpy(ae->name, AudioEmulaw, sizeof ae->name); + ae->encoding = AUDIO_ENCODING_ULAW; + ae->precision = 8; + ae->flags = 0; + break; + case 3: + /* 8-bit A-Law Companded. */ + strlcpy(ae->name, AudioEalaw, sizeof ae->name); + ae->encoding = AUDIO_ENCODING_ALAW; + ae->precision = 8; + ae->flags = 0; + break; + default: + return (EINVAL); + } + + return (0); +} + +/* + * For some reason SGI has decided to standardize their sound hardware + * interfaces on 24-bit PCM even though the AD1843 codec used in the + * Moosehead A/V Board only supports 16-bit and 8-bit formats. + * Therefore we must convert everything to 24-bit samples only to have + * the MACE hardware convert them back into 16-bit samples again. To + * complicate matters further, the 24-bit samples are embedded 32-bit + * integers. The 8-bit and 16-bit samples are first converted into + * 24-bit samples by padding them to the right with zeroes. Then they + * are sign-extended into 32-bit integers. This conversion is + * conveniently done through the software encoding layer of the high + * level audio driver by using the functions below. Conversion of + * mu-law and A-law formats is done by the hardware. + */ + +static void +linear16_to_linear24_be(void *hdl, u_char *p, int cc) +{ + u_char *q = p; + + p += cc; + q += cc * 2; + while ((cc -= 2) >= 0) { + q -= 4; + q[3] = 0; + q[2] = *--p; + q[1] = *--p; + q[0] = (*p & 0x80) ? 0xff : 0; + } +} + +static void +linear24_to_linear16_be(void *hdl, u_char *p, int cc) +{ + u_char *q = p; + + while ((cc -= 4) >= 0) { + *q++ = p[1]; + *q++ = p[2]; + p += 4; + } +} + +static void +ulinear8_to_ulinear24_be(void *hdl, u_char *p, int cc) +{ + u_char *q = p; + + p += cc; + q += cc * 4; + while (--cc >= 0) { + q -= 4; + q[3] = 0; + q[2] = 0; + q[1] = *--p; + q[0] = (*p & 0x80) ? 0xff : 0; + } +} + +static void +ulinear24_to_ulinear8_be(void *hdl, u_char *p, int cc) +{ + u_char *q = p; + + while ((cc -= 4) >= 0) { + *q++ = p[1]; + p += 4; + } +} + +static void +linear16_to_linear24_be_mts(void *hdl, u_char *p, int cc) +{ + u_char *q = p; + + p += cc; + q += cc * 4; + while ((cc -= 2) >= 0) { + q -= 8; + q[3] = q[7] = 0; + q[2] = q[6] = *--p; + q[1] = q[5] = *--p; + q[0] = q[4] = (*p & 0x80) ? 0xff : 0; + } +} + +static void +ulinear8_to_ulinear24_be_mts(void *hdl, u_char *p, int cc) +{ + u_char *q = p; + + p += cc; + q += cc * 8; + while (--cc >= 0) { + q -= 8; + q[3] = q[7] = 0; + q[2] = q[6] = 0; + q[1] = q[5] = *--p; + q[0] = q[4] = (*p & 0x80) ? 0xff : 0; + } +} + +static int +mavb_set_play_rate(struct mavb_softc *sc, u_long sample_rate) +{ + if (sample_rate < 4000 || sample_rate > 48000) + return (EINVAL); + + if (sc->sc_play_rate != sample_rate) { + ad1843_reg_write(sc, AD1843_CLOCK2_SAMPLE_RATE, sample_rate); + sc->sc_play_rate = sample_rate; + } + return (0); +} + +static int +mavb_set_play_format(struct mavb_softc *sc, u_int encoding) +{ + u_int16_t value; + u_int format; + + switch(encoding) { + case AUDIO_ENCODING_ULINEAR_BE: + format = AD1843_PCM8; + break; + case AUDIO_ENCODING_SLINEAR_BE: + format = AD1843_PCM16; + break; + case AUDIO_ENCODING_ULAW: + format = AD1843_ULAW; + break; + case AUDIO_ENCODING_ALAW: + format = AD1843_ALAW; + break; + default: + return (EINVAL); + } + + if (sc->sc_play_format != format) { + value = ad1843_reg_read(sc, AD1843_SERIAL_INTERFACE); + value &= ~AD1843_DA1F_MASK; + value |= (format << AD1843_DA1F_SHIFT); + ad1843_reg_write(sc, AD1843_SERIAL_INTERFACE, value); + sc->sc_play_format = format; + } + return (0); +} + +int +mavb_set_params(void *hdl, int setmode, int usemode, + struct audio_params *play, struct audio_params *rec) +{ + struct mavb_softc *sc = (struct mavb_softc *)hdl; + int error; + + DPRINTF(1, ("%s: mavb_set_params: sample=%ld precision=%d " + "channels=%d\n", sc->sc_dev.dv_xname, play->sample_rate, + play->precision, play->channels)); + + if (setmode & AUMODE_PLAY) { + switch (play->encoding) { + case AUDIO_ENCODING_ULAW: + case AUDIO_ENCODING_ALAW: + case AUDIO_ENCODING_ULINEAR_BE: + if (play->precision != 8) + return (EINVAL); + switch (play->channels) { + case 1: + play->factor = 8; + play->sw_code = ulinear8_to_ulinear24_be_mts; + break; + case 2: + play->factor = 4; + play->sw_code = ulinear8_to_ulinear24_be; + break; + default: + return (EINVAL); + } + break; + case AUDIO_ENCODING_SLINEAR_BE: + if (play->precision == 16) { + switch (play->channels) { + case 1: + play->factor = 4; + play->sw_code = + linear16_to_linear24_be_mts; + break; + case 2: + play->factor = 2; + play->sw_code = + linear16_to_linear24_be; + break; + default: + return (EINVAL); + } + } else { + return (EINVAL); + } + break; + default: + return (EINVAL); + } + + error = mavb_set_play_rate(sc, play->sample_rate); + if (error) + return (error); + + error = mavb_set_play_format(sc, play->encoding); + if (error) + return (error); + } + + if (setmode & AUMODE_RECORD) { + switch (rec->encoding) { + case AUDIO_ENCODING_ULAW: + case AUDIO_ENCODING_ALAW: + case AUDIO_ENCODING_ULINEAR_BE: + if (rec->precision != 8) + return (EINVAL); + rec->factor = 4; + rec->sw_code = ulinear24_to_ulinear8_be; + break; + case AUDIO_ENCODING_SLINEAR_BE: + if (rec->precision == 16) { + rec->factor = 2; + rec->sw_code = linear24_to_linear16_be; + } else { + return (EINVAL); + } + break; + default: + return (EINVAL); + } + } + + return (0); +} + +int +mavb_round_blocksize(void *hdl, int bs) +{ + /* Block size should be a multiple of 32. */ + return bs & ~0x1f; +} + +int +mavb_halt_output(void *hdl) +{ + struct mavb_softc *sc = (struct mavb_softc *)hdl; + + DPRINTF(1, ("%s: mavb_halt_output called\n", sc->sc_dev.dv_xname)); + + bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CHANNEL2_CONTROL, 0); + return (0); +} + +int +mavb_halt_input(void *hdl) +{ + return (0); +} + +int +mavb_getdev(void *hdl, struct audio_device *ret) +{ + *ret = mavb_device; + return (0); +} + +int +mavb_set_port(void *hdl, struct mixer_ctrl *mc) +{ + struct mavb_softc *sc = (struct mavb_softc *)hdl; + u_char left, right; + u_int16_t value; + + DPRINTF(1, ("%s: mavb_set_port: dev=%d\n", sc->sc_dev.dv_xname, + mc->dev)); + + switch (mc->dev) { + case AD1843_MASTER_LVL: + left = AUDIO_MAX_GAIN - + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; + right = AUDIO_MAX_GAIN - + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; + value = ad1843_reg_read(sc, AD1843_DAC1_ANALOG_GAIN); + value &= ~(AD1843_LDA1G_MASK | AD1843_RDA1G_MASK); + value |= ((left >> 2) << AD1843_LDA1G_SHIFT); + value |= ((right >> 2) << AD1843_RDA1G_SHIFT); + ad1843_reg_write(sc, AD1843_DAC1_ANALOG_GAIN, value); + break; + default: + return (EINVAL); + } + + return (0); +} + +int +mavb_get_port(void *hdl, struct mixer_ctrl *mc) +{ + struct mavb_softc *sc = (struct mavb_softc *)hdl; + u_char left, right; + u_int16_t value; + + DPRINTF(1, ("%s: mavb_get_port: dev=%d\n", sc->sc_dev.dv_xname, + mc->dev)); + + switch (mc->dev) { + case AD1843_MASTER_LVL: + value = ad1843_reg_read(sc, AD1843_DAC1_ANALOG_GAIN); + left = (value & AD1843_LDA1G_MASK) >> AD1843_LDA1G_SHIFT; + right = (value & AD1843_RDA1G_MASK) >> AD1843_RDA1G_SHIFT; + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = + AUDIO_MAX_GAIN - (left << 2); + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = + AUDIO_MAX_GAIN - (right << 2); + break; + default: + return (EINVAL); + } + + return (0); +} + +int +mavb_query_devinfo(void *hdl, struct mixer_devinfo *di) +{ + di->prev = di->next = AUDIO_MIXER_LAST; + + switch (di->index) { + case AD1843_OUTPUT_CLASS: + di->type = AUDIO_MIXER_CLASS; + di->mixer_class = AD1843_OUTPUT_CLASS; + strlcpy(di->label.name, AudioCoutputs, sizeof di->label.name); + break; + + case AD1843_MASTER_LVL: + di->type = AUDIO_MIXER_VALUE; + di->mixer_class = AD1843_OUTPUT_CLASS; + strlcpy(di->label.name, AudioNmaster, sizeof di->label.name); + di->un.v.num_channels = 2; + strlcpy(di->un.v.units.name, AudioNvolume, + sizeof di->un.v.units.name); + break; + + default: + return (EINVAL); + } + + return (0); +} + +int +mavb_get_props(void *hdl) +{ + return (AUDIO_PROP_FULLDUPLEX | AUDIO_PROP_INDEPENDENT); +} + +static void +mavb_dma_output(struct mavb_softc *sc) +{ + bus_space_tag_t st = sc->sc_st; + bus_space_handle_t sh = sc->sc_sh; + u_int64_t write_ptr; + u_int64_t depth; + caddr_t src, dst; + int count; + + write_ptr = bus_space_read_8(st, sh, MAVB_CHANNEL2_WRITE_PTR); + depth = bus_space_read_8(st, sh, MAVB_CHANNEL2_DEPTH); + + dst = sc->sc_ring + write_ptr; + src = sc->sc_get; + + count = (MAVB_ISA_RING_SIZE - depth - 32); + while (--count >= 0) { + *dst++ = *src++; + if (dst >= sc->sc_ring + MAVB_ISA_RING_SIZE) + dst = sc->sc_ring; + if (src >= sc->sc_end) + src = sc->sc_start; + if (++sc->sc_count >= sc->sc_blksize) { + if (sc->sc_intr) + sc->sc_intr(sc->sc_intrarg); + sc->sc_count = 0; + } + } + + write_ptr = dst - sc->sc_ring; + bus_space_write_8(st, sh, MAVB_CHANNEL2_WRITE_PTR, write_ptr); + sc->sc_get = src; +} + +int +mavb_trigger_output(void *hdl, void *start, void *end, int blksize, + void (*intr)(void *), void *intrarg, + struct audio_params *param) +{ + struct mavb_softc *sc = (struct mavb_softc *)hdl; + + DPRINTF(1, ("%s: mavb_trigger_output: start=%p end=%p " + "blksize=%d intr=%p(%p)\n", sc->sc_dev.dv_xname, + start, end, blksize, intr, intrarg)); + + sc->sc_blksize = blksize; + sc->sc_intr = intr; + sc->sc_intrarg = intrarg; + + sc->sc_start = sc->sc_get = start; + sc->sc_end = end; + + sc->sc_count = 0; + + bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CHANNEL2_CONTROL, + MAVB_CHANNEL_RESET); + delay(1000); + bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CHANNEL2_CONTROL, 0); + + mavb_dma_output(sc); + + bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CHANNEL2_CONTROL, + MAVB_CHANNEL_DMA_ENABLE | MAVB_CHANNEL_INT_50); + return (0); +} + +int +mavb_trigger_input(void *hdl, void *start, void *end, int blksize, + void (*intr)(void *), void *intrarg, + struct audio_params *param) +{ + return (0); +} + +static void +mavb_button_repeat(void *hdl) +{ + struct mavb_softc *sc = (struct mavb_softc *)hdl; + u_int64_t intmask, control; + u_int16_t value, left, right; + + DPRINTF(1, ("%s: mavb_repeat called\n", sc->sc_dev.dv_xname)); + +#define MAVB_CONTROL_VOLUME_BUTTONS \ + (MAVB_CONTROL_VOLUME_BUTTON_UP | MAVB_CONTROL_VOLUME_BUTTON_DOWN) + + control = bus_space_read_8(sc->sc_st, sc->sc_sh, MAVB_CONTROL); + if (control & MAVB_CONTROL_VOLUME_BUTTONS) { + value = ad1843_reg_read(sc, AD1843_DAC1_ANALOG_GAIN); + left = (value & AD1843_LDA1G_MASK) >> AD1843_LDA1G_SHIFT; + right = (value & AD1843_RDA1G_MASK) >> AD1843_RDA1G_SHIFT; + if (control & MAVB_CONTROL_VOLUME_BUTTON_UP) { + control &= ~MAVB_CONTROL_VOLUME_BUTTON_UP; + if (left > 0) + left--; /* attenuation! */ + if (right > 0) + right--; + } + if (control & MAVB_CONTROL_VOLUME_BUTTON_DOWN) { + control &= ~MAVB_CONTROL_VOLUME_BUTTON_DOWN; + if (left < 63) + left++; + if (right < 63) + right++; + } + bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CONTROL, control); + + value &= ~(AD1843_LDA1G_MASK | AD1843_RDA1G_MASK); + value |= (left << AD1843_LDA1G_SHIFT); + value |= (right << AD1843_RDA1G_SHIFT); + ad1843_reg_write(sc, AD1843_DAC1_ANALOG_GAIN, value); + + timeout_add(&sc->sc_volume_button_to, + (hz * MAVB_VOLUME_BUTTON_REPEAT_DELN) / 1000); + } else { + /* Enable volume button interrupts again. */ + intmask = bus_space_read_8(sc->sc_st, sc->sc_isash, + MACE_ISA_INT_MASK); + bus_space_write_8(sc->sc_st, sc->sc_isash, MACE_ISA_INT_MASK, + intmask | MACE_ISA_INT_AUDIO_SC); + } +} + +static int +mavb_intr(void *arg) +{ + struct mavb_softc *sc = arg; + u_int64_t intstat, intmask; + + intstat = bus_space_read_8(sc->sc_st, sc->sc_isash, MACE_ISA_INT_STAT); + DPRINTF(MAVB_DEBUG_INTR, ("%s: mavb_intr: intstat = 0x%lx\n", + sc->sc_dev.dv_xname, intstat)); + + if (intstat & MACE_ISA_INT_AUDIO_SC) { + /* Disable volume button interrupts. */ + intmask = bus_space_read_8(sc->sc_st, sc->sc_isash, + MACE_ISA_INT_MASK); + bus_space_write_8(sc->sc_st, sc->sc_isash, MACE_ISA_INT_MASK, + intmask & ~MACE_ISA_INT_AUDIO_SC); + + timeout_add(&sc->sc_volume_button_to, + (hz * MAVB_VOLUME_BUTTON_REPEAT_DEL1) / 1000); + } + + if (intstat & MACE_ISA_INT_AUDIO_DMA2) + mavb_dma_output(sc); + + return 1; +} + +int +mavb_match(struct device *parent, void *match, void *aux) +{ + struct confargs *ca = aux; + + if (ca->ca_sys != SGI_O2 || strcmp(ca->ca_name, mavb_cd.cd_name)) + return (0); + + return (1); +} + +void +mavb_attach(struct device *parent, struct device *self, void *aux) +{ + struct mavb_softc *sc = (void *)self; + struct confargs *ca = aux; + bus_dma_segment_t seg; + u_int64_t control; + u_int16_t value; + int rseg; + + sc->sc_st = ca->ca_iot; + if (bus_space_map(sc->sc_st, ca->ca_baseaddr, MAVB_NREGS, 0, + &sc->sc_sh) != 0) { + printf(": can't map i/o space\n"); + return; + } + + /* XXX We need access to some of the MACE ISA registers. */ + extern bus_space_handle_t mace_h; + bus_space_subregion(sc->sc_st, mace_h, 0, MAVB_ISA_NREGS, + &sc->sc_isash); + + /* Set up DMA structures. */ + sc->sc_dmat = ca->ca_dmat; + if (bus_dmamap_create(sc->sc_dmat, 4 * MAVB_ISA_RING_SIZE, 1, + 4 * MAVB_ISA_RING_SIZE, 0, 0, &sc->sc_dmamap)) { + printf(": can't create MACE ISA DMA map\n"); + return; + } + + if (bus_dmamem_alloc(sc->sc_dmat, 4 * MAVB_ISA_RING_SIZE, + sizeof (u_int64_t), 0, &seg, 1, &rseg, BUS_DMA_NOWAIT)) { + printf(": can't allocate ring buffer\n"); + return; + } + + if (bus_dmamem_map(sc->sc_dmat, &seg, rseg, 4 * MAVB_ISA_RING_SIZE, + &sc->sc_ring, BUS_DMA_COHERENT)) { + printf(": can't map ring buffer\n"); + return; + } + + if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_ring, + 4 * MAVB_ISA_RING_SIZE, NULL, BUS_DMA_NOWAIT)) { + printf(": can't load MACE ISA DMA map\n"); + return; + } + + sc->sc_ring += MAVB_ISA_RING_SIZE; /* XXX */ + + bus_space_write_8(sc->sc_st, sc->sc_isash, MACE_ISA_RING_BASE, + sc->sc_dmamap->dm_segs[0].ds_addr); + + /* Establish interrupt. */ + BUS_INTR_ESTABLISH(ca, NULL, ca->ca_intr, IST_EDGE, IPL_AUDIO, + mavb_intr, sc, sc->sc_dev.dv_xname); + + control = bus_space_read_8(sc->sc_st, sc->sc_sh, MAVB_CONTROL); + if (!(control & MAVB_CONTROL_CODEC_PRESENT)) { + printf(": no codec present\n"); + return; + } + + /* 2. Assert the RESET signal. */ + bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CONTROL, + MAVB_CONTROL_RESET); + delay(1); /* at least 100 ns */ + + /* 3. Deassert the RESET signal and enter a wait period to + allow the AD1843 internal clocks and the external + crystal oscillator to stabilize. */ + bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CONTROL, 0); + delay(800); /* typically 400 us to 800 us */ + if (ad1843_reg_read(sc, AD1843_CODEC_STATUS) & AD1843_INIT) { + printf(": codec not ready\n"); + return; + } + + /* 4. Put the conversion sources into standby. */ + value = ad1843_reg_read(sc, AD1843_FUNDAMENTAL_SETTINGS); + ad1843_reg_write(sc, AD1843_FUNDAMENTAL_SETTINGS, + value & ~AD1843_PDNI); + delay (500000); /* approximately 474 ms */ + if (ad1843_reg_read(sc, AD1843_CODEC_STATUS) & AD1843_PDNO) { + printf(": can't power up conversion resources\n"); + return; + } + + /* 5. Power up the clock generators and enable clock output pins. */ + value = ad1843_reg_read(sc, AD1843_FUNDAMENTAL_SETTINGS); + ad1843_reg_write(sc, AD1843_FUNDAMENTAL_SETTINGS, value | AD1843_C2EN); + + /* 6. Configure conversion resources while they are in standby. */ + value = ad1843_reg_read(sc, AD1843_CHANNEL_SAMPLE_RATE); + ad1843_reg_write(sc, AD1843_CHANNEL_SAMPLE_RATE, + value | (2 << AD1843_DA1C_SHIFT)); + + /* 7. Enable conversion resources. */ + value = ad1843_reg_read(sc, AD1843_CHANNEL_POWER_DOWN); + ad1843_reg_write(sc, AD1843_CHANNEL_POWER_DOWN, value | AD1843_DA1EN); + + /* 8. Configure conversion resources while they are enabled. */ + value = ad1843_reg_read(sc, AD1843_DAC1_ANALOG_GAIN); + ad1843_reg_write(sc, AD1843_DAC1_ANALOG_GAIN, + value & ~(AD1843_LDA1GM | AD1843_RDA1GM)); + value = ad1843_reg_read(sc, AD1843_DAC1_DIGITAL_GAIN); + ad1843_reg_write(sc, AD1843_DAC1_DIGITAL_GAIN, + value & ~(AD1843_LDA1AM | AD1843_RDA1AM)); + value = ad1843_reg_read(sc, AD1843_MISC_SETTINGS); + ad1843_reg_write(sc, AD1843_MISC_SETTINGS, + value & ~(AD1843_HPOM | AD1843_MPOM)); + + value = ad1843_reg_read(sc, AD1843_CODEC_STATUS); + printf(": AD1843 rev %d\n", (u_int)value & AD1843_REVISION_MASK); + + sc->sc_play_rate = 48000; + sc->sc_play_format = AD1843_PCM8; + + timeout_set(&sc->sc_volume_button_to, mavb_button_repeat, sc); + + audio_attach_mi(&mavb_sa_hw_if, sc, &sc->sc_dev); + + return; +} + +u_int16_t +ad1843_reg_read(struct mavb_softc *sc, ad1843_addr_t addr) +{ + bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CODEC_CONTROL, + (addr & MAVB_CODEC_ADDRESS_MASK) << MAVB_CODEC_ADDRESS_SHIFT | + MAVB_CODEC_READ); + delay(200); + return bus_space_read_8(sc->sc_st, sc->sc_sh, MAVB_CODEC_STATUS); +} + +u_int16_t +ad1843_reg_write(struct mavb_softc *sc, ad1843_addr_t addr, u_int16_t value) +{ + bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CODEC_CONTROL, + (addr & MAVB_CODEC_ADDRESS_MASK) << MAVB_CODEC_ADDRESS_SHIFT | + (value & MAVB_CODEC_WORD_MASK) << MAVB_CODEC_WORD_SHIFT); + delay(200); + return bus_space_read_8(sc->sc_st, sc->sc_sh, MAVB_CODEC_STATUS); +} + +void +ad1843_dump_regs(struct mavb_softc *sc) +{ + u_int16_t addr; + + for (addr = 0; addr < AD1843_NREGS; addr++) + printf ("%d: 0x%04x\n", (int)addr, ad1843_reg_read(sc, addr)); +} |