summaryrefslogtreecommitdiff
path: root/sys/arch/sgi/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/sgi/dev')
-rw-r--r--sys/arch/sgi/dev/mavb.c339
1 files changed, 271 insertions, 68 deletions
diff --git a/sys/arch/sgi/dev/mavb.c b/sys/arch/sgi/dev/mavb.c
index f59afd912c5..dd38632a85d 100644
--- a/sys/arch/sgi/dev/mavb.c
+++ b/sys/arch/sgi/dev/mavb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mavb.c,v 1.10 2009/10/26 18:00:06 miod Exp $ */
+/* $OpenBSD: mavb.c,v 1.11 2009/11/18 21:13:17 jakemsr Exp $ */
/*
* Copyright (c) 2005 Mark Kettenis
@@ -54,6 +54,12 @@ int mavb_debug = ~MAVB_DEBUG_INTR;
/* XXX We need access to some of the MACE ISA registers. */
#define MAVB_ISA_NREGS 0x20
+#define MAVB_ISA_RING_SIZE 0x4000 /* Mace ISA DMA ring size. */
+#define MAVB_CHAN_RING_SIZE 0x1000 /* DMA buffer size per channel. */
+#define MAVB_CHAN_INTR_SIZE 0x0800 /* Interrupt on 50% buffer transfer. */
+#define MAVB_CHAN_CHUNK_SIZE 0x0400 /* Move data in 25% buffer chunks. */
+
+
/*
* AD1843 Mixer.
*/
@@ -62,6 +68,7 @@ enum {
AD1843_RECORD_CLASS,
AD1843_ADC_SOURCE, /* ADC Source Select */
AD1843_ADC_GAIN, /* ADC Input Gain */
+ AD1843_ADC_MIC_GAIN, /* ADC Mic Input Gain */
AD1843_INPUT_CLASS,
AD1843_DAC1_GAIN, /* DAC1 Analog/Digital Gain/Attenuation */
@@ -107,6 +114,18 @@ const char *ad1843_input[] = {
AudioNmono /* AD1843_MISC_SETTINGS */
};
+struct mavb_chan {
+ caddr_t hw_start;
+ caddr_t sw_start;
+ caddr_t sw_end;
+ caddr_t sw_cur;
+ void (*intr)(void *);
+ void *intrarg;
+ u_long rate;
+ u_int format;
+ int blksize;
+};
+
struct mavb_softc {
struct device sc_dev;
bus_space_tag_t sc_st;
@@ -117,19 +136,10 @@ struct mavb_softc {
/* 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 mavb_chan play;
+ struct mavb_chan rec;
struct timeout sc_volume_button_to;
};
@@ -168,6 +178,7 @@ 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 *);
+void mavb_get_default_params(void *, int, struct audio_params *);
struct audio_hw_if mavb_sa_hw_if = {
mavb_open,
@@ -196,7 +207,7 @@ struct audio_hw_if mavb_sa_hw_if = {
mavb_get_props,
mavb_trigger_output,
mavb_trigger_input,
- 0
+ mavb_get_default_params
};
struct audio_device mavb_device = {
@@ -357,48 +368,107 @@ ulinear8_to_ulinear24_be_mts(void *hdl, u_char *p, int cc)
}
}
+void
+mavb_get_default_params(void *hdl, int mode, struct audio_params *p)
+{
+ p->sample_rate = 48000;
+ p->encoding = AUDIO_ENCODING_SLINEAR_BE;
+ p->precision = 16;
+ p->channels = 2;
+ p->factor = 2;
+ if (mode == AUMODE_PLAY)
+ p->sw_code = linear16_to_linear24_be;
+ else
+ p->sw_code = linear24_to_linear16_be;
+}
+
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) {
+ if (sc->play.rate != sample_rate) {
ad1843_reg_write(sc, AD1843_CLOCK2_SAMPLE_RATE, sample_rate);
- sc->sc_play_rate = sample_rate;
+ sc->play.rate = sample_rate;
}
return (0);
}
static int
-mavb_set_play_format(struct mavb_softc *sc, u_int encoding)
+mavb_set_rec_rate(struct mavb_softc *sc, u_long sample_rate)
{
- u_int16_t value;
- u_int format;
+ if (sample_rate < 4000 || sample_rate > 48000)
+ return (EINVAL);
+ if (sc->rec.rate != sample_rate) {
+ ad1843_reg_write(sc, AD1843_CLOCK1_SAMPLE_RATE, sample_rate);
+ sc->rec.rate = sample_rate;
+ }
+ return (0);
+}
+
+static int
+mavb_get_format(u_int encoding, u_int *format)
+{
switch(encoding) {
case AUDIO_ENCODING_ULINEAR_BE:
- format = AD1843_PCM8;
+ *format = AD1843_PCM8;
break;
case AUDIO_ENCODING_SLINEAR_BE:
- format = AD1843_PCM16;
+ *format = AD1843_PCM16;
break;
case AUDIO_ENCODING_ULAW:
- format = AD1843_ULAW;
+ *format = AD1843_ULAW;
break;
case AUDIO_ENCODING_ALAW:
- format = AD1843_ALAW;
+ *format = AD1843_ALAW;
break;
default:
return (EINVAL);
}
+ return (0);
+}
+
+static int
+mavb_set_play_format(struct mavb_softc *sc, u_int encoding)
+{
+ u_int16_t value;
+ u_int format;
+ int err;
- if (sc->sc_play_format != format) {
+ err = mavb_get_format(encoding, &format);
+ if (err)
+ return (err);
+
+ if (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;
+ sc->play.format = format;
+ }
+ return (0);
+}
+
+static int
+mavb_set_rec_format(struct mavb_softc *sc, u_int encoding)
+{
+ u_int16_t value;
+ u_int format;
+ int err;
+
+ err = mavb_get_format(encoding, &format);
+ if (err)
+ return (err);
+
+ if (sc->rec.format != format) {
+ value = ad1843_reg_read(sc, AD1843_SERIAL_INTERFACE);
+ value &= ~(AD1843_ADRF_MASK | AD1843_ADLF_MASK);
+ value |= (format << AD1843_ADRF_SHIFT) |
+ (format << AD1843_ADLF_SHIFT);
+ ad1843_reg_write(sc, AD1843_SERIAL_INTERFACE, value);
+ sc->rec.format = format;
}
return (0);
}
@@ -488,6 +558,17 @@ mavb_set_params(void *hdl, int setmode, int usemode,
default:
return (EINVAL);
}
+
+ /* stereo to mono conversions not yet implemented */
+ rec->channels = 2;
+
+ error = mavb_set_rec_rate(sc, rec->sample_rate);
+ if (error)
+ return (error);
+
+ error = mavb_set_rec_format(sc, rec->encoding);
+ if (error)
+ return (error);
}
return (0);
@@ -496,8 +577,12 @@ mavb_set_params(void *hdl, int setmode, int usemode,
int
mavb_round_blocksize(void *hdl, int bs)
{
- /* Block size should be a multiple of 32. */
- return (bs + 0x1f) & ~0x1f;
+ if (bs == 0)
+ bs = MAVB_CHAN_INTR_SIZE;
+ else
+ bs = (bs + MAVB_CHAN_INTR_SIZE - 1) &
+ ~(MAVB_CHAN_INTR_SIZE - 1);
+ return (bs);
}
int
@@ -514,6 +599,11 @@ mavb_halt_output(void *hdl)
int
mavb_halt_input(void *hdl)
{
+ struct mavb_softc *sc = (struct mavb_softc *)hdl;
+
+ DPRINTF(1, ("%s: mavb_halt_input called\n", sc->sc_dev.dv_xname));
+
+ bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CHANNEL1_CONTROL, 0);
return (0);
}
@@ -552,6 +642,14 @@ mavb_set_port(void *hdl, struct mixer_ctrl *mc)
value |= ((right >> 4) << AD1843_RIG_SHIFT);
ad1843_reg_write(sc, AD1843_ADC_SOURCE_GAIN, value);
break;
+ case AD1843_ADC_MIC_GAIN:
+ value = ad1843_reg_read(sc, AD1843_ADC_SOURCE_GAIN);
+ if (mc->un.ord == 0)
+ value &= ~(AD1843_LMGE | AD1843_RMGE);
+ else
+ value |= (AD1843_LMGE | AD1843_RMGE);
+ ad1843_reg_write(sc, AD1843_ADC_SOURCE_GAIN, value);
+ break;
case AD1843_DAC1_GAIN:
left = AUDIO_MAX_GAIN -
@@ -670,7 +768,11 @@ mavb_get_port(void *hdl, struct mixer_ctrl *mc)
mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
(left << 4) | left;
mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
- (right << 2) | right;
+ (right << 4) | right;
+ break;
+ case AD1843_ADC_MIC_GAIN:
+ value = ad1843_reg_read(sc, AD1843_ADC_SOURCE_GAIN);
+ mc->un.ord = (value & AD1843_LMGE) ? 1 : 0;
break;
case AD1843_DAC1_GAIN:
@@ -780,6 +882,19 @@ mavb_query_devinfo(void *hdl, struct mixer_devinfo *di)
strlcpy(di->un.v.units.name, AudioNvolume,
sizeof di->un.v.units.name);
break;
+ case AD1843_ADC_MIC_GAIN:
+ di->type = AUDIO_MIXER_ENUM;
+ di->mixer_class = AD1843_RECORD_CLASS;
+ strlcpy(di->label.name, AudioNmicrophone "." AudioNpreamp,
+ sizeof di->label.name);
+ di->un.e.num_mem = 2;
+ strlcpy(di->un.e.member[0].label.name, AudioNoff,
+ sizeof di->un.e.member[0].label.name);
+ di->un.e.member[0].ord = 0;
+ strlcpy(di->un.e.member[1].label.name, AudioNon,
+ sizeof di->un.e.member[1].label.name);
+ di->un.e.member[1].ord = 1;
+ break;
case AD1843_INPUT_CLASS:
di->type = AUDIO_MIXER_CLASS;
@@ -915,39 +1030,91 @@ 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;
+ caddr_t src, dst, end;
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;
+ end = sc->play.hw_start + MAVB_CHAN_RING_SIZE;
+ dst = sc->play.hw_start + write_ptr;
+ src = sc->play.sw_cur;
- count = (MAVB_ISA_RING_SIZE - depth - 32);
+ if (write_ptr % MAVB_CHAN_CHUNK_SIZE) {
+ printf("%s: write_ptr=%d\n", sc->sc_dev.dv_xname, write_ptr);
+ return;
+ }
+ if ((src - sc->play.sw_start) % MAVB_CHAN_CHUNK_SIZE) {
+ printf("%s: src=%d\n", sc->sc_dev.dv_xname,
+ src - sc->play.sw_start);
+ return;
+ }
+
+ count = MAVB_CHAN_INTR_SIZE / MAVB_CHAN_CHUNK_SIZE;
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;
+ memcpy(dst, src, MAVB_CHAN_CHUNK_SIZE);
+ dst += MAVB_CHAN_CHUNK_SIZE;
+ src += MAVB_CHAN_CHUNK_SIZE;
+ if (dst >= end)
+ dst = sc->play.hw_start;
+ if (src >= sc->play.sw_end)
+ src = sc->play.sw_start;
+ if (!((src - sc->play.sw_start) % sc->play.blksize)) {
+ if (sc->play.intr)
+ sc->play.intr(sc->play.intrarg);
}
}
-
- write_ptr = dst - sc->sc_ring;
+ write_ptr = dst - sc->play.hw_start;
bus_space_write_8(st, sh, MAVB_CHANNEL2_WRITE_PTR, write_ptr);
- sc->sc_get = src;
+ sc->play.sw_cur = src;
+}
+
+static void
+mavb_dma_input(struct mavb_softc *sc)
+{
+ bus_space_tag_t st = sc->sc_st;
+ bus_space_handle_t sh = sc->sc_sh;
+ u_int64_t read_ptr;
+ caddr_t src, dst, end;
+ int count;
+
+ read_ptr = bus_space_read_8(st, sh, MAVB_CHANNEL1_READ_PTR);
+
+ end = sc->rec.hw_start + MAVB_CHAN_RING_SIZE;
+ src = sc->rec.hw_start + read_ptr;
+ dst = sc->rec.sw_cur;
+
+ if (read_ptr % MAVB_CHAN_CHUNK_SIZE) {
+ printf("%s: read_ptr=%d\n", sc->sc_dev.dv_xname, read_ptr);
+ return;
+ }
+ if ((dst - sc->rec.sw_start) % MAVB_CHAN_CHUNK_SIZE) {
+ printf("%s: dst=%d\n", sc->sc_dev.dv_xname,
+ dst - sc->rec.sw_start);
+ return;
+ }
+
+ count = MAVB_CHAN_INTR_SIZE / MAVB_CHAN_CHUNK_SIZE;
+ while (--count >= 0) {
+ memcpy(dst, src, MAVB_CHAN_CHUNK_SIZE);
+ dst += MAVB_CHAN_CHUNK_SIZE;
+ src += MAVB_CHAN_CHUNK_SIZE;
+ if (src >= end)
+ src = sc->rec.hw_start;
+ if (dst >= sc->rec.sw_end)
+ dst = sc->rec.sw_start;
+ if (!((dst - sc->rec.sw_start) % sc->rec.blksize)) {
+ if (sc->rec.intr)
+ sc->rec.intr(sc->rec.intrarg);
+ }
+ }
+ read_ptr = src - sc->rec.hw_start;
+ bus_space_write_8(st, sh, MAVB_CHANNEL1_READ_PTR, read_ptr);
+ sc->rec.sw_cur = dst;
}
int
mavb_trigger_output(void *hdl, void *start, void *end, int blksize,
- void (*intr)(void *), void *intrarg,
- struct audio_params *param)
+ void (*intr)(void *), void *intrarg, struct audio_params *param)
{
struct mavb_softc *sc = (struct mavb_softc *)hdl;
@@ -955,32 +1122,59 @@ mavb_trigger_output(void *hdl, void *start, void *end, int blksize,
"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->play.blksize = blksize;
+ sc->play.intr = intr;
+ sc->play.intrarg = intrarg;
- sc->sc_start = sc->sc_get = start;
- sc->sc_end = end;
-
- sc->sc_count = 0;
+ sc->play.sw_start = sc->play.sw_cur = start;
+ sc->play.sw_end = end;
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);
+ /* Fill first 25% of buffer with silence. */
+ bzero(sc->play.hw_start, MAVB_CHAN_CHUNK_SIZE);
+ bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CHANNEL2_WRITE_PTR,
+ MAVB_CHAN_CHUNK_SIZE);
+
+ /* Fill next 50% of buffer with audio data. */
mavb_dma_output(sc);
+ /* The buffer is now 75% full. Start DMA and get interrupts
+ * when the buffer is 25% full. The interrupt handler fills
+ * in 50% of the buffer size, putting it back to 75% full.
+ */
bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CHANNEL2_CONTROL,
- MAVB_CHANNEL_DMA_ENABLE | MAVB_CHANNEL_INT_50);
+ MAVB_CHANNEL_DMA_ENABLE | MAVB_CHANNEL_INT_25);
return (0);
}
int
mavb_trigger_input(void *hdl, void *start, void *end, int blksize,
- void (*intr)(void *), void *intrarg,
- struct audio_params *param)
+ 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->rec.blksize = blksize;
+ sc->rec.intr = intr;
+ sc->rec.intrarg = intrarg;
+
+ sc->rec.sw_start = sc->rec.sw_cur = start;
+ sc->rec.sw_end = end;
+
+ bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CHANNEL1_CONTROL,
+ MAVB_CHANNEL_RESET);
+ delay(1000);
+ bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CHANNEL1_CONTROL, 0);
+
+ bus_space_write_8(sc->sc_st, sc->sc_sh, MAVB_CHANNEL1_CONTROL,
+ MAVB_CHANNEL_DMA_ENABLE | MAVB_CHANNEL_INT_50);
return (0);
}
@@ -1054,6 +1248,9 @@ mavb_intr(void *arg)
(hz * MAVB_VOLUME_BUTTON_REPEAT_DEL1) / 1000);
}
+ if (intstat & MACE_ISA_INT_AUDIO_DMA1)
+ mavb_dma_input(sc);
+
if (intstat & MACE_ISA_INT_AUDIO_DMA2)
mavb_dma_output(sc);
@@ -1099,31 +1296,32 @@ mavb_attach(struct device *parent, struct device *self, void *aux)
/* Set up DMA structures. */
sc->sc_dmat = maa->maa_dmat;
- if (bus_dmamap_create(sc->sc_dmat, 4 * MAVB_ISA_RING_SIZE, 1,
- 4 * MAVB_ISA_RING_SIZE, 0, 0, &sc->sc_dmamap)) {
+ if (bus_dmamap_create(sc->sc_dmat, MAVB_ISA_RING_SIZE, 1,
+ 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,
+ if (bus_dmamem_alloc(sc->sc_dmat, MAVB_ISA_RING_SIZE,
MACE_ISA_RING_ALIGN, 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,
+ if (bus_dmamem_map(sc->sc_dmat, &seg, rseg, 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)) {
+ 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 */
+ sc->rec.hw_start = sc->sc_ring;
+ sc->play.hw_start = sc->sc_ring + MAVB_CHAN_RING_SIZE;
bus_space_write_8(sc->sc_st, sc->sc_isash, MACE_ISA_RING_BASE,
sc->sc_dmamap->dm_segs[0].ds_addr);
@@ -1159,17 +1357,22 @@ mavb_attach(struct device *parent, struct device *self, void *aux)
/* 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);
+ ad1843_reg_write(sc, AD1843_FUNDAMENTAL_SETTINGS,
+ value | AD1843_C1EN | AD1843_C2EN);
/* 6. Configure conversion resources while they are in standby. */
+ value = ad1843_reg_read(sc, AD1843_SERIAL_INTERFACE);
+ ad1843_reg_write(sc, AD1843_SERIAL_INTERFACE, value | AD1843_ADTLK);
value = ad1843_reg_read(sc, AD1843_CHANNEL_SAMPLE_RATE);
ad1843_reg_write(sc, AD1843_CHANNEL_SAMPLE_RATE,
- value | (2 << AD1843_DA1C_SHIFT));
+ value | (2 << AD1843_DA1C_SHIFT) |
+ (1 << AD1843_ADRC_SHIFT) | (1 << AD1843_ADLC_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 | AD1843_AAMEN));
+ value | (AD1843_DA1EN | AD1843_ANAEN | AD1843_AAMEN |
+ AD1843_ADREN | AD1843_ADLEN));
/* 8. Configure conversion resources while they are enabled. */
value = ad1843_reg_read(sc, AD1843_DAC1_ANALOG_GAIN);
@@ -1185,8 +1388,8 @@ mavb_attach(struct device *parent, struct device *self, void *aux)
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;
+ sc->play.rate = sc->rec.rate = 48000;
+ sc->play.format = sc->rec.format = AD1843_PCM8;
timeout_set(&sc->sc_volume_button_to, mavb_button_repeat, sc);