i810_audio
A message that is not in the archive (sent off-list or not yet received): [email protected]
Hi everybody,
Quoted 4 lines · Nathan Bryant
Can you have a look at Doug Ledford's 0.13 driver? this incorporates most or all of the fixes you mentioned, except for SiS support, and some other fixes; it hasn't been incorporated into the main kernel quite yet because it needs more testing.
I have integrated the SiS patches into Doug's code. Chances are that it works now also in combination with artsd. Can anybody test this please? And report your success (or failure)? In case it does not work you might want to change the fragsize to dmabuf->userfragsize inside the i810_poll function (line 1596). If I use dmabuf->userfragsize, however, I get loads of DMA buffer over/underruns in combination with xmms. According to the sepc, I think dmabuf->fragsize should be as correct as dmabuf->userfragsize. I have not found and minimum available space requirement in the poll man page or the OSS documentation I have?
The patch attached to this email is still relative to the drivers/sound/i810_audio.c file from the 2.4.17 kernel distribution. A patched i810_audio.c can be found at
http://www.infosys.tuwien.ac.at/Staff/tom/SiS7012/
Fixes I applied except for the SiS integration:
* drain_dac
Use interruptible_sleep_on_timeout instead of schedule_timeout. I think this is cleaner. Set the timeout to
(dmabuf->count * HZ * 2) / (dmabuf->rate * 4)
since we play dmabuf->rate*4 bytes per second (16bit samples stereo). Added stop_dac at the end. Otherwise the system gets locked up sometimes.
* i810_read, i810_write
Set the timeout to (dmabuf->size * HZ * 2) / (dmabuf->rate * 4) since we record / play dmabuf->rate*4 bytes per second (16bit samples stereo).
* SNDCTL_DSP_GETxPTR
Don't change the recording / playback buffer. A detailed explanation can be found within the patch
Please correct me, if any of the above is wrong.
Quoted 3 lines
i've put a lot of work into this driver and i'd like to see everyone working on i810 code continue to work off a single base of source code rather than ending up with yet another fork...
Thanks for the hint! Never was my plan; just did not know that Doug had more recent code than the code found in the standard kernel distribution.
Thomas
Signature · 4 lines
Thomas Gschwind Email: [email protected] Technische Universität Wien Argentinierstraße 8/E1841 Tel: +43 (1) 58801 ext. 18412 A-1040 Wien, Austria, Europe Fax: +43 (1) 58801 ext. 18491
i810_audio.c renamed from i810_audio.c.orig +399 -253
@@ -2,6 +2,9 @@* Intel i810 and friends ICH driver for Linux * Alan Cox <[email protected]> * +* SiS7012 code by +* Thomas Gschwind <[email protected]> +* * Built from: * Low level code: Zach Brown (original nonworking i810 OSS driver) * Jaroslav Kysela <[email protected]> (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 <linux/smp_lock.h> #include <linux/ac97_codec.h> #include <linux/wrapper.h> +#include <linux/compiler.h> #include <asm/uaccess.h> #include <asm/hardirq.h>
@@ -102,15 +106,19 @@#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 ; -static unsigned int clocking=48000 ; +static unsigned int clocking=0 ;static int spdif_locked=0 ;//#define DEBUG //#define DEBUG2 //#define DEBUG_INTERRUPTS +//#define DEBUG_MMAP #define ADC_RUNNING 1#define DAC_RUNNING 2
@@ -197,7 +205,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.14"/* magic numbers to protect our data structures */ #define I810_CARD_MAGIC 0x5072696E/* "Prin" */
@@ -220,7 +228,8 @@ICH82901AB ,INTEL440MX ,INTELICH2 , -INTELICH3 +INTELICH3 , +SI7012 }; static char * card_names[] = {
@@ -228,7 +237,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 +252,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,} };
@@ -357,39 +369,17 @@struct i810_channel *(*alloc_rec_pcm_channel)(struct i810_card *);struct i810_channel *(*alloc_rec_mic_channel)(struct i810_card *);void (*free_pcm_channel)(struct i810_card *,int chan); + +/* We have a *very* long init time possibly, so use this to block */ +/* attempts to open our devices before we are ready (stops oops'es) */ +int initializing; };static struct i810_card *devs =NULL ;static int i810_open_mixdev (struct inode *inode,struct file *file); -static int i810_ioctl_mixdev (struct inode *inode,struct file *file,unsigned int cmd, -unsigned long arg); - -static inline unsigned ld2 (unsigned int x) -{ -unsigned r =0 ; - -if (x >=0x10000 ) { - x >>=16 ; - r +=16 ; - } -if (x >=0x100 ) { - x >>=8 ; - r +=8 ; - } -if (x >=0x10 ) { - x >>=4 ; - r +=4 ; - } -if (x >=4 ) { - x >>=2 ; - r +=2 ; - } -if (x >=2 ) - r++; -return r; -} - +static int i810_ioctl_mixdev (struct inode *inode,struct file *file, +unsigned int cmd,unsigned long arg);static u16 i810_ac97_get (struct ac97_codec *dev,u8 reg);static void i810_ac97_set (struct ac97_codec *dev,u8 reg,u16 data);
@@ -666,47 +656,38 @@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; -} - -//static void resync_dma_ptrs(struct i810_state *state, int rec) -//{ -// struct dmabuf *dmabuf = &state->dmabuf; -// struct i810_channel *c; -// int offset; -// -// if(rec) { -// c = dmabuf->read_channel; -// } else { -// c = dmabuf->write_channel; -// } -// if(c==NULL) -// return; -// offset = inb(state->card->iobase+c->port+OFF_CIV); -// if(offset == inb(state->card->iobase+c->port+OFF_LVI)) -// offset++; -// offset *= dmabuf->fragsize; -// -// dmabuf->hwptr=dmabuf->swptr = 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) % dmabuf->dmasize; +else +return ((civ +1 ) * dmabuf->fragsize -2 * picb) % dmabuf->dmasize; +} +/* Stop recording (lock held) */ static inline void __stop_adc (struct i810_state *state) {
@@ -718,7 +699,10 @@// wait for the card to acknowledge shutdown while (inb (card->iobase +PI_CR ) !=0 ) ;// now clear any latent interrupt bits (like the halt bit) -outb (inb (card->iobase +PI_SR ), card->iobase +PI_SR ); +if (card->pci_id ==PCI_DEVICE_ID_SI_7012 ) +outb (inb (card->iobase +PI_PICB ), card->iobase +PI_PICB ); +else +outb (inb (card->iobase +PI_SR ), card->iobase +PI_SR );outl (inl (card->iobase +GLOB_STA ) &INT_PI , card->iobase +GLOB_STA ); }
@@ -732,21 +716,27 @@spin_unlock_irqrestore (&card->lock, flags); } -static void start_adc (struct i810_state *state) +static inline void __start_adc (struct i810_state *state) {struct dmabuf *dmabuf = &state->dmabuf; -struct i810_card *card = state->card; -unsigned long flags;if (dmabuf->count < dmabuf->dmasize && dmabuf->ready && !dmabuf->enable && (dmabuf->trigger &PCM_ENABLE_INPUT )) { -spin_lock_irqsave (&card->lock, flags); dmabuf->enable |=ADC_RUNNING ; -outb ((1 <<4 ) | (1 <<2 ) |1 , card->iobase +PI_CR ); -spin_unlock_irqrestore (&card->lock, flags); +outb ((1 <<4 ) | (1 <<2 ) |1 , state->card->iobase +PI_CR ); } } +static void start_adc (struct i810_state *state) +{ +struct i810_card *card = state->card; +unsigned long flags; + +spin_lock_irqsave (&card->lock, flags); +__start_adc (state); +spin_unlock_irqrestore (&card->lock, flags); +} +/* stop playback (lock held) */ static inline void __stop_dac (struct i810_state *state) {
@@ -758,7 +748,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 ); }
@@ -772,20 +765,25 @@spin_unlock_irqrestore (&card->lock, flags); } -static void start_dac (struct i810_state *state) +static inline void __start_dac (struct i810_state *state) {struct dmabuf *dmabuf = &state->dmabuf; -struct i810_card *card = state->card; -unsigned long flags;if (dmabuf->count >0 && dmabuf->ready && !dmabuf->enable && (dmabuf->trigger &PCM_ENABLE_OUTPUT )) { -spin_lock_irqsave (&card->lock, flags); dmabuf->enable |=DAC_RUNNING ; -outb ((1 <<4 ) | (1 <<2 ) |1 , card->iobase +PO_CR ); -spin_unlock_irqrestore (&card->lock, flags); +outb ((1 <<4 ) | (1 <<2 ) |1 , state->card->iobase +PO_CR ); } } +static void start_dac (struct i810_state *state) +{ +struct i810_card *card = state->card; +unsigned long flags; + +spin_lock_irqsave (&card->lock, flags); +__start_dac (state); +spin_unlock_irqrestore (&card->lock, flags); +}#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)#define DMABUF_MINORDER 1
@@ -805,6 +803,8 @@ dmabuf->ossfragsize = (PAGE_SIZE <<DMABUF_DEFAULTORDER )/dmabuf->ossmaxfrags; size = dmabuf->ossfragsize * dmabuf->ossmaxfrags; +if (dmabuf->rawbuf && (PAGE_SIZE << dmabuf->buforder) == size) +return 0 ;/* alloc enough to satisfy the oss params */ for (order =DMABUF_DEFAULTORDER ; order >=DMABUF_MINORDER ; order--) {if ( (PAGE_SIZE <<order) > size )
@@ -873,14 +873,19 @@ dmabuf->swptr = dmabuf->hwptr =0 ;spin_unlock_irqrestore (&state->card->lock, flags); -/* allocate DMA buffer if not allocated yet */ -if (dmabuf->rawbuf) -dealloc_dmabuf (state); +/* allocate DMA buffer, let alloc_dmabuf determine if we are already +* allocated well enough or if we should replace the current buffer +* (assuming one is already allocated, if it isn't, then allocate it). +*/ if ((ret =alloc_dmabuf (state)))return ret;/* FIXME: figure out all this OSS fragment stuff */ /* I did, it now does what it should according to the OSS API. DL */ +/* We may not have realloced our dmabuf, but the fragment size to +* fragment number ratio may have changed, so go ahead and reprogram +* things +*/ dmabuf->dmasize =PAGE_SIZE << dmabuf->buforder; dmabuf->numfrag =SG_LEN ; dmabuf->fragsize = dmabuf->dmasize/dmabuf->numfrag;
@@ -922,6 +927,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
@@ -935,7 +942,6 @@outl (virt_to_bus (&c->sg[0 ]), state->card->iobase+c->port+OFF_BDBAR );outb (0 , state->card->iobase+c->port+OFF_CIV );outb (0 , state->card->iobase+c->port+OFF_LVI ); - dmabuf->count =0 ;spin_unlock_irqrestore (&state->card->lock, flags);
@@ -969,31 +975,34 @@else port += dmabuf->write_channel->port; -if (dmabuf->mapped) { -if (rec) - dmabuf->swptr = (dmabuf->hwptr + dmabuf->dmasize - - dmabuf->count) % dmabuf->dmasize; -else - dmabuf->swptr = (dmabuf->hwptr + dmabuf->count) - % dmabuf->dmasize; - } -/* -* two special cases, count == 0 on write -* means no data, and count == dmasize -* means no data on read, handle appropriately +/* if we are currently stopped, then our CIV is actually set to our +* *last* sg segment and we are ready to wrap to the next. However, +* if we set our LVI to the last sg segment, then it won't wrap to +* the next sg segment, it won't even get a start. So, instead, when +* we are stopped, we set both the LVI value and also we increment +* the CIV value to the next sg segment to be played so that when +* we call start_{dac,adc}, things will operate properly */ -if (!rec && dmabuf->count ==0 ) { -outb (inb (port+OFF_CIV ),port+OFF_LVI ); -return ; - } -if (rec && dmabuf->count == dmabuf->dmasize) { -outb (inb (port+OFF_CIV ),port+OFF_LVI ); -return ; +if (!dmabuf->enable && dmabuf->ready) { +if (rec && dmabuf->count < dmabuf->dmasize && + (dmabuf->trigger &PCM_ENABLE_INPUT )) + { +outb ((inb (port+OFF_CIV )+1 )&31 , port+OFF_LVI ); +__start_adc (state); +while ( !(inb (port +OFF_CR ) & ((1 <<4 ) | (1 <<2 ))) ) ; + }else if (!rec && dmabuf->count && + (dmabuf->trigger &PCM_ENABLE_OUTPUT )) + { +outb ((inb (port+OFF_CIV )+1 )&31 , port+OFF_LVI ); +__start_dac (state); +while ( !(inb (port +OFF_CR ) & ((1 <<4 ) | (1 <<2 ))) ) ; + } } +/* swptr - 1 is the tail of our transfer */ x = (dmabuf->dmasize + dmabuf->swptr -1 ) % dmabuf->dmasize; x /= dmabuf->fragsize; -outb (x&31 , port+OFF_LVI ); +outb (x, port+OFF_LVI ); }static void i810_update_lvi (struct i810_state *state,int rec)
@@ -1020,7 +1029,9 @@/* update hardware pointer */ hwptr =i810_get_dma_addr (state,1 ); diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; -// printk("HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff); +#if defined(DEBUG_INTERRUPTS ) || defined(DEBUG_MMAP ) +printk ("ADC HWP %d,%d,%d\n" , hwptr, dmabuf->hwptr, diff); +#endif dmabuf->hwptr = hwptr; dmabuf->total_bytes += diff; dmabuf->count += diff;
@@ -1043,7 +1054,9 @@/* update hardware pointer */ hwptr =i810_get_dma_addr (state,0 ); diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; -// printk("HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff); +#if defined(DEBUG_INTERRUPTS ) || defined(DEBUG_MMAP ) +printk ("DAC HWP %d,%d,%d\n" , hwptr, dmabuf->hwptr, diff); +#endif dmabuf->hwptr = hwptr; dmabuf->total_bytes += diff; dmabuf->count -= diff;
@@ -1055,7 +1068,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),
@@ -1068,7 +1081,43 @@ } } -static int drain_dac (struct i810_state *state,int nonblock) +static inline int i810_get_free_write_space (struct i810_state *state) +{ +struct dmabuf *dmabuf = &state->dmabuf; +int free; + +i810_update_ptr (state); +// catch underruns during playback +if (dmabuf->count <0 ) { + dmabuf->count =0 ; + dmabuf->swptr = dmabuf->hwptr; + } + free = dmabuf->dmasize - dmabuf->count; + free -= (dmabuf->hwptr % dmabuf->fragsize); +if (free <0 ) +return (0 ); +return (free); +} + +static inline int i810_get_available_read_data (struct i810_state *state) +{ +struct dmabuf *dmabuf = &state->dmabuf; +int avail; + +i810_update_ptr (state); +// catch overruns during record +if (dmabuf->count > dmabuf->dmasize) { + dmabuf->count = dmabuf->dmasize; + dmabuf->swptr = dmabuf->hwptr; + } + avail = dmabuf->count; + avail -= (dmabuf->hwptr % dmabuf->fragsize); +if (avail <0 ) +return (0 ); +return (avail); +} + +static int drain_dac (struct i810_state *state) {DECLARE_WAITQUEUE (wait, current);struct dmabuf *dmabuf = &state->dmabuf;
@@ -1093,33 +1142,41 @@if (count <=0 )break ; -if (signal_pending (current)) -break ; - +/* +* This will make sure that our LVI is correct, that our +* pointer is updated, and that the DAC is running. We +* have to force the setting of dmabuf->trigger to avoid +* any possible deadlocks. +*/ + dmabuf->trigger =PCM_ENABLE_OUTPUT ;i810_update_lvi (state,0 ); -if (dmabuf->enable !=DAC_RUNNING ) -start_dac (state); -if (nonblock) { -remove_wait_queue (&dmabuf->wait, &wait); -set_current_state (TASK_RUNNING ); -return -EBUSY ; - } +if (signal_pending (current)) +break ; - tmo = (dmabuf->dmasize *HZ ) / dmabuf->rate; - tmo >>=1 ; -if (!schedule_timeout (tmo ? tmo :1 ) && tmo){ +/* +* set the timeout to exactly twice as long as it *should* +* take for the DAC to drain the DMA buffer +* TG: have to divide by (dmabuf->rate * 4) since +* we play dmabuf->rate * 4 byte/sec?!? +* TG: use interruptible_sleep_on_timeout this +* automatically wakes us up at the end of playback +* (interrupt) => wake_up +*/ + tmo = (count *HZ *2 ) / (dmabuf->rate *4 ); +if (!interruptible_sleep_on_timeout (&dmabuf->wait, tmo ? tmo :1 ) && tmo) {printk (KERN_ERR "i810_audio: drain_dac, dma timeout?\n" );break ; } } +set_current_state (TASK_RUNNING ); +/* TG: call stop_dac, otherwise we might crash when the device +* is closed at least when using a SiS7012. +*/ stop_dac (state); -synchronize_irq ();remove_wait_queue (&dmabuf->wait, &wait); -set_current_state (TASK_RUNNING );if (signal_pending (current))return -ERESTARTSYS ; -return 0 ; }
@@ -1152,12 +1209,20 @@ 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 if (status &DMA_INT_COMPLETE ) { +/* only wake_up() waiters if this interrupt signals +* us being beyond a userfragsize of data open or +* available, and i810_update_ptr() does that for +* us +*/ i810_update_ptr (state);#ifdef DEBUG_INTERRUPTS printk("COMP %d ", dmabuf->hwptr /
@@ -1166,6 +1231,7 @@ }if (status &DMA_INT_LVI ) { +/* wake_up() unconditionally on LVI */ i810_update_ptr (state);wake_up (&dmabuf->wait);#ifdef DEBUG_INTERRUPTS
@@ -1174,7 +1240,9 @@ }if (status &DMA_INT_DCH ) { +/* wake_up() unconditionally on DCH */ i810_update_ptr (state); +wake_up (&dmabuf->wait);if (dmabuf->enable &DAC_RUNNING ) count = dmabuf->count;else
@@ -1182,13 +1250,21 @@if (count >0 ) {outb (inb (port+OFF_CR ) |1 , port+OFF_CR ); }else { +if (dmabuf->enable &DAC_RUNNING ) +__stop_dac (state); +if (dmabuf->enable &ADC_RUNNING ) +__stop_adc (state); + dmabuf->enable =0 ;wake_up (&dmabuf->wait);#ifdef DEBUG_INTERRUPTS printk ("DCH - STOP " );#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" );
@@ -1254,7 +1330,6 @@return ret;if (!access_ok (VERIFY_WRITE , buffer, count))return -EFAULT ; - dmabuf->trigger &= ~PCM_ENABLE_OUTPUT ; ret =0 ;add_wait_queue (&dmabuf->wait, &waita);
@@ -1271,10 +1346,7 @@continue ; } swptr = dmabuf->swptr; -if (dmabuf->count > dmabuf->dmasize) { - dmabuf->count = dmabuf->dmasize; - } - cnt = dmabuf->count - dmabuf->fragsize; + cnt =i810_get_available_read_data (state);// this is to make the copy_to_user simpler below if (cnt > (dmabuf->dmasize - swptr)) cnt = dmabuf->dmasize - swptr;
@@ -1284,18 +1356,30 @@ cnt = count;if (cnt <=0 ) {unsigned long tmo; -if (!dmabuf->enable) { - dmabuf->trigger |=PCM_ENABLE_INPUT ; -start_adc (state); - } +/* +* Don't let us deadlock. The ADC won't start if +* dmabuf->trigger isn't set. A call to SETTRIGGER +* could have turned it off after we set it to on +* previously. +*/ + dmabuf->trigger =PCM_ENABLE_INPUT ; +/* +* This does three things. Updates LVI to be correct, +* makes sure the ADC is running, and updates the +* hwptr. +*/ 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 ; +/* Set the timeout to how long it would take to fill +* two of our buffers. If we haven't been woke up +* by then, then we know something is wrong. +* TG: have to divide by (dmabuf->rate * 4) since +* we play dmabuf->rate * 4 byte/sec +*/ + tmo = (dmabuf->dmasize *HZ *2 ) / (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,7 +1399,7 @@ }if (signal_pending (current)) { ret = ret ? ret : -ERESTARTSYS ; -return ret; + goto done; }continue ; }
@@ -1342,8 +1426,6 @@ ret += cnt; }i810_update_lvi (state,1 ); -if (!(dmabuf->enable &ADC_RUNNING )) -start_adc (state); done:set_current_state (TASK_RUNNING );remove_wait_queue (&dmabuf->wait, &waita);
@@ -1384,7 +1466,6 @@return ret;if (!access_ok (VERIFY_READ , buffer, count))return -EFAULT ; - dmabuf->trigger &= ~PCM_ENABLE_INPUT ; ret =0 ;add_wait_queue (&dmabuf->wait, &waita);
@@ -1402,12 +1483,14 @@ } swptr = dmabuf->swptr; -if (dmabuf->count <0 ) { - dmabuf->count =0 ; - } - cnt = dmabuf->dmasize - swptr; -if (cnt > (dmabuf->dmasize - dmabuf->count)) - cnt = dmabuf->dmasize - dmabuf->count; + cnt =i810_get_free_write_space (state); +/* Bound the maximum size to how much we can copy to the +* dma buffer before we hit the end. If we have more to +* copy then it will get done in a second pass of this +* loop starting from the beginning of the buffer. +*/ +if (cnt > (dmabuf->dmasize - swptr)) + cnt = dmabuf->dmasize - swptr;spin_unlock_irqrestore (&state->card->lock, flags);#ifdef DEBUG2
@@ -1417,20 +1500,23 @@ cnt = count;if (cnt <=0 ) {unsigned long tmo; -// There is data waiting to be played +/* +* Force the trigger setting since we would +* deadlock with it set any other way +*/ + dmabuf->trigger =PCM_ENABLE_OUTPUT ;i810_update_lvi (state,0 ); -if (!dmabuf->enable && dmabuf->count) { -/* force the starting incase SETTRIGGER has been used */ -/* to stop it, otherwise this is a deadlock situation */ - dmabuf->trigger |=PCM_ENABLE_OUTPUT ; -start_dac (state); - }if (file->f_flags &O_NONBLOCK ) {if (!ret) ret = -EAGAIN ; goto ret; } -/* Not strictly correct but works */ - tmo = (dmabuf->dmasize *HZ ) / (dmabuf->rate *4 ); +/* Set the timeout to how long it would take to fill +* two of our buffers. If we haven't been woke up +* by then, then we know something is wrong. +* TG: have to divide by (dmabuf->rate * 4) since +* we play dmabuf->rate * 4 byte/sec +*/ + tmo = (dmabuf->dmasize *HZ *2 ) / (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
@@ -1481,9 +1567,7 @@memset (dmabuf->rawbuf + swptr,'\0' , x); }i810_update_lvi (state,0 ); -if (!dmabuf->enable && dmabuf->count >= dmabuf->userfragsize) -start_dac (state); - ret: +ret:set_current_state (TASK_RUNNING );remove_wait_queue (&dmabuf->wait, &waita);
@@ -1497,27 +1581,32 @@struct dmabuf *dmabuf = &state->dmabuf;unsigned long flags;unsigned int mask =0 ; +int fragsize;if (!dmabuf->ready)return 0 ;poll_wait (file, &dmabuf->wait, wait);spin_lock_irqsave (&state->card->lock, flags); -i810_update_ptr (state); -if (file->f_mode &FMODE_READ && dmabuf->enable &ADC_RUNNING ) { -if (dmabuf->count >= (signed )dmabuf->fragsize) +/* TG: already called from i810_get_available_read_data and +* i810_get_free_write_space */ +/* i810_update_ptr(state); */ +/* TG: If we use userfragsize, xmms generates a DMA buffer +* overrun every other second */ +if (state->card->pci_id ==PCI_DEVICE_ID_SI_7012 ) + fragsize=dmabuf->fragsize; +else + fragsize=dmabuf->userfragsize; +if (dmabuf->enable &ADC_RUNNING || + dmabuf->trigger &PCM_ENABLE_INPUT ) { +if (i810_get_available_read_data (state) >= fragsize) mask |=POLLIN |POLLRDNORM ; } -if (file->f_mode &FMODE_WRITE && dmabuf->enable &DAC_RUNNING ) { -if (dmabuf->mapped) { -if (dmabuf->count >= (signed )dmabuf->fragsize) - mask |=POLLOUT |POLLWRNORM ; - }else { -if ((signed )dmabuf->dmasize >= dmabuf->count + (signed )dmabuf->fragsize) - mask |=POLLOUT |POLLWRNORM ; - } +if (dmabuf->enable &DAC_RUNNING || + dmabuf->trigger &PCM_ENABLE_OUTPUT ) { +if (i810_get_free_write_space (state) >= fragsize) + mask |=POLLOUT |POLLWRNORM ; }spin_unlock_irqrestore (&state->card->lock, flags); -return mask; }
@@ -1559,12 +1648,9 @@ size, vma->vm_page_prot)) goto out; dmabuf->mapped =1 ; -if (vma->vm_flags &VM_WRITE ) - dmabuf->count = dmabuf->dmasize; -else - dmabuf->count =0 ; + dmabuf->trigger =0 ; ret =0 ; -#ifdef DEBUG +#ifdef DEBUG_MMAP printk ("i810_audio: mmap'ed %ld bytes of data space\n" , size);#endif out:
@@ -1575,16 +1661,15 @@static int i810_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg) {struct i810_state *state = (struct i810_state *)file->private_data; +struct i810_channel *c =NULL ;struct dmabuf *dmabuf = &state->dmabuf;unsigned long flags;audio_buf_info abinfo;count_info cinfo;unsigned int i_glob_cnt; -int val =0 , mapped, ret; +int val =0 , ret;struct ac97_codec *codec = state->card->ac97_codec[0 ]; - mapped = ((file->f_mode &FMODE_WRITE ) && dmabuf->mapped) || - ((file->f_mode &FMODE_READ ) && dmabuf->mapped);#ifdef DEBUG printk ("i810_audio: i810_ioctl, arg=0x%x, cmd=" , arg ? *(int *)arg :0 );#endif
@@ -1601,13 +1686,23 @@#ifdef DEBUG printk ("SNDCTL_DSP_RESET\n" );#endif -/* FIXME: spin_lock ? */ +spin_lock_irqsave (&state->card->lock, flags);if (dmabuf->enable ==DAC_RUNNING ) { -stop_dac (state); + c = dmabuf->write_channel; +__stop_dac (state); }if (dmabuf->enable ==ADC_RUNNING ) { -stop_adc (state); + c = dmabuf->read_channel; +__stop_adc (state); + } +if (c !=NULL ) { +outb (2 , state->card->iobase+c->port+OFF_CR );/* reset DMA machine */ +outl (virt_to_bus (&c->sg[0 ]), state->card->iobase+c->port+OFF_BDBAR ); +outb (0 , state->card->iobase+c->port+OFF_CIV ); +outb (0 , state->card->iobase+c->port+OFF_LVI ); } + +spin_unlock_irqrestore (&state->card->lock, flags);synchronize_irq (); dmabuf->ready =0 ; dmabuf->swptr = dmabuf->hwptr =0 ;
@@ -1620,10 +1715,8 @@#endif if (dmabuf->enable !=DAC_RUNNING || file->f_flags &O_NONBLOCK )return 0 ; -drain_dac (state,0 ); - dmabuf->ready =0 ; - dmabuf->swptr = dmabuf->hwptr =0 ; - dmabuf->count = dmabuf->total_bytes =0 ; +drain_dac (state); + dmabuf->total_bytes =0 ;return 0 ;case SNDCTL_DSP_SPEED :/* set smaple rate */
@@ -1676,7 +1769,6 @@#endif if (get_user (val, (int *)arg))return -EFAULT ; -if (dmabuf->enable &DAC_RUNNING ) {stop_dac (state); }
@@ -1711,16 +1803,7 @@#endif if (get_user (val, (int *)arg))return -EFAULT ; - -switch ( val ) { -case AFMT_S16_LE : -break ; -case AFMT_QUERY : -default : - val =AFMT_S16_LE ; -break ; - } -return put_user (val, (int *)arg); +return put_user (AFMT_S16_LE , (int *)arg);case SNDCTL_DSP_CHANNELS :#ifdef DEBUG
@@ -1820,22 +1903,47 @@ dmabuf->ossfragsize =1 <<(val &0xffff ); dmabuf->ossmaxfrags = (val >>16 ) &0xffff ; -if (dmabuf->ossmaxfrags <=4 ) - dmabuf->ossmaxfrags =4 ; -else if (dmabuf->ossmaxfrags <=8 ) - dmabuf->ossmaxfrags =8 ; -else if (dmabuf->ossmaxfrags <=16 ) - dmabuf->ossmaxfrags =16 ; -else - dmabuf->ossmaxfrags =32 ; +if (!dmabuf->ossfragsize || !dmabuf->ossmaxfrags) +return -EINVAL ; +/* +* Bound the frag size into our allowed range of 256 - 4096 +*/ +if (dmabuf->ossfragsize <256 ) + dmabuf->ossfragsize =256 ; +else if (dmabuf->ossfragsize >4096 ) + dmabuf->ossfragsize =4096 ; +/* +* The numfrags could be something reasonable, or it could +* be 0xffff meaning "Give me as much as possible". So, +* we check the numfrags * fragsize doesn't exceed our +* 64k buffer limit, nor is it less than our 8k minimum. +* If it fails either one of these checks, then adjust the +* number of fragments, not the size of them. It's OK if +* our number of fragments doesn't equal 32 or anything +* like our hardware based number now since we are using +* a different frag count for the hardware. Before we get +* into this though, bound the maxfrags to avoid overflow +* issues. A reasonable bound would be 64k / 256 since our +* maximum buffer size is 64k and our minimum frag size is +* 256. On the other end, our minimum buffer size is 8k and +* our maximum frag size is 4k, so the lower bound should +* be 2. +*/ + +if (dmabuf->ossmaxfrags >256 ) + dmabuf->ossmaxfrags =256 ; +else if (dmabuf->ossmaxfrags <2 ) + dmabuf->ossmaxfrags =2 ; + val = dmabuf->ossfragsize * dmabuf->ossmaxfrags; -if (val <16384 ) - val =16384 ; -if (val >65536 ) - val =65536 ; - dmabuf->ossmaxfrags = val/dmabuf->ossfragsize; -if (dmabuf->ossmaxfrags<4 ) - dmabuf->ossfragsize = val/4 ; +while (val <8192 ) { + val <<=1 ; + dmabuf->ossmaxfrags <<=1 ; + } +while (val >65536 ) { + val >>=1 ; + dmabuf->ossmaxfrags >>=1 ; + } dmabuf->ready =0 ;#ifdef DEBUG printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val,
@@ -1853,13 +1961,13 @@i810_update_ptr (state); abinfo.fragsize = dmabuf->userfragsize; abinfo.fragstotal = dmabuf->userfrags; -if (dmabuf->mapped) - abinfo.bytes = dmabuf->count; -else - abinfo.bytes = dmabuf->dmasize - dmabuf->fragsize - dmabuf->count; +if (dmabuf->mapped) + abinfo.bytes = dmabuf->dmasize; +else + abinfo.bytes =i810_get_free_write_space (state); abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;spin_unlock_irqrestore (&state->card->lock, flags); -#ifdef DEBUG +#if defined(DEBUG ) || defined(DEBUG_MMAP )printk ("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n" , abinfo.bytes, abinfo.fragsize, abinfo.fragments, abinfo.fragstotal);#endif
@@ -1871,17 +1979,34 @@if (!dmabuf->ready && (val =prog_dmabuf (state,0 )) !=0 )return val;spin_lock_irqsave (&state->card->lock, flags); -i810_update_ptr (state); + val =i810_get_free_write_space (state); cinfo.bytes = dmabuf->total_bytes; cinfo.ptr = dmabuf->hwptr; - cinfo.blocks = (dmabuf->dmasize - dmabuf->count)/dmabuf->userfragsize; -if (dmabuf->mapped) { - dmabuf->count = (dmabuf->dmasize - - (dmabuf->count & (dmabuf->userfragsize-1 ))); +/* TG: can this possibly be correct? +* according to OSS doc, cinfo.blocks should return the +* number of fragment transitions since the previous call +* to this ioctl. Shouldn't it be better to set +* cinfo.blocks to 0 if we do not support this? +* Why would we want to update the playback pointer? This +* is not mentioned in the OSS doc! +* val==number_free_bytes, hence we increase swptr by val. +* This means that we set the buffer to full. It is +* likely, however, that the hwptr has increased since the +* call to this ioctl, hence part of the buffer is being +* played twice! +*/ +#ifndef USE_ORIGINAL_SNDCTL_DSP_GETxPTR_CODE + cinfo.blocks =0 ; +#else + cinfo.blocks = val/dmabuf->userfragsize; +if (dmabuf->mapped && (dmabuf->trigger &PCM_ENABLE_OUTPUT )) { + dmabuf->count += val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;__i810_update_lvi (state,0 ); } +#endif spin_unlock_irqrestore (&state->card->lock, flags); -#ifdef DEBUG +#if defined(DEBUG ) || defined(DEBUG_MMAP )printk ("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n" , cinfo.bytes, cinfo.blocks, cinfo.ptr, dmabuf->count);#endif
@@ -1893,13 +2018,12 @@if (!dmabuf->ready && (val =prog_dmabuf (state,1 )) !=0 )return val;spin_lock_irqsave (&state->card->lock, flags); -i810_update_ptr (state); + abinfo.bytes =i810_get_available_read_data (state); abinfo.fragsize = dmabuf->userfragsize; abinfo.fragstotal = dmabuf->userfrags; - abinfo.bytes = dmabuf->dmasize - dmabuf->count; abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;spin_unlock_irqrestore (&state->card->lock, flags); -#ifdef DEBUG +#if defined(DEBUG ) || defined(DEBUG_MMAP )printk ("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n" , abinfo.bytes, abinfo.fragsize, abinfo.fragments, abinfo.fragstotal);#endif
@@ -1911,16 +2035,21 @@if (!dmabuf->ready && (val =prog_dmabuf (state,0 )) !=0 )return val;spin_lock_irqsave (&state->card->lock, flags); -i810_update_ptr (state); + val =i810_get_available_read_data (state); cinfo.bytes = dmabuf->total_bytes; - cinfo.blocks = dmabuf->count/dmabuf->userfragsize; cinfo.ptr = dmabuf->hwptr; +/* TG: here, the same applies as for SNDCTL_DSP_GETOPTR */ +#ifndef USE_ORIGINAL_SNDCTL_DSP_GETxPTR_CODE + cinfo.blocks =0 ; +#else + cinfo.blocks = dmabuf->count/dmabuf->userfragsize;if (dmabuf->mapped) { dmabuf->count &= (dmabuf->userfragsize-1 );__i810_update_lvi (state,1 ); } +#endif spin_unlock_irqrestore (&state->card->lock, flags); -#ifdef DEBUG +#if defined(DEBUG ) || defined(DEBUG_MMAP )printk ("SNDCTL_DSP_GETIPTR %d, %d, %d, %d\n" , cinfo.bytes, cinfo.blocks, cinfo.ptr, dmabuf->count);#endif
@@ -1950,7 +2079,7 @@case SNDCTL_DSP_SETTRIGGER :if (get_user (val, (int *)arg))return -EFAULT ; -#ifdef DEBUG +#if defined(DEBUG ) || defined(DEBUG_MMAP )printk ("SNDCTL_DSP_SETTRIGGER 0x%x\n" , val);#endif if ( !(val &PCM_ENABLE_INPUT ) && dmabuf->enable ==ADC_RUNNING ) {
@@ -1960,7 +2089,7 @@stop_dac (state); } dmabuf->trigger = val; -if (val &PCM_ENABLE_OUTPUT ) { +if (val &PCM_ENABLE_OUTPUT && !(dmabuf->enable &DAC_RUNNING )) {if (!dmabuf->write_channel) { dmabuf->ready =0 ; dmabuf->write_channel = state->card->alloc_pcm_channel (state->card);
@@ -1970,13 +2099,18 @@if (!dmabuf->ready && (ret =prog_dmabuf (state,0 )))return ret;if (dmabuf->mapped) { - dmabuf->count = dmabuf->dmasize; -i810_update_lvi (state,0 ); - } -if (!dmabuf->enable && dmabuf->count > dmabuf->userfragsize) +spin_lock_irqsave (&state->card->lock, flags); +i810_update_ptr (state); + dmabuf->count =0 ; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count =i810_get_free_write_space (state); + dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; +__i810_update_lvi (state,0 ); +spin_unlock_irqrestore (&state->card->lock, flags); + }else start_dac (state); } -if (val &PCM_ENABLE_INPUT ) { +if (val &PCM_ENABLE_INPUT && !(dmabuf->enable &ADC_RUNNING )) {if (!dmabuf->read_channel) { dmabuf->ready =0 ; dmabuf->read_channel = state->card->alloc_rec_pcm_channel (state->card);
@@ -1986,12 +2120,14 @@if (!dmabuf->ready && (ret =prog_dmabuf (state,1 )))return ret;if (dmabuf->mapped) { +spin_lock_irqsave (&state->card->lock, flags); +i810_update_ptr (state); + dmabuf->swptr = dmabuf->hwptr; dmabuf->count =0 ; -i810_update_lvi (state,1 ); +spin_unlock_irqrestore (&state->card->lock, flags); } -if (!dmabuf->enable && dmabuf->count < - (dmabuf->dmasize - dmabuf->userfragsize)) -start_adc (state); +i810_update_lvi (state,1 ); +start_adc (state); }return 0 ;
@@ -2195,7 +2331,11 @@/* find an avaiable virtual channel (instance of /dev/dsp) */ while (card !=NULL ) { -for (i =0 ; i <NR_HW_CH ; i++) { +for (i =0 ; i <50 && card->initializing; i++) { +set_current_state (TASK_UNINTERRUPTIBLE ); +schedule_timeout (HZ /20 ); + } +for (i =0 ; i <NR_HW_CH && !card->initializing; i++) {if (card->states[i] ==NULL ) { state = card->states[i] = (struct i810_state *)kmalloc (sizeof (struct i810_state ),GFP_KERNEL );
@@ -2229,8 +2369,8 @@ card->states[i] =NULL ;;return -EBUSY ; } -i810_set_adc_rate (state,8000 ); dmabuf->trigger |=PCM_ENABLE_INPUT ; +i810_set_adc_rate (state,8000 ); }if (file->f_mode &FMODE_WRITE ) {if ((dmabuf->write_channel = card->alloc_pcm_channel (card)) ==NULL ) {
@@ -2241,13 +2381,13 @@/* Initialize to 8kHz? What if we don't support 8kHz? */ /* Let's change this to check for S/PDIF stuff */ + dmabuf->trigger |=PCM_ENABLE_OUTPUT ;if ( spdif_locked ) {i810_set_dac_rate (state, spdif_locked);i810_set_spdif_output (state,AC97_EA_SPSA_3_4 , spdif_locked); }else {i810_set_dac_rate (state,8000 ); } - dmabuf->trigger |=PCM_ENABLE_OUTPUT ; }/* set default sample format. According to OSS Programmer's Guide /dev/dsp
@@ -2276,7 +2416,7 @@/* stop DMA state machine and free DMA buffers/channels */ if (dmabuf->enable &DAC_RUNNING || (dmabuf->count && (dmabuf->trigger &PCM_ENABLE_OUTPUT ))) { -drain_dac (state,0 ); +drain_dac (state); }if (dmabuf->enable &ADC_RUNNING ) {stop_adc (state);
@@ -2344,13 +2484,18 @@int minor =MINOR (inode->i_rdev);struct i810_card *card = devs; -for (card = devs; card !=NULL ; card = card->next) -for (i =0 ; i <NR_AC97 ; i++) +for (card = devs; card !=NULL ; card = card->next) { +for (i =0 ; i <50 && card->initializing; i++) { +set_current_state (TASK_UNINTERRUPTIBLE ); +schedule_timeout (HZ /20 ); + } +for (i =0 ; i <NR_AC97 && !card->initializing; i++)if (card->ac97_codec[i] !=NULL && card->ac97_codec[i]->dev_mixer == minor) { file->private_data = card->ac97_codec[i];return 0 ; } + }return -ENODEV ; }
@@ -2696,6 +2841,7 @@ }memset (card,0 ,sizeof (*card)); + card->initializing =1 ; card->iobase =pci_resource_start (pci_dev,1 ); card->ac97base =pci_resource_start (pci_dev,0 ); card->pci_dev = pci_dev;
@@ -2752,7 +2898,8 @@ }pci_set_drvdata (pci_dev, card); -if (clocking ==48000 ) { +if (clocking ==0 ) { + clocking =48000 ;i810_configure_clocking (); }
@@ -2767,11 +2914,12 @@if (card->ac97_codec[i] !=NULL ) {unregister_sound_mixer (card->ac97_codec[i]->dev_mixer);kfree (card->ac97_codec[i]); + card->ac97_codec[i] =NULL ; }kfree (card);return -ENODEV ; } - + card->initializing =0 ;return 0 ; }
@@ -2789,6 +2937,7 @@if (card->ac97_codec[i] !=NULL ) {unregister_sound_mixer (card->ac97_codec[i]->dev_mixer);kfree (card->ac97_codec[i]); + card->ac97_codec[i] =NULL ; }unregister_sound_dsp (card->dev_audio);kfree (card);
@@ -2957,9 +3106,6 @@if (ftsodell !=0 ) {printk ("i810_audio: ftsodell is now a deprecated option.\n" ); } -if (clocking ==48000 ) { -i810_configure_clocking (); - }if (spdif_locked >0 ) {if (spdif_locked ==32000 || spdif_locked ==44100 || spdif_locked ==48000 ) {printk ("i810_audio: Enabling S/PDIF at sample rate %dHz.\n" , spdif_locked);
Quoted 5 lines · Thomas Gschwind
i810_audio.c renamed from i810_audio.c.orig +3
-0+/* TG: can this possibly be correct? +* according to OSS doc, cinfo.blocks should return the +* number of fragment transitions since the previous call
i810_audio.c line 1997
Quoted 10 lines
i810_audio.c renamed from i810_audio.c.orig +10
-0+* to this ioctl. Shouldn't it be better to set +* cinfo.blocks to 0 if we do not support this? +* Why would we want to update the playback pointer? This +* is not mentioned in the OSS doc! +* val==number_free_bytes, hence we increase swptr by val. +* This means that we set the buffer to full. It is +* likely, however, that the hwptr has increased since the +* call to this ioctl, hence part of the buffer is being +* played twice! +*/
It is correct. MMAP mode doesn't work without it, and we only care about this IOCTL during mmap mode. I think you'll find that it should have been setting blocks correctly in mmap mode, the api sequence look like this (pseudocode:)
open("/dev/dsp");
mmap(...);
// apps that use mmap mode (only Quake* AFAIK, maybe some other games) will write zeroes to fill mmap buffer at this point
ioctl(SNDCTL_DSP_SETTRIGGER, &PCM_ENABLE_OUTPUT); // this updates LVI to point to entire 64K buffer and starts DAC. card begins playing silence. at this point, dmabuf->count = 64K and begins counting down to zero.
ioctl(SNDCTL_DSP_GETOPTR, &foo); // Quake will call this right before it needs to actually start playing sound (several seconds after the initial SETTRIGGER call, long enough for the 64K of silence to be played and for the DAC to stop with DCH interrupt.) Since the game *needs* to have the current output pointer before it can start writing data to be played, we take this as a signal that it *intends to do so*, and update the LVI to play the next 64K of ring-buffer. Note that the mmap-api spec in OSS docs says that the hardware should be set to loop around the ring buffer *ad infinitum* in mmap mode, with no further input, so setting it to simply play the first 64K--intead of infinity--can't hurt. (We can't tell the hardware to loop around the buffer forever since there is no command to do so. The only other way to handle that would be to make the completition interrupt handlers take care of it, which is more error prone in my opinion, interrupts could be missed plus it pays to keep the interrupt handlers as simple as possible.) So we rely on the app calling GETOPTR often enough to keep the LVI chasing around the buffer - which is *has* to do anyway if it wants to play meaningful data. If it stops calling GETOPTR, that means it has frozen or slowed down to much to handle audio anyway. Neat solution, no? ;-)
Note that, after every call to GETOPTR, dmabuf->count gets set to 64K, and the returned value for blocks is based on dmabuf->count, so the semantics should actually be correct; we do:
cinfo.blocks = (dmabuf->dmasize - dmabuf->count)/dmabuf->userfragsize;
initially after GETOPTR, count = 64K, so blocks = (64K - 64K) / fragsize; ( = 0, correct according to docs) count winds down to zero, updated by i810_update_ptr: assume next call to GETOPTR occurs exactly 64K later:
blocks = (64K - 0) / userfragsize = the number of blocks played since last call
so, please put it back the way it was ;)
Quoted 12 lines
i810_audio.c renamed from i810_audio.c.orig +7
-0+#ifndef USE_ORIGINAL_SNDCTL_DSP_GETxPTR_CODE + cinfo.blocks =0 ; +#else + cinfo.blocks = val/dmabuf->userfragsize; +if (dmabuf->mapped && (dmabuf->trigger &PCM_ENABLE_OUTPUT )) { + dmabuf->count += val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;__i810_update_lvi(state, 0); }+#endif
Quoted 11 lines · Thomas Gschwind
I have integrated the SiS patches into Doug's code. Chances are that it works now also in combination with artsd. Can anybody test this please? And report your success (or failure)? In case it does not work you might want to change the fragsize to
dmabuf->userfragsizeinside thei810_pollfunction (line 1596). If I usedmabuf->userfragsize, however, I get loads of DMA buffer over/underruns in combination with xmms. According to the sepc, I thinkdmabuf->fragsizeshould be as correct asdmabuf->userfragsize. I have not found and minimum available space requirement in the poll man page or the OSS documentation I have?
Can you elaborate a little more? What do the buffer underflow sound like, does it stop playing (silence) for a very short period and then start again, or does it write ahead too far and overrite data that's currently playing, causing garbled or repeated sound? Your email to me describing this scenario left me a little confused.
The former would indicate simple scheduling latency--nothing that can be done about that--and the latter might be a problem with i810_update_ptr or get_free_write_space in your implementation. I haven't looked at your code too much yet...
I assume you're using xmms with artsd. What is your artsd fragment size? (Click on kde control center, go to sound/sound server/sound i/o)
I assume you're using the artsd default of 4096, which is larger than the actual hardware chunk size of 2048. If your problem is nothing more than latency-induced underruns, perhaps it would make more sense to use MIN(userfragsize, fragsize) to determine the return status for i810_poll. Doug, do you have any thoughts?
A buffer overrun is not the same as an underflow, even when we're talking about a ring buffer ;)
Quoted 8 lines
Fixes I applied except for the SiS integration:
*drain_dac
Useinterruptible_sleep_on_timeoutinstead ofschedule_timeout. I think this is cleaner. Set the timeout to
(dmabuf->count* HZ * 2) / (dmabuf->rate* 4)
since we play dmabuf->rate*4 bytes per second (16bit samples stereo). Addedstop_dacat the end. Otherwise the system gets locked up sometimes.
Some sort of fix in the drain_dac area is probably needed for the real Intel chips too; with 0.13 I was seeing a drain_dac: dma_timeout printk occasionally on my setup which I hadn't bothered to debug yet. Didn't hurt the machine and I figured it was maybe just dropped or latent interrupts, so I got lazy. ;)
Quoted 5 lines
*
i810_read,i810_write
Set the timeout to (dmabuf->size* HZ * 2) / (dmabuf->rate* 4) since we record / play dmabuf->rate*4 bytes per second (16bit samples stereo).
Does this solve a problem for your SiS chip? i810_write seemed to be working fine on my setup. (Intel hardware.)
Quoted 13 lines · Nathan Bryant
Quoted 11 lines · Thomas Gschwind
I have integrated the SiS patches into Doug's code. Chances are that it works now also in combination with artsd. Can anybody test this please? And report your success (or failure)? In case it does not work you might want to change the fragsize to
dmabuf->userfragsizeinside thei810_pollfunction (line 1596). If I usedmabuf->userfragsize, however, I get loads of DMA buffer over/underruns in combination with xmms. According to the sepc, I thinkdmabuf->fragsizeshould be as correct asdmabuf->userfragsize. I have not found and minimum available space requirement in the poll man page or the OSS documentation I have?
Quoted 1 line
Can you elaborate a little more? What do the buffer underflow sound like, does it stop playing (silence) for a very short period and then start again, or does it write ahead too far and overrite data that's currently playing, causing garbled or repeated sound? Your email to me describing this scenario left me a little confused.
Actually, this is moot. Making it user fragsize instead of userfragsize is the *wrong* thing to do. They are not the same and the semantics are pretty clear cut.
fragsize: The actual size of each dma ring buffer, of which we *always* have 32. This is a hardware limitation. It's used for various calculations, but is only ever set in prog_dmabuf().
userfragsize: The SETFRAGMENT ioctl allows the user to tell us how often they want interrupted with updates about output/input progress and what size block they expect that update to indicate. So, when the user requests a userfragsize of 4k, they are saying "Tell me about output completion with every 4k block you complete". They also tell us how many blocks they want allocated. The only requirement we have is that userfragsize * userfragcount (can't remember the variable name I actually call this in the code) == fragsize * 32. Then, we use userfragsize to tell us how often to program the ring buffers for completion interrupts. For instance, if we have 32 buffers of 512 bytes and userfragsize is 4k and userfragcount is 4, then we would program every 8th ring buffer to deliver a completion interrupt. That's the relationship between the two. So, in the poll routine, we need to alert userspace when userfragsize is reached, not fragsize. They might be the same, and they might not.
Quoted 5 lines
The former would indicate simple scheduling latency--nothing that can be done about that--and the latter might be a problem with
i810_update_ptrorget_free_write_spacein your implementation. I haven't looked at your code too much yet...I assume you're using xmms with artsd. What is your artsd fragment size? (Click on kde control center, go to
sound/soundserver/sound i/o)I assume you're using the artsd default of 4096, which is larger than the actual hardware chunk size of 2048. If your problem is nothing more than latency-induced underruns, perhaps it would make more sense to use
MIN(userfragsize, fragsize)to determine the return status fori810_poll. Doug, do you have any thoughts?
If the above doesn't answer any questions, then let me know and I'll elaborate further.
Quoted 11 lines
A buffer overrun is not the same as an underflow, even when we're talking about a ring buffer ;)
Quoted 8 lines
Fixes I applied except for the SiS integration:
*drain_dac
Useinterruptible_sleep_on_timeoutinstead ofschedule_timeout. I think this is cleaner. Set the timeout to (dmabuf->count* HZ *
2) / (dmabuf->rate* 4)
since we play dmabuf->rate*4 bytes per second (16bit samples stereo). Addedstop_dacat the end. Otherwise the system gets locked up
sometimes.
Quoted 9 lines
Some sort of fix in the
drain_dacarea is probably needed for the real Intel chips too; with 0.13 I was seeing adrain_dac:dma_timeoutprintk occasionally on my setup which I hadn't bothered to debug yet. Didn't hurt the machine and I figured it was maybe just dropped or latent interrupts, so I got lazy. ;)Quoted 5 lines
*
i810_read,i810_write
Set the timeout to (dmabuf->size* HZ * 2) / (dmabuf->rate* 4) since we record / play dmabuf->rate*4 bytes per second (16bit samples stereo).Does this solve a problem for your SiS chip?
i810_writeseemed to be working fine on my setup. (Intel hardware.)
The timeout in these areas is intentionally left over long. I don't mind it being close to the actual real expect time of completion, but do make sure it's a few ticks past completion time or else you might race against the completion interrupt depending on where you are in the current timer tick cycle.
Signature · 3 lines
Doug Ledford <[email protected]> http://people.redhat.com/dledford Please check my web site for aic7xxx updates/answers before e-mailing me about problems
Hi Thomas. I've been looking over the patch and trying to evaluate the various bug fixes that you mention against the patch. My comments are inline (and ignore the fact that I'm positive my cut and past job will mangle the patch itself, this is for comments, not for applying). I'm also replying to your comments.
Quoted 9 lines · Tom
Doug,
attched you can find the latest SiS7012 patch. This one is relative to your
i810_audio.cdriver.Fixes it applies except for the SiS integration:
*drain_dac
Useinterruptible_sleep_on_timeoutinstead ofschedule_timeout. I think this is cleaner.
This is fine.
Quoted 3 lines
Set the timeout to
(dmabuf->count* HZ * 2) / (dmabuf->rate* 4)
since we play dmabuf->rate*4 bytes per second (16bit samples stereo).
This is fine as well.
Quoted 1 line
Added
stop_dacat the end. Otherwise the system gets locked up sometimes.
This is (I think) a red herring. I would suspect that the lockup you are seeing is more likely to have something to do with the changes in i810_get_dma_address(). I'm referring to the change to the do {}while() loop in particular. Also, if the change you made in i810_get_dma_address() is needed for the SiS chipset, then there would appear to be a hardware bug that needs worked around. However, I think this is the wrong way to solve it. In prog_dmabuf(), we set the control bits on the device such that when it finishes the LVI, it's *suppossed* to stop playback. Now, the only way I can think of that the CIV register would update as fast as you can read it in i810_get_dma_address() is if at the end of LVI, the card went into a loop and the CIV is essentially running from 0 to 31 and going round and round without doing anything. In that case, the proper fix may actually be to go into the interrupt handler and on LVI interrupt with count==0 (for playback) call stop_dac and do similarly for record. This is because I suspect that we aren't ever getting the DCH Stopped interrupt on the SiS card. Of course, the DCH Stop interrupt causes us to call stop_{dac,adc} as appropriate, so the call to stop_dac in drain_dac is redundant if this code is working as advertised. I suspect that fixing things in the interrupt handler will make the stop_dac call in drain_dac and the change in i810_get_dma_address() both unnecessary. Can you test that for me?
Something else you might want to check into on the SiS chipset. I actually wish Intel had done the i810 with the SR and PICB registers swapped because you are suppossed to be able to do a 32bit read from the CIV register location and get CIV, LVI, and SR all in one go. With the SiS that means you could get CIV, LVI, and PCIB all in one go, making the while loop in i810_get_dma_address totally unnecessary on the SiS chipset (and making a slight boost to performance as well) as long as the SiS also supports a 32bit read from CIV.
Quoted 4 lines
*
i810_read,i810_write
Set the timeout to (dmabuf->size* HZ * 2) / (dmabuf->rate* 4) since we record / play dmabuf->rate*4 bytes per second (16bit samples stereo).
Again, fine.
Quoted 3 lines
*
SNDCTL_DSP_GETxPTR
Don't change the recording / playback buffer. A detailed explanation can be found within the patch
As commented on by Nathan, this isn't right.
Quoted 4 lines
Please correct me, if any of the above is wrong.
Thomas
Now, I've been talking with Ben LaHaise here at Red Hat and he pointed out to me that the wait queue handling in i810_read and likely also in i810_write are both wrong. Ben and I are looking into that and I'll put out a new version of the file that fixes those (obvious) problems shortly. When I've got my 0.14 version out, could you please repatch your stuff against it and look into the items I've mentioned here?
Signature · 3 lines
Doug Ledford <[email protected]> http://people.redhat.com/dledford Please check my web site for aic7xxx updates/answers before e-mailing me about problems
Quoted 8 lines · Doug Ledford
Quoted 4 lines
Fixes it applies except for the SiS integration:
*drain_dac
Useinterruptible_sleep_on_timeoutinstead ofschedule_timeout. I think this is cleaner.This is fine.
Oops, this is one of the mistakes Ben pointed out to me. interruptible_sleep_on_timeout(wait_queue head, timeout) overwrites the wait queue that we've already added ourselves to. schedule_timeout() does the right thing here. (interruptible_sleep_on_timeout doesn't really buy us much of anything we care about either, so it's find to stay with schedule_timeout)
Signature · 3 lines
Doug Ledford <[email protected]> http://people.redhat.com/dledford Please check my web site for aic7xxx updates/answers before e-mailing me about problems
Quoted 3 lines · Thomas Gschwind
I have integrated the SiS patches into Doug's code. Chances are that
I would just like to report a full sccess with the aforementioned patch. Hardware is a SIS7012, on a SIS735 chipset board. Even artsd seems to work fine.
Quoted 11 lines · Doug Ledford
Quoted 8 lines · Doug Ledford
Quoted 4 lines
Fixes it applies except for the SiS integration:
*drain_dac
Useinterruptible_sleep_on_timeoutinstead ofschedule_timeout. I think this is cleaner.
Quoted 9 lines
Quoted 1 line
This is fine.
Oops, this is one of the mistakes Ben pointed out to me.
interruptible_sleep_on_timeout(wait_queue head, timeout)overwrites the wait queue that we've already added ourselves to.schedule_timeout()does the right thing here. (interruptible_sleep_on_timeoutdoesn't really buy us much of anything we care about either, so it's find to stay withschedule_timeout)
OK, various clean ups made, and enough of the SiS code included that I think it should work, plus one change to the i810 interrupt handler that will (hopefully) render the other change you made to get_dma_addr and drain_dac unnecessary. If people could please download and test the new 0.14 version of the driver on my site, I would appreciate it.
http://people.redhat.com/dledford/i810_audio.c.gz
Signature · 3 lines
Doug Ledford <[email protected]> http://people.redhat.com/dledford Please check my web site for aic7xxx updates/answers before e-mailing me about problems
Hi,
Quoted 7 lines
OK, various clean ups made, and enough of the SiS code included that I think it should work, plus one change to the i810 interrupt handler that will (hopefully) render the other change you made to
get_dma_addranddrain_dacunnecessary. If people could please download and test the new 0.14 version of the driver on my site, I would appreciate it.
Hmmm, maybe way too much cleanups !? :)
-->
i810_audio.c: In function `i810_get_dma_addr':
i810_audio.c:658: warning: unused variable c' i810_audio.c: In function __stop_dac':
i810_audio.c:747: `PI_OR' undeclared (first use in this function) i810_audio.c:747: (Each undeclared identifier is reported only once i810_audio.c:747: for each function it appears in.) make[2]: *** [i810_audio.o] Error 1
make[1]: *** [_modsubdir_sound] Error 2
make: *** [_mod_drivers] Error 2
ps
just got a note from a friend that .13 has tendency to lockup with heavy network traffic in the same time, no oops, nothing, ..
Signature · 3 lines
Mario Mikočević (Mozgy) mozgy at hinet dot hr My favourite FUBAR ...
Quoted 12 lines · Mario Mikocevic
Hi,
Quoted 4 lines
OK, various clean ups made, and enough of the SiS code included that I think it should work, plus one change to the i810 interrupt handler that will (hopefully) render the other change you made to
get_dma_addranddrain_dacunnecessary. If people could please download and test the new 0.14 version of the driver on my site, I would appreciate it.Hmmm, maybe way too much cleanups !? :)
-->
Quoted 10 lines
i810_audio.c: In function `i810_get_dma_addr':
i810_audio.c:658: warning: unused variablec' i810_audio.c: In function__stop_dac':
i810_audio.c:747: `PI_OR' undeclared (first use in this function) i810_audio.c:747: (Each undeclared identifier is reported only once i810_audio.c:747: for each function it appears in.) make[2]: *** [i810_audio.o] Error 1
make[1]: *** [_modsubdir_sound] Error 2
make: *** [_mod_drivers] Error 2
Sorry. Version that compiles is now on my web site.
Quoted 5 lines
ps
just got a note from a friend that .13 has tendency to lockup with heavy network traffic in the same time, no oops, nothing, ..
Signature · 3 lines
Doug Ledford <[email protected]> http://people.redhat.com/dledford Please check my web site for aic7xxx updates/answers before e-mailing me about problems
Quoted 9 lines · Mario Mikocevic
Hi,
Quoted 4 lines
OK, various clean ups made, and enough of the SiS code included that I think it should work, plus one change to the i810 interrupt handler that will (hopefully) render the other change you made to
get_dma_addranddrain_dacunnecessary. If people could please download and test the new 0.14 version of the driver on my site, I would appreciate it.Hmmm, maybe way too much cleanups !? :)
Works fine for me with the attached patch. (artsd, mmap mode ok so far)
drivers/sound/i810_audio.c renamed from i810_audio.c.14 +1 -2
@@ -655,7 +655,6 @@ {struct dmabuf *dmabuf = &state->dmabuf;unsigned int civ, offset, port, port_picb, bytes =2 ; -struct i810_channel *c;if (!dmabuf->enable)return 0 ;
@@ -744,7 +743,7 @@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 +PI_OR ); +outb (inb (card->iobase +PO_SR ), card->iobase +PO_SR );outl (inl (card->iobase +GLOB_STA ) &INT_PO , card->iobase +GLOB_STA ); }
Quoted 6 lines · Doug Ledford
Quoted 2 lines
Added
stop_dacat the end. Otherwise the system gets locked up
sometimes.This is (I think) a red herring. I would suspect that the lockup you are seeing is more likely to have something to do with the changes in
i810_get_dma_address(). I'm referring to the change to the do {}while() loop in particular. Also, if the change you made ini810_get_dma_address()is needed for the SiS chipset, then there would appear to be a hardware bug that needs worked around. However, I think this is the wrong way to solve it. Inprog_dmabuf(), we set the control bits on the device such that when it finishes the LVI, it's *suppossed* to stop playback. Now, the only way I can think of that the CIV register would update as fast as you can read it ini810_get_dma_address()is if at the end of LVI, the card went into a loop and the CIV is essentially running from 0 to 31 and going round and round without doing anything. In that case, the proper fix may actually be to go into the interrupt handler and on LVI interrupt with count==0 (for playback) callstop_dacand do similarly for record. This is because I suspect that we aren't ever getting the DCH Stopped interrupt on the SiS card. Of course, the DCH Stop interrupt causes us to call stop_{dac,adc} as appropriate, so the call tostop_dacindrain_dacis redundant if this code is working as advertised. I suspect that fixing things in the interrupt handler will make thestop_daccall indrain_dacand the change ini810_get_dma_address()both unnecessary. Can you test that for me?
1) Is the LVI interrupt supposed to arrive when the chip *starts* playing the last buffer?
2) Does SiS actually do it this way?
If your theory on why the registers are spinning is correct, and if we receive the LVI interrupt with too much latency, your code will still deadlock, Doug. (The LVI interrupt handler calls update_ptr first thing, which calls get_dma_address.) Furthermore, if this turns out to be the case, the LVI IRQ handler uses dmabuf->count to determine whether to call stop_dac, and needs to call update_ptr to update dmabuf->count... so an explicit stop_dac might be needed elsewhere.
Even if the LVI interrupt comes at the beginning of the buffer, those 2048 bytes will play in 10.67 ms. Can we really guarantee that kind of latency?
Quoted 8 lines · Nathan Bryant
Quoted 6 lines · Doug Ledford
Quoted 2 lines
Added
stop_dacat the end. Otherwise the system gets locked up
sometimes.
Quoted 4 lines
Quoted 1 line
This is (I think) a red herring. I would suspect that the lockup you are seeing is more likely to have something to do with the changes in
i810_get_dma_address(). I'm referring to the change to the do {}while() loop in particular. Also, if the change you made ini810_get_dma_address()is needed for the SiS chipset, then there would appear to be a hardware bug that needs worked around. However, I think this is the wrong way to solve it. Inprog_dmabuf(), we set the control bits on the device such that when it finishes the LVI, it's *suppossed* to stop playback. Now, the only way I can think of that the CIV register would update as fast as you can read it ini810_get_dma_address()is if at the end of LVI, the card went into a loop and the CIV is essentially running from 0 to 31 and going round and round without doing anything. In that case, the proper fix may actually be to go into the interrupt handler and on LVI interrupt with count==0 (for playback) callstop_dacand do similarly for record. This is because I suspect that we aren't ever getting the DCH Stopped interrupt on the SiS card. Of course, the DCH Stop interrupt causes us to call stop_{dac,adc} as appropriate, so the call tostop_dacindrain_dacis redundant if this code is working as advertised. I suspect that fixing things in the interrupt handler will make thestop_daccall indrain_dacand the change ini810_get_dma_address()both unnecessary. Can you test that for me?1) Is the LVI interrupt supposed to arrive when the chip *starts* playing the last buffer?
No, when the last buffer is complete.
Quoted 1 line
2) Does SiS actually do it this way?
No clue. If they follow the spec, then yes.
Quoted 1 line
If your theory on why the registers are spinning is correct, and if we receive the LVI interrupt with too much latency, your code will still deadlock, Doug. (The LVI interrupt handler calls
update_ptrfirst thing, which callsget_dma_address.)
My theory about get_dma_address at this point is that the upper 3 bits of CIV are garbage and are subject to different states each time you read the register. Since I bounded the read by using &31 to mask off the garbage bits, that should no longer be an issue.
Quoted 1 line
Furthermore, if this turns out to be the case, the LVI IRQ handler uses
dmabuf->countto determine whether to callstop_dac, and needs to callupdate_ptrto updatedmabuf->count... so an explicitstop_dacmight be needed elsewhere.
I don't think so. See above.
Quoted 2 lines
Even if the LVI interrupt comes at the beginning of the buffer, those 2048 bytes will play in 10.67 ms. Can we really guarantee that kind of latency?
We don't need to. This is also where the spurious DMA Overrun on write errors were coming from. We *expect* to have a dma overrun on write if the last block was not completely filled (notice in i810_write() that we filled the remainder of the block with silence). So, we check if LVI == CIV when we appear to have an overrun. If they are the same, then no overrun, we just played the silence until the end of the buffer, then things stopped because this was the LVI, and all is good and right in the world. If they aren't the same (which they aren't if you are getting garbage in the upper 3 bits that is random), then we print out spurious messages. I think a lot of the problems that people have been chasing with the SiS chipset are this exact kind of thing. You'll also notice that support for the NVIDIA nForce Audio is now in 0.15. Based upon the fact that the guy from NVIDIA that contacted me had been seeing the same sort of random DMA Overrun on write messages, I'm guessing that both the SiS and the NVIDIA audio devices have this same garbage in the upper 3 bits of CIV register problem.
Signature · 3 lines
Doug Ledford <[email protected]> http://people.redhat.com/dledford Please check my web site for aic7xxx updates/answers before e-mailing me about problems
Quoted 6 lines · Nathan Bryant
1) Is the LVI interrupt supposed to arrive when the chip *starts* playing the last buffer?
2) Does SiS actually do it this way?If your theory on why the registers are spinning is correct, and if we receive the LVI interrupt with too much latency, your code will still deadlock, Doug. (The LVI interrupt handler calls
update_ptrfirst thing, which callsget_dma_address.) Furthermore, if this turns out to be the case, the LVI IRQ handler usesdmabuf->countto determine whether to callstop_dac, and needs to callupdate_ptrto updatedmabuf->count... so an explicitstop_dacmight be needed elsewhere.Even if the LVI interrupt comes at the beginning of the buffer, those 2048 bytes will play in 10.67 ms. Can we really guarantee that kind of latency?
Add to this, if SiS isn't sending DCH, and LVI arrives at the beginning of the last buffer, count is still > 0, so we don't call stop_dac. And we're right back where we started.
Hi,
Quoted 7 lines · Doug Ledford
OK, various clean ups made, and enough of the SiS code included that I think it should work, plus one change to the i810 interrupt handler that will (hopefully) render the other change you made to
get_dma_addranddrain_dacunnecessary. If people could please download and test the new 0.14 version of the driver on my site, I would appreciate it.
Well, ver 0.18 works fine for me _and_ for a friend of mine who _had_ problems with high network traffic while playing sound.
I haven't tried it on my webcast farm yet.
Intel 810 + AC97 Audio, version 0.18, 09:27:34 Jan 9 2002
PCI: Found IRQ 9 for device 00:1f.5
PCI: Sharing IRQ 9 with 00:1f.3
PCI: Sharing IRQ 9 with 02:09.0
PCI: Setting latency timer of device 00:1f.5 to 64
i810: Intel ICH2 found at IO 0xc400 and 0xc300, IRQ 9i810_audio: Audio Controller supports 6 channels.ac97_codec: AC97 Audio codec, id: 0x4352:0x5934 (Cirrus Logic CS4299 rev D)i810_audio: AC'97 codec 0 supports AMAP, total channels = 2
Signature · 3 lines
Mario Mikočević (Mozgy) mozgy at hinet dot hr My favourite FUBAR ...