summaryrefslogtreecommitdiff
path: root/sys/dev/pci/auvia.c
diff options
context:
space:
mode:
authorJacob Meuser <jakemsr@cvs.openbsd.org>2008-01-15 02:52:51 +0000
committerJacob Meuser <jakemsr@cvs.openbsd.org>2008-01-15 02:52:51 +0000
commitfdb9fd0866050d52511af5964c7dcaa8163829dc (patch)
tree84fe430f5e1050ce3953703eab2ca5e5dd0a8d1c /sys/dev/pci/auvia.c
parentea2aa03ab2ea9e57ed9db458346e303e1e7a5f28 (diff)
bring in lots of ac97(4) and auvia(4) updates from NetBSD
user visible changes: - when setting up ac97(4) mixer items, for some features like surround sound, check if the feature if supported by the codec and only add items for that feature if it is - auvia(4) now supports multichannel playback with at least some multichannel ac97(4) codecs fixes problems for me and ajacoutot with different auvia(4) devices/codecs. no regressions reported by testers. if you have an ac97(4) audio device and are using mixerctl.conf(5), you may want/need to update it. ok ratchov
Diffstat (limited to 'sys/dev/pci/auvia.c')
-rw-r--r--sys/dev/pci/auvia.c241
1 files changed, 155 insertions, 86 deletions
diff --git a/sys/dev/pci/auvia.c b/sys/dev/pci/auvia.c
index cb09c29ce39..8d62f3c4cf5 100644
--- a/sys/dev/pci/auvia.c
+++ b/sys/dev/pci/auvia.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: auvia.c,v 1.33 2005/05/06 01:45:22 miod Exp $ */
-/* $NetBSD: auvia.c,v 1.7 2000/11/15 21:06:33 jdolecek Exp $ */
+/* $OpenBSD: auvia.c,v 1.34 2008/01/15 02:52:50 jakemsr Exp $ */
+/* $NetBSD: auvia.c,v 1.28 2002/11/04 16:38:49 kent Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
@@ -79,14 +79,13 @@ struct auvia_dma_op {
#define AUVIA_DMAOP_COUNT(x) ((x)&0x00FFFFFF)
};
-/* rev. H and later seem to support only fixed rate 44.1 kHz */
-#define AUVIA_FIXED_RATE 44100
-
int auvia_match(struct device *, void *, void *);
void auvia_attach(struct device *, struct device *, void *);
int auvia_open(void *, int);
void auvia_close(void *);
-int auvia_query_encoding(void *addr, struct audio_encoding *fp);
+int auvia_query_encoding(void *, struct audio_encoding *);
+void auvia_set_params_sub(struct auvia_softc *, struct auvia_softc_chan *,
+ struct audio_params *);
int auvia_set_params(void *, int, int, struct audio_params *,
struct audio_params *);
int auvia_round_blocksize(void *, int);
@@ -131,6 +130,7 @@ struct cfattach auvia_ca = {
#define AUVIA_PLAY_BASE 0x00
#define AUVIA_RECORD_BASE 0x10
+/* *_RP_* are offsets from AUVIA_PLAY_BASE or AUVIA_RECORD_BASE */
#define AUVIA_RP_STAT 0x00
#define AUVIA_RPSTAT_INTR 0x03
#define AUVIA_RP_CONTROL 0x01
@@ -141,7 +141,7 @@ struct cfattach auvia_ca = {
#define AUVIA_RPCTRL_STOP 0x04
#define AUVIA_RPCTRL_EOL 0x02
#define AUVIA_RPCTRL_FLAG 0x01
-#define AUVIA_RP_MODE 0x02
+#define AUVIA_RP_MODE 0x02 /* 82c686 specific */
#define AUVIA_RPMODE_INTR_FLAG 0x01
#define AUVIA_RPMODE_INTR_EOL 0x02
#define AUVIA_RPMODE_STEREO 0x10
@@ -156,7 +156,16 @@ struct cfattach auvia_ca = {
#define VIA8233_RATEFMT_STEREO 0x00100000
#define VIA8233_RATEFMT_16BIT 0x00200000
-#define VIA_RP_DMAOPS_COUNT 0x0C
+#define VIA_RP_DMAOPS_COUNT 0x0c
+
+#define VIA8233_MP_BASE 0x40
+ /* STAT, CONTROL, DMAOPS_BASE, DMAOPS_COUNT are valid */
+#define VIA8233_OFF_MP_FORMAT 0x02
+#define VIA8233_MP_FORMAT_8BIT 0x00
+#define VIA8233_MP_FORMAT_16BIT 0x80
+#define VIA8233_MP_FORMAT_CHANNLE_MASK 0x70 /* 1, 2, 4, 6 */
+#define VIA8233_OFF_MP_SCRATCH 0x03
+#define VIA8233_OFF_MP_STOP 0x08
#define AUVIA_CODEC_CTL 0x80
#define AUVIA_CODEC_READ 0x00800000
@@ -164,6 +173,15 @@ struct cfattach auvia_ca = {
#define AUVIA_CODEC_PRIVALID 0x02000000
#define AUVIA_CODEC_INDEX(x) ((x)<<16)
+#define CH_WRITE1(sc, ch, off, v) \
+ bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (ch)->sc_base + (off), v)
+#define CH_WRITE4(sc, ch, off, v) \
+ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (ch)->sc_base + (off), v)
+#define CH_READ1(sc, ch, off) \
+ bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (ch)->sc_base + (off))
+#define CH_READ4(sc, ch, off) \
+ bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (ch)->sc_base + (off))
+
#define TIMEOUT 50
struct audio_hw_if auvia_hw_if = {
@@ -229,8 +247,12 @@ auvia_attach(struct device *parent, struct device *self, void *aux)
pcireg_t pr;
int r, i;
- if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_VIATECH_VT8233_AC97)
+ sc->sc_play.sc_base = AUVIA_PLAY_BASE;
+ sc->sc_record.sc_base = AUVIA_RECORD_BASE;
+ if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_VIATECH_VT8233_AC97) {
sc->sc_flags |= AUVIA_FLAGS_VT8233;
+ sc->sc_play.sc_base = VIA8233_MP_BASE;
+ }
if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_TYPE_IO, 0, &sc->sc_iot,
&sc->sc_ioh, NULL, &iosize, 0)) {
@@ -514,15 +536,57 @@ auvia_query_encoding(void *addr, struct audio_encoding *fp)
}
}
+void
+auvia_set_params_sub(struct auvia_softc *sc, struct auvia_softc_chan *ch,
+ struct audio_params *p)
+{
+ u_int32_t v;
+ u_int16_t regval;
+
+ if (!(sc->sc_flags & AUVIA_FLAGS_VT8233)) {
+ regval = (p->channels == 2 ? AUVIA_RPMODE_STEREO : 0)
+ | (p->precision * p->factor == 16 ?
+ AUVIA_RPMODE_16BIT : 0)
+ | AUVIA_RPMODE_INTR_FLAG | AUVIA_RPMODE_INTR_EOL
+ | AUVIA_RPMODE_AUTOSTART;
+ ch->sc_reg = regval;
+ } else if (ch->sc_base != VIA8233_MP_BASE) {
+ v = CH_READ4(sc, ch, VIA8233_RP_RATEFMT);
+ v &= ~(VIA8233_RATEFMT_48K | VIA8233_RATEFMT_STEREO
+ | VIA8233_RATEFMT_16BIT);
+
+ v |= VIA8233_RATEFMT_48K * (p->sample_rate / 20)
+ / (48000 / 20);
+ if (p->channels == 2)
+ v |= VIA8233_RATEFMT_STEREO;
+ if (p->precision == 16)
+ v |= VIA8233_RATEFMT_16BIT;
+
+ CH_WRITE4(sc, ch, VIA8233_RP_RATEFMT, v);
+ } else {
+ static const u_int32_t slottab[7] =
+ { 0, 0xff000011, 0xff000021, 0,
+ 0xff004321, 0, 0xff436521};
+
+ regval = (p->precision == 16
+ ? VIA8233_MP_FORMAT_16BIT : VIA8233_MP_FORMAT_8BIT)
+ | (p->channels << 4);
+ CH_WRITE1(sc, ch, VIA8233_OFF_MP_FORMAT, regval);
+ CH_WRITE4(sc, ch, VIA8233_OFF_MP_STOP, slottab[p->channels]);
+ }
+}
+
int
auvia_set_params(void *addr, int setmode, int usemode,
struct audio_params *play, struct audio_params *rec)
{
struct auvia_softc *sc = addr;
+ struct auvia_softc_chan *ch;
struct audio_params *p;
- u_int16_t regval;
- int mode, base;
+ struct ac97_codec_if* codec = sc->codec_if;
+ int reg, mode;
+ u_int16_t ext_id;
/* for mode in (RECORD, PLAY) */
for (mode = AUMODE_RECORD; mode != -1;
@@ -532,35 +596,58 @@ auvia_set_params(void *addr, int setmode, int usemode,
if (mode == AUMODE_PLAY) {
p = play;
- base = AUVIA_PLAY_BASE;
+ ch = &sc->sc_play;
+ reg = AC97_REG_PCM_FRONT_DAC_RATE;
} else {
p = rec;
- base = AUVIA_RECORD_BASE;
+ ch = &sc->sc_record;
+ reg = AC97_REG_PCM_LR_ADC_RATE;
}
- if (sc->sc_flags & AUVIA_FLAGS_VT8233) {
- u_int32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
- base + VIA8233_RP_RATEFMT) & ~(VIA8233_RATEFMT_48K
- | VIA8233_RATEFMT_STEREO | VIA8233_RATEFMT_16BIT);
-
- v |= VIA8233_RATEFMT_48K *
- (p->sample_rate / 20) / (48000 / 20);
-
- if (p->channels == 2)
- v |= VIA8233_RATEFMT_STEREO;
- if (p->precision == 16)
- v |= VIA8233_RATEFMT_16BIT;
-
- bus_space_write_4(sc->sc_iot, sc->sc_ioh,
- base + VIA8233_RP_RATEFMT, v);
+ if (ch->sc_base == VIA8233_MP_BASE && mode == AUMODE_PLAY) {
+ ext_id = codec->vtbl->get_caps(codec);
+ if (p->channels == 1) {
+ /* ok */
+ } else if (p->channels == 2) {
+ /* ok */
+ } else if (p->channels == 4
+ && ext_id & AC97_EXT_AUDIO_SDAC) {
+ /* ok */
+ } else if (p->channels == 6
+ && (ext_id & AC97_BITS_6CH) == AC97_BITS_6CH) {
+ /* ok */
+ } else {
+ return (EINVAL);
+ }
+ } else {
+ if (p->channels != 1 && p->channels != 2)
+ return (EINVAL);
}
- if (ac97_set_rate(sc->codec_if, p, mode))
+ if ((p->sample_rate < 4000 || p->sample_rate > 48000) ||
+ (p->precision != 8 && p->precision != 16))
return (EINVAL);
- if ((p->precision != 8 && p->precision != 16) ||
- (p->channels != 1 && p->channels != 2))
- return (EINVAL);
+ if (AC97_IS_FIXED_RATE(codec)) {
+ p->sample_rate = AC97_SINGLE_RATE;
+ } else {
+ if (codec->vtbl->set_rate(codec, reg, &p->sample_rate))
+ return (EINVAL);
+
+ if (ch->sc_base == VIA8233_MP_BASE &&
+ mode == AUMODE_PLAY) {
+ reg = AC97_REG_PCM_SURR_DAC_RATE;
+ if (p->channels >= 4
+ && codec->vtbl->set_rate(codec, reg,
+ &p->sample_rate))
+ return (EINVAL);
+ reg = AC97_REG_PCM_LFE_DAC_RATE;
+ if (p->channels == 6
+ && codec->vtbl->set_rate(codec, reg,
+ &p->sample_rate))
+ return (EINVAL);
+ }
+ }
p->factor = 1;
p->sw_code = 0;
@@ -605,17 +692,7 @@ auvia_set_params(void *addr, int setmode, int usemode,
default:
return (EINVAL);
}
-
- regval = (p->channels == 2 ? AUVIA_RPMODE_STEREO : 0)
- | (p->precision * p->factor == 16 ?
- AUVIA_RPMODE_16BIT : 0)
- | AUVIA_RPMODE_INTR_FLAG | AUVIA_RPMODE_INTR_EOL
- | AUVIA_RPMODE_AUTOSTART;
-
- if (mode == AUMODE_PLAY)
- sc->sc_play.sc_reg = regval;
- else
- sc->sc_record.sc_reg = regval;
+ auvia_set_params_sub(sc, ch, p);
}
return 0;
@@ -633,10 +710,10 @@ int
auvia_halt_output(void *addr)
{
struct auvia_softc *sc = addr;
+ struct auvia_softc_chan *ch = &(sc->sc_play);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
-
+ CH_WRITE1(sc, ch, AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
+ ch->sc_intr = NULL;
return 0;
}
@@ -645,10 +722,10 @@ int
auvia_halt_input(void *addr)
{
struct auvia_softc *sc = addr;
+ struct auvia_softc_chan *ch = &(sc->sc_record);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
-
+ CH_WRITE1(sc, ch, AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
+ ch->sc_intr = NULL;
return 0;
}
@@ -878,25 +955,21 @@ auvia_trigger_output(void *addr, void *start, void *end, int blksize,
ch->sc_intr = intr;
ch->sc_arg = arg;
- bus_space_write_4(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + AUVIA_RP_DMAOPS_BASE,
+ CH_WRITE4(sc, ch, AUVIA_RP_DMAOPS_BASE,
ch->sc_dma_ops_dma->map->dm_segs[0].ds_addr);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + AUVIA_RP_MODE, ch->sc_reg);
-
if (sc->sc_flags & AUVIA_FLAGS_VT8233) {
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + VIA8233_RP_DXS_LVOL, 0);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + VIA8233_RP_DXS_RVOL, 0);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + AUVIA_RP_CONTROL,
+ if (ch->sc_base != VIA8233_MP_BASE) {
+ CH_WRITE1(sc, ch, VIA8233_RP_DXS_LVOL, 0);
+ CH_WRITE1(sc, ch, VIA8233_RP_DXS_RVOL, 0);
+ }
+ CH_WRITE1(sc, ch, AUVIA_RP_CONTROL,
AUVIA_RPCTRL_START | AUVIA_RPCTRL_AUTOSTART |
AUVIA_RPCTRL_STOP | AUVIA_RPCTRL_EOL | AUVIA_RPCTRL_FLAG);
- } else
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
+ } else {
+ CH_WRITE1(sc, ch, AUVIA_RP_MODE, ch->sc_reg);
+ CH_WRITE1(sc, ch, AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
+ }
return 0;
}
@@ -923,25 +996,21 @@ auvia_trigger_input(void *addr, void *start, void *end, int blksize,
ch->sc_intr = intr;
ch->sc_arg = arg;
- bus_space_write_4(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + AUVIA_RP_DMAOPS_BASE,
- ch->sc_dma_ops_dma->map->dm_segs[0].ds_addr);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + AUVIA_RP_MODE, ch->sc_reg);
+ CH_WRITE4(sc, ch, AUVIA_RP_DMAOPS_BASE,
+ ch->sc_dma_ops_dma->map->dm_segs[0].ds_addr);
if (sc->sc_flags & AUVIA_FLAGS_VT8233) {
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + VIA8233_RP_DXS_LVOL, 0);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + VIA8233_RP_DXS_RVOL, 0);
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + AUVIA_RP_CONTROL,
+ if (ch->sc_base != VIA8233_MP_BASE) {
+ CH_WRITE1(sc, ch, VIA8233_RP_DXS_LVOL, 0);
+ CH_WRITE1(sc, ch, VIA8233_RP_DXS_RVOL, 0);
+ }
+ CH_WRITE1(sc, ch, AUVIA_RP_CONTROL,
AUVIA_RPCTRL_START | AUVIA_RPCTRL_AUTOSTART |
AUVIA_RPCTRL_STOP | AUVIA_RPCTRL_EOL | AUVIA_RPCTRL_FLAG);
- } else
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
-
+ } else {
+ CH_WRITE1(sc, ch, AUVIA_RP_MODE, ch->sc_reg);
+ CH_WRITE1(sc, ch, AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
+ }
return 0;
}
@@ -950,30 +1019,30 @@ int
auvia_intr(void *arg)
{
struct auvia_softc *sc = arg;
+ struct auvia_softc_chan *ch;
u_int8_t r;
int i = 0;
- r = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + AUVIA_RP_STAT);
+
+ ch = &sc->sc_record;
+ r = CH_READ1(sc, ch, AUVIA_RP_STAT);
if (r & AUVIA_RPSTAT_INTR) {
if (sc->sc_record.sc_intr)
sc->sc_record.sc_intr(sc->sc_record.sc_arg);
/* clear interrupts */
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_RECORD_BASE + AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
+ CH_WRITE1(sc, ch, AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
i++;
}
- r = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + AUVIA_RP_STAT);
+ ch = &sc->sc_play;
+ r = CH_READ1(sc, ch, AUVIA_RP_STAT);
if (r & AUVIA_RPSTAT_INTR) {
if (sc->sc_play.sc_intr)
sc->sc_play.sc_intr(sc->sc_play.sc_arg);
/* clear interrupts */
- bus_space_write_1(sc->sc_iot, sc->sc_ioh,
- AUVIA_PLAY_BASE + AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
+ CH_WRITE1(sc, ch, AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
i++;
}