Support for 4ch/6ch audio playback with VT8233/VT8235.

This commit is contained in:
kent 2002-10-16 15:27:28 +00:00
parent 706c1ce906
commit 565a6b3384
2 changed files with 164 additions and 121 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: auvia.c,v 1.26 2002/10/08 13:10:24 kent Exp $ */
/* $NetBSD: auvia.c,v 1.27 2002/10/16 15:27:28 kent Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
@ -47,7 +47,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: auvia.c,v 1.26 2002/10/08 13:10:24 kent Exp $");
__KERNEL_RCSID(0, "$NetBSD: auvia.c,v 1.27 2002/10/16 15:27:28 kent Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -90,7 +90,9 @@ int auvia_match(struct device *, struct cfdata *, 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);
@ -130,6 +132,7 @@ CFATTACH_DECL(auvia, sizeof (struct auvia_softc),
#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
@ -140,7 +143,7 @@ CFATTACH_DECL(auvia, sizeof (struct auvia_softc),
#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
@ -157,12 +160,30 @@ CFATTACH_DECL(auvia, sizeof (struct auvia_softc),
#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
#define AUVIA_CODEC_BUSY 0x01000000
#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 = {
@ -228,16 +249,19 @@ auvia_attach(struct device *parent, struct device *self, void *aux)
struct pci_attach_args *pa = aux;
struct auvia_softc *sc = (struct auvia_softc *) self;
const char *intrstr = NULL;
struct mixer_ctrl ctl;
pci_chipset_tag_t pc = pa->pa_pc;
pcitag_t pt = pa->pa_tag;
pci_intr_handle_t ih;
bus_size_t iosize;
pcireg_t pr;
int r, i;
int r;
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)) {
@ -319,37 +343,6 @@ auvia_attach(struct device *parent, struct device *self, void *aux)
return;
}
/* disable mutes */
for (i = 0; i < 4; i++) {
static struct {
char *class, *device;
} d[] = {
{ AudioCoutputs, AudioNmaster},
{ AudioCinputs, AudioNdac},
{ AudioCinputs, AudioNcd},
{ AudioCinputs, AudioNline},
{ AudioCrecord, AudioNvolume},
};
ctl.type = AUDIO_MIXER_ENUM;
ctl.un.ord = 0;
ctl.dev = sc->codec_if->vtbl->get_portnum_by_name(sc->codec_if,
d[i].class, d[i].device, AudioNmute);
auvia_set_port(sc, &ctl);
}
/* set a reasonable default volume */
ctl.type = AUDIO_MIXER_VALUE;
ctl.un.value.num_channels = 2;
ctl.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = \
ctl.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 199;
ctl.dev = sc->codec_if->vtbl->get_portnum_by_name(sc->codec_if,
AudioCoutputs, AudioNmaster, NULL);
auvia_set_port(sc, &ctl);
audio_attach_mi(&auvia_hw_if, sc, &sc->sc_dev);
}
@ -540,16 +533,58 @@ 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->hw_precision == 16
? VIA8233_MP_FORMAT_16BIT : VIA8233_MP_FORMAT_8BIT)
| (p->hw_channels << 4);
CH_WRITE1(sc, ch, VIA8233_OFF_MP_FORMAT, regval);
CH_WRITE4(sc, ch, VIA8233_OFF_MP_STOP, slottab[p->hw_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 reg, mode, base;
struct ac97_codec_if* codec;
int reg, mode;
u_int16_t ext_id;
codec = sc->codec_if;
/* for mode in (RECORD, PLAY) */
for (mode = AUMODE_RECORD; mode != -1;
mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
@ -558,43 +593,54 @@ auvia_set_params(void *addr, int setmode, int usemode,
if (mode == AUMODE_PLAY ) {
p = play;
base = AUVIA_PLAY_BASE;
ch = &sc->sc_play;
} else {
p = rec;
base = AUVIA_RECORD_BASE;
ch = &sc->sc_record;
}
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) {
ext_id = codec->vtbl->get_extcaps(codec);
if (p->channels == 1) {
/* ok */
} else if (p->channels == 2) {
/* ok */
} else if (p->channels == 4
&& ext_id & AC97_EXT_AUDIO_SDAC) {
/* ok */
#define BITS_6CH (AC97_EXT_AUDIO_SDAC | AC97_EXT_AUDIO_CDAC | AC97_EXT_AUDIO_LDAC)
} else if (p->channels == 6
&& (ext_id & BITS_6CH) == BITS_6CH) {
/* ok */
} else {
return (EINVAL);
}
} else {
if (p->channels != 1 && p->channels != 2)
return (EINVAL);
}
if (p->sample_rate < 4000 || p->sample_rate > 48000 ||
(p->precision != 8 && p->precision != 16) ||
(p->channels != 1 && p->channels != 2))
(p->precision != 8 && p->precision != 16))
return (EINVAL);
reg = mode == AUMODE_PLAY ?
AC97_REG_PCM_FRONT_DAC_RATE : AC97_REG_PCM_LR_ADC_RATE;
if (IS_FIXED_RATE(sc->codec_if)) {
if (IS_FIXED_RATE(codec)) {
/* Enable aurateconv */
p->hw_sample_rate = AC97_SINGLE_RATE;
} else {
if (sc->codec_if->vtbl->set_rate(sc->codec_if, reg,
&p->sample_rate))
if (codec->vtbl->set_rate(codec, reg, &p->sample_rate))
return (EINVAL);
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);
}
@ -602,14 +648,19 @@ auvia_set_params(void *addr, int setmode, int usemode,
p->sw_code = 0;
switch (p->encoding) {
case AUDIO_ENCODING_SLINEAR_BE:
if (p->precision == 16)
if (p->precision == 16) {
p->sw_code = swap_bytes;
else
p->hw_encoding = AUDIO_ENCODING_SLINEAR_LE;
} else {
p->sw_code = change_sign8;
p->hw_encoding = AUDIO_ENCODING_ULINEAR;
}
break;
case AUDIO_ENCODING_SLINEAR_LE:
if (p->precision != 16)
if (p->precision != 16) {
p->sw_code = change_sign8;
p->hw_encoding = AUDIO_ENCODING_ULINEAR;
}
break;
case AUDIO_ENCODING_ULINEAR_BE:
if (p->precision == 16) {
@ -617,41 +668,45 @@ auvia_set_params(void *addr, int setmode, int usemode,
p->sw_code = swap_bytes_change_sign16_le;
else
p->sw_code = change_sign16_swap_bytes_le;
p->hw_encoding = AUDIO_ENCODING_SLINEAR_LE;
}
break;
case AUDIO_ENCODING_ULINEAR_LE:
if (p->precision == 16)
if (p->precision == 16) {
p->sw_code = change_sign16_le;
p->hw_encoding = AUDIO_ENCODING_SLINEAR_LE;
}
break;
case AUDIO_ENCODING_ULAW:
if (p->precision != 8)
return (EINVAL);
if (mode == AUMODE_PLAY) {
p->factor = 2;
p->sw_code = mulaw_to_slinear16_le;
} else
p->hw_encoding = AUDIO_ENCODING_SLINEAR_LE;
p->hw_precision = 16;
} else {
p->sw_code = ulinear8_to_mulaw;
p->hw_encoding = AUDIO_ENCODING_ULINEAR;
}
break;
case AUDIO_ENCODING_ALAW:
if (p->precision != 8)
return (EINVAL);
if (mode == AUMODE_PLAY) {
p->factor = 2;
p->sw_code = alaw_to_slinear16_le;
} else
p->hw_encoding = AUDIO_ENCODING_SLINEAR_LE;
p->hw_precision = 16;
} else {
p->sw_code = ulinear8_to_alaw;
p->hw_encoding = AUDIO_ENCODING_ULINEAR;
}
break;
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;
@ -669,10 +724,9 @@ 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);
return 0;
}
@ -681,10 +735,9 @@ 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_PLAY_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
CH_WRITE1(sc, ch, AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
return 0;
}
@ -949,24 +1002,20 @@ auvia_trigger_output(void *addr, void *start, void *end,
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);
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_MODE, ch->sc_reg);
bus_space_write_1(sc->sc_iot, sc->sc_ioh,
AUVIA_PLAY_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
CH_WRITE1(sc, ch, AUVIA_RP_MODE, ch->sc_reg);
CH_WRITE1(sc, ch, AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
}
return 0;
@ -996,24 +1045,18 @@ auvia_trigger_input(void *addr, void *start, void *end,
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);
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,
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_MODE, ch->sc_reg);
bus_space_write_1(sc->sc_iot, sc->sc_ioh,
AUVIA_RECORD_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
CH_WRITE1(sc, ch, AUVIA_RP_MODE, ch->sc_reg);
CH_WRITE1(sc, ch, AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
}
return 0;
@ -1024,32 +1067,31 @@ int
auvia_intr(void *arg)
{
struct auvia_softc *sc = arg;
struct auvia_softc_chan *ch;
u_int8_t r;
int rval;
rval = 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);
rval = 1;
}
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);
rval = 1;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: auviavar.h,v 1.4 2002/10/08 13:10:24 kent Exp $ */
/* $NetBSD: auviavar.h,v 1.5 2002/10/16 15:27:28 kent Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
@ -47,6 +47,7 @@ struct auvia_softc_chan {
struct auvia_dma_op *sc_dma_ops;
struct auvia_dma *sc_dma_ops_dma;
u_int16_t sc_dma_op_count;
int sc_base;
u_int16_t sc_reg;
};