From: Jacopo Mondi Date: Sat, 28 Mar 2026 16:34:51 +0000 (+0100) Subject: media: rzv2h-ivc: Wait for frame end in stop_streaming X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b4e6ddce16b8ae0ddbec7c96c675779260ad4697;p=thirdparty%2Fkernel%2Flinux.git media: rzv2h-ivc: Wait for frame end in stop_streaming The rzv2h-ivc driver fails to handle back-2-back streaming sessions that do not go through a peripheral reset. As the driver uses an autosuspend delay of 2 seconds, it is quite possible that two consecutive streaming sessions won't go through a suspend/resume sequence. If the peripheral is not reset the second streaming session hangs and no frames are delivered to the ISP. This is because the stop_streaming() procedure implemented in the driver doesn't match what's prescribed by the chip datasheet: 1) The chip manual suggests to poll the RZV2H_IVC_FM_INT_STAT_STPEND bit of RZV2H_IVC_REG_FM_INT_STA instead of polling on RZV2H_IVC_REG_FM_STOP and prescribes to clear the bit after polling has completed 2) More importantly: the RZV2H_IVC_REG_FM_STOP_FSTOP bit has to be set on RZV2H_IVC_REG_FM_STOP -only- if a frame transfer to the ISP is in progress. Setting the RZV2H_IVC_REG_FM_STOP_FSTOP bit when no frame is being transferred causes the polling routine to timeout and the next streaming session fails to start As a frame transfer of an image in 1920x1080@10bi takes 5 milliseconds at most, it is quite possible that the frame transfer completion interrupt races with the stop procedure. Instead of forcing a frame transfer abort, simply wait for the in-progress transfer to complete by polling the ivc->vvalid_ifp status variable in an hand-rolled loop that allows to inspect the variable while holding the spinlock, to allow the irq handler to complete the current buffer. With this change, streaming back-2-back without suspending the peripheral works successfully. Cc: stable@vger.kernel.org Fixes: f0b3984d821b ("media: platform: Add Renesas Input Video Control block driver") Signed-off-by: Jacopo Mondi Reviewed-by: Daniel Scally Signed-off-by: Hans Verkuil --- diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c index b167f1bab7ef2..932fed38cf3fc 100644 --- a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c +++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c @@ -297,12 +297,33 @@ err_return_buffers: static void rzv2h_ivc_stop_streaming(struct vb2_queue *q) { struct rzv2h_ivc *ivc = vb2_get_drv_priv(q); - u32 val = 0; + unsigned int loop = 5; - rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_STOP, RZV2H_IVC_REG_FM_STOP_FSTOP); - readl_poll_timeout(ivc->base + RZV2H_IVC_REG_FM_STOP, - val, !(val & RZV2H_IVC_REG_FM_STOP_FSTOP), - 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC); + /* + * If no frame transfer is in progress, we're done, otherwise, wait for + * the transfer to complete. + * + * Transferring a 1920x1080@10bit frame to the ISP takes less than 5 + * msec so sleep for 2.5 msec (+- 25%) and give up after 5 attempts. + */ + for (; loop > 0; loop--) { + unsigned int vvalid_ifp; + + /* + * Inspect the ivc->vvalid_ifp variable holding the spinlock not + * to the race with the rzv2h_ivc_buffer_done() call in the irq + * handler. + */ + scoped_guard(spinlock_irq, &ivc->spinlock) { + vvalid_ifp = ivc->vvalid_ifp; + } + if (vvalid_ifp < 2) + break; + + fsleep(2500); + } + if (!loop) + dev_err(ivc->dev, "Failed to stop streaming\n"); rzv2h_ivc_return_buffers(ivc, VB2_BUF_STATE_ERROR); video_device_pipeline_stop(&ivc->vdev.dev);