--- i810_audio.c.orig Fri Dec 28 01:41:26 2001 +++ i810_audio.c Tue Jan 1 20:51:45 2002 @@ -2,6 +2,9 @@ * Intel i810 and friends ICH driver for Linux * Alan Cox * + * SiS7012 code by + * Thomas Gschwind + * * Built from: * Low level code: Zach Brown (original nonworking i810 OSS driver) * Jaroslav Kysela (working ALSA driver) @@ -14,7 +17,7 @@ * Analog Devices (A major AC97 codec maker) * Intel Corp (you've probably heard of them already) * - * AC97 clues and assistance provided by + * AC97 clues and assistance provided by * Analog Devices * Zach 'Fufu' Brown * Jeff Garzik @@ -84,6 +87,7 @@ #include #include #include +#include #include #include @@ -102,6 +106,9 @@ #ifndef PCI_DEVICE_ID_INTEL_440MX #define PCI_DEVICE_ID_INTEL_440MX 0x7195 #endif +#ifndef PCI_DEVICE_ID_SI_7012 +#define PCI_DEVICE_ID_SI_7012 0x7012 +#endif static int ftsodell=0; static int strict_clocking=0; @@ -197,7 +204,7 @@ #define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI) -#define DRIVER_VERSION "0.04" +#define DRIVER_VERSION "0.05" /* magic numbers to protect our data structures */ #define I810_CARD_MAGIC 0x5072696E /* "Prin" */ @@ -220,7 +227,8 @@ ICH82901AB, INTEL440MX, INTELICH2, - INTELICH3 + INTELICH3, + SI7012 }; static char * card_names[] = { @@ -228,7 +236,8 @@ "Intel ICH 82901AB", "Intel 440MX", "Intel ICH2", - "Intel ICH3" + "Intel ICH3", + "SiS 7012" }; static struct pci_device_id i810_pci_tbl [] __initdata = { @@ -242,6 +251,8 @@ PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH2}, {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH3}, + {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7012, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, SI7012}, {0,} }; @@ -666,24 +677,37 @@ static inline unsigned i810_get_dma_addr(struct i810_state *state, int rec) { struct dmabuf *dmabuf = &state->dmabuf; - unsigned int civ, offset; - struct i810_channel *c; + struct i810_card *card = state->card; + unsigned int civ, picb, tries=2; + int port, port_picb; if (!dmabuf->enable) return 0; + if (rec) - c = dmabuf->read_channel; + port = card->iobase + dmabuf->read_channel->port; else - c = dmabuf->write_channel; + port = card->iobase + dmabuf->write_channel->port; + + /* picb and sr are swapped on SiS7012 */ + if (card->pci_id == PCI_DEVICE_ID_SI_7012) + port_picb = port + OFF_SR; + else + port_picb = port + OFF_PICB; + do { - civ = inb(state->card->iobase+c->port+OFF_CIV); - offset = (civ + 1) * dmabuf->fragsize - - 2 * inw(state->card->iobase+c->port+OFF_PICB); + civ = inb(port+OFF_CIV); + picb = inw(port_picb); /* CIV changed before we read PICB (very seldom) ? * then PICB was rubbish, so try again */ - } while (civ != inb(state->card->iobase+c->port+OFF_CIV)); - - return offset; + } while (unlikely(civ != inb(port+OFF_CIV) && --tries)); + if (!tries) picb=0; + + /* SiS7012 counts bytes, not samples */ + if (card->pci_id == PCI_DEVICE_ID_SI_7012) + return (civ + 1) * dmabuf->fragsize - picb; + else + return (civ + 1) * dmabuf->fragsize - 2 * picb; } //static void resync_dma_ptrs(struct i810_state *state, int rec) @@ -758,7 +782,10 @@ // wait for the card to acknowledge shutdown while( inb(card->iobase + PO_CR) != 0 ) ; // now clear any latent interrupt bits (like the halt bit) - outb( inb(card->iobase + PO_SR), card->iobase + PO_SR ); + if (card->pci_id == PCI_DEVICE_ID_SI_7012) + outb( inb(card->iobase + PO_PICB), card->iobase + PO_PICB ); + else + outb( inb(card->iobase + PO_SR), card->iobase + PO_SR ); outl( inl(card->iobase + GLOB_STA) & INT_PO, card->iobase + GLOB_STA); } @@ -922,6 +949,8 @@ sg->busaddr=virt_to_bus(dmabuf->rawbuf+dmabuf->fragsize*i); // the card will always be doing 16bit stereo sg->control=dmabuf->fragsamples; + if (state->card->pci_id == PCI_DEVICE_ID_SI_7012) + sg->control *= 2; sg->control|=CON_BUFPAD; // set us up to get IOC interrupts as often as needed to // satisfy numfrag requirements, no more @@ -961,9 +990,10 @@ static void __i810_update_lvi(struct i810_state *state, int rec) { struct dmabuf *dmabuf = &state->dmabuf; + struct i810_card *card = state->card; int x, port; - port = state->card->iobase; + port = card->iobase; if(rec) port += dmabuf->read_channel->port; else @@ -1055,7 +1085,7 @@ if(inb(state->card->iobase + PO_CIV) != inb(state->card->iobase + PO_LVI)) { printk(KERN_WARNING "i810_audio: DMA overrun on write\n"); - printk("i810_audio: CIV %d, LVI %d, hwptr %x, " + printk("i810_audio: CIV %d, LVI %d, hwptr %d, " "count %d\n", inb(state->card->iobase + PO_CIV), inb(state->card->iobase + PO_LVI), @@ -1106,9 +1136,8 @@ return -EBUSY; } - tmo = (dmabuf->dmasize * HZ) / dmabuf->rate; - tmo >>= 1; - if (!schedule_timeout(tmo ? tmo : 1) && tmo){ + tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 4); + if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { printk(KERN_ERR "i810_audio: drain_dac, dma timeout?\n"); break; } @@ -1152,7 +1181,10 @@ port+=c->port; - status = inw(port + OFF_SR); + if(card->pci_id == PCI_DEVICE_ID_SI_7012) + status = inw(port + OFF_PICB); + else + status = inw(port + OFF_SR); #ifdef DEBUG_INTERRUPTS printk("NUM %d PORT %X IRQ ( ST%d ", c->num, c->port, status); #endif @@ -1188,7 +1220,10 @@ #endif } } - outw(status & DMA_INT_MASK, port + OFF_SR); + if (card->pci_id == PCI_DEVICE_ID_SI_7012) + outw(status & DMA_INT_MASK, port + OFF_PICB); + else + outw(status & DMA_INT_MASK, port + OFF_SR); } #ifdef DEBUG_INTERRUPTS printk(")\n"); @@ -1270,18 +1305,18 @@ } continue; } + swptr = dmabuf->swptr; if (dmabuf->count > dmabuf->dmasize) { dmabuf->count = dmabuf->dmasize; } - cnt = dmabuf->count - dmabuf->fragsize; // this is to make the copy_to_user simpler below - if(cnt > (dmabuf->dmasize - swptr)) - cnt = dmabuf->dmasize - swptr; + cnt = dmabuf->dmasize - swptr; + if(cnt > (dmabuf->count - dmabuf->fragsize)) + cnt = dmabuf->count - dmabuf->fragsize; + spin_unlock_irqrestore(&card->lock, flags); - if (cnt > count) - cnt = count; if (cnt <= 0) { unsigned long tmo; if(!dmabuf->enable) { @@ -1291,11 +1326,10 @@ i810_update_lvi(state,1); if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; - return ret; + goto done; } /* This isnt strictly right for the 810 but it'll do */ - tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); - tmo >>= 1; + tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 4); /* There are two situations when sleep_on_timeout returns, one is when the interrupt is serviced correctly and the process is waked up by ISR ON TIME. Another is when timeout is expired, which means that @@ -1315,11 +1349,13 @@ } if (signal_pending(current)) { ret = ret ? ret : -ERESTARTSYS; - return ret; + goto done; } continue; } + if (cnt > count) + cnt = count; if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { if (!ret) ret = -EFAULT; goto done; @@ -1406,15 +1442,13 @@ dmabuf->count = 0; } cnt = dmabuf->dmasize - swptr; - if(cnt > (dmabuf->dmasize - dmabuf->count)) - cnt = dmabuf->dmasize - dmabuf->count; + if(cnt > (dmabuf->dmasize - dmabuf->count - dmabuf->fragsize)) + cnt = dmabuf->dmasize - dmabuf->count - dmabuf->fragsize; spin_unlock_irqrestore(&state->card->lock, flags); #ifdef DEBUG2 printk(KERN_INFO "i810_audio: i810_write: %d bytes available space\n", cnt); #endif - if (cnt > count) - cnt = count; if (cnt <= 0) { unsigned long tmo; // There is data waiting to be played @@ -1455,6 +1489,9 @@ } continue; } + + if (cnt > count) + cnt = count; if (copy_from_user(dmabuf->rawbuf+swptr,buffer,cnt)) { if (!ret) ret = -EFAULT; goto ret; @@ -1896,7 +1933,7 @@ i810_update_ptr(state); abinfo.fragsize = dmabuf->userfragsize; abinfo.fragstotal = dmabuf->userfrags; - abinfo.bytes = dmabuf->dmasize - dmabuf->count; + abinfo.bytes = dmabuf->dmasize - dmabuf->count - dmabuf->fragsize; abinfo.fragments = abinfo.bytes / dmabuf->userfragsize; spin_unlock_irqrestore(&state->card->lock, flags); #ifdef DEBUG