The odd/even trick makes the SAA7146A automatically switch between two buffers. The ugly side of this is that you have to busy-wait for it to really switch buffers and then _copy_ the data out of the DMA buffer.
Yep, I tried doing a busy wait before I came up with the solution to infer the pointer position from the olddma pointer. I did a patch which catches the suspect pointer and just busy waits until it has moved to a different location. It often takes several microseconds for the DMA pointer to make up its mind.
I have attached the patch against the dvb-kernel code for reference in case anyone wants to try it out. I don't recommend it since it doesn't appear to work any better than the solution I posted earlier and it consumes a few more CPU cycles doing the busy waits.
Jon
diff -urw cvs/dvb-kernel/linux/drivers/media/dvb/ttpci/budget-core.c
dvb-kernel/linux/drivers/media/dvb/ttpci/budget-core.c
--- cvs/dvb-kernel/linux/drivers/media/dvb/ttpci/budget-core.c 2003-06-26
18:42:36.000000000 +0100
+++ dvb-kernel/linux/drivers/media/dvb/ttpci/budget-core.c 2003-08-30
01:25:11.000000000 +0100
@@ -68,6 +68,18 @@
u32 olddma = budget->ttbp;
u32 newdma = saa7146_read(budget->dev, PCI_VDP3);
+#define MAX_COUNT 100
+ int count = MAX_COUNT;
+ while ((( newdma == 0) || (newdma == TS_BUFLEN / 2)) && (count != 0)){
+ /* DMA pointer looks suspect */
+ udelay(1);
+ newdma = saa7146_read(budget->dev, PCI_VDP3);
+ count--;
+ }
+ if (count < (MAX_COUNT/2)) {
+ ERR(("DMA stuck for %dus\n", MAX_COUNT - count));
+ }
+
/* nearest lower position divisible by 188 */
newdma -= newdma % 188;
