From: sashiko-bot@kernel.org
To: "Loic Poulain" <loic.poulain@oss.qualcomm.com>
Cc: devicetree@vger.kernel.org, robh@kernel.org, conor+dt@kernel.org,
media-ci@linuxtv.org
Subject: Re: [PATCH v14 6/9] media: qcom: camss: Add CAMSS Offline Processing Engine driver
Date: Fri, 06 Nov 2026 14:42:03 +0000 [thread overview]
Message-ID: <sashiko-outbox-201600@kernel.org> (raw)
In-Reply-To: <20261106-camss-isp-ope-v14-6-49c426cd2715@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 11 potential
issues to consider:
- [High] media: qcom: camss: unbalanced and racy pipeline teardown in
ope_unregister()
- [Medium] media: qcom: camss: race between ope_sched_cancel() and
ope_remove()
- [Medium] media: qcom: camss: ope_queues_release() aborts active jobs
on shared ctx
- [Medium] media: qcom: camss: race in ope_job_finish() before removing
buffers
- [Medium] media: qcom: camss: vertical phase step overflow in
ope_prog_stripe()
- [Medium] media: qcom: camss: core clock enabled without required cx
OPP vote
- [Medium] media: qcom: camss: premature devm_pm_runtime_enable() in
ope_init_power()
- [Medium] media: qcom: camss: premature video device publication in
ope_probe()
- [Medium] media: qcom: camss: ope: ope_flush_hw() fails unstarted jobs
on suspend
- [Low] media: qcom: camss: wrong errno in ope_g_parm() and ope_s_parm()
on capture
- [Low] media: qcom: camss: missing kernel-doc for vdev and subdev in
pipeline.h
--
commit 754daff99307e8b7e1677889efa986d5ec3c5ea3
Author: Loic Poulain <loic.poulain@oss.qualcomm.com>
media: qcom: camss: Add CAMSS Offline Processing Engine driver
This commit adds a V4L2 memory-to-memory ISP driver for the Qualcomm Offline
Processing Engine (OPE), along with a declarative media-controller pipeline
builder and an ISP parameter buffer parser.
Link: https://github.com/jwrdegoede/libcamera/commits/camss_pipeline_v2.1/
Link: https://github.com/loicpoulain/camss-isp-m2m-test
> diff --git a/drivers/media/platform/qcom/camss/ope/core.c b/drivers/media/platform/qcom/camss/ope/core.c
> new file mode 100644
> index 0000000000000..11dca901e0f65
> --- /dev/null
> +++ b/drivers/media/platform/qcom/camss/ope/core.c
[ ... ]
> +/* Downscaler fixed-point helpers */
> +#define Q21(v) (((uint64_t)(v)) << 21)
> +#define DS_Q21(n, d) ((uint32_t)div_u64((uint64_t)(n) << 21, (d)))
> +#define DS_RESOLUTION(in, out) \
> + (((out) * 128 <= (in)) ? 0x0U : \
> + ((out) * 16 <= (in)) ? 0x1U : \
> + ((out) * 8 <= (in)) ? 0x2U : 0x3U)
[ ... ]
> +static void ope_prog_stripe(struct ope_ctx *ctx, struct ope_stripe *stripe)
> +{
[ ... ]
> + /* Downscalers */
> + for (int i = 0; i < OPE_DS_MAX; i++) {
> + struct ope_dsc_config *dsc = &stripe->dsc[i];
> + u32 base = ope_ds_base[i];
> + u32 cfg = 0;
> +
> + if (dsc->input_width != dsc->output_width) {
> + dsc->phase_step_h |=
> + DS_RESOLUTION(dsc->input_width,
> + dsc->output_width) << 30;
> + cfg |= OPE_PP_DOWNSCALE_MN_DS_CFG_H_SCALE_EN;
> + }
> + if (dsc->input_height != dsc->output_height) {
> + dsc->phase_step_v |=
> + DS_RESOLUTION(dsc->input_height,
> + dsc->output_height) << 30;
> + cfg |= OPE_PP_DOWNSCALE_MN_DS_CFG_V_SCALE_EN;
> + }
> +
> + ope_write_pp(ope, OPE_PP_DOWNSCALE_MN_DS_CFG(base), cfg);
> + ope_write_pp(ope, OPE_PP_DOWNSCALE_MN_DS_IMAGE_SIZE_CFG(base),
> + ((dsc->input_width - 1) << 16) | (dsc->input_height - 1));
> + ope_write_pp(ope, OPE_PP_DOWNSCALE_MN_DS_MN_H_CFG(base),
> + dsc->phase_step_h);
> + ope_write_pp(ope, OPE_PP_DOWNSCALE_MN_DS_MN_V_CFG(base),
> + dsc->phase_step_v);
[Severity: Medium]
Can dsc->phase_step_v overflow the 30-bit phase step field in
OPE_PP_DOWNSCALE_MN_DS_MN_V_CFG at the maximum vertical downscaling ratio?
The driver allows a sink crop height of OPE_MAX_H (8192) and a source compose
height of OPE_MIN_H (16). At that 512x ratio, DS_Q21(8192, 16) evaluates to
512 << 21 (0x40000000, or BIT(30)). ORing DS_RESOLUTION(8192, 16) << 30
(0x0U) into dsc->phase_step_v leaves bit 30 set and bits [29:0] zero, so
OPE_PP_DOWNSCALE_MN_DS_MN_V_CFG is programmed with a resolution selector of 1
instead of 0 and a phase step of 0.
[ ... ]
> +static void ope_job_finish(struct ope_ctx *ctx, enum vb2_buffer_state state)
> +{
> + struct vb2_v4l2_buffer *src, *dst;
> + struct ope_dev *ope = ctx->ope;
> +
> + scoped_guard(spinlock_irqsave, &ope->sched_lock) {
> + if (ope->curr_ctx != ctx)
> + return;
> +
> + ope->curr_ctx = NULL;
> + ope->hw_running = false;
> + ctx->queued = false;
> +
> + /* A failed job leaves the configuration in an unknown state */
> + if (state != VB2_BUF_STATE_DONE)
> + ope->hw_ctx = NULL;
> + }
> +
> + src = ope_queue_remove(ctx, OPE_QUEUE_FRAME_IN);
> + dst = ope_queue_remove(ctx, OPE_QUEUE_DISP_OUT);
[Severity: Medium]
Can releasing ope->sched_lock before removing src and dst from rdy_queue let
a concurrent ope_try_schedule() schedule a new job against the buffers being
completed?
ope_run_job() only peeks the buffers at the head of rdy_queue via
ope_buf_dma_addr(), leaving them queued until ope_job_finish() removes them:
CPU0 CPU1
ope_job_finish()
lock(&ope->sched_lock)
ope->curr_ctx = NULL
ctx->queued = false
unlock(&ope->sched_lock)
ope_buf_queue()
ope_try_schedule()
ope_job_ready() -> true
lock(&ope->sched_lock)
ctx->queued = true
ope->curr_ctx = ctx
schedule_work(&ope->run_work)
ope_run_work()
ope_run_job()
ope_buf_dma_addr() <-- peeks finished buf
ope_queue_remove(FRAME_IN)
ope_queue_remove(DISP_OUT)
vb2_buffer_done()
Removing src and dst from rdy_queue before clearing ope->curr_ctx and
ctx->queued under ope->sched_lock would close this window.
[ ... ]
> +static void ope_flush_hw(struct ope_dev *ope)
> +{
> + struct ope_ctx *ctx;
> +
> + guard(mutex)(&ope->mutex);
> +
> + /* Drain a handler that may still be reading registers */
> + synchronize_irq(ope->irq);
> +
> + scoped_guard(spinlock_irqsave, &ope->sched_lock)
> + ctx = ope->curr_ctx;
> +
> + if (!ctx)
> + return;
> +
> + if (ope_hw_reset_poll(ope))
> + dev_err(ope->dev, "reset timeout while flushing\n");
> +
> + scoped_guard(spinlock_irqsave, &ope->sched_lock)
> + ope->hw_ctx = NULL;
> +
> + ope_glut_cache_invalidate(ope);
> +
> + ope_job_finish(ctx, VB2_BUF_STATE_ERROR);
> +}
[Severity: Medium]
Does ope_flush_hw() fail unstarted jobs with VB2_BUF_STATE_ERROR during
runtime or system suspend?
ope_runtime_suspend() first calls ope_sched_pause(), which runs
cancel_work_sync(&ope->run_work). If run_work was pending after
ope_try_schedule() set ope->curr_ctx = ctx and ctx->queued = true,
cancel_work_sync() cancels it before ope_run_job() starts hardware execution
(ope->hw_running is still false). Because ope_flush_hw() only checks
ope->curr_ctx, it resets the idle hardware and calls ope_job_finish(ctx,
VB2_BUF_STATE_ERROR), dequeuing and failing the unstarted buffers instead of
clearing ctx->queued and ope->curr_ctx so ope_sched_resume() can reschedule
them on resume.
> +
> +static void ope_sched_cancel(struct ope_ctx *ctx)
> +{
> + struct ope_dev *ope = ctx->ope;
> + bool running;
> +
> + ope_sched_pause(ope);
> +
> + scoped_guard(spinlock_irqsave, &ope->sched_lock) {
> + running = (ope->hw_running && ope->hw_ctx == ctx);
> + ctx->queued = false;
> + if (running)
> + ope->hw_running = false;
> + }
> +
> + /* Drain any handler that loaded ctx before hw_running was cleared */
> + synchronize_irq(ope->irq);
> +
> + /* If the hardware is running the context, reset it */
> + if (running) {
> + if (ope_hw_reset_sync(ope))
> + dev_err(ope->dev, "reset timeout during cancel\n");
> +
> + ope_job_finish(ctx, VB2_BUF_STATE_ERROR);
> + }
[Severity: Medium]
What prevents ope_sched_cancel() from racing with ope_remove() ->
ope_unregister() and accessing unclocked MMIO registers or timing out on
reset_complete?
CPU0 CPU1
ope_stop_streaming()
ope_sched_cancel()
lock(&ope->sched_lock)
running = true
ope->hw_running = false
unlock(&ope->sched_lock)
ope_remove()
disable_irq(ope->irq)
ope_unregister()
ope_sched_pause(ope)
ope_flush_hw(ope)
pm_runtime_force_suspend(ope->dev)
clk_bulk_disable_unprepare()
ope_hw_reset_sync(ope)
ope_write(..., OPE_TOP_RESET_CMD, ...) <-- clocks disabled / IRQ off
Because ope_remove() disables ope->irq and ope_unregister() forces runtime
suspend without serializing against ctx->vbq_lock or an in-flight
ope_sched_cancel(), ope_hw_reset_sync() can write to unclocked registers or
wait for a reset-done interrupt that can no longer fire.
[ ... ]
> +static void ope_adjust_power(struct ope_dev *ope)
> +{
[ ... ]
> + ret = dev_pm_opp_set_rate(ope->dev, pixclk);
> + if (ret)
> + dev_warn(ope->dev, "Failed to set OPP rate: %d\n", ret);
> +
> + ret = icc_set_bw(ope->icc_data, loadavg, loadpeak);
> + if (ret)
> + dev_warn(ope->dev, "Failed to set data path BW: %d\n", ret);
> +
> + ret = icc_set_bw(ope->icc_config, loadconfig, loadconfig * 5);
> + if (ret)
> + dev_warn(ope->dev, "Failed to set config path BW: %d\n", ret);
> +
> + cpu_latency_qos_update_request(&ope->qos_req, latency);
> +}
> +
> +static int ope_start_streaming(struct vb2_queue *q, unsigned int count)
> +{
[ ... ]
> + ret = pm_runtime_resume_and_get(ctx->ope->dev);
> + if (ret) {
> + dev_err(ctx->ope->dev, "resume failed (%d)\n", ret);
> + goto err_pipeline_stop;
> + }
> +
> + /* From here on a job is allowed to consume this queue's buffers */
> + scoped_guard(spinlock_irqsave, &ctx->queues_lock)
> + ctx->queues[idx].streaming = true;
> +
> + if (idx == OPE_QUEUE_FRAME_IN) {
> + ctx->fmt_in.sequence = 0;
> + ope_adjust_power(ctx->ope);
> + } else if (idx == OPE_QUEUE_DISP_OUT) {
> + ctx->fmt_out.sequence = 0;
> + }
[ ... ]
> +static void ope_stop_streaming(struct vb2_queue *q)
> +{
[ ... ]
> + if (idx == OPE_QUEUE_FRAME_IN)
> + ope_adjust_power(ctx->ope);
> +
> + pm_runtime_put(ctx->ope->dev);
[Severity: Medium]
Does calling ope_adjust_power() after pm_runtime_resume_and_get() and before
pm_runtime_put() leave the core clock enabled without its required cx OPP
vote?
On QCM2290, gcc_camss_ope_clk_src uses clk_rcg2_shared_ops. When
OPE_QUEUE_FRAME_IN stops streaming in ope_stop_streaming(),
ope_adjust_power() calls dev_pm_opp_set_rate(ope->dev, 0), which drops the
required-opps vote on the cx power domain without lowering the clock rate
while the device is still runtime-active and GCC_CAMSS_OPE_CLK is still
enabled. Similarly, in ope_start_streaming(), pm_runtime_resume_and_get()
runs ope_runtime_resume(), which enables the clocks at the cached frequency
from the previous session and writes MMIO registers in __ope_irq_init()
before ope_adjust_power() votes for the OPP and icc_config bandwidth (or even
before OPE_QUEUE_FRAME_IN starts streaming if OPE_QUEUE_DISP_OUT starts
first).
[ ... ]
> +static int ope_g_parm(struct file *file, void *priv, struct v4l2_streamparm *sp)
> +{
> + struct ope_ctx *ctx = ope_ctx_from_file(file);
> +
> + if (!V4L2_TYPE_IS_OUTPUT(sp->type))
> + return -EINVAL;
> +
> + sp->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
> + sp->parm.output.timeperframe = ctx->fmt_in.timeperframe;
> +
> + return 0;
> +}
> +
> +static int ope_s_parm(struct file *file, void *priv, struct v4l2_streamparm *sp)
> +{
> + struct ope_ctx *ctx = ope_ctx_from_file(file);
> + struct v4l2_fract *tpf = &sp->parm.output.timeperframe;
> +
> + if (!V4L2_TYPE_IS_OUTPUT(sp->type))
> + return -EINVAL;
[Severity: Low]
Should ope_g_parm() and ope_s_parm() return -ENOTTY instead of -EINVAL when
called on the capture video node?
Both ope_input and ope_disp_output share ope_video_ioctl_ops, so the V4L2
core enables VIDIOC_G_PARM and VIDIOC_S_PARM on ope_disp_output as well.
When userspace calls either ioctl on ope_disp_output with
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, returning -EINVAL instead of -ENOTTY
violates the V4L2 specification and fails v4l2-compliance.
[ ... ]
> +static void ope_queues_release(struct ope_ctx *ctx, struct v4l2_fh *fh)
> +{
> + bool owned = false;
> +
> + for (unsigned int i = 0; i < OPE_QUEUE_COUNT; i++)
> + owned |= (ctx->queues[i].vq.owner == fh);
> +
> + if (!owned)
> + return;
> +
> + ope_sched_cancel(ctx);
> +
> + for (unsigned int i = 0; i < OPE_QUEUE_COUNT; i++) {
> + struct vb2_queue *vq = &ctx->queues[i].vq;
> +
> + if (vq->owner != fh)
> + continue;
> +
> + vb2_queue_release(vq);
> + vq->owner = NULL;
> + }
> +
> + ope_sched_resume(ctx->ope);
> +}
[Severity: Medium]
Does closing the ope_params file descriptor while a frame is being processed
abort the active job on ope_input and ope_disp_output?
All three video nodes share ope->shared_ctx, and userspace opens each node as
a separate file handle. In ope_stop_streaming(), stopping OPE_QUEUE_PARAMS
intentionally avoids calling ope_sched_cancel(ctx) because parameters are
consumed by the CPU before a job starts. However, if the file handle owning
OPE_QUEUE_PARAMS (or a non-streaming queue) is closed, ope_queues_release()
unconditionally calls ope_sched_cancel(ctx), resetting the hardware and
failing the in-flight input and capture buffers with VB2_BUF_STATE_ERROR.
Since vb2_queue_release(vq) already invokes ope_stop_streaming(vq) when a
queue is streaming, the outer ope_sched_cancel() and ope_sched_resume() calls
here appear unnecessary.
[ ... ]
> +static int ope_v4l2_register(struct ope_dev *ope)
> +{
[ ... ]
> + ret = camss_isp_pipeline_register(ope->pipeline, &ope->v4l2_dev, ope_entity_descs,
> + ARRAY_SIZE(ope_entity_descs));
> + if (ret)
> + goto err_v4l2;
> +
> + /* Expose the graph to userspace only once every entity and link exists */
> + ret = media_device_register(&ope->mdev);
> + if (ret)
> + goto err_pipeline;
> +
> + ope->pipeline->drv_priv = ope;
> + return 0;
> +
> +err_pipeline:
> + camss_isp_pipeline_unregister(ope->pipeline);
> +err_v4l2:
> + /* Rely on the v4l2_device release callback to free @ope */
> + v4l2_device_unregister(&ope->v4l2_dev);
> + v4l2_device_put(&ope->v4l2_dev);
> + return ret;
> +}
> +
> +static void ope_unregister(struct ope_dev *ope)
> +{
> + struct ope_ctx *ctx;
> + int ret;
> +
> + ope_sched_pause(ope);
> + ope_flush_hw(ope);
> +
> + ret = pm_runtime_force_suspend(ope->dev);
> + if (ret)
> + pm_runtime_disable(ope->dev);
> +
> + if (ope->pipeline)
> + camss_isp_pipeline_unregister(ope->pipeline);
> +
> + scoped_guard(mutex, &ope->mutex) {
> + cpu_latency_qos_update_request(&ope->qos_req, PM_QOS_DEFAULT_VALUE);
> + v4l2_device_unregister(&ope->v4l2_dev);
> + }
> +
> + /* Invalidate the MC graph only once the hardware is marked gone */
> + media_device_unregister(&ope->mdev);
[Severity: High]
Can tearing down the media pipeline in camss_isp_pipeline_unregister() and
ope_unregister() corrupt pipe->start_count or dereference a NULL
pad->graph_obj.mdev?
In ope_start_streaming(), each streaming video node calls
video_device_pipeline_alloc_start(vdev), which sets pad->pipe on every pad in
the connected graph and increments pipe->start_count once per streaming
queue. During teardown:
drivers/media/platform/qcom/camss/ope/pipeline.c:camss_isp_pipeline_unregister() {
...
for (i = (int)pipeline->num_entities - 1; i >= 0; i--) {
struct camss_isp_pipeline_entity *slot = &pipeline->entities[i];
if (slot->registered) {
switch (slot->obj_type) {
case MEDIA_ENTITY_TYPE_VIDEO_DEVICE:
if (video_device_pipeline(&slot->vdev))
video_device_pipeline_stop(&slot->vdev);
video_unregister_device(&slot->vdev);
break;
case MEDIA_ENTITY_TYPE_V4L2_SUBDEV:
v4l2_device_unregister_subdev(&slot->subdev);
break;
...
}
Because pad->pipe is set across the entire graph whenever at least one queue
is streaming, video_device_pipeline(&slot->vdev) is non-NULL on all three
video devices even if only one or two queues started streaming. At i = 4
(ope_disp_output), video_device_pipeline_stop() is called even if only
ope_input (i = 0) was streaming; conversely, if two or more queues are
streaming (pipe->start_count >= 2), stopping ope_disp_output at i = 4 leaves
pipe active while the subdevs at i = 3 and i = 2 are unregistered with their
pads still linked in pipe->pads.
In addition, neither camss_isp_pipeline_unregister() nor ope_stop_streaming()
serializes its video_device_pipeline() check and video_device_pipeline_stop()
call under ctx->vbq_lock, and media_device_unregister(&ope->mdev) clears
pad->graph_obj.mdev to NULL on all remaining pads while concurrent
ope_start_streaming() or ope_stop_streaming() calls can still enter
media_pipeline_alloc_start() or media_pipeline_stop() and dereference
mdev = pad->graph_obj.mdev at mutex_lock(&mdev->graph_mutex).
[ ... ]
> +static int ope_init_power(struct ope_dev *ope)
> +{
[ ... ]
> + ret = devm_pm_runtime_enable(dev);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
[ ... ]
> +static int ope_probe(struct platform_device *pdev)
> +{
[ ... ]
> + ret = ope_init_power(ope);
> + if (ret) {
> + ret = dev_err_probe(dev, ret, "Power init failed\n");
> + goto err_free;
> + }
> +
> + ret = ope_init_mmio(ope);
> + if (ret) {
> + ret = dev_err_probe(dev, ret, "MMIO init failed\n");
> + goto err_free;
> + }
[Severity: Medium]
Can enabling runtime PM in ope_init_power() cause a NULL pointer dereference
in ope_runtime_resume() or ope_runtime_suspend()?
ope_init_power() calls devm_pm_runtime_enable(dev) before ope_init_mmio()
maps ope->base, ope->base_rd, ope->base_wr, and ope->base_pp, so a runtime
resume triggered via sysfs before ope_init_mmio() completes will dereference
NULL MMIO pointers in ope_runtime_resume() -> __ope_irq_init(). In addition,
if ope_probe() fails after ope_init_power(), the error path clears platform
drvdata to NULL and frees ope before devres disables runtime PM, so a runtime
PM callback running during probe unwind will dereference
dev_get_drvdata(dev) == NULL.
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0) {
> + ret = dev_err_probe(dev, irq, "Unable to get IRQ\n");
> + goto err_free;
> + }
> +
> + ope->irq = irq;
> +
> + /* Requested before the first resume, so that ope->irq is always valid */
> + ret = devm_request_irq(dev, ope->irq, ope_irq, IRQF_NO_AUTOEN,
> + "camss-ope", ope);
> + if (ret < 0) {
> + ret = dev_err_probe(dev, ret, "Requesting IRQ failed\n");
> + goto err_free;
> + }
> +
> + ret = ope_soft_reset(ope);
> + if (ret)
> + goto err_free;
> +
> + cpu_latency_qos_add_request(&ope->qos_req, PM_QOS_DEFAULT_VALUE);
> +
> + /* ope ownership is transferred to ope_v4l2_register(), even on failure */
> + ret = ope_v4l2_register(ope);
> + if (ret) {
> + platform_set_drvdata(pdev, NULL);
> + return dev_err_probe(dev, ret, "V4L2 registration failed\n");
> + }
> +
> + /* Everything @ope_irq() touches is in place, let interrupts in */
> + enable_irq(ope->irq);
[Severity: Medium]
Can userspace open and use /dev/videoN before subdevs, media links, and
ope->irq are ready?
In camss_isp_pipeline_register(), video_register_device() publishes
ope_input (index 0) and ope_params (index 1) to userspace before the subdevs
ope_proc (index 2) and ope_disp (index 3) are initialized and before pad
links are created. Furthermore, ope_probe() only calls enable_irq(ope->irq)
after ope_v4l2_register() returns, so an early opener that starts or cancels
streaming before enable_irq() runs will time out waiting for hardware
interrupts, and if a later step in ope_v4l2_register() fails after ope_open()
creates ope->shared_ctx, ope->shared_ctx is not cleaned up on the error path.
> diff --git a/drivers/media/platform/qcom/camss/ope/pipeline.h b/drivers/media/platform/qcom/camss/ope/pipeline.h
> new file mode 100644
> index 0000000000000..62e4116012ad0
> --- /dev/null
> +++ b/drivers/media/platform/qcom/camss/ope/pipeline.h
[ ... ]
> + * Fields used only for MEDIA_ENTITY_TYPE_VIDEO_DEVICE:
> + * @vdev.caps: V4L2_CAP_* device capabilities.
> + * The video device direction (VFL_DIR_RX/TX/M2M) is derived
> + * automatically from @caps by the builder.
> + * @vdev.drvdata: Opaque pointer set via video_set_drvdata() after registration.
> + * @vdev.fops: File operations (mandatory).
> + * @vdev.ioctl_ops: ioctl operations (may be NULL).
> + * @vdev.entity_ops: Media entity operations (may be NULL).
> + *
> + * Fields used only for MEDIA_ENTITY_TYPE_V4L2_SUBDEV:
> + * @subdev.ops: Subdev operations (mandatory).
> + * @subdev.internal_ops: Internal subdev operations (may be NULL).
> + * @subdev.entity_ops: Media entity operations (may be NULL).
> + */
> +struct camss_isp_entity_desc {
> + const char *name;
> + u32 obj_type;
> + u32 function;
> + const struct camss_isp_pad_desc *pads;
> +
> + union {
> + /* MEDIA_ENTITY_TYPE_VIDEO_DEVICE */
> + struct {
> + u32 caps;
> + void *drvdata;
> + const struct v4l2_file_operations *fops;
> + const struct v4l2_ioctl_ops *ioctl_ops;
> + const struct media_entity_operations *entity_ops;
> + } vdev;
> + /* MEDIA_ENTITY_TYPE_V4L2_SUBDEV */
> + struct {
> + const struct v4l2_subdev_ops *ops;
> + const struct v4l2_subdev_internal_ops *internal_ops;
> + const struct media_entity_operations *entity_ops;
> + } subdev;
> + };
> +};
[Severity: Low]
The kernel-doc comment for struct camss_isp_entity_desc describes the nested
@vdev.* and @subdev.* fields, but omits @vdev: and @subdev: entries for the
named inner structs themselves, which causes scripts/kernel-doc to warn about
undescribed struct members 'vdev' and 'subdev'.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261106-camss-isp-ope-v14-0-49c426cd2715@oss.qualcomm.com?part=6
next prev parent reply other threads:[~2026-11-06 14:42 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-06 13:55 [PATCH v14 0/9] media: qcom: camss: CAMSS Offline Processing Engine support Loic Poulain
2026-11-06 13:55 ` [PATCH v14 1/9] media: qcom: camss: Add V4L2 meta format for CAMSS ISP parameters Loic Poulain
2026-11-06 13:55 ` [PATCH v14 2/9] dt-bindings: media: qcom: Add CAMSS Offline Processing Engine (OPE) Loic Poulain
2026-11-06 13:55 ` [PATCH v14 3/9] dt-bindings: media: qcom,qcm2290-camss-ope: Document shikra compatible Loic Poulain
2026-11-06 13:55 ` [PATCH v14 4/9] media: uapi: Add CAMSS ISP configuration definition Loic Poulain
2026-11-06 13:55 ` [PATCH v14 5/9] media: Documentation: uapi: Add qcom-camss ISP params documentation Loic Poulain
2026-11-06 13:55 ` [PATCH v14 6/9] media: qcom: camss: Add CAMSS Offline Processing Engine driver Loic Poulain
2026-11-06 14:42 ` sashiko-bot [this message]
2026-11-06 13:55 ` [PATCH v14 7/9] arm64: dts: qcom: agatti: Add OPE node Loic Poulain
2026-11-06 14:13 ` sashiko-bot
2026-11-06 13:55 ` [PATCH v14 8/9] arm64: dts: qcom: shikra: " Loic Poulain
2026-11-06 13:55 ` [PATCH v14 9/9] arm64: defconfig: Enable Qualcomm CAMSS OPE driver Loic Poulain
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-201600@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=loic.poulain@oss.qualcomm.com \
--cc=media-ci@linuxtv.org \
--cc=robh@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox