On Thu, 2006-18-05 at 09:41 +0900, Charles Plessy wrote: 
> Hi all,
> 
> just a "me too" mail:
> 
> Le Thu, May 18, 2006 at 12:02:01AM +0200, Børge Holen a écrit :
> > I can also remember half way throught a ogg/mp3 playlist when it also 
> > scrambled the output, this has only happened ONCE.
> 
> I experience the same on my 8,1 powermac, but more systematically. It
> takes usually more than one hour of continuous listening before it
> happens, and then it happens sort of stochastically. I am not using
> anything else than xmms, so I did not figure out if it is a xmms or a
> driver problem. Stop/Starting the listening stops the scrambling.

This is exactly the problem that I have experienced since a while now.
The problem is that some interrupts get lost. This results in a broken
address calculation and the new data is written to the wrong place.

The attached patch fixes this by checking the 'frame_count'.
What I don't really understand is, that the first time the interrupt
gets executed, the frame_count is 8 _less_ of what I would have expected
My guess is that the dma controller reads the last 32 bytes, and then
the interrupt gets fired.

diff --git a/soundbus/i2sbus/i2sbus-pcm.c b/soundbus/i2sbus/i2sbus-pcm.c
index 9eadf83..8511234 100644
--- a/soundbus/i2sbus/i2sbus-pcm.c
+++ b/soundbus/i2sbus/i2sbus-pcm.c
@@ -440,6 +440,11 @@ static int i2sbus_pcm_trigger(struct i2s
                        return -ENXIO;
                }
 
+               /* get the current frame_count - 32 bytes. This is just guessed,
+                  but it seems that the interrupt triggers as soon as the last 
32 bytes
+                  are cached or something. */
+               pi->frame_count = in_le32(&i2sdev->intfregs->frame_count) - 
0x20 / (pi->substream->runtime->sample_bits / 8);
+
                /* wake up the chip with the next descriptor */
                out_le32(&pi->dbdma->control, (RUN|WAKE) | ((RUN|WAKE)<<16));
                /* off you go! */
@@ -488,13 +493,29 @@ static snd_pcm_uframes_t i2sbus_pcm_poin
 static inline void handle_interrupt(struct i2sbus_dev *i2sdev, int in)
 {
        struct pcm_info *pi;
+       u32 fc;
+       u32 delta;
 
        get_pcm_info(i2sdev, in, &pi, NULL);
        if (!pi->substream) {
                printk(KERN_INFO "i2sbus: got %s irq while not active!\n", 
in?"rx":"tx");
                return;
        }
-       pi->current_period = (pi->current_period+1) % (pi->periods);
+       
+       fc = in_le32(&i2sdev->intfregs->frame_count);
+       /* a counter overflow does not change the calculation. */
+       delta = fc - pi->frame_count;
+       
+       if (delta <= pi->substream->runtime->period_size) {
+               pi->current_period = pi->current_period + 1;
+               delta = 0;
+       } else while (delta >= pi->substream->runtime->period_size) {
+               pi->current_period = pi->current_period + 1;
+               delta = delta - pi->substream->runtime->period_size;
+       }
+       
+       pi->frame_count = fc - delta;
+       pi->current_period = pi->current_period % pi->periods;
        snd_pcm_period_elapsed(pi->substream);
 }
 
diff --git a/soundbus/i2sbus/i2sbus.h b/soundbus/i2sbus/i2sbus.h
index b054e02..f5d16aa 100644
--- a/soundbus/i2sbus/i2sbus.h
+++ b/soundbus/i2sbus/i2sbus.h
@@ -41,6 +41,7 @@ struct pcm_info {
        struct snd_pcm_substream *substream;
        int current_period;
        int periods;
+       u32 frame_count;
        struct dbdma_command_mem dbdma_ring;
        volatile struct dbdma_regs __iomem *dbdma;
 };


Reply via email to