misc: rp1-pio: two small fixes and support for cyclic output DMA - #7663
hansverkuil wants to merge 3 commits into
Conversation
|
This looks sensible to me. Just in case there are other use cases in future I'd prefer |
|
It makes sense to rename cyclic_tx to tx. I'll wait until #7664 is merged, then I'll rebase and update those issues. checkpatch complains about the Assisted-by tag: do you want me to drop those and just mention it in the commit log? Or not mention the AI use at all? I'm not sure what the guidelines are for AI use in the RPi kernel repo. |
|
I've merged #7664 now. I'm not bothered about the checkpatch complaint - the 7.2 checkpatch is happy with it - it's up to you whether you credit the AI. |
In the case of cyclic DMA it was possible to accept < 2 buffers or a zero buffer size. Check this and return -EINVAL in that case. Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org> Assisted-by: Claude-Code:claude-opus-5
07c3292 to
a7c3ab1
Compare
|
Rebased and renamed cyclic_tx to tx, which was a good change as it made the code a bit easier to understand. |
pelwell
left a comment
There was a problem hiding this comment.
I'm going to paste Claude's review of the second commit verbatim, because I'll miss some nuance if I rewrite it:
- The start of the first period is lost (real bug). For TX, the cyclic descriptor is submitted and started at config time, before anything has
been written. The TX DREQ is asserted whenever the FIFO has space, even with the SM disabled. So the DMAC immediately reads the first
FIFO-depth of words (plus anything it prefetches internally) from period 0, which at that point is still zeros from dma_alloc_coherent. When
the user then fills period 0, the DMA is already past those words. The output therefore starts with about 8 zero words instead of the user's
first words, and no underflow is reported. This contradicts the new comment about filling the whole buffer before enabling the SM. Fix: for
TX, delay dmaengine_submit()/dma_async_issue_pending() until the first write, or until the buffer has been primed. - The writer doesn't catch up properly after an underflow. The callback always calls up(), so if the writer falls behind (head > tail), the
semaphore count rises above buf_count. The writer then writes periods the DMA has already sent. When tail catches up to head, the next write
lands in the period the DMA is currently reading, so the data is torn. It then settles one period short of full lead. It would be better to
have the writer jump tail to head + 1 when it detects an underflow, and replace the counting semaphore with a wait on tail - head < buf_count. - Merged callbacks throw the counts off (affects RX too). dw-axi-dmac reports each finished period through vchan_cyclic_callback(), which just
sets vc->cyclic and schedules a tasklet. If two periods finish before the tasklet runs, the driver gets only one callback. After that,
head_idx and the semaphore are permanently one behind the hardware. For TX, each miss silently reduces how far ahead the writer can get, and
underflow detection works from the wrong head. With small periods under load this is realistic. Using the residue from dmaengine_tx_status()
to find the real position would be more robust. This problem already exists in the RX path, but TX makes it more visible. - The underflow check is best effort. The callback runs in a tasklet, possibly well after the DMA has started the next period. A write that
completes in that gap isn't flagged even though stale data went out. A comment should say so. - The new guard in rp1_pio_sm_xfer_data() is a separate fix. It also protects cyclic RX: before this commit, the kernel API on a cyclic channel
would index bufs[tail % buf_count] when only bufs[0] is allocated. It deserves a mention in the commit message, or its own commit. - Minor points:
- dma_wmb() doesn't order against anything the DMAC can see, since the DMA never reads tail_idx. Its comment ("Pair with the DMAC's reads") is
misleading. It does no harm, but it doesn't achieve anything either. - A zero-byte TX write now blocks and sends a full period of zeros, whereas RX rejects !bytes. Pick one behaviour on purpose.
- Returning -EPIPE even though the data was queued is documented in the uapi header. Still, a caller that retries on error will send that
period twice, so the wording could state that explicitly. - The re-indented dmaengine_prep_dma_cyclic() arguments still aren't aligned with the opening parenthesis (checkpatch --strict).
- dma_wmb() doesn't order against anything the DMAC can see, since the DMA never reads tail_idx. Its comment ("Pair with the DMAC's reads") is
Existing RX issue, not caused by these commits: after -EOVERFLOW, rp1_pio_sm_rx_user() returns without advancing tail_idx. Meanwhile head - tail
keeps growing, so every later read also returns -EOVERFLOW. The stream can't recover without reconfiguring.
| * Cyclic DMA is currently only supported for FROM_SM, and | ||
| * needs at least two real buffers to cycle through. | ||
| */ | ||
| if (dir != RP1_PIO_DIR_FROM_SM || (!buf_size || buf_count < 2)) |
There was a problem hiding this comment.
I think the !buf_size check is effectively covered by the conditions on lines 1050-1051.
There was a problem hiding this comment.
Actually, no: this check also catches the corner case where both buf_size and buf_count are 0, which is a special corner case of the non-cyclic DMA, but for cyclic DMA this makes no sense.
There was a problem hiding this comment.
But that's caught by || buf_count < 2), no?
Two pre-existing bugs in cyclic (FROM_SM) DMA: - After the DMA callback posts the semaphore, the reader only checks the semaphore count, not the actual head_idx/tail_idx relationship. If the completion tasklet is delayed enough that two periods finish before it runs, only one wakeup is delivered and head_idx/the semaphore permanently lag the hardware by one period, throwing off later flow control and overflow detection. Fix this by waiting on the live head_idx - tail_idx relationship (via a waitqueue) instead of counting a fixed number of wakeups. - Once rp1_pio_sm_rx_user() returns -EOVERFLOW it never advances tail_idx, so head_idx - tail_idx keeps growing and every subsequent read also returns -EOVERFLOW: the stream can't recover without reconfiguring. Fix this by resyncing tail_idx to the oldest period that is still valid whenever an overflow is detected, so later reads succeed again. Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org> Assisted-by: Claude-Code:claude-opus-5
This adds the missing support for cyclic DMA for output (i.e. memory to PIO). For output, the TX DREQ is asserted whenever the FIFO has room, even while the state machine is disabled, so starting the DMA at config time (before userspace has written anything) would let it drain the still-zeroed first period into the FIFO. Defer dma_async_issue_pending() until the first write has filled that period. The writer waits on the live head_idx/tail_idx relationship (reusing the cyclic_wait mechanism the previous commit introduced for cyclic RX) rather than a counting semaphore, so a write can't land on a period the DMA has already started reading. If the DMA does catch up with the writer, the write is redirected to the next period the DMA hasn't reached yet and -EPIPE is reported, instead of tearing the period currently being transferred. Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org> Assisted-by: Claude-Code:claude-opus-5
a7c3ab1 to
12f2a2d
Compare
|
Pushed new code (I let my AI work on the issues your AI found...). Let's see what your AI now thinks of these two patches. Tomorrow I plan to do some actual testing for these corner cases, just to verify what all these AIs have been doing! |
|
Straight from the horse's mouth: 48904ca — fix cyclic RX flow control and overflow recovery
12f2a2d — cyclic DMA for output Improvements: starting the DMA on the first write fixes the zero words at the start. The writer now skips ahead after an underflow instead of
|
The first two patches are small fixes, and the third adds support for cyclic DMA output. Tested and verified with pio-la and spdif encoding/decoding.