[alsa-devel] Stale data interleaved in capture driver
My DMA is configured with a debug mode in which it sends an a simple increasing counter value in the format I wish to receive: an IRQ every 32Bytes (half-frame). it transfers 32KBytes of data, and then restarts from it's original offset. This is tested in bare-metal mode as working.
I wrote my handler as described in the high-frequency interrupts described here: http://www.alsa-project.org/~tiwai/writing-an-alsa-driver/ch05s07.html
My data is valid until 1/4 buffer full, then it interleaves stale (lower numbers) in, replacing the desired numbers. It's sporadic, and it appears to repeat, as if ALSA never returns to the 0-offset of dma_area. I've dumped a snip below. If the formatting is lost, I can post a google drive or an image somewhere.
Thanks, Rob
00004000: 8182 8183 8184 8185 00004008: 8186 8187 8188 8189 00004010: 8190 8191 8192 1 00004018: 2 3 4 5 00004020: 6 7 8 8201 00004028: 8202 8203 8204 8205 00004030: 8206 8207 8208 8209 00004038: 8210 8211 8212 8213 00004040: 8214 8215 8216 8217 00004048: 8218 8219 8220 8221 00004050: 8222 8223 8224 8225 00004058: 8226 8227 8228 8229 00004060: 8230 8231 8232 8233 00004068: 8234 8235 8236 8237 00004070: 8238 8239 8240 49 00004078: 50 51 52 53 00004080: 54 55 56 8249 00004088: 8250 8251 8252 8253 00004090: 8254 8255 8256 65 00004098: 66 67 68 69 000040a0: 70 71 72 73 000040a8: 74 75 76 77 000040b0: 78 79 80 8273 000040b8: 8274 8275 8276 8277 000040c0: 8278 8279 8280 8281 000040c8: 8282 8283 8284 8285 000040d0: 8286 8287 8288 8289 000040d8: 8290 8291 8292 8293 000040e0: 8294 8295 8296 8297 000040e8: 8298 8299 8300 8301 000040f0: 8302 8303 8304 8305 000040f8: 8306 8307 8308 8309 00004100: 8310 8311 8312 121
Rob Nertney wrote:
an IRQ every 32Bytes (half-frame)
I wrote my handler as described in the high-frequency interrupts described here: http://www.alsa-project.org/~tiwai/writing-an-alsa-driver/ch05s07.html
This does not make sense; you need a timer only when you do not have a proper interrupt. Why do you think you need to do it this way?
My data is valid until 1/4 buffer full, then it interleaves stale (lower numbers) in, replacing the desired numbers. It's sporadic, and it appears to repeat, as if ALSA never returns to the 0-offset of dma_area.
ALSA just reports what your driver tells it; this sounds like a bug in your driver or in the hardware.
Regards, Clemens
Thanks Clemens, sorry for the delay, this got sent to spam for some reason
I've made changes to remove the high-frequency. I originally tried that method because my DMA IRQ fires whenever it writes 1/2 frame (it doesn't have coalescing support yet). Ive noticed that whatever I set my buffer/periods to, i always get one good buffer-size chunk in the WAV file, then it displays the very first half-frame of data, then correctly continues onward, but interleaving two sets of incrementing data. I think it might be when I increment buf_pos for the pointer callback, or maybe something else.
Here's how I have the hardware/driver right now:
I have a DMA which is looping across 64Kbytes. I verified this is correctly in simulation. The DMA is configured to interrupt at 32Bytes (A half frame. My Full frame is 16ch, S32LE = 64Bytes).
The driver counts the 32Byte IRQs until it reaches one period worth. From what I can tell (please correct me if I'm wrong), the periods_min and periods_max are up to me to define, since i don't interrupt on a period boundary. I divide the 64K buffer into 8 periods of 8KB each. Since my frame is 64Bytes, that is 128 frames/period. Since the DMA fires at each half frame (due to FPGA configuration), This is 256 IRQs I need to count to, before I call snd_pcm_period_elapsed.
//pertinent snd_pcm_hardware lines: .buffer_bytes_max = 65536, .period_bytes_min = 65536/8, .period_bytes_max = 65536/8, .periods_min = 8, .periods_max = 8,
static irqreturn_t my_dma_irq(int irq, void *dev_id) { struct my_device *dev = dev_id; unsigned int last_ptr, size;
spin_lock(&dev->lock); dev->irq_count++; dev->buf_pos += 32; //32 bytes have been transferred
if(dev->buf_pos >= (dev->pcm_buffer_size)) dev->buf_pos = 0;
if(dev->irq_count == 256){ //256 IRQs elapsed==128 frames == 8Kbytes == 1/8 max_buffer spin_unlock(&dev->lock); snd_pcm_period_elapsed(dev->substream); spin_lock(&dev->lock); dev->irq_count = 0; } } handle_irq(); spin_unlock(&dev->lock); return IRQ_HANDLED;
On Sun, Jul 31, 2016 at 11:34 PM, Clemens Ladisch clemens@ladisch.de wrote:
Rob Nertney wrote:
an IRQ every 32Bytes (half-frame)
I wrote my handler as described in the high-frequency interrupts
described
here: http://www.alsa-project.org/~tiwai/writing-an-alsa-driver/ch05s07.html
This does not make sense; you need a timer only when you do not have a proper interrupt. Why do you think you need to do it this way?
My data is valid until 1/4 buffer full, then it interleaves stale (lower numbers) in, replacing the desired numbers. It's sporadic, and it appears to repeat, as if ALSA never returns to the 0-offset of dma_area.
ALSA just reports what your driver tells it; this sounds like a bug in your driver or in the hardware.
Regards, Clemens
Rob Nertney wrote:
I've made changes to remove the high-frequency. I originally tried that method because my DMA IRQ fires whenever it writes 1/2 frame (it doesn't have coalescing support yet).
The snd_pcm_hardware is supposed to describe the hardware.
I have a DMA which is looping across 64Kbytes.
This is buffer_bytes.
The DMA is configured to interrupt at 32Bytes
This is period_bytes.
From what I can tell (please correct me if I'm wrong), the periods_min and periods_max are up to me to define
They describe how often the *hardware* can interrupt.
since i don't interrupt on a period boundary.
Periods are *defined* as the data between two interrupts.
Mapping ALSA periods to something else makes sense only if the hardware cannot interrupt after a fixed number of bytes.
Regards, Clemens
Thanks Clemens. Remind me to buy you a beer next time I'm in Germany, or you're on the West Coast of the US :)
I think it was because of the IRQ counting I implemented the handler. I noticed in hardware that during a buffer_bytes loop-around, the IRQ handle time would go up drastically, and I would miss other IRQs from the DMA, breaking the offset that ALSA thought it was at, and where the DMA was actually at. I am running more tests tonight.
I've rewritten the DMA to fire an IRQ only at the write-completion of (buffer_bytes/2) and (buffer_bytes) (which will always be a multiple of my frame length). The pointer callback now reads a hardware frame-position too. I think this will emulate an actual sound card much more closely. The IRQ handler simply calls snd_pcm_period_elapsed, and the pointer callback simply returns the hardware frame count I am currently at in the buffer.
64K buffer / 64Byte frames = 1024 frames/buffer. 2 periods in the buffer = 512 Frames/IRQ
buffer_bytes_max = 65535 period_bytes_min = 32768 period_bytes_max = 32768 periods_min = 2 periods_max = 2
Hopefully my testing tonight goes well.
Thanks, Rob
On Thu, Aug 4, 2016 at 1:53 AM, Clemens Ladisch clemens@ladisch.de wrote:
Rob Nertney wrote:
I've made changes to remove the high-frequency. I originally tried that method because my DMA IRQ fires whenever it writes 1/2 frame (it doesn't have coalescing support yet).
The snd_pcm_hardware is supposed to describe the hardware.
I have a DMA which is looping across 64Kbytes.
This is buffer_bytes.
The DMA is configured to interrupt at 32Bytes
This is period_bytes.
From what I can tell (please correct me if I'm wrong), the periods_min and periods_max are up to me to define
They describe how often the *hardware* can interrupt.
since i don't interrupt on a period boundary.
Periods are *defined* as the data between two interrupts.
Mapping ALSA periods to something else makes sense only if the hardware cannot interrupt after a fixed number of bytes.
Regards, Clemens
It was just easier to code it to do an exact /2 rather than a programmable periodic value, which is what i'm going to implement next.
I notice that after 1 full buffer, the IRQ handle time (from entry to handled) for snd_pcm_period_elapsed is almost 1/2 of a second. Is that expected for large (64K) buffers, especially with only 2 periods?
Does the buffer copy at the period intervals for period sizes? or at the buffer_bytes size? If the copy is on the period size, then I really should implement the programmable periods in the DMA.
On Fri, Aug 5, 2016 at 1:18 AM, Clemens Ladisch clemens@ladisch.de wrote:
Rob Nertney wrote:
I've rewritten the DMA to fire an IRQ only at the write-completion of (buffer_bytes/2) and (buffer_bytes)
Is the DMA actually restricted to two interrupts per buffer? If not, your driver should support arbitrary period sizes.
Regards, Clemens
participants (2)
-
Clemens Ladisch
-
Rob Nertney