MFC (revision 1.24)

Add support for (latest) VIA VT8251 audio controller.

A slight difference of this chip from its previous siblings is that
it need a gentle "wake up" on every (full) DMA buffer completion to
avoid stalled interrupt handler.

Thanks to George Hartzell for permission on doing remote debugging.

PR:		i386/95949
Tested by:	[1] George Hartzel
          	myself (remotely)
Approved by:	re (hrs)

[1] http://lists.freebsd.org/pipermail/freebsd-multimedia/2006-April/004003.html
This commit is contained in:
ariff 2006-04-25 14:41:30 +00:00
parent 7735df0379
commit 9c393c5830

View File

@ -54,6 +54,7 @@ SND_DECLARE_FILE("$FreeBSD$");
#define VIA8233_REV_ID_8233A 0x40
#define VIA8233_REV_ID_8235 0x50
#define VIA8233_REV_ID_8237 0x60
#define VIA8233_REV_ID_8251 0x70
#define SEGS_PER_CHAN 2 /* Segments per channel */
#define NDXSCHANS 4 /* No of DXS channels */
@ -100,7 +101,7 @@ struct via_info {
struct ac97_info *codec;
unsigned int bufsz;
int dxs_src;
int dxs_src, dma_eol_wake;
struct via_chinfo pch[NDXSCHANS + NMSGDCHANS];
struct via_chinfo rch[NWRCHANS];
@ -711,16 +712,26 @@ static void
via_intr(void *p)
{
struct via_info *via = p;
int i, stat;
int i, reg, stat;
/* Poll playback channels */
snd_mtxlock(via->lock);
for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) {
if (via->pch[i].channel == NULL)
continue;
stat = via->pch[i].rbase + VIA_RP_STATUS;
if (via_rd(via, stat, 1) & SGD_STATUS_INTR) {
via_wr(via, stat, SGD_STATUS_INTR, 1);
reg = via->pch[i].rbase + VIA_RP_STATUS;
stat = via_rd(via, reg, 1);
if (stat & SGD_STATUS_INTR) {
if (via->dma_eol_wake && ((stat & SGD_STATUS_EOL) ||
!(stat & SGD_STATUS_ACTIVE))) {
via_wr(via,
via->pch[i].rbase + VIA_RP_CONTROL,
SGD_CONTROL_START |
SGD_CONTROL_AUTOSTART |
SGD_CONTROL_I_EOL |
SGD_CONTROL_I_FLAG, 1);
}
via_wr(via, reg, stat, 1);
snd_mtxunlock(via->lock);
chn_intr(via->pch[i].channel);
snd_mtxlock(via->lock);
@ -731,9 +742,19 @@ via_intr(void *p)
for (i = 0; i < NWRCHANS; i++) {
if (via->rch[i].channel == NULL)
continue;
stat = via->rch[i].rbase + VIA_RP_STATUS;
if (via_rd(via, stat, 1) & SGD_STATUS_INTR) {
via_wr(via, stat, SGD_STATUS_INTR, 1);
reg = via->rch[i].rbase + VIA_RP_STATUS;
stat = via_rd(via, reg, 1);
if (stat & SGD_STATUS_INTR) {
if (via->dma_eol_wake && ((stat & SGD_STATUS_EOL) ||
!(stat & SGD_STATUS_ACTIVE))) {
via_wr(via,
via->rch[i].rbase + VIA_RP_CONTROL,
SGD_CONTROL_START |
SGD_CONTROL_AUTOSTART |
SGD_CONTROL_I_EOL |
SGD_CONTROL_I_FLAG, 1);
}
via_wr(via, reg, stat, 1);
snd_mtxunlock(via->lock);
chn_intr(via->rch[i].channel);
snd_mtxlock(via->lock);
@ -769,6 +790,9 @@ via_probe(device_t dev)
case VIA8233_REV_ID_8237:
device_set_desc(dev, "VIA VT8237");
return BUS_PROBE_DEFAULT;
case VIA8233_REV_ID_8251:
device_set_desc(dev, "VIA VT8251");
return BUS_PROBE_DEFAULT;
default:
device_set_desc(dev, "VIA VT8233X"); /* Unknown */
return BUS_PROBE_DEFAULT;
@ -846,6 +870,7 @@ via_attach(device_t dev)
struct via_info *via = 0;
char status[SND_STATUSLEN];
int i, via_dxs_disabled, via_dxs_src, via_dxs_chnum, via_sgd_chnum;
uint32_t revid;
if ((via = malloc(sizeof *via, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
device_printf(dev, "cannot allocate softc\n");
@ -937,10 +962,21 @@ via_attach(device_t dev)
snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s",
rman_get_start(via->reg), rman_get_start(via->irq),PCM_KLDSTRING(snd_via8233));
revid = pci_get_revid(dev);
/*
* VIA8251 lost its interrupt after DMA EOL, and need
* a gentle spank on its face within interrupt handler.
*/
if (revid == VIA8233_REV_ID_8251)
via->dma_eol_wake = 1;
else
via->dma_eol_wake = 0;
/*
* Decide whether DXS had to be disabled or not
*/
if (pci_get_revid(dev) == VIA8233_REV_ID_8233A) {
if (revid == VIA8233_REV_ID_8233A) {
/*
* DXS channel is disabled. Reports from multiple users
* that it plays at half-speed. Do not see this behaviour