summaryrefslogtreecommitdiff
path: root/sys/arch/sgi/dev
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2005-01-02 00:25:42 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2005-01-02 00:25:42 +0000
commit294f89dcd89487b1e381a215a0c963b43e4a26f1 (patch)
tree0194e649c6cd95656f8c0755a562de5d81c46d53 /sys/arch/sgi/dev
parent21a6c595cf47808e2eb97228220a07b2312fa9e2 (diff)
Moosehead A/V Board audio driver.
Diffstat (limited to 'sys/arch/sgi/dev')
-rw-r--r--sys/arch/sgi/dev/mavb.c977
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));
+}