* [PATCH 0/3] media: camss: three fixes for the CSID power and VFE streaming paths
@ 2026-10-29 12:31 Qianhui Xu
2026-10-29 12:31 ` [PATCH 1/3] media: camss: csid: unwind the power-on path through a single error label Qianhui Xu
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Qianhui Xu @ 2026-10-29 12:31 UTC (permalink / raw)
To: linux-media
Cc: Mauro Carvalho Chehab, Bryan O'Donoghue, Vladimir Zapolskiy,
Loic Poulain, linux-arm-msm, linux-kernel
These three patches are fixes found while bringing camss up on a new SoC,
but none of them depends on that new SoC support: each one is against code
that already exists in mainline and can be reviewed on its own.
As posted earlier, the SM8750 specific patches of the previous series are
withdrawn and that work stays with the existing upstream series:
<179325717815.489400.16602455641444097904@mail.ustc.edu.cn>.
1/3 csid: the power-on path repeats its cleanup sequence in every error
branch and gets the first one wrong: when pm_runtime_resume_and_get()
fails, the parent device reference taken by parent_dev_ops->get() is
never dropped, so the VFE stays powered. Fold the four open-coded
blocks into a single label chain and route the pm_runtime failure
through the parent put as well.
2/3 video: the write master of the newer VFE generations can only be
programmed with a physically contiguous payload, because the frame
increment registers describe one linear address range. A buffer whose
plane was not contiguous was silently written past the end of the
first scatterlist entry. Reject such buffers instead, and add a
prepare_buffer() video operation so SoC code can add its own
alignment requirements after the generic checks.
For the contiguity requirement that Bryan asked to see cited: the
downstream write master driver (camera-kernel
drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver3.c)
programs each write master with a single frame increment of
stride * height (line 3865) and aligns it to 256 bytes (line 3908),
with no per-fragment addressing. If this citation is not considered
sufficient, patch 2/3 will be dropped.
3/3 video: when one subdevice fails to start, the ones already started
further up the pipeline were left streaming and the next start tried
to start them again. Factor the teardown loop out of
video_stop_streaming() and run it from the error path; also keep
stopping the remaining subdevices when one of them fails to stop
instead of returning early.
All three are build tested with W=1 on drivers/media/platform/qcom/camss/
and apply cleanly on v7.3-rc7. They touch only camss-csid.c and
camss-video.c, which no patch of the Kaanapali series (v23) or of the
SM8450 series changes, so they apply independently of both.
This series intentionally contains no SoC enablement, no DT binding and no
CSIPHY change; those are being discussed in separate threads.
Qianhui Xu (3):
media: camss: csid: unwind the power-on path through a single error
label
media: camss: video: check that a buffer is DMA contiguous and add a
prepare hook
media: camss: video: stop the subdevices already started when a start
fails
.../media/platform/qcom/camss/camss-csid.c | 45 +++++------
.../media/platform/qcom/camss/camss-video.c | 78 +++++++++++--------
.../media/platform/qcom/camss/camss-video.h | 1 +
3 files changed, 68 insertions(+), 56 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH 1/3] media: camss: csid: unwind the power-on path through a single error label 2026-10-29 12:31 [PATCH 0/3] media: camss: three fixes for the CSID power and VFE streaming paths Qianhui Xu @ 2026-10-29 12:31 ` Qianhui Xu 2026-10-29 12:31 ` [PATCH 2/3] media: camss: video: check that a buffer is DMA contiguous and add a prepare hook Qianhui Xu 2026-10-29 12:31 ` [PATCH 3/3] media: camss: video: stop the subdevices already started when a start fails Qianhui Xu 2 siblings, 0 replies; 7+ messages in thread From: Qianhui Xu @ 2026-10-29 12:31 UTC (permalink / raw) To: linux-media Cc: Mauro Carvalho Chehab, Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain, linux-arm-msm, linux-kernel csid_set_power() repeats the same cleanup sequence in every error branch and gets it wrong in the first one: when pm_runtime_resume_and_get() fails, the parent device reference taken by parent_dev_ops->get() is never dropped, so a failing power-on leaks it and the matching VFE stays powered. Replace the four open-coded unwind blocks with gotos to a shared label chain and route the pm_runtime failure through the parent put as well. No functional change on the success path. Signed-off-by: Qianhui Xu <xiaqinghuan@mail.ustc.edu.cn> --- .../media/platform/qcom/camss/camss-csid.c | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 48459b46a9..592c1dfc97 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -696,44 +696,28 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) ret = pm_runtime_resume_and_get(dev); if (ret < 0) - return ret; + goto put_parent; ret = regulator_bulk_enable(csid->num_supplies, csid->supplies); - if (ret < 0) { - pm_runtime_put_sync(dev); - return ret; - } + if (ret < 0) + goto put_pm; ret = csid_set_clock_rates(csid); - if (ret < 0) { - regulator_bulk_disable(csid->num_supplies, - csid->supplies); - pm_runtime_put_sync(dev); - return ret; - } + if (ret < 0) + goto disable_regulators; ret = camss_enable_clocks(csid->nclocks, csid->clock, dev); - if (ret < 0) { - regulator_bulk_disable(csid->num_supplies, - csid->supplies); - pm_runtime_put_sync(dev); - return ret; - } + if (ret < 0) + goto disable_regulators; csid->phy.need_vc_update = true; enable_irq(csid->irq); ret = csid->res->hw_ops->reset(csid); - if (ret < 0) { - disable_irq(csid->irq); - camss_disable_clocks(csid->nclocks, csid->clock); - regulator_bulk_disable(csid->num_supplies, - csid->supplies); - pm_runtime_put_sync(dev); - return ret; - } + if (ret < 0) + goto disable_clocks; csid->res->hw_ops->hw_version(csid); } else { @@ -746,6 +730,17 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) } return ret; + +disable_clocks: + disable_irq(csid->irq); + camss_disable_clocks(csid->nclocks, csid->clock); +disable_regulators: + regulator_bulk_disable(csid->num_supplies, csid->supplies); +put_pm: + pm_runtime_put_sync(dev); +put_parent: + csid->res->parent_dev_ops->put(camss, csid->id); + return ret; } /* ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/3] media: camss: video: check that a buffer is DMA contiguous and add a prepare hook 2026-10-29 12:31 [PATCH 0/3] media: camss: three fixes for the CSID power and VFE streaming paths Qianhui Xu 2026-10-29 12:31 ` [PATCH 1/3] media: camss: csid: unwind the power-on path through a single error label Qianhui Xu @ 2026-10-29 12:31 ` Qianhui Xu 2026-10-29 13:01 ` Loic Poulain 2026-10-29 14:02 ` Bryan O'Donoghue 2026-10-29 12:31 ` [PATCH 3/3] media: camss: video: stop the subdevices already started when a start fails Qianhui Xu 2 siblings, 2 replies; 7+ messages in thread From: Qianhui Xu @ 2026-10-29 12:31 UTC (permalink / raw) To: linux-media Cc: Mauro Carvalho Chehab, Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain, linux-arm-msm, linux-kernel The write master of the newer VFE generations can only be programmed with a buffer whose payload is physically contiguous, because the frame increment registers describe one linear address range. Check that the DMA mapping of every plane is contiguous for at least sizeimage bytes and reject the buffer otherwise, instead of silently writing past the end of the first scatterlist entry. Also let the SoC code add its own alignment requirements to a queued buffer through a new prepare_buffer() video operation, which runs after the generic checks. Signed-off-by: Qianhui Xu <xiaqinghuan@mail.ustc.edu.cn> --- .../media/platform/qcom/camss/camss-video.c | 18 +++++++++++++++++- .../media/platform/qcom/camss/camss-video.h | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 0852eb6f13..027cf024f9 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -155,7 +155,10 @@ static int video_buf_init(struct vb2_buffer *vb) const struct v4l2_pix_format_mplane *format = &video->active_fmt.fmt.pix_mp; struct sg_table *sgt; - unsigned int i; + struct scatterlist *sg; + dma_addr_t end; + unsigned int i, j; + size_t contiguous; for (i = 0; i < format->num_planes; i++) { sgt = vb2_dma_sg_plane_desc(vb, i); @@ -163,6 +166,16 @@ static int video_buf_init(struct vb2_buffer *vb) return -EFAULT; buffer->addr[i] = sg_dma_address(sgt->sgl); + end = buffer->addr[i]; + contiguous = 0; + for_each_sgtable_dma_sg(sgt, sg, j) { + if (sg_dma_address(sg) != end) + break; + contiguous += sg_dma_len(sg); + end += sg_dma_len(sg); + } + if (contiguous < format->plane_fmt[i].sizeimage) + return -EINVAL; } if (format->pixelformat == V4L2_PIX_FMT_NV12 || @@ -193,6 +206,9 @@ static int video_buf_prepare(struct vb2_buffer *vb) vbuf->field = V4L2_FIELD_NONE; + if (video->ops->prepare_buffer) + return video->ops->prepare_buffer(video, vb); + return 0; } diff --git a/drivers/media/platform/qcom/camss/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h index d3e56e240a..af046ecb7f 100644 --- a/drivers/media/platform/qcom/camss/camss-video.h +++ b/drivers/media/platform/qcom/camss/camss-video.h @@ -28,6 +28,7 @@ struct camss_buffer { struct camss_video; struct camss_video_ops { + int (*prepare_buffer)(struct camss_video *vid, struct vb2_buffer *vb); int (*queue_buffer)(struct camss_video *vid, struct camss_buffer *buf); int (*flush_buffers)(struct camss_video *vid, enum vb2_buffer_state state); ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/3] media: camss: video: check that a buffer is DMA contiguous and add a prepare hook 2026-10-29 12:31 ` [PATCH 2/3] media: camss: video: check that a buffer is DMA contiguous and add a prepare hook Qianhui Xu @ 2026-10-29 13:01 ` Loic Poulain 2026-10-29 14:02 ` Bryan O'Donoghue 1 sibling, 0 replies; 7+ messages in thread From: Loic Poulain @ 2026-10-29 13:01 UTC (permalink / raw) To: Qianhui Xu Cc: linux-media, Mauro Carvalho Chehab, Bryan O'Donoghue, Vladimir Zapolskiy, linux-arm-msm, linux-kernel On Thu, Oct 29, 2026 at 1:31 PM Qianhui Xu <xiaqinghuan@mail.ustc.edu.cn> wrote: > > The write master of the newer VFE generations can only be programmed with > a buffer whose payload is physically contiguous, because the frame > increment registers describe one linear address range. Check that the DMA > mapping of every plane is contiguous for at least sizeimage bytes and > reject the buffer otherwise, instead of silently writing past the end of > the first scatterlist entry. Have you tested the non-contiguous case? It’s a bit surprising to me, since the device-visible IOVA is still contiguous. Perhaps this is related to frame subsampling, in which case the frame buffers themselves would need to remain contiguous to take advantage of the hardware frame-increment mechanism. But even then, the relevant address space from the hardware's perspective should still be the IOVA. Regards, Loic > > Also let the SoC code add its own alignment requirements to a queued > buffer through a new prepare_buffer() video operation, which runs after > the generic checks. > > Signed-off-by: Qianhui Xu <xiaqinghuan@mail.ustc.edu.cn> > --- > .../media/platform/qcom/camss/camss-video.c | 18 +++++++++++++++++- > .../media/platform/qcom/camss/camss-video.h | 1 + > 2 files changed, 18 insertions(+), 1 deletion(-) > > diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c > index 0852eb6f13..027cf024f9 100644 > --- a/drivers/media/platform/qcom/camss/camss-video.c > +++ b/drivers/media/platform/qcom/camss/camss-video.c > @@ -155,7 +155,10 @@ static int video_buf_init(struct vb2_buffer *vb) > const struct v4l2_pix_format_mplane *format = > &video->active_fmt.fmt.pix_mp; > struct sg_table *sgt; > - unsigned int i; > + struct scatterlist *sg; > + dma_addr_t end; > + unsigned int i, j; > + size_t contiguous; > > for (i = 0; i < format->num_planes; i++) { > sgt = vb2_dma_sg_plane_desc(vb, i); > @@ -163,6 +166,16 @@ static int video_buf_init(struct vb2_buffer *vb) > return -EFAULT; > > buffer->addr[i] = sg_dma_address(sgt->sgl); > + end = buffer->addr[i]; > + contiguous = 0; > + for_each_sgtable_dma_sg(sgt, sg, j) { > + if (sg_dma_address(sg) != end) > + break; > + contiguous += sg_dma_len(sg); > + end += sg_dma_len(sg); > + } > + if (contiguous < format->plane_fmt[i].sizeimage) > + return -EINVAL; > } > > if (format->pixelformat == V4L2_PIX_FMT_NV12 || > @@ -193,6 +206,9 @@ static int video_buf_prepare(struct vb2_buffer *vb) > > vbuf->field = V4L2_FIELD_NONE; > > + if (video->ops->prepare_buffer) > + return video->ops->prepare_buffer(video, vb); > + > return 0; > } > > diff --git a/drivers/media/platform/qcom/camss/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h > index d3e56e240a..af046ecb7f 100644 > --- a/drivers/media/platform/qcom/camss/camss-video.h > +++ b/drivers/media/platform/qcom/camss/camss-video.h > @@ -28,6 +28,7 @@ struct camss_buffer { > struct camss_video; > > struct camss_video_ops { > + int (*prepare_buffer)(struct camss_video *vid, struct vb2_buffer *vb); > int (*queue_buffer)(struct camss_video *vid, struct camss_buffer *buf); > int (*flush_buffers)(struct camss_video *vid, > enum vb2_buffer_state state); > ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/3] media: camss: video: check that a buffer is DMA contiguous and add a prepare hook 2026-10-29 12:31 ` [PATCH 2/3] media: camss: video: check that a buffer is DMA contiguous and add a prepare hook Qianhui Xu 2026-10-29 13:01 ` Loic Poulain @ 2026-10-29 14:02 ` Bryan O'Donoghue 1 sibling, 0 replies; 7+ messages in thread From: Bryan O'Donoghue @ 2026-10-29 14:02 UTC (permalink / raw) To: Qianhui Xu, linux-media Cc: Mauro Carvalho Chehab, Vladimir Zapolskiy, Loic Poulain, linux-arm-msm, linux-kernel On 29/10/2026 12:31, Qianhui Xu wrote: > The write master of the newer VFE generations can only be programmed with > a buffer whose payload is physically contiguous The first problem is that is _not_ what your code checks - you should use sg_phys() for that - which gets the physical address of a CPU scatterlist entry. What your code currently does is check that the IOVA addresses are contiguous. But what happens is an IOVA allocation is made which is linearly contiguous in the IOVA address space and then SMMU entries map the IOVA back to physical entries. So all we need to do is give the IOVA base to CAMSS and it can write IOVA buffers - its the SMMU's job to ensure the resulting upstream writes land at the right physical address. > increment registers describe one linear address range. Check that the DMA > mapping of every plane is contiguous for at least sizeimage bytes and > reject the buffer otherwise, instead of silently writing past the end of > the first scatterlist entry. > Also let the SoC code add its own alignment requirements to a queued > buffer through a new prepare_buffer() video operation, which runs after > the generic checks. > > Signed-off-by: Qianhui Xu <xiaqinghuan@mail.ustc.edu.cn> And I can prove it too. buildroot# uname -a Linux buildroot 7.3.0-rc1+ #19 SMP PREEMPT Thu Oct 29 13:26:34 GMT 2026 aarch64 GNU/Linux buildroot# dmesg | grep -i crd [ 0.000000] Machine model: Qualcomm Technologies, Inc. X1E80100 CRD buildroot# dmesg | grep "plane 0" qcom-camss acb7000.isp: plane 0 chunk 0: phys 0x000000089a800000 len 8388608 qcom-camss acb7000.isp: plane 0 chunk 1: phys 0x000000089a600000 len 2097152 qcom-camss acb7000.isp: plane 0 chunk 2: phys 0x0000000899900000 len 1048576 qcom-camss acb7000.isp: plane 0 chunk 3: phys 0x0000000899b00000 len 131072 qcom-camss acb7000.isp: plane 0: 11677696 bytes, 6 cpu entries, 5 physical breaks, largest physical run 8388608, 1 dma segments, 0 iova breaks, iova 0x00000000ff000000 Plane 0 == 11677696 bytes Largest physical run = 8388608 Interestingly we see some physically adjacent allocations here and then also non-contiguous allocations, which is the negation of the thesis, constraining IOVA to physically contiguous maps is provably incorrect. NAK diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 0852eb6f13..d2df368d5f 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -146,6 +146,44 @@ static int video_queue_setup(struct vb2_queue *q, return 0; } +/* DEBUG: how a plane is laid out in physical and IOVA space */ +static void video_buf_dump_sg(struct camss_video *video, unsigned int plane, + struct sg_table *sgt) +{ + dma_addr_t iova = sg_dma_address(sgt->sgl); + unsigned int j, pbreaks = 0, dbreaks = 0; + size_t total = 0, run = 0, maxrun = 0; + struct scatterlist *sg; + phys_addr_t pend = 0; + dma_addr_t dend = 0; + + for_each_sgtable_sg(sgt, sg, j) { + if (j && sg_phys(sg) != pend) { + pbreaks++; + run = 0; + } + if (j < 4) + dev_info(video->camss->dev, "plane %u chunk %u: phys %pa len %u\n", + plane, j, &(phys_addr_t){ sg_phys(sg) }, sg->length); + run += sg->length; + if (run > maxrun) + maxrun = run; + total += sg->length; + pend = sg_phys(sg) + sg->length; + } + + for_each_sgtable_dma_sg(sgt, sg, j) { + if (j && sg_dma_address(sg) != dend) + dbreaks++; + dend = sg_dma_address(sg) + sg_dma_len(sg); + } + + dev_info(video->camss->dev, + "plane %u: %zu bytes, %u cpu entries, %u physical breaks, largest physical run %zu, %u dma segments, %u iova breaks, iova %pad\n", + plane, total, sgt->orig_nents, pbreaks, maxrun, sgt->nents, + dbreaks, &iova); +} + static int video_buf_init(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); @@ -163,6 +201,7 @@ static int video_buf_init(struct vb2_buffer *vb) return -EFAULT; buffer->addr[i] = sg_dma_address(sgt->sgl); + video_buf_dump_sg(video, i, sgt); } if (format->pixelformat == V4L2_PIX_FMT_NV12 || ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 3/3] media: camss: video: stop the subdevices already started when a start fails 2026-10-29 12:31 [PATCH 0/3] media: camss: three fixes for the CSID power and VFE streaming paths Qianhui Xu 2026-10-29 12:31 ` [PATCH 1/3] media: camss: csid: unwind the power-on path through a single error label Qianhui Xu 2026-10-29 12:31 ` [PATCH 2/3] media: camss: video: check that a buffer is DMA contiguous and add a prepare hook Qianhui Xu @ 2026-10-29 12:31 ` Qianhui Xu 2026-10-29 15:00 ` Loic Poulain 2 siblings, 1 reply; 7+ messages in thread From: Qianhui Xu @ 2026-10-29 12:31 UTC (permalink / raw) To: linux-media Cc: Mauro Carvalho Chehab, Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain, linux-arm-msm, linux-kernel When one subdevice fails to start, the ones already started further up the pipeline were left streaming, and the next start would try to start them again. Factor the teardown loop out of video_stop_streaming() into video_stop_subdevices() and run it from the error path, stopping at the subdevice that failed. While at it, keep stopping the remaining subdevices when one of them fails to stop instead of returning early: the pipeline is being torn down, so a failure there must not leave the rest running. Signed-off-by: Qianhui Xu <xiaqinghuan@mail.ustc.edu.cn> --- .../media/platform/qcom/camss/camss-video.c | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 027cf024f9..ee9ffd0432 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -265,11 +265,37 @@ static int video_prepare_streaming(struct vb2_queue *q) return ret; } +/* Stop the started subdevices, excluding the one that failed to start. */ +static void video_stop_subdevices(struct camss_video *video, + struct media_entity *stop) +{ + struct media_entity *entity = &video->vdev.entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret; + + while (entity != stop) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + entity = pad->entity; + if (entity == stop) + break; + subdev = media_entity_to_v4l2_subdev(entity); + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD) + dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret); + } +} + static int video_start_streaming(struct vb2_queue *q, unsigned int count) { struct camss_video *video = vb2_get_drv_priv(q); struct video_device *vdev = &video->vdev; - struct media_entity *entity; + struct media_entity *entity = &vdev->entity; struct media_pad *pad; struct v4l2_subdev *subdev; int ret; @@ -284,7 +310,6 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count) if (ret < 0) goto error; - entity = &vdev->entity; while (1) { pad = &entity->pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) @@ -305,6 +330,7 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count) return 0; error: + video_stop_subdevices(video, entity); video_device_pipeline_stop(vdev); flush_buffers: @@ -316,35 +342,9 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count) static void video_stop_streaming(struct vb2_queue *q) { struct camss_video *video = vb2_get_drv_priv(q); - struct video_device *vdev = &video->vdev; - struct media_entity *entity; - struct media_pad *pad; - struct v4l2_subdev *subdev; - int ret; - - entity = &vdev->entity; - while (1) { - pad = &entity->pads[0]; - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - break; - - pad = media_pad_remote_pad_first(pad); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - break; - - entity = pad->entity; - subdev = media_entity_to_v4l2_subdev(entity); - - ret = v4l2_subdev_call(subdev, video, s_stream, 0); - - if (ret) { - dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret); - return; - } - } - - video_device_pipeline_stop(vdev); + video_stop_subdevices(video, NULL); + video_device_pipeline_stop(&video->vdev); video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); } ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 3/3] media: camss: video: stop the subdevices already started when a start fails 2026-10-29 12:31 ` [PATCH 3/3] media: camss: video: stop the subdevices already started when a start fails Qianhui Xu @ 2026-10-29 15:00 ` Loic Poulain 0 siblings, 0 replies; 7+ messages in thread From: Loic Poulain @ 2026-10-29 15:00 UTC (permalink / raw) To: Qianhui Xu Cc: linux-media, Mauro Carvalho Chehab, Bryan O'Donoghue, Vladimir Zapolskiy, linux-arm-msm, linux-kernel On Thu, Oct 29, 2026 at 1:31 PM Qianhui Xu <xiaqinghuan@mail.ustc.edu.cn> wrote: > > When one subdevice fails to start, the ones already started further up the > pipeline were left streaming, and the next start would try to start them > again. Factor the teardown loop out of video_stop_streaming() into > video_stop_subdevices() and run it from the error path, stopping at the > subdevice that failed. > > While at it, keep stopping the remaining subdevices when one of them fails > to stop instead of returning early: the pipeline is being torn down, so a > failure there must not leave the rest running. > > Signed-off-by: Qianhui Xu <xiaqinghuan@mail.ustc.edu.cn> > --- > .../media/platform/qcom/camss/camss-video.c | 60 +++++++++---------- > 1 file changed, 30 insertions(+), 30 deletions(-) > > diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c > index 027cf024f9..ee9ffd0432 100644 > --- a/drivers/media/platform/qcom/camss/camss-video.c > +++ b/drivers/media/platform/qcom/camss/camss-video.c > @@ -265,11 +265,37 @@ static int video_prepare_streaming(struct vb2_queue *q) > return ret; > } > > +/* Stop the started subdevices, excluding the one that failed to start. */ > +static void video_stop_subdevices(struct camss_video *video, > + struct media_entity *stop) > +{ > + struct media_entity *entity = &video->vdev.entity; > + struct v4l2_subdev *subdev; > + struct media_pad *pad; > + int ret; > + > + while (entity != stop) { > + pad = &entity->pads[0]; > + if (!(pad->flags & MEDIA_PAD_FL_SINK)) > + break; > + pad = media_pad_remote_pad_first(pad); > + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) > + break; > + entity = pad->entity; > + if (entity == stop) > + break; > + subdev = media_entity_to_v4l2_subdev(entity); > + ret = v4l2_subdev_call(subdev, video, s_stream, 0); > + if (ret && ret != -ENOIOCTLCMD) > + dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret); > + } > +} > + Now you're here, It might also make sense to introduce a generic CAMSS helper to walk the media graph upstream and start/stop all subdevices in the pipeline, something along the lines of camss_video_subdevs_set_stream(.. bool enable). While doing so, you could leverage more modern framework helpers, such as: - v4l2_subdev_enable_streams() and v4l2_subdev_disable_streams() instead of legacy s_stream() - media_entity_remote_source_pad_unique() to locate the upstream entity rather than relying on pads[0] Regards, Loic > static int video_start_streaming(struct vb2_queue *q, unsigned int count) > { > struct camss_video *video = vb2_get_drv_priv(q); > struct video_device *vdev = &video->vdev; > - struct media_entity *entity; > + struct media_entity *entity = &vdev->entity; > struct media_pad *pad; > struct v4l2_subdev *subdev; > int ret; > @@ -284,7 +310,6 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count) > if (ret < 0) > goto error; > > - entity = &vdev->entity; > while (1) { > pad = &entity->pads[0]; > if (!(pad->flags & MEDIA_PAD_FL_SINK)) > @@ -305,6 +330,7 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count) > return 0; > > error: > + video_stop_subdevices(video, entity); > video_device_pipeline_stop(vdev); > > flush_buffers: > @@ -316,35 +342,9 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count) > static void video_stop_streaming(struct vb2_queue *q) > { > struct camss_video *video = vb2_get_drv_priv(q); > - struct video_device *vdev = &video->vdev; > - struct media_entity *entity; > - struct media_pad *pad; > - struct v4l2_subdev *subdev; > - int ret; > - > - entity = &vdev->entity; > - while (1) { > - pad = &entity->pads[0]; > - if (!(pad->flags & MEDIA_PAD_FL_SINK)) > - break; > - > - pad = media_pad_remote_pad_first(pad); > - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) > - break; > - > - entity = pad->entity; > - subdev = media_entity_to_v4l2_subdev(entity); > - > - ret = v4l2_subdev_call(subdev, video, s_stream, 0); > - > - if (ret) { > - dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret); > - return; > - } > - } > - > - video_device_pipeline_stop(vdev); > > + video_stop_subdevices(video, NULL); > + video_device_pipeline_stop(&video->vdev); > video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); > } > > ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-29 15:01 UTC | newest] Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-29 12:31 [PATCH 0/3] media: camss: three fixes for the CSID power and VFE streaming paths Qianhui Xu 2026-10-29 12:31 ` [PATCH 1/3] media: camss: csid: unwind the power-on path through a single error label Qianhui Xu 2026-10-29 12:31 ` [PATCH 2/3] media: camss: video: check that a buffer is DMA contiguous and add a prepare hook Qianhui Xu 2026-10-29 13:01 ` Loic Poulain 2026-10-29 14:02 ` Bryan O'Donoghue 2026-10-29 12:31 ` [PATCH 3/3] media: camss: video: stop the subdevices already started when a start fails Qianhui Xu 2026-10-29 15:00 ` Loic Poulain
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®