Hi,

It seems, the ICH3 chipset (82801CA/CAM AC'97) in my laptop doesn't
behave according the Intel's specification: 'FIFOE interrupts' are
generated on buffer underrun/overrun even though FEIE bit is not set in
the control register. It doesn't happen too often, but when it does
happen the driver's idea of the hardware state gets 'desynchronised'
with the actual state, as interrupt handler doesn't recognise this
condition and preparing next buffer fragment increases card's LVI
register while the CIV register remains without changes - the card had
not finished processing the current fragment yet. In the result card's
CIV and LVI registers indicate the same buffer fragment, and from this
point on every buffer fragment prepared by driver is effectively the
last-and-only valid fragment available to the hardware. Which
p-p-p-pops!

To summarise: hardware generates an interrupt when it should not - the
driver should handle it (the simplest solution: ignore). The attached
patch:
 - adds interrupt-on-FIFOE handling(/ignoring) to handler routine

Possibly it addresses/resolves #0000086, #0000145, #0000131, ...?

Two notes:
 - although the solution is quite general and should not break things on
other chipset versions, I've only tested it (successfully!) on my ICH3;
 - I'm not sure how does it affect input devices - the driver initial
intention is not to handle this kind of interrupts anyway, but well...


I hope this helps and makes some of you as happy as, finally, me!

Arek

Index: alsa-kernel/pci/intel8x0.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/intel8x0.c,v
retrieving revision 1.135
diff -b -p -u -r1.135 intel8x0.c
--- alsa-kernel/pci/intel8x0.c	14 Apr 2004 17:24:32 -0000	1.135
+++ alsa-kernel/pci/intel8x0.c	20 Apr 2004 14:51:41 -0000
@@ -777,9 +777,13 @@ static inline void snd_intel8x0_update(i
 	civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
 	if (civ == ichdev->civ) {
 		// snd_printd("civ same %d\n", civ);
+		if (igetbyte(chip, port + ICH_REG_OFF_SR) & ICH_FIFOE) {
+			step = 0;
+		} else {
 		step = 1;
 		ichdev->civ++;
 		ichdev->civ &= ICH_REG_LVI_MASK;
+		}
 	} else {
 		step = civ - ichdev->civ;
 		if (step < 0)
@@ -789,6 +793,7 @@ static inline void snd_intel8x0_update(i
 		ichdev->civ = civ;
 	}
 
+	if (step > 0) {
 	ichdev->position += step * ichdev->fragsize1;
 	ichdev->position %= ichdev->size;
 	ichdev->lvi += step;
@@ -808,6 +813,7 @@ static inline void snd_intel8x0_update(i
 		spin_unlock(&chip->reg_lock);
 		snd_pcm_period_elapsed(ichdev->substream);
 		spin_lock(&chip->reg_lock);
+		}
 	}
 	iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
 }

Reply via email to