From 05ec592de0dd984b3c639f8e7dec9441b5bcf83b Mon Sep 17 00:00:00 2001 From: Fritz Koenig Date: Tue, 24 Mar 2026 14:00:06 -0700 Subject: [PATCH 001/394] Documentation: media: Fix v4l2_vp9_segmentation feature_data is defined as __s16 in the header. Signed-off-by: Fritz Koenig Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../userspace-api/media/v4l/ext-ctrls-codec-stateless.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst index 3b1e05c6eb13..d7a3b8ef03a7 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst @@ -1877,7 +1877,7 @@ params syntax' of the :ref:`vp9` specification for more details. :stub-columns: 0 :widths: 1 1 2 - * - __u8 + * - __s16 - ``feature_data[8][4]`` - Data attached to each feature. Data entry is only valid if the feature is enabled. The array shall be indexed with segment number as the first dimension From afbe4bc252d90a6f8fad869b06d5430f615f22f9 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 24 Mar 2026 11:13:26 +0800 Subject: [PATCH 002/394] media: v4l2-ctrls: validate HEVC active reference counts HEVC slice parameters are shared stateless V4L2 controls, but the common validation path does not verify the active L0/L1 reference counts before driver-specific code consumes them. The original report came from Cedrus, but the active count bounds are not Cedrus-specific. Validate them in the common HEVC slice control path so stateless HEVC drivers get the same basic guarantees as soon as the control is queued. Do not reject ref_idx_l0/ref_idx_l1 entries here. Existing userspace may use out-of-range sentinel values such as 0xff for missing references, and some hardware can use that information for concealment. Keep this common check limited to the active reference counts. Fixes: d395a78db9eab ("media: hevc: Add decode params control") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-ctrls-core.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index 6b375720e395..ba047d7d8601 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -971,6 +971,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, struct v4l2_ctrl_hevc_ext_sps_st_rps *p_hevc_st_rps; struct v4l2_ctrl_hevc_sps *p_hevc_sps; struct v4l2_ctrl_hevc_pps *p_hevc_pps; + struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params; struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering; struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params; struct v4l2_area *area; @@ -1260,6 +1261,18 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, break; case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: + p_hevc_slice_params = p; + + if (p_hevc_slice_params->num_ref_idx_l0_active_minus1 >= + V4L2_HEVC_DPB_ENTRIES_NUM_MAX) + return -EINVAL; + + if (p_hevc_slice_params->slice_type != V4L2_HEVC_SLICE_TYPE_B) + break; + + if (p_hevc_slice_params->num_ref_idx_l1_active_minus1 >= + V4L2_HEVC_DPB_ENTRIES_NUM_MAX) + return -EINVAL; break; case V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS: From 10358ea986c3c85516d1c8206486464f79d36e76 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 24 Mar 2026 16:08:56 +0800 Subject: [PATCH 003/394] media: cedrus: skip invalid H.264 reference list entries Cedrus consumes H.264 ref_pic_list0/ref_pic_list1 entries from the stateless slice control and later uses their indices to look up decode->dpb[] in _cedrus_write_ref_list(). Rejecting such controls in cedrus_try_ctrl() would break existing userspace, since stateless H.264 reference lists may legitimately carry out-of-range indices for missing references. Instead, guard the actual DPB lookup in Cedrus and skip entries whose indices do not fit the fixed V4L2_H264_NUM_DPB_ENTRIES array. This keeps the fix local to the driver use site and avoids out-of-bounds reads from malformed or unsupported reference list entries. Fixes: e000e1fa4bdbd ("media: uapi: h264: Update reference lists") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Reviewed-by: Nicolas Dufresne Acked-by: Jernej Skrabec Tested-by: Chen-Yu Tsai Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/sunxi/cedrus/cedrus_h264.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c index 3e2843ef6cce..fc54d993b11f 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c @@ -210,6 +210,9 @@ static void _cedrus_write_ref_list(struct cedrus_ctx *ctx, u8 dpb_idx; dpb_idx = ref_list[i].index; + if (dpb_idx >= V4L2_H264_NUM_DPB_ENTRIES) + continue; + dpb = &decode->dpb[dpb_idx]; if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) From 018f2dc3a42098d3b04edd1480adc51d268adb14 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 25 Dec 2025 18:36:12 +0800 Subject: [PATCH 004/394] dt-bindings: media: sun4i-a10-video-engine: Add interconnect properties The Allwinner video engine sits behind the MBUS that is represented as an interconnect. Make sure that the interconnect properties are valid in the binding. Fixes: d41662e52a03 ("media: dt-bindings: media: allwinner,sun4i-a10-video-engine: Add R40 compatible") Cc: stable@vger.kernel.org Signed-off-by: Chen-Yu Tsai Acked-by: Rob Herring (Arm) Acked-by: Jernej Skrabec Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../media/allwinner,sun4i-a10-video-engine.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml index 541325f900a1..01f2afa023f0 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml @@ -63,6 +63,16 @@ properties: CMA pool to use for buffers allocation instead of the default CMA pool. + # FIXME: This should be made required eventually once every SoC will + # have the MBUS declared. + interconnects: + maxItems: 1 + + # FIXME: This should be made required eventually once every SoC will + # have the MBUS declared. + interconnect-names: + const: dma-mem + required: - compatible - reg From b1845a227fda37b2fe5327df3ca0015d7e290235 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Wed, 1 Apr 2026 11:44:15 +0200 Subject: [PATCH 005/394] media: mtk-jpeg: cancel workqueue on release for supported platforms only Since a recent fix the mtk_jpeg_release function cancels any pending or running work present in the driver workqueue using cancel_work_sync function. Currently, only the multicore based variants use this workqueue and they have the jpeg_worker platform data field initialized with a workqueue callback function. For the others, this field value remain NULL by default. The cancel_work_sync function is unconditionally called in mtk_jpeg_release function, even for the variants that do not use the workqueue. This call generates a WARN_ON print in __flush_work because the workqueue callback function presence check fails in __flush_work function (used by cancel_work_sync). So, to avoid these warnings, call cancel_work_sync only if a workqueue callback is defined in platform data. Fixes: 34c519feef3e ("media: mtk-jpeg: fix use-after-free in release path due to uncancelled work") Cc: stable@vger.kernel.org Signed-off-by: Louis-Alexis Eyraud Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index 8c684756d5fc..d147ec483081 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -1202,7 +1202,8 @@ static int mtk_jpeg_release(struct file *file) struct mtk_jpeg_dev *jpeg = video_drvdata(file); struct mtk_jpeg_ctx *ctx = mtk_jpeg_file_to_ctx(file); - cancel_work_sync(&ctx->jpeg_work); + if (jpeg->variant->jpeg_worker) + cancel_work_sync(&ctx->jpeg_work); mutex_lock(&jpeg->lock); v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); v4l2_ctrl_handler_free(&ctx->ctrl_hdl); From b20157147089a9c16a38c7810e2fe6f2df8e3277 Mon Sep 17 00:00:00 2001 From: Brandon Brnich Date: Fri, 20 Mar 2026 13:05:26 -0500 Subject: [PATCH 006/394] media: chips-media: wave5: Move src_buf Removal to finish_encode During encoder processing, there is a case where the IRQ response could return the buffer back to userspace via v4l2_m2m_buf_done call. In this time, userspace could queue up this same buffer before start_encode removes the index from the ready queue. This would then lead to a case where the buffer in the ready queue could be a self loop due to the WRITE_ONCE(prev->next, new) call in __list_add. When __list_del is finally called, the loop is already made so nothing points back to ready queue list head and pointers are poisoned. A buffer should not be marked as DONE before the buffer is removed from m2m ready queue. Move removal entirely to finish_encode. Fixes: 9707a6254a8a6 ("media: chips-media: wave5: Add the v4l2 layer") Cc: stable@vger.kernel.org Signed-off-by: Brandon Brnich Tested-by: Jackson Lee Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../chips-media/wave5/wave5-vpu-enc.c | 29 +++---------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index 7613fcdbafed..c605a91718d8 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -226,13 +226,6 @@ static int start_encode(struct vpu_instance *inst, u32 *fail_res) } else { dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame success\n", __func__); - /* - * Remove the source buffer from the ready-queue now and finish - * it in the videobuf2 framework once the index is returned by the - * firmware in finish_encode - */ - if (src_buf) - v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, src_buf->vb2_buf.index); } return 0; @@ -259,27 +252,13 @@ static void wave5_vpu_enc_finish_encode(struct vpu_instance *inst) __func__, enc_output_info.pic_type, enc_output_info.recon_frame_index, enc_output_info.enc_src_idx, enc_output_info.enc_pic_byte, enc_output_info.pts); - /* - * The source buffer will not be found in the ready-queue as it has been - * dropped after sending of the encode firmware command, locate it in - * the videobuf2 queue directly - */ if (enc_output_info.enc_src_idx >= 0) { - struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_src_vq(m2m_ctx), - enc_output_info.enc_src_idx); - if (vb->state != VB2_BUF_STATE_ACTIVE) - dev_warn(inst->dev->dev, - "%s: encoded buffer (%d) was not in ready queue %i.", - __func__, enc_output_info.enc_src_idx, vb->state); - else - src_buf = to_vb2_v4l2_buffer(vb); - - if (src_buf) { + src_buf = v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, enc_output_info.enc_src_idx); + if (!src_buf) { + dev_warn(inst->dev->dev, "%s: no source buffer found\n", __func__); + } else { inst->timestamp = src_buf->vb2_buf.timestamp; v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - } else { - dev_warn(inst->dev->dev, "%s: no source buffer with index: %d found\n", - __func__, enc_output_info.enc_src_idx); } } From 5d801b59633f6af60bb0e18d3bbb18b7b040a6d9 Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Tue, 24 Mar 2026 14:03:57 +0900 Subject: [PATCH 007/394] media: v4l2-controls: Add control for background detection Add a generic V4L2 boolean control V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION that allows encoders to detect background regions in a frame and use fewer bits or skip mode to encode them, potentially reducing bitrate for streams with stationary scenes. Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst | 6 ++++++ drivers/media/v4l2-core/v4l2-ctrls-defs.c | 2 ++ include/uapi/linux/v4l2-controls.h | 2 ++ 3 files changed, 10 insertions(+) diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst index c8890cb5e00a..ab865a1a6ba9 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst @@ -737,6 +737,12 @@ enum v4l2_mpeg_video_frame_skip_mode - Enable writing sample aspect ratio in the Video Usability Information. Applicable to the H264 encoder. +``V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION (boolean)`` + If enabled, the encoder detect a background region in frame and + use low bits or skip mode to encode the background region. + If a lot of scenes are stationary or background, It may help to + reduce the video bitrate. Applicable to the encoder. + .. _v4l2-mpeg-video-h264-vui-sar-idc: ``V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC`` diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c index 551426c4cd01..e062f2088490 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c @@ -889,6 +889,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY: return "Display Delay"; case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE: return "Display Delay Enable"; case V4L2_CID_MPEG_VIDEO_AU_DELIMITER: return "Generate Access Unit Delimiters"; + case V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION: return "Background Detection"; case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: return "H263 I-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: return "H263 P-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: return "H263 B-Frame QP Value"; @@ -1296,6 +1297,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: case V4L2_CID_MPEG_VIDEO_AU_DELIMITER: + case V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION: case V4L2_CID_WIDE_DYNAMIC_RANGE: case V4L2_CID_IMAGE_STABILIZATION: case V4L2_CID_RDS_RECEPTION: diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 68dd0c4e47b2..affec0ab4781 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -464,6 +464,8 @@ enum v4l2_mpeg_video_intra_refresh_period_type { V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC = 1, }; +#define V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION (V4L2_CID_CODEC_BASE + 238) + /* CIDs for the MPEG-2 Part 2 (H.262) codec */ #define V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL (V4L2_CID_CODEC_BASE+270) enum v4l2_mpeg_video_mpeg2_level { From ffa7cb083c134042e2125cf37f488b407573607f Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Tue, 24 Mar 2026 14:03:58 +0900 Subject: [PATCH 008/394] media: chips-media: wave5: Add support for background detection Implement V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION in the Wave5 encoder driver. When enabled, the hardware detects background regions in a frame and uses fewer bits or skip mode to encode them, reducing bitrate for streams with stationary scenes. Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/chips-media/wave5/wave5-hw.c | 4 +++- drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c | 7 +++++++ drivers/media/platform/chips-media/wave5/wave5-vpuapi.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c index 687ce6ccf3ae..c516d125f553 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-hw.c +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c @@ -49,6 +49,7 @@ #define FASTIO_ADDRESS_MASK GENMASK(15, 0) #define SEQ_PARAM_PROFILE_MASK GENMASK(30, 24) +#define SEQ_BG_PARAM_REG_DATA 0x3800410 static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason, const char *func); @@ -1838,7 +1839,8 @@ int wave5_vpu_enc_init_seq(struct vpu_instance *inst) vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_4_7, 0); vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_ROT_PARAM, rot_mir_mode); - vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_BG_PARAM, 0); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_BG_PARAM, + SEQ_BG_PARAM_REG_DATA | p_param->bg_detection); vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_LAMBDA_ADDR, 0); vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CONF_WIN_TOP_BOT, p_param->conf_win_bot << 16 | p_param->conf_win_top); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index c605a91718d8..e806973305ce 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -759,6 +759,9 @@ static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_BITRATE: inst->bit_rate = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION: + inst->enc_param.bg_detection = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_GOP_SIZE: inst->enc_param.avc_idr_period = ctrl->val; break; @@ -1184,6 +1187,7 @@ static int wave5_set_enc_openparam(struct enc_open_param *open_param, open_param->wave_param.beta_offset_div2 = input.beta_offset_div2; open_param->wave_param.decoding_refresh_type = input.decoding_refresh_type; open_param->wave_param.intra_period = input.intra_period; + open_param->wave_param.bg_detection = input.bg_detection; if (inst->std == W_HEVC_ENC) { if (input.intra_period == 0) { open_param->wave_param.decoding_refresh_type = DEC_REFRESH_TYPE_IDR; @@ -1679,6 +1683,9 @@ static int wave5_vpu_open_enc(struct file *filp) v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_AU_DELIMITER, 0, 1, 1, 1); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION, + 0, 1, 1, 0); v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h index c64135769869..dc31689e0d27 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h @@ -570,6 +570,7 @@ struct enc_wave_param { u32 transform8x8_enable: 1; /* enable 8x8 intra prediction and 8x8 transform */ u32 mb_level_rc_enable: 1; /* enable MB-level rate control */ u32 forced_idr_header_enable: 1; /* enable header encoding before IDR frame */ + u32 bg_detection: 1; /* enable background detection */ }; struct enc_open_param { From f8505f9d6b223a26854003bfdba1a0772457886d Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Tue, 24 Mar 2026 14:03:59 +0900 Subject: [PATCH 009/394] media: chips-media: wave5: Support CBP profile Constrained Baseline Profile (CBP) and Baseline Profile (BP) have been treated as the same. Introduce the ability to differentiate between the two. Fixes: 9707a6254a8a ("media: chips-media: wave5: Add the v4l2 layer") Cc: stable@vger.kernel.org Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Tested-by: Brandon Brnich Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/chips-media/wave5/wave5-hw.c | 3 +++ drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c | 5 ++++- drivers/media/platform/chips-media/wave5/wave5-vpuapi.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c index c516d125f553..2392bce8d840 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-hw.c +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c @@ -1763,6 +1763,9 @@ int wave5_vpu_enc_init_seq(struct vpu_instance *inst) (p_param->skip_intra_trans << 25) | (p_param->strong_intra_smooth_enable << 27) | (p_param->en_still_picture << 30); + else if (inst->std == W_AVC_ENC) + reg_val |= (p_param->constraint_set1_flag << 29); + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SPS_PARAM, reg_val); reg_val = (p_param->lossless_enable) | diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index e806973305ce..e6ac76635178 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -918,6 +918,8 @@ static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: inst->enc_param.profile = H264_PROFILE_BP; inst->bit_depth = 8; + if (ctrl->val == V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) + inst->enc_param.constraint_set1_flag = 1; break; case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: inst->enc_param.profile = H264_PROFILE_MP; @@ -1194,6 +1196,7 @@ static int wave5_set_enc_openparam(struct enc_open_param *open_param, open_param->wave_param.intra_period = input.avc_idr_period; } } else { + open_param->wave_param.constraint_set1_flag = input.constraint_set1_flag; open_param->wave_param.avc_idr_period = input.avc_idr_period; } open_param->wave_param.entropy_coding_mode = input.entropy_coding_mode; @@ -1666,7 +1669,7 @@ static int wave5_vpu_open_enc(struct file *filp) -6, 6, 1, 0); v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, - 0, 1, 1, 1); + 0, 1, 1, 0); v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, 0, 1, 1, 0); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h index dc31689e0d27..7b08fef58217 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h @@ -570,6 +570,7 @@ struct enc_wave_param { u32 transform8x8_enable: 1; /* enable 8x8 intra prediction and 8x8 transform */ u32 mb_level_rc_enable: 1; /* enable MB-level rate control */ u32 forced_idr_header_enable: 1; /* enable header encoding before IDR frame */ + u32 constraint_set1_flag: 1; /* enable CBP */ u32 bg_detection: 1; /* enable background detection */ }; From 37340f0744442273facd9a785ecab10af985e4db Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Tue, 24 Mar 2026 14:04:00 +0900 Subject: [PATCH 010/394] media: chips-media: wave5: Add Support for Packed YUV422 Formats Wave5 encoder is capable of reading in numerous raw pixel formats. Expose these formats and properly configure encoder if selected. Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Tested-by: Brandon Brnich Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../platform/chips-media/wave5/wave5-helper.h | 2 +- .../chips-media/wave5/wave5-vpu-enc.c | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.h b/drivers/media/platform/chips-media/wave5/wave5-helper.h index d61fdbda359d..e6f241012c3b 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-helper.h +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.h @@ -11,7 +11,7 @@ #include "wave5-vpu.h" #define FMT_TYPES 2 -#define MAX_FMTS 12 +#define MAX_FMTS 16 const char *state_to_str(enum vpu_instance_state state); void wave5_cleanup_instance(struct vpu_instance *inst, struct file *filp); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index e6ac76635178..e6c94b6f2671 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -90,6 +90,22 @@ static const struct vpu_format enc_fmt_list[FMT_TYPES][MAX_FMTS] = { .v4l2_pix_fmt = V4L2_PIX_FMT_NV61M, .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YUYV, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_YVYU, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_UYVY, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, + { + .v4l2_pix_fmt = V4L2_PIX_FMT_VYUY, + .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW], + }, } }; @@ -1140,6 +1156,22 @@ static int wave5_set_enc_openparam(struct enc_open_param *open_param, else open_param->src_format = FORMAT_420; + switch (info->format) { + case V4L2_PIX_FMT_YUYV: + open_param->packed_format = PACKED_YUYV; + break; + case V4L2_PIX_FMT_YVYU: + open_param->packed_format = PACKED_YVYU; + break; + case V4L2_PIX_FMT_UYVY: + open_param->packed_format = PACKED_UYVY; + break; + case V4L2_PIX_FMT_VYUY: + open_param->packed_format = PACKED_VYUY; + break; + default: + break; + } open_param->wave_param.gop_preset_idx = PRESET_IDX_IPP_SINGLE; open_param->wave_param.hvs_qp_scale = 2; open_param->wave_param.hvs_max_delta_qp = 10; From e0f5d6ae76423ec5b6f97c7c3e1f02187b988afd Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Tue, 24 Jun 2025 14:29:38 +0200 Subject: [PATCH 011/394] media: verisilicon: Export only needed pixels formats Some pixel formats can only be produced if the decoder outputs reference pictures directly. In some cases, such as AV1 film-grain, the use of the post-processor is strictly required. In this case, only enumerate the post-processor supported formats. The exception is when V4L2_FMTDESC_FLAG_ENUM_ALL is set, in this case, we enumerate everything regardless of the state. Signed-off-by: Benjamin Gaignard Fixes: bcd4f091cf1e ("media: verisilicon: Use V4L2_FMTDESC_FLAG_ENUM_ALL flag") Cc: stable@vger.kernel.org Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/hantro_v4l2.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c index fcf3bd9bcda2..83af9fa1ce94 100644 --- a/drivers/media/platform/verisilicon/hantro_v4l2.c +++ b/drivers/media/platform/verisilicon/hantro_v4l2.c @@ -222,6 +222,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv, unsigned int num_fmts, i, j = 0; bool skip_mode_none, enum_all_formats; u32 index = f->index & ~V4L2_FMTDESC_FLAG_ENUM_ALL; + bool need_postproc = ctx->need_postproc; /* * If the V4L2_FMTDESC_FLAG_ENUM_ALL flag is set, we want to enumerate all @@ -230,6 +231,9 @@ static int vidioc_enum_fmt(struct file *file, void *priv, enum_all_formats = !!(f->index & V4L2_FMTDESC_FLAG_ENUM_ALL); f->index = index; + if (enum_all_formats) + need_postproc = HANTRO_AUTO_POSTPROC; + /* * When dealing with an encoder: * - on the capture side we want to filter out all MODE_NONE formats. @@ -242,7 +246,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv, */ skip_mode_none = capture == ctx->is_encoder; - formats = hantro_get_formats(ctx, &num_fmts, HANTRO_AUTO_POSTPROC); + formats = hantro_get_formats(ctx, &num_fmts, need_postproc); for (i = 0; i < num_fmts; i++) { bool mode_none = formats[i].codec_mode == HANTRO_MODE_NONE; fmt = &formats[i]; From 166ea048d9a3ae29248ed1ce0761b975bced76e0 Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Thu, 2 Apr 2026 10:06:36 -0400 Subject: [PATCH 012/394] media: rkvdec: Introduce a global bitwriter helper The use of structures with bitfields is good when the values are somewhat aligned. More mis-alignement means that compilers need to do more gymnastics to edit the fields values. Some cases have been reported with CLang on specific architectures like armhf and hexagon, where the compiler would allocate a bigger local stack than needed or even completely freeze during compilation. Some fixes have been provided to ease the issues, but the real fix here is to use a bitwriter instead of heavily unaligned bitfields. This is a preparation commit to provide a global bitwriter interface for the whole driver. Signed-off-by: Detlev Casanova Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../rockchip/rkvdec/rkvdec-bitwriter.h | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h new file mode 100644 index 000000000000..288467941abf --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Video Decoder bit writer + * + * Copyright (C) 2026 Collabora, Ltd. + * Detlev Casanova + * Copyright (C) 2019 Collabora, Ltd. + * Boris Brezillon + */ + +#ifndef RKVDEC_BIT_WRITER_H_ +#define RKVDEC_BIT_WRITER_H_ + +#include +#include + +struct rkvdec_bw_field { + u16 offset; + u8 len; +}; + +#define BW_FIELD(_offset, _len) ((struct rkvdec_bw_field){ _offset, _len }) + +static inline void rkvdec_set_bw_field(u32 *buf, struct rkvdec_bw_field field, u32 value) +{ + u8 bit = field.offset % 32; + u16 word = field.offset / 32; + u64 mask = GENMASK_ULL(bit + field.len - 1, bit); + u64 val = ((u64)value << bit) & mask; + + buf[word] &= ~mask; + buf[word] |= val; + if (bit + field.len > 32) { + buf[word + 1] &= ~(mask >> 32); + buf[word + 1] |= val >> 32; + } +} + +#endif /* RKVDEC_BIT_WRITER_H_ */ From adfc4fa79b62a76cd5509b0339fab971404c46d2 Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Thu, 2 Apr 2026 10:06:37 -0400 Subject: [PATCH 013/394] media: rkvdec: Use the global bitwriter instead of local one Both rkvdec-h264.c and rkvdec-hevc.c use their own bitwriter function and macros. Move to using the global one introduced before. Signed-off-by: Detlev Casanova Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../platform/rockchip/rkvdec/rkvdec-h264.c | 109 +++++------ .../platform/rockchip/rkvdec/rkvdec-hevc.c | 171 ++++++++---------- 2 files changed, 119 insertions(+), 161 deletions(-) diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c index d3202cecb988..ffa606038192 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c @@ -16,6 +16,7 @@ #include "rkvdec-regs.h" #include "rkvdec-cabac.h" #include "rkvdec-h264-common.h" +#include "rkvdec-bitwriter.h" /* Size with u32 units. */ #define RKV_CABAC_INIT_BUFFER_SIZE (3680 + 128) @@ -25,56 +26,48 @@ struct rkvdec_sps_pps_packet { u32 info[8]; }; -struct rkvdec_ps_field { - u16 offset; - u8 len; -}; - -#define PS_FIELD(_offset, _len) \ - ((struct rkvdec_ps_field){ _offset, _len }) - -#define SEQ_PARAMETER_SET_ID PS_FIELD(0, 4) -#define PROFILE_IDC PS_FIELD(4, 8) -#define CONSTRAINT_SET3_FLAG PS_FIELD(12, 1) -#define CHROMA_FORMAT_IDC PS_FIELD(13, 2) -#define BIT_DEPTH_LUMA PS_FIELD(15, 3) -#define BIT_DEPTH_CHROMA PS_FIELD(18, 3) -#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG PS_FIELD(21, 1) -#define LOG2_MAX_FRAME_NUM_MINUS4 PS_FIELD(22, 4) -#define MAX_NUM_REF_FRAMES PS_FIELD(26, 5) -#define PIC_ORDER_CNT_TYPE PS_FIELD(31, 2) -#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 PS_FIELD(33, 4) -#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG PS_FIELD(37, 1) -#define PIC_WIDTH_IN_MBS PS_FIELD(38, 9) -#define PIC_HEIGHT_IN_MBS PS_FIELD(47, 9) -#define FRAME_MBS_ONLY_FLAG PS_FIELD(56, 1) -#define MB_ADAPTIVE_FRAME_FIELD_FLAG PS_FIELD(57, 1) -#define DIRECT_8X8_INFERENCE_FLAG PS_FIELD(58, 1) -#define MVC_EXTENSION_ENABLE PS_FIELD(59, 1) -#define NUM_VIEWS PS_FIELD(60, 2) -#define VIEW_ID(i) PS_FIELD(62 + ((i) * 10), 10) -#define NUM_ANCHOR_REFS_L(i) PS_FIELD(82 + ((i) * 11), 1) -#define ANCHOR_REF_L(i) PS_FIELD(83 + ((i) * 11), 10) -#define NUM_NON_ANCHOR_REFS_L(i) PS_FIELD(104 + ((i) * 11), 1) -#define NON_ANCHOR_REFS_L(i) PS_FIELD(105 + ((i) * 11), 10) -#define PIC_PARAMETER_SET_ID PS_FIELD(128, 8) -#define PPS_SEQ_PARAMETER_SET_ID PS_FIELD(136, 5) -#define ENTROPY_CODING_MODE_FLAG PS_FIELD(141, 1) -#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG PS_FIELD(142, 1) -#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) PS_FIELD(143 + ((i) * 5), 5) -#define WEIGHTED_PRED_FLAG PS_FIELD(153, 1) -#define WEIGHTED_BIPRED_IDC PS_FIELD(154, 2) -#define PIC_INIT_QP_MINUS26 PS_FIELD(156, 7) -#define PIC_INIT_QS_MINUS26 PS_FIELD(163, 6) -#define CHROMA_QP_INDEX_OFFSET PS_FIELD(169, 5) -#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG PS_FIELD(174, 1) -#define CONSTRAINED_INTRA_PRED_FLAG PS_FIELD(175, 1) -#define REDUNDANT_PIC_CNT_PRESENT PS_FIELD(176, 1) -#define TRANSFORM_8X8_MODE_FLAG PS_FIELD(177, 1) -#define SECOND_CHROMA_QP_INDEX_OFFSET PS_FIELD(178, 5) -#define SCALING_LIST_ENABLE_FLAG PS_FIELD(183, 1) -#define SCALING_LIST_ADDRESS PS_FIELD(184, 32) -#define IS_LONG_TERM(i) PS_FIELD(216 + (i), 1) +#define SEQ_PARAMETER_SET_ID BW_FIELD(0, 4) +#define PROFILE_IDC BW_FIELD(4, 8) +#define CONSTRAINT_SET3_FLAG BW_FIELD(12, 1) +#define CHROMA_FORMAT_IDC BW_FIELD(13, 2) +#define BIT_DEPTH_LUMA BW_FIELD(15, 3) +#define BIT_DEPTH_CHROMA BW_FIELD(18, 3) +#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG BW_FIELD(21, 1) +#define LOG2_MAX_FRAME_NUM_MINUS4 BW_FIELD(22, 4) +#define MAX_NUM_REF_FRAMES BW_FIELD(26, 5) +#define PIC_ORDER_CNT_TYPE BW_FIELD(31, 2) +#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 BW_FIELD(33, 4) +#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG BW_FIELD(37, 1) +#define PIC_WIDTH_IN_MBS BW_FIELD(38, 9) +#define PIC_HEIGHT_IN_MBS BW_FIELD(47, 9) +#define FRAME_MBS_ONLY_FLAG BW_FIELD(56, 1) +#define MB_ADAPTIVE_FRAME_FIELD_FLAG BW_FIELD(57, 1) +#define DIRECT_8X8_INFERENCE_FLAG BW_FIELD(58, 1) +#define MVC_EXTENSION_ENABLE BW_FIELD(59, 1) +#define NUM_VIEWS BW_FIELD(60, 2) +#define VIEW_ID(i) BW_FIELD(62 + ((i) * 10), 10) +#define NUM_ANCHOR_REFS_L(i) BW_FIELD(82 + ((i) * 11), 1) +#define ANCHOR_REF_L(i) BW_FIELD(83 + ((i) * 11), 10) +#define NUM_NON_ANCHOR_REFS_L(i) BW_FIELD(104 + ((i) * 11), 1) +#define NON_ANCHOR_REFS_L(i) BW_FIELD(105 + ((i) * 11), 10) +#define PIC_PARAMETER_SET_ID BW_FIELD(128, 8) +#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(136, 5) +#define ENTROPY_CODING_MODE_FLAG BW_FIELD(141, 1) +#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG BW_FIELD(142, 1) +#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) BW_FIELD(143 + ((i) * 5), 5) +#define WEIGHTED_PRED_FLAG BW_FIELD(153, 1) +#define WEIGHTED_BIPRED_IDC BW_FIELD(154, 2) +#define PIC_INIT_QP_MINUS26 BW_FIELD(156, 7) +#define PIC_INIT_QS_MINUS26 BW_FIELD(163, 6) +#define CHROMA_QP_INDEX_OFFSET BW_FIELD(169, 5) +#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG BW_FIELD(174, 1) +#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(175, 1) +#define REDUNDANT_PIC_CNT_PRESENT BW_FIELD(176, 1) +#define TRANSFORM_8X8_MODE_FLAG BW_FIELD(177, 1) +#define SECOND_CHROMA_QP_INDEX_OFFSET BW_FIELD(178, 5) +#define SCALING_LIST_ENABLE_FLAG BW_FIELD(183, 1) +#define SCALING_LIST_ADDRESS BW_FIELD(184, 32) +#define IS_LONG_TERM(i) BW_FIELD(216 + (i), 1) /* Data structure describing auxiliary buffer format. */ struct rkvdec_h264_priv_tbl { @@ -91,20 +84,6 @@ struct rkvdec_h264_ctx { struct rkvdec_regs regs; }; -static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value) -{ - u8 bit = field.offset % 32, word = field.offset / 32; - u64 mask = GENMASK_ULL(bit + field.len - 1, bit); - u64 val = ((u64)value << bit) & mask; - - buf[word] &= ~mask; - buf[word] |= val; - if (bit + field.len > 32) { - buf[word + 1] &= ~(mask >> 32); - buf[word + 1] |= val >> 32; - } -} - static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run) { @@ -128,7 +107,7 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id]; memset(hw_ps, 0, sizeof(*hw_ps)); -#define WRITE_PPS(value, field) set_ps_field(hw_ps->info, field, value) +#define WRITE_PPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value) /* write sps */ WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID); WRITE_PPS(sps->profile_idc, PROFILE_IDC); diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c index ac8b825d080a..87abf93dfd5e 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c @@ -18,6 +18,7 @@ #include "rkvdec-regs.h" #include "rkvdec-cabac.h" #include "rkvdec-hevc-common.h" +#include "rkvdec-bitwriter.h" /* Size in u8/u32 units. */ #define RKV_SCALING_LIST_SIZE 1360 @@ -34,80 +35,72 @@ struct rkvdec_rps_packet { u32 info[RKV_RPS_SIZE]; }; -struct rkvdec_ps_field { - u16 offset; - u8 len; -}; - -#define PS_FIELD(_offset, _len) \ - ((struct rkvdec_ps_field){ _offset, _len }) - /* SPS */ -#define VIDEO_PARAMETER_SET_ID PS_FIELD(0, 4) -#define SEQ_PARAMETER_SET_ID PS_FIELD(4, 4) -#define CHROMA_FORMAT_IDC PS_FIELD(8, 2) -#define PIC_WIDTH_IN_LUMA_SAMPLES PS_FIELD(10, 13) -#define PIC_HEIGHT_IN_LUMA_SAMPLES PS_FIELD(23, 13) -#define BIT_DEPTH_LUMA PS_FIELD(36, 4) -#define BIT_DEPTH_CHROMA PS_FIELD(40, 4) -#define LOG2_MAX_PIC_ORDER_CNT_LSB PS_FIELD(44, 5) -#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE PS_FIELD(49, 2) -#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE PS_FIELD(51, 3) -#define LOG2_MIN_TRANSFORM_BLOCK_SIZE PS_FIELD(54, 3) -#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE PS_FIELD(57, 2) -#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER PS_FIELD(59, 3) -#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA PS_FIELD(62, 3) -#define SCALING_LIST_ENABLED_FLAG PS_FIELD(65, 1) -#define AMP_ENABLED_FLAG PS_FIELD(66, 1) -#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG PS_FIELD(67, 1) -#define PCM_ENABLED_FLAG PS_FIELD(68, 1) -#define PCM_SAMPLE_BIT_DEPTH_LUMA PS_FIELD(69, 4) -#define PCM_SAMPLE_BIT_DEPTH_CHROMA PS_FIELD(73, 4) -#define PCM_LOOP_FILTER_DISABLED_FLAG PS_FIELD(77, 1) -#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE PS_FIELD(78, 3) -#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE PS_FIELD(81, 3) -#define NUM_SHORT_TERM_REF_PIC_SETS PS_FIELD(84, 7) -#define LONG_TERM_REF_PICS_PRESENT_FLAG PS_FIELD(91, 1) -#define NUM_LONG_TERM_REF_PICS_SPS PS_FIELD(92, 6) -#define SPS_TEMPORAL_MVP_ENABLED_FLAG PS_FIELD(98, 1) -#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG PS_FIELD(99, 1) +#define VIDEO_PARAMETER_SET_ID BW_FIELD(0, 4) +#define SEQ_PARAMETER_SET_ID BW_FIELD(4, 4) +#define CHROMA_FORMAT_IDC BW_FIELD(8, 2) +#define PIC_WIDTH_IN_LUMA_SAMPLES BW_FIELD(10, 13) +#define PIC_HEIGHT_IN_LUMA_SAMPLES BW_FIELD(23, 13) +#define BIT_DEPTH_LUMA BW_FIELD(36, 4) +#define BIT_DEPTH_CHROMA BW_FIELD(40, 4) +#define LOG2_MAX_PIC_ORDER_CNT_LSB BW_FIELD(44, 5) +#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(49, 2) +#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(51, 3) +#define LOG2_MIN_TRANSFORM_BLOCK_SIZE BW_FIELD(54, 3) +#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE BW_FIELD(57, 2) +#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER BW_FIELD(59, 3) +#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA BW_FIELD(62, 3) +#define SCALING_LIST_ENABLED_FLAG BW_FIELD(65, 1) +#define AMP_ENABLED_FLAG BW_FIELD(66, 1) +#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG BW_FIELD(67, 1) +#define PCM_ENABLED_FLAG BW_FIELD(68, 1) +#define PCM_SAMPLE_BIT_DEPTH_LUMA BW_FIELD(69, 4) +#define PCM_SAMPLE_BIT_DEPTH_CHROMA BW_FIELD(73, 4) +#define PCM_LOOP_FILTER_DISABLED_FLAG BW_FIELD(77, 1) +#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(78, 3) +#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(81, 3) +#define NUM_SHORT_TERM_REF_PIC_SETS BW_FIELD(84, 7) +#define LONG_TERM_REF_PICS_PRESENT_FLAG BW_FIELD(91, 1) +#define NUM_LONG_TERM_REF_PICS_SPS BW_FIELD(92, 6) +#define SPS_TEMPORAL_MVP_ENABLED_FLAG BW_FIELD(98, 1) +#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG BW_FIELD(99, 1) /* PPS */ -#define PIC_PARAMETER_SET_ID PS_FIELD(128, 6) -#define PPS_SEQ_PARAMETER_SET_ID PS_FIELD(134, 4) -#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG PS_FIELD(138, 1) -#define OUTPUT_FLAG_PRESENT_FLAG PS_FIELD(139, 1) -#define NUM_EXTRA_SLICE_HEADER_BITS PS_FIELD(140, 13) -#define SIGN_DATA_HIDING_ENABLED_FLAG PS_FIELD(153, 1) -#define CABAC_INIT_PRESENT_FLAG PS_FIELD(154, 1) -#define NUM_REF_IDX_L0_DEFAULT_ACTIVE PS_FIELD(155, 4) -#define NUM_REF_IDX_L1_DEFAULT_ACTIVE PS_FIELD(159, 4) -#define INIT_QP_MINUS26 PS_FIELD(163, 7) -#define CONSTRAINED_INTRA_PRED_FLAG PS_FIELD(170, 1) -#define TRANSFORM_SKIP_ENABLED_FLAG PS_FIELD(171, 1) -#define CU_QP_DELTA_ENABLED_FLAG PS_FIELD(172, 1) -#define LOG2_MIN_CU_QP_DELTA_SIZE PS_FIELD(173, 3) -#define PPS_CB_QP_OFFSET PS_FIELD(176, 5) -#define PPS_CR_QP_OFFSET PS_FIELD(181, 5) -#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG PS_FIELD(186, 1) -#define WEIGHTED_PRED_FLAG PS_FIELD(187, 1) -#define WEIGHTED_BIPRED_FLAG PS_FIELD(188, 1) -#define TRANSQUANT_BYPASS_ENABLED_FLAG PS_FIELD(189, 1) -#define TILES_ENABLED_FLAG PS_FIELD(190, 1) -#define ENTROPY_CODING_SYNC_ENABLED_FLAG PS_FIELD(191, 1) -#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG PS_FIELD(192, 1) -#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG PS_FIELD(193, 1) -#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG PS_FIELD(194, 1) -#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG PS_FIELD(195, 1) -#define PPS_BETA_OFFSET_DIV2 PS_FIELD(196, 4) -#define PPS_TC_OFFSET_DIV2 PS_FIELD(200, 4) -#define LISTS_MODIFICATION_PRESENT_FLAG PS_FIELD(204, 1) -#define LOG2_PARALLEL_MERGE_LEVEL PS_FIELD(205, 3) -#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG PS_FIELD(208, 1) -#define NUM_TILE_COLUMNS PS_FIELD(212, 5) -#define NUM_TILE_ROWS PS_FIELD(217, 5) -#define COLUMN_WIDTH(i) PS_FIELD(256 + ((i) * 8), 8) -#define ROW_HEIGHT(i) PS_FIELD(416 + ((i) * 8), 8) -#define SCALING_LIST_ADDRESS PS_FIELD(592, 32) +#define PIC_PARAMETER_SET_ID BW_FIELD(128, 6) +#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(134, 4) +#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG BW_FIELD(138, 1) +#define OUTPUT_FLAG_PRESENT_FLAG BW_FIELD(139, 1) +#define NUM_EXTRA_SLICE_HEADER_BITS BW_FIELD(140, 13) +#define SIGN_DATA_HIDING_ENABLED_FLAG BW_FIELD(153, 1) +#define CABAC_INIT_PRESENT_FLAG BW_FIELD(154, 1) +#define NUM_REF_IDX_L0_DEFAULT_ACTIVE BW_FIELD(155, 4) +#define NUM_REF_IDX_L1_DEFAULT_ACTIVE BW_FIELD(159, 4) +#define INIT_QP_MINUS26 BW_FIELD(163, 7) +#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(170, 1) +#define TRANSFORM_SKIP_ENABLED_FLAG BW_FIELD(171, 1) +#define CU_QP_DELTA_ENABLED_FLAG BW_FIELD(172, 1) +#define LOG2_MIN_CU_QP_DELTA_SIZE BW_FIELD(173, 3) +#define PPS_CB_QP_OFFSET BW_FIELD(176, 5) +#define PPS_CR_QP_OFFSET BW_FIELD(181, 5) +#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG BW_FIELD(186, 1) +#define WEIGHTED_PRED_FLAG BW_FIELD(187, 1) +#define WEIGHTED_BIPRED_FLAG BW_FIELD(188, 1) +#define TRANSQUANT_BYPASS_ENABLED_FLAG BW_FIELD(189, 1) +#define TILES_ENABLED_FLAG BW_FIELD(190, 1) +#define ENTROPY_CODING_SYNC_ENABLED_FLAG BW_FIELD(191, 1) +#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG BW_FIELD(192, 1) +#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG BW_FIELD(193, 1) +#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG BW_FIELD(194, 1) +#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG BW_FIELD(195, 1) +#define PPS_BETA_OFFSET_DIV2 BW_FIELD(196, 4) +#define PPS_TC_OFFSET_DIV2 BW_FIELD(200, 4) +#define LISTS_MODIFICATION_PRESENT_FLAG BW_FIELD(204, 1) +#define LOG2_PARALLEL_MERGE_LEVEL BW_FIELD(205, 3) +#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG BW_FIELD(208, 1) +#define NUM_TILE_COLUMNS BW_FIELD(212, 5) +#define NUM_TILE_ROWS BW_FIELD(217, 5) +#define COLUMN_WIDTH(i) BW_FIELD(256 + ((i) * 8), 8) +#define ROW_HEIGHT(i) BW_FIELD(416 + ((i) * 8), 8) +#define SCALING_LIST_ADDRESS BW_FIELD(592, 32) /* Data structure describing auxiliary buffer format. */ struct rkvdec_hevc_priv_tbl { @@ -123,20 +116,6 @@ struct rkvdec_hevc_ctx { struct rkvdec_regs regs; }; -static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value) -{ - u8 bit = field.offset % 32, word = field.offset / 32; - u64 mask = GENMASK_ULL(bit + field.len - 1, bit); - u64 val = ((u64)value << bit) & mask; - - buf[word] &= ~mask; - buf[word] |= val; - if (bit + field.len > 32) { - buf[word + 1] &= ~(mask >> 32); - buf[word + 1] |= val >> 32; - } -} - static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run) { @@ -159,7 +138,7 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id]; memset(hw_ps, 0, sizeof(*hw_ps)); -#define WRITE_PPS(value, field) set_ps_field(hw_ps->info, field, value) +#define WRITE_PPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value) /* write sps */ WRITE_PPS(sps->video_parameter_set_id, VIDEO_PARAMETER_SET_ID); WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID); @@ -321,17 +300,17 @@ static void assemble_sw_rps(struct rkvdec_ctx *ctx, int i, j; unsigned int lowdelay; -#define WRITE_RPS(value, field) set_ps_field(hw_ps->info, field, value) +#define WRITE_RPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value) -#define REF_PIC_LONG_TERM_L0(i) PS_FIELD((i) * 5, 1) -#define REF_PIC_IDX_L0(i) PS_FIELD(1 + ((i) * 5), 4) -#define REF_PIC_LONG_TERM_L1(i) PS_FIELD(((i) < 5 ? 75 : 132) + ((i) * 5), 1) -#define REF_PIC_IDX_L1(i) PS_FIELD(((i) < 4 ? 76 : 128) + ((i) * 5), 4) +#define REF_PIC_LONG_TERM_L0(n) BW_FIELD((n) * 5, 1) +#define REF_PIC_IDX_L0(n) BW_FIELD(1 + ((n) * 5), 4) +#define REF_PIC_LONG_TERM_L1(n) BW_FIELD(((n) < 5 ? 75 : 132) + ((n) * 5), 1) +#define REF_PIC_IDX_L1(n) BW_FIELD(((n) < 4 ? 76 : 128) + ((n) * 5), 4) -#define LOWDELAY PS_FIELD(182, 1) -#define LONG_TERM_RPS_BIT_OFFSET PS_FIELD(183, 10) -#define SHORT_TERM_RPS_BIT_OFFSET PS_FIELD(193, 9) -#define NUM_RPS_POC PS_FIELD(202, 4) +#define LOWDELAY BW_FIELD(182, 1) +#define LONG_TERM_RPS_BIT_OFFSET BW_FIELD(183, 10) +#define SHORT_TERM_RPS_BIT_OFFSET BW_FIELD(193, 9) +#define NUM_RPS_POC BW_FIELD(202, 4) for (j = 0; j < run->num_slices; j++) { uint st_bit_offset = 0; From 9b77225be5972a1e13a1b535caddba997e976035 Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Thu, 2 Apr 2026 10:06:38 -0400 Subject: [PATCH 014/394] media: rkvdec: common: Drop bitfields for the bitwriter Currently, the common code files for hevc and h264 use structs with bitfields to represent the HW RPS buffer. Because the bitfields are mostly unaligned and numerous, it brings compiler issues, especially with clang. To prevent that, switch to using the global bitwriter previously introduced instead. Signed-off-by: Detlev Casanova Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../rockchip/rkvdec/rkvdec-h264-common.c | 51 +--------- .../rockchip/rkvdec/rkvdec-h264-common.h | 40 ++------ .../rockchip/rkvdec/rkvdec-hevc-common.c | 93 ++++--------------- .../rockchip/rkvdec/rkvdec-hevc-common.h | 57 +++--------- 4 files changed, 44 insertions(+), 197 deletions(-) diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c index e28f06394470..54639512e456 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c @@ -21,51 +21,6 @@ #define RKVDEC_NUM_REFLIST 3 -static void set_dpb_info(struct rkvdec_rps_entry *entries, - u8 reflist, - u8 refnum, - u8 info, - bool bottom) -{ - struct rkvdec_rps_entry *entry = &entries[(reflist * 4) + refnum / 8]; - u8 idx = refnum % 8; - - switch (idx) { - case 0: - entry->dpb_info0 = info; - entry->bottom_flag0 = bottom; - break; - case 1: - entry->dpb_info1 = info; - entry->bottom_flag1 = bottom; - break; - case 2: - entry->dpb_info2 = info; - entry->bottom_flag2 = bottom; - break; - case 3: - entry->dpb_info3 = info; - entry->bottom_flag3 = bottom; - break; - case 4: - entry->dpb_info4 = info; - entry->bottom_flag4 = bottom; - break; - case 5: - entry->dpb_info5 = info; - entry->bottom_flag5 = bottom; - break; - case 6: - entry->dpb_info6 = info; - entry->bottom_flag6 = bottom; - break; - case 7: - entry->dpb_info7 = info; - entry->bottom_flag7 = bottom; - break; - } -} - void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run) { @@ -111,7 +66,7 @@ void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder, if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) continue; - hw_rps->frame_num[i] = builder->refs[i].frame_num; + rkvdec_set_bw_field(hw_rps->info, RPS_FRAME_NUM(i), builder->refs[i].frame_num); } for (j = 0; j < RKVDEC_NUM_REFLIST; j++) { @@ -138,7 +93,9 @@ void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder, dpb_valid = !!(run->ref_buf[ref->index]); bottom = ref->fields == V4L2_H264_BOTTOM_FIELD_REF; - set_dpb_info(hw_rps->entries, j, i, ref->index | (dpb_valid << 4), bottom); + rkvdec_set_bw_field(hw_rps->info, RPS_ENTRY_DPB_INFO(j, i), + ref->index | (dpb_valid << 4)); + rkvdec_set_bw_field(hw_rps->info, RPS_ENTRY_BOTTOM_FLAG(j, i), bottom); } } } diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h index 5336370507d6..f04b700b863c 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h @@ -16,6 +16,7 @@ #include #include "rkvdec.h" +#include "rkvdec-bitwriter.h" struct rkvdec_h264_scaling_list { u8 scaling_list_4x4[6][16]; @@ -38,39 +39,16 @@ struct rkvdec_h264_run { struct vb2_buffer *ref_buf[V4L2_H264_NUM_DPB_ENTRIES]; }; -struct rkvdec_rps_entry { - u32 dpb_info0: 5; - u32 bottom_flag0: 1; - u32 view_index_off0: 1; - u32 dpb_info1: 5; - u32 bottom_flag1: 1; - u32 view_index_off1: 1; - u32 dpb_info2: 5; - u32 bottom_flag2: 1; - u32 view_index_off2: 1; - u32 dpb_info3: 5; - u32 bottom_flag3: 1; - u32 view_index_off3: 1; - u32 dpb_info4: 5; - u32 bottom_flag4: 1; - u32 view_index_off4: 1; - u32 dpb_info5: 5; - u32 bottom_flag5: 1; - u32 view_index_off5: 1; - u32 dpb_info6: 5; - u32 bottom_flag6: 1; - u32 view_index_off6: 1; - u32 dpb_info7: 5; - u32 bottom_flag7: 1; - u32 view_index_off7: 1; -} __packed; +#define RPS_FRAME_NUM(i) BW_FIELD((i) * 16, 16) +#define RPS_ENTRY_DPB_INFO(l, e) BW_FIELD(288 + (l) * 7 * 32 + (e) * 7, 5) //l: 0-2, e: 0-31 +#define RPS_ENTRY_BOTTOM_FLAG(l, e) BW_FIELD(293 + (l) * 7 * 32 + (e) * 7, 1) //l: 0-2, e: 0-31 +#define RPS_ENTRY_VIEW_INDEX_OFF(l, e) BW_FIELD(294 + (l) * 7 * 32 + (e) * 7, 1) //l: 0-2, e: 0-31 + +#define RKVDEC_H264_RPS_SIZE ALIGN(288 + 3 * 7 * 32, 128) struct rkvdec_rps { - u16 frame_num[16]; - u32 reserved0; - struct rkvdec_rps_entry entries[12]; - u32 reserved1[66]; -} __packed; + u32 info[RKVDEC_H264_RPS_SIZE / 8 / 4]; +}; void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run); void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder, diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c index 3119f3bc9f98..f89602075121 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c @@ -74,72 +74,6 @@ void compute_tiles_non_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size row_height[i] = pic_in_cts_height - sum; } -static void set_ref_poc(struct rkvdec_rps_short_term_ref_set *set, int poc, int value, int flag) -{ - switch (poc) { - case 0: - set->delta_poc0 = value; - set->used_flag0 = flag; - break; - case 1: - set->delta_poc1 = value; - set->used_flag1 = flag; - break; - case 2: - set->delta_poc2 = value; - set->used_flag2 = flag; - break; - case 3: - set->delta_poc3 = value; - set->used_flag3 = flag; - break; - case 4: - set->delta_poc4 = value; - set->used_flag4 = flag; - break; - case 5: - set->delta_poc5 = value; - set->used_flag5 = flag; - break; - case 6: - set->delta_poc6 = value; - set->used_flag6 = flag; - break; - case 7: - set->delta_poc7 = value; - set->used_flag7 = flag; - break; - case 8: - set->delta_poc8 = value; - set->used_flag8 = flag; - break; - case 9: - set->delta_poc9 = value; - set->used_flag9 = flag; - break; - case 10: - set->delta_poc10 = value; - set->used_flag10 = flag; - break; - case 11: - set->delta_poc11 = value; - set->used_flag11 = flag; - break; - case 12: - set->delta_poc12 = value; - set->used_flag12 = flag; - break; - case 13: - set->delta_poc13 = value; - set->used_flag13 = flag; - break; - case 14: - set->delta_poc14 = value; - set->used_flag14 = flag; - break; - } -} - static void assemble_scalingfactor0(struct rkvdec_ctx *ctx, u8 *output, const struct v4l2_ctrl_hevc_scaling_matrix *input) { @@ -218,10 +152,11 @@ static void rkvdec_hevc_assemble_hw_lt_rps(struct rkvdec_hevc_run *run, struct r return; for (int i = 0; i < sps->num_long_term_ref_pics_sps; i++) { - rps->refs[i].lt_ref_pic_poc_lsb = - run->ext_sps_lt_rps[i].lt_ref_pic_poc_lsb_sps; - rps->refs[i].used_by_curr_pic_lt_flag = - !!(run->ext_sps_lt_rps[i].flags & V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT); + rkvdec_set_bw_field(rps->info, RPS_LT_REF_PIC_POC_LSB(i), + run->ext_sps_lt_rps[i].lt_ref_pic_poc_lsb_sps); + rkvdec_set_bw_field(rps->info, RPS_LT_REF_USED_BY_CURR_PIC(i), + !!(run->ext_sps_lt_rps[i].flags & + V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT)); } } @@ -235,18 +170,24 @@ static void rkvdec_hevc_assemble_hw_st_rps(struct rkvdec_hevc_run *run, struct r int j = 0; const struct calculated_rps_st_set *set = &calculated_rps_st_sets[i]; - rps->short_term_ref_sets[i].num_negative = set->num_negative_pics; - rps->short_term_ref_sets[i].num_positive = set->num_positive_pics; + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_NUM_NEGATIVE(i), + set->num_negative_pics); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_NUM_POSITIVE(i), + set->num_positive_pics); for (; j < set->num_negative_pics; j++) { - set_ref_poc(&rps->short_term_ref_sets[i], j, - set->delta_poc_s0[j], set->used_by_curr_pic_s0[j]); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_DELTA_POC(i, j), + set->delta_poc_s0[j]); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_USED(i, j), + set->used_by_curr_pic_s0[j]); } poc = j; for (j = 0; j < set->num_positive_pics; j++) { - set_ref_poc(&rps->short_term_ref_sets[i], poc + j, - set->delta_poc_s1[j], set->used_by_curr_pic_s1[j]); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_DELTA_POC(i, poc + j), + set->delta_poc_s1[j]); + rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_USED(i, poc + j), + set->used_by_curr_pic_s1[j]); } } } diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h index 6f4faca4c091..2a9b7719ab2d 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h @@ -19,53 +19,24 @@ #include #include "rkvdec.h" +#include "rkvdec-bitwriter.h" -struct rkvdec_rps_refs { - u16 lt_ref_pic_poc_lsb; - u16 used_by_curr_pic_lt_flag : 1; - u16 reserved : 15; -} __packed; +#define RPS_LT_REF_PIC_POC_LSB(i) BW_FIELD(0 + (i) * 32, 16) // i: 0-31 +#define RPS_LT_REF_USED_BY_CURR_PIC(i) BW_FIELD(16 + (i) * 32, 1) // i: 0-31 -struct rkvdec_rps_short_term_ref_set { - u32 num_negative : 4; - u32 num_positive : 4; - u32 delta_poc0 : 16; - u32 used_flag0 : 1; - u32 delta_poc1 : 16; - u32 used_flag1 : 1; - u32 delta_poc2 : 16; - u32 used_flag2 : 1; - u32 delta_poc3 : 16; - u32 used_flag3 : 1; - u32 delta_poc4 : 16; - u32 used_flag4 : 1; - u32 delta_poc5 : 16; - u32 used_flag5 : 1; - u32 delta_poc6 : 16; - u32 used_flag6 : 1; - u32 delta_poc7 : 16; - u32 used_flag7 : 1; - u32 delta_poc8 : 16; - u32 used_flag8 : 1; - u32 delta_poc9 : 16; - u32 used_flag9 : 1; - u32 delta_poc10 : 16; - u32 used_flag10 : 1; - u32 delta_poc11 : 16; - u32 used_flag11 : 1; - u32 delta_poc12 : 16; - u32 used_flag12 : 1; - u32 delta_poc13 : 16; - u32 used_flag13 : 1; - u32 delta_poc14 : 16; - u32 used_flag14 : 1; - u32 reserved_bits : 25; - u32 reserved[3]; -} __packed; +#define RPS_ST_REF_SET_NUM_NEGATIVE(i) BW_FIELD(1024 + ((i) * 384), 4) // i: 0-63 +#define RPS_ST_REF_SET_NUM_POSITIVE(i) BW_FIELD(1028 + ((i) * 384), 4) // i: 0-63 + +// i: 0-63, j: 0-14 +#define RPS_ST_REF_SET_DELTA_POC(i, j) BW_FIELD(1032 + ((i) * 384) + ((j) * 17), 16) + +// i: 0-63, j: 0-14 +#define RPS_ST_REF_SET_USED(i, j) BW_FIELD(1048 + ((i) * 384) + ((j) * 17), 1) + +#define RKVDEC_RPS_HEVC_SIZE ALIGN(1032 + 64 * 384, 128) struct rkvdec_rps { - struct rkvdec_rps_refs refs[32]; - struct rkvdec_rps_short_term_ref_set short_term_ref_sets[64]; + u32 info[RKVDEC_RPS_HEVC_SIZE / 8 / 4]; } __packed; struct rkvdec_hevc_run { From 4ae45bf4663ed93c61e9f716e81455122fb66ee2 Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Thu, 2 Apr 2026 10:06:39 -0400 Subject: [PATCH 015/394] media: rkvdec: vdpu383: Drop bitfields for the bitwriter The VDPU383 support for hevc and h264 use structs with bitfields to represent the SPS and PPS. Because the fields are mostly unaligned and numerous, it brings compiler issues, especially with clang. To prevent that, switch to using the global bitwriter previously introduced instead. Signed-off-by: Detlev Casanova Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../rockchip/rkvdec/rkvdec-vdpu383-h264.c | 337 +++++------- .../rockchip/rkvdec/rkvdec-vdpu383-hevc.c | 502 ++++++++---------- 2 files changed, 353 insertions(+), 486 deletions(-) diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c index fb4f849d7366..5ec755733916 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c @@ -15,105 +15,64 @@ #include "rkvdec-cabac.h" #include "rkvdec-vdpu383-regs.h" #include "rkvdec-h264-common.h" +#include "rkvdec-bitwriter.h" -struct rkvdec_sps { - u16 seq_parameter_set_id: 4; - u16 profile_idc: 8; - u16 constraint_set3_flag: 1; - u16 chroma_format_idc: 2; - u16 bit_depth_luma: 3; - u16 bit_depth_chroma: 3; - u16 qpprime_y_zero_transform_bypass_flag: 1; - u16 log2_max_frame_num_minus4: 4; - u16 max_num_ref_frames: 5; - u16 pic_order_cnt_type: 2; - u16 log2_max_pic_order_cnt_lsb_minus4: 4; - u16 delta_pic_order_always_zero_flag: 1; +#define SEQ_PARAMETER_SET_ID BW_FIELD(0, 4) +#define PROFILE_IDC BW_FIELD(4, 8) +#define CONSTRAINT_SET3_FLAG BW_FIELD(12, 1) +#define CHROMA_FORMAT_IDC BW_FIELD(13, 2) +#define BIT_DEPTH_LUMA BW_FIELD(15, 3) +#define BIT_DEPTH_CHROMA BW_FIELD(18, 3) +#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG BW_FIELD(21, 1) +#define LOG2_MAX_FRAME_NUM_MINUS4 BW_FIELD(22, 4) +#define MAX_NUM_REF_FRAMES BW_FIELD(26, 5) +#define PIC_ORDER_CNT_TYPE BW_FIELD(31, 2) +#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 BW_FIELD(33, 4) +#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG BW_FIELD(37, 1) +#define PIC_WIDTH_IN_MBS BW_FIELD(38, 16) +#define PIC_HEIGHT_IN_MBS BW_FIELD(54, 16) +#define FRAME_MBS_ONLY_FLAG BW_FIELD(70, 1) +#define MB_ADAPTIVE_FRAME_FIELD_FLAG BW_FIELD(71, 1) +#define DIRECT_8X8_INFERENCE_FLAG BW_FIELD(72, 1) +#define MVC_EXTENSION_ENABLE BW_FIELD(73, 1) +#define NUM_VIEWS BW_FIELD(74, 2) +#define VIEW_ID(i) BW_FIELD(76 + ((i) * 10), 10) // i: 0-1 - u16 pic_width_in_mbs: 16; - u16 pic_height_in_mbs: 16; +#define PIC_PARAMETER_SET_ID BW_FIELD(96, 8) +#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(104, 5) +#define ENTROPY_CODING_MODE_FLAG BW_FIELD(109, 1) +#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG BW_FIELD(110, 1) +#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) BW_FIELD(111 + ((i) * 5), 5) // i: 0-1 +#define WEIGHTED_PRED_FLAG BW_FIELD(121, 1) +#define WEIGHTED_BIPRED_IDC BW_FIELD(122, 2) +#define PIC_INIT_QP_MINUS26 BW_FIELD(124, 7) +#define PIC_INIT_QS_MINUS26 BW_FIELD(131, 6) +#define CHROMA_QP_INDEX_OFFSET BW_FIELD(137, 5) +#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG BW_FIELD(142, 1) +#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(143, 1) +#define REDUNDANT_PIC_CNT_PRESENT BW_FIELD(144, 1) +#define TRANSFORM_8X8_MODE_FLAG BW_FIELD(145, 1) +#define SECOND_CHROMA_QP_INDEX_OFFSET BW_FIELD(146, 5) +#define SCALING_LIST_ENABLE_FLAG BW_FIELD(151, 1) +#define IS_LONG_TERM(i) BW_FIELD(152 + (i), 1) // i: 0-15 - u16 frame_mbs_only_flag: 1; - u16 mb_adaptive_frame_field_flag: 1; - u16 direct_8x8_inference_flag: 1; - u16 mvc_extension_enable: 1; - u16 num_views: 2; - u16 view_id0: 10; - u16 view_id1: 10; -} __packed; +#define PIC_FIELD_FLAG BW_FIELD(184, 1) +#define PIC_ASSOCIATED_FLAG BW_FIELD(185, 1) +#define CUR_TOP_FIELD BW_FIELD(186, 32) +#define CUR_BOT_FIELD BW_FIELD(218, 32) -struct rkvdec_pps { - u32 pic_parameter_set_id: 8; - u32 pps_seq_parameter_set_id: 5; - u32 entropy_coding_mode_flag: 1; - u32 bottom_field_pic_order_in_frame_present_flag: 1; - u32 num_ref_idx_l0_default_active_minus1: 5; - u32 num_ref_idx_l1_default_active_minus1: 5; - u32 weighted_pred_flag: 1; - u32 weighted_bipred_idc: 2; - u32 pic_init_qp_minus26: 7; - u32 pic_init_qs_minus26: 6; - u32 chroma_qp_index_offset: 5; - u32 deblocking_filter_control_present_flag: 1; - u32 constrained_intra_pred_flag: 1; - u32 redundant_pic_cnt_present: 1; - u32 transform_8x8_mode_flag: 1; - u32 second_chroma_qp_index_offset: 5; - u32 scaling_list_enable_flag: 1; - u32 is_longterm: 16; - u32 voidx: 16; +#define TOP_FIELD_ORDER_CNT(i) BW_FIELD(250 + (i) * 64, 32) // i: 0-15 +#define BOT_FIELD_ORDER_CNT(i) BW_FIELD(282 + (i) * 64, 32) // i: 0-15 - // dpb - u32 pic_field_flag: 1; - u32 pic_associated_flag: 1; - u32 cur_top_field: 32; - u32 cur_bot_field: 32; +#define REF_FIELD_FLAGS(i) BW_FIELD(1274 + (i), 1) // i: 0-15 +#define REF_TOPFIELD_USED(i) BW_FIELD(1290 + (i), 1) // i: 0-15 +#define REF_BOTFIELD_USED(i) BW_FIELD(1306 + (i), 1) // i: 0-15 +#define REF_COLMV_USE_FLAG(i) BW_FIELD(1322 + (i), 1) // i: 0-15 - u32 top_field_order_cnt0: 32; - u32 bot_field_order_cnt0: 32; - u32 top_field_order_cnt1: 32; - u32 bot_field_order_cnt1: 32; - u32 top_field_order_cnt2: 32; - u32 bot_field_order_cnt2: 32; - u32 top_field_order_cnt3: 32; - u32 bot_field_order_cnt3: 32; - u32 top_field_order_cnt4: 32; - u32 bot_field_order_cnt4: 32; - u32 top_field_order_cnt5: 32; - u32 bot_field_order_cnt5: 32; - u32 top_field_order_cnt6: 32; - u32 bot_field_order_cnt6: 32; - u32 top_field_order_cnt7: 32; - u32 bot_field_order_cnt7: 32; - u32 top_field_order_cnt8: 32; - u32 bot_field_order_cnt8: 32; - u32 top_field_order_cnt9: 32; - u32 bot_field_order_cnt9: 32; - u32 top_field_order_cnt10: 32; - u32 bot_field_order_cnt10: 32; - u32 top_field_order_cnt11: 32; - u32 bot_field_order_cnt11: 32; - u32 top_field_order_cnt12: 32; - u32 bot_field_order_cnt12: 32; - u32 top_field_order_cnt13: 32; - u32 bot_field_order_cnt13: 32; - u32 top_field_order_cnt14: 32; - u32 bot_field_order_cnt14: 32; - u32 top_field_order_cnt15: 32; - u32 bot_field_order_cnt15: 32; - - u32 ref_field_flags: 16; - u32 ref_topfield_used: 16; - u32 ref_botfield_used: 16; - u32 ref_colmv_use_flag: 16; - - u32 reserved0: 30; - u32 reserved[3]; -} __packed; +#define SPS_SIZE ALIGN(1322 + 16, 128) struct rkvdec_sps_pps { - struct rkvdec_sps sps; - struct rkvdec_pps pps; + u32 info[SPS_SIZE / 8 / 4]; } __packed; /* Data structure describing auxiliary buffer format. */ @@ -130,67 +89,6 @@ struct rkvdec_h264_ctx { struct vdpu383_regs_h26x regs; }; -static noinline_for_stack void set_field_order_cnt(struct rkvdec_pps *pps, const struct v4l2_h264_dpb_entry *dpb) -{ - pps->top_field_order_cnt0 = dpb[0].top_field_order_cnt; - pps->bot_field_order_cnt0 = dpb[0].bottom_field_order_cnt; - pps->top_field_order_cnt1 = dpb[1].top_field_order_cnt; - pps->bot_field_order_cnt1 = dpb[1].bottom_field_order_cnt; - pps->top_field_order_cnt2 = dpb[2].top_field_order_cnt; - pps->bot_field_order_cnt2 = dpb[2].bottom_field_order_cnt; - pps->top_field_order_cnt3 = dpb[3].top_field_order_cnt; - pps->bot_field_order_cnt3 = dpb[3].bottom_field_order_cnt; - pps->top_field_order_cnt4 = dpb[4].top_field_order_cnt; - pps->bot_field_order_cnt4 = dpb[4].bottom_field_order_cnt; - pps->top_field_order_cnt5 = dpb[5].top_field_order_cnt; - pps->bot_field_order_cnt5 = dpb[5].bottom_field_order_cnt; - pps->top_field_order_cnt6 = dpb[6].top_field_order_cnt; - pps->bot_field_order_cnt6 = dpb[6].bottom_field_order_cnt; - pps->top_field_order_cnt7 = dpb[7].top_field_order_cnt; - pps->bot_field_order_cnt7 = dpb[7].bottom_field_order_cnt; - pps->top_field_order_cnt8 = dpb[8].top_field_order_cnt; - pps->bot_field_order_cnt8 = dpb[8].bottom_field_order_cnt; - pps->top_field_order_cnt9 = dpb[9].top_field_order_cnt; - pps->bot_field_order_cnt9 = dpb[9].bottom_field_order_cnt; - pps->top_field_order_cnt10 = dpb[10].top_field_order_cnt; - pps->bot_field_order_cnt10 = dpb[10].bottom_field_order_cnt; - pps->top_field_order_cnt11 = dpb[11].top_field_order_cnt; - pps->bot_field_order_cnt11 = dpb[11].bottom_field_order_cnt; - pps->top_field_order_cnt12 = dpb[12].top_field_order_cnt; - pps->bot_field_order_cnt12 = dpb[12].bottom_field_order_cnt; - pps->top_field_order_cnt13 = dpb[13].top_field_order_cnt; - pps->bot_field_order_cnt13 = dpb[13].bottom_field_order_cnt; - pps->top_field_order_cnt14 = dpb[14].top_field_order_cnt; - pps->bot_field_order_cnt14 = dpb[14].bottom_field_order_cnt; - pps->top_field_order_cnt15 = dpb[15].top_field_order_cnt; - pps->bot_field_order_cnt15 = dpb[15].bottom_field_order_cnt; -} - -static noinline_for_stack void set_dec_params(struct rkvdec_pps *pps, const struct v4l2_ctrl_h264_decode_params *dec_params) -{ - const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; - - for (int i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) - pps->is_longterm |= (1 << i); - pps->ref_field_flags |= - (!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD)) << i; - pps->ref_colmv_use_flag |= - (!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) << i; - pps->ref_topfield_used |= - (!!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF)) << i; - pps->ref_botfield_used |= - (!!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF)) << i; - } - pps->pic_field_flag = - !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC); - pps->pic_associated_flag = - !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD); - - pps->cur_top_field = dec_params->top_field_order_cnt; - pps->cur_bot_field = dec_params->bottom_field_order_cnt; -} - static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run) { @@ -202,6 +100,7 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu; struct rkvdec_sps_pps *hw_ps; u32 pic_width, pic_height; + int i; /* * HW read the SPS/PPS information from PPS packet index by PPS id. @@ -213,23 +112,25 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, memset(hw_ps, 0, sizeof(*hw_ps)); /* write sps */ - hw_ps->sps.seq_parameter_set_id = sps->seq_parameter_set_id; - hw_ps->sps.profile_idc = sps->profile_idc; - hw_ps->sps.constraint_set3_flag = !!(sps->constraint_set_flags & (1 << 3)); - hw_ps->sps.chroma_format_idc = sps->chroma_format_idc; - hw_ps->sps.bit_depth_luma = sps->bit_depth_luma_minus8; - hw_ps->sps.bit_depth_chroma = sps->bit_depth_chroma_minus8; - hw_ps->sps.qpprime_y_zero_transform_bypass_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS); - hw_ps->sps.log2_max_frame_num_minus4 = sps->log2_max_frame_num_minus4; - hw_ps->sps.max_num_ref_frames = sps->max_num_ref_frames; - hw_ps->sps.pic_order_cnt_type = sps->pic_order_cnt_type; - hw_ps->sps.log2_max_pic_order_cnt_lsb_minus4 = - sps->log2_max_pic_order_cnt_lsb_minus4; - hw_ps->sps.delta_pic_order_always_zero_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO); - hw_ps->sps.mvc_extension_enable = 0; - hw_ps->sps.num_views = 0; + rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, PROFILE_IDC, sps->profile_idc); + rkvdec_set_bw_field(hw_ps->info, CONSTRAINT_SET3_FLAG, + !!(sps->constraint_set_flags & (1 << 3))); + rkvdec_set_bw_field(hw_ps->info, CHROMA_FORMAT_IDC, sps->chroma_format_idc); + rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_LUMA, sps->bit_depth_luma_minus8); + rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_CHROMA, sps->bit_depth_chroma_minus8); + rkvdec_set_bw_field(hw_ps->info, QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS)); + rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_FRAME_NUM_MINUS4, + sps->log2_max_frame_num_minus4); + rkvdec_set_bw_field(hw_ps->info, MAX_NUM_REF_FRAMES, sps->max_num_ref_frames); + rkvdec_set_bw_field(hw_ps->info, PIC_ORDER_CNT_TYPE, sps->pic_order_cnt_type); + rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4, + sps->log2_max_pic_order_cnt_lsb_minus4); + rkvdec_set_bw_field(hw_ps->info, DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO)); + rkvdec_set_bw_field(hw_ps->info, MVC_EXTENSION_ENABLE, 0); + rkvdec_set_bw_field(hw_ps->info, NUM_VIEWS, 0); /* * Use the SPS values since they are already in macroblocks @@ -245,48 +146,72 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, if (!!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)) pic_height /= 2; - hw_ps->sps.pic_width_in_mbs = pic_width; - hw_ps->sps.pic_height_in_mbs = pic_height; + rkvdec_set_bw_field(hw_ps->info, PIC_WIDTH_IN_MBS, pic_width); + rkvdec_set_bw_field(hw_ps->info, PIC_HEIGHT_IN_MBS, pic_height); - hw_ps->sps.frame_mbs_only_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY); - hw_ps->sps.mb_adaptive_frame_field_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD); - hw_ps->sps.direct_8x8_inference_flag = - !!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE); + rkvdec_set_bw_field(hw_ps->info, FRAME_MBS_ONLY_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)); + rkvdec_set_bw_field(hw_ps->info, MB_ADAPTIVE_FRAME_FIELD_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD)); + rkvdec_set_bw_field(hw_ps->info, DIRECT_8X8_INFERENCE_FLAG, + !!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE)); /* write pps */ - hw_ps->pps.pic_parameter_set_id = pps->pic_parameter_set_id; - hw_ps->pps.pps_seq_parameter_set_id = pps->seq_parameter_set_id; - hw_ps->pps.entropy_coding_mode_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE); - hw_ps->pps.bottom_field_pic_order_in_frame_present_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT); - hw_ps->pps.num_ref_idx_l0_default_active_minus1 = - pps->num_ref_idx_l0_default_active_minus1; - hw_ps->pps.num_ref_idx_l1_default_active_minus1 = - pps->num_ref_idx_l1_default_active_minus1; - hw_ps->pps.weighted_pred_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED); - hw_ps->pps.weighted_bipred_idc = pps->weighted_bipred_idc; - hw_ps->pps.pic_init_qp_minus26 = pps->pic_init_qp_minus26; - hw_ps->pps.pic_init_qs_minus26 = pps->pic_init_qs_minus26; - hw_ps->pps.chroma_qp_index_offset = pps->chroma_qp_index_offset; - hw_ps->pps.deblocking_filter_control_present_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT); - hw_ps->pps.constrained_intra_pred_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED); - hw_ps->pps.redundant_pic_cnt_present = - !!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT); - hw_ps->pps.transform_8x8_mode_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE); - hw_ps->pps.second_chroma_qp_index_offset = pps->second_chroma_qp_index_offset; - hw_ps->pps.scaling_list_enable_flag = - !!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT); + rkvdec_set_bw_field(hw_ps->info, PIC_PARAMETER_SET_ID, pps->pic_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, PPS_SEQ_PARAMETER_SET_ID, pps->seq_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, ENTROPY_CODING_MODE_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE)); + rkvdec_set_bw_field(hw_ps->info, BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG, + !!(pps->flags & + V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(0), + pps->num_ref_idx_l0_default_active_minus1); + rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(1), + pps->num_ref_idx_l1_default_active_minus1); + rkvdec_set_bw_field(hw_ps->info, WEIGHTED_PRED_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED)); + rkvdec_set_bw_field(hw_ps->info, WEIGHTED_BIPRED_IDC, pps->weighted_bipred_idc); + rkvdec_set_bw_field(hw_ps->info, PIC_INIT_QP_MINUS26, pps->pic_init_qp_minus26); + rkvdec_set_bw_field(hw_ps->info, PIC_INIT_QS_MINUS26, pps->pic_init_qs_minus26); + rkvdec_set_bw_field(hw_ps->info, CHROMA_QP_INDEX_OFFSET, pps->chroma_qp_index_offset); + rkvdec_set_bw_field(hw_ps->info, DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, CONSTRAINED_INTRA_PRED_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED)); + rkvdec_set_bw_field(hw_ps->info, REDUNDANT_PIC_CNT_PRESENT, + !!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, TRANSFORM_8X8_MODE_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE)); + rkvdec_set_bw_field(hw_ps->info, SECOND_CHROMA_QP_INDEX_OFFSET, + pps->second_chroma_qp_index_offset); + rkvdec_set_bw_field(hw_ps->info, SCALING_LIST_ENABLE_FLAG, + !!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)); - set_field_order_cnt(&hw_ps->pps, dpb); - set_dec_params(&hw_ps->pps, dec_params); + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + rkvdec_set_bw_field(hw_ps->info, TOP_FIELD_ORDER_CNT(i), + dpb[i].top_field_order_cnt); + rkvdec_set_bw_field(hw_ps->info, BOT_FIELD_ORDER_CNT(i), + dpb[i].bottom_field_order_cnt); + rkvdec_set_bw_field(hw_ps->info, IS_LONG_TERM(i), + !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)); + rkvdec_set_bw_field(hw_ps->info, REF_FIELD_FLAGS(i), + !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD)); + rkvdec_set_bw_field(hw_ps->info, REF_COLMV_USE_FLAG(i), + !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)); + rkvdec_set_bw_field(hw_ps->info, REF_TOPFIELD_USED(i), + !!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF)); + rkvdec_set_bw_field(hw_ps->info, REF_BOTFIELD_USED(i), + !!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF)); + } + + rkvdec_set_bw_field(hw_ps->info, PIC_FIELD_FLAG, + !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)); + rkvdec_set_bw_field(hw_ps->info, PIC_ASSOCIATED_FLAG, + !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)); + + rkvdec_set_bw_field(hw_ps->info, CUR_TOP_FIELD, dec_params->top_field_order_cnt); + rkvdec_set_bw_field(hw_ps->info, CUR_BOT_FIELD, dec_params->bottom_field_order_cnt); } static void rkvdec_write_regs(struct rkvdec_ctx *ctx) diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c index 96d938ee70b0..3575338a531a 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c @@ -13,149 +13,106 @@ #include "rkvdec-rcb.h" #include "rkvdec-hevc-common.h" #include "rkvdec-vdpu383-regs.h" +#include "rkvdec-bitwriter.h" + +#define VIDEO_PARAMETER_SET_ID BW_FIELD(0, 4) +#define SEQ_PARAMETER_SET_ID BW_FIELD(4, 4) +#define CHROMA_FORMAT_IDC BW_FIELD(8, 2) +#define PIC_WIDTH_IN_LUMA_SAMPLES BW_FIELD(10, 16) +#define PIC_HEIGHT_IN_LUMA_SAMPLES BW_FIELD(26, 16) +#define BIT_DEPTH_LUMA BW_FIELD(42, 3) +#define BIT_DEPTH_CHROMA BW_FIELD(45, 3) +#define LOG2_MAX_PIC_ORDER_CNT_LSB BW_FIELD(48, 5) +#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(53, 2) +#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(55, 3) +#define LOG2_MIN_TRANSFORM_BLOCK_SIZE BW_FIELD(58, 3) +#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE BW_FIELD(61, 2) +#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER BW_FIELD(63, 3) +#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA BW_FIELD(66, 3) +#define SCALING_LIST_ENABLED_FLAG BW_FIELD(69, 1) +#define AMP_ENABLED_FLAG BW_FIELD(70, 1) +#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG BW_FIELD(71, 1) +#define PCM_ENABLED_FLAG BW_FIELD(72, 1) +#define PCM_SAMPLE_BIT_DEPTH_LUMA BW_FIELD(73, 4) +#define PCM_SAMPLE_BIT_DEPTH_CHROMA BW_FIELD(77, 4) +#define PCM_LOOP_FILTER_DISABLED_FLAG BW_FIELD(81, 1) +#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(82, 3) +#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(85, 3) +#define NUM_SHORT_TERM_REF_PIC_SETS BW_FIELD(88, 7) +#define LONG_TERM_REF_PICS_PRESENT_FLAG BW_FIELD(95, 1) +#define NUM_LONG_TERM_REF_PICS_SPS BW_FIELD(96, 6) +#define SPS_TEMPORAL_MVP_ENABLED_FLAG BW_FIELD(102, 1) +#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG BW_FIELD(103, 1) +#define SPS_MAX_DEC_PIC_BUFFERING_MINUS1 BW_FIELD(111, 4) +#define SEPARATE_COLOUR_PLANE_FLAG BW_FIELD(115, 1) +#define HIGH_PRECISION_OFFSETS_ENABLED_FLAG BW_FIELD(116, 1) +#define PERSISTENT_RICE_ADAPTATION_ENABLED_FLAG BW_FIELD(117, 1) + +/* PPS */ +#define PIC_PARAMETER_SET_ID BW_FIELD(118, 6) +#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(124, 4) +#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG BW_FIELD(128, 1) +#define OUTPUT_FLAG_PRESENT_FLAG BW_FIELD(129, 1) +#define NUM_EXTRA_SLICE_HEADER_BITS BW_FIELD(130, 13) +#define SIGN_DATA_HIDING_ENABLED_FLAG BW_FIELD(143, 1) +#define CABAC_INIT_PRESENT_FLAG BW_FIELD(144, 1) +#define NUM_REF_IDX_L0_DEFAULT_ACTIVE BW_FIELD(145, 4) +#define NUM_REF_IDX_L1_DEFAULT_ACTIVE BW_FIELD(149, 4) +#define INIT_QP_MINUS26 BW_FIELD(153, 7) +#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(160, 1) +#define TRANSFORM_SKIP_ENABLED_FLAG BW_FIELD(161, 1) +#define CU_QP_DELTA_ENABLED_FLAG BW_FIELD(162, 1) +#define LOG2_MIN_CU_QP_DELTA_SIZE BW_FIELD(163, 3) +#define PPS_CB_QP_OFFSET BW_FIELD(166, 5) +#define PPS_CR_QP_OFFSET BW_FIELD(171, 5) +#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG BW_FIELD(176, 1) +#define WEIGHTED_PRED_FLAG BW_FIELD(177, 1) +#define WEIGHTED_BIPRED_FLAG BW_FIELD(178, 1) +#define TRANSQUANT_BYPASS_ENABLED_FLAG BW_FIELD(179, 1) +#define TILES_ENABLED_FLAG BW_FIELD(180, 1) +#define ENTROPY_CODING_SYNC_ENABLED_FLAG BW_FIELD(181, 1) +#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG BW_FIELD(182, 1) +#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG BW_FIELD(183, 1) +#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG BW_FIELD(184, 1) +#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG BW_FIELD(185, 1) +#define PPS_BETA_OFFSET_DIV2 BW_FIELD(186, 4) +#define PPS_TC_OFFSET_DIV2 BW_FIELD(190, 4) +#define LISTS_MODIFICATION_PRESENT_FLAG BW_FIELD(194, 1) +#define LOG2_PARALLEL_MERGE_LEVEL BW_FIELD(195, 3) +#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG BW_FIELD(198, 1) + +/* pps extensions */ +#define LOG2_MAX_TRANSFORM_SKIP_BLOCK_SIZE BW_FIELD(202, 2) +#define CROSS_COMPONENT_PREDICTION_ENABLED_FLAG BW_FIELD(204, 1) +#define CHROMA_QP_OFFSET_LIST_ENABLED_FLAG BW_FIELD(205, 1) +#define LOG2_MIN_CU_CHROMA_QP_DELTA_SIZE BW_FIELD(206, 3) +#define CB_QP_OFFSET_LIST(i) BW_FIELD(209 + (i) * 5, 5) // i: 0-5 +#define CB_CR_OFFSET_LIST(i) BW_FIELD(239 + (i) * 5, 5) // i: 0-5 +#define CHROMA_QP_OFFSET_LIST_LEN_MINUS1 BW_FIELD(269, 3) + +/* mvc0 && mvc1 */ +#define MVC_FF BW_FIELD(272, 16) +#define MVC_00 BW_FIELD(288, 9) + +/* poc info */ +#define RESERVED2 BW_FIELD(297, 3) +#define CURRENT_POC BW_FIELD(300, 32) +#define REF_PIC_POC(i) BW_FIELD(332 + (i) * 32, 32) // i: 0-14 +#define RESERVED3 BW_FIELD(812, 32) +#define REF_IS_VALID(i) BW_FIELD(844 + (i), 1) // i: 0-14 +#define RESERVED4 BW_FIELD(859, 1) + +/* tile info*/ +#define NUM_TILE_COLUMNS BW_FIELD(860, 5) +#define NUM_TILE_ROWS BW_FIELD(865, 5) +#define COLUMN_WIDTH(i) BW_FIELD(870 + (i) * 12, 12) // i: 0-19 +#define ROW_HEIGHT(i) BW_FIELD(1110 + (i) * 12, 12) // i: 0-21 + +#define HEVC_SPS_SIZE ALIGN(1110 + 22 * 12, 256) struct rkvdec_hevc_sps_pps { - // SPS - u16 video_parameters_set_id : 4; - u16 seq_parameters_set_id_sps : 4; - u16 chroma_format_idc : 2; - u16 width : 16; - u16 height : 16; - u16 bit_depth_luma : 3; - u16 bit_depth_chroma : 3; - u16 max_pic_order_count_lsb : 5; - u16 diff_max_min_luma_coding_block_size : 2; - u16 min_luma_coding_block_size : 3; - u16 min_transform_block_size : 3; - u16 diff_max_min_transform_block_size : 2; - u16 max_transform_hierarchy_depth_inter : 3; - u16 max_transform_hierarchy_depth_intra : 3; - u16 scaling_list_enabled_flag : 1; - u16 amp_enabled_flag : 1; - u16 sample_adaptive_offset_enabled_flag : 1; - u16 pcm_enabled_flag : 1; - u16 pcm_sample_bit_depth_luma : 4; - u16 pcm_sample_bit_depth_chroma : 4; - u16 pcm_loop_filter_disabled_flag : 1; - u16 diff_max_min_pcm_luma_coding_block_size : 3; - u16 min_pcm_luma_coding_block_size : 3; - u16 num_short_term_ref_pic_sets : 7; - u16 long_term_ref_pics_present_flag : 1; - u16 num_long_term_ref_pics_sps : 6; - u16 sps_temporal_mvp_enabled_flag : 1; - u16 strong_intra_smoothing_enabled_flag : 1; - u16 reserved0 : 7; - u16 sps_max_dec_pic_buffering_minus1 : 4; - u16 separate_colour_plane_flag : 1; - u16 high_precision_offsets_enabled_flag : 1; - u16 persistent_rice_adaptation_enabled_flag : 1; - - // PPS - u16 picture_parameters_set_id : 6; - u16 seq_parameters_set_id_pps : 4; - u16 dependent_slice_segments_enabled_flag : 1; - u16 output_flag_present_flag : 1; - u16 num_extra_slice_header_bits : 13; - u16 sign_data_hiding_enabled_flag : 1; - u16 cabac_init_present_flag : 1; - u16 num_ref_idx_l0_default_active : 4; - u16 num_ref_idx_l1_default_active : 4; - u16 init_qp_minus26 : 7; - u16 constrained_intra_pred_flag : 1; - u16 transform_skip_enabled_flag : 1; - u16 cu_qp_delta_enabled_flag : 1; - u16 log2_min_cb_size : 3; - u16 pps_cb_qp_offset : 5; - u16 pps_cr_qp_offset : 5; - u16 pps_slice_chroma_qp_offsets_present_flag : 1; - u16 weighted_pred_flag : 1; - u16 weighted_bipred_flag : 1; - u16 transquant_bypass_enabled_flag : 1; - u16 tiles_enabled_flag : 1; - u16 entropy_coding_sync_enabled_flag : 1; - u16 pps_loop_filter_across_slices_enabled_flag : 1; - u16 loop_filter_across_tiles_enabled_flag : 1; - u16 deblocking_filter_override_enabled_flag : 1; - u16 pps_deblocking_filter_disabled_flag : 1; - u16 pps_beta_offset_div2 : 4; - u16 pps_tc_offset_div2 : 4; - u16 lists_modification_present_flag : 1; - u16 log2_parallel_merge_level : 3; - u16 slice_segment_header_extension_present_flag : 1; - u16 reserved1 : 3; - - // pps extensions - u16 log2_max_transform_skip_block_size : 2; - u16 cross_component_prediction_enabled_flag : 1; - u16 chroma_qp_offset_list_enabled_flag : 1; - u16 log2_min_cu_chroma_qp_delta_size : 3; - u16 cb_qp_offset_list0 : 5; - u16 cb_qp_offset_list1 : 5; - u16 cb_qp_offset_list2 : 5; - u16 cb_qp_offset_list3 : 5; - u16 cb_qp_offset_list4 : 5; - u16 cb_qp_offset_list5 : 5; - u16 cb_cr_offset_list0 : 5; - u16 cb_cr_offset_list1 : 5; - u16 cb_cr_offset_list2 : 5; - u16 cb_cr_offset_list3 : 5; - u16 cb_cr_offset_list4 : 5; - u16 cb_cr_offset_list5 : 5; - u16 chroma_qp_offset_list_len_minus1 : 3; - - /* mvc0 && mvc1 */ - u16 mvc_ff : 16; - u16 mvc_00 : 9; - - /* poc info */ - u16 reserved2 : 3; - u32 current_poc : 32; - u32 ref_pic_poc0 : 32; - u32 ref_pic_poc1 : 32; - u32 ref_pic_poc2 : 32; - u32 ref_pic_poc3 : 32; - u32 ref_pic_poc4 : 32; - u32 ref_pic_poc5 : 32; - u32 ref_pic_poc6 : 32; - u32 ref_pic_poc7 : 32; - u32 ref_pic_poc8 : 32; - u32 ref_pic_poc9 : 32; - u32 ref_pic_poc10 : 32; - u32 ref_pic_poc11 : 32; - u32 ref_pic_poc12 : 32; - u32 ref_pic_poc13 : 32; - u32 ref_pic_poc14 : 32; - u32 reserved3 : 32; - u32 ref_is_valid : 15; - u32 reserved4 : 1; - - /* tile info*/ - u16 num_tile_columns : 5; - u16 num_tile_rows : 5; - u32 column_width0 : 24; - u32 column_width1 : 24; - u32 column_width2 : 24; - u32 column_width3 : 24; - u32 column_width4 : 24; - u32 column_width5 : 24; - u32 column_width6 : 24; - u32 column_width7 : 24; - u32 column_width8 : 24; - u32 column_width9 : 24; - u32 row_height0 : 24; - u32 row_height1 : 24; - u32 row_height2 : 24; - u32 row_height3 : 24; - u32 row_height4 : 24; - u32 row_height5 : 24; - u32 row_height6 : 24; - u32 row_height7 : 24; - u32 row_height8 : 24; - u32 row_height9 : 24; - u32 row_height10 : 24; - u32 reserved5 : 2; - u32 padding; -} __packed; + u32 info[HEVC_SPS_SIZE / 8 / 4]; +}; struct rkvdec_hevc_priv_tbl { struct rkvdec_hevc_sps_pps param_set; @@ -171,51 +128,6 @@ struct rkvdec_hevc_ctx { struct vdpu383_regs_h26x regs; }; -static void set_column_row(struct rkvdec_hevc_sps_pps *hw_ps, u16 *column, u16 *row) -{ - hw_ps->column_width0 = column[0] | (column[1] << 12); - hw_ps->row_height0 = row[0] | (row[1] << 12); - hw_ps->column_width1 = column[2] | (column[3] << 12); - hw_ps->row_height1 = row[2] | (row[3] << 12); - hw_ps->column_width2 = column[4] | (column[5] << 12); - hw_ps->row_height2 = row[4] | (row[5] << 12); - hw_ps->column_width3 = column[6] | (column[7] << 12); - hw_ps->row_height3 = row[6] | (row[7] << 12); - hw_ps->column_width4 = column[8] | (column[9] << 12); - hw_ps->row_height4 = row[8] | (row[9] << 12); - hw_ps->column_width5 = column[10] | (column[11] << 12); - hw_ps->row_height5 = row[10] | (row[11] << 12); - hw_ps->column_width6 = column[12] | (column[13] << 12); - hw_ps->row_height6 = row[12] | (row[13] << 12); - hw_ps->column_width7 = column[14] | (column[15] << 12); - hw_ps->row_height7 = row[14] | (row[15] << 12); - hw_ps->column_width8 = column[16] | (column[17] << 12); - hw_ps->row_height8 = row[16] | (row[17] << 12); - hw_ps->column_width9 = column[18] | (column[19] << 12); - hw_ps->row_height9 = row[18] | (row[19] << 12); - - hw_ps->row_height10 = row[20] | (row[21] << 12); -} - -static void set_pps_ref_pic_poc(struct rkvdec_hevc_sps_pps *hw_ps, const struct v4l2_hevc_dpb_entry *dpb) -{ - hw_ps->ref_pic_poc0 = dpb[0].pic_order_cnt_val; - hw_ps->ref_pic_poc1 = dpb[1].pic_order_cnt_val; - hw_ps->ref_pic_poc2 = dpb[2].pic_order_cnt_val; - hw_ps->ref_pic_poc3 = dpb[3].pic_order_cnt_val; - hw_ps->ref_pic_poc4 = dpb[4].pic_order_cnt_val; - hw_ps->ref_pic_poc5 = dpb[5].pic_order_cnt_val; - hw_ps->ref_pic_poc6 = dpb[6].pic_order_cnt_val; - hw_ps->ref_pic_poc7 = dpb[7].pic_order_cnt_val; - hw_ps->ref_pic_poc8 = dpb[8].pic_order_cnt_val; - hw_ps->ref_pic_poc9 = dpb[9].pic_order_cnt_val; - hw_ps->ref_pic_poc10 = dpb[10].pic_order_cnt_val; - hw_ps->ref_pic_poc11 = dpb[11].pic_order_cnt_val; - hw_ps->ref_pic_poc12 = dpb[12].pic_order_cnt_val; - hw_ps->ref_pic_poc13 = dpb[13].pic_order_cnt_val; - hw_ps->ref_pic_poc14 = dpb[14].pic_order_cnt_val; -} - static void assemble_hw_pps(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run) { @@ -245,104 +157,130 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, memset(hw_ps, 0, sizeof(*hw_ps)); /* write sps */ - hw_ps->video_parameters_set_id = sps->video_parameter_set_id; - hw_ps->seq_parameters_set_id_sps = sps->seq_parameter_set_id; - hw_ps->chroma_format_idc = sps->chroma_format_idc; + rkvdec_set_bw_field(hw_ps->info, VIDEO_PARAMETER_SET_ID, sps->video_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, CHROMA_FORMAT_IDC, sps->chroma_format_idc); log2_min_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3; width = sps->pic_width_in_luma_samples; height = sps->pic_height_in_luma_samples; - hw_ps->width = width; - hw_ps->height = height; - hw_ps->bit_depth_luma = sps->bit_depth_luma_minus8 + 8; - hw_ps->bit_depth_chroma = sps->bit_depth_chroma_minus8 + 8; - hw_ps->max_pic_order_count_lsb = sps->log2_max_pic_order_cnt_lsb_minus4 + 4; - hw_ps->diff_max_min_luma_coding_block_size = sps->log2_diff_max_min_luma_coding_block_size; - hw_ps->min_luma_coding_block_size = sps->log2_min_luma_coding_block_size_minus3 + 3; - hw_ps->min_transform_block_size = sps->log2_min_luma_transform_block_size_minus2 + 2; - hw_ps->diff_max_min_transform_block_size = - sps->log2_diff_max_min_luma_transform_block_size; - hw_ps->max_transform_hierarchy_depth_inter = sps->max_transform_hierarchy_depth_inter; - hw_ps->max_transform_hierarchy_depth_intra = sps->max_transform_hierarchy_depth_intra; - hw_ps->scaling_list_enabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED); - hw_ps->amp_enabled_flag = !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED); - hw_ps->sample_adaptive_offset_enabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET); + + rkvdec_set_bw_field(hw_ps->info, PIC_WIDTH_IN_LUMA_SAMPLES, width); + rkvdec_set_bw_field(hw_ps->info, PIC_HEIGHT_IN_LUMA_SAMPLES, height); + rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_LUMA, sps->bit_depth_luma_minus8 + 8); + rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_CHROMA, sps->bit_depth_chroma_minus8 + 8); + rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_PIC_ORDER_CNT_LSB, + sps->log2_max_pic_order_cnt_lsb_minus4 + 4); + rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE, + sps->log2_diff_max_min_luma_coding_block_size); + rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_LUMA_CODING_BLOCK_SIZE, + sps->log2_min_luma_coding_block_size_minus3 + 3); + rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_TRANSFORM_BLOCK_SIZE, + sps->log2_min_luma_transform_block_size_minus2 + 2); + rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE, + sps->log2_diff_max_min_luma_transform_block_size); + rkvdec_set_bw_field(hw_ps->info, MAX_TRANSFORM_HIERARCHY_DEPTH_INTER, + sps->max_transform_hierarchy_depth_inter); + rkvdec_set_bw_field(hw_ps->info, MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA, + sps->max_transform_hierarchy_depth_intra); + rkvdec_set_bw_field(hw_ps->info, SCALING_LIST_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, AMP_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET)); pcm_enabled = !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED); - hw_ps->pcm_enabled_flag = pcm_enabled; - hw_ps->pcm_sample_bit_depth_luma = - pcm_enabled ? sps->pcm_sample_bit_depth_luma_minus1 + 1 : 0; - hw_ps->pcm_sample_bit_depth_chroma = - pcm_enabled ? sps->pcm_sample_bit_depth_chroma_minus1 + 1 : 0; - hw_ps->pcm_loop_filter_disabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED); - hw_ps->diff_max_min_pcm_luma_coding_block_size = - sps->log2_diff_max_min_pcm_luma_coding_block_size; - hw_ps->min_pcm_luma_coding_block_size = - pcm_enabled ? sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 : 0; - hw_ps->num_short_term_ref_pic_sets = sps->num_short_term_ref_pic_sets; - hw_ps->long_term_ref_pics_present_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT); - hw_ps->num_long_term_ref_pics_sps = sps->num_long_term_ref_pics_sps; - hw_ps->sps_temporal_mvp_enabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED); - hw_ps->strong_intra_smoothing_enabled_flag = - !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED); - hw_ps->sps_max_dec_pic_buffering_minus1 = sps->sps_max_dec_pic_buffering_minus1; + rkvdec_set_bw_field(hw_ps->info, PCM_ENABLED_FLAG, pcm_enabled); + rkvdec_set_bw_field(hw_ps->info, PCM_SAMPLE_BIT_DEPTH_LUMA, + pcm_enabled ? sps->pcm_sample_bit_depth_luma_minus1 + 1 : 0); + rkvdec_set_bw_field(hw_ps->info, PCM_SAMPLE_BIT_DEPTH_CHROMA, + pcm_enabled ? sps->pcm_sample_bit_depth_chroma_minus1 + 1 : 0); + rkvdec_set_bw_field(hw_ps->info, PCM_LOOP_FILTER_DISABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED)); + rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE, + sps->log2_diff_max_min_pcm_luma_coding_block_size); + rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE, + pcm_enabled ? sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 : 0); + rkvdec_set_bw_field(hw_ps->info, NUM_SHORT_TERM_REF_PIC_SETS, + sps->num_short_term_ref_pic_sets); + rkvdec_set_bw_field(hw_ps->info, LONG_TERM_REF_PICS_PRESENT_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_LONG_TERM_REF_PICS_SPS, + sps->num_long_term_ref_pics_sps); + rkvdec_set_bw_field(hw_ps->info, SPS_TEMPORAL_MVP_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, STRONG_INTRA_SMOOTHING_ENABLED_FLAG, + !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, SPS_MAX_DEC_PIC_BUFFERING_MINUS1, + sps->sps_max_dec_pic_buffering_minus1); /* write pps */ - hw_ps->picture_parameters_set_id = pps->pic_parameter_set_id; - hw_ps->seq_parameters_set_id_pps = sps->seq_parameter_set_id; - hw_ps->dependent_slice_segments_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED); - hw_ps->output_flag_present_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT); - hw_ps->num_extra_slice_header_bits = pps->num_extra_slice_header_bits; - hw_ps->sign_data_hiding_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED); - hw_ps->cabac_init_present_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT); - hw_ps->num_ref_idx_l0_default_active = pps->num_ref_idx_l0_default_active_minus1 + 1; - hw_ps->num_ref_idx_l1_default_active = pps->num_ref_idx_l1_default_active_minus1 + 1; - hw_ps->init_qp_minus26 = pps->init_qp_minus26; - hw_ps->constrained_intra_pred_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED); - hw_ps->transform_skip_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED); - hw_ps->cu_qp_delta_enabled_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED); - hw_ps->log2_min_cb_size = log2_min_cb_size + - sps->log2_diff_max_min_luma_coding_block_size - - pps->diff_cu_qp_delta_depth; - hw_ps->pps_cb_qp_offset = pps->pps_cb_qp_offset; - hw_ps->pps_cr_qp_offset = pps->pps_cr_qp_offset; - hw_ps->pps_slice_chroma_qp_offsets_present_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT); - hw_ps->weighted_pred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED); - hw_ps->weighted_bipred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED); - hw_ps->transquant_bypass_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED); + rkvdec_set_bw_field(hw_ps->info, PIC_PARAMETER_SET_ID, pps->pic_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id); + rkvdec_set_bw_field(hw_ps->info, DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, OUTPUT_FLAG_PRESENT_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_EXTRA_SLICE_HEADER_BITS, + pps->num_extra_slice_header_bits); + rkvdec_set_bw_field(hw_ps->info, SIGN_DATA_HIDING_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, CABAC_INIT_PRESENT_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L0_DEFAULT_ACTIVE, + pps->num_ref_idx_l0_default_active_minus1 + 1); + rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L1_DEFAULT_ACTIVE, + pps->num_ref_idx_l1_default_active_minus1 + 1); + rkvdec_set_bw_field(hw_ps->info, INIT_QP_MINUS26, pps->init_qp_minus26); + rkvdec_set_bw_field(hw_ps->info, CONSTRAINED_INTRA_PRED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED)); + rkvdec_set_bw_field(hw_ps->info, TRANSFORM_SKIP_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, CU_QP_DELTA_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_CU_QP_DELTA_SIZE, log2_min_cb_size + + sps->log2_diff_max_min_luma_coding_block_size - + pps->diff_cu_qp_delta_depth); + rkvdec_set_bw_field(hw_ps->info, PPS_CB_QP_OFFSET, pps->pps_cb_qp_offset); + rkvdec_set_bw_field(hw_ps->info, PPS_CR_QP_OFFSET, pps->pps_cr_qp_offset); + rkvdec_set_bw_field(hw_ps->info, PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG, + !!(pps->flags & + V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, WEIGHTED_PRED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED)); + rkvdec_set_bw_field(hw_ps->info, WEIGHTED_BIPRED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED)); + rkvdec_set_bw_field(hw_ps->info, TRANSQUANT_BYPASS_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED)); tiles_enabled = !!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED); - hw_ps->tiles_enabled_flag = tiles_enabled; - hw_ps->entropy_coding_sync_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED); - hw_ps->pps_loop_filter_across_slices_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED); - hw_ps->loop_filter_across_tiles_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED); - hw_ps->deblocking_filter_override_enabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED); - hw_ps->pps_deblocking_filter_disabled_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER); - hw_ps->pps_beta_offset_div2 = pps->pps_beta_offset_div2; - hw_ps->pps_tc_offset_div2 = pps->pps_tc_offset_div2; - hw_ps->lists_modification_present_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT); - hw_ps->log2_parallel_merge_level = pps->log2_parallel_merge_level_minus2 + 2; - hw_ps->slice_segment_header_extension_present_flag = - !!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT); - hw_ps->num_tile_columns = tiles_enabled ? pps->num_tile_columns_minus1 + 1 : 1; - hw_ps->num_tile_rows = tiles_enabled ? pps->num_tile_rows_minus1 + 1 : 1; - hw_ps->mvc_ff = 0xffff; + rkvdec_set_bw_field(hw_ps->info, TILES_ENABLED_FLAG, tiles_enabled); + rkvdec_set_bw_field(hw_ps->info, ENTROPY_CODING_SYNC_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG, + !!(pps->flags & + V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG, + !!(pps->flags & + V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED)); + rkvdec_set_bw_field(hw_ps->info, PPS_DEBLOCKING_FILTER_DISABLED_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER)); + rkvdec_set_bw_field(hw_ps->info, PPS_BETA_OFFSET_DIV2, pps->pps_beta_offset_div2); + rkvdec_set_bw_field(hw_ps->info, PPS_TC_OFFSET_DIV2, pps->pps_tc_offset_div2); + rkvdec_set_bw_field(hw_ps->info, LISTS_MODIFICATION_PRESENT_FLAG, + !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, LOG2_PARALLEL_MERGE_LEVEL, + pps->log2_parallel_merge_level_minus2 + 2); + rkvdec_set_bw_field(hw_ps->info, SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG, + !!(pps->flags & + V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT)); + rkvdec_set_bw_field(hw_ps->info, NUM_TILE_COLUMNS, + tiles_enabled ? pps->num_tile_columns_minus1 + 1 : 1); + rkvdec_set_bw_field(hw_ps->info, NUM_TILE_ROWS, + tiles_enabled ? pps->num_tile_rows_minus1 + 1 : 1); + rkvdec_set_bw_field(hw_ps->info, MVC_FF, 0xffff); // Setup tiles information memset(column_width, 0, sizeof(column_width)); @@ -367,15 +305,19 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, row_height[0] = (height + max_cu_width - 1) / max_cu_width; } - set_column_row(hw_ps, column_width, row_height); + for (i = 0; i < 20; i++) + rkvdec_set_bw_field(hw_ps->info, COLUMN_WIDTH(i), column_width[i]); + for (i = 0; i < 22; i++) + rkvdec_set_bw_field(hw_ps->info, ROW_HEIGHT(i), row_height[i]); // Setup POC information - hw_ps->current_poc = dec_params->pic_order_cnt_val; + rkvdec_set_bw_field(hw_ps->info, CURRENT_POC, dec_params->pic_order_cnt_val); - set_pps_ref_pic_poc(hw_ps, dec_params->dpb); for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { - u32 valid = !!(dec_params->num_active_dpb_entries > i); - hw_ps->ref_is_valid |= valid << i; + rkvdec_set_bw_field(hw_ps->info, REF_IS_VALID(i), + !!(dec_params->num_active_dpb_entries > i)); + rkvdec_set_bw_field(hw_ps->info, REF_PIC_POC(i), + dec_params->dpb[i].pic_order_cnt_val); } } From 7cdbd7bb21949a8fda10c7104a2b12ee363cbf5c Mon Sep 17 00:00:00 2001 From: Brandon Brnich Date: Thu, 2 Apr 2026 13:45:53 -0500 Subject: [PATCH 016/394] media: chips-media: wave5: Release m2m_ctx after Instance Removed from List Possible use after free if IRQ thread manages to obtain spinlock between m2m_ctx release and wave5_release function removing stream instance from list of active instances. The IRQ thread looks for the m2m_ctx which is freed so null pointer dereference occurs. Signed-off-by: Brandon Brnich Reviewed-by: Nicolas Dufresne Tested-by: Jackson Lee Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/chips-media/wave5/wave5-helper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c index 53a0ac068c2e..c3d34be833ff 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-helper.c +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c @@ -68,7 +68,6 @@ int wave5_vpu_release_device(struct file *filp, int ret = 0; unsigned long flags; - v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); /* * To prevent Null reference exception, the existing irq handler were * separated to two modules. @@ -89,6 +88,9 @@ int wave5_vpu_release_device(struct file *filp, list_del_init(&inst->list); spin_unlock_irqrestore(&inst->dev->irq_spinlock, flags); mutex_unlock(&inst->dev->irq_lock); + + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); + if (inst->state != VPU_INST_STATE_NONE) { u32 fail_res; From 7d5d364f8b2dcc9b6b92456fb55632fde4a4d96f Mon Sep 17 00:00:00 2001 From: Brandon Brnich Date: Thu, 2 Apr 2026 13:45:54 -0500 Subject: [PATCH 017/394] media: chips-media: wave5: Fix Reports from Kernel Lock Validator handle_dynamic_resolution change requires that the state_lock be acquired based on the lockdep_assert_held. However, the handle_dynamic_resolution_change call in initialize_sequence does not properly obtain the lock before calling. Since the v4l2_ctrl_find and s_ctrl can sleep, they should not be called while a lock is already held. Store off the fbc_buf_count then properly update control once lock has been freed. Signed-off-by: Brandon Brnich Tested-by: Jackson Lee Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../chips-media/wave5/wave5-vpu-dec.c | 47 +++++++++++++------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index d419076d7052..bb2ba9204a83 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -283,10 +283,23 @@ static void send_eos_event(struct vpu_instance *inst) inst->sent_eos = true; } +static void wave5_update_min_bufs_ctrl(struct vpu_instance *inst, u32 fbc_buf_count) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + struct v4l2_ctrl *ctrl; + + if (!fbc_buf_count || fbc_buf_count == v4l2_m2m_num_dst_bufs_ready(m2m_ctx)) + return; + + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, fbc_buf_count); +} + static int handle_dynamic_resolution_change(struct vpu_instance *inst) { struct v4l2_fh *fh = &inst->v4l2_fh; - struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; static const struct v4l2_event vpu_event_src_ch = { .type = V4L2_EVENT_SOURCE_CHANGE, @@ -305,14 +318,6 @@ static int handle_dynamic_resolution_change(struct vpu_instance *inst) inst->needs_reallocation = true; inst->fbc_buf_count = initial_info->min_frame_buffer_count + 1; - if (inst->fbc_buf_count != v4l2_m2m_num_dst_bufs_ready(m2m_ctx)) { - struct v4l2_ctrl *ctrl; - - ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, - V4L2_CID_MIN_BUFFERS_FOR_CAPTURE); - if (ctrl) - v4l2_ctrl_s_ctrl(ctrl, inst->fbc_buf_count); - } if (p_dec_info->initial_info_obtained) { const struct vpu_format *vpu_fmt; @@ -439,19 +444,24 @@ static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst) if ((dec_info.index_frame_display == DISPLAY_IDX_FLAG_SEQ_END || dec_info.sequence_changed)) { unsigned long flags; + u32 fbc_buf_count = 0; spin_lock_irqsave(&inst->state_spinlock, flags); if (!v4l2_m2m_has_stopped(m2m_ctx)) { switch_state(inst, VPU_INST_STATE_STOP); - if (dec_info.sequence_changed) + if (dec_info.sequence_changed) { handle_dynamic_resolution_change(inst); - else + fbc_buf_count = inst->fbc_buf_count; + } else { send_eos_event(inst); + } flag_last_buffer_done(inst); } spin_unlock_irqrestore(&inst->state_spinlock, flags); + + wave5_update_min_bufs_ctrl(inst, fbc_buf_count); } if (inst->sent_eos && @@ -1592,8 +1602,9 @@ static const struct vpu_instance_ops wave5_vpu_dec_inst_ops = { static int initialize_sequence(struct vpu_instance *inst) { struct dec_initial_info initial_info; - int ret = 0; unsigned long flags; + u32 fbc_buf_count; + int ret = 0; memset(&initial_info, 0, sizeof(struct dec_initial_info)); @@ -1617,8 +1628,11 @@ static int initialize_sequence(struct vpu_instance *inst) spin_lock_irqsave(&inst->state_spinlock, flags); handle_dynamic_resolution_change(inst); + fbc_buf_count = inst->fbc_buf_count; spin_unlock_irqrestore(&inst->state_spinlock, flags); + wave5_update_min_bufs_ctrl(inst, fbc_buf_count); + return 0; } @@ -1659,6 +1673,7 @@ static void wave5_vpu_dec_device_run(void *priv) ret = initialize_sequence(inst); if (ret) { unsigned long flags; + u32 fbc_buf_count = 0; spin_lock_irqsave(&inst->state_spinlock, flags); if (wave5_is_draining_or_eos(inst) && @@ -1667,14 +1682,18 @@ static void wave5_vpu_dec_device_run(void *priv) switch_state(inst, VPU_INST_STATE_STOP); - if (vb2_is_streaming(dst_vq)) + if (vb2_is_streaming(dst_vq)) { send_eos_event(inst); - else + } else { handle_dynamic_resolution_change(inst); + fbc_buf_count = inst->fbc_buf_count; + } flag_last_buffer_done(inst); } spin_unlock_irqrestore(&inst->state_spinlock, flags); + + wave5_update_min_bufs_ctrl(inst, fbc_buf_count); } else { set_instance_state(inst, VPU_INST_STATE_INIT_SEQ); } From d99732334aaf33b9f93926b70b6a11c2cef3de39 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 7 Apr 2026 01:14:02 +0300 Subject: [PATCH 018/394] media: cedrus: Fix missing cleanup in error path According to the documentation struct v4l2_fh has to be cleaned up with v4l2_fh_exit() before being freed. [1] Currently there is no actual bug here, when v4l2_fh_exit() isn't called. v4l2_fh_exit() in this case only destroys internal mutex. But it may change in the future, when v4l2_fh_init/v4l2_fh_exit will be enhanced. 1. https://docs.kernel.org/driver-api/media/v4l2-fh.html Signed-off-by: Samuel Holland Signed-off-by: Andrey Skvortsov Fixes: 50e761516f2b ("media: platform: Add Cedrus VPU decoder driver") Cc: stable@vger.kernel.org Acked-by: Paul Kocialkowski Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/sunxi/cedrus/cedrus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index 6600245dff0e..1d2130f35fff 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -391,6 +391,7 @@ static int cedrus_open(struct file *file) err_m2m_release: v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); err_free: + v4l2_fh_exit(&ctx->fh); kfree(ctx); mutex_unlock(&dev->dev_mutex); From f0a22f1d602ed499a192284de5e811a73421f0c7 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 7 Apr 2026 01:14:40 +0300 Subject: [PATCH 019/394] media: cedrus: Fix failure to clean up hardware on probe failure If V4L2 device fails to register, then SRAM still be claimed and as a result driver will not be able to probe again. cedrus 1c0e000.video-codec: Failed to claim SRAM cedrus 1c0e000.video-codec: Failed to probe hardware cedrus 1c0e000.video-codec: probe with driver cedrus failed with error -16 cedrus_hw_remove undoes everything that was previously done by cedrus_hw_probe, such as disabling runtime power management and releasing the claimed SRAM and reserved memory region. Signed-off-by: Samuel Holland Signed-off-by: Andrey Skvortsov Fixes: 50e761516f2b ("media: platform: Add Cedrus VPU decoder driver") Acked-by: Paul Kocialkowski Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/sunxi/cedrus/cedrus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index 1d2130f35fff..ee0e286add67 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -477,7 +477,7 @@ static int cedrus_probe(struct platform_device *pdev) ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { dev_err(&pdev->dev, "Failed to register V4L2 device\n"); - return ret; + goto err_hw; } vfd = &dev->vfd; @@ -538,6 +538,8 @@ static int cedrus_probe(struct platform_device *pdev) v4l2_m2m_release(dev->m2m_dev); err_v4l2: v4l2_device_unregister(&dev->v4l2_dev); +err_hw: + cedrus_hw_remove(dev); return ret; } From 94826832483708d4386226e6d966dd946f37a2bf Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Fri, 10 Apr 2026 08:15:42 +0200 Subject: [PATCH 020/394] media: verisilicon: remove hantro_run declaration The function hantro_run() is declared but never defined nor used, remove the dangling declaration. Signed-off-by: Rouven Czerwinski Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/hantro_hw.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h index 5f2011529f02..13e573f1f19d 100644 --- a/drivers/media/platform/verisilicon/hantro_hw.h +++ b/drivers/media/platform/verisilicon/hantro_hw.h @@ -427,7 +427,6 @@ extern const struct hantro_postproc_ops rockchip_vpu981_postproc_ops; extern const u32 hantro_vp8_dec_mc_filter[8][6]; void hantro_watchdog(struct work_struct *work); -void hantro_run(struct hantro_ctx *ctx); void hantro_irq_done(struct hantro_dev *vpu, enum vb2_buffer_state result); void hantro_start_prepare_run(struct hantro_ctx *ctx); From 968b741872914a15363af0daf24ed2e82ec355f3 Mon Sep 17 00:00:00 2001 From: Nas Chung Date: Wed, 15 Apr 2026 18:25:21 +0900 Subject: [PATCH 021/394] media: v4l2-common: Add YUV24 format info The YUV24 format is missing an entry in the v4l2_format_info(). The YUV24 format is the packed YUV 4:4:4 formats with 8 bits per component. Fixes: 0376a51fbe5e ("media: v4l: Add packed YUV444 24bpp pixel format") Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 554c591e1113..55bcd5975d9f 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -281,6 +281,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_Y212, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_Y216, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_YUV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_MT2110T, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }}, { .format = V4L2_PIX_FMT_MT2110R, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2, From d3dcde2dc1f182b93261f1a14651356cc2d12a7b Mon Sep 17 00:00:00 2001 From: Biju Das Date: Mon, 30 Mar 2026 12:00:10 +0100 Subject: [PATCH 022/394] media: dt-bindings: media: renesas,fcp: Document RZ/G3L FCPVD IP The FCPVD block on the RZ/G3L SoC is identical to the one found on the RZ/G2L SoC. Document RZ/G3L FCPVD IP. Signed-off-by: Biju Das Acked-by: Rob Herring (Arm) Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260330110012.131273-1-biju.das.jz@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/renesas,fcp.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.yaml b/Documentation/devicetree/bindings/media/renesas,fcp.yaml index b5eff6fec8a9..86b176a634e1 100644 --- a/Documentation/devicetree/bindings/media/renesas,fcp.yaml +++ b/Documentation/devicetree/bindings/media/renesas,fcp.yaml @@ -30,6 +30,7 @@ properties: - renesas,r9a07g043u-fcpvd # RZ/G2UL - renesas,r9a07g044-fcpvd # RZ/G2{L,LC} - renesas,r9a07g054-fcpvd # RZ/V2L + - renesas,r9a08g046-fcpvd # RZ/G3L - renesas,r9a09g056-fcpvd # RZ/V2N - renesas,r9a09g057-fcpvd # RZ/V2H(P) - const: renesas,fcpv # Generic FCP for VSP fallback @@ -77,6 +78,7 @@ allOf: - renesas,r9a07g043u-fcpvd - renesas,r9a07g044-fcpvd - renesas,r9a07g054-fcpvd + - renesas,r9a08g046-fcpvd - renesas,r9a09g056-fcpvd - renesas,r9a09g057-fcpvd then: From b01725760314f9bb592b15cc0c692eefdb20633d Mon Sep 17 00:00:00 2001 From: Biju Das Date: Mon, 30 Mar 2026 11:56:29 +0100 Subject: [PATCH 023/394] media: dt-bindings: media: renesas,vsp1: Document RZ/G3L VSPD The VSPD block on the RZ/G3L SoC is identical to the one found on the RZ/G2L SoC. Document RZ/G3L VSPD. Signed-off-by: Biju Das Acked-by: Rob Herring (Arm) Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260330105637.130189-1-biju.das.jz@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/renesas,vsp1.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml index 07a97dd87a5b..5447b9b78930 100644 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml @@ -25,6 +25,7 @@ properties: - enum: - renesas,r9a07g043u-vsp2 # RZ/G2UL - renesas,r9a07g054-vsp2 # RZ/V2L + - renesas,r9a08g046-vsp2 # RZ/G3L - renesas,r9a09g056-vsp2 # RZ/V2N - renesas,r9a09g057-vsp2 # RZ/V2H(P) - const: renesas,r9a07g044-vsp2 # RZ/G2L fallback From acefeeee00c427b28f77a7217dad7216582e341f Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 8 Apr 2026 12:37:02 +0200 Subject: [PATCH 024/394] media: dt-bindings: media: renesas,fcp: Document RZ/G3E SoC The FCPVD block on the RZ/G3E SoC is identical to the one found on the RZ/G2L SoC. No driver changes are required, as `renesas,fcpv` will be used as a fallback compatible string on the RZ/G3E SoC. Reviewed-by: Geert Uytterhoeven Acked-by: Krzysztof Kozlowski Signed-off-by: Tommaso Merciai Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/5aaf561a66c24639477a99d3861ca969a81673f0.1775636898.git.tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/renesas,fcp.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.yaml b/Documentation/devicetree/bindings/media/renesas,fcp.yaml index 86b176a634e1..5e11ae0ee456 100644 --- a/Documentation/devicetree/bindings/media/renesas,fcp.yaml +++ b/Documentation/devicetree/bindings/media/renesas,fcp.yaml @@ -31,6 +31,7 @@ properties: - renesas,r9a07g044-fcpvd # RZ/G2{L,LC} - renesas,r9a07g054-fcpvd # RZ/V2L - renesas,r9a08g046-fcpvd # RZ/G3L + - renesas,r9a09g047-fcpvd # RZ/G3E - renesas,r9a09g056-fcpvd # RZ/V2N - renesas,r9a09g057-fcpvd # RZ/V2H(P) - const: renesas,fcpv # Generic FCP for VSP fallback @@ -79,6 +80,7 @@ allOf: - renesas,r9a07g044-fcpvd - renesas,r9a07g054-fcpvd - renesas,r9a08g046-fcpvd + - renesas,r9a09g047-fcpvd - renesas,r9a09g056-fcpvd - renesas,r9a09g057-fcpvd then: From d9c8c4adf23d17549c0ec9c85b99d85a0ee6cf18 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 8 Apr 2026 12:37:01 +0200 Subject: [PATCH 025/394] media: dt-bindings: media: renesas,vsp1: Document RZ/G3E The VSPD block on the RZ/G3E SoC is identical to the one found on the RZ/G2L SoC. No driver changes are required, as `renesas,r9a07g044-vsp2` will be used as a fallback compatible string on the RZ/G3E SoC. Reviewed-by: Geert Uytterhoeven Acked-by: Krzysztof Kozlowski Signed-off-by: Tommaso Merciai Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/c1e2eb36af01e0cddd8050ba70847c3a0821c91e.1775636898.git.tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/renesas,vsp1.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml index 5447b9b78930..803358780f01 100644 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml @@ -26,6 +26,7 @@ properties: - renesas,r9a07g043u-vsp2 # RZ/G2UL - renesas,r9a07g054-vsp2 # RZ/V2L - renesas,r9a08g046-vsp2 # RZ/G3L + - renesas,r9a09g047-vsp2 # RZ/G3E - renesas,r9a09g056-vsp2 # RZ/V2N - renesas,r9a09g057-vsp2 # RZ/V2H(P) - const: renesas,r9a07g044-vsp2 # RZ/G2L fallback From 0692c2602e4cd410aa045f8991bd1c142b2e56f9 Mon Sep 17 00:00:00 2001 From: Riccardo Boninsegna Date: Sun, 29 Mar 2026 12:37:09 +0200 Subject: [PATCH 026/394] media: rc: mceusb: Add support for 04eb:e033 This is a Sonix SN8P2202XG microcontroller with firmware compatible with the already supported Northstar 04eb:e004, implementing an MCE IR receiver (PCB seems to be tracked for a transmitter too but missing related parts). Found in a Skintek SK-CR-IN+IR ( http://www.skintek.it/SK-CR-IN+IR.php ) internal 3.5 inch USB card reader and MCE receiver combo (implemented by, and wired as, separate USB devices) PCB marking: AU6475 966816 STIR REV:A02 MCE Signed-off-by: Riccardo Boninsegna Signed-off-by: Sean Young --- drivers/media/rc/mceusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 6a9e4382a224..39ba7f6a2549 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -397,6 +397,8 @@ static const struct usb_device_id mceusb_dev_table[] = { { USB_DEVICE(VENDOR_COMPRO, 0x3082) }, /* Northstar Systems, Inc. eHome Infrared Transceiver */ { USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) }, + /* Northstar Systems, Inc. eHome Infrared Transceiver - variant */ + { USB_DEVICE(VENDOR_NORTHSTAR, 0xe033) }, /* TiVo PC IR Receiver */ { USB_DEVICE(VENDOR_TIVO, 0x2000), .driver_info = TIVO_KIT }, From 94637cc807da2443f34c6368ff64b2c85c6c6d13 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 12:11:37 +0200 Subject: [PATCH 027/394] media: imon_raw: Refactor endpoint lookup Use the common USB helper for looking up interrupt-in endpoints instead of open coding. Signed-off-by: Johan Hovold Signed-off-by: Sean Young --- drivers/media/rc/imon_raw.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/media/rc/imon_raw.c b/drivers/media/rc/imon_raw.c index 3a526dea6532..295acd6ba9e1 100644 --- a/drivers/media/rc/imon_raw.c +++ b/drivers/media/rc/imon_raw.c @@ -105,26 +105,16 @@ static void imon_ir_rx(struct urb *urb) static int imon_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_endpoint_descriptor *ir_ep = NULL; - struct usb_host_interface *idesc; + struct usb_endpoint_descriptor *ir_ep; struct usb_device *udev; struct rc_dev *rcdev; struct imon *imon; - int i, ret; + int ret; udev = interface_to_usbdev(intf); - idesc = intf->cur_altsetting; - for (i = 0; i < idesc->desc.bNumEndpoints; i++) { - struct usb_endpoint_descriptor *ep = &idesc->endpoint[i].desc; - - if (usb_endpoint_is_int_in(ep)) { - ir_ep = ep; - break; - } - } - - if (!ir_ep) { + ret = usb_find_int_in_endpoint(intf->cur_altsetting, &ir_ep); + if (ret) { dev_err(&intf->dev, "IR endpoint missing"); return -ENODEV; } From 5a6ec95b0b471105ac601a21db87a03f7f1af967 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 12:11:38 +0200 Subject: [PATCH 028/394] media: irtoy: Refactor endpoint lookup Use the common USB helpers for looking up bulk and interrupt endpoints (and determining max packet size) instead of open coding. Note that the device has two bulk endpoints so there is no functional change here. Signed-off-by: Johan Hovold Signed-off-by: Sean Young --- drivers/media/rc/ir_toy.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c index 089833e41178..ee645882dd5a 100644 --- a/drivers/media/rc/ir_toy.c +++ b/drivers/media/rc/ir_toy.c @@ -393,27 +393,15 @@ static int irtoy_probe(struct usb_interface *intf, { struct usb_host_interface *idesc = intf->cur_altsetting; struct usb_device *usbdev = interface_to_usbdev(intf); - struct usb_endpoint_descriptor *ep_in = NULL; - struct usb_endpoint_descriptor *ep_out = NULL; - struct usb_endpoint_descriptor *ep = NULL; + struct usb_endpoint_descriptor *ep_in, *ep_out; struct irtoy *irtoy; struct rc_dev *rc; struct urb *urb; - int i, pipe, err = -ENOMEM; + int pipe, err; - for (i = 0; i < idesc->desc.bNumEndpoints; i++) { - ep = &idesc->endpoint[i].desc; - - if (!ep_in && usb_endpoint_is_bulk_in(ep) && - usb_endpoint_maxp(ep) == MAX_PACKET) - ep_in = ep; - - if (!ep_out && usb_endpoint_is_bulk_out(ep) && - usb_endpoint_maxp(ep) == MAX_PACKET) - ep_out = ep; - } - - if (!ep_in || !ep_out) { + err = usb_find_common_endpoints(idesc, &ep_in, &ep_out, NULL, NULL); + if (err || usb_endpoint_maxp(ep_in) != MAX_PACKET || + usb_endpoint_maxp(ep_out) != MAX_PACKET) { dev_err(&intf->dev, "required endpoints not found\n"); return -ENODEV; } @@ -422,6 +410,7 @@ static int irtoy_probe(struct usb_interface *intf, if (!irtoy) return -ENOMEM; + err = -ENOMEM; irtoy->in = kmalloc(MAX_PACKET, GFP_KERNEL); if (!irtoy->in) goto free_irtoy; From 0bcbfd1c1142d85faef8df5cb679d37f71394c5f Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 21 Mar 2026 23:41:50 +0200 Subject: [PATCH 029/394] media: v4l2-subdev: Fail {enable,disable}_streams and s_streaming nicely If a sub-device does not set enable_streams() and disable_streams() pad ops while it sets the s_stream() video op to v4l2_subdev_s_stream_helper(), enabling or disabling streaming either way on the sub-device will result calling v4l2_subdev_s_stream_helper() and v4l2_subdev_{enable,disable}_streams() recursively, exhausting the stack. Return -ENOIOCTLCMD in this case to handle the situation gracefully. Fixes: b62949ddaa52 ("media: subdev: Support single-stream case in v4l2_subdev_enable/disable_streams()") Cc: stable@vger.kernel.org Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart --- drivers/media/v4l2-core/v4l2-subdev.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 831c69c958b8..d00d27d49060 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2504,6 +2504,10 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable) u64 source_mask = 0; int pad_index = -1; + if (WARN_ON(!v4l2_subdev_has_op(sd, pad, enable_streams) || + !v4l2_subdev_has_op(sd, pad, disable_streams))) + return -ENOIOCTLCMD; + /* * Find the source pad. This helper is meant for subdevs that have a * single source pad, so failures shouldn't happen, but catch them From 65e0242c375284bd5b884106cd722591291d0e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Wed, 25 Mar 2026 13:01:18 +0100 Subject: [PATCH 030/394] media: mgb4: Fix DV timings limits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide the real DV timings limits in VIDIOC_DV_TIMINGS_CAP. For the outputs the pixelclock is limited by the CMT table <25000kHz, 2*94642kHz>, for the inputs a slightly broader range is possible. The minimal supported/tested resolution is 64px. Signed-off-by: Martin Tůma Signed-off-by: Hans Verkuil --- drivers/media/pci/mgb4/mgb4_vin.c | 8 ++++---- drivers/media/pci/mgb4/mgb4_vout.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/pci/mgb4/mgb4_vin.c b/drivers/media/pci/mgb4/mgb4_vin.c index 74fb00c4a408..b24785455691 100644 --- a/drivers/media/pci/mgb4/mgb4_vin.c +++ b/drivers/media/pci/mgb4/mgb4_vin.c @@ -85,12 +85,12 @@ static const struct mgb4_i2c_kv gmsl1_i2c[] = { static const struct v4l2_dv_timings_cap video_timings_cap = { .type = V4L2_DV_BT_656_1120, .bt = { - .min_width = 240, + .min_width = 64, .max_width = 4096, - .min_height = 240, + .min_height = 64, .max_height = 4096, - .min_pixelclock = 1843200, /* 320 x 240 x 24Hz */ - .max_pixelclock = 530841600, /* 4096 x 2160 x 60Hz */ + .min_pixelclock = 20000000, + .max_pixelclock = 200000000, .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | diff --git a/drivers/media/pci/mgb4/mgb4_vout.c b/drivers/media/pci/mgb4/mgb4_vout.c index 7725bcd55e4c..22fbc9e53819 100644 --- a/drivers/media/pci/mgb4/mgb4_vout.c +++ b/drivers/media/pci/mgb4/mgb4_vout.c @@ -53,12 +53,12 @@ static const struct mgb4_i2c_kv gmsl1_i2c[] = { static const struct v4l2_dv_timings_cap video_timings_cap = { .type = V4L2_DV_BT_656_1120, .bt = { - .min_width = 240, + .min_width = 64, .max_width = 4096, - .min_height = 240, + .min_height = 64, .max_height = 4096, - .min_pixelclock = 1843200, /* 320 x 240 x 24Hz */ - .max_pixelclock = 530841600, /* 4096 x 2160 x 60Hz */ + .min_pixelclock = 25000000, + .max_pixelclock = 189284000, .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | From e024767f90f9f50bfcce4b20bb74237ad72450f3 Mon Sep 17 00:00:00 2001 From: Ken Lin Date: Thu, 2 Apr 2026 15:50:08 +0800 Subject: [PATCH 031/394] media: platform: cros-ec: Add Kulnex and Moxoe to the match table The Google Kulnex and Moxoe device uses the same approach as Google Brask which enables the HDMI CEC via the cros-ec-cec driver. Signed-off-by: Ken Lin Signed-off-by: Hans Verkuil --- drivers/media/cec/platform/cros-ec/cros-ec-cec.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c index 419b9a7abcce..4b3f9bff6067 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -334,6 +334,10 @@ static const struct cec_dmi_match cec_dmi_match_table[] = { { "Google", "Dirks", "0000:00:02.0", port_ab_conns }, /* Google Moxie */ { "Google", "Moxie", "0000:00:02.0", port_b_conns }, + /* Google Kulnex */ + { "Google", "Kulnex", "0000:00:02.0", port_b_conns }, + /* Google Moxoe */ + { "Google", "Moxoe", "0000:00:02.0", port_b_conns }, }; static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, From 560d29f4eef9fdac49fd5373502c42df4fc92752 Mon Sep 17 00:00:00 2001 From: Kells Ping Date: Mon, 20 Apr 2026 10:37:14 +0800 Subject: [PATCH 032/394] media: platform: cros-ec: Add Dirkson to the match table The Google Dirkson device uses the same approach as the Google Brask which enables the HDMI CEC via the cros-ec-cec driver. Signed-off-by: Kells Ping Signed-off-by: Hans Verkuil --- drivers/media/cec/platform/cros-ec/cros-ec-cec.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c index 4b3f9bff6067..ec1da9cd3674 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -338,6 +338,8 @@ static const struct cec_dmi_match cec_dmi_match_table[] = { { "Google", "Kulnex", "0000:00:02.0", port_b_conns }, /* Google Moxoe */ { "Google", "Moxoe", "0000:00:02.0", port_b_conns }, + /* Google Dirkson */ + { "Google", "Dirkson", "0000:00:02.0", port_ab_conns }, }; static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, From c3a78691be8245e52ced489f268e413f18061ac2 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Fri, 24 Apr 2026 23:36:01 +0900 Subject: [PATCH 033/394] media: cec: seco: unregister adapter on IR probe failure If secocec_ir_probe() fails after cec_register_adapter() succeeds, probe returns an error and the driver remove callback is not called. The current unwind path unregisters the notifier and then falls through to cec_delete_adapter(), which violates the CEC adapter lifetime rules after a successful registration. Add a registered-adapter unwind path that unregisters the notifier and the adapter instead. Fixes: daef95769b3a ("media: seco-cec: add Consumer-IR support") Cc: stable@vger.kernel.org Signed-off-by: Myeonghun Pak Signed-off-by: Hans Verkuil --- drivers/media/cec/platform/seco/seco-cec.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/cec/platform/seco/seco-cec.c b/drivers/media/cec/platform/seco/seco-cec.c index b7bb49f02395..97ed9654c78a 100644 --- a/drivers/media/cec/platform/seco/seco-cec.c +++ b/drivers/media/cec/platform/seco/seco-cec.c @@ -649,7 +649,7 @@ static int secocec_probe(struct platform_device *pdev) ret = secocec_ir_probe(secocec); if (ret) - goto err_notifier; + goto err_unregister_adapter; platform_set_drvdata(pdev, secocec); @@ -657,6 +657,10 @@ static int secocec_probe(struct platform_device *pdev) return ret; +err_unregister_adapter: + cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap); + cec_unregister_adapter(secocec->cec_adap); + goto err; err_notifier: cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap); err_delete_adapter: From d7fe0d53b2a8b08f6042cc89315118dee49e072e Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 26 Mar 2026 15:15:23 +0200 Subject: [PATCH 034/394] =?UTF-8?q?media:=20dw9719:=20Add=20back=20the=20I?= =?UTF-8?q?=C2=B2C=20device=20id=20table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The I²C device id table is necessary as the device may be, besides through system firmware, also instantiated in the IPU bridge so matching takes place using the I²C device id table. Add back the table, with ids for all supported devices. Reported-by: Michael Anthony Closes: https://lore.kernel.org/linux-media/AMBP190MB2678E7DC048409068260DCE8ED4AA@AMBP190MB2678.EURP190.PROD.OUTLOOK.COM/ Fixes: 15faf0fa1472 ("media: i2c: dw9719: Remove unused i2c device id table") Cc: stable@vger.kernel.org # for v6.19 and later Signed-off-by: Sakari Ailus --- drivers/media/i2c/dw9719.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c index 59558335989e..3b7ba88fd67c 100644 --- a/drivers/media/i2c/dw9719.c +++ b/drivers/media/i2c/dw9719.c @@ -439,6 +439,15 @@ static void dw9719_remove(struct i2c_client *client) pm_runtime_set_suspended(&client->dev); } +static const struct i2c_device_id dw9719_id_table[] = { + { .name = "dw9718s", .driver_data = (kernel_ulong_t)DW9718S }, + { .name = "dw9719", .driver_data = (kernel_ulong_t)DW9719 }, + { .name = "dw9761", .driver_data = (kernel_ulong_t)DW9761 }, + { .name = "dw9800k", .driver_data = (kernel_ulong_t)DW9800K }, + { } +}; +MODULE_DEVICE_TABLE(i2c, dw9719_id_table); + static const struct of_device_id dw9719_of_table[] = { { .compatible = "dongwoon,dw9718s", .data = (const void *)DW9718S }, { .compatible = "dongwoon,dw9719", .data = (const void *)DW9719 }, @@ -459,6 +468,7 @@ static struct i2c_driver dw9719_i2c_driver = { }, .probe = dw9719_probe, .remove = dw9719_remove, + .id_table = dw9719_id_table, }; module_i2c_driver(dw9719_i2c_driver); From d3a9a8cf2d7fd61a2f63df61f6cbc0a9bb007cc0 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Mon, 13 Apr 2026 17:12:44 +0200 Subject: [PATCH 035/394] staging: media: ipu7: fix double-free and use-after-free in error paths In both ipu7_isys_init() and ipu7_psys_init(), pdata is allocated and then passed to ipu7_bus_initialize_device(), which stores it in adev->pdata. The ipu7_bus_release() function frees adev->pdata when the device's reference count drops to zero. Two error paths incorrectly call kfree(pdata) after the device teardown has already freed it: 1. When ipu7_mmu_init() fails: put_device() is called, which drops the reference count to zero and triggers ipu7_bus_release() -> kfree(pdata). The subsequent kfree(pdata) is a double-free. 2. When ipu7_bus_add_device() fails: it calls auxiliary_device_uninit() internally, which calls put_device() -> ipu7_bus_release() -> kfree(pdata). The subsequent kfree(pdata) is again a double-free. Note that the kfree(pdata) when ipu7_bus_initialize_device() itself fails is correct, because in that case auxiliary_device_init() failed and the release function was never set up, so pdata must be freed manually. Additionally, the error code was not saved before calling put_device(), causing ERR_CAST() to dereference the already-freed adev pointer when constructing the return value. Fix this by saving the error from dev_err_probe() before put_device() and returning ERR_PTR() instead. Remove the redundant kfree(pdata) calls and fix the use-after-free in the return values of the two affected error paths. Fixes: b7fe4c0019b1 ("media: staging/ipu7: add Intel IPU7 PCI device driver") Cc: stable@vger.kernel.org Reviewed-by: Dan Carpenter Signed-off-by: Alexandru Hossu Signed-off-by: Sakari Ailus --- drivers/staging/media/ipu7/ipu7.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c index c771e763f8c5..310e3f24e571 100644 --- a/drivers/staging/media/ipu7/ipu7.c +++ b/drivers/staging/media/ipu7/ipu7.c @@ -2169,21 +2169,18 @@ ipu7_isys_init(struct pci_dev *pdev, struct device *parent, isys_adev->mmu = ipu7_mmu_init(dev, base, ISYS_MMID, &ipdata->hw_variant); if (IS_ERR(isys_adev->mmu)) { - dev_err_probe(dev, PTR_ERR(isys_adev->mmu), - "ipu7_mmu_init(isys_adev->mmu) failed\n"); + ret = dev_err_probe(dev, PTR_ERR(isys_adev->mmu), + "ipu7_mmu_init(isys_adev->mmu) failed\n"); put_device(&isys_adev->auxdev.dev); - kfree(pdata); - return ERR_CAST(isys_adev->mmu); + return ERR_PTR(ret); } isys_adev->mmu->dev = &isys_adev->auxdev.dev; isys_adev->subsys = IPU_IS; ret = ipu7_bus_add_device(isys_adev); - if (ret) { - kfree(pdata); + if (ret) return ERR_PTR(ret); - } return isys_adev; } @@ -2216,21 +2213,18 @@ ipu7_psys_init(struct pci_dev *pdev, struct device *parent, psys_adev->mmu = ipu7_mmu_init(&pdev->dev, base, PSYS_MMID, &ipdata->hw_variant); if (IS_ERR(psys_adev->mmu)) { - dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), - "ipu7_mmu_init(psys_adev->mmu) failed\n"); + ret = dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), + "ipu7_mmu_init(psys_adev->mmu) failed\n"); put_device(&psys_adev->auxdev.dev); - kfree(pdata); - return ERR_CAST(psys_adev->mmu); + return ERR_PTR(ret); } psys_adev->mmu->dev = &psys_adev->auxdev.dev; psys_adev->subsys = IPU_PS; ret = ipu7_bus_add_device(psys_adev); - if (ret) { - kfree(pdata); + if (ret) return ERR_PTR(ret); - } return psys_adev; } From 062ba06f971c0c9f9b4cf37b3b7e8ee8a759e11c Mon Sep 17 00:00:00 2001 From: Lian Xiangyu Date: Tue, 24 Mar 2026 01:31:40 +0800 Subject: [PATCH 036/394] staging: media: ipu7: remove 'U' suffix from hexadecimal literals The ipu7 driver's TODO specifies that the 'U' suffix should be removed from hexadecimal values in register definitions. This patch cleans up the definitions in the header files within the ipu7 directory to comply with the requirements and improve consistency. The modification was verified by comparing the disassembly of the built-in.a archive before and after the change. The MD5 hashes of the disassembly output remained identical, confirming that this is a purely cosmetic cleanup with no functional impact on the binary. Signed-off-by: Lian Xiangyu Signed-off-by: Sakari Ailus --- .../staging/media/ipu7/abi/ipu7_fw_boot_abi.h | 78 +++++++++---------- .../media/ipu7/abi/ipu7_fw_common_abi.h | 4 +- .../staging/media/ipu7/abi/ipu7_fw_msg_abi.h | 12 +-- .../staging/media/ipu7/ipu7-buttress-regs.h | 10 +-- 4 files changed, 52 insertions(+), 52 deletions(-) diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h index a1519c4fe661..56b90aab83ea 100644 --- a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h @@ -42,7 +42,7 @@ struct ia_gofo_logger_config { ((IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET) + \ (u32)(IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP)) #define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PRIMARY_OFFSET (0U) -#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET (0x3000U / 4U) +#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET (0x3000 / 4U) #define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 2U) #define IA_GOFO_HKR_HIF_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ @@ -75,7 +75,7 @@ enum ia_gofo_boot_uc_tile_frequency_units { }; #define IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(boot_state) \ - (0xdead0000U == ((boot_state) & 0xffff0000U)) + (0xdead0000 == ((boot_state) & 0xffff0000)) struct ia_gofo_boot_config { u32 length; @@ -104,35 +104,35 @@ struct ia_gofo_secondary_boot_config { #pragma pack(pop) -#define IA_GOFO_WDT_TIMEOUT_ERR 0xdead0401U -#define IA_GOFO_MEM_FATAL_DME_ERR 0xdead0801U -#define IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR 0xdead0802U -#define IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR 0xdead0803U -#define IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR 0xdead0804U -#define IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR 0xdead0805U -#define IA_GOFO_DOUBLE_EXCEPTION_ERR 0xdead0806U -#define IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR 0xdead1000U -#define IA_GOFO_BIST_DATA_INTEGRITY_FAILURE 0xdead1010U +#define IA_GOFO_WDT_TIMEOUT_ERR 0xdead0401 +#define IA_GOFO_MEM_FATAL_DME_ERR 0xdead0801 +#define IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR 0xdead0802 +#define IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR 0xdead0803 +#define IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR 0xdead0804 +#define IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR 0xdead0805 +#define IA_GOFO_DOUBLE_EXCEPTION_ERR 0xdead0806 +#define IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR 0xdead1000 +#define IA_GOFO_BIST_DATA_INTEGRITY_FAILURE 0xdead1010 enum ia_gofo_boot_state { - IA_GOFO_FW_BOOT_STATE_SECONDARY_BOOT_CONFIG_READY = 0x57a7b000U, - IA_GOFO_FW_BOOT_STATE_UNINIT = 0x57a7e000U, - IA_GOFO_FW_BOOT_STATE_STARTING_0 = 0x57a7d000U, - IA_GOFO_FW_BOOT_STATE_CACHE_INIT_DONE = 0x57a7d010U, - IA_GOFO_FW_BOOT_STATE_MEM_INIT_DONE = 0x57a7d020U, - IA_GOFO_FW_BOOT_STATE_STACK_INIT_DONE = 0x57a7d030U, - IA_GOFO_FW_BOOT_STATE_EARLY_BOOT_DONE = 0x57a7d100U, - IA_GOFO_FW_BOOT_STATE_BOOT_CONFIG_START = 0x57a7d200U, - IA_GOFO_FW_BOOT_STATE_QUEUE_INIT_DONE = 0x57a7d300U, - IA_GOFO_FW_BOOT_STATE_READY = 0x57a7e100U, - IA_GOFO_FW_BOOT_STATE_CRIT_UNSPECIFIED = 0xdead0001U, - IA_GOFO_FW_BOOT_STATE_CRIT_CFG_PTR = 0xdead0101U, - IA_GOFO_FW_BOOT_STATE_CRIT_CFG_VERSION = 0xdead0201U, - IA_GOFO_FW_BOOT_STATE_CRIT_MSG_VERSION = 0xdead0301U, + IA_GOFO_FW_BOOT_STATE_SECONDARY_BOOT_CONFIG_READY = 0x57a7b000, + IA_GOFO_FW_BOOT_STATE_UNINIT = 0x57a7e000, + IA_GOFO_FW_BOOT_STATE_STARTING_0 = 0x57a7d000, + IA_GOFO_FW_BOOT_STATE_CACHE_INIT_DONE = 0x57a7d010, + IA_GOFO_FW_BOOT_STATE_MEM_INIT_DONE = 0x57a7d020, + IA_GOFO_FW_BOOT_STATE_STACK_INIT_DONE = 0x57a7d030, + IA_GOFO_FW_BOOT_STATE_EARLY_BOOT_DONE = 0x57a7d100, + IA_GOFO_FW_BOOT_STATE_BOOT_CONFIG_START = 0x57a7d200, + IA_GOFO_FW_BOOT_STATE_QUEUE_INIT_DONE = 0x57a7d300, + IA_GOFO_FW_BOOT_STATE_READY = 0x57a7e100, + IA_GOFO_FW_BOOT_STATE_CRIT_UNSPECIFIED = 0xdead0001, + IA_GOFO_FW_BOOT_STATE_CRIT_CFG_PTR = 0xdead0101, + IA_GOFO_FW_BOOT_STATE_CRIT_CFG_VERSION = 0xdead0201, + IA_GOFO_FW_BOOT_STATE_CRIT_MSG_VERSION = 0xdead0301, IA_GOFO_FW_BOOT_STATE_CRIT_WDT_TIMEOUT = IA_GOFO_WDT_TIMEOUT_ERR, - IA_GOFO_FW_BOOT_STATE_WRONG_DATA_SECTION_UNPACKING = 0xdead0501U, - IA_GOFO_FW_BOOT_STATE_WRONG_RO_DATA_SECTION_UNPACKING = 0xdead0601U, - IA_GOFO_FW_BOOT_STATE_INVALID_UNTRUSTED_ADDR_MIN = 0xdead0701U, + IA_GOFO_FW_BOOT_STATE_WRONG_DATA_SECTION_UNPACKING = 0xdead0501, + IA_GOFO_FW_BOOT_STATE_WRONG_RO_DATA_SECTION_UNPACKING = 0xdead0601, + IA_GOFO_FW_BOOT_STATE_INVALID_UNTRUSTED_ADDR_MIN = 0xdead0701, IA_GOFO_FW_BOOT_STATE_CRIT_MEM_FATAL_DME = IA_GOFO_MEM_FATAL_DME_ERR, IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_LOCAL = IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR, @@ -146,18 +146,18 @@ enum ia_gofo_boot_state { IA_GOFO_DOUBLE_EXCEPTION_ERR, IA_GOFO_FW_BOOT_STATE_CRIT_BIST_DMEM_FAULT_DETECTION_ERR = IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR, - IA_GOFO_FW_BOOT_STATE_CRIT_DATA_INTEGRITY_FAILURE = 0xdead1010U, - IA_GOFO_FW_BOOT_STATE_CRIT_STACK_CHK_FAILURE = 0xdead1011U, + IA_GOFO_FW_BOOT_STATE_CRIT_DATA_INTEGRITY_FAILURE = 0xdead1010, + IA_GOFO_FW_BOOT_STATE_CRIT_STACK_CHK_FAILURE = 0xdead1011, IA_GOFO_FW_BOOT_STATE_CRIT_SYSCOM_CONTEXT_INTEGRITY_FAILURE = - 0xdead1012U, - IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013U, - IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014U, - IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015U, - IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001U, - IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200U, - IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300U, - IA_GOFO_FW_BOOT_HW_CMD_ACK_TIMEOUT = 0x57a7e400U, - IA_GOFO_FW_BOOT_SYSTEM_CYCLES_ERROR = 0x57a7e500U + 0xdead1012, + IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013, + IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014, + IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015, + IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001, + IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200, + IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300, + IA_GOFO_FW_BOOT_HW_CMD_ACK_TIMEOUT = 0x57a7e400, + IA_GOFO_FW_BOOT_SYSTEM_CYCLES_ERROR = 0x57a7e500 }; #endif diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h index 7bb6fac585a3..398a13350480 100644 --- a/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h @@ -60,7 +60,7 @@ struct ia_gofo_tlv_list { #define IA_GOFO_MSG_ERR_MAX_DETAILS (4U) #define IA_GOFO_MSG_ERR_OK (0U) -#define IA_GOFO_MSG_ERR_UNSPECIFED (0xffffffffU) +#define IA_GOFO_MSG_ERR_UNSPECIFED (0xffffffff) #define IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED (0U) #define IA_GOFO_MSG_ERR_IS_OK(err) (IA_GOFO_MSG_ERR_OK == (err).err_code) @@ -145,7 +145,7 @@ struct ia_gofo_msg_indirect { #define IA_GOFO_MSG_LOG_DOC_FMT_ID_MIN (0U) #define IA_GOFO_MSG_LOG_DOC_FMT_ID_MAX (4095U) -#define IA_GOFO_MSG_LOG_FMT_ID_INVALID (0xfffffffU) +#define IA_GOFO_MSG_LOG_FMT_ID_INVALID (0xfffffff) struct ia_gofo_msg_log_info { u16 log_counter; diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h index 8a78dd0936df..311248385993 100644 --- a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h @@ -69,11 +69,11 @@ struct ipu7_msg_cb_profile { #define IPU_MSG_NODE_MAX_PROFILES (2U) #define IPU_MSG_NODE_DEF_PROFILE_IDX (0U) -#define IPU_MSG_NODE_RSRC_ID_EXT_IP (0xffU) +#define IPU_MSG_NODE_RSRC_ID_EXT_IP (0xff) -#define IPU_MSG_NODE_DONT_CARE_TEB_HI (0xffffffffU) -#define IPU_MSG_NODE_DONT_CARE_TEB_LO (0xffffffffU) -#define IPU_MSG_NODE_RSRC_ID_IS (0xfeU) +#define IPU_MSG_NODE_DONT_CARE_TEB_HI (0xffffffff) +#define IPU_MSG_NODE_DONT_CARE_TEB_LO (0xffffffff) +#define IPU_MSG_NODE_RSRC_ID_IS (0xfe) struct ipu7_msg_node { struct ia_gofo_tlv_header tlv_header; @@ -160,7 +160,7 @@ struct ipu7_msg_link_ep_pair { #define IPU_MSG_LINK_FOREIGN_KEY_MAX (64U) #define IPU_MSG_LINK_PBK_ID_DONT_CARE (255U) #define IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE (255U) -#define IPU_MSG_LINK_TERM_ID_DONT_CARE (0xffU) +#define IPU_MSG_LINK_TERM_ID_DONT_CARE (0xff) struct ipu7_msg_link { struct ia_gofo_tlv_header tlv_header; @@ -333,7 +333,7 @@ enum ipu7_msg_err_device { #pragma pack(pop) #pragma pack(push, 1) -#define IPU_MSG_GRAPH_ID_UNKNOWN (0xffU) +#define IPU_MSG_GRAPH_ID_UNKNOWN (0xff) #define IPU_MSG_GRAPH_SEND_MSG_ENABLED 1U #define IPU_MSG_GRAPH_SEND_MSG_DISABLED 0U diff --git a/drivers/staging/media/ipu7/ipu7-buttress-regs.h b/drivers/staging/media/ipu7/ipu7-buttress-regs.h index 3eafd6a3813d..7b646aa538cf 100644 --- a/drivers/staging/media/ipu7/ipu7-buttress-regs.h +++ b/drivers/staging/media/ipu7/ipu7-buttress-regs.h @@ -287,7 +287,7 @@ #define BUTTRESS_TSC_CMD_START_TSC_SYNC BIT(0) #define BUTTRESS_PWR_STATUS_HH_STATUS_SHIFT 11 -#define BUTTRESS_PWR_STATUS_HH_STATUS_MASK (0x3U << 11) +#define BUTTRESS_PWR_STATUS_HH_STATUS_MASK (0x3 << 11) #define BUTTRESS_TSW_WA_SOFT_RESET BIT(8) /* new for PTL */ #define BUTTRESS_SEL_PB_TIMESTAMP BIT(9) @@ -326,8 +326,8 @@ #define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK 0xffff /* IS/PS freq control */ -#define BUTTRESS_IS_FREQ_CTL_RATIO_MASK 0xffU -#define BUTTRESS_PS_FREQ_CTL_RATIO_MASK 0xffU +#define BUTTRESS_IS_FREQ_CTL_RATIO_MASK 0xff +#define BUTTRESS_PS_FREQ_CTL_RATIO_MASK 0xff #define IPU7_IS_FREQ_MAX 450 #define IPU7_IS_FREQ_MIN 50 @@ -350,11 +350,11 @@ /* buttree power status */ #define IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 0 #define IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK \ - (0x3U << IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT) + (0x3 << IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT) #define IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 4 #define IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK \ - (0x3U << IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT) + (0x3 << IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT) #define IPU_BUTTRESS_PWR_STATE_DN_DONE 0x0 #define IPU_BUTTRESS_PWR_STATE_UP_PROCESS 0x1 From 477620dccf3e9481ed6ae67cb5c747f25751c531 Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Wed, 1 Apr 2026 18:25:47 +0200 Subject: [PATCH 037/394] media: intel/ipu6: Improve DWC PHY HSFREQRANGE band selection for overlapping ranges The get_hsfreq_by_mbps() function searches the freqranges[] table backward (from highest to lowest index). Because adjacent frequency bands overlap, a data rate that falls in the overlap region always lands on the higher-indexed band. For data rates up to 1500 Mbps (index 42) every band uses osc_freq_target 335. Starting at index 43 (1461-1640 Mbps) the osc_freq_target drops to 208. A sensor running at 1498 Mbps sits in the overlap between index 42 (1414-1588, osc 335) and index 43 (1461-1640, osc 208). The backward search picks index 43, programming the lower osc_freq_target of 208 instead of the optimal 335. This causes DDL lock instability and CSI-2 CRC errors on affected configurations, such as the OmniVision OV08X40 sensor on Intel Arrow Lake platforms (Dell Pro Max 16). Rewrite get_hsfreq_by_mbps() to select the optimal band: 1. Among bands whose min/max range covers the data rate, prefer the one with the higher osc_freq_target. 2. If osc_freq_target is equal, prefer the band whose default_mbps is closest to the requested rate. Since the frequency ranges are monotonically increasing, the loop exits early once min exceeds the requested rate. For 1498 Mbps this now correctly selects index 42 (osc_freq_target 335, range 1414-1588) instead of index 43 (osc_freq_target 208, range 1461-1640). Fixes: 1e7eeb301696 ("media: intel/ipu6: add the CSI2 DPHY implementation") Cc: stable@vger.kernel.org Signed-off-by: Marco Nenciarini Signed-off-by: Sakari Ailus --- .../media/pci/intel/ipu6/ipu6-isys-dwc-phy.c | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c index db2874843453..237906cb131a 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c @@ -288,15 +288,27 @@ static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = { static u16 get_hsfreq_by_mbps(u32 mbps) { - unsigned int i = DPHY_FREQ_RANGE_NUM; + u16 best = DPHY_FREQ_RANGE_INVALID_INDEX; + unsigned int i; - while (i--) { - if (freqranges[i].default_mbps == mbps || - (mbps >= freqranges[i].min && mbps <= freqranges[i].max)) - return i; + for (i = 0; i < DPHY_FREQ_RANGE_NUM; i++) { + if (mbps > freqranges[i].max) + continue; + + if (mbps < freqranges[i].min) + break; + + if (best == DPHY_FREQ_RANGE_INVALID_INDEX || + freqranges[i].osc_freq_target > + freqranges[best].osc_freq_target || + (freqranges[i].osc_freq_target == + freqranges[best].osc_freq_target && + abs((int)mbps - (int)freqranges[i].default_mbps) < + abs((int)mbps - (int)freqranges[best].default_mbps))) + best = i; } - return DPHY_FREQ_RANGE_INVALID_INDEX; + return best; } static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys, From 658810422076140f9a003a174f73b0eeaffa0611 Mon Sep 17 00:00:00 2001 From: Michael Riesch Date: Fri, 27 Mar 2026 00:10:00 +0100 Subject: [PATCH 038/394] media: dt-bindings: rockchip,rk3568-mipi-csi2: add rk3588 compatible The RK3588 MIPI CSI-2 receivers are compatible to the ones found in the RK3568. Introduce a list of compatible variants and add the RK3588 variant to it. Acked-by: Rob Herring (Arm) Signed-off-by: Michael Riesch Signed-off-by: Sakari Ailus --- .../bindings/media/rockchip,rk3568-mipi-csi2.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml index 4ac4a3b6f406..fbcf28e9e1da 100644 --- a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml +++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml @@ -16,9 +16,14 @@ description: properties: compatible: - enum: - - fsl,imx93-mipi-csi2 - - rockchip,rk3568-mipi-csi2 + oneOf: + - enum: + - fsl,imx93-mipi-csi2 + - rockchip,rk3568-mipi-csi2 + - items: + - enum: + - rockchip,rk3588-mipi-csi2 + - const: rockchip,rk3568-mipi-csi2 reg: maxItems: 1 From 7d8bf3d8f91073f4db347ed3aa6302b56107499c Mon Sep 17 00:00:00 2001 From: Ruslan Valiyev Date: Tue, 17 Mar 2026 17:05:44 +0000 Subject: [PATCH 039/394] media: vidtv: fix NULL pointer dereference in vidtv_mux_push_si syzbot reported a general protection fault in vidtv_psi_ts_psi_write_into [1]. vidtv_mux_get_pid_ctx() can return NULL, but vidtv_mux_push_si() does not check for this before dereferencing the returned pointer to access the continuity counter. This leads to a general protection fault when accessing a near-NULL address. The root cause is that vidtv_mux_pid_ctx_init() does not check the return value of vidtv_mux_create_pid_ctx_once() for PMT section PIDs. If the allocation fails, the PID context is never created, but init returns success. The subsequent vidtv_mux_push_si() call then gets NULL from vidtv_mux_get_pid_ctx() and crashes. Fix both the root cause (add error check in vidtv_mux_pid_ctx_init for PMT PIDs) and add defensive NULL checks in vidtv_mux_push_si for all vidtv_mux_get_pid_ctx() calls. [1] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN PTI KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] Workqueue: events vidtv_mux_tick RIP: 0010:vidtv_psi_ts_psi_write_into+0x54a/0xbc0 drivers/media/test-drivers/vidtv/vidtv_psi.c:197 Call Trace: vidtv_psi_table_header_write_into drivers/media/test-drivers/vidtv/vidtv_psi.c:799 [inline] vidtv_psi_pmt_write_into+0x3b2/0xa70 drivers/media/test-drivers/vidtv/vidtv_psi.c:1231 vidtv_mux_push_si+0x932/0xe80 drivers/media/test-drivers/vidtv/vidtv_mux.c:196 vidtv_mux_tick+0xe9b/0x1480 drivers/media/test-drivers/vidtv/vidtv_mux.c:408 Fixes: f90cf6079bf67 ("media: vidtv: add a bridge driver") Cc: stable@vger.kernel.org Reported-by: syzbot+814c351d094f4f1a1b86@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=814c351d094f4f1a1b86 Signed-off-by: Ruslan Valiyev Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vidtv/vidtv_mux.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/test-drivers/vidtv/vidtv_mux.c b/drivers/media/test-drivers/vidtv/vidtv_mux.c index f0134e38a1fb..ea2ea53102f9 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_mux.c +++ b/drivers/media/test-drivers/vidtv/vidtv_mux.c @@ -101,7 +101,8 @@ static int vidtv_mux_pid_ctx_init(struct vidtv_mux *m) /* add a ctx for all PMT sections */ while (p) { pid = vidtv_psi_get_pat_program_pid(p); - vidtv_mux_create_pid_ctx_once(m, pid); + if (!vidtv_mux_create_pid_ctx_once(m, pid)) + goto free; p = p->next; } @@ -170,6 +171,9 @@ static u32 vidtv_mux_push_si(struct vidtv_mux *m) nit_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_NIT_PID); eit_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_EIT_PID); + if (!pat_ctx || !sdt_ctx || !nit_ctx || !eit_ctx) + return 0; + pat_args.offset = m->mux_buf_offset; pat_args.continuity_counter = &pat_ctx->cc; @@ -186,6 +190,8 @@ static u32 vidtv_mux_push_si(struct vidtv_mux *m) } pmt_ctx = vidtv_mux_get_pid_ctx(m, pmt_pid); + if (!pmt_ctx) + continue; pmt_args.offset = m->mux_buf_offset; pmt_args.pmt = m->si.pmt_secs[i]; From 34ca1065a4b2cf776c28d7cece6f6c9d90f7e3a3 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Wed, 18 Mar 2026 01:21:53 +0800 Subject: [PATCH 040/394] media: ti: vpe: Fix fwnode_handle leak in vip_probe_complete() In vip_probe_complete(), the fwnode_handle reference is not released if the loop continues via the default switch case or if alloc_port() fails. This results in a reference count leak. Switch to using the __free(fwnode_handle) cleanup attribute to ensure the reference is automatically released when the handle goes out of scope. Fixes: fc2873aa4a21 ("media: ti: vpe: Add the VIP driver") Cc: stable@vger.kernel.org Signed-off-by: Felix Gu Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/vpe/vip.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c index 0e91e87bda9b..a3c5e966f64c 100644 --- a/drivers/media/platform/ti/vpe/vip.c +++ b/drivers/media/platform/ti/vpe/vip.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -3389,7 +3390,6 @@ static int vip_probe_complete(struct platform_device *pdev) struct vip_port *port; struct vip_dev *dev; struct device_node *parent = pdev->dev.of_node; - struct fwnode_handle *ep = NULL; unsigned int syscon_args[5]; int ret, i, slice_id, port_id, p; @@ -3411,8 +3411,9 @@ static int vip_probe_complete(struct platform_device *pdev) ctrl->syscon_bit_field[i] = syscon_args[i + 1]; for (p = 0; p < (VIP_NUM_PORTS * VIP_NUM_SLICES); p++) { - ep = fwnode_graph_get_next_endpoint_by_regs(of_fwnode_handle(parent), - p, 0); + struct fwnode_handle *ep __free(fwnode_handle) = + fwnode_graph_get_next_endpoint_by_regs( + of_fwnode_handle(parent), p, 0); if (!ep) continue; @@ -3447,7 +3448,6 @@ static int vip_probe_complete(struct platform_device *pdev) port = dev->ports[port_id]; vip_register_subdev_notify(port, ep); - fwnode_handle_put(ep); } return 0; } From 5e78a431a3f74c632351791d7902c60199d1ce83 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Wed, 18 Mar 2026 01:21:54 +0800 Subject: [PATCH 041/394] media: ti: vpe: Fix the error code of devm_request_irq() Return the actual error code from devm_request_irq() instead of incorrectly returning -ENOMEM. Fixes: fc2873aa4a21 ("media: ti: vpe: Add the VIP driver") Cc: stable@vger.kernel.org Signed-off-by: Felix Gu Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/vpe/vip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c index a3c5e966f64c..bd34c982b427 100644 --- a/drivers/media/platform/ti/vpe/vip.c +++ b/drivers/media/platform/ti/vpe/vip.c @@ -3472,7 +3472,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice) ret = devm_request_irq(&pdev->dev, dev->irq, vip_irq, 0, VIP_MODULE_NAME, dev); if (ret < 0) - return -ENOMEM; + return ret; spin_lock_init(&dev->slock); mutex_init(&dev->mutex); From e8f319eae96a3d718e810d52432020a2b77f5f60 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Wed, 18 Mar 2026 01:21:55 +0800 Subject: [PATCH 042/394] media: ti: vpe: Fix the error code of devm_kzalloc() in vip_probe_slice() In vip_probe_slice(), the error check for devm_kzalloc() incorrectly uses PTR_ERR_OR_ZERO() which returns 0 for NULL pointer. Return -ENOMEM for devm_kzalloc() failure. Fixes: fc2873aa4a21 ("media: ti: vpe: Add the VIP driver") Cc: stable@vger.kernel.org Signed-off-by: Felix Gu Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/vpe/vip.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c index bd34c982b427..cb0a5a07a3d4 100644 --- a/drivers/media/platform/ti/vpe/vip.c +++ b/drivers/media/platform/ti/vpe/vip.c @@ -3490,7 +3490,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice) parser = devm_kzalloc(&pdev->dev, sizeof(*dev->parser), GFP_KERNEL); if (!parser) - return PTR_ERR_OR_ZERO(parser); + return -ENOMEM; parser->base = dev->base + (slice ? VIP_SLICE1_PARSER : VIP_SLICE0_PARSER); if (IS_ERR(parser->base)) @@ -3502,7 +3502,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice) dev->sc_assigned = VIP_NOT_ASSIGNED; sc = devm_kzalloc(&pdev->dev, sizeof(*dev->sc), GFP_KERNEL); if (!sc) - return PTR_ERR_OR_ZERO(sc); + return -ENOMEM; sc->base = dev->base + (slice ? VIP_SLICE1_SC : VIP_SLICE0_SC); if (IS_ERR(sc->base)) @@ -3514,7 +3514,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice) dev->csc_assigned = VIP_NOT_ASSIGNED; csc = devm_kzalloc(&pdev->dev, sizeof(*dev->csc), GFP_KERNEL); if (!csc) - return PTR_ERR_OR_ZERO(csc); + return -ENOMEM; csc->base = dev->base + (slice ? VIP_SLICE1_CSC : VIP_SLICE0_CSC); if (IS_ERR(csc->base)) From 165a7c73123eabd0f4f496601c811a23930d5671 Mon Sep 17 00:00:00 2001 From: Tomasz Unger Date: Fri, 20 Mar 2026 08:31:54 +0100 Subject: [PATCH 043/394] staging: media: av7110: remove dead code in av7110.c Remove commented-out line of dead code that serves no purpose. Signed-off-by: Tomasz Unger Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 014d0c6f0a8b..094207093b62 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -762,7 +762,6 @@ static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) u16 buf[20]; int ret, i; u16 handle; -// u16 mode = 0x0320; u16 mode = 0xb96a; dprintk(4, "%p\n", av7110); From b3c33615e56b830397965c20d4994b274edcd9df Mon Sep 17 00:00:00 2001 From: Tomasz Unger Date: Fri, 20 Mar 2026 11:07:37 +0100 Subject: [PATCH 044/394] staging: media: av7110: remove print_time() dead code The DEBUG_TIMING macro is commented out and can never be defined, making the print_time() function body always empty. Remove the commented-out macro, the unused function definition and all its call sites as they serve no purpose. Signed-off-by: Tomasz Unger Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 094207093b62..edf1ef937354 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -314,17 +314,6 @@ static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, } } -//#define DEBUG_TIMING -static inline void print_time(char *s) -{ -#ifdef DEBUG_TIMING - struct timespec64 ts; - - ktime_get_real_ts64(&ts); - pr_info("%s(): %ptSp\n", s, &ts); -#endif -} - #define DEBI_READ 0 #define DEBI_WRITE 1 static inline void start_debi_dma(struct av7110 *av7110, int dir, @@ -353,7 +342,6 @@ static void debiirq(struct tasklet_struct *t) int handle = (type >> 8) & 0x1f; unsigned int xfer = 0; - print_time("debi"); dprintk(4, "type 0x%04x\n", type); if (type == -1) { @@ -473,7 +461,6 @@ static void gpioirq(struct tasklet_struct *t) txbuf = irdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); len = (av7110->debilen + 3) & ~3; - print_time("gpio"); dprintk(8, "GPIO0 irq 0x%04x %d\n", av7110->debitype, av7110->debilen); switch (av7110->debitype & 0xff) { @@ -2784,8 +2771,6 @@ static void av7110_irq(struct saa7146_dev *dev, u32 *isr) { struct av7110 *av7110 = dev->ext_priv; - //print_time("av7110_irq"); - /* Note: Don't try to handle the DEBI error irq (MASK_18), in * intel mode the timeout is asserted all the time... */ From d1162a5adbb5e95953d460b5bde3a04cd4473fe9 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 25 Mar 2026 13:57:42 +0300 Subject: [PATCH 045/394] media: synopsys: hdmirx: Fix HPD lane hold time Increase time of holding HPD lane low by 50ms. This fixes EDID change not detected by source/display side. Fixes: 7b59b132ad43 ("media: platform: synopsys: Add support for HDMI input driver") Cc: stable@vger.kernel.org Reported-by: Ross Cawston Closes: https://lore.kernel.org/linux-rockchip/20260209061654.54757-1-ross@r-sc.ca/ Signed-off-by: Dmitry Osipenko Signed-off-by: Hans Verkuil --- drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c index 61ad20b18b8d..4c8957505a50 100644 --- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c @@ -506,9 +506,9 @@ static void hdmirx_hpd_ctrl(struct snps_hdmirx_dev *hdmirx_dev, bool en) hdmirx_writel(hdmirx_dev, CORE_CONFIG, hdmirx_dev->hpd_trigger_level_high ? en : !en); - /* 100ms delay as per HDMI spec */ + /* 100ms delay as per HDMI spec + extra 50ms to cover internal delay */ if (!en) - msleep(100); + msleep(100 + 50); } static void hdmirx_write_edid_data(struct snps_hdmirx_dev *hdmirx_dev, From 253c8ef7d57da0c74db251f385324faaa5ae2257 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 11:23:30 +0000 Subject: [PATCH 046/394] media: aspeed: fix missing of_reserved_mem_device_release() on probe failure aspeed_video_init() calls of_reserved_mem_device_init() to associate reserved memory regions with the device. When aspeed_video_setup_video() subsequently fails in aspeed_video_probe(), the error path frees the JPEG buffer and unprepares the clocks but does not release the reserved memory association, leaking the rmem_assigned_device entry on the global list. The normal remove path already calls of_reserved_mem_device_release() correctly; only the probe error path was missing it. Add the missing of_reserved_mem_device_release() call to the aspeed_video_setup_video() failure cleanup. Fixes: d2b4387f3bdf ("media: platform: Add Aspeed Video Engine driver") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Signed-off-by: Hans Verkuil --- drivers/media/platform/aspeed/aspeed-video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c index 41cb96f60110..a292275f6b7b 100644 --- a/drivers/media/platform/aspeed/aspeed-video.c +++ b/drivers/media/platform/aspeed/aspeed-video.c @@ -2343,6 +2343,7 @@ static int aspeed_video_probe(struct platform_device *pdev) rc = aspeed_video_setup_video(video); if (rc) { aspeed_video_free_buf(video, &video->jpeg); + of_reserved_mem_device_release(&pdev->dev); clk_unprepare(video->vclk); clk_unprepare(video->eclk); return rc; From 54e8f9888547eb916cabcc0bc0bc39730324c9ee Mon Sep 17 00:00:00 2001 From: Chethan C Date: Sat, 28 Mar 2026 22:20:34 +0530 Subject: [PATCH 047/394] staging: media: av7110: fix coding style Fix indentation and alignment issues reported by checkpatch.pl. Rename enums av7110_rec_play_state, av7110_type_rec_play_format, and av7110_encoder_command to follow kernel naming style. Rename wssData and wssMode to wss_data and wss_mode to avoid camelCase identifiers. Signed-off-by: Chethan C Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 31 +++++--- drivers/staging/media/av7110/av7110.h | 4 +- drivers/staging/media/av7110/av7110_av.c | 89 ++++++++++++++++------- drivers/staging/media/av7110/av7110_ca.c | 3 +- drivers/staging/media/av7110/av7110_hw.h | 71 +++++++++--------- drivers/staging/media/av7110/av7110_ir.c | 4 +- drivers/staging/media/av7110/av7110_v4l.c | 22 +++--- 7 files changed, 134 insertions(+), 90 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index edf1ef937354..656bffd8c3d6 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -121,19 +121,22 @@ static void init_av7110_av(struct av7110 *av7110) if (ret < 0) pr_err("cannot set internal volume to maximum:%d\n", ret); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, - 1, (u16)av7110->display_ar); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_MONITOR_TYPE, 1, av7110->display_ar); if (ret < 0) pr_err("unable to set aspect ratio\n"); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, - 1, av7110->display_panscan); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_PANSCAN_TYPE, 1, + av7110->display_panscan); if (ret < 0) pr_err("unable to set pan scan\n"); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 2, wss_cfg_4_3); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_WSS_CONFIG, 2, 2, wss_cfg_4_3); if (ret < 0) pr_err("unable to configure 4:3 wss\n"); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 3, wss_cfg_16_9); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_WSS_CONFIG, 2, 3, wss_cfg_16_9); if (ret < 0) pr_err("unable to configure 16:9 wss\n"); @@ -704,7 +707,8 @@ static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, if (av7110->audiostate.bypass_mode) aflags |= 0x8000; - return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, MultiPID, 6, + return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_MULTI_PID, 6, pcrpid, vpid, apid, ttpid, subpid, aflags); } @@ -771,7 +775,7 @@ static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) av7110_p2t_init(&av7110->p2t_filter[dvbdmxfilter->index], dvbdmxfeed); } - buf[0] = (COMTYPE_PID_FILTER << 8) + AddPIDFilter; + buf[0] = (COMTYPE_PID_FILTER << 8) + AV7110_ADD_PID_FILTER; buf[1] = 16; buf[2] = dvbdmxfeed->pid; buf[3] = mode; @@ -814,7 +818,7 @@ static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) av7110->handle2filter[handle] = NULL; - buf[0] = (COMTYPE_PID_FILTER << 8) + DelPIDFilter; + buf[0] = (COMTYPE_PID_FILTER << 8) + AV7110_DEL_PID_FILTER; buf[1] = 1; buf[2] = handle; ret = av7110_fw_request(av7110, buf, 3, answ, 2); @@ -859,7 +863,8 @@ static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) if (dvbdmxfeed->pes_type < 2 && npids[0]) if (av7110->fe_synced) { - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SCAN, 0); if (ret) return ret; } @@ -1897,11 +1902,13 @@ static int av7110_fe_lock_fix(struct av7110 *av7110, enum fe_status status) av7110->pids[DMX_PES_TELETEXT], 0, av7110->pids[DMX_PES_PCR]); if (!ret) - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SCAN, 0); } else { ret = SetPIDs(av7110, 0, 0, 0, 0, 0); if (!ret) { - ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, FlushTSQueue, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, + AV7110_FLUSH_TS_QUEUE, 0); if (!ret) ret = av7110_wait_msgstate(av7110, GPMQBusy); } diff --git a/drivers/staging/media/av7110/av7110.h b/drivers/staging/media/av7110/av7110.h index b584754f4be0..797dda437cbe 100644 --- a/drivers/staging/media/av7110/av7110.h +++ b/drivers/staging/media/av7110/av7110.h @@ -243,8 +243,8 @@ struct av7110 { struct dvb_video_events video_events; video_size_t video_size; - u16 wssMode; - u16 wssData; + u16 wss_mode; + u16 wss_data; struct infrared ir; diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c index 2993ac43c49c..22f1335d371b 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/av7110/av7110_av.c @@ -111,7 +111,7 @@ int av7110_av_start_record(struct av7110 *av7110, int av, if (av7110->playing || (av7110->rec_mode & av)) return -EBUSY; - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, AV7110_REC_PLAY_STOP, 0); dvbdmx->recording = 1; av7110->rec_mode |= av; @@ -121,7 +121,9 @@ int av7110_av_start_record(struct av7110 *av7110, int av, dvbdmx->pesfilter[0]->pid, dvb_filter_pes2ts_cb, (void *)dvbdmx->pesfilter[0]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_AUDIO_PES, 0); break; case RP_VIDEO: @@ -129,7 +131,9 @@ int av7110_av_start_record(struct av7110 *av7110, int av, dvbdmx->pesfilter[1]->pid, dvb_filter_pes2ts_cb, (void *)dvbdmx->pesfilter[1]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_VIDEO_PES, 0); break; case RP_AV: @@ -141,7 +145,9 @@ int av7110_av_start_record(struct av7110 *av7110, int av, dvbdmx->pesfilter[1]->pid, dvb_filter_pes2ts_cb, (void *)dvbdmx->pesfilter[1]); - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AV_PES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_AV_PES, 0); break; } return ret; @@ -158,7 +164,7 @@ int av7110_av_start_play(struct av7110 *av7110, int av) if (av7110->playing & av) return -EBUSY; - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, AV7110_REC_PLAY_STOP, 0); if (av7110->playing == RP_NONE) { av7110_ipack_reset(&av7110->ipack[0]); @@ -168,15 +174,21 @@ int av7110_av_start_play(struct av7110 *av7110, int av) av7110->playing |= av; switch (av7110->playing) { case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_AUDIO_PES, 0); break; case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_VIDEO_PES, 0); av7110->sinfo = 0; break; case RP_AV: av7110->sinfo = 0; - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AV_PES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_AV_PES, 0); break; } return ret; @@ -190,15 +202,19 @@ int av7110_av_stop(struct av7110 *av7110, int av) if (!(av7110->playing & av) && !(av7110->rec_mode & av)) return 0; - av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, AV7110_REC_PLAY_STOP, 0); if (av7110->playing) { av7110->playing &= ~av; switch (av7110->playing) { case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_AUDIO_PES, 0); break; case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PLAY, 2, + AV7110_VIDEO_PES, 0); break; case RP_NONE: ret = av7110_set_vidmode(av7110, av7110->vidmode); @@ -208,10 +224,14 @@ int av7110_av_stop(struct av7110 *av7110, int av) av7110->rec_mode &= ~av; switch (av7110->rec_mode) { case RP_AUDIO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_AUDIO_PES, 0); break; case RP_VIDEO: - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_RECORD, 2, + AV7110_VIDEO_PES, 0); break; case RP_NONE: break; @@ -325,7 +345,8 @@ int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) dprintk(2, "av7110:%p\n", av7110); - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_LOAD_VID_CODE, 1, mode); if (!ret && !av7110->playing) { ret = ChangePIDs(av7110, av7110->pids[DMX_PES_VIDEO], @@ -333,7 +354,8 @@ int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) av7110->pids[DMX_PES_TELETEXT], 0, av7110->pids[DMX_PES_PCR]); if (!ret) - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SCAN, 0); } return ret; } @@ -1168,7 +1190,8 @@ static int dvb_video_ioctl(struct file *file, } if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) { if (av7110->playing == RP_AV) { - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_STOP, 0); if (ret) break; av7110->playing &= ~RP_VIDEO; @@ -1184,7 +1207,8 @@ static int dvb_video_ioctl(struct file *file, case VIDEO_FREEZE: av7110->videostate.play_state = VIDEO_FREEZED; if (av7110->playing & RP_VIDEO) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_PAUSE, 0); else ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); if (!ret) @@ -1193,7 +1217,8 @@ static int dvb_video_ioctl(struct file *file, case VIDEO_CONTINUE: if (av7110->playing & RP_VIDEO) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_CONTINUE, 0); if (!ret) ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); if (!ret) { @@ -1248,8 +1273,9 @@ static int dvb_video_ioctl(struct file *file, if (ret < 0) break; av7110->videostate.display_format = format; - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, - 1, av7110->display_panscan); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_PANSCAN_TYPE, 1, + av7110->display_panscan); break; } @@ -1259,8 +1285,8 @@ static int dvb_video_ioctl(struct file *file, break; } av7110->display_ar = arg; - ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, - 1, (u16)arg); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_MONITOR_TYPE, 1, arg); break; #ifdef CONFIG_COMPAT @@ -1291,7 +1317,8 @@ static int dvb_video_ioctl(struct file *file, //note: arg is ignored by firmware if (av7110->playing & RP_VIDEO) ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Scan_I, 2, AV_PES, 0); + AV7110_REC_PLAY_SCAN_I, 2, + AV7110_AV_PES, 0); else ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg); if (!ret) { @@ -1303,7 +1330,9 @@ static int dvb_video_ioctl(struct file *file, case VIDEO_SLOWMOTION: if (av7110->playing & RP_VIDEO) { if (av7110->trickmode != TRICK_SLOW) - ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); + ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, + AV7110_REC_PLAY_SLOW, 2, + 0, 0); if (!ret) ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); } else { @@ -1329,15 +1358,18 @@ static int dvb_video_ioctl(struct file *file, av7110_ipack_reset(&av7110->ipack[1]); if (av7110->playing == RP_AV) { ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Play, 2, AV_PES, 0); + AV7110_REC_PLAY_PLAY, 2, + AV7110_AV_PES, 0); if (ret) break; if (av7110->trickmode == TRICK_FAST) ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Scan_I, 2, AV_PES, 0); + AV7110_REC_PLAY_SCAN_I, 2, + AV7110_AV_PES, 0); if (av7110->trickmode == TRICK_SLOW) { ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Slow, 2, 0, 0); + AV7110_REC_PLAY_SLOW, 2, + 0, 0); if (!ret) ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); } @@ -1483,7 +1515,8 @@ static int dvb_audio_ioctl(struct file *file, av7110_ipack_reset(&av7110->ipack[0]); if (av7110->playing == RP_AV) ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, - __Play, 2, AV_PES, 0); + AV7110_REC_PLAY_PLAY, 2, + AV7110_AV_PES, 0); break; case AUDIO_SET_ID: diff --git a/drivers/staging/media/av7110/av7110_ca.c b/drivers/staging/media/av7110/av7110_ca.c index 63d9c97a5190..24876c8fdc8c 100644 --- a/drivers/staging/media/av7110/av7110_ca.c +++ b/drivers/staging/media/av7110/av7110_ca.c @@ -307,7 +307,8 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) mutex_unlock(&av7110->ioctl_mutex); return -EINVAL; } - av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, + av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SET_DESCR, 5, (descr->index << 8) | descr->parity, (descr->cw[0] << 8) | descr->cw[1], (descr->cw[2] << 8) | descr->cw[3], diff --git a/drivers/staging/media/av7110/av7110_hw.h b/drivers/staging/media/av7110/av7110_hw.h index d4579f411c56..3afe474da871 100644 --- a/drivers/staging/media/av7110/av7110_hw.h +++ b/drivers/staging/media/av7110/av7110_hw.h @@ -21,12 +21,12 @@ enum av7110_bootstate { }; enum av7110_type_rec_play_format { - RP_None, - AudioPES, - AudioMp2, - AudioPCM, - VideoPES, - AV_PES + AV7110_RP_NONE, + AV7110_AUDIO_PES, + AV7110_AUDIO_MP2, + AV7110_AUDIO_PCM, + AV7110_VIDEO_PES, + AV7110_AV_PES, }; enum av7110_osd_palette_type { @@ -112,19 +112,19 @@ enum av7110_osd_command { }; enum av7110_pid_command { - MultiPID, - VideoPID, - AudioPID, - InitFilt, - FiltError, - NewVersion, - CacheError, - AddPIDFilter, - DelPIDFilter, - Scan, - SetDescr, - SetIR, - FlushTSQueue + AV7110_MULTI_PID, + AV7110_VIDEO_PID, + AV7110_AUDIO_PID, + AV7110_INIT_FILT, + AV7110_FILT_ERROR, + AV7110_NEW_VERSION, + AV7110_CACHE_ERROR, + AV7110_ADD_PID_FILTER, + AV7110_DEL_PID_FILTER, + AV7110_SCAN, + AV7110_SET_DESCR, + AV7110_SET_IR, + AV7110_FLUSH_TS_QUEUE, }; enum av7110_mpeg_command { @@ -158,24 +158,24 @@ enum av7110_request_command { }; enum av7110_encoder_command { - SetVidMode, - SetTestMode, - LoadVidCode, - SetMonitorType, - SetPanScanType, - SetFreezeMode, - SetWSSConfig + AV7110_SET_VID_MODE, + AV7110_SET_TEST_MODE, + AV7110_LOAD_VID_CODE, + AV7110_SET_MONITOR_TYPE, + AV7110_SET_PANSCAN_TYPE, + AV7110_SET_FREEZE_MODE, + AV7110_SET_WSS_CONFIG, }; enum av7110_rec_play_state { - __Record, - __Stop, - __Play, - __Pause, - __Slow, - __FF_IP, - __Scan_I, - __Continue + AV7110_REC_PLAY_RECORD, + AV7110_REC_PLAY_STOP, + AV7110_REC_PLAY_PLAY, + AV7110_REC_PLAY_PAUSE, + AV7110_REC_PLAY_SLOW, + AV7110_REC_PLAY_FF_IP, + AV7110_REC_PLAY_SCAN_I, + AV7110_REC_PLAY_CONTINUE, }; enum av7110_fw_cmd_misc { @@ -452,7 +452,8 @@ static inline int SendDAC(struct av7110 *av7110, u8 addr, u8 data) static inline int av7710_set_video_mode(struct av7110 *av7110, int mode) { - return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode); + return av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_VID_MODE, 1, mode); } static inline int vidcom(struct av7110 *av7110, u32 com, u32 arg) diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c index fdae467fd7ab..9b7bf1857868 100644 --- a/drivers/staging/media/av7110/av7110_ir.c +++ b/drivers/staging/media/av7110/av7110_ir.c @@ -71,8 +71,8 @@ int av7110_set_ir_config(struct av7110 *av7110) { dprintk(4, "ir config = %08x\n", av7110->ir.ir_config); - return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, - av7110->ir.ir_config); + return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, + AV7110_SET_IR, 1, av7110->ir.ir_config); } static int change_protocol(struct rc_dev *rcdev, u64 *rc_type) diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c index 200a7a29ea31..aed7581fa8a5 100644 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ b/drivers/staging/media/av7110/av7110_v4l.c @@ -556,7 +556,7 @@ static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, if (FW_VERSION(av7110->arm_app) < 0x2623) return -EINVAL; memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); - if (av7110->wssMode) { + if (av7110->wss_mode) { f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; } @@ -596,14 +596,14 @@ static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, return -EINVAL; if (f->fmt.sliced.service_set & V4L2_SLICED_WSS_625) { /* WSS controlled by userspace */ - av7110->wssMode = 1; - av7110->wssData = 0; + av7110->wss_mode = 1; + av7110->wss_data = 0; } else { /* WSS controlled by firmware */ - av7110->wssMode = 0; - av7110->wssData = 0; + av7110->wss_mode = 0; + av7110->wss_data = 0; return av7110_fw_cmd(av7110, COMTYPE_ENCODER, - SetWSSConfig, 1, 0); + AV7110_SET_WSS_CONFIG, 1, 0); } return 0; } @@ -616,17 +616,19 @@ static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size int rc; dprintk(2, "\n"); - if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof(d)) + if (FW_VERSION(av7110->arm_app) < 0x2623 || + !av7110->wss_mode || count != sizeof(d)) return -EINVAL; if (copy_from_user(&d, data, count)) return -EFAULT; if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23) return -EINVAL; if (d.id) - av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0]; + av7110->wss_data = ((d.data[1] << 8) & 0x3f00) | d.data[0]; else - av7110->wssData = 0x8000; - rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData); + av7110->wss_data = 0x8000; + rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, + AV7110_SET_WSS_CONFIG, 2, 1, av7110->wss_data); return (rc < 0) ? rc : count; } From 60ca00792bce46ec170c7ed101f376186d4cf8a9 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 18:17:49 +0000 Subject: [PATCH 048/394] media: nuvoton: npcm-video: fix error handling in npcm_video_init() npcm_video_init() has two error handling issues after of_reserved_mem_device_init() is called: When dma_set_mask_and_coherent() fails, the function releases the reserved memory but does not return, allowing execution to fall through into npcm_video_ece_init() with a failed DMA configuration. When npcm_video_ece_init() fails, the function returns an error without calling of_reserved_mem_device_release(), leaking the reserved memory association. Fix both by adding the missing return after the DMA mask failure and adding the missing of_reserved_mem_device_release() call on the ECE init error path. Fixes: 46c15a4ff1f4 ("media: nuvoton: Add driver for NPCM video capture and encoding engine") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Signed-off-by: Hans Verkuil --- drivers/media/platform/nuvoton/npcm-video.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c index b2a562e1ee1c..5c6bddfe8073 100644 --- a/drivers/media/platform/nuvoton/npcm-video.c +++ b/drivers/media/platform/nuvoton/npcm-video.c @@ -1720,10 +1720,12 @@ static int npcm_video_init(struct npcm_video *video) if (rc) { dev_err(dev, "Failed to set DMA mask\n"); of_reserved_mem_device_release(dev); + return rc; } rc = npcm_video_ece_init(video); if (rc) { + of_reserved_mem_device_release(dev); dev_err(dev, "Failed to initialize ECE\n"); return rc; } From 50cc0e547da50b887e63dfa1ad203cd5b735d01e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 18:18:09 +0000 Subject: [PATCH 049/394] media: nuvoton: npcm-video: fix memory leaks in probe and remove npcm_video_probe() allocates the npcm_video structure with kzalloc_obj() but never frees it on any probe error path or in npcm_video_remove(), leaking the allocation on every failed probe and every normal unbind. Additionally, when npcm_video_setup_video() fails, the reserved memory association established by of_reserved_mem_device_init() in npcm_video_init() is not released, leaking the rmem_assigned_device entry on the global list. Fix both by adding kfree(video) to all probe error paths and to npcm_video_remove(), and adding the missing of_reserved_mem_device_release() call when npcm_video_setup_video() fails. Fixes: 46c15a4ff1f4 ("media: nuvoton: Add driver for NPCM video capture and encoding engine") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Signed-off-by: Hans Verkuil --- drivers/media/platform/nuvoton/npcm-video.c | 32 +++++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c index 5c6bddfe8073..52505af35c08 100644 --- a/drivers/media/platform/nuvoton/npcm-video.c +++ b/drivers/media/platform/nuvoton/npcm-video.c @@ -1750,42 +1750,55 @@ static int npcm_video_probe(struct platform_device *pdev) regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) { dev_err(&pdev->dev, "Failed to parse VCD reg in DTS\n"); - return PTR_ERR(regs); + rc = PTR_ERR(regs); + goto err_free; } video->vcd_regmap = devm_regmap_init_mmio(&pdev->dev, regs, &npcm_video_regmap_cfg); if (IS_ERR(video->vcd_regmap)) { dev_err(&pdev->dev, "Failed to initialize VCD regmap\n"); - return PTR_ERR(video->vcd_regmap); + rc = PTR_ERR(video->vcd_regmap); + goto err_free; } video->reset = devm_reset_control_get(&pdev->dev, NULL); if (IS_ERR(video->reset)) { dev_err(&pdev->dev, "Failed to get VCD reset control in DTS\n"); - return PTR_ERR(video->reset); + rc = PTR_ERR(video->reset); + goto err_free; } video->gcr_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "nuvoton,sysgcr"); - if (IS_ERR(video->gcr_regmap)) - return PTR_ERR(video->gcr_regmap); + if (IS_ERR(video->gcr_regmap)) { + rc = PTR_ERR(video->gcr_regmap); + goto err_free; + } video->gfx_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "nuvoton,sysgfxi"); - if (IS_ERR(video->gfx_regmap)) - return PTR_ERR(video->gfx_regmap); + if (IS_ERR(video->gfx_regmap)) { + rc = PTR_ERR(video->gfx_regmap); + goto err_free; + } rc = npcm_video_init(video); if (rc) - return rc; + goto err_free; rc = npcm_video_setup_video(video); if (rc) - return rc; + goto err_release_mem; dev_info(video->dev, "NPCM video driver probed\n"); return 0; + +err_release_mem: + of_reserved_mem_device_release(&pdev->dev); +err_free: + kfree(video); + return rc; } static void npcm_video_remove(struct platform_device *pdev) @@ -1800,6 +1813,7 @@ static void npcm_video_remove(struct platform_device *pdev) v4l2_device_unregister(v4l2_dev); if (video->ece.enable) npcm_video_ece_stop(video); + kfree(video); of_reserved_mem_device_release(dev); } From 237b133817b99af32f5d698f1e45c3f029e41792 Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sun, 29 Mar 2026 19:39:42 +0100 Subject: [PATCH 050/394] media: dead code cleanup in kconfig for VIDEO_SOLO6X10 The same kconfig 'select FONT_8x16' appears twice for VIDEO_SOLO6X10. I propose removing the second instance, as it is effectively dead code. This dead code was found by kconfirm, a static analysis tool for Kconfig. Signed-off-by: Julian Braha Signed-off-by: Hans Verkuil --- drivers/media/pci/solo6x10/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/pci/solo6x10/Kconfig b/drivers/media/pci/solo6x10/Kconfig index adb247847e00..cc4a7b088370 100644 --- a/drivers/media/pci/solo6x10/Kconfig +++ b/drivers/media/pci/solo6x10/Kconfig @@ -8,7 +8,6 @@ config VIDEO_SOLO6X10 select VIDEOBUF2_DMA_SG select VIDEOBUF2_DMA_CONTIG select SND_PCM - select FONT_8x16 help This driver supports the Bluecherry H.264 and MPEG-4 hardware compression capture cards and other Softlogic-based ones. From 99feb05904bf6ee58077f179fc3aa7c19ed891e2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 12:11:36 +0200 Subject: [PATCH 051/394] media: si470x-usb: refactor endpoint lookup Use the common USB helper for looking up interrupt-in endpoints instead of open coding. Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil --- drivers/media/radio/si470x/radio-si470x-usb.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index 318b5f6d4202..869b1e7e34b9 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -565,8 +565,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, { struct si470x_device *radio; struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - int i, int_end_size, retval; + int int_end_size, retval; unsigned char version_warning = 0; /* private data allocation and initialization */ @@ -595,12 +594,8 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, iface_desc = intf->cur_altsetting; /* Set up interrupt endpoint information. */ - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_int_in(endpoint)) - radio->int_in_endpoint = endpoint; - } - if (!radio->int_in_endpoint) { + retval = usb_find_int_in_endpoint(iface_desc, &radio->int_in_endpoint); + if (retval) { dev_info(&intf->dev, "could not find interrupt in endpoint\n"); retval = -EIO; goto err_usbbuf; From 84d87d51940044da8ed8ae838874d7edc8e979d7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 12:11:39 +0200 Subject: [PATCH 052/394] media: gspca: refactor endpoint lookup Use the common USB helper for looking up interrupt-in endpoints instead of open coding. Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil --- drivers/media/usb/gspca/gspca.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index f3d3f441c851..594d73e50b9f 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -208,22 +208,17 @@ static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, static void gspca_input_create_urb(struct gspca_dev *gspca_dev) { struct usb_interface *intf; - struct usb_host_interface *intf_desc; struct usb_endpoint_descriptor *ep; - int i; + int ret; if (gspca_dev->sd_desc->int_pkt_scan) { intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); - intf_desc = intf->cur_altsetting; - for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { - ep = &intf_desc->endpoint[i].desc; - if (usb_endpoint_dir_in(ep) && - usb_endpoint_xfer_int(ep)) { - alloc_and_submit_int_urb(gspca_dev, ep); - break; - } - } + ret = usb_find_int_in_endpoint(intf->cur_altsetting, &ep); + if (ret) + return; + + alloc_and_submit_int_urb(gspca_dev, ep); } } From b8b6b2bf0ee0c9d175e80effac6698e82739b7dc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 12:11:40 +0200 Subject: [PATCH 053/394] media: hdpvr: refactor endpoint lookup Use the common USB helper for looking up bulk-in endpoints instead of open coding. Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil --- drivers/media/usb/hdpvr/hdpvr-core.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c index d42336836b18..849a2be416bd 100644 --- a/drivers/media/usb/hdpvr/hdpvr-core.c +++ b/drivers/media/usb/hdpvr/hdpvr-core.c @@ -265,13 +265,10 @@ static int hdpvr_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct hdpvr_device *dev; - struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; #if IS_ENABLED(CONFIG_I2C) struct i2c_client *client; #endif - size_t buffer_size; - int i; int dev_num; int retval = -ENOMEM; @@ -321,25 +318,18 @@ static int hdpvr_probe(struct usb_interface *interface, /* set up the endpoint information */ /* use only the first bulk-in and bulk-out endpoints */ - iface_desc = interface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - - if (!dev->bulk_in_endpointAddr && - usb_endpoint_is_bulk_in(endpoint)) { - /* USB interface description is buggy, reported max - * packet size is 512 bytes, windows driver uses 8192 */ - buffer_size = 8192; - dev->bulk_in_size = buffer_size; - dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; - } - - } - if (!dev->bulk_in_endpointAddr) { + if (usb_find_bulk_in_endpoint(interface->cur_altsetting, &endpoint)) { v4l2_err(&dev->v4l2_dev, "Could not find bulk-in endpoint\n"); goto error_put_usb; } + /* + * USB interface description is buggy, reported max packet size is 512 + * bytes, windows driver uses 8192 + */ + dev->bulk_in_size = 8192; + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; + /* init the device */ if (hdpvr_device_init(dev)) { v4l2_err(&dev->v4l2_dev, "device init failed\n"); From fe90217607fc82010713aa5eb4d2518300fefe6a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 12:11:41 +0200 Subject: [PATCH 054/394] media: s2255: refactor endpoint lookup Use the common USB helper for looking up bulk-in endpoints instead of open coding. Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 2c02873d09b5..0b8182edf8e4 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -2240,18 +2240,14 @@ static int s2255_probe(struct usb_interface *interface, iface_desc = interface->cur_altsetting; dev_dbg(&interface->dev, "num EP: %d\n", iface_desc->desc.bNumEndpoints); - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { - /* we found the bulk in endpoint */ - dev->read_endpoint = endpoint->bEndpointAddress; - } - } - if (!dev->read_endpoint) { + if (usb_find_bulk_in_endpoint(iface_desc, &endpoint)) { dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); goto errorEP; } + + dev->read_endpoint = endpoint->bEndpointAddress; + timer_setup(&dev->timer, s2255_timer, 0); init_waitqueue_head(&dev->fw_data->wait_fw); for (i = 0; i < MAX_CHANNELS; i++) { From 2282f979560af6bbc8ee2c1ee8663197312cee5b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2026 12:08:31 +0200 Subject: [PATCH 055/394] media: vpif_capture: fix OF node reference imbalance The driver reuses the OF node of the parent device but fails to take another reference to balance the one dropped by the platform bus code when unbinding the parent and releasing the child devices. Fix this by using the intended helper for reusing OF nodes. Fixes: 4a5f8ae50b66 ("[media] davinci: vpif_capture: get subdevs from DT when available") Cc: stable@vger.kernel.org # 4.13 Cc: Kevin Hilman Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/davinci/vpif_capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c index 15df3ea2f77e..91cb6223561a 100644 --- a/drivers/media/platform/ti/davinci/vpif_capture.c +++ b/drivers/media/platform/ti/davinci/vpif_capture.c @@ -1498,7 +1498,7 @@ vpif_capture_get_pdata(struct platform_device *pdev, * video ports & endpoints data. */ if (pdev->dev.parent && pdev->dev.parent->of_node) - pdev->dev.of_node = pdev->dev.parent->of_node; + device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) return pdev->dev.platform_data; From e42469304440d6b351b34eddb9284f198ceaf969 Mon Sep 17 00:00:00 2001 From: Josh Hesketh Date: Fri, 10 Apr 2026 16:25:02 +0100 Subject: [PATCH 056/394] staging: media: av7110: remove dead code from av7110_hw.c Remove functions av7110_reset_arm() and av7110_send_ci_cmd() which have both been disabled behind #if 0 since the introduction to staging. Code can be recovered from git history. Signed-off-by: Josh Hesketh Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110_hw.c | 46 ------------------------ 1 file changed, 46 deletions(-) diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c index 49ce295771e4..b0bd7666d48b 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/av7110/av7110_hw.c @@ -95,29 +95,6 @@ u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, unsigned int co return result; } -/* av7110 ARM core boot stuff */ -#if 0 -void av7110_reset_arm(struct av7110 *av7110) -{ - saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO); - - /* Disable DEBI and GPIO irq */ - SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03); - SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); - - saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI); - msleep(30); /* the firmware needs some time to initialize */ - - ARM_ResetMailBox(av7110); - - SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); - SAA7146_IER_ENABLE(av7110->dev, MASK_03); - - av7110->arm_ready = 1; - dprintk(1, "reset ARM\n"); -} -#endif /* 0 */ - static int waitdebi(struct av7110 *av7110, int adr, int state) { int k; @@ -498,29 +475,6 @@ int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...) return ret; } -#if 0 -int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len) -{ - int i, ret; - u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom), - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - dprintk(4, "%p\n", av7110); - - for (i = 0; i < len && i < 32; i++) { - if (i % 2 == 0) - cmd[(i / 2) + 2] = (u16)(buf[i]) << 8; - else - cmd[(i / 2) + 2] |= buf[i]; - } - - ret = av7110_send_fw_cmd(av7110, cmd, 18); - if (ret && ret != -ERESTARTSYS) - pr_err("%s(): error %d\n", __func__, ret); - return ret; -} -#endif /* 0 */ - int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, int request_buf_len, u16 *reply_buf, int reply_buf_len) { From 3eaac9e02d8591d3c790db572ef1c8fa5a841fdb Mon Sep 17 00:00:00 2001 From: Zhaoyang Yu <2426767509@qq.com> Date: Wed, 15 Apr 2026 14:49:09 +0000 Subject: [PATCH 057/394] media: dm1105: fix missing error check for dma_alloc_coherent The return value of dm1105_dma_map(), which handles DMA memory allocation, is ignored in dm1105_hw_init(). If dma_alloc_coherent() fails, the driver will proceed using a NULL pointer for DMA transfers, leading to a kernel oops or invalid hardware access. Fix this by checking the return value and propagating -ENOMEM on failure. Signed-off-by: Zhaoyang Yu <2426767509@qq.com> Signed-off-by: Hans Verkuil --- drivers/media/pci/dm1105/dm1105.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index bbd24769ae56..c33d811cbafa 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -768,6 +768,8 @@ static void dm1105_ir_exit(struct dm1105_dev *dm1105) static int dm1105_hw_init(struct dm1105_dev *dev) { + int ret; + dm1105_disable_irqs(dev); dm_writeb(DM1105_HOST_CTR, 0); @@ -778,7 +780,10 @@ static int dm1105_hw_init(struct dm1105_dev *dev) dm_writew(DM1105_TSCTR, 0xc10a); /* map DMA and set address */ - dm1105_dma_map(dev); + ret = dm1105_dma_map(dev); + if (ret) + return -ENOMEM; + dm1105_set_dma_addr(dev); /* big buffer */ dm_writel(DM1105_RLEN, 5 * DM1105_DMA_BYTES); From 9aa21e1549db8882ff77b691e7714153df21dff0 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 15 Apr 2026 23:28:26 +0800 Subject: [PATCH 058/394] media: vidtv: fix reference leak on failed device registration When platform_device_register() fails in vidtv_bridge_init(), the embedded struct device in vidtv_bridge_dev has already been initialized by device_initialize(), but the failure path returns the error without dropping the device reference for the current platform device: vidtv_bridge_init() -> platform_device_register(&vidtv_bridge_dev) -> device_initialize(&vidtv_bridge_dev.dev) -> setup_pdev_dma_masks(&vidtv_bridge_dev) -> platform_device_add(&vidtv_bridge_dev) This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before returning the error. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fixes: f90cf6079bf67 ("media: vidtv: add a bridge driver") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vidtv/vidtv_bridge.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c index a8a76434989c..fd69b4ee16f4 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c @@ -594,8 +594,10 @@ static int __init vidtv_bridge_init(void) int ret; ret = platform_device_register(&vidtv_bridge_dev); - if (ret) + if (ret) { + platform_device_put(&vidtv_bridge_dev); return ret; + } ret = platform_driver_register(&vidtv_bridge_driver); if (ret) From 33e2b833c66b890a0d71c4fa82d4c97143f7f75f Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 15 Apr 2026 23:45:37 +0800 Subject: [PATCH 059/394] media: vimc: fix reference leak on failed device registration When platform_device_register() fails in vimc_init(), the embedded struct device in vimc_pdev has already been initialized by device_initialize(), but the failure path returns the error without dropping the device reference for the current platform device: vimc_init() -> platform_device_register(&vimc_pdev) -> device_initialize(&vimc_pdev.dev) -> setup_pdev_dma_masks(&vimc_pdev) -> platform_device_add(&vimc_pdev) This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before returning the error. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fixes: 4babf057c143f ("media: vimc: allocate vimc_device dynamically") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vimc/vimc-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c index 15167e127461..fee0c7a09c4f 100644 --- a/drivers/media/test-drivers/vimc/vimc-core.c +++ b/drivers/media/test-drivers/vimc/vimc-core.c @@ -421,6 +421,7 @@ static int __init vimc_init(void) if (ret) { dev_err(&vimc_pdev.dev, "platform device registration failed (err=%d)\n", ret); + platform_device_put(&vimc_pdev); return ret; } From a07c179a92e949172ca52f6d4a13202ea88cd4b7 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Thu, 16 Apr 2026 00:20:58 +0800 Subject: [PATCH 060/394] media: vivid: fix cleanup bugs in vivid_init() When platform_device_register() fails in vivid_init(), the embedded struct device in vivid_pdev has already been initialized by device_initialize(), but the failure path jumps to free_output_strings without dropping the device reference for the current platform device: vivid_init() -> platform_device_register(&vivid_pdev) -> device_initialize(&vivid_pdev.dev) -> setup_pdev_dma_masks(&vivid_pdev) -> platform_device_add(&vivid_pdev) This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before jumping to the common cleanup path. Also, the unreg_driver label incorrectly calls platform_driver_register() instead of platform_driver_unregister(), which breaks cleanup when workqueue creation fails after successful driver registration. Fix that as well. The reference leak was identified by a static analysis tool I developed and confirmed by manual review. The incorrect cleanup call was found during code inspection. Fixes: f46d740fb0258 ("[media] vivid: turn this into a platform_device") Fixes: d7c969f37515d ("media: vivid: Add 'Is Connected To' menu controls") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vivid/vivid-core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index c8bf9b4d406c..62cfb5feb2cf 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -2289,8 +2289,10 @@ static int __init vivid_init(void) } } ret = platform_device_register(&vivid_pdev); - if (ret) + if (ret) { + platform_device_put(&vivid_pdev); goto free_output_strings; + } ret = platform_driver_register(&vivid_pdrv); if (ret) goto unreg_device; @@ -2311,7 +2313,7 @@ static int __init vivid_init(void) destroy_hdmi_wq: destroy_workqueue(update_hdmi_ctrls_workqueue); unreg_driver: - platform_driver_register(&vivid_pdrv); + platform_driver_unregister(&vivid_pdrv); unreg_device: platform_device_unregister(&vivid_pdev); free_output_strings: From 033ff0420e4c9c240ae5523fff39770298efa964 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 17 Apr 2026 14:53:30 +0800 Subject: [PATCH 061/394] media: marvell-cam: fix missing pci_disable_device() on remove During manual code audit, we found that cafe_pci_probe() enables the PCI device with pci_enable_device(), and its probe error path properly calls pci_disable_device() on failure. However, cafe_pci_remove() tears down the controller and frees the driver data without disabling the PCI device, leaving the remove path inconsistent with probe cleanup. Add the missing pci_disable_device() call to cafe_pci_remove(). Fixes: abfa3df36c01 ("[media] marvell-cam: Separate out the Marvell camera core") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Hans Verkuil --- drivers/media/platform/marvell/cafe-driver.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/marvell/cafe-driver.c b/drivers/media/platform/marvell/cafe-driver.c index 632c15572aa8..22034df6cba9 100644 --- a/drivers/media/platform/marvell/cafe-driver.c +++ b/drivers/media/platform/marvell/cafe-driver.c @@ -609,6 +609,7 @@ static void cafe_pci_remove(struct pci_dev *pdev) return; } cafe_shutdown(cam); + pci_disable_device(pdev); kfree(cam); } From 62a08bc8916ef36fddd5e4069df381f6379bbecb Mon Sep 17 00:00:00 2001 From: Philipp Matthias Hahn Date: Mon, 20 Apr 2026 15:54:43 +0200 Subject: [PATCH 062/394] media: gspca: Fix comment in sd_init() Fix spelling mistake of{ -> f}. Signed-off-by: Philipp Matthias Hahn Signed-off-by: Hans Verkuil --- drivers/media/usb/gspca/sonixb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c index 4d655e2da9cb..f251a886024f 100644 --- a/drivers/media/usb/gspca/sonixb.c +++ b/drivers/media/usb/gspca/sonixb.c @@ -943,7 +943,7 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - const __u8 stop = 0x09; /* Disable stream turn of LED */ + const __u8 stop = 0x09; /* Disable stream, turn off LED */ reg_w(gspca_dev, 0x01, &stop, 1); From e0f1c9a90ef665f2587c274a8fed59f2dfc575a6 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Sun, 26 Apr 2026 22:16:31 +0900 Subject: [PATCH 063/394] media: ti: vpe: unwind v4l2 device registration on probe error If the vpe_top resource is missing, vpe_probe() returns -ENODEV after v4l2_device_register() has succeeded. Probe failures do not call the driver's remove callback, so the v4l2 device remains registered on that error path. Route that failure through the existing v4l2_device_unregister() unwind label, matching the other errors after v4l2_device_register(). Fixes: 4d59c7d45585 ("media: ti-vpe: vpe: Add missing null pointer checks") Cc: stable@vger.kernel.org Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Yemike Abhilash Chandra Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/vpe/vpe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/ti/vpe/vpe.c b/drivers/media/platform/ti/vpe/vpe.c index a7e5a85e72a1..81bd1f9cee30 100644 --- a/drivers/media/platform/ti/vpe/vpe.c +++ b/drivers/media/platform/ti/vpe/vpe.c @@ -2539,7 +2539,8 @@ static int vpe_probe(struct platform_device *pdev) "vpe_top"); if (!dev->res) { dev_err(&pdev->dev, "missing 'vpe_top' resources data\n"); - return -ENODEV; + ret = -ENODEV; + goto v4l2_dev_unreg; } /* From 1a65db225b25bb8c8febf16974c060e0cc242eb9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 28 Apr 2026 16:50:08 +0200 Subject: [PATCH 064/394] media: pci: dm1105: Free allocated workqueue Destroy allocated workqueue in remove() callback to free its resources, thus fixing memory leak. Fixes: 519a4bdcf822 ("V4L/DVB (11984): Add support for yet another SDMC DM1105 based DVB-S card.") Cc: Signed-off-by: Krzysztof Kozlowski Signed-off-by: Hans Verkuil --- drivers/media/pci/dm1105/dm1105.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index c33d811cbafa..80d2e143384b 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -1199,6 +1199,7 @@ static void dm1105_remove(struct pci_dev *pdev) dm1105_hw_exit(dev); free_irq(pdev->irq, dev); + destroy_workqueue(dev->wq); pci_iounmap(pdev, dev->io_mem); pci_release_regions(pdev); pci_disable_device(pdev); From fb00ee47e565e9928b6516cd92878200f47e0d3b Mon Sep 17 00:00:00 2001 From: Maha Maryam Javaid Date: Wed, 29 Apr 2026 13:37:51 -0400 Subject: [PATCH 065/394] staging: media: av7110: fix typo in av7110.c Fix spelling mistake: connectd -> connected Signed-off-by: Maha Maryam Javaid Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 656bffd8c3d6..862aee993889 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -2265,7 +2265,7 @@ static int frontend_init(struct av7110 *av7110) * original Roberto Deza's hardware: * * rps1 code for budgetpatch will copy internal HS event to GPIO3 pin. - * GPIO3 is in budget-patch hardware connectd to port B VSYNC + * GPIO3 is in budget-patch hardware connected to port B VSYNC * HS is an internal event of 7146, accessible with RPS * and temporarily raised high every n lines * (n in defined in the RPS_THRESH1 counter threshold) From 2fc030f9ff287efc4510f4ae5efee0f23e64b608 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 30 Apr 2026 15:03:36 +0200 Subject: [PATCH 066/394] media: i2c: drop unneeded dependencies on OF_GPIO OF_GPIO is selected automatically on all OF systems. Any symbols it controls also provide stubs so there's really no reason to select it explicitly. For Kconfig entries that have no other dependencies: convert it to requiring OF to avoid new symbols popping up for everyone in make config, for others just drop it altogether. Signed-off-by: Bartosz Golaszewski Signed-off-by: Hans Verkuil --- drivers/media/i2c/Kconfig | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 8f2ba4121586..a3ab48607dcf 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -237,7 +237,7 @@ config VIDEO_IMX319 config VIDEO_IMX334 tristate "Sony IMX334 sensor support" - depends on OF_GPIO + depends on OF select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony @@ -248,7 +248,7 @@ config VIDEO_IMX334 config VIDEO_IMX335 tristate "Sony IMX335 sensor support" - depends on OF_GPIO + depends on OF select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony @@ -268,7 +268,7 @@ config VIDEO_IMX355 config VIDEO_IMX412 tristate "Sony IMX412 sensor support" - depends on OF_GPIO + depends on OF help This is a Video4Linux2 sensor driver for the Sony IMX412 camera. @@ -278,7 +278,7 @@ config VIDEO_IMX412 config VIDEO_IMX415 tristate "Sony IMX415 sensor support" - depends on OF_GPIO + depends on OF select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony @@ -703,7 +703,7 @@ config VIDEO_OV8865 config VIDEO_OV9282 tristate "OmniVision OV9282 sensor support" - depends on OF_GPIO + depends on OF select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the OmniVision @@ -1271,7 +1271,6 @@ config VIDEO_BT866 config VIDEO_ISL7998X tristate "Intersil ISL7998x video decoder" depends on VIDEO_DEV && I2C - depends on OF_GPIO select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -1309,7 +1308,6 @@ config VIDEO_MAX9286 tristate "Maxim MAX9286 GMSL deserializer support" depends on I2C && I2C_MUX depends on VIDEO_DEV - depends on OF_GPIO select V4L2_FWNODE select VIDEO_V4L2_SUBDEV_API select MEDIA_CONTROLLER From 084973ebd67b28f0945c5d45408f86c58b540110 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Sun, 26 Apr 2026 21:43:49 +0900 Subject: [PATCH 067/394] media: stm32: dcmi: unregister notifier on probe failure dcmi_graph_init() registers the async notifier before dcmi_probe() toggles the reset line. If reset_control_assert() or reset_control_deassert() fails afterwards, probe returns through err_cleanup and the driver core will not call dcmi_remove(). Unregister the notifier before cleaning it up on that error path, matching the successful remove path and the V4L2 async notifier lifetime rules. Signed-off-by: Myeonghun Pak Signed-off-by: Hans Verkuil Fixes: d079f94c9046 ("media: platform: Switch to v4l2_async_notifier_add_subdev") Cc: stable@vger.kernel.org [hverkuil: added Fixes tag] --- drivers/media/platform/st/stm32/stm32-dcmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index e5663fbe6422..eeb0199864dd 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -2195,6 +2195,7 @@ static int dcmi_probe(struct platform_device *pdev) return 0; err_cleanup: + v4l2_async_nf_unregister(&dcmi->notifier); v4l2_async_nf_cleanup(&dcmi->notifier); err_media_entity_cleanup: media_entity_cleanup(&dcmi->vdev->entity); From a562d6dc86bdfdd299e1b4734977a8d63e803583 Mon Sep 17 00:00:00 2001 From: Zile Xiong Date: Fri, 20 Mar 2026 14:54:45 +0800 Subject: [PATCH 068/394] media: vb2: use ssize_t for vb2_read/vb2_write vb2_read() and vb2_write() return size_t, but propagate negative errno values from __vb2_perform_fileio(). This relies on implicit signed/unsigned conversions in callers (e.g. vb2_fop_read()) to recover error codes: __vb2_perform_fileio() -> -EINVAL vb2_read() -> (size_t)-EINVAL vb2_fop_read() -> -EINVAL This relies on implicit conversions that are not obvious. These helpers are exported (EXPORT_SYMBOL_GPL) and part of the vb2 API, so changing their return type may affect existing users. However, they conceptually follow read/write semantics, where ssize_t is typically used to return either a byte count or a negative error code. Switch vb2_read() and vb2_write() to ssize_t, and update __vb2_perform_fileio() accordingly. Signed-off-by: Zile Xiong Acked-by: Marek Szyprowski Fixes: b25748fe6126 ("[media] v4l: videobuf2: add read() and write() emulator") Cc: stable@vger.kernel.org Signed-off-by: Hans Verkuil --- drivers/media/common/videobuf2/videobuf2-core.c | 12 ++++++------ include/media/videobuf2-core.h | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index adf668b213c2..b0a6084f1757 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -2990,8 +2990,8 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q) * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) * @read: access mode selector (1 means read, 0 means write) */ -static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblock, int read) +static ssize_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblock, int read) { struct vb2_fileio_data *fileio; struct vb2_fileio_buf *buf; @@ -3154,15 +3154,15 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ return ret; } -size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblocking) +ssize_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblocking) { return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1); } EXPORT_SYMBOL_GPL(vb2_read); -size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, - loff_t *ppos, int nonblocking) +ssize_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, + loff_t *ppos, int nonblocking) { return __vb2_perform_fileio(q, (char __user *) data, count, ppos, nonblocking, 0); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 4424d481d7f7..4b4f4c15c53a 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -1093,8 +1093,8 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file, * @ppos: file handle position tracking pointer * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) */ -size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblock); +ssize_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, + loff_t *ppos, int nonblock); /** * vb2_write() - implements write() syscall logic. * @q: pointer to &struct vb2_queue with videobuf2 queue. @@ -1103,8 +1103,8 @@ size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, * @ppos: file handle position tracking pointer * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) */ -size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, - loff_t *ppos, int nonblock); +ssize_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, + loff_t *ppos, int nonblock); /** * typedef vb2_thread_fnc - callback function for use with vb2_thread. From a0701e387b46e2481c05b47f1235b954bfc2af3e Mon Sep 17 00:00:00 2001 From: Wang Jun <1742789905@qq.com> Date: Fri, 20 Mar 2026 15:04:53 +0800 Subject: [PATCH 069/394] media: cx23885: add ioremap return check and cleanup Add a check for the return value of pci_ioremap_bar() in cx23885_dev_setup(). If ioremap for BAR0 fails, release the already allocated PCI memory region, decrement the device count, and return -ENODEV. This prevents a potential null pointer dereference and ensures proper cleanup on memory mapping failure. Fixes: d19770e5178a ("V4L/DVB (6150): Add CX23885/CX23887 PCIe bridge driver") Cc: stable@vger.kernel.org Signed-off-by: Wang Jun <1742789905@qq.com> Signed-off-by: Hans Verkuil --- drivers/media/pci/cx23885/cx23885-core.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 4a8af8b88d84..9b92e8db494c 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -1002,8 +1002,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) } /* PCIe stuff */ - dev->lmmio = ioremap(pci_resource_start(dev->pci, 0), - pci_resource_len(dev->pci, 0)); + dev->lmmio = pci_ioremap_bar(dev->pci, 0); + if (!dev->lmmio) { + dev_err(&dev->pci->dev, "CORE %s: can't ioremap MMIO memory\n", + dev->name); + goto err_release_region; + } dev->bmmio = (u8 __iomem *)dev->lmmio; @@ -1109,6 +1113,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) } return 0; + +err_release_region: + release_mem_region(pci_resource_start(dev->pci, 0), + pci_resource_len(dev->pci, 0)); + cx23885_devcount--; + return -ENODEV; } static void cx23885_dev_unregister(struct cx23885_dev *dev) From 7d6358ab02866e5b7ed8d3a00805297617bbb0ec Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 30 Mar 2026 11:37:27 +0200 Subject: [PATCH 070/394] media: cx231xx: fix devres lifetime USB drivers bind to USB interfaces and any device managed resources should have their lifetime tied to the interface rather than parent USB device. This avoids issues like memory leaks when drivers are unbound without their devices being physically disconnected (e.g. on probe deferral or configuration changes). Fix the driver state lifetime so that it is released on driver unbind. Fixes: 184a82784d50 ("[media] cx231xx: use devm_ functions to allocate memory") Cc: stable@vger.kernel.org # 3.17 Signed-off-by: Johan Hovold Signed-off-by: Hans Verkuil --- drivers/media/usb/cx231xx/cx231xx-cards.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index b75535d6abaf..69b24205bc56 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1573,7 +1573,8 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, dev->video_mode.end_point_addr, dev->video_mode.num_alt); - dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->video_mode.num_alt, GFP_KERNEL); + dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->video_mode.num_alt, GFP_KERNEL); if (dev->video_mode.alt_max_pkt_size == NULL) return -ENOMEM; @@ -1614,7 +1615,8 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, dev->vbi_mode.num_alt); /* compute alternate max packet sizes for vbi */ - dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->vbi_mode.num_alt, GFP_KERNEL); + dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->vbi_mode.num_alt, GFP_KERNEL); if (dev->vbi_mode.alt_max_pkt_size == NULL) return -ENOMEM; @@ -1656,7 +1658,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, "sliced CC EndPoint Addr 0x%x, Alternate settings: %i\n", dev->sliced_cc_mode.end_point_addr, dev->sliced_cc_mode.num_alt); - dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->sliced_cc_mode.num_alt, GFP_KERNEL); + dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->sliced_cc_mode.num_alt, + GFP_KERNEL); if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) return -ENOMEM; @@ -1720,7 +1724,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface, udev = interface_to_usbdev(interface); /* allocate memory for our device state and initialize it */ - dev = devm_kzalloc(&udev->dev, sizeof(*dev), GFP_KERNEL); + dev = devm_kzalloc(&interface->dev, sizeof(*dev), GFP_KERNEL); if (dev == NULL) { retval = -ENOMEM; goto err_if; @@ -1850,7 +1854,9 @@ static int cx231xx_usb_probe(struct usb_interface *interface, dev->ts1_mode.end_point_addr, dev->ts1_mode.num_alt); - dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->ts1_mode.num_alt, GFP_KERNEL); + dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32, + dev->ts1_mode.num_alt, + GFP_KERNEL); if (dev->ts1_mode.alt_max_pkt_size == NULL) { retval = -ENOMEM; goto err_video_alt; From f86ed548386e3050e5f8f25b450d09dc009d9a88 Mon Sep 17 00:00:00 2001 From: Ma Ke Date: Thu, 2 Apr 2026 15:35:29 +0800 Subject: [PATCH 071/394] media: saa7134: Fix a possible memory leak in saa7134_video_init1 In saa7134_video_init1(), the return value of the first saa7134_pgtable_alloc() is not checked. If it fails, the function continues as if successful, leaving the driver with an invalid page table. Additionally, if vb2_queue_init() for the VBI queue fails after the video queue page table has been allocated, the allocated memory is not freed before returning. The second saa7134_pgtable_alloc() also lacks a return value check. Errors occur during device probing before the device is fully registered, the normal cleanup path in saa7134_finidev() is not executed, leading to memory leaks and potential use of uninitialized DMA resources. Check the return value of both saa7134_pgtable_alloc() calls and propagate errors. On failure of any later step, free allocated page tables to avoid memory leaks. Ensure control handlers are also released on error to prevent further resource leakage. Found by code review. Signed-off-by: Ma Ke Cc: stable@vger.kernel.org Fixes: a00e68888d5d ("[media] saa7134: move saa7134_pgtable to saa7134_dmaqueue") Signed-off-by: Hans Verkuil --- drivers/media/pci/saa7134/saa7134-video.c | 25 ++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 4a51b873e47a..2b1672737d84 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1714,8 +1714,10 @@ int saa7134_video_init1(struct saa7134_dev *dev) q->dev = &dev->pci->dev; ret = vb2_queue_init(q); if (ret) - return ret; - saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + ret = saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt); + if (ret) + goto err_free_ctrl; q = &dev->vbi_vbq; q->type = V4L2_BUF_TYPE_VBI_CAPTURE; @@ -1732,11 +1734,24 @@ int saa7134_video_init1(struct saa7134_dev *dev) q->lock = &dev->lock; q->dev = &dev->pci->dev; ret = vb2_queue_init(q); - if (ret) - return ret; - saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt); + if (ret) { + saa7134_pgtable_free(dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + } + + ret = saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt); + if (ret) { + saa7134_pgtable_free(dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + } return 0; + +err_free_ctrl: + v4l2_ctrl_handler_free(&dev->ctrl_handler); + if (card_has_radio(dev)) + v4l2_ctrl_handler_free(&dev->radio_ctrl_handler); + return ret; } void saa7134_video_fini(struct saa7134_dev *dev) From c8e13b0dcb7084152cf8274f24e4c5a4ffec6439 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Sun, 12 Apr 2026 17:24:16 +0000 Subject: [PATCH 072/394] media: tegra-video: tegra210: remove redundant NULL check in dequeue_buf_done list_first_entry() does not returns NULL when the list is known to be non-empty. The NULL check before list_del_init() is therefore redundant. Remove the unnecessary check. Signed-off-by: Hungyu Lin Reviewed-by: Dan Carpenter Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/tegra210.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/media/tegra-video/tegra210.c b/drivers/staging/media/tegra-video/tegra210.c index da99f19a39e7..a6606768fa03 100644 --- a/drivers/staging/media/tegra-video/tegra210.c +++ b/drivers/staging/media/tegra-video/tegra210.c @@ -362,8 +362,7 @@ dequeue_buf_done(struct tegra_vi_channel *chan) buf = list_first_entry(&chan->done, struct tegra_channel_buffer, queue); - if (buf) - list_del_init(&buf->queue); + list_del_init(&buf->queue); spin_unlock(&chan->done_lock); return buf; From cc20e81da6d99926f94fad7af21f75c07e865769 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 14 Apr 2026 16:32:39 +0800 Subject: [PATCH 073/394] media: em28xx-video: fix missing res_free() on init_usb_xfer failure res_get() is called before em28xx_init_usb_xfer(), but the error path of em28xx_init_usb_xfer() does not release the resource, leading to a persistent busy state. Signed-off-by: Haoxiang Li Signed-off-by: Hans Verkuil --- drivers/media/usb/em28xx/em28xx-video.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 4a0ce9c5ee4b..da0422c65e5f 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1248,8 +1248,10 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) dev->max_pkt_size, dev->packet_multiplier, em28xx_urb_data_copy); - if (rc < 0) + if (rc < 0) { + res_free(dev, vq->type); return rc; + } /* * djh: it's not clear whether this code is still needed. I'm From 680daf40a82d483949f87f0d8f98639dc47e610c Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Wed, 22 Apr 2026 20:17:34 +0530 Subject: [PATCH 074/394] media: rtl2832: fix use-after-free in rtl2832_remove() cancel_delayed_work_sync() is called before i2c_mux_del_adapters() in rtl2832_remove(). While the cancel waits for any running instance of i2c_gate_work to finish, it does not prevent the timer from being rescheduled by a concurrent thread. During probe, the r820t_attach() call attempts I2C transfers through the mux adapter. These transfers go through i2c_mux_master_xfer(), which calls rtl2832_deselect() after the transfer completes, rescheduling i2c_gate_work via schedule_delayed_work(). If this transfer is still in flight when rtl2832_remove() runs, rtl2832_deselect() can reschedule i2c_gate_work after it has been cancelled, causing a use-after-free when kfree(dev) is called. Fix this by calling i2c_mux_del_adapters() before cancel_delayed_work_sync(). Once the mux adapter is unregistered, no new I2C transfers can go through it, so rtl2832_deselect() can no longer reschedule i2c_gate_work. The subsequent cancel_delayed_work_sync() is then guaranteed to be final. Fixes: cddcc40b1b15 ("[media] rtl2832: convert to use an explicit i2c mux core") Cc: stable@vger.kernel.org Reported-by: syzbot+019ced393ab913002b75@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=019ced393ab913002b75 Signed-off-by: Deepanshu Kartikey Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/rtl2832.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index d8e1546aea5e..9898f729304a 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -1115,10 +1115,10 @@ static void rtl2832_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - cancel_delayed_work_sync(&dev->i2c_gate_work); - i2c_mux_del_adapters(dev->muxc); + cancel_delayed_work_sync(&dev->i2c_gate_work); + regmap_exit(dev->regmap); kfree(dev); From e4107c6b301d1afa4e3ae471f964da83d15ead4a Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Sat, 25 Apr 2026 20:03:16 -0400 Subject: [PATCH 075/394] media: tegra-vde: Add HAS_IOMEM dependency to match SRAM select kconfiglint reports: K002: config VIDEO_TEGRA_VDE selects visible symbol SRAM which has dependencies VIDEO_TEGRA_VDE selects SRAM, which is defined in drivers/misc/Kconfig as: config SRAM bool "Generic on-chip SRAM driver" depends on HAS_IOMEM The NVIDIA Tegra video decoder driver was originally introduced in commit cd6c56feb591 ("media: staging: media: Introduce NVIDIA Tegra video decoder driver") as a staging driver with `depends on ARCH_TEGRA || COMPILE_TEST` and `select SRAM`. Since all Tegra SoCs have HAS_IOMEM, the SRAM dependency was implicitly satisfied for real hardware configurations. The driver was later de-staged in commit 8bd4aaf438e3 ("media: staging: tegra-vde: De-stage driver") and relocated to drivers/media/platform/nvidia/tegra-vde/ in commit 9b18ef7c9ff4 ("media: platform: rename tegra/vde/ to nvidia/tegra-vde/"). Throughout these moves, the `select SRAM` remained without a corresponding HAS_IOMEM dependency. Under COMPILE_TEST on a hypothetical architecture without HAS_IOMEM (such as UML in some configurations), the select would force SRAM on without its HAS_IOMEM dependency being met. Add an explicit `depends on HAS_IOMEM` to make the dependency chain complete and prevent this misconfiguration under COMPILE_TEST. Assisted-by: Claude:claude-opus-4-6 kconfiglint Signed-off-by: Sasha Levin Signed-off-by: Hans Verkuil --- drivers/media/platform/nvidia/tegra-vde/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/nvidia/tegra-vde/Kconfig b/drivers/media/platform/nvidia/tegra-vde/Kconfig index 2fe13f39c95b..f05fcc94deca 100644 --- a/drivers/media/platform/nvidia/tegra-vde/Kconfig +++ b/drivers/media/platform/nvidia/tegra-vde/Kconfig @@ -2,6 +2,7 @@ config VIDEO_TEGRA_VDE tristate "NVIDIA Tegra Video Decoder Engine driver" depends on V4L_MEM2MEM_DRIVERS depends on ARCH_TEGRA || COMPILE_TEST + depends on HAS_IOMEM depends on VIDEO_DEV select DMA_SHARED_BUFFER select IOMMU_IOVA From caced3578bf9f104a4aaad8f46c4c719e705d9a6 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Fri, 1 May 2026 23:28:31 +0300 Subject: [PATCH 076/394] media: v4l2-ctrls-request: add NULL check in v4l2_ctrl_request_complete() If CONFIG_MEDIA_CONTROLLER is undefined, media_request_object_find() will always return NULL, so its 2nd call in v4l2_ctrl_request_complete() would fail as well as the 1st one and thus cause hdl to have a wrong value (at the top of memory) and list_for_each_entry() to iterate over the garbage data located there. Add NULL check for the 2nd call and place the error cleanup at the end of v4l2_ctrl_request_complete()... Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool. Fixes: c3bf5129f339 ("media: v4l2-ctrls: always copy the controls on completion") Cc: stable@vger.kernel.org Signed-off-by: Sergey Shtylyov Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-ctrls-request.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ctrls-request.c b/drivers/media/v4l2-core/v4l2-ctrls-request.c index 4b7e6981b8d6..5c2fc767c6a6 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-request.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-request.c @@ -348,13 +348,12 @@ void v4l2_ctrl_request_complete(struct media_request *req, ret = v4l2_ctrl_handler_init(hdl, (main_hdl->nr_of_buckets - 1) * 8); if (!ret) ret = v4l2_ctrl_request_bind(req, hdl, main_hdl); - if (ret) { - v4l2_ctrl_handler_free(hdl); - kfree(hdl); - return; - } + if (ret) + goto error; hdl->request_is_queued = true; obj = media_request_object_find(req, &req_ops, main_hdl); + if (!obj) + goto error; } hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj); @@ -389,6 +388,11 @@ void v4l2_ctrl_request_complete(struct media_request *req, mutex_unlock(main_hdl->lock); media_request_object_complete(obj); media_request_object_put(obj); + return; + +error: + v4l2_ctrl_handler_free(hdl); + kfree(hdl); } EXPORT_SYMBOL(v4l2_ctrl_request_complete); From 0b42657bea6ba635226e8ef551076d024ceacdc9 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 31 Mar 2026 10:43:40 +0200 Subject: [PATCH 077/394] media: v4l2-common: Always register clock with device-specific name If we need to register a dummy fixed-frequency clock, always register it using a device-specific name. This supports the use case where a system has two of the same sensor, meaning two instances of the same driver, which previously both tried (and failed) to create a clock with the same name. Signed-off-by: Paul Cercueil Reviewed-by: Mehdi Djait Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 55bcd5975d9f..bceafc4e92c8 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -793,14 +793,15 @@ struct clk *__devm_v4l2_sensor_clk_get(struct device *dev, const char *id, if (ret) return ERR_PTR(ret == -EINVAL ? -EPROBE_DEFER : ret); - if (!id) { + if (id) + clk_id = kasprintf(GFP_KERNEL, "clk-%s-%s", dev_name(dev), id); + else clk_id = kasprintf(GFP_KERNEL, "clk-%s", dev_name(dev)); - if (!clk_id) - return ERR_PTR(-ENOMEM); - id = clk_id; - } - clk_hw = devm_clk_hw_register_fixed_rate(dev, id, NULL, 0, rate); + if (!clk_id) + return ERR_PTR(-ENOMEM); + + clk_hw = devm_clk_hw_register_fixed_rate(dev, clk_id, NULL, 0, rate); if (IS_ERR(clk_hw)) return ERR_CAST(clk_hw); From d9f8489c4325607d8a7c4a2860ce8c72a9874ff1 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 26 Mar 2026 12:42:49 +0200 Subject: [PATCH 078/394] media: Documentation: Use right function to test device power state Tell driver authors to use pm_runtime_get_if_active() instead of pm_runtime_get_if_in_use() to check the device's power state in the s_ctrl callback. pm_runtime_get_if_active() is the right function to use here since it returns non-zero if the device is powered on rather than its PM runtime usage_count is non-zero. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart --- Documentation/driver-api/media/camera-sensor.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/media/camera-sensor.rst b/Documentation/driver-api/media/camera-sensor.rst index 94bd1dae82d5..c8552f70f496 100644 --- a/Documentation/driver-api/media/camera-sensor.rst +++ b/Documentation/driver-api/media/camera-sensor.rst @@ -114,7 +114,7 @@ of the device. This is because the power state of the device is only changed after the power state transition has taken place. The ``s_ctrl`` callback can be used to obtain device's power state after the power state transition: -.. c:function:: int pm_runtime_get_if_in_use(struct device *dev); +.. c:function:: int pm_runtime_get_if_active(struct device *dev); The function returns a non-zero value if it succeeded getting the power count or runtime PM was disabled, in either of which cases the driver may proceed to From 9a54c285630c0072daef5d526c3f0b697e901065 Mon Sep 17 00:00:00 2001 From: Bin Du Date: Wed, 6 May 2026 17:32:43 +0800 Subject: [PATCH 079/394] media: platform: amd: Introduce amd isp4 capture driver AMD isp4 capture is a v4l2 media device which implements media controller interface. It has one sub-device (AMD ISP4 sub-device) endpoint which can be connected to a remote CSI2 TX endpoint. It supports only one physical interface for now. Also add ISP4 driver related entry info into the MAINTAINERS file Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Signed-off-by: Sakari Ailus --- MAINTAINERS | 13 +++ drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 1 + drivers/media/platform/amd/Kconfig | 3 + drivers/media/platform/amd/Makefile | 3 + drivers/media/platform/amd/isp4/Kconfig | 15 +++ drivers/media/platform/amd/isp4/Makefile | 6 ++ drivers/media/platform/amd/isp4/isp4.c | 127 +++++++++++++++++++++++ drivers/media/platform/amd/isp4/isp4.h | 17 +++ 9 files changed, 186 insertions(+) create mode 100644 drivers/media/platform/amd/Kconfig create mode 100644 drivers/media/platform/amd/Makefile create mode 100644 drivers/media/platform/amd/isp4/Kconfig create mode 100644 drivers/media/platform/amd/isp4/Makefile create mode 100644 drivers/media/platform/amd/isp4/isp4.c create mode 100644 drivers/media/platform/amd/isp4/isp4.h diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..3b495a4dc786 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1152,6 +1152,19 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux.git F: drivers/iommu/amd/ F: include/linux/amd-iommu.h +AMD ISP4 DRIVER +M: Bin Du +M: Nirujogi Pratap +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media.git +F: drivers/media/platform/amd/Kconfig +F: drivers/media/platform/amd/Makefile +F: drivers/media/platform/amd/isp4/Kconfig +F: drivers/media/platform/amd/isp4/Makefile +F: drivers/media/platform/amd/isp4/isp4.c +F: drivers/media/platform/amd/isp4/isp4.h + AMD KFD M: Felix Kuehling L: amd-gfx@lists.freedesktop.org diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 3f0b7bb68cc9..0b33e927bd59 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -63,6 +63,7 @@ config VIDEO_MUX # Platform drivers - Please keep it alphabetically sorted source "drivers/media/platform/allegro-dvt/Kconfig" +source "drivers/media/platform/amd/Kconfig" source "drivers/media/platform/amlogic/Kconfig" source "drivers/media/platform/amphion/Kconfig" source "drivers/media/platform/arm/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 6d5f79ddfcc3..16c185752474 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -6,6 +6,7 @@ # Place here, alphabetically sorted by directory # (e. g. LC_ALL=C sort Makefile) obj-y += allegro-dvt/ +obj-y += amd/ obj-y += amlogic/ obj-y += amphion/ obj-y += arm/ diff --git a/drivers/media/platform/amd/Kconfig b/drivers/media/platform/amd/Kconfig new file mode 100644 index 000000000000..25af49f246b2 --- /dev/null +++ b/drivers/media/platform/amd/Kconfig @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +source "drivers/media/platform/amd/isp4/Kconfig" diff --git a/drivers/media/platform/amd/Makefile b/drivers/media/platform/amd/Makefile new file mode 100644 index 000000000000..8bfc1955f22e --- /dev/null +++ b/drivers/media/platform/amd/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += isp4/ diff --git a/drivers/media/platform/amd/isp4/Kconfig b/drivers/media/platform/amd/isp4/Kconfig new file mode 100644 index 000000000000..55dd2dc453a2 --- /dev/null +++ b/drivers/media/platform/amd/isp4/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0+ + +config VIDEO_AMD_ISP4_CAPTURE + tristate "AMD ISP4 and camera driver" + depends on DRM_AMD_ISP && VIDEO_DEV && HAS_DMA + select VIDEOBUF2_CORE + select VIDEOBUF2_MEMOPS + select VIDEOBUF2_V4L2 + select VIDEOBUF2_VMALLOC + select VIDEO_V4L2_SUBDEV_API + help + This is support for AMD ISP4 and camera subsystem driver. + Say Y here to enable the ISP4 and camera device for video capture. + To compile this driver as a module, choose M here. The module will + be called amd_isp4_capture. diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile new file mode 100644 index 000000000000..500b81ce5d14 --- /dev/null +++ b/drivers/media/platform/amd/isp4/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2025 Advanced Micro Devices, Inc. + +obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o +amd_isp4_capture-objs := isp4.o diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c new file mode 100644 index 000000000000..58b21258b6d3 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include +#include +#include + +#include "isp4.h" + +#define ISP4_DRV_NAME "amd_isp_capture" + +static const struct { + const char *name; + u32 status_mask; + u32 en_mask; + u32 ack_mask; + u32 rb_int_num; +} isp4_irq[] = { + /* The IRQ order is aligned with the isp4_subdev.fw_resp_thread order */ + { + .name = "isp_irq_global", + .rb_int_num = 4, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT12 */ + }, + { + .name = "isp_irq_stream1", + .rb_int_num = 0, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT9 */ + }, +}; + +static irqreturn_t isp4_irq_handler(int irq, void *arg) +{ + return IRQ_HANDLED; +} + +static int isp4_capture_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int irq[ARRAY_SIZE(isp4_irq)]; + struct isp4_device *isp_dev; + int ret; + + isp_dev = devm_kzalloc(dev, sizeof(*isp_dev), GFP_KERNEL); + if (!isp_dev) + return -ENOMEM; + + dev->init_name = ISP4_DRV_NAME; + + for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) { + irq[i] = platform_get_irq(pdev, isp4_irq[i].rb_int_num); + if (irq[i] < 0) + return dev_err_probe(dev, irq[i], + "fail to get irq %d\n", + isp4_irq[i].rb_int_num); + + ret = devm_request_irq(dev, irq[i], isp4_irq_handler, + IRQF_NO_AUTOEN, isp4_irq[i].name, dev); + if (ret) + return dev_err_probe(dev, ret, "fail to req irq %d\n", + irq[i]); + } + + isp_dev->v4l2_dev.mdev = &isp_dev->mdev; + + strscpy(isp_dev->mdev.model, "amd_isp41_mdev", + sizeof(isp_dev->mdev.model)); + isp_dev->mdev.dev = dev; + media_device_init(&isp_dev->mdev); + + snprintf(isp_dev->v4l2_dev.name, sizeof(isp_dev->v4l2_dev.name), + "AMD-V4L2-ROOT"); + ret = v4l2_device_register(dev, &isp_dev->v4l2_dev); + if (ret) { + dev_err_probe(dev, ret, "fail register v4l2 device\n"); + goto err_clean_media; + } + + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + ret = media_device_register(&isp_dev->mdev); + if (ret) { + dev_err_probe(dev, ret, "fail to register media device\n"); + goto err_isp4_deinit; + } + + platform_set_drvdata(pdev, isp_dev); + + return 0; + +err_isp4_deinit: + pm_runtime_disable(dev); + v4l2_device_unregister(&isp_dev->v4l2_dev); +err_clean_media: + media_device_cleanup(&isp_dev->mdev); + + return ret; +} + +static void isp4_capture_remove(struct platform_device *pdev) +{ + struct isp4_device *isp_dev = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + media_device_unregister(&isp_dev->mdev); + pm_runtime_disable(dev); + v4l2_device_unregister(&isp_dev->v4l2_dev); + media_device_cleanup(&isp_dev->mdev); +} + +static struct platform_driver isp4_capture_drv = { + .probe = isp4_capture_probe, + .remove = isp4_capture_remove, + .driver = { + .name = ISP4_DRV_NAME, + } +}; + +module_platform_driver(isp4_capture_drv); + +MODULE_ALIAS("platform:" ISP4_DRV_NAME); +MODULE_IMPORT_NS("DMA_BUF"); + +MODULE_DESCRIPTION("AMD ISP4 Driver"); +MODULE_AUTHOR("Bin Du "); +MODULE_AUTHOR("Pratap Nirujogi "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/amd/isp4/isp4.h b/drivers/media/platform/amd/isp4/isp4.h new file mode 100644 index 000000000000..7f2db0dfa2d9 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_H_ +#define _ISP4_H_ + +#include +#include + +struct isp4_device { + struct v4l2_device v4l2_dev; + struct media_device mdev; +}; + +#endif /* _ISP4_H_ */ From f2f2c3547d6283e79de424c3c6e759899608fa1e Mon Sep 17 00:00:00 2001 From: Bin Du Date: Wed, 6 May 2026 17:32:44 +0800 Subject: [PATCH 080/394] media: platform: amd: low level support for isp4 firmware Low level functions for accessing the registers and mapping to their ranges. This change also includes register definitions for ring buffer used to communicate with ISP Firmware. Ring buffer is the communication interface between driver and ISP Firmware. Command and responses are exchanged through the ring buffer. Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Signed-off-by: Sakari Ailus --- MAINTAINERS | 1 + drivers/media/platform/amd/isp4/isp4_hw_reg.h | 124 ++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 drivers/media/platform/amd/isp4/isp4_hw_reg.h diff --git a/MAINTAINERS b/MAINTAINERS index 3b495a4dc786..e2561d95d251 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1164,6 +1164,7 @@ F: drivers/media/platform/amd/isp4/Kconfig F: drivers/media/platform/amd/isp4/Makefile F: drivers/media/platform/amd/isp4/isp4.c F: drivers/media/platform/amd/isp4/isp4.h +F: drivers/media/platform/amd/isp4/isp4_hw_reg.h AMD KFD M: Felix Kuehling diff --git a/drivers/media/platform/amd/isp4/isp4_hw_reg.h b/drivers/media/platform/amd/isp4/isp4_hw_reg.h new file mode 100644 index 000000000000..09c76f75c5ee --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_hw_reg.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_HW_REG_H_ +#define _ISP4_HW_REG_H_ + +#include + +#define ISP_SOFT_RESET 0x62000 +#define ISP_SYS_INT0_EN 0x62010 +#define ISP_SYS_INT0_STATUS 0x62014 +#define ISP_SYS_INT0_ACK 0x62018 +#define ISP_CCPU_CNTL 0x62054 +#define ISP_STATUS 0x62058 +#define ISP_LOG_RB_BASE_LO0 0x62148 +#define ISP_LOG_RB_BASE_HI0 0x6214c +#define ISP_LOG_RB_SIZE0 0x62150 +#define ISP_LOG_RB_RPTR0 0x62154 +#define ISP_LOG_RB_WPTR0 0x62158 +#define ISP_RB_BASE_LO1 0x62170 +#define ISP_RB_BASE_HI1 0x62174 +#define ISP_RB_SIZE1 0x62178 +#define ISP_RB_RPTR1 0x6217c +#define ISP_RB_WPTR1 0x62180 +#define ISP_RB_BASE_LO2 0x62184 +#define ISP_RB_BASE_HI2 0x62188 +#define ISP_RB_SIZE2 0x6218c +#define ISP_RB_RPTR2 0x62190 +#define ISP_RB_WPTR2 0x62194 +#define ISP_RB_BASE_LO3 0x62198 +#define ISP_RB_BASE_HI3 0x6219c +#define ISP_RB_SIZE3 0x621a0 +#define ISP_RB_RPTR3 0x621a4 +#define ISP_RB_WPTR3 0x621a8 +#define ISP_RB_BASE_LO4 0x621ac +#define ISP_RB_BASE_HI4 0x621b0 +#define ISP_RB_SIZE4 0x621b4 +#define ISP_RB_RPTR4 0x621b8 +#define ISP_RB_WPTR4 0x621bc +#define ISP_RB_BASE_LO5 0x621c0 +#define ISP_RB_BASE_HI5 0x621c4 +#define ISP_RB_SIZE5 0x621c8 +#define ISP_RB_RPTR5 0x621cc +#define ISP_RB_WPTR5 0x621d0 +#define ISP_RB_BASE_LO6 0x621d4 +#define ISP_RB_BASE_HI6 0x621d8 +#define ISP_RB_SIZE6 0x621dc +#define ISP_RB_RPTR6 0x621e0 +#define ISP_RB_WPTR6 0x621e4 +#define ISP_RB_BASE_LO7 0x621e8 +#define ISP_RB_BASE_HI7 0x621ec +#define ISP_RB_SIZE7 0x621f0 +#define ISP_RB_RPTR7 0x621f4 +#define ISP_RB_WPTR7 0x621f8 +#define ISP_RB_BASE_LO8 0x621fc +#define ISP_RB_BASE_HI8 0x62200 +#define ISP_RB_SIZE8 0x62204 +#define ISP_RB_RPTR8 0x62208 +#define ISP_RB_WPTR8 0x6220c +#define ISP_RB_BASE_LO9 0x62210 +#define ISP_RB_BASE_HI9 0x62214 +#define ISP_RB_SIZE9 0x62218 +#define ISP_RB_RPTR9 0x6221c +#define ISP_RB_WPTR9 0x62220 +#define ISP_RB_BASE_LO10 0x62224 +#define ISP_RB_BASE_HI10 0x62228 +#define ISP_RB_SIZE10 0x6222c +#define ISP_RB_RPTR10 0x62230 +#define ISP_RB_WPTR10 0x62234 +#define ISP_RB_BASE_LO11 0x62238 +#define ISP_RB_BASE_HI11 0x6223c +#define ISP_RB_SIZE11 0x62240 +#define ISP_RB_RPTR11 0x62244 +#define ISP_RB_WPTR11 0x62248 +#define ISP_RB_BASE_LO12 0x6224c +#define ISP_RB_BASE_HI12 0x62250 +#define ISP_RB_SIZE12 0x62254 +#define ISP_RB_RPTR12 0x62258 +#define ISP_RB_WPTR12 0x6225c + +#define ISP_POWER_STATUS 0x60000 + +/* ISP_SOFT_RESET */ +#define ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK 0x00000001UL + +/* ISP_CCPU_CNTL */ +#define ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK 0x00040000UL + +/* ISP_STATUS */ +#define ISP_STATUS__CCPU_REPORT_MASK 0x000000feUL + +/* ISP_SYS_INT0_STATUS */ +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK 0x00010000UL +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT10_INT_MASK 0x00040000UL +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT11_INT_MASK 0x00100000UL +#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK 0x00400000UL + +/* ISP_SYS_INT0_EN */ +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK 0x00010000UL +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT10_EN_MASK 0x00040000UL +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT11_EN_MASK 0x00100000UL +#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK 0x00400000UL + +/* ISP_SYS_INT0_ACK */ +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK 0x00010000UL +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT10_ACK_MASK 0x00040000UL +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT11_ACK_MASK 0x00100000UL +#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK 0x00400000UL + +/* Helper functions for reading isp registers */ +static inline u32 isp4hw_rreg(void __iomem *base, u32 reg) +{ + return readl(base + reg); +} + +/* Helper functions for writing isp registers */ +static inline void isp4hw_wreg(void __iomem *base, u32 reg, u32 val) +{ + return writel(val, base + reg); +} + +#endif /* _ISP4_HW_REG_H_ */ From 4c5feef6a62c22b578344891232872056415a3dd Mon Sep 17 00:00:00 2001 From: Bin Du Date: Wed, 6 May 2026 17:32:45 +0800 Subject: [PATCH 081/394] media: platform: amd: Add isp4 fw and hw interface ISP firmware controls ISP HW pipeline using dedicated embedded processor called ccpu. The communication between ISP FW and driver is using commands and response messages sent through the ring buffer. Command buffers support either global setting that is not specific to the stream and support stream specific parameters. Response buffers contain ISP FW notification information such as frame buffer done and command done. IRQ is used for receiving response buffer from ISP firmware, which is handled in the main isp4 media device. ISP ccpu is booted up through the firmware loading helper function prior to stream start. Memory used for command buffer and response buffer needs to be allocated from amdgpu buffer manager because isp4 is a child device of amdgpu. Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Signed-off-by: Sakari Ailus --- MAINTAINERS | 3 + drivers/media/platform/amd/isp4/Makefile | 3 +- .../platform/amd/isp4/isp4_fw_cmd_resp.h | 318 +++++++ .../media/platform/amd/isp4/isp4_interface.c | 833 ++++++++++++++++++ .../media/platform/amd/isp4/isp4_interface.h | 144 +++ 5 files changed, 1300 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h create mode 100644 drivers/media/platform/amd/isp4/isp4_interface.c create mode 100644 drivers/media/platform/amd/isp4/isp4_interface.h diff --git a/MAINTAINERS b/MAINTAINERS index e2561d95d251..1d19168cecaa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1164,7 +1164,10 @@ F: drivers/media/platform/amd/isp4/Kconfig F: drivers/media/platform/amd/isp4/Makefile F: drivers/media/platform/amd/isp4/isp4.c F: drivers/media/platform/amd/isp4/isp4.h +F: drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h F: drivers/media/platform/amd/isp4/isp4_hw_reg.h +F: drivers/media/platform/amd/isp4/isp4_interface.c +F: drivers/media/platform/amd/isp4/isp4_interface.h AMD KFD M: Felix Kuehling diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index 500b81ce5d14..c7eadd33fc97 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -3,4 +3,5 @@ # Copyright (C) 2025 Advanced Micro Devices, Inc. obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o -amd_isp4_capture-objs := isp4.o +amd_isp4_capture-objs := isp4.o \ + isp4_interface.o diff --git a/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h new file mode 100644 index 000000000000..88bacb00355c --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h @@ -0,0 +1,318 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_FW_CMD_RESP_H_ +#define _ISP4_FW_CMD_RESP_H_ + +/* + * Two types of command/response channel. + * Type Global Command has one command/response channel. + * Type Stream Command has one command/response channel. + *----------- ------------ + *| | --------------------------- | | + *| | ---->| Global Command |----> | | + *| | --------------------------- | | + *| | | | + *| | | | + *| | --------------------------- | | + *| | ---->| Stream Command |----> | | + *| | --------------------------- | | + *| | | | + *| | | | + *| | | | + *| HOST | | Firmware | + *| | | | + *| | | | + *| | -------------------------- | | + *| | <----| Global Response |<---- | | + *| | -------------------------- | | + *| | | | + *| | | | + *| | -------------------------- | | + *| | <----| Stream Response |<---- | | + *| | -------------------------- | | + *| | | | + *| | | | + *----------- ------------ + */ + +/* + * cmd_id is in the format of following type: + * type: indicate command type, global/stream commands. + * group: indicate the command group. + * id: A unique command identification in one type and group. + * |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->| + * | type | group | id | + */ + +#define ISP4FW_CMD_TYPE_SHIFT 24 +#define ISP4FW_CMD_GROUP_SHIFT 16 +#define ISP4FW_CMD_TYPE_STREAM_CTRL (0x2U << ISP4FW_CMD_TYPE_SHIFT) + +#define ISP4FW_CMD_GROUP_STREAM_CTRL (0x1U << ISP4FW_CMD_GROUP_SHIFT) +#define ISP4FW_CMD_GROUP_STREAM_BUFFER (0x4U << ISP4FW_CMD_GROUP_SHIFT) + +/* Stream Command */ +#define ISP4FW_CMD_ID_SET_STREAM_CONFIG (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x1) +#define ISP4FW_CMD_ID_SET_OUT_CHAN_PROP (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x3) +#define ISP4FW_CMD_ID_ENABLE_OUT_CHAN (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x5) +#define ISP4FW_CMD_ID_START_STREAM (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x7) +#define ISP4FW_CMD_ID_STOP_STREAM (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x8) + +/* Stream Buffer Command */ +#define ISP4FW_CMD_ID_SEND_BUFFER (ISP4FW_CMD_TYPE_STREAM_CTRL\ + | ISP4FW_CMD_GROUP_STREAM_BUFFER | 0x1) + +/* + * resp_id is in the format of following type: + * type: indicate command type, global/stream commands. + * group: indicate the command group. + * id: A unique command identification in one type and group. + * |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->| + * | type | group | id | + */ + +#define ISP4FW_RESP_GROUP_SHIFT 16 + +#define ISP4FW_RESP_GROUP_GENERAL (0x1 << ISP4FW_RESP_GROUP_SHIFT) +#define ISP4FW_RESP_GROUP_NOTIFICATION (0x3 << ISP4FW_RESP_GROUP_SHIFT) + +/* General Response */ +#define ISP4FW_RESP_ID_CMD_DONE (ISP4FW_RESP_GROUP_GENERAL | 0x1) + +/* Notification */ +#define ISP4FW_RESP_ID_NOTI_FRAME_DONE (ISP4FW_RESP_GROUP_NOTIFICATION | 0x1) + +#define ISP4FW_CMD_STATUS_SUCCESS 0 +#define ISP4FW_CMD_STATUS_FAIL 1 +#define ISP4FW_CMD_STATUS_SKIPPED 2 + +#define ISP4FW_ADDR_SPACE_TYPE_GPU_VA 4 + +#define ISP4FW_MEMORY_POOL_SIZE (100 * 1024 * 1024) + +/* + * standard ISP pipeline: mipicsi=>isp + */ +#define ISP4FW_MIPI0_ISP_PIPELINE_ID 0x5f91 + +enum isp4fw_sensor_id { + /* Sensor id for ISP input from MIPI port 0 */ + ISP4FW_SENSOR_ID_ON_MIPI0 = 0, +}; + +enum isp4fw_stream_id { + ISP4FW_STREAM_ID_INVALID = -1, + ISP4FW_STREAM_ID_1 = 0, + ISP4FW_STREAM_ID_2 = 1, + ISP4FW_STREAM_ID_3 = 2, + ISP4FW_STREAM_ID_MAXIMUM +}; + +enum isp4fw_image_format { + /* 4:2:0,semi-planar, 8-bit */ + ISP4FW_IMAGE_FORMAT_NV12 = 1, + /* interleave, 4:2:2, 8-bit */ + ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED = 7, +}; + +enum isp4fw_pipe_out_ch { + ISP4FW_ISP_PIPE_OUT_CH_PREVIEW = 0, +}; + +enum isp4fw_yuv_range { + ISP4FW_ISP_YUV_RANGE_FULL = 0, /* YUV value range in 0~255 */ + ISP4FW_ISP_YUV_RANGE_NARROW = 1, /* YUV value range in 16~235 */ + ISP4FW_ISP_YUV_RANGE_MAX +}; + +enum isp4fw_buffer_type { + ISP4FW_BUFFER_TYPE_PREVIEW = 8, + ISP4FW_BUFFER_TYPE_META_INFO = 10, + ISP4FW_BUFFER_TYPE_MEM_POOL = 15, +}; + +enum isp4fw_buffer_status { + /* The buffer is INVALID */ + ISP4FW_BUFFER_STATUS_INVALID, + /* The buffer is not filled with image data */ + ISP4FW_BUFFER_STATUS_SKIPPED, + /* The buffer is available and awaiting to be filled */ + ISP4FW_BUFFER_STATUS_EXIST, + /* The buffer is filled with image data */ + ISP4FW_BUFFER_STATUS_DONE, + /* The buffer is unavailable */ + ISP4FW_BUFFER_STATUS_LACK, + /* The buffer is dirty, probably caused by LMI leakage */ + ISP4FW_BUFFER_STATUS_DIRTY, + ISP4FW_BUFFER_STATUS_MAX +}; + +enum isp4fw_buffer_source { + /* The buffer is from the stream buffer queue */ + ISP4FW_BUFFER_SOURCE_STREAM, +}; + +struct isp4fw_error_code { + u32 code1; + u32 code2; + u32 code3; + u32 code4; + u32 code5; +}; + +/* Command Structure for FW */ + +struct isp4fw_cmd { + u32 cmd_seq_num; + u32 cmd_id; + u32 cmd_param[12]; + u16 cmd_stream_id; + u8 cmd_silent_resp; + u8 reserved; + u32 cmd_check_sum; +}; + +struct isp4fw_resp_cmd_done { + /* + * The host2fw command seqNum. + * To indicate which command this response refers to. + */ + u32 cmd_seq_num; + /* The host2fw command id for host double check. */ + u32 cmd_id; + /* + * Indicate the command process status. + * 0 means success. 1 means fail. 2 means skipped + */ + u16 cmd_status; + /* + * If cmd_status is 1, the command failed. The host can check + * isp4fw_error_code for details. + */ + u16 isp4fw_error_code; + /* The response payload type varies by cmd. */ + u8 payload[36]; +}; + +struct isp4fw_resp_param_package { + u32 package_addr_lo; /* The low 32 bit of the pkg address. */ + u32 package_addr_hi; /* The high 32 bit of the pkg address. */ + u32 package_size; /* The total pkg size in bytes. */ + u32 package_check_sum; /* The byte sum of the pkg. */ +}; + +struct isp4fw_resp { + u32 resp_seq_num; + u32 resp_id; + union { + struct isp4fw_resp_cmd_done cmd_done; + struct isp4fw_resp_param_package frame_done; + u32 resp_param[12]; + } param; + u8 reserved[4]; + u32 resp_check_sum; +}; + +struct isp4fw_mipi_pipe_path_cfg { + u32 b_enable; + enum isp4fw_sensor_id isp4fw_sensor_id; +}; + +struct isp4fw_isp_pipe_path_cfg { + u32 isp_pipe_id; /* pipe ids for pipeline construction */ +}; + +struct isp4fw_isp_stream_cfg { + /* Isp mipi path */ + struct isp4fw_mipi_pipe_path_cfg mipi_pipe_path_cfg; + /* Isp pipe path */ + struct isp4fw_isp_pipe_path_cfg isp_pipe_path_cfg; + /* enable TNR */ + u32 b_enable_tnr; + /* + * Number of frames for RTA processing. + * Set to 0 to use the firmware's default value. + */ + u32 rta_frames_per_proc; +}; + +struct isp4fw_image_prop { + enum isp4fw_image_format image_format; + u32 width; + u32 height; + u32 luma_pitch; + u32 chroma_pitch; + enum isp4fw_yuv_range yuv_range; +}; + +struct isp4fw_buffer { + /* + * A check num for debug usage, host can set the buf_tags + * to different numbers + */ + u32 buf_tags; + union { + u32 value; + struct { + u32 space : 16; + u32 vmid : 16; + } bit; + } vmid_space; + u32 buf_base_a_lo; /* Low address of buffer A */ + u32 buf_base_a_hi; /* High address of buffer A */ + u32 buf_size_a; /* Buffer size of buffer A */ + + u32 buf_base_b_lo; /* Low address of buffer B */ + u32 buf_base_b_hi; /* High address of buffer B */ + u32 buf_size_b; /* Buffer size of buffer B */ + + u32 buf_base_c_lo; /* Low address of buffer C */ + u32 buf_base_c_hi; /* High address of buffer C */ + u32 buf_size_c; /* Buffer size of buffer C */ +}; + +struct isp4fw_buffer_meta_info { + u32 enabled; /* enabled flag */ + enum isp4fw_buffer_status status; /* BufferStatus */ + struct isp4fw_error_code err; /* err code */ + enum isp4fw_buffer_source source; /* BufferSource */ + struct isp4fw_image_prop image_prop; /* image_prop */ + struct isp4fw_buffer buffer; /* buffer info */ +}; + +struct isp4fw_meta_info { + u32 poc; /* frame id */ + u32 fc_id; /* frame ctl id */ + u32 time_stamp_lo; /* timestamp low 32 bits */ + u32 time_stamp_hi; /* timestamp_high 32 bits */ + struct isp4fw_buffer_meta_info preview; /* preview BufferMetaInfo */ +}; + +struct isp4fw_cmd_send_buffer { + enum isp4fw_buffer_type buffer_type; + struct isp4fw_buffer buffer; /* buffer info */ +}; + +struct isp4fw_cmd_set_out_ch_prop { + enum isp4fw_pipe_out_ch ch; /* ISP output channel */ + struct isp4fw_image_prop image_prop; /* image property */ +}; + +struct isp4fw_cmd_enable_out_ch { + enum isp4fw_pipe_out_ch ch; /* ISP output channel */ + u32 is_enable; /* If channel is enabled or not */ +}; + +struct isp4fw_cmd_set_stream_cfg { + struct isp4fw_isp_stream_cfg stream_cfg; /* stream path config */ +}; + +#endif /* _ISP4_FW_CMD_RESP_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c new file mode 100644 index 000000000000..fc9ad7d91a7e --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_interface.c @@ -0,0 +1,833 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include + +#include "isp4_fw_cmd_resp.h" +#include "isp4_hw_reg.h" +#include "isp4_interface.h" + +#define ISP4IF_FW_RESP_RB_IRQ_EN_MASK \ + (ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK\ + | ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK) + +#define ISP4IF_FW_CMD_TIMEOUT (HZ / 2) + +struct isp4if_rb_config { + const char *name; + u32 index; + u32 reg_rptr; + u32 reg_wptr; + u32 reg_base_lo; + u32 reg_base_hi; + u32 reg_size; + u32 val_size; + u64 base_mc_addr; + void *base_sys_addr; +}; + +/* FW cmd ring buffer configuration */ +static struct isp4if_rb_config isp4if_cmd_rb_config[ISP4IF_STREAM_ID_MAX] = { + { + .name = "CMD_RB_GBL0", + .index = 3, + .reg_rptr = ISP_RB_RPTR4, + .reg_wptr = ISP_RB_WPTR4, + .reg_base_lo = ISP_RB_BASE_LO4, + .reg_base_hi = ISP_RB_BASE_HI4, + .reg_size = ISP_RB_SIZE4, + }, + { + .name = "CMD_RB_STR1", + .index = 0, + .reg_rptr = ISP_RB_RPTR1, + .reg_wptr = ISP_RB_WPTR1, + .reg_base_lo = ISP_RB_BASE_LO1, + .reg_base_hi = ISP_RB_BASE_HI1, + .reg_size = ISP_RB_SIZE1, + }, + { + .name = "CMD_RB_STR2", + .index = 1, + .reg_rptr = ISP_RB_RPTR2, + .reg_wptr = ISP_RB_WPTR2, + .reg_base_lo = ISP_RB_BASE_LO2, + .reg_base_hi = ISP_RB_BASE_HI2, + .reg_size = ISP_RB_SIZE2, + }, + { + .name = "CMD_RB_STR3", + .index = 2, + .reg_rptr = ISP_RB_RPTR3, + .reg_wptr = ISP_RB_WPTR3, + .reg_base_lo = ISP_RB_BASE_LO3, + .reg_base_hi = ISP_RB_BASE_HI3, + .reg_size = ISP_RB_SIZE3, + }, +}; + +/* FW resp ring buffer configuration */ +static struct isp4if_rb_config isp4if_resp_rb_config[ISP4IF_STREAM_ID_MAX] = { + { + .name = "RES_RB_GBL0", + .index = 3, + .reg_rptr = ISP_RB_RPTR12, + .reg_wptr = ISP_RB_WPTR12, + .reg_base_lo = ISP_RB_BASE_LO12, + .reg_base_hi = ISP_RB_BASE_HI12, + .reg_size = ISP_RB_SIZE12, + }, + { + .name = "RES_RB_STR1", + .index = 0, + .reg_rptr = ISP_RB_RPTR9, + .reg_wptr = ISP_RB_WPTR9, + .reg_base_lo = ISP_RB_BASE_LO9, + .reg_base_hi = ISP_RB_BASE_HI9, + .reg_size = ISP_RB_SIZE9, + }, + { + .name = "RES_RB_STR2", + .index = 1, + .reg_rptr = ISP_RB_RPTR10, + .reg_wptr = ISP_RB_WPTR10, + .reg_base_lo = ISP_RB_BASE_LO10, + .reg_base_hi = ISP_RB_BASE_HI10, + .reg_size = ISP_RB_SIZE10, + }, + { + .name = "RES_RB_STR3", + .index = 2, + .reg_rptr = ISP_RB_RPTR11, + .reg_wptr = ISP_RB_WPTR11, + .reg_base_lo = ISP_RB_BASE_LO11, + .reg_base_hi = ISP_RB_BASE_HI11, + .reg_size = ISP_RB_SIZE11, + }, +}; + +/* FW log ring buffer configuration */ +static struct isp4if_rb_config isp4if_log_rb_config = { + .name = "LOG_RB", + .index = 0, + .reg_rptr = ISP_LOG_RB_RPTR0, + .reg_wptr = ISP_LOG_RB_WPTR0, + .reg_base_lo = ISP_LOG_RB_BASE_LO0, + .reg_base_hi = ISP_LOG_RB_BASE_HI0, + .reg_size = ISP_LOG_RB_SIZE0, +}; + +static struct isp4if_gpu_mem_info * +isp4if_gpu_mem_alloc(struct isp4_interface *ispif, u32 mem_size) +{ + struct isp4if_gpu_mem_info *mem_info; + struct device *dev = ispif->dev; + int ret; + + mem_info = kmalloc_obj(*mem_info, GFP_KERNEL); + if (!mem_info) + return NULL; + + mem_info->mem_size = mem_size; + ret = isp_kernel_buffer_alloc(dev, mem_info->mem_size, + &mem_info->mem_handle, + &mem_info->gpu_mc_addr, + &mem_info->sys_addr); + if (ret) { + kfree(mem_info); + return NULL; + } + + return mem_info; +} + +static void isp4if_gpu_mem_free(struct isp4_interface *ispif, + struct isp4if_gpu_mem_info **mem_info_ptr) +{ + struct isp4if_gpu_mem_info *mem_info = *mem_info_ptr; + struct device *dev = ispif->dev; + + if (!mem_info) { + dev_err(dev, "invalid mem_info\n"); + return; + } + + *mem_info_ptr = NULL; + isp_kernel_buffer_free(&mem_info->mem_handle, &mem_info->gpu_mc_addr, + &mem_info->sys_addr); + kfree(mem_info); +} + +static void isp4if_dealloc_fw_gpumem(struct isp4_interface *ispif) +{ + isp4if_gpu_mem_free(ispif, &ispif->fw_mem_pool); + isp4if_gpu_mem_free(ispif, &ispif->fw_cmd_resp_buf); + isp4if_gpu_mem_free(ispif, &ispif->fw_log_buf); + + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) + isp4if_gpu_mem_free(ispif, &ispif->meta_info_buf[i]); +} + +static int isp4if_alloc_fw_gpumem(struct isp4_interface *ispif) +{ + struct device *dev = ispif->dev; + + ispif->fw_mem_pool = isp4if_gpu_mem_alloc(ispif, + ISP4FW_MEMORY_POOL_SIZE); + if (!ispif->fw_mem_pool) + goto error_no_memory; + + ispif->fw_cmd_resp_buf = + isp4if_gpu_mem_alloc(ispif, ISP4IF_RB_PMBMAP_MEM_SIZE); + if (!ispif->fw_cmd_resp_buf) + goto error_no_memory; + + ispif->fw_log_buf = + isp4if_gpu_mem_alloc(ispif, ISP4IF_FW_LOG_RINGBUF_SIZE); + if (!ispif->fw_log_buf) + goto error_no_memory; + + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { + ispif->meta_info_buf[i] = + isp4if_gpu_mem_alloc(ispif, ISP4IF_META_INFO_BUF_SIZE); + if (!ispif->meta_info_buf[i]) + goto error_no_memory; + } + + return 0; + +error_no_memory: + dev_err(dev, "failed to allocate gpu memory\n"); + return -ENOMEM; +} + +static u32 isp4if_compute_check_sum(const void *buf, size_t buf_size) +{ + const u8 *surplus_ptr; + const u32 *buffer; + u32 checksum = 0; + size_t i; + + buffer = (const u32 *)buf; + for (i = 0; i < buf_size / sizeof(u32); i++) + checksum += buffer[i]; + + surplus_ptr = (const u8 *)&buffer[i]; + /* add surplus data crc checksum */ + for (i = 0; i < buf_size % sizeof(u32); i++) + checksum += surplus_ptr[i]; + + return checksum; +} + +void isp4if_clear_cmdq(struct isp4_interface *ispif) +{ + struct isp4if_cmd_element *buf_node, *tmp_node; + LIST_HEAD(free_list); + + scoped_guard(spinlock, &ispif->cmdq_lock) + list_splice_init(&ispif->cmdq, &free_list); + + list_for_each_entry_safe(buf_node, tmp_node, &free_list, list) + kfree(buf_node); +} + +static bool isp4if_is_cmdq_rb_full(struct isp4_interface *ispif, + enum isp4if_stream_id stream) +{ + struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream]; + u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; + u32 len = rb_config->val_size; + u32 rd_ptr, wr_ptr; + u32 bytes_free; + + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + + /* + * Read and write pointers are equal, indicating the ring buffer + * is empty + */ + if (wr_ptr == rd_ptr) + return false; + + if (wr_ptr > rd_ptr) + bytes_free = len - (wr_ptr - rd_ptr); + else + bytes_free = rd_ptr - wr_ptr; + + /* + * Ignore one byte from the bytes free to prevent rd_ptr from equaling + * wr_ptr when the ring buffer is full, because rd_ptr == wr_ptr is + * supposed to indicate that the ring buffer is empty. + */ + return bytes_free <= sizeof(struct isp4fw_cmd); +} + +struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif, + u32 seq_num, u32 cmd_id) +{ + struct isp4if_cmd_element *ele; + + guard(spinlock)(&ispif->cmdq_lock); + + list_for_each_entry(ele, &ispif->cmdq, list) { + if (ele->seq_num == seq_num && ele->cmd_id == cmd_id) { + list_del(&ele->list); + return ele; + } + } + + return NULL; +} + +/* Must check that isp4if_is_cmdq_rb_full() == false before calling */ +static int isp4if_insert_isp_fw_cmd(struct isp4_interface *ispif, + enum isp4if_stream_id stream, + const struct isp4fw_cmd *cmd) +{ + struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream]; + u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; + void *mem_sys = rb_config->base_sys_addr; + const u32 cmd_sz = sizeof(*cmd); + struct device *dev = ispif->dev; + u32 len = rb_config->val_size; + const void *src = cmd; + u32 rd_ptr, wr_ptr; + u32 bytes_to_end; + + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + if (rd_ptr >= len || wr_ptr >= len) { + dev_err(dev, + "rb invalid: stream=%u, rd=%u, wr=%u, len=%u, cmd_sz=%u\n", + stream, rd_ptr, wr_ptr, len, cmd_sz); + return -EINVAL; + } + + bytes_to_end = len - wr_ptr; + if (bytes_to_end >= cmd_sz) { + /* FW cmd is just a straight copy to the write pointer */ + memcpy(mem_sys + wr_ptr, src, cmd_sz); + isp4hw_wreg(ispif->mmio, wreg, (wr_ptr + cmd_sz) % len); + } else { + /* + * FW cmd is split because the ring buffer needs to wrap + * around + */ + memcpy(mem_sys + wr_ptr, src, bytes_to_end); + memcpy(mem_sys, src + bytes_to_end, cmd_sz - bytes_to_end); + isp4hw_wreg(ispif->mmio, wreg, cmd_sz - bytes_to_end); + } + + return 0; +} + +static inline enum isp4if_stream_id isp4if_get_fw_stream(u32 cmd_id) +{ + return ISP4IF_STREAM_ID_1; +} + +static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, + const void *package, + u32 package_size, bool sync) +{ + enum isp4if_stream_id stream = isp4if_get_fw_stream(cmd_id); + struct isp4if_cmd_element *ele = NULL; + struct device *dev = ispif->dev; + struct isp4fw_cmd cmd; + u32 seq_num; + int ret; + + if (package_size > sizeof(cmd.cmd_param)) { + dev_err(dev, "fail pkgsize(%u) > %zu cmd:0x%x, stream %d\n", + package_size, sizeof(cmd.cmd_param), cmd_id, stream); + return -EINVAL; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = cmd_id; + switch (stream) { + case ISP4IF_STREAM_ID_GLOBAL: + cmd.cmd_stream_id = ISP4FW_STREAM_ID_INVALID; + break; + case ISP4IF_STREAM_ID_1: + cmd.cmd_stream_id = ISP4FW_STREAM_ID_1; + break; + default: + dev_err(dev, "fail bad stream id %d\n", stream); + return -EINVAL; + } + + /* Allocate the sync command object early and outside of the lock */ + if (sync) { + ele = kmalloc_obj(*ele, GFP_KERNEL); + if (!ele) + return -ENOMEM; + + /* Get two references: one for the resp thread, one for us */ + atomic_set(&ele->refcnt, 2); + init_completion(&ele->cmd_done); + } + + if (package && package_size) + memcpy(cmd.cmd_param, package, package_size); + + scoped_guard(mutex, &ispif->isp4if_mutex) { + ret = read_poll_timeout(isp4if_is_cmdq_rb_full, ret, !ret, + ISP4IF_RB_FULL_SLEEP_US, + ISP4IF_RB_FULL_TIMEOUT_US, false, ispif, + stream); + if (ret) { + struct isp4if_rb_config *rb_config = + &isp4if_resp_rb_config[stream]; + u32 rd_ptr = isp4hw_rreg(ispif->mmio, + rb_config->reg_rptr); + u32 wr_ptr = isp4hw_rreg(ispif->mmio, + rb_config->reg_wptr); + + dev_err(dev, + "fail to get free cmdq slot, stream (%d),rd %u, wr %u\n", + stream, rd_ptr, wr_ptr); + ret = -ETIMEDOUT; + goto free_ele; + } + + seq_num = ispif->host2fw_seq_num++; + cmd.cmd_seq_num = seq_num; + cmd.cmd_check_sum = isp4if_compute_check_sum(&cmd, sizeof(cmd) + - sizeof(u32)); + + /* + * only append the fw cmd to queue when its response needs to + * be waited for, currently there are only two such commands, + * disable channel and stop stream which are only sent after + * close camera + */ + if (ele) { + ele->seq_num = seq_num; + ele->cmd_id = cmd_id; + scoped_guard(spinlock, &ispif->cmdq_lock) + list_add_tail(&ele->list, &ispif->cmdq); + } + + ret = isp4if_insert_isp_fw_cmd(ispif, stream, &cmd); + if (ret) { + dev_err(dev, + "fail for insert_isp_fw_cmd cmd_id (0x%08x)\n", + cmd_id); + goto err_dequeue_ele; + } + } + + if (ele) { + ret = wait_for_completion_timeout(&ele->cmd_done, + ISP4IF_FW_CMD_TIMEOUT); + if (!ret) { + ret = -ETIMEDOUT; + goto err_dequeue_ele; + } + + ret = 0; + goto put_ele_ref; + } + + return 0; + +err_dequeue_ele: + /* + * Try to remove the command from the queue. If that fails, then it + * means the response thread is currently using the object, and we need + * to use the refcount to avoid a use-after-free by either side. + */ + if (ele && isp4if_rm_cmd_from_cmdq(ispif, seq_num, cmd_id)) + goto free_ele; + +put_ele_ref: + /* Don't free the command if we didn't put the last reference */ + if (ele && atomic_dec_return(&ele->refcnt)) + ele = NULL; + +free_ele: + /* + * The response handler or the timeout/error path must dequeue the + * command element before we own the final reference. + */ + if (ele) + INIT_LIST_HEAD(&ele->list); + kfree(ele); + return ret; +} + +static int isp4if_send_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_info *buf_info) +{ + struct isp4fw_cmd_send_buffer cmd; + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.buffer_type = ISP4FW_BUFFER_TYPE_PREVIEW; + cmd.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(buf_info->planes[0].mc_addr, + &cmd.buffer.buf_base_a_lo, + &cmd.buffer.buf_base_a_hi); + cmd.buffer.buf_size_a = buf_info->planes[0].len; + + isp4if_split_addr64(buf_info->planes[1].mc_addr, + &cmd.buffer.buf_base_b_lo, + &cmd.buffer.buf_base_b_hi); + cmd.buffer.buf_size_b = buf_info->planes[1].len; + + isp4if_split_addr64(buf_info->planes[2].mc_addr, + &cmd.buffer.buf_base_c_lo, + &cmd.buffer.buf_base_c_hi); + cmd.buffer.buf_size_c = buf_info->planes[2].len; + + return isp4if_send_fw_cmd(ispif, ISP4FW_CMD_ID_SEND_BUFFER, &cmd, + sizeof(cmd), false); +} + +static void isp4if_init_rb_config(struct isp4_interface *ispif, + struct isp4if_rb_config *rb_config) +{ + isp4hw_wreg(ispif->mmio, rb_config->reg_rptr, 0x0); + isp4hw_wreg(ispif->mmio, rb_config->reg_wptr, 0x0); + isp4hw_wreg(ispif->mmio, rb_config->reg_base_lo, + rb_config->base_mc_addr); + isp4hw_wreg(ispif->mmio, rb_config->reg_base_hi, + rb_config->base_mc_addr >> 32); + isp4hw_wreg(ispif->mmio, rb_config->reg_size, rb_config->val_size); +} + +static int isp4if_fw_init(struct isp4_interface *ispif) +{ + u32 aligned_rb_chunk_size = ISP4IF_RB_PMBMAP_MEM_CHUNK & 0xffffffc0; + struct isp4if_rb_config *rb_config; + u32 offset; + unsigned int i; + + /* initialize CMD_RB streams */ + for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) { + rb_config = (isp4if_cmd_rb_config + i); + offset = aligned_rb_chunk_size * rb_config->index; + + rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE; + rb_config->base_sys_addr = + ispif->fw_cmd_resp_buf->sys_addr + offset; + rb_config->base_mc_addr = + ispif->fw_cmd_resp_buf->gpu_mc_addr + offset; + + isp4if_init_rb_config(ispif, rb_config); + } + + /* initialize RESP_RB streams */ + for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) { + rb_config = (isp4if_resp_rb_config + i); + offset = aligned_rb_chunk_size * + (rb_config->index + ISP4IF_RESP_CHAN_TO_RB_OFFSET - 1); + + rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE; + rb_config->base_sys_addr = + ispif->fw_cmd_resp_buf->sys_addr + offset; + rb_config->base_mc_addr = + ispif->fw_cmd_resp_buf->gpu_mc_addr + offset; + + isp4if_init_rb_config(ispif, rb_config); + } + + /* initialize LOG_RB stream */ + rb_config = &isp4if_log_rb_config; + rb_config->val_size = ISP4IF_FW_LOG_RINGBUF_SIZE; + rb_config->base_mc_addr = ispif->fw_log_buf->gpu_mc_addr; + rb_config->base_sys_addr = ispif->fw_log_buf->sys_addr; + + isp4if_init_rb_config(ispif, rb_config); + + return 0; +} + +static int isp4if_wait_fw_ready(struct isp4_interface *ispif, + u32 isp_status_addr) +{ + struct device *dev = ispif->dev; + u32 timeout_ms = 100; + u32 interval_ms = 1; + u32 reg_val; + + /* wait for FW initialize done! */ + if (!read_poll_timeout(isp4hw_rreg, reg_val, reg_val + & ISP_STATUS__CCPU_REPORT_MASK, + interval_ms * 1000, timeout_ms * 1000, false, + ispif->mmio, isp_status_addr)) + return 0; + + dev_err(dev, "ISP CCPU FW boot failed\n"); + + return -ETIME; +} + +static void isp4if_enable_ccpu(struct isp4_interface *ispif) +{ + u32 reg_val; + + reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET); + reg_val &= (~ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK); + isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val); + + usleep_range(100, 150); + + reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL); + reg_val &= (~ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK); + isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val); +} + +static void isp4if_disable_ccpu(struct isp4_interface *ispif) +{ + u32 reg_val; + + reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL); + reg_val |= ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK; + isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val); + + usleep_range(100, 150); + + reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET); + reg_val |= ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK; + isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val); +} + +static int isp4if_fw_boot(struct isp4_interface *ispif) +{ + struct device *dev = ispif->dev; + + if (ispif->status != ISP4IF_STATUS_PWR_ON) { + dev_err(dev, "invalid isp power status %d\n", ispif->status); + return -EINVAL; + } + + isp4if_disable_ccpu(ispif); + + isp4if_fw_init(ispif); + + /* clear ccpu status */ + isp4hw_wreg(ispif->mmio, ISP_STATUS, 0x0); + + isp4if_enable_ccpu(ispif); + + if (isp4if_wait_fw_ready(ispif, ISP_STATUS)) { + isp4if_disable_ccpu(ispif); + return -EINVAL; + } + + /* enable interrupts */ + isp4hw_wreg(ispif->mmio, ISP_SYS_INT0_EN, + ISP4IF_FW_RESP_RB_IRQ_EN_MASK); + + ispif->status = ISP4IF_STATUS_FW_RUNNING; + + dev_dbg(dev, "ISP CCPU FW boot success\n"); + + return 0; +} + +int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, + struct isp4fw_resp *resp) +{ + struct isp4if_rb_config *rb_config = &isp4if_resp_rb_config[stream]; + u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr; + void *mem_sys = rb_config->base_sys_addr; + const u32 resp_sz = sizeof(*resp); + struct device *dev = ispif->dev; + u32 len = rb_config->val_size; + u32 rd_ptr, wr_ptr; + u32 bytes_to_end; + void *dst = resp; + u32 checksum; + + rd_ptr = isp4hw_rreg(ispif->mmio, rreg); + wr_ptr = isp4hw_rreg(ispif->mmio, wreg); + if (rd_ptr >= len || wr_ptr >= len) + goto err_rb_invalid; + + /* + * Read and write pointers are equal, indicating the ring buffer is + * empty + */ + if (rd_ptr == wr_ptr) + return -ENODATA; + + bytes_to_end = len - rd_ptr; + if (bytes_to_end >= resp_sz) { + /* FW response is just a straight copy from the read pointer */ + if (wr_ptr > rd_ptr && wr_ptr - rd_ptr < resp_sz) + goto err_rb_invalid; + + memcpy(dst, mem_sys + rd_ptr, resp_sz); + isp4hw_wreg(ispif->mmio, rreg, (rd_ptr + resp_sz) % len); + } else { + /* + * FW response is split because the ring buffer wrapped + * around + */ + if (wr_ptr > rd_ptr || wr_ptr < resp_sz - bytes_to_end) + goto err_rb_invalid; + + memcpy(dst, mem_sys + rd_ptr, bytes_to_end); + memcpy(dst + bytes_to_end, mem_sys, resp_sz - bytes_to_end); + isp4hw_wreg(ispif->mmio, rreg, resp_sz - bytes_to_end); + } + + checksum = isp4if_compute_check_sum(resp, resp_sz - sizeof(u32)); + if (checksum != resp->resp_check_sum) { + dev_err(dev, "resp checksum 0x%x,should 0x%x,rptr %u,wptr %u\n", + checksum, resp->resp_check_sum, rd_ptr, wr_ptr); + dev_err(dev, "(%u), seqNo %u, resp_id (0x%x)\n", + stream, resp->resp_seq_num, + resp->resp_id); + return -EINVAL; + } + + return 0; + +err_rb_invalid: + dev_err(dev, + "rb invalid: stream=%u, rd=%u, wr=%u, len=%u, resp_sz=%u\n", + stream, rd_ptr, wr_ptr, len, resp_sz); + return -EINVAL; +} + +int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size) +{ + return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, false); +} + +int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size) +{ + return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, true); +} + +void isp4if_clear_bufq(struct isp4_interface *ispif) +{ + struct isp4if_img_buf_node *buf_node, *tmp_node; + LIST_HEAD(free_list); + + scoped_guard(spinlock, &ispif->bufq_lock) + list_splice_init(&ispif->bufq, &free_list); + + list_for_each_entry_safe(buf_node, tmp_node, &free_list, node) + kfree(buf_node); +} + +void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node) +{ + kfree(buf_node); +} + +struct isp4if_img_buf_node * +isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info) +{ + struct isp4if_img_buf_node *node; + + node = kmalloc_obj(*node, GFP_KERNEL); + if (node) + node->buf_info = *buf_info; + + return node; +} + +struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif) +{ + struct isp4if_img_buf_node *buf_node; + + guard(spinlock)(&ispif->bufq_lock); + + buf_node = list_first_entry_or_null(&ispif->bufq, typeof(*buf_node), + node); + if (buf_node) + list_del(&buf_node->node); + + return buf_node; +} + +int isp4if_queue_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_node *buf_node) +{ + int ret; + + ret = isp4if_send_buffer(ispif, &buf_node->buf_info); + if (ret) + return ret; + + scoped_guard(spinlock, &ispif->bufq_lock) + list_add_tail(&buf_node->node, &ispif->bufq); + + return 0; +} + +int isp4if_stop(struct isp4_interface *ispif) +{ + isp4if_disable_ccpu(ispif); + + isp4if_dealloc_fw_gpumem(ispif); + + return 0; +} + +int isp4if_start(struct isp4_interface *ispif) +{ + int ret; + + ret = isp4if_alloc_fw_gpumem(ispif); + if (ret) + return ret; + + ret = isp4if_fw_boot(ispif); + if (ret) + goto failed_fw_boot; + + return 0; + +failed_fw_boot: + isp4if_dealloc_fw_gpumem(ispif); + return ret; +} + +int isp4if_deinit(struct isp4_interface *ispif) +{ + isp4if_clear_cmdq(ispif); + + isp4if_clear_bufq(ispif); + + mutex_destroy(&ispif->isp4if_mutex); + + return 0; +} + +int isp4if_init(struct isp4_interface *ispif, struct device *dev, + void __iomem *isp_mmio) +{ + ispif->dev = dev; + ispif->mmio = isp_mmio; + + spin_lock_init(&ispif->cmdq_lock); /* used for cmdq access */ + spin_lock_init(&ispif->bufq_lock); /* used for bufq access */ + mutex_init(&ispif->isp4if_mutex); /* used for commands sent to ispfw */ + + INIT_LIST_HEAD(&ispif->cmdq); + INIT_LIST_HEAD(&ispif->bufq); + + return 0; +} diff --git a/drivers/media/platform/amd/isp4/isp4_interface.h b/drivers/media/platform/amd/isp4/isp4_interface.h new file mode 100644 index 000000000000..ce3ac9b9e5cd --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_interface.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_INTERFACE_H_ +#define _ISP4_INTERFACE_H_ + +#include +#include +#include +#include + +struct isp4fw_resp; + +#define ISP4IF_RB_MAX 25 +#define ISP4IF_RESP_CHAN_TO_RB_OFFSET 9 +#define ISP4IF_RB_PMBMAP_MEM_SIZE (SZ_16M - 1) +#define ISP4IF_RB_PMBMAP_MEM_CHUNK \ + (ISP4IF_RB_PMBMAP_MEM_SIZE / (ISP4IF_RB_MAX - 1)) +#define ISP4IF_HOST2FW_COMMAND_SIZE sizeof(struct isp4fw_cmd) +#define ISP4IF_MAX_NUM_HOST2FW_COMMAND 40 +#define ISP4IF_FW_CMD_BUF_SIZE \ + (ISP4IF_MAX_NUM_HOST2FW_COMMAND * ISP4IF_HOST2FW_COMMAND_SIZE) +#define ISP4IF_RB_FULL_SLEEP_US (33 * USEC_PER_MSEC) +#define ISP4IF_RB_FULL_TIMEOUT_US (10 * ISP4IF_RB_FULL_SLEEP_US) + +#define ISP4IF_META_INFO_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000) +#define ISP4IF_MAX_STREAM_BUF_COUNT 8 + +#define ISP4IF_FW_LOG_RINGBUF_SIZE SZ_2M + +enum isp4if_stream_id { + ISP4IF_STREAM_ID_GLOBAL = 0, + ISP4IF_STREAM_ID_1 = 1, + ISP4IF_STREAM_ID_MAX = 4 +}; + +enum isp4if_status { + ISP4IF_STATUS_PWR_OFF, + ISP4IF_STATUS_PWR_ON, + ISP4IF_STATUS_FW_RUNNING, + ISP4IF_FSM_STATUS_MAX +}; + +struct isp4if_gpu_mem_info { + u64 mem_size; + u64 gpu_mc_addr; + void *sys_addr; + void *mem_handle; +}; + +struct isp4if_img_buf_info { + struct { + void *sys_addr; + u64 mc_addr; + u32 len; + } planes[3]; +}; + +struct isp4if_img_buf_node { + struct list_head node; + struct isp4if_img_buf_info buf_info; +}; + +struct isp4if_cmd_element { + struct list_head list; + u32 seq_num; + u32 cmd_id; + struct completion cmd_done; + atomic_t refcnt; +}; + +struct isp4_interface { + struct device *dev; + void __iomem *mmio; + + spinlock_t cmdq_lock; /* used for cmdq access */ + spinlock_t bufq_lock; /* used for bufq access */ + struct mutex isp4if_mutex; /* used to send fw cmd and read fw log */ + + struct list_head cmdq; /* commands sent to fw */ + struct list_head bufq; /* buffers sent to fw */ + + enum isp4if_status status; + u32 host2fw_seq_num; + + /* ISP fw buffers */ + struct isp4if_gpu_mem_info *fw_log_buf; + struct isp4if_gpu_mem_info *fw_cmd_resp_buf; + struct isp4if_gpu_mem_info *fw_mem_pool; + struct isp4if_gpu_mem_info *meta_info_buf[ISP4IF_MAX_STREAM_BUF_COUNT]; +}; + +static inline void isp4if_split_addr64(u64 addr, u32 *lo, u32 *hi) +{ + if (lo) + *lo = addr & 0xffffffff; + + if (hi) + *hi = addr >> 32; +} + +static inline u64 isp4if_join_addr64(u32 lo, u32 hi) +{ + return (((u64)hi) << 32) | (u64)lo; +} + +int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, + struct isp4fw_resp *resp); + +int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size); + +int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id, + const void *package, u32 package_size); + +struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif, + u32 seq_num, u32 cmd_id); + +void isp4if_clear_cmdq(struct isp4_interface *ispif); + +void isp4if_clear_bufq(struct isp4_interface *ispif); + +void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node); + +struct isp4if_img_buf_node * +isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info); + +struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif); + +int isp4if_queue_buffer(struct isp4_interface *ispif, + struct isp4if_img_buf_node *buf_node); + +int isp4if_stop(struct isp4_interface *ispif); + +int isp4if_start(struct isp4_interface *ispif); + +int isp4if_deinit(struct isp4_interface *ispif); + +int isp4if_init(struct isp4_interface *ispif, struct device *dev, + void __iomem *isp_mmio); + +#endif /* _ISP4_INTERFACE_H_ */ From 4e5e7a7ddb4ab9ac35928d7dc72efc8797639dc3 Mon Sep 17 00:00:00 2001 From: Bin Du Date: Wed, 6 May 2026 17:32:46 +0800 Subject: [PATCH 082/394] media: platform: amd: isp4 subdev and firmware loading handling added Isp4 sub-device is implementing v4l2 sub-device interface. It has one capture video node, and supports only preview stream. It manages firmware states, stream configuration. Add interrupt handling and notification for isp firmware to isp-subdevice. Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Signed-off-by: Sakari Ailus --- MAINTAINERS | 2 + drivers/media/platform/amd/isp4/Makefile | 3 +- drivers/media/platform/amd/isp4/isp4.c | 104 +- drivers/media/platform/amd/isp4/isp4.h | 7 +- drivers/media/platform/amd/isp4/isp4_subdev.c | 1029 +++++++++++++++++ drivers/media/platform/amd/isp4/isp4_subdev.h | 120 ++ 6 files changed, 1259 insertions(+), 6 deletions(-) create mode 100644 drivers/media/platform/amd/isp4/isp4_subdev.c create mode 100644 drivers/media/platform/amd/isp4/isp4_subdev.h diff --git a/MAINTAINERS b/MAINTAINERS index 1d19168cecaa..1e02d9967f60 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1168,6 +1168,8 @@ F: drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h F: drivers/media/platform/amd/isp4/isp4_hw_reg.h F: drivers/media/platform/amd/isp4/isp4_interface.c F: drivers/media/platform/amd/isp4/isp4_interface.h +F: drivers/media/platform/amd/isp4/isp4_subdev.c +F: drivers/media/platform/amd/isp4/isp4_subdev.h AMD KFD M: Felix Kuehling diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index c7eadd33fc97..7e4854b6a4cd 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o amd_isp4_capture-objs := isp4.o \ - isp4_interface.o + isp4_interface.o \ + isp4_subdev.o diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c index 58b21258b6d3..e62f5d652d81 100644 --- a/drivers/media/platform/amd/isp4/isp4.c +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -3,13 +3,18 @@ * Copyright (C) 2025 Advanced Micro Devices, Inc. */ +#include #include #include #include #include "isp4.h" +#include "isp4_hw_reg.h" #define ISP4_DRV_NAME "amd_isp_capture" +#define ISP4_FW_RESP_RB_IRQ_STATUS_MASK \ + (ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK | \ + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK) static const struct { const char *name; @@ -17,27 +22,103 @@ static const struct { u32 en_mask; u32 ack_mask; u32 rb_int_num; -} isp4_irq[] = { +} isp4_irq[ISP4SD_MAX_FW_RESP_STREAM_NUM] = { /* The IRQ order is aligned with the isp4_subdev.fw_resp_thread order */ { .name = "isp_irq_global", + .status_mask = + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK, + .en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK, + .ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK, .rb_int_num = 4, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT12 */ }, { .name = "isp_irq_stream1", + .status_mask = + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK, + .en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK, + .ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK, .rb_int_num = 0, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT9 */ }, }; +void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable) +{ + u32 intr_en; + + /* Synchronize ISP_SYS_INT0_EN writes with the IRQ handler's writes */ + spin_lock_irq(&isp_subdev->irq_lock); + intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN); + if (enable) + intr_en |= isp4_irq[index].en_mask; + else + intr_en &= ~isp4_irq[index].en_mask; + + isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en); + spin_unlock_irq(&isp_subdev->irq_lock); +} + +static void isp4_wake_up_resp_thread(struct isp4_subdev *isp_subdev, u32 index) +{ + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[index]; + + thread_ctx->resp_ready = true; + wake_up_interruptible(&thread_ctx->waitq); +} + static irqreturn_t isp4_irq_handler(int irq, void *arg) { + struct isp4_subdev *isp_subdev = arg; + u32 intr_ack = 0, intr_en = 0, intr_status; + int seen = 0; + + /* Get the ISP_SYS interrupt status */ + intr_status = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_STATUS); + intr_status &= ISP4_FW_RESP_RB_IRQ_STATUS_MASK; + + /* Find which ISP_SYS interrupts fired */ + for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) { + if (intr_status & isp4_irq[i].status_mask) { + intr_ack |= isp4_irq[i].ack_mask; + intr_en |= isp4_irq[i].en_mask; + seen |= BIT(i); + } + } + + /* + * Disable the ISP_SYS interrupts that fired. Must be done before waking + * the response threads, since they re-enable interrupts when finished. + * The lock synchronizes RMW of INT0_EN with isp4_enable_interrupt(). + */ + spin_lock(&isp_subdev->irq_lock); + intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN) & ~intr_en; + isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en); + spin_unlock(&isp_subdev->irq_lock); + + /* + * Clear the ISP_SYS interrupts. This must be done after the interrupts + * are disabled, so that ISP FW won't flag any new interrupts on these + * streams, and thus we don't need to clear interrupts again before + * re-enabling them in the response thread. + */ + isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_ACK, intr_ack); + + /* + * The operation `(seen >> i) << i` is logically equivalent to + * `seen &= ~BIT(i)`, with fewer instructions after compilation. + */ + for (int i; (i = ffs(seen)); seen = (seen >> i) << i) + isp4_wake_up_resp_thread(isp_subdev, i - 1); + return IRQ_HANDLED; } static int isp4_capture_probe(struct platform_device *pdev) { + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]; struct device *dev = &pdev->dev; - int irq[ARRAY_SIZE(isp4_irq)]; + struct isp4_subdev *isp_subdev; struct isp4_device *isp_dev; int ret; @@ -47,6 +128,12 @@ static int isp4_capture_probe(struct platform_device *pdev) dev->init_name = ISP4_DRV_NAME; + isp_subdev = &isp_dev->isp_subdev; + isp_subdev->mmio = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(isp_subdev->mmio)) + return dev_err_probe(dev, PTR_ERR(isp_subdev->mmio), + "isp ioremap fail\n"); + for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) { irq[i] = platform_get_irq(pdev, isp4_irq[i].rb_int_num); if (irq[i] < 0) @@ -55,7 +142,8 @@ static int isp4_capture_probe(struct platform_device *pdev) isp4_irq[i].rb_int_num); ret = devm_request_irq(dev, irq[i], isp4_irq_handler, - IRQF_NO_AUTOEN, isp4_irq[i].name, dev); + IRQF_NO_AUTOEN, isp4_irq[i].name, + isp_subdev); if (ret) return dev_err_probe(dev, ret, "fail to req irq %d\n", irq[i]); @@ -78,6 +166,13 @@ static int isp4_capture_probe(struct platform_device *pdev) pm_runtime_set_suspended(dev); pm_runtime_enable(dev); + spin_lock_init(&isp_subdev->irq_lock); + ret = isp4sd_init(&isp_dev->isp_subdev, &isp_dev->v4l2_dev, irq); + if (ret) { + dev_err_probe(dev, ret, "fail init isp4 sub dev\n"); + goto err_pm_disable; + } + ret = media_device_register(&isp_dev->mdev); if (ret) { dev_err_probe(dev, ret, "fail to register media device\n"); @@ -89,6 +184,8 @@ static int isp4_capture_probe(struct platform_device *pdev) return 0; err_isp4_deinit: + isp4sd_deinit(&isp_dev->isp_subdev); +err_pm_disable: pm_runtime_disable(dev); v4l2_device_unregister(&isp_dev->v4l2_dev); err_clean_media: @@ -103,6 +200,7 @@ static void isp4_capture_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; media_device_unregister(&isp_dev->mdev); + isp4sd_deinit(&isp_dev->isp_subdev); pm_runtime_disable(dev); v4l2_device_unregister(&isp_dev->v4l2_dev); media_device_cleanup(&isp_dev->mdev); diff --git a/drivers/media/platform/amd/isp4/isp4.h b/drivers/media/platform/amd/isp4/isp4.h index 7f2db0dfa2d9..2db6683d6d8b 100644 --- a/drivers/media/platform/amd/isp4/isp4.h +++ b/drivers/media/platform/amd/isp4/isp4.h @@ -6,12 +6,15 @@ #ifndef _ISP4_H_ #define _ISP4_H_ -#include -#include +#include +#include "isp4_subdev.h" struct isp4_device { struct v4l2_device v4l2_dev; + struct isp4_subdev isp_subdev; struct media_device mdev; }; +void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable); + #endif /* _ISP4_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c new file mode 100644 index 000000000000..6d571b7f8840 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -0,0 +1,1029 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include +#include + +#include "isp4.h" +#include "isp4_fw_cmd_resp.h" +#include "isp4_interface.h" + +#define ISP4SD_MIN_BUF_CNT_BEF_START_STREAM 4 + +#define ISP4SD_PERFORMANCE_STATE_LOW 0 +#define ISP4SD_PERFORMANCE_STATE_HIGH 1 + +/* align 32KB */ +#define ISP4SD_META_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000) + +#define to_isp4_subdev(sd) container_of(sd, struct isp4_subdev, sdev) + +static const char *isp4sd_entity_name = "amd isp4"; + +static const char *isp4sd_thread_name[ISP4SD_MAX_FW_RESP_STREAM_NUM] = { + "amd_isp4_thread_global", + "amd_isp4_thread_stream1", +}; + +static void isp4sd_module_enable(struct isp4_subdev *isp_subdev, bool enable) +{ + if (isp_subdev->enable_gpio) { + gpiod_set_value(isp_subdev->enable_gpio, enable ? 1 : 0); + dev_dbg(isp_subdev->dev, "%s isp_subdev module\n", + enable ? "enable" : "disable"); + } +} + +static int isp4sd_setup_fw_mem_pool(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_send_buffer buf_type; + struct device *dev = isp_subdev->dev; + int ret; + + if (!ispif->fw_mem_pool) { + dev_err(dev, "fail to alloc mem pool\n"); + return -ENOMEM; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&buf_type, 0, sizeof(buf_type)); + buf_type.buffer_type = ISP4FW_BUFFER_TYPE_MEM_POOL; + buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(ispif->fw_mem_pool->gpu_mc_addr, + &buf_type.buffer.buf_base_a_lo, + &buf_type.buffer.buf_base_a_hi); + buf_type.buffer.buf_size_a = ispif->fw_mem_pool->mem_size; + + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, + &buf_type, sizeof(buf_type)); + if (ret) { + dev_err(dev, "send fw mem pool 0x%llx(%u) fail %d\n", + ispif->fw_mem_pool->gpu_mc_addr, + buf_type.buffer.buf_size_a, ret); + return ret; + } + + dev_dbg(dev, "send fw mem pool 0x%llx(%u) suc\n", + ispif->fw_mem_pool->gpu_mc_addr, buf_type.buffer.buf_size_a); + + return 0; +} + +static int isp4sd_set_stream_path(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_set_stream_cfg cmd; + struct device *dev = isp_subdev->dev; + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id = + ISP4FW_SENSOR_ID_ON_MIPI0; + cmd.stream_cfg.mipi_pipe_path_cfg.b_enable = true; + cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id = + ISP4FW_MIPI0_ISP_PIPELINE_ID; + + cmd.stream_cfg.b_enable_tnr = true; + dev_dbg(dev, "isp4fw_sensor_id %d, pipeId 0x%x EnableTnr %u\n", + cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id, + cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id, + cmd.stream_cfg.b_enable_tnr); + + return isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_STREAM_CONFIG, + &cmd, sizeof(cmd)); +} + +static int isp4sd_send_meta_buf(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_send_buffer buf_type; + struct device *dev = isp_subdev->dev; + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&buf_type, 0, sizeof(buf_type)); + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { + struct isp4if_gpu_mem_info *meta_info_buf = + isp_subdev->ispif.meta_info_buf[i]; + int ret; + + if (!meta_info_buf) { + dev_err(dev, "fail for no meta info buf(%u)\n", i); + return -ENOMEM; + } + + buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO; + buf_type.buffer.vmid_space.bit.space = + ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(meta_info_buf->gpu_mc_addr, + &buf_type.buffer.buf_base_a_lo, + &buf_type.buffer.buf_base_a_hi); + buf_type.buffer.buf_size_a = meta_info_buf->mem_size; + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, + &buf_type, sizeof(buf_type)); + if (ret) { + dev_err(dev, "send meta info(%u) fail\n", i); + return ret; + } + } + + dev_dbg(dev, "send meta info suc\n"); + return 0; +} + +static bool isp4sd_get_str_out_prop(struct isp4_subdev *isp_subdev, + struct isp4fw_image_prop *out_prop, + struct v4l2_subdev_state *state, u32 pad) +{ + struct device *dev = isp_subdev->dev; + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_state_get_format(state, pad, 0); + if (!format) { + dev_err(dev, "fail get subdev state format\n"); + return false; + } + + switch (format->code) { + case MEDIA_BUS_FMT_YUYV8_1_5X8: + out_prop->image_format = ISP4FW_IMAGE_FORMAT_NV12; + out_prop->width = format->width; + out_prop->height = format->height; + out_prop->luma_pitch = format->width; + out_prop->chroma_pitch = out_prop->width; + break; + case MEDIA_BUS_FMT_YUYV8_1X16: + out_prop->image_format = ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED; + out_prop->width = format->width; + out_prop->height = format->height; + out_prop->luma_pitch = format->width * 2; + out_prop->chroma_pitch = 0; + break; + default: + dev_err(dev, "fail for bad image format:0x%x\n", + format->code); + return false; + } + + if (!out_prop->width || !out_prop->height) + return false; + + return true; +} + +static int isp4sd_kickoff_stream(struct isp4_subdev *isp_subdev, u32 w, u32 h) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + + if (sensor_info->status == ISP4SD_START_STATUS_STARTED) + return 0; + + if (sensor_info->status == ISP4SD_START_STATUS_START_FAIL) { + dev_err(dev, "fail for previous start fail\n"); + return -EINVAL; + } + + dev_dbg(dev, "w:%u,h:%u\n", w, h); + + if (isp4sd_send_meta_buf(isp_subdev)) { + dev_err(dev, "fail to send meta buf\n"); + sensor_info->status = ISP4SD_START_STATUS_START_FAIL; + return -EINVAL; + } + + sensor_info->status = ISP4SD_START_STATUS_OFF; + + if (!sensor_info->start_stream_cmd_sent && + sensor_info->buf_sent_cnt >= ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) { + int ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_START_STREAM, + NULL, 0); + if (ret) { + dev_err(dev, "fail to start stream\n"); + return ret; + } + + sensor_info->start_stream_cmd_sent = true; + } else { + dev_dbg(dev, + "no send START_STREAM, start_sent %u, buf_sent %u\n", + sensor_info->start_stream_cmd_sent, + sensor_info->buf_sent_cnt); + } + + return 0; +} + +static int isp4sd_setup_output(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_output_info *output_info = + &isp_subdev->sensor_info.output_info; + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_set_out_ch_prop cmd_ch_prop; + struct isp4fw_cmd_enable_out_ch cmd_ch_en; + struct device *dev = isp_subdev->dev; + int ret; + + if (output_info->start_status == ISP4SD_START_STATUS_STARTED) + return 0; + + if (output_info->start_status == ISP4SD_START_STATUS_START_FAIL) { + dev_err(dev, "fail for previous start fail\n"); + return -EINVAL; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd_ch_prop, 0, sizeof(cmd_ch_prop)); + cmd_ch_prop.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; + + if (!isp4sd_get_str_out_prop(isp_subdev, + &cmd_ch_prop.image_prop, state, pad)) { + dev_err(dev, "fail to get out prop\n"); + return -EINVAL; + } + + dev_dbg(dev, "channel:%d,fmt %d,w:h=%u:%u,lp:%u,cp%u\n", + cmd_ch_prop.ch, + cmd_ch_prop.image_prop.image_format, + cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height, + cmd_ch_prop.image_prop.luma_pitch, + cmd_ch_prop.image_prop.chroma_pitch); + + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_OUT_CHAN_PROP, + &cmd_ch_prop, sizeof(cmd_ch_prop)); + if (ret) { + output_info->start_status = ISP4SD_START_STATUS_START_FAIL; + dev_err(dev, "fail to set out prop\n"); + return ret; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&cmd_ch_en, 0, sizeof(cmd_ch_en)); + cmd_ch_en.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; + cmd_ch_en.is_enable = true; + ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_ENABLE_OUT_CHAN, + &cmd_ch_en, sizeof(cmd_ch_en)); + if (ret) { + output_info->start_status = ISP4SD_START_STATUS_START_FAIL; + dev_err(dev, "fail to enable channel\n"); + return ret; + } + + dev_dbg(dev, "enable channel %d\n", cmd_ch_en.ch); + + if (!sensor_info->start_stream_cmd_sent) { + ret = isp4sd_kickoff_stream(isp_subdev, + cmd_ch_prop.image_prop.width, + cmd_ch_prop.image_prop.height); + if (ret) { + dev_err(dev, "kickoff stream fail %d\n", ret); + return ret; + } + /* + * sensor_info->start_stream_cmd_sent will be set to true + * 1. in isp4sd_kickoff_stream, if app first send buffer then + * start stream + * 2. in isp_set_stream_buf, if app first start stream, then + * send buffer because ISP FW has the requirement, host needs + * to send buffer before send start stream cmd + */ + if (sensor_info->start_stream_cmd_sent) { + sensor_info->status = ISP4SD_START_STATUS_STARTED; + output_info->start_status = ISP4SD_START_STATUS_STARTED; + dev_dbg(dev, "kickoff stream suc,start cmd sent\n"); + } + } else { + dev_dbg(dev, "stream running, no need kickoff\n"); + output_info->start_status = ISP4SD_START_STATUS_STARTED; + } + + dev_dbg(dev, "setup output suc\n"); + return 0; +} + +static int isp4sd_init_stream(struct isp4_subdev *isp_subdev) +{ + struct device *dev = isp_subdev->dev; + int ret; + + ret = isp4sd_setup_fw_mem_pool(isp_subdev); + if (ret) { + dev_err(dev, "fail to setup fw mem pool\n"); + return ret; + } + + ret = isp4sd_set_stream_path(isp_subdev); + if (ret) { + dev_err(dev, "fail to setup stream path\n"); + return ret; + } + + return 0; +} + +static void isp4sd_uninit_stream(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4sd_output_info *output_info = &sensor_info->output_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_state_get_format(state, pad, 0); + if (!format) { + dev_err(isp_subdev->dev, "fail to get v4l2 format\n"); + } else { + memset(format, 0, sizeof(*format)); + format->code = MEDIA_BUS_FMT_YUYV8_1_5X8; + } + + isp4if_clear_bufq(ispif); + isp4if_clear_cmdq(ispif); + + sensor_info->start_stream_cmd_sent = false; + sensor_info->buf_sent_cnt = 0; + + sensor_info->status = ISP4SD_START_STATUS_OFF; + output_info->start_status = ISP4SD_START_STATUS_OFF; +} + +static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev, + enum isp4if_stream_id stream_id, + struct isp4fw_resp_cmd_done *para) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4if_cmd_element *ele = + isp4if_rm_cmd_from_cmdq(ispif, para->cmd_seq_num, para->cmd_id); + struct device *dev = isp_subdev->dev; + + dev_dbg(dev, "stream %d,cmd (0x%08x)(%d),seq %u, ele %p\n", + stream_id, + para->cmd_id, para->cmd_status, para->cmd_seq_num, + ele); + + if (ele) { + complete(&ele->cmd_done); + if (atomic_dec_and_test(&ele->refcnt)) + kfree(ele); + } +} + +static struct isp4fw_meta_info * +isp4sd_get_meta_by_mc(struct isp4_subdev *isp_subdev, u64 mc) +{ + for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) { + struct isp4if_gpu_mem_info *meta_info_buf = + isp_subdev->ispif.meta_info_buf[i]; + + if (meta_info_buf->gpu_mc_addr == mc) + return meta_info_buf->sys_addr; + } + + return NULL; +} + +static void isp4sd_send_meta_info(struct isp4_subdev *isp_subdev, + u64 meta_info_mc) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4fw_cmd_send_buffer buf_type; + struct device *dev = isp_subdev->dev; + + if (isp_subdev->sensor_info.status != ISP4SD_START_STATUS_STARTED) { + dev_warn(dev, "not working status %i, meta_info 0x%llx\n", + isp_subdev->sensor_info.status, meta_info_mc); + return; + } + + /* + * The struct will be shared with ISP FW, use memset() to guarantee + * padding bits are zeroed, since this is not guaranteed on all + * compilers. + */ + memset(&buf_type, 0, sizeof(buf_type)); + buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO; + buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA; + isp4if_split_addr64(meta_info_mc, + &buf_type.buffer.buf_base_a_lo, + &buf_type.buffer.buf_base_a_hi); + buf_type.buffer.buf_size_a = ISP4SD_META_BUF_SIZE; + + if (isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER, + &buf_type, sizeof(buf_type))) + dev_err(dev, "fail send meta_info 0x%llx\n", + meta_info_mc); + else + dev_dbg(dev, "resend meta_info 0x%llx\n", meta_info_mc); +} + +static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, + enum isp4if_stream_id stream_id, + struct isp4fw_resp_param_package *para) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + struct isp4if_img_buf_node *prev; + struct isp4fw_meta_info *meta; + u64 mc; + + mc = isp4if_join_addr64(para->package_addr_lo, para->package_addr_hi); + meta = isp4sd_get_meta_by_mc(isp_subdev, mc); + if (!meta) { + dev_err(dev, "fail to get meta from mc %llx\n", mc); + return; + } + + dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%i\n", + ktime_get_ns(), stream_id, meta->poc, meta->preview.enabled, + meta->preview.status); + + if (meta->preview.enabled && + (meta->preview.status == ISP4FW_BUFFER_STATUS_SKIPPED || + meta->preview.status == ISP4FW_BUFFER_STATUS_DONE || + meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) { + prev = isp4if_dequeue_buffer(ispif); + if (prev) + isp4if_dealloc_buffer_node(prev); + else + dev_err(dev, "fail null prev buf\n"); + + } else if (meta->preview.enabled) { + dev_err(dev, "fail bad preview status %u\n", + meta->preview.status); + } + + if (isp_subdev->sensor_info.status == ISP4SD_START_STATUS_STARTED) + isp4sd_send_meta_info(isp_subdev, mc); + + dev_dbg(dev, "stream_id:%d, status:%d\n", stream_id, + isp_subdev->sensor_info.status); +} + +static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev, + enum isp4if_stream_id stream_id) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + struct isp4fw_resp resp; + + while (true) { + if (isp4if_f2h_resp(ispif, stream_id, &resp)) { + /* Re-enable the interrupt */ + isp4_intr_enable(isp_subdev, stream_id, true); + /* + * Recheck to see if there is a new response. + * To ensure that an in-flight interrupt is not lost, + * enabling the interrupt must occur _before_ checking + * for a new response, hence a memory barrier is needed. + * Disable the interrupt again if there was a new + * response. + */ + mb(); + if (likely(isp4if_f2h_resp(ispif, stream_id, &resp))) + break; + + isp4_intr_enable(isp_subdev, stream_id, false); + } + + switch (resp.resp_id) { + case ISP4FW_RESP_ID_CMD_DONE: + isp4sd_fw_resp_cmd_done(isp_subdev, stream_id, + &resp.param.cmd_done); + break; + case ISP4FW_RESP_ID_NOTI_FRAME_DONE: + isp4sd_fw_resp_frame_done(isp_subdev, stream_id, + &resp.param.frame_done); + break; + default: + dev_err(dev, "-><- fail respid (0x%x)\n", + resp.resp_id); + break; + } + } +} + +static s32 isp4sd_fw_resp_thread(void *context) +{ + struct isp4_subdev_thread_param *para = context; + struct isp4_subdev *isp_subdev = para->isp_subdev; + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[para->idx]; + struct device *dev = isp_subdev->dev; + + dev_dbg(dev, "[%u] fw resp thread started\n", para->idx); + while (true) { + wait_event_interruptible(thread_ctx->waitq, + thread_ctx->resp_ready); + thread_ctx->resp_ready = false; + + if (kthread_should_stop()) { + dev_dbg(dev, "[%u] fw resp thread quit\n", para->idx); + break; + } + + isp4sd_fw_resp_func(isp_subdev, para->idx); + } + + return 0; +} + +static int isp4sd_stop_resp_proc_threads(struct isp4_subdev *isp_subdev) +{ + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) { + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[i]; + + if (thread_ctx->thread) { + kthread_stop(thread_ctx->thread); + thread_ctx->thread = NULL; + } + } + + return 0; +} + +static int isp4sd_start_resp_proc_threads(struct isp4_subdev *isp_subdev) +{ + struct device *dev = isp_subdev->dev; + + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) { + struct isp4sd_thread_handler *thread_ctx = + &isp_subdev->fw_resp_thread[i]; + + isp_subdev->isp_resp_para[i].idx = i; + isp_subdev->isp_resp_para[i].isp_subdev = isp_subdev; + init_waitqueue_head(&thread_ctx->waitq); + thread_ctx->resp_ready = false; + + thread_ctx->thread = kthread_run(isp4sd_fw_resp_thread, + &isp_subdev->isp_resp_para[i], + isp4sd_thread_name[i]); + if (IS_ERR(thread_ctx->thread)) { + dev_err(dev, "create thread [%d] fail\n", i); + thread_ctx->thread = NULL; + isp4sd_stop_resp_proc_threads(isp_subdev); + return -EINVAL; + } + } + + return 0; +} + +int isp4sd_pwroff_and_deinit(struct v4l2_subdev *sd) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_LOW; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + if (sensor_info->status == ISP4SD_START_STATUS_STARTED) { + dev_err(dev, "fail for stream still running\n"); + return -EINVAL; + } + + sensor_info->status = ISP4SD_START_STATUS_OFF; + + if (isp_subdev->irq_enabled) { + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) + disable_irq(isp_subdev->irq[i]); + isp_subdev->irq_enabled = false; + } + + isp4sd_stop_resp_proc_threads(isp_subdev); + dev_dbg(dev, "isp_subdev stop resp proc threads suc\n"); + + isp4if_stop(ispif); + + ret = dev_pm_genpd_set_performance_state(dev, perf_state); + if (ret) + dev_err(dev, + "fail to set isp_subdev performance state %u,ret %d\n", + perf_state, ret); + + /* hold ccpu reset */ + isp4hw_wreg(isp_subdev->mmio, ISP_SOFT_RESET, 0); + isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0); + ret = pm_runtime_put_sync(dev); + if (ret) + dev_err(dev, "power off isp_subdev fail %d\n", ret); + else + dev_dbg(dev, "power off isp_subdev suc\n"); + + ispif->status = ISP4IF_STATUS_PWR_OFF; + isp4if_clear_cmdq(ispif); + isp4sd_module_enable(isp_subdev, false); + + /* + * When opening the camera, isp4sd_module_enable(isp_subdev, true) is + * called. Hardware requires at least a 20ms delay between disabling + * and enabling the module, so a sleep is added to ensure ISP stability + * during quick reopen scenarios. + */ + msleep(20); + + return 0; +} + +int isp4sd_pwron_and_init(struct v4l2_subdev *sd) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + if (ispif->status == ISP4IF_STATUS_FW_RUNNING) { + dev_dbg(dev, "camera already opened, do nothing\n"); + return 0; + } + + isp4sd_module_enable(isp_subdev, true); + + if (ispif->status < ISP4IF_STATUS_PWR_ON) { + unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_HIGH; + + ret = pm_runtime_resume_and_get(dev); + if (ret) { + dev_err(dev, "fail to power on isp_subdev ret %d\n", + ret); + goto err_deinit; + } + + /* ISPPG ISP Power Status */ + isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0x7FF); + ret = dev_pm_genpd_set_performance_state(dev, perf_state); + if (ret) { + dev_err(dev, + "fail to set performance state %u, ret %d\n", + perf_state, ret); + goto err_deinit; + } + + ispif->status = ISP4IF_STATUS_PWR_ON; + } + + isp_subdev->sensor_info.start_stream_cmd_sent = false; + isp_subdev->sensor_info.buf_sent_cnt = 0; + + ret = isp4if_start(ispif); + if (ret) { + dev_err(dev, "fail to start isp_subdev interface\n"); + goto err_deinit; + } + + if (isp4sd_start_resp_proc_threads(isp_subdev)) { + dev_err(dev, "isp_start_resp_proc_threads fail\n"); + goto err_deinit; + } + + dev_dbg(dev, "create resp threads ok\n"); + + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) + enable_irq(isp_subdev->irq[i]); + isp_subdev->irq_enabled = true; + + return 0; +err_deinit: + isp4sd_pwroff_and_deinit(sd); + return -EINVAL; +} + +static int isp4sd_stop_stream(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4sd_output_info *output_info = &sensor_info->output_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + + guard(mutex)(&isp_subdev->ops_mutex); + dev_dbg(dev, "status %i\n", output_info->start_status); + + if (output_info->start_status == ISP4SD_START_STATUS_STARTED) { + struct isp4fw_cmd_enable_out_ch cmd_ch_disable; + int ret; + + /* + * The struct will be shared with ISP FW, use memset() to + * guarantee padding bits are zeroed, since this is not + * guaranteed on all compilers. + */ + memset(&cmd_ch_disable, 0, sizeof(cmd_ch_disable)); + cmd_ch_disable.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW; + /* `cmd_ch_disable.is_enable` is already false */ + ret = isp4if_send_command_sync(ispif, + ISP4FW_CMD_ID_ENABLE_OUT_CHAN, + &cmd_ch_disable, + sizeof(cmd_ch_disable)); + if (ret) + dev_err(dev, "fail to disable stream\n"); + else + dev_dbg(dev, "wait disable stream suc\n"); + + ret = isp4if_send_command_sync(ispif, ISP4FW_CMD_ID_STOP_STREAM, + NULL, 0); + if (ret) + dev_err(dev, "fail to stop stream\n"); + else + dev_dbg(dev, "wait stop stream suc\n"); + } + + isp4sd_uninit_stream(isp_subdev, state, pad); + + /* + * Return success to ensure the stop process proceeds, + * and disregard any errors since they are not fatal. + */ + return 0; +} + +static int isp4sd_start_stream(struct isp4_subdev *isp_subdev, + struct v4l2_subdev_state *state, u32 pad) +{ + struct isp4sd_output_info *output_info = + &isp_subdev->sensor_info.output_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + + if (ispif->status != ISP4IF_STATUS_FW_RUNNING) { + dev_err(dev, "fail, bad fsm %d\n", ispif->status); + return -EINVAL; + } + + switch (output_info->start_status) { + case ISP4SD_START_STATUS_OFF: + break; + case ISP4SD_START_STATUS_STARTED: + dev_dbg(dev, "stream already started, do nothing\n"); + return 0; + case ISP4SD_START_STATUS_START_FAIL: + dev_err(dev, "stream previously failed to start\n"); + return -EINVAL; + } + + ret = isp4sd_init_stream(isp_subdev); + if (ret) { + dev_err(dev, "fail to init isp_subdev stream\n"); + goto err_stop_stream; + } + + ret = isp4sd_setup_output(isp_subdev, state, pad); + if (ret) { + dev_err(dev, "fail to setup output\n"); + goto err_stop_stream; + } + + return 0; + +err_stop_stream: + isp4sd_stop_stream(isp_subdev, state, pad); + return ret; +} + +int isp4sd_ioc_send_img_buf(struct v4l2_subdev *sd, + struct isp4if_img_buf_info *buf_info) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + struct isp4_interface *ispif = &isp_subdev->ispif; + struct isp4if_img_buf_node *buf_node; + struct device *dev = isp_subdev->dev; + int ret; + + guard(mutex)(&isp_subdev->ops_mutex); + + if (ispif->status != ISP4IF_STATUS_FW_RUNNING) { + dev_err(dev, "fail send img buf for bad fsm %d\n", + ispif->status); + return -EINVAL; + } + + buf_node = isp4if_alloc_buffer_node(buf_info); + if (!buf_node) { + dev_err(dev, "fail alloc sys img buf info node\n"); + return -ENOMEM; + } + + ret = isp4if_queue_buffer(ispif, buf_node); + if (ret) { + dev_err(dev, "fail to queue image buf, %d\n", ret); + goto error_release_buf_node; + } + + if (!isp_subdev->sensor_info.start_stream_cmd_sent) { + isp_subdev->sensor_info.buf_sent_cnt++; + + if (isp_subdev->sensor_info.buf_sent_cnt >= + ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) { + ret = isp4if_send_command(ispif, + ISP4FW_CMD_ID_START_STREAM, + NULL, 0); + if (ret) { + dev_err(dev, "fail to START_STREAM"); + goto error_release_buf_node; + } + isp_subdev->sensor_info.start_stream_cmd_sent = true; + isp_subdev->sensor_info.output_info.start_status = + ISP4SD_START_STATUS_STARTED; + isp_subdev->sensor_info.status = + ISP4SD_START_STATUS_STARTED; + } else { + dev_dbg(dev, + "no send start, required %u, buf sent %u\n", + ISP4SD_MIN_BUF_CNT_BEF_START_STREAM, + isp_subdev->sensor_info.buf_sent_cnt); + } + } + + return 0; + +error_release_buf_node: + isp4if_dealloc_buffer_node(buf_node); + return ret; +} + +static const struct v4l2_subdev_video_ops isp4sd_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static int isp4sd_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct isp4sd_output_info *stream_info = + &(to_isp4_subdev(sd)->sensor_info.output_info); + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); + + if (!fmt) { + dev_err(sd->dev, "fail to get state format\n"); + return -EINVAL; + } + + *fmt = format->format; + switch (fmt->code) { + case MEDIA_BUS_FMT_YUYV8_1X16: + stream_info->image_size = fmt->width * fmt->height * 2; + break; + case MEDIA_BUS_FMT_YUYV8_1_5X8: + default: + stream_info->image_size = fmt->width * fmt->height * 3 / 2; + break; + } + + if (!stream_info->image_size) { + dev_err(sd->dev, + "fail set pad format,code 0x%x,width %u, height %u\n", + fmt->code, fmt->width, fmt->height); + return -EINVAL; + } + + dev_dbg(sd->dev, "set pad format suc, code:%x w:%u h:%u size:%u\n", + fmt->code, fmt->width, fmt->height, + stream_info->image_size); + + return 0; +} + +static int isp4sd_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + + return isp4sd_start_stream(isp_subdev, state, pad); +} + +static int isp4sd_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct isp4_subdev *isp_subdev = to_isp4_subdev(sd); + + return isp4sd_stop_stream(isp_subdev, state, pad); +} + +static const struct v4l2_subdev_pad_ops isp4sd_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = isp4sd_set_pad_format, + .enable_streams = isp4sd_enable_streams, + .disable_streams = isp4sd_disable_streams, +}; + +static const struct v4l2_subdev_ops isp4sd_subdev_ops = { + .video = &isp4sd_video_ops, + .pad = &isp4sd_pad_ops, +}; + +int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]) +{ + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info; + struct isp4_interface *ispif = &isp_subdev->ispif; + struct device *dev = v4l2_dev->dev; + int ret; + + isp_subdev->dev = dev; + v4l2_subdev_init(&isp_subdev->sdev, &isp4sd_subdev_ops); + isp_subdev->sdev.owner = THIS_MODULE; + isp_subdev->sdev.dev = dev; + snprintf(isp_subdev->sdev.name, sizeof(isp_subdev->sdev.name), "%s", + dev_name(dev)); + + isp_subdev->sdev.entity.name = isp4sd_entity_name; + isp_subdev->sdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; + isp_subdev->sdev_pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&isp_subdev->sdev.entity, 1, + &isp_subdev->sdev_pad); + if (ret) { + dev_err(dev, "fail to init isp4 subdev entity pad %d\n", ret); + return ret; + } + + ret = v4l2_subdev_init_finalize(&isp_subdev->sdev); + if (ret < 0) { + dev_err(dev, "fail to init finalize isp4 subdev %d\n", + ret); + return ret; + } + + ret = v4l2_device_register_subdev(v4l2_dev, &isp_subdev->sdev); + if (ret) { + dev_err(dev, "fail to register isp4 subdev to V4L2 device %d\n", + ret); + goto err_media_clean_up; + } + + isp4if_init(ispif, dev, isp_subdev->mmio); + + mutex_init(&isp_subdev->ops_mutex); + sensor_info->status = ISP4SD_START_STATUS_OFF; + + /* create ISP enable gpio control */ + isp_subdev->enable_gpio = devm_gpiod_get(isp_subdev->dev, + "enable_isp", + GPIOD_OUT_LOW); + if (IS_ERR(isp_subdev->enable_gpio)) { + ret = PTR_ERR(isp_subdev->enable_gpio); + dev_err(dev, "fail to get gpiod %d\n", ret); + goto err_subdev_unreg; + } + + for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) + isp_subdev->irq[i] = irq[i]; + + isp_subdev->host2fw_seq_num = 1; + ispif->status = ISP4IF_STATUS_PWR_OFF; + + return 0; + +err_subdev_unreg: + v4l2_device_unregister_subdev(&isp_subdev->sdev); +err_media_clean_up: + v4l2_subdev_cleanup(&isp_subdev->sdev); + media_entity_cleanup(&isp_subdev->sdev.entity); + return ret; +} + +void isp4sd_deinit(struct isp4_subdev *isp_subdev) +{ + struct isp4_interface *ispif = &isp_subdev->ispif; + + v4l2_device_unregister_subdev(&isp_subdev->sdev); + media_entity_cleanup(&isp_subdev->sdev.entity); + isp4if_deinit(ispif); + isp4sd_module_enable(isp_subdev, false); + + ispif->status = ISP4IF_STATUS_PWR_OFF; +} diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.h b/drivers/media/platform/amd/isp4/isp4_subdev.h new file mode 100644 index 000000000000..ceade9fec5d9 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_subdev.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_SUBDEV_H_ +#define _ISP4_SUBDEV_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "isp4_fw_cmd_resp.h" +#include "isp4_hw_reg.h" +#include "isp4_interface.h" + +/* + * One is for none sensor specific response which is not used now. + * Another is for sensor specific response + */ +#define ISP4SD_MAX_FW_RESP_STREAM_NUM 2 + +/* Indicates the ISP status */ +enum isp4sd_status { + ISP4SD_STATUS_PWR_OFF, + ISP4SD_STATUS_PWR_ON, + ISP4SD_STATUS_FW_RUNNING, + ISP4SD_STATUS_MAX +}; + +/* Indicates sensor and output stream status */ +enum isp4sd_start_status { + ISP4SD_START_STATUS_OFF, + ISP4SD_START_STATUS_STARTED, + ISP4SD_START_STATUS_START_FAIL, +}; + +struct isp4sd_img_buf_node { + struct list_head node; + struct isp4if_img_buf_info buf_info; +}; + +/* This is ISP output after processing Bayer raw sensor input */ +struct isp4sd_output_info { + enum isp4sd_start_status start_status; + u32 image_size; +}; + +/* + * Struct for sensor info used as ISP input or source. + * status: sensor status. + * output_info: ISP output after processing the sensor input. + * start_stream_cmd_sent: indicates if ISP4FW_CMD_ID_START_STREAM was sent + * to firmware. + * buf_sent_cnt: number of buffers sent to receive images. + */ +struct isp4sd_sensor_info { + struct isp4sd_output_info output_info; + enum isp4sd_start_status status; + bool start_stream_cmd_sent; + u32 buf_sent_cnt; +}; + +/* + * The thread is created by the driver to handle firmware responses which will + * be waken up when a firmware-to-driver response interrupt occurs. + */ +struct isp4sd_thread_handler { + struct task_struct *thread; + wait_queue_head_t waitq; + bool resp_ready; +}; + +struct isp4_subdev_thread_param { + u32 idx; + struct isp4_subdev *isp_subdev; +}; + +struct isp4_subdev { + struct v4l2_subdev sdev; + struct isp4_interface ispif; + + struct media_pad sdev_pad; + + enum isp4sd_status isp_status; + /* mutex used to synchronize the operation with firmware */ + struct mutex ops_mutex; + + struct isp4sd_thread_handler + fw_resp_thread[ISP4SD_MAX_FW_RESP_STREAM_NUM]; + + u32 host2fw_seq_num; + + struct isp4sd_sensor_info sensor_info; + + /* gpio descriptor */ + struct gpio_desc *enable_gpio; + struct device *dev; + void __iomem *mmio; + struct isp4_subdev_thread_param + isp_resp_para[ISP4SD_MAX_FW_RESP_STREAM_NUM]; + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]; + bool irq_enabled; + /* spin lock to access ISP_SYS_INT0_EN exclusively */ + spinlock_t irq_lock; +}; + +int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, + int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]); +void isp4sd_deinit(struct isp4_subdev *isp_subdev); +int isp4sd_ioc_send_img_buf(struct v4l2_subdev *sd, + struct isp4if_img_buf_info *buf_info); +int isp4sd_pwron_and_init(struct v4l2_subdev *sd); +int isp4sd_pwroff_and_deinit(struct v4l2_subdev *sd); + +#endif /* _ISP4_SUBDEV_H_ */ From 2ccf48af22709b90ea9419cd828c92652d7709ac Mon Sep 17 00:00:00 2001 From: Bin Du Date: Wed, 6 May 2026 17:32:47 +0800 Subject: [PATCH 083/394] media: platform: amd: isp4 video node and buffers handling added Isp video implements v4l2 video interface and supports NV12 and YUYV. It manages buffers, pipeline power and state. Cherry-picked Sultan's DMA buffer related fix from branch v6.16-drm-tip-isp4-for-amd on https://github.com/kerneltoast/kernel_x86_laptop.git Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Signed-off-by: Sakari Ailus --- MAINTAINERS | 2 + drivers/media/platform/amd/isp4/Makefile | 3 +- drivers/media/platform/amd/isp4/isp4.c | 11 + drivers/media/platform/amd/isp4/isp4_subdev.c | 13 +- drivers/media/platform/amd/isp4/isp4_subdev.h | 2 + drivers/media/platform/amd/isp4/isp4_video.c | 797 ++++++++++++++++++ drivers/media/platform/amd/isp4/isp4_video.h | 57 ++ 7 files changed, 881 insertions(+), 4 deletions(-) create mode 100644 drivers/media/platform/amd/isp4/isp4_video.c create mode 100644 drivers/media/platform/amd/isp4/isp4_video.h diff --git a/MAINTAINERS b/MAINTAINERS index 1e02d9967f60..2dc09b84a188 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1170,6 +1170,8 @@ F: drivers/media/platform/amd/isp4/isp4_interface.c F: drivers/media/platform/amd/isp4/isp4_interface.h F: drivers/media/platform/amd/isp4/isp4_subdev.c F: drivers/media/platform/amd/isp4/isp4_subdev.h +F: drivers/media/platform/amd/isp4/isp4_video.c +F: drivers/media/platform/amd/isp4/isp4_video.h AMD KFD M: Felix Kuehling diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index 7e4854b6a4cd..3fa0ee6d8a96 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o amd_isp4_capture-objs := isp4.o \ isp4_interface.o \ - isp4_subdev.o + isp4_subdev.o \ + isp4_video.o \ No newline at end of file diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c index e62f5d652d81..9480dfffbcb2 100644 --- a/drivers/media/platform/amd/isp4/isp4.c +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -173,6 +173,17 @@ static int isp4_capture_probe(struct platform_device *pdev) goto err_pm_disable; } + ret = media_create_pad_link(&isp_dev->isp_subdev.sdev.entity, + 0, + &isp_dev->isp_subdev.isp_vdev.vdev.entity, + 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err_probe(dev, ret, "fail to create pad link\n"); + goto err_isp4_deinit; + } + ret = media_device_register(&isp_dev->mdev); if (ret) { dev_err_probe(dev, ret, "fail to register media device\n"); diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c index 6d571b7f8840..5202232d50c5 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.c +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -467,11 +467,13 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, meta->preview.status == ISP4FW_BUFFER_STATUS_DONE || meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) { prev = isp4if_dequeue_buffer(ispif); - if (prev) + if (prev) { + isp4vid_handle_frame_done(&isp_subdev->isp_vdev, + &prev->buf_info); isp4if_dealloc_buffer_node(prev); - else + } else { dev_err(dev, "fail null prev buf\n"); - + } } else if (meta->preview.enabled) { dev_err(dev, "fail bad preview status %u\n", meta->preview.status); @@ -1006,6 +1008,10 @@ int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, isp_subdev->host2fw_seq_num = 1; ispif->status = ISP4IF_STATUS_PWR_OFF; + ret = isp4vid_dev_init(&isp_subdev->isp_vdev, &isp_subdev->sdev); + if (ret) + goto err_subdev_unreg; + return 0; err_subdev_unreg: @@ -1020,6 +1026,7 @@ void isp4sd_deinit(struct isp4_subdev *isp_subdev) { struct isp4_interface *ispif = &isp_subdev->ispif; + isp4vid_dev_deinit(&isp_subdev->isp_vdev); v4l2_device_unregister_subdev(&isp_subdev->sdev); media_entity_cleanup(&isp_subdev->sdev.entity); isp4if_deinit(ispif); diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.h b/drivers/media/platform/amd/isp4/isp4_subdev.h index ceade9fec5d9..ddf6bdf4a62a 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.h +++ b/drivers/media/platform/amd/isp4/isp4_subdev.h @@ -17,6 +17,7 @@ #include "isp4_fw_cmd_resp.h" #include "isp4_hw_reg.h" #include "isp4_interface.h" +#include "isp4_video.h" /* * One is for none sensor specific response which is not used now. @@ -83,6 +84,7 @@ struct isp4_subdev_thread_param { struct isp4_subdev { struct v4l2_subdev sdev; struct isp4_interface ispif; + struct isp4vid_dev isp_vdev; struct media_pad sdev_pad; diff --git a/drivers/media/platform/amd/isp4/isp4_video.c b/drivers/media/platform/amd/isp4/isp4_video.c new file mode 100644 index 000000000000..0cebb39f98e1 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_video.c @@ -0,0 +1,797 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include +#include +#include + +#include "isp4_interface.h" +#include "isp4_subdev.h" +#include "isp4_video.h" + +#define ISP4VID_ISP_DRV_NAME "amd_isp_capture" +#define ISP4VID_MAX_PREVIEW_FPS 30 +#define ISP4VID_DEFAULT_FMT V4L2_PIX_FMT_NV12 + +#define ISP4VID_PAD_VIDEO_OUTPUT 0 + +/* time perframe default */ +#define ISP4VID_ISP_TPF_DEFAULT isp4vid_tpfs[0] + +static const char *const isp4vid_video_dev_name = "Preview"; + +/* Sizes must be in increasing order */ +static const struct v4l2_frmsize_discrete isp4vid_frmsize[] = { + {640, 360}, + {640, 480}, + {1280, 720}, + {1280, 960}, + {1920, 1080}, + {1920, 1440}, + {2560, 1440}, + {2880, 1620}, + {2880, 1624}, + {2888, 1808}, +}; + +static const u32 isp4vid_formats[] = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_YUYV +}; + +/* time perframe list */ +static const struct v4l2_fract isp4vid_tpfs[] = { + { 1, ISP4VID_MAX_PREVIEW_FPS } +}; + +void isp4vid_handle_frame_done(struct isp4vid_dev *isp_vdev, + const struct isp4if_img_buf_info *img_buf) +{ + struct isp4vid_capture_buffer *isp4vid_buf; + void *vbuf; + + scoped_guard(mutex, &isp_vdev->buf_list_lock) { + isp4vid_buf = list_first_entry_or_null(&isp_vdev->buf_list, + typeof(*isp4vid_buf), + list); + if (!isp4vid_buf) + return; + + vbuf = vb2_plane_vaddr(&isp4vid_buf->vb2.vb2_buf, 0); + + if (vbuf != img_buf->planes[0].sys_addr) { + dev_err(isp_vdev->dev, "Invalid vbuf\n"); + return; + } + + list_del(&isp4vid_buf->list); + } + + /* Fill the buffer */ + isp4vid_buf->vb2.vb2_buf.timestamp = ktime_get_ns(); + isp4vid_buf->vb2.sequence = isp_vdev->sequence++; + isp4vid_buf->vb2.field = V4L2_FIELD_ANY; + + vb2_set_plane_payload(&isp4vid_buf->vb2.vb2_buf, + 0, isp_vdev->format.sizeimage); + + vb2_buffer_done(&isp4vid_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE); + + dev_dbg(isp_vdev->dev, "call vb2_buffer_done(size=%u)\n", + isp_vdev->format.sizeimage); +} + +static const struct v4l2_pix_format isp4vid_fmt_default = { + .width = 1920, + .height = 1080, + .pixelformat = ISP4VID_DEFAULT_FMT, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, +}; + +static void isp4vid_capture_return_all_buffers(struct isp4vid_dev *isp_vdev, + enum vb2_buffer_state state) +{ + struct isp4vid_capture_buffer *vbuf, *node; + + scoped_guard(mutex, &isp_vdev->buf_list_lock) { + list_for_each_entry_safe(vbuf, node, &isp_vdev->buf_list, list) + vb2_buffer_done(&vbuf->vb2.vb2_buf, state); + INIT_LIST_HEAD(&isp_vdev->buf_list); + } + + dev_dbg(isp_vdev->dev, "call vb2_buffer_done(%d)\n", state); +} + +static int isp4vid_vdev_link_validate(struct media_link *link) +{ + return 0; +} + +static const struct media_entity_operations isp4vid_vdev_ent_ops = { + .link_validate = isp4vid_vdev_link_validate, +}; + +static const struct v4l2_file_operations isp4vid_vdev_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int isp4vid_ioctl_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + strscpy(cap->driver, ISP4VID_ISP_DRV_NAME, sizeof(cap->driver)); + snprintf(cap->card, sizeof(cap->card), "%s", ISP4VID_ISP_DRV_NAME); + cap->capabilities |= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; + + dev_dbg(isp_vdev->dev, "%s|capabilities=0x%X\n", isp_vdev->vdev.name, + cap->capabilities); + + return 0; +} + +static int isp4vid_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + f->fmt.pix = isp_vdev->format; + + return 0; +} + +static int isp4vid_fill_buffer_size(struct v4l2_pix_format *fmt) +{ + int ret = 0; + + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + fmt->bytesperline = fmt->width; + fmt->sizeimage = fmt->bytesperline * fmt->height * 3 / 2; + break; + case V4L2_PIX_FMT_YUYV: + fmt->bytesperline = fmt->width * 2; + fmt->sizeimage = fmt->bytesperline * fmt->height; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int isp4vid_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + struct v4l2_pix_format *format = &f->fmt.pix; + const struct v4l2_frmsize_discrete *fsz; + size_t i; + + /* + * Check if the hardware supports the requested format, use the default + * format otherwise. + */ + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) + if (isp4vid_formats[i] == format->pixelformat) + break; + + if (i == ARRAY_SIZE(isp4vid_formats)) + format->pixelformat = ISP4VID_DEFAULT_FMT; + + switch (format->pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUYV: + fsz = v4l2_find_nearest_size(isp4vid_frmsize, + ARRAY_SIZE(isp4vid_frmsize), + width, height, format->width, + format->height); + format->width = fsz->width; + format->height = fsz->height; + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n", + isp_vdev->vdev.name, + format->pixelformat); + return -EINVAL; + } + + /* + * There is no need to check the return value, as failure will never + * happen here + */ + isp4vid_fill_buffer_size(format); + + if (format->field == V4L2_FIELD_ANY) + format->field = isp4vid_fmt_default.field; + + if (format->colorspace == V4L2_COLORSPACE_DEFAULT) + format->colorspace = isp4vid_fmt_default.colorspace; + + return 0; +} + +static int isp4vid_set_fmt_2_isp(struct v4l2_subdev *sdev, + struct v4l2_pix_format *pix_fmt) +{ + struct v4l2_subdev_format fmt = {}; + + switch (pix_fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + fmt.format.code = MEDIA_BUS_FMT_YUYV8_1_5X8; + break; + case V4L2_PIX_FMT_YUYV: + fmt.format.code = MEDIA_BUS_FMT_YUYV8_1X16; + break; + default: + return -EINVAL; + } + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.pad = ISP4VID_PAD_VIDEO_OUTPUT; + fmt.format.width = pix_fmt->width; + fmt.format.height = pix_fmt->height; + return v4l2_subdev_call(sdev, pad, set_fmt, NULL, &fmt); +} + +static int isp4vid_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + int ret; + + /* Do not change the format while stream is on */ + if (vb2_is_busy(&isp_vdev->vbq)) + return -EBUSY; + + ret = isp4vid_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + dev_dbg(isp_vdev->dev, "%s|width height:%ux%u->%ux%u\n", + isp_vdev->vdev.name, + isp_vdev->format.width, isp_vdev->format.height, + f->fmt.pix.width, f->fmt.pix.height); + dev_dbg(isp_vdev->dev, "%s|pixelformat:0x%x-0x%x\n", + isp_vdev->vdev.name, isp_vdev->format.pixelformat, + f->fmt.pix.pixelformat); + dev_dbg(isp_vdev->dev, "%s|bytesperline:%u->%u\n", + isp_vdev->vdev.name, isp_vdev->format.bytesperline, + f->fmt.pix.bytesperline); + dev_dbg(isp_vdev->dev, "%s|sizeimage:%u->%u\n", + isp_vdev->vdev.name, isp_vdev->format.sizeimage, + f->fmt.pix.sizeimage); + + isp_vdev->format = f->fmt.pix; + ret = isp4vid_set_fmt_2_isp(isp_vdev->isp_sdev, &isp_vdev->format); + + return ret; +} + +static int isp4vid_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + switch (f->index) { + case 0: + f->pixelformat = V4L2_PIX_FMT_NV12; + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_YUYV; + break; + default: + return -EINVAL; + } + + dev_dbg(isp_vdev->dev, "%s|index=%d, pixelformat=0x%X\n", + isp_vdev->vdev.name, f->index, f->pixelformat); + + return 0; +} + +static int isp4vid_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) { + if (isp4vid_formats[i] == fsize->pixel_format) + break; + } + + if (i == ARRAY_SIZE(isp4vid_formats)) + return -EINVAL; + + if (fsize->index < ARRAY_SIZE(isp4vid_frmsize)) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete = isp4vid_frmsize[fsize->index]; + dev_dbg(isp_vdev->dev, "%s|size[%d]=%dx%d\n", + isp_vdev->vdev.name, fsize->index, + fsize->discrete.width, fsize->discrete.height); + } else { + return -EINVAL; + } + + return 0; +} + +static int isp4vid_ioctl_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct isp4vid_dev *isp_vdev = video_drvdata(file); + size_t i; + + if (fival->index >= ARRAY_SIZE(isp4vid_tpfs)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) + if (isp4vid_formats[i] == fival->pixel_format) + break; + + if (i == ARRAY_SIZE(isp4vid_formats)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(isp4vid_frmsize); i++) + if (isp4vid_frmsize[i].width == fival->width && + isp4vid_frmsize[i].height == fival->height) + break; + + if (i == ARRAY_SIZE(isp4vid_frmsize)) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = isp4vid_tpfs[fival->index]; + v4l2_simplify_fraction(&fival->discrete.numerator, + &fival->discrete.denominator, 8, 333); + + dev_dbg(isp_vdev->dev, "%s|interval[%d]=%d/%d\n", + isp_vdev->vdev.name, fival->index, + fival->discrete.numerator, + fival->discrete.denominator); + + return 0; +} + +static int isp4vid_ioctl_g_param(struct file *file, void *priv, + struct v4l2_streamparm *param) +{ + struct v4l2_captureparm *capture = ¶m->parm.capture; + struct isp4vid_dev *isp_vdev = video_drvdata(file); + + if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + capture->capability = V4L2_CAP_TIMEPERFRAME; + capture->timeperframe = isp_vdev->timeperframe; + capture->readbuffers = 0; + + dev_dbg(isp_vdev->dev, "%s|timeperframe=%d/%d\n", isp_vdev->vdev.name, + capture->timeperframe.numerator, + capture->timeperframe.denominator); + + return 0; +} + +static const struct v4l2_ioctl_ops isp4vid_vdev_ioctl_ops = { + .vidioc_querycap = isp4vid_ioctl_querycap, + .vidioc_enum_fmt_vid_cap = isp4vid_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = isp4vid_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = isp4vid_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = isp4vid_try_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_g_parm = isp4vid_ioctl_g_param, + .vidioc_s_parm = isp4vid_ioctl_g_param, + .vidioc_enum_framesizes = isp4vid_enum_framesizes, + .vidioc_enum_frameintervals = isp4vid_ioctl_enum_frameintervals, +}; + +static unsigned int isp4vid_get_image_size(struct v4l2_pix_format *fmt) +{ + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + return fmt->width * fmt->height * 3 / 2; + case V4L2_PIX_FMT_YUYV: + return fmt->width * fmt->height * 2; + default: + return 0; + } +} + +static int isp4vid_qops_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + unsigned int q_num_bufs = vb2_get_num_buffers(vq); + + if (*nplanes > 1) { + dev_err(isp_vdev->dev, + "fail to setup queue, no mplane supported %u\n", + *nplanes); + return -EINVAL; + } + + if (*nplanes == 1) { + unsigned int size; + + size = isp4vid_get_image_size(&isp_vdev->format); + if (sizes[0] < size) { + dev_err(isp_vdev->dev, + "fail for small plane size %u, %u expected\n", + sizes[0], size); + return -EINVAL; + } + } + + if (q_num_bufs + *nbuffers < ISP4IF_MAX_STREAM_BUF_COUNT) + *nbuffers = ISP4IF_MAX_STREAM_BUF_COUNT - q_num_bufs; + + switch (isp_vdev->format.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_YUYV: { + *nplanes = 1; + sizes[0] = max(sizes[0], isp_vdev->format.sizeimage); + isp_vdev->format.sizeimage = sizes[0]; + } + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n", + isp_vdev->vdev.name, isp_vdev->format.pixelformat); + return -EINVAL; + } + + dev_dbg(isp_vdev->dev, "%s|*nbuffers=%u *nplanes=%u sizes[0]=%u\n", + isp_vdev->vdev.name, + *nbuffers, *nplanes, sizes[0]); + + return 0; +} + +static void isp4vid_qops_buffer_queue(struct vb2_buffer *vb) +{ + struct isp4vid_capture_buffer *buf = + container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf); + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue); + struct isp4if_img_buf_info *img_buf = &buf->img_buf; + void *vaddr = vb2_plane_vaddr(vb, 0); + + dev_dbg(isp_vdev->dev, "queue buf, vaddr %p, gpuva 0x%llx, size %u\n", + vaddr, buf->gpu_addr, vb->planes[0].length); + + switch (isp_vdev->format.pixelformat) { + case V4L2_PIX_FMT_NV12: { + u32 y_size = isp_vdev->format.sizeimage / 3 * 2; + u32 uv_size = isp_vdev->format.sizeimage / 3; + + img_buf->planes[0].len = y_size; + img_buf->planes[0].sys_addr = vaddr; + img_buf->planes[0].mc_addr = buf->gpu_addr; + + dev_dbg(isp_vdev->dev, "img_buf[0]: mc=0x%llx size=%u\n", + img_buf->planes[0].mc_addr, + img_buf->planes[0].len); + + img_buf->planes[1].len = uv_size; + img_buf->planes[1].sys_addr = vaddr + y_size; + img_buf->planes[1].mc_addr = buf->gpu_addr + y_size; + + dev_dbg(isp_vdev->dev, "img_buf[1]: mc=0x%llx size=%u\n", + img_buf->planes[1].mc_addr, + img_buf->planes[1].len); + + img_buf->planes[2].len = 0; + } + break; + case V4L2_PIX_FMT_YUYV: { + img_buf->planes[0].len = isp_vdev->format.sizeimage; + img_buf->planes[0].sys_addr = vaddr; + img_buf->planes[0].mc_addr = buf->gpu_addr; + + dev_dbg(isp_vdev->dev, "img_buf[0]: mc=0x%llx size=%u\n", + img_buf->planes[0].mc_addr, + img_buf->planes[0].len); + + img_buf->planes[1].len = 0; + img_buf->planes[2].len = 0; + } + break; + default: + dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n", + isp_vdev->vdev.name, isp_vdev->format.pixelformat); + return; + } + + if (isp_vdev->stream_started) + isp4sd_ioc_send_img_buf(isp_vdev->isp_sdev, img_buf); + + scoped_guard(mutex, &isp_vdev->buf_list_lock) + list_add_tail(&buf->list, &isp_vdev->buf_list); +} + +static int isp4vid_qops_start_streaming(struct vb2_queue *vq, + unsigned int count) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + struct isp4vid_capture_buffer *isp4vid_buf; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret = 0; + + isp_vdev->sequence = 0; + + ret = isp4sd_pwron_and_init(isp_vdev->isp_sdev); + if (ret) { + dev_err(isp_vdev->dev, "power up isp fail %d\n", ret); + goto release_buffers; + } + + entity = &isp_vdev->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, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_dbg(isp_vdev->dev, "fail start streaming: %s %d\n", + subdev->name, ret); + goto release_buffers; + } + } + + list_for_each_entry(isp4vid_buf, &isp_vdev->buf_list, list) + isp4sd_ioc_send_img_buf(isp_vdev->isp_sdev, + &isp4vid_buf->img_buf); + + isp_vdev->stream_started = true; + + return 0; + +release_buffers: + isp4vid_capture_return_all_buffers(isp_vdev, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void isp4vid_qops_stop_streaming(struct vb2_queue *vq) +{ + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq); + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret; + + entity = &isp_vdev->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 < 0 && ret != -ENOIOCTLCMD) + dev_dbg(isp_vdev->dev, "fail stop streaming: %s %d\n", + subdev->name, ret); + } + + isp_vdev->stream_started = false; + isp4sd_pwroff_and_deinit(isp_vdev->isp_sdev); + + /* Release all active buffers */ + isp4vid_capture_return_all_buffers(isp_vdev, VB2_BUF_STATE_ERROR); +} + +static int isp4vid_qops_buf_init(struct vb2_buffer *vb) +{ + struct isp4vid_capture_buffer *buf = + container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf); + struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue); + void *mem_priv = vb->planes[0].mem_priv; + struct device *dev = isp_vdev->dev; + u64 gpu_addr; + void *bo; + int ret; + + if (vb->planes[0].dbuf) { + buf->dbuf = vb->planes[0].dbuf; + } else { + /* + * HAS_DMA is a Kconfig dependency so CONFIG_HAS_DMA is always + * defined when this driver is compiled. The #else branch is + * kept as a safeguard in case the dependency is ever removed. + */ +#ifdef CONFIG_HAS_DMA + buf->dbuf = vb2_vmalloc_memops.get_dmabuf(vb, mem_priv, 0); + if (IS_ERR_OR_NULL(buf->dbuf)) { + dev_err(dev, "fail to get dma buf\n"); + return -EINVAL; + } +#else + dev_err(dev, "get dmabuf fail -- CONFIG_HAS_DMA not defined\n"); + buf->dbuf = NULL; + return -EINVAL; +#endif + } + + /* create isp user BO and obtain gpu_addr */ + ret = isp_user_buffer_alloc(dev, buf->dbuf, &bo, &gpu_addr); + if (ret) { + dev_err(dev, "fail to create isp user BO\n"); + if (!vb->planes[0].dbuf) { + dma_buf_put(buf->dbuf); + buf->dbuf = NULL; + } + + return ret; + } + + buf->bo = bo; + buf->gpu_addr = gpu_addr; + return 0; +} + +static void isp4vid_qops_buf_cleanup(struct vb2_buffer *vb) +{ + struct isp4vid_capture_buffer *buf = + container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf); + + if (buf->bo) { + isp_user_buffer_free(buf->bo); + buf->bo = NULL; + } + + /* + * Only put dmabufs we obtained ourselves via get_dmabuf, not ones + * provided by the framework for DMABUF import + */ + if (buf->dbuf && buf->dbuf != vb->planes[0].dbuf) + dma_buf_put(buf->dbuf); + + buf->dbuf = NULL; +} + +static const struct vb2_ops isp4vid_qops = { + .queue_setup = isp4vid_qops_queue_setup, + .buf_init = isp4vid_qops_buf_init, + .buf_cleanup = isp4vid_qops_buf_cleanup, + .start_streaming = isp4vid_qops_start_streaming, + .stop_streaming = isp4vid_qops_stop_streaming, + .buf_queue = isp4vid_qops_buffer_queue, +}; + +int isp4vid_dev_init(struct isp4vid_dev *isp_vdev, struct v4l2_subdev *isp_sd) +{ + const char *vdev_name = isp4vid_video_dev_name; + struct v4l2_device *v4l2_dev; + struct video_device *vdev; + struct vb2_queue *q; + int ret; + + if (!isp_vdev || !isp_sd || !isp_sd->v4l2_dev) + return -EINVAL; + + v4l2_dev = isp_sd->v4l2_dev; + vdev = &isp_vdev->vdev; + + isp_vdev->isp_sdev = isp_sd; + isp_vdev->dev = v4l2_dev->dev; + + /* Initialize the vb2_queue struct */ + mutex_init(&isp_vdev->vbq_lock); + q = &isp_vdev->vbq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->buf_struct_size = sizeof(struct isp4vid_capture_buffer); + q->min_queued_buffers = 2; + q->ops = &isp4vid_qops; + q->drv_priv = isp_vdev; + q->mem_ops = &vb2_vmalloc_memops; + q->lock = &isp_vdev->vbq_lock; + q->dev = v4l2_dev->dev; + ret = vb2_queue_init(q); + if (ret) { + dev_err(v4l2_dev->dev, "vb2_queue_init error:%d\n", ret); + return ret; + } + + /* Initialize buffer list and its lock */ + mutex_init(&isp_vdev->buf_list_lock); + INIT_LIST_HEAD(&isp_vdev->buf_list); + + /* Set default frame format */ + isp_vdev->format = isp4vid_fmt_default; + isp_vdev->timeperframe = ISP4VID_ISP_TPF_DEFAULT; + v4l2_simplify_fraction(&isp_vdev->timeperframe.numerator, + &isp_vdev->timeperframe.denominator, 8, 333); + + ret = isp4vid_fill_buffer_size(&isp_vdev->format); + if (ret) { + dev_err(v4l2_dev->dev, "fail to fill buffer size: %d\n", ret); + goto err_release_vb2_queue; + } + + ret = isp4vid_set_fmt_2_isp(isp_sd, &isp_vdev->format); + if (ret) { + dev_err(v4l2_dev->dev, "fail init format :%d\n", ret); + goto err_release_vb2_queue; + } + + /* Initialize the video_device struct */ + isp_vdev->vdev.entity.name = vdev_name; + isp_vdev->vdev.entity.function = MEDIA_ENT_F_IO_V4L; + isp_vdev->vdev_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&isp_vdev->vdev.entity, 1, + &isp_vdev->vdev_pad); + + if (ret) { + dev_err(v4l2_dev->dev, "init media entity pad fail:%d\n", ret); + goto err_release_vb2_queue; + } + + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + vdev->entity.ops = &isp4vid_vdev_ent_ops; + vdev->release = video_device_release_empty; + vdev->fops = &isp4vid_vdev_fops; + vdev->ioctl_ops = &isp4vid_vdev_ioctl_ops; + vdev->lock = NULL; + vdev->queue = q; + vdev->v4l2_dev = v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + strscpy(vdev->name, vdev_name, sizeof(vdev->name)); + video_set_drvdata(vdev, isp_vdev); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(v4l2_dev->dev, "register video device fail:%d\n", ret); + goto err_entity_cleanup; + } + + return 0; + +err_entity_cleanup: + media_entity_cleanup(&isp_vdev->vdev.entity); +err_release_vb2_queue: + vb2_queue_release(q); + return ret; +} + +void isp4vid_dev_deinit(struct isp4vid_dev *isp_vdev) +{ + vb2_video_unregister_device(&isp_vdev->vdev); +} diff --git a/drivers/media/platform/amd/isp4/isp4_video.h b/drivers/media/platform/amd/isp4/isp4_video.h new file mode 100644 index 000000000000..c66451e26166 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_video.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_VIDEO_H_ +#define _ISP4_VIDEO_H_ + +#include +#include + +#include "isp4_interface.h" + +struct isp4vid_capture_buffer { + /* + * struct vb2_v4l2_buffer must be the first element + * the videobuf2 framework will allocate this struct based on + * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of + * memory as a vb2_buffer + */ + struct vb2_v4l2_buffer vb2; + struct isp4if_img_buf_info img_buf; + struct list_head list; + struct dma_buf *dbuf; + void *bo; + u64 gpu_addr; +}; + +struct isp4vid_dev { + struct video_device vdev; + struct media_pad vdev_pad; + struct v4l2_pix_format format; + + /* mutex that protects vbq */ + struct mutex vbq_lock; + struct vb2_queue vbq; + + /* mutex that protects buf_list */ + struct mutex buf_list_lock; + struct list_head buf_list; + + u32 sequence; + bool stream_started; + + struct device *dev; + struct v4l2_subdev *isp_sdev; + struct v4l2_fract timeperframe; +}; + +int isp4vid_dev_init(struct isp4vid_dev *isp_vdev, struct v4l2_subdev *isp_sd); + +void isp4vid_dev_deinit(struct isp4vid_dev *isp_vdev); + +void isp4vid_handle_frame_done(struct isp4vid_dev *isp_vdev, + const struct isp4if_img_buf_info *img_buf); + +#endif /* _ISP4_VIDEO_H_ */ From ec4bec227b9d3796cb78af0d3d9bcdd26f0769c5 Mon Sep 17 00:00:00 2001 From: Bin Du Date: Wed, 6 May 2026 17:32:48 +0800 Subject: [PATCH 084/394] media: platform: amd: isp4 debug fs logging and more descriptive errors Add debug fs for isp4 driver and add more detailed descriptive error info to some of the log message Co-developed-by: Sultan Alsawaf Signed-off-by: Sultan Alsawaf Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Signed-off-by: Sakari Ailus --- MAINTAINERS | 2 + drivers/media/platform/amd/isp4/Makefile | 3 +- drivers/media/platform/amd/isp4/isp4.c | 4 + drivers/media/platform/amd/isp4/isp4_debug.c | 271 ++++++++++++++++++ drivers/media/platform/amd/isp4/isp4_debug.h | 41 +++ .../media/platform/amd/isp4/isp4_interface.c | 25 +- drivers/media/platform/amd/isp4/isp4_subdev.c | 29 +- drivers/media/platform/amd/isp4/isp4_subdev.h | 5 + 8 files changed, 360 insertions(+), 20 deletions(-) create mode 100644 drivers/media/platform/amd/isp4/isp4_debug.c create mode 100644 drivers/media/platform/amd/isp4/isp4_debug.h diff --git a/MAINTAINERS b/MAINTAINERS index 2dc09b84a188..b92e9625d84c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1164,6 +1164,8 @@ F: drivers/media/platform/amd/isp4/Kconfig F: drivers/media/platform/amd/isp4/Makefile F: drivers/media/platform/amd/isp4/isp4.c F: drivers/media/platform/amd/isp4/isp4.h +F: drivers/media/platform/amd/isp4/isp4_debug.c +F: drivers/media/platform/amd/isp4/isp4_debug.h F: drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h F: drivers/media/platform/amd/isp4/isp4_hw_reg.h F: drivers/media/platform/amd/isp4/isp4_interface.c diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile index 3fa0ee6d8a96..3849062e17f3 100644 --- a/drivers/media/platform/amd/isp4/Makefile +++ b/drivers/media/platform/amd/isp4/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o amd_isp4_capture-objs := isp4.o \ + isp4_debug.o \ isp4_interface.o \ isp4_subdev.o \ - isp4_video.o \ No newline at end of file + isp4_video.o diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c index 9480dfffbcb2..bf6b8e26c2c0 100644 --- a/drivers/media/platform/amd/isp4/isp4.c +++ b/drivers/media/platform/amd/isp4/isp4.c @@ -9,6 +9,7 @@ #include #include "isp4.h" +#include "isp4_debug.h" #include "isp4_hw_reg.h" #define ISP4_DRV_NAME "amd_isp_capture" @@ -191,6 +192,7 @@ static int isp4_capture_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, isp_dev); + isp_debugfs_create(isp_dev); return 0; @@ -210,6 +212,8 @@ static void isp4_capture_remove(struct platform_device *pdev) struct isp4_device *isp_dev = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + isp_debugfs_remove(isp_dev); + media_device_unregister(&isp_dev->mdev); isp4sd_deinit(&isp_dev->isp_subdev); pm_runtime_disable(dev); diff --git a/drivers/media/platform/amd/isp4/isp4_debug.c b/drivers/media/platform/amd/isp4/isp4_debug.c new file mode 100644 index 000000000000..2fc00fc9a194 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_debug.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include "isp4.h" +#include "isp4_debug.h" +#include "isp4_hw_reg.h" +#include "isp4_interface.h" + +#define ISP4DBG_FW_LOG_RINGBUF_SIZE (2 * 1024 * 1024) +#define ISP4DBG_MACRO_2_STR(X) #X +#define ISP4DBG_ONE_TIME_LOG_LEN 510 + +#ifdef CONFIG_DEBUG_FS + +void isp_debugfs_create(struct isp4_device *isp_dev) +{ + isp_dev->isp_subdev.debugfs_dir = debugfs_create_dir("amd_isp4", NULL); + debugfs_create_bool("fw_log_enable", 0644, + isp_dev->isp_subdev.debugfs_dir, + &isp_dev->isp_subdev.enable_fw_log); + isp_dev->isp_subdev.fw_log_output = + devm_kzalloc(isp_dev->isp_subdev.dev, + ISP4DBG_FW_LOG_RINGBUF_SIZE + 32, + GFP_KERNEL); +} + +void isp_debugfs_remove(struct isp4_device *isp_dev) +{ + debugfs_remove_recursive(isp_dev->isp_subdev.debugfs_dir); + isp_dev->isp_subdev.debugfs_dir = NULL; +} + +static u32 isp_fw_fill_rb_log(struct isp4_subdev *isp, void *sys, u32 rb_size) +{ + struct isp4_interface *ispif = &isp->ispif; + char *buf = isp->fw_log_output; + struct device *dev = isp->dev; + u32 rd_ptr, wr_ptr; + u32 total_cnt = 0; + u32 offset = 0; + u32 cnt; + + if (!sys || !rb_size) + return 0; + + guard(mutex)(&ispif->isp4if_mutex); + + rd_ptr = isp4hw_rreg(isp->mmio, ISP_LOG_RB_RPTR0); + wr_ptr = isp4hw_rreg(isp->mmio, ISP_LOG_RB_WPTR0); + + do { + if (wr_ptr > rd_ptr) + cnt = wr_ptr - rd_ptr; + else if (wr_ptr < rd_ptr) + cnt = rb_size - rd_ptr; + else + goto quit; + + if (cnt > rb_size) { + dev_err(dev, "fail bad fw log size %u\n", cnt); + goto quit; + } + + memcpy(buf + offset, sys + rd_ptr, cnt); + + offset += cnt; + total_cnt += cnt; + rd_ptr = (rd_ptr + cnt) % rb_size; + } while (rd_ptr < wr_ptr); + + isp4hw_wreg(isp->mmio, ISP_LOG_RB_RPTR0, rd_ptr); + +quit: + return total_cnt; +} + +void isp_fw_log_print(struct isp4_subdev *isp) +{ + struct isp4_interface *ispif = &isp->ispif; + char *fw_log_buf = isp->fw_log_output; + u32 cnt; + + if (!isp->enable_fw_log || !fw_log_buf) + return; + + cnt = isp_fw_fill_rb_log(isp, ispif->fw_log_buf->sys_addr, + ispif->fw_log_buf->mem_size); + + if (cnt) { + char temp_ch; + char *str; + char *end; + /* line end */ + char *le; + + str = (char *)fw_log_buf; + end = ((char *)fw_log_buf + cnt); + fw_log_buf[cnt] = 0; + + while (str < end) { + le = strchr(str, 0x0A); + if ((le && str + ISP4DBG_ONE_TIME_LOG_LEN >= le) || + (!le && str + ISP4DBG_ONE_TIME_LOG_LEN >= end)) { + if (le) + *le = 0; + + if (*str != '\0') + dev_dbg(isp->dev, "%s", str); + + if (le) { + *le = 0x0A; + str = le + 1; + } else { + break; + } + } else { + u32 tmp_len = ISP4DBG_ONE_TIME_LOG_LEN; + + temp_ch = str[tmp_len]; + str[tmp_len] = 0; + dev_dbg(isp->dev, "%s", str); + str[tmp_len] = temp_ch; + str = &str[tmp_len]; + } + } + } +} +#endif + +char *isp4dbg_get_buf_src_str(u32 src) +{ + switch (src) { + case ISP4FW_BUFFER_SOURCE_STREAM: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_SOURCE_STREAM); + default: + return "Unknown buf source"; + } +} + +char *isp4dbg_get_buf_done_str(u32 status) +{ + switch (status) { + case ISP4FW_BUFFER_STATUS_INVALID: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_INVALID); + case ISP4FW_BUFFER_STATUS_SKIPPED: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_SKIPPED); + case ISP4FW_BUFFER_STATUS_EXIST: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_EXIST); + case ISP4FW_BUFFER_STATUS_DONE: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_DONE); + case ISP4FW_BUFFER_STATUS_LACK: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_LACK); + case ISP4FW_BUFFER_STATUS_DIRTY: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_DIRTY); + case ISP4FW_BUFFER_STATUS_MAX: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_MAX); + default: + return "Unknown Buf Done Status"; + } +} + +char *isp4dbg_get_img_fmt_str(int fmt /* enum isp4fw_image_format * */) +{ + switch (fmt) { + case ISP4FW_IMAGE_FORMAT_NV12: + return "NV12"; + case ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED: + return "YUV422INTERLEAVED"; + default: + return "unknown fmt"; + } +} + +void isp4dbg_show_bufmeta_info(struct device *dev, char *pre, + void *in, void *orig_buf) +{ + struct isp4fw_buffer_meta_info *p; + struct isp4if_img_buf_info *orig; + + if (!in) + return; + + if (!pre) + pre = ""; + + p = in; + orig = orig_buf; + + dev_dbg(dev, "%s(%s) en:%d,stat:%s(%u),src:%s\n", pre, + isp4dbg_get_img_fmt_str(p->image_prop.image_format), + p->enabled, isp4dbg_get_buf_done_str(p->status), p->status, + isp4dbg_get_buf_src_str(p->source)); + + dev_dbg(dev, "%p,0x%llx(%u) %p,0x%llx(%u) %p,0x%llx(%u)\n", + orig->planes[0].sys_addr, orig->planes[0].mc_addr, + orig->planes[0].len, orig->planes[1].sys_addr, + orig->planes[1].mc_addr, orig->planes[1].len, + orig->planes[2].sys_addr, orig->planes[2].mc_addr, + orig->planes[2].len); +} + +char *isp4dbg_get_buf_type(u32 type) +{ + /* enum isp4fw_buffer_type */ + switch (type) { + case ISP4FW_BUFFER_TYPE_PREVIEW: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_PREVIEW); + case ISP4FW_BUFFER_TYPE_META_INFO: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_META_INFO); + case ISP4FW_BUFFER_TYPE_MEM_POOL: + return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_MEM_POOL); + default: + return "unknown type"; + } +} + +char *isp4dbg_get_cmd_str(u32 cmd) +{ + switch (cmd) { + case ISP4FW_CMD_ID_START_STREAM: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_START_STREAM); + case ISP4FW_CMD_ID_STOP_STREAM: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_STOP_STREAM); + case ISP4FW_CMD_ID_SEND_BUFFER: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SEND_BUFFER); + case ISP4FW_CMD_ID_SET_STREAM_CONFIG: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SET_STREAM_CONFIG); + case ISP4FW_CMD_ID_SET_OUT_CHAN_PROP: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SET_OUT_CHAN_PROP); + case ISP4FW_CMD_ID_ENABLE_OUT_CHAN: + return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_ENABLE_OUT_CHAN); + default: + return "unknown cmd"; + } +} + +char *isp4dbg_get_resp_str(u32 cmd) +{ + switch (cmd) { + case ISP4FW_RESP_ID_CMD_DONE: + return ISP4DBG_MACRO_2_STR(ISP4FW_RESP_ID_CMD_DONE); + case ISP4FW_RESP_ID_NOTI_FRAME_DONE: + return ISP4DBG_MACRO_2_STR(ISP4FW_RESP_ID_NOTI_FRAME_DONE); + default: + return "unknown respid"; + } +} + +char *isp4dbg_get_if_stream_str(u32 stream /* enum fw_cmd_resp_stream_id */) +{ + switch (stream) { + case ISP4IF_STREAM_ID_GLOBAL: + return "STREAM_GLOBAL"; + case ISP4IF_STREAM_ID_1: + return "STREAM1"; + default: + return "unknown streamID"; + } +} + +char *isp4dbg_get_out_ch_str(int ch /* enum isp4fw_pipe_out_ch */) +{ + switch ((enum isp4fw_pipe_out_ch)ch) { + case ISP4FW_ISP_PIPE_OUT_CH_PREVIEW: + return "prev"; + default: + return "unknown channel"; + } +} diff --git a/drivers/media/platform/amd/isp4/isp4_debug.h b/drivers/media/platform/amd/isp4/isp4_debug.h new file mode 100644 index 000000000000..d1262e03ae64 --- /dev/null +++ b/drivers/media/platform/amd/isp4/isp4_debug.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _ISP4_DEBUG_H_ +#define _ISP4_DEBUG_H_ + +#include +#include + +#include "isp4_subdev.h" + +#ifdef CONFIG_DEBUG_FS +struct isp4_device; + +void isp_debugfs_create(struct isp4_device *isp_dev); +void isp_debugfs_remove(struct isp4_device *isp_dev); +void isp_fw_log_print(struct isp4_subdev *isp); + +#else + +/* to avoid checkpatch warning */ +#define isp_debugfs_create(cam) ((void)(cam)) +#define isp_debugfs_remove(cam) ((void)(cam)) +#define isp_fw_log_print(isp) ((void)(isp)) + +#endif /* CONFIG_DEBUG_FS */ + +void isp4dbg_show_bufmeta_info(struct device *dev, char *pre, void *p, + void *orig_buf /* struct sys_img_buf_handle */); +char *isp4dbg_get_img_fmt_str(int fmt /* enum _image_format_t */); +char *isp4dbg_get_out_ch_str(int ch /* enum _isp_pipe_out_ch_t */); +char *isp4dbg_get_cmd_str(u32 cmd); +char *isp4dbg_get_buf_type(u32 type);/* enum _buffer_type_t */ +char *isp4dbg_get_resp_str(u32 resp); +char *isp4dbg_get_buf_src_str(u32 src); +char *isp4dbg_get_buf_done_str(u32 status); +char *isp4dbg_get_if_stream_str(u32 stream); + +#endif /* _ISP4_DEBUG_H_ */ diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c index fc9ad7d91a7e..15f14eddd683 100644 --- a/drivers/media/platform/amd/isp4/isp4_interface.c +++ b/drivers/media/platform/amd/isp4/isp4_interface.c @@ -5,6 +5,7 @@ #include +#include "isp4_debug.h" #include "isp4_fw_cmd_resp.h" #include "isp4_hw_reg.h" #include "isp4_interface.h" @@ -302,8 +303,9 @@ static int isp4if_insert_isp_fw_cmd(struct isp4_interface *ispif, wr_ptr = isp4hw_rreg(ispif->mmio, wreg); if (rd_ptr >= len || wr_ptr >= len) { dev_err(dev, - "rb invalid: stream=%u, rd=%u, wr=%u, len=%u, cmd_sz=%u\n", - stream, rd_ptr, wr_ptr, len, cmd_sz); + "rb invalid: stream=%u(%s), rd=%u, wr=%u, len=%u, cmd_sz=%u\n", + stream, isp4dbg_get_if_stream_str(stream), rd_ptr, + wr_ptr, len, cmd_sz); return -EINVAL; } @@ -394,8 +396,9 @@ static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, rb_config->reg_wptr); dev_err(dev, - "fail to get free cmdq slot, stream (%d),rd %u, wr %u\n", - stream, rd_ptr, wr_ptr); + "failed to get free cmdq slot, stream %s(%d),rd %u, wr %u\n", + isp4dbg_get_if_stream_str(stream), stream, + rd_ptr, wr_ptr); ret = -ETIMEDOUT; goto free_ele; } @@ -421,8 +424,8 @@ static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, ret = isp4if_insert_isp_fw_cmd(ispif, stream, &cmd); if (ret) { dev_err(dev, - "fail for insert_isp_fw_cmd cmd_id (0x%08x)\n", - cmd_id); + "fail for insert_isp_fw_cmd cmd_id %s(0x%08x)\n", + isp4dbg_get_cmd_str(cmd_id), cmd_id); goto err_dequeue_ele; } } @@ -692,8 +695,9 @@ int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, if (checksum != resp->resp_check_sum) { dev_err(dev, "resp checksum 0x%x,should 0x%x,rptr %u,wptr %u\n", checksum, resp->resp_check_sum, rd_ptr, wr_ptr); - dev_err(dev, "(%u), seqNo %u, resp_id (0x%x)\n", - stream, resp->resp_seq_num, + dev_err(dev, "%s(%u), seqNo %u, resp_id %s(0x%x)\n", + isp4dbg_get_if_stream_str(stream), stream, + resp->resp_seq_num, isp4dbg_get_resp_str(resp->resp_id), resp->resp_id); return -EINVAL; } @@ -702,8 +706,9 @@ int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream, err_rb_invalid: dev_err(dev, - "rb invalid: stream=%u, rd=%u, wr=%u, len=%u, resp_sz=%u\n", - stream, rd_ptr, wr_ptr, len, resp_sz); + "rb invalid: stream=%u(%s), rd=%u, wr=%u, len=%u, resp_sz=%u\n", + stream, isp4dbg_get_if_stream_str(stream), rd_ptr, wr_ptr, len, + resp_sz); return -EINVAL; } diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c index 5202232d50c5..48deea79ce6c 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.c +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -7,6 +7,7 @@ #include #include "isp4.h" +#include "isp4_debug.h" #include "isp4_fw_cmd_resp.h" #include "isp4_interface.h" @@ -263,9 +264,9 @@ static int isp4sd_setup_output(struct isp4_subdev *isp_subdev, return -EINVAL; } - dev_dbg(dev, "channel:%d,fmt %d,w:h=%u:%u,lp:%u,cp%u\n", - cmd_ch_prop.ch, - cmd_ch_prop.image_prop.image_format, + dev_dbg(dev, "channel:%s,fmt %s,w:h=%u:%u,lp:%u,cp%u\n", + isp4dbg_get_out_ch_str(cmd_ch_prop.ch), + isp4dbg_get_img_fmt_str(cmd_ch_prop.image_prop.image_format), cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height, cmd_ch_prop.image_prop.luma_pitch, cmd_ch_prop.image_prop.chroma_pitch); @@ -294,7 +295,8 @@ static int isp4sd_setup_output(struct isp4_subdev *isp_subdev, return ret; } - dev_dbg(dev, "enable channel %d\n", cmd_ch_en.ch); + dev_dbg(dev, "enable channel %s\n", + isp4dbg_get_out_ch_str(cmd_ch_en.ch)); if (!sensor_info->start_stream_cmd_sent) { ret = isp4sd_kickoff_stream(isp_subdev, @@ -381,8 +383,9 @@ static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev, isp4if_rm_cmd_from_cmdq(ispif, para->cmd_seq_num, para->cmd_id); struct device *dev = isp_subdev->dev; - dev_dbg(dev, "stream %d,cmd (0x%08x)(%d),seq %u, ele %p\n", + dev_dbg(dev, "stream %d,cmd %s(0x%08x)(%d),seq %u, ele %p\n", stream_id, + isp4dbg_get_cmd_str(para->cmd_id), para->cmd_id, para->cmd_status, para->cmd_seq_num, ele); @@ -458,8 +461,9 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, return; } - dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%i\n", + dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%s(%i)\n", ktime_get_ns(), stream_id, meta->poc, meta->preview.enabled, + isp4dbg_get_buf_done_str(meta->preview.status), meta->preview.status); if (meta->preview.enabled && @@ -468,6 +472,8 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) { prev = isp4if_dequeue_buffer(ispif); if (prev) { + isp4dbg_show_bufmeta_info(dev, "prev", &meta->preview, + &prev->buf_info); isp4vid_handle_frame_done(&isp_subdev->isp_vdev, &prev->buf_info); isp4if_dealloc_buffer_node(prev); @@ -475,8 +481,9 @@ static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev, dev_err(dev, "fail null prev buf\n"); } } else if (meta->preview.enabled) { - dev_err(dev, "fail bad preview status %u\n", - meta->preview.status); + dev_err(dev, "fail bad preview status %u(%s)\n", + meta->preview.status, + isp4dbg_get_buf_done_str(meta->preview.status)); } if (isp_subdev->sensor_info.status == ISP4SD_START_STATUS_STARTED) @@ -493,6 +500,9 @@ static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev, struct device *dev = isp_subdev->dev; struct isp4fw_resp resp; + if (stream_id == ISP4IF_STREAM_ID_1) + isp_fw_log_print(isp_subdev); + while (true) { if (isp4if_f2h_resp(ispif, stream_id, &resp)) { /* Re-enable the interrupt */ @@ -522,7 +532,8 @@ static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev, &resp.param.frame_done); break; default: - dev_err(dev, "-><- fail respid (0x%x)\n", + dev_err(dev, "-><- fail respid %s(0x%x)\n", + isp4dbg_get_resp_str(resp.resp_id), resp.resp_id); break; } diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.h b/drivers/media/platform/amd/isp4/isp4_subdev.h index ddf6bdf4a62a..20ea08a830af 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.h +++ b/drivers/media/platform/amd/isp4/isp4_subdev.h @@ -109,6 +109,11 @@ struct isp4_subdev { bool irq_enabled; /* spin lock to access ISP_SYS_INT0_EN exclusively */ spinlock_t irq_lock; +#ifdef CONFIG_DEBUG_FS + bool enable_fw_log; + struct dentry *debugfs_dir; + char *fw_log_output; +#endif }; int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev, From 3cd9b7011519c3fffffb7b6752fc7603be52dc1d Mon Sep 17 00:00:00 2001 From: Bin Du Date: Wed, 6 May 2026 17:32:49 +0800 Subject: [PATCH 085/394] Documentation: add documentation of AMD isp 4 driver Add documentation for AMD ISP 4 and describe the main components Co-developed-by: Svetoslav Stoilov Signed-off-by: Svetoslav Stoilov Signed-off-by: Bin Du Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Sultan Alsawaf Tested-by: Alexey Zagorodnikov Tested-by: Kate Hsuan Signed-off-by: Sakari Ailus --- Documentation/admin-guide/media/amdisp4-1.rst | 63 +++++++++++++++++++ Documentation/admin-guide/media/amdisp4.dot | 6 ++ .../admin-guide/media/v4l-drivers.rst | 1 + MAINTAINERS | 2 + 4 files changed, 72 insertions(+) create mode 100644 Documentation/admin-guide/media/amdisp4-1.rst create mode 100644 Documentation/admin-guide/media/amdisp4.dot diff --git a/Documentation/admin-guide/media/amdisp4-1.rst b/Documentation/admin-guide/media/amdisp4-1.rst new file mode 100644 index 000000000000..878141154f96 --- /dev/null +++ b/Documentation/admin-guide/media/amdisp4-1.rst @@ -0,0 +1,63 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: + +==================================== +AMD Image Signal Processor (amdisp4) +==================================== + +Introduction +============ + +This file documents the driver for the AMD ISP4 that is part of +AMD Ryzen AI Max 300 Series. + +The driver is located under drivers/media/platform/amd/isp4 and uses +the Media-Controller API. + +The driver exposes one video capture device to userspace and provide +web camera like interface. Internally the video device is connected +to the isp4 sub-device responsible for communication with the CCPU FW. + +Topology +======== + +.. _amdisp4_topology_graph: + +.. kernel-figure:: amdisp4.dot + :alt: Diagram of the media pipeline topology + :align: center + + + +The driver has 1 sub-device: Representing isp4 image signal processor. +The driver has 1 video device: Capture device for retrieving images. + +- ISP4 Image Signal Processing Subdevice Node + +--------------------------------------------- + +The isp4 is represented as a single V4L2 subdev, the sub-device does not +provide interface to the user space. The sub-device is connected to one video node +(isp4_capture) with immutable active link. The sub-device represents ISP with +connected sensor similar to smart cameras (sensors with integrated ISP). +sub-device has only one link to the video device for capturing the frames. +The sub-device communicates with CCPU FW for streaming configuration and +buffer management. + + +- isp4_capture - Frames Capture Video Node + +------------------------------------------ + +Isp4_capture is a capture device to capture frames to memory. +The entity is connected to isp4 sub-device. The video device +provides web camera like interface to userspace. It supports +mmap and dma buf types of memory. + +Capturing Video Frames Example +============================== + +.. code-block:: bash + + v4l2-ctl "-d" "/dev/video0" "--set-fmt-video=width=1920,height=1080,pixelformat=NV12" "--stream-mmap" "--stream-count=10" diff --git a/Documentation/admin-guide/media/amdisp4.dot b/Documentation/admin-guide/media/amdisp4.dot new file mode 100644 index 000000000000..978f30c1a31a --- /dev/null +++ b/Documentation/admin-guide/media/amdisp4.dot @@ -0,0 +1,6 @@ +digraph board { + rankdir=TB + n00000001 [label="{{} | amd isp4\n | { 0}}", shape=Mrecord, style=filled, fillcolor=green] + n00000001:port0 -> n00000003 [style=bold] + n00000003 [label="Preview\n/dev/video0", shape=box, style=filled, fillcolor=yellow] +} diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst index d31da8e0a54f..4621eae9fa1e 100644 --- a/Documentation/admin-guide/media/v4l-drivers.rst +++ b/Documentation/admin-guide/media/v4l-drivers.rst @@ -9,6 +9,7 @@ Video4Linux (V4L) driver-specific documentation .. toctree:: :maxdepth: 2 + amdisp4-1 bttv c3-isp cafe_ccic diff --git a/MAINTAINERS b/MAINTAINERS index b92e9625d84c..81d53481d3f7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1158,6 +1158,8 @@ M: Nirujogi Pratap L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git +F: Documentation/admin-guide/media/amdisp4-1.rst +F: Documentation/admin-guide/media/amdisp4.dot F: drivers/media/platform/amd/Kconfig F: drivers/media/platform/amd/Makefile F: drivers/media/platform/amd/isp4/Kconfig From 0913cbfaa912f539f1275099ff75303948afad88 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 26 Jan 2026 11:53:33 +0200 Subject: [PATCH 086/394] media: imx219: Rename "PIXEL_ARRAY" as "ACTIVE_AREA" The imx219 driver uses macros for denoting the size of the pixel array. The values reflect the area of manufacturer-designated visible pixels, reflect this in the naming by calling it "ACTIVE_AREA" instead of "PIXEL_ARRAY". Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx219.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 7da02ce5da15..a09699299b4e 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -142,10 +142,10 @@ /* IMX219 native and active pixel array size. */ #define IMX219_NATIVE_WIDTH 3296U #define IMX219_NATIVE_HEIGHT 2480U -#define IMX219_PIXEL_ARRAY_LEFT 8U -#define IMX219_PIXEL_ARRAY_TOP 8U -#define IMX219_PIXEL_ARRAY_WIDTH 3280U -#define IMX219_PIXEL_ARRAY_HEIGHT 2464U +#define IMX219_ACTIVE_AREA_LEFT 8U +#define IMX219_ACTIVE_AREA_TOP 8U +#define IMX219_ACTIVE_AREA_WIDTH 3280U +#define IMX219_ACTIVE_AREA_HEIGHT 2464U /* Mode : resolution and related config&values */ struct imx219_mode { @@ -675,13 +675,13 @@ static int imx219_set_framefmt(struct imx219 *imx219, bpp = imx219_get_format_bpp(format); cci_write(imx219->regmap, IMX219_REG_X_ADD_STA_A, - crop->left - IMX219_PIXEL_ARRAY_LEFT, &ret); + crop->left - IMX219_ACTIVE_AREA_LEFT, &ret); cci_write(imx219->regmap, IMX219_REG_X_ADD_END_A, - crop->left - IMX219_PIXEL_ARRAY_LEFT + crop->width - 1, &ret); + crop->left - IMX219_ACTIVE_AREA_LEFT + crop->width - 1, &ret); cci_write(imx219->regmap, IMX219_REG_Y_ADD_STA_A, - crop->top - IMX219_PIXEL_ARRAY_TOP, &ret); + crop->top - IMX219_ACTIVE_AREA_TOP, &ret); cci_write(imx219->regmap, IMX219_REG_Y_ADD_END_A, - crop->top - IMX219_PIXEL_ARRAY_TOP + crop->height - 1, &ret); + crop->top - IMX219_ACTIVE_AREA_TOP + crop->height - 1, &ret); imx219_get_binning(state, &bin_h, &bin_v); cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_H, bin_h, &ret); @@ -867,8 +867,8 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, * Use binning to maximize the crop rectangle size, and centre it in the * sensor. */ - bin_h = min(IMX219_PIXEL_ARRAY_WIDTH / format->width, 2U); - bin_v = min(IMX219_PIXEL_ARRAY_HEIGHT / format->height, 2U); + bin_h = min(IMX219_ACTIVE_AREA_WIDTH / format->width, 2U); + bin_v = min(IMX219_ACTIVE_AREA_HEIGHT / format->height, 2U); /* Ensure bin_h and bin_v are same to avoid 1:2 or 2:1 stretching */ binning = min(bin_h, bin_v); @@ -967,10 +967,10 @@ static int imx219_get_selection(struct v4l2_subdev *sd, case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = IMX219_PIXEL_ARRAY_TOP; - sel->r.left = IMX219_PIXEL_ARRAY_LEFT; - sel->r.width = IMX219_PIXEL_ARRAY_WIDTH; - sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT; + sel->r.top = IMX219_ACTIVE_AREA_TOP; + sel->r.left = IMX219_ACTIVE_AREA_LEFT; + sel->r.width = IMX219_ACTIVE_AREA_WIDTH; + sel->r.height = IMX219_ACTIVE_AREA_HEIGHT; return 0; } From 2c4f1ba7354312ad2d6e34e70a518a51a9344715 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 21 Feb 2026 01:38:15 +0200 Subject: [PATCH 087/394] media: imx219: Fix maximum frame length in lines The driver used the maximum frame length in lines value of 0xffff, but the maximum appears to be 0xfffe instead. Fix it. Fixes: 1283b3b8f82b ("media: i2c: Add driver for Sony IMX219 sensor") Cc: stable@vger.kernel.org Signed-off-by: Sakari Ailus Reviewed-by: Dave Stevenson Reviewed-by: Laurent Pinchart --- drivers/media/i2c/imx219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index a09699299b4e..0b9ff639e9f7 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -72,7 +72,7 @@ /* V_TIMING internal */ #define IMX219_REG_FRM_LENGTH_A CCI_REG16(0x0160) -#define IMX219_FLL_MAX 0xffff +#define IMX219_FLL_MAX 0xfffe #define IMX219_VBLANK_MIN 32 #define IMX219_REG_LINE_LENGTH_A CCI_REG16(0x0162) #define IMX219_LLP_MIN 0x0d78 From 224a4333fab19b7ccb321accdbbd969de0cc247f Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 11 Mar 2026 15:27:30 +0200 Subject: [PATCH 088/394] media: imx219: Set horizontal blanking on mode change The driver UAPI is mode-based, allowing the user to choose a mode from a small list based on the output size. The vertical blanking is set based on the mode, do the same for horizontal blanking so the frame rate obtained is constant. Additionally, it's best to use a known-good horizontal blanking value as choosing the value freely may affect image quality. While the minimum value may not be the best value for horizontal blanking, at least it is constant rather than a minimum value of a different configuration. Signed-off-by: Sakari Ailus Reviewed-by: Dave Stevenson Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- drivers/media/i2c/imx219.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 0b9ff639e9f7..223d3753cc93 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -837,11 +837,9 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; u8 bin_h, bin_v, binning; - u32 prev_line_len; int ret; format = v4l2_subdev_state_get_format(state, 0); - prev_line_len = format->width + imx219->hblank->val; /* * Adjust the requested format to match the closest mode. The Bayer @@ -882,7 +880,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { int exposure_max; int exposure_def; - int hblank, llp_min; + int llp_min; int pixel_rate; /* Update limits and set FPS to default */ @@ -924,15 +922,8 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, llp_min - mode->width); if (ret) return ret; - /* - * Retain PPL setting from previous mode so that the - * line time does not change on a mode change. - * Limits have to be recomputed as the controls define - * the blanking only, so PPL values need to have the - * mode width subtracted. - */ - hblank = prev_line_len - mode->width; - ret = __v4l2_ctrl_s_ctrl(imx219->hblank, hblank); + + ret = __v4l2_ctrl_s_ctrl(imx219->hblank, llp_min - mode->width); if (ret) return ret; From d07c23216d8bd4dfadc33911259033984309e8a7 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Apr 2026 00:41:34 +0300 Subject: [PATCH 089/394] media: imx274: Remove redundant kernel-doc comments Remove kernel-doc comments from regular callback functions. These comments have no information value. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- drivers/media/i2c/imx274.c | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 8ec78b60bea6..241821572e03 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -897,14 +897,6 @@ static int imx274_regulators_get(struct device *dev, struct stimx274 *imx274) imx274->supplies); } -/** - * imx274_s_ctrl - This is used to set the imx274 V4L2 controls - * @ctrl: V4L2 control to be set - * - * This function is used to set the V4L2 controls for the imx274 sensor. - * - * Return: 0 on success, errors otherwise - */ static int imx274_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); @@ -1059,16 +1051,6 @@ static int __imx274_change_compose(struct stimx274 *imx274, return 0; } -/** - * imx274_get_fmt - Get the pad format - * @sd: Pointer to V4L2 Sub device structure - * @sd_state: Pointer to sub device state structure - * @fmt: Pointer to pad level media bus format - * - * This function is used to get the pad format information. - * - * Return: 0 on success - */ static int imx274_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -1081,16 +1063,6 @@ static int imx274_get_fmt(struct v4l2_subdev *sd, return 0; } -/** - * imx274_set_fmt - This is used to set the pad format - * @sd: Pointer to V4L2 Sub device structure - * @sd_state: Pointer to sub device state information structure - * @format: Pointer to pad level media bus format - * - * This function is used to set the pad format. - * - * Return: 0 on success - */ static int imx274_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) @@ -1423,16 +1395,6 @@ static void imx274_load_default(struct stimx274 *priv) priv->ctrls.test_pattern->val = TEST_PATTERN_DISABLED; } -/** - * imx274_s_stream - It is used to start/stop the streaming. - * @sd: V4L2 Sub device - * @on: Flag (True / False) - * - * This function controls the start or stop of streaming for the - * imx274 sensor. - * - * Return: 0 on success, errors otherwise - */ static int imx274_s_stream(struct v4l2_subdev *sd, int on) { struct stimx274 *imx274 = to_imx274(sd); From bf4430a0c9309faf5dd0aab86eb449fe01b8d534 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Apr 2026 00:43:33 +0300 Subject: [PATCH 090/394] media: imx334: Remove redundant kernel-doc comments Remove kernel-doc comments from regular callback functions. These comments have no information value. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- drivers/media/i2c/imx334.c | 93 -------------------------------------- 1 file changed, 93 deletions(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 9654f9268056..553a16b84f4d 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -566,18 +566,6 @@ static int imx334_update_exp_gain(struct imx334 *imx334, u32 exposure, u32 gain) return ret; } -/** - * imx334_set_ctrl() - Set subdevice control - * @ctrl: pointer to v4l2_ctrl structure - * - * Supported controls: - * - V4L2_CID_VBLANK - * - cluster controls: - * - V4L2_CID_ANALOGUE_GAIN - * - V4L2_CID_EXPOSURE - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx334 *imx334 = @@ -678,14 +666,6 @@ static int imx334_get_format_code(struct imx334 *imx334, u32 code) return imx334_mbus_codes[0]; } -/** - * imx334_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * @code: V4L2 sub-device code enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -698,14 +678,6 @@ static int imx334_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -/** - * imx334_enum_frame_size() - Enumerate V4L2 sub-device frame sizes - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * @fsize: V4L2 sub-device size enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fsize) @@ -749,14 +721,6 @@ static void imx334_fill_pad_format(struct imx334 *imx334, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -/** - * imx334_get_pad_format() - Get subdevice pad format - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -776,14 +740,6 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd, return 0; } -/** - * imx334_set_pad_format() - Set subdevice pad format - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -815,13 +771,6 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, return ret; } -/** - * imx334_init_state() - Initialize sub-device state - * @sd: pointer to imx334 V4L2 sub-device structure - * @sd_state: V4L2 sub-device state - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { @@ -856,15 +805,6 @@ static int imx334_set_framefmt(struct imx334 *imx334) return -EINVAL; } -/** - * imx334_enable_streams() - Enable specified streams for the sensor - * @sd: pointer to the V4L2 subdevice - * @state: pointer to the subdevice state - * @pad: pad number for which streams are enabled - * @streams_mask: bitmask specifying the streams to enable - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) @@ -929,15 +869,6 @@ static int imx334_enable_streams(struct v4l2_subdev *sd, return ret; } -/** - * imx334_disable_streams() - Enable specified streams for the sensor - * @sd: pointer to the V4L2 subdevice - * @state: pointer to the subdevice state - * @pad: pad number for which streams are disabled - * @streams_mask: bitmask specifying the streams to disable - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_disable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) @@ -1067,12 +998,6 @@ static const struct v4l2_subdev_internal_ops imx334_internal_ops = { .init_state = imx334_init_state, }; -/** - * imx334_power_on() - Sensor power on sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_power_on(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1101,12 +1026,6 @@ static int imx334_power_on(struct device *dev) return ret; } -/** - * imx334_power_off() - Sensor power off sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1206,12 +1125,6 @@ static int imx334_init_controls(struct imx334 *imx334) return 0; } -/** - * imx334_probe() - I2C client device binding - * @client: pointer to i2c client device - * - * Return: 0 if successful, error code otherwise. - */ static int imx334_probe(struct i2c_client *client) { struct imx334 *imx334; @@ -1311,12 +1224,6 @@ static int imx334_probe(struct i2c_client *client) return ret; } -/** - * imx334_remove() - I2C client device unbinding - * @client: pointer to I2C client device - * - * Return: 0 if successful, error code otherwise. - */ static void imx334_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); From 28aeed7586637b860201bad419e01f800d7cb7f0 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Apr 2026 00:44:09 +0300 Subject: [PATCH 091/394] media: imx335: Remove redundant kernel-doc comments Remove kernel-doc comments from regular callback functions. These comments have no information value. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- drivers/media/i2c/imx335.c | 87 -------------------------------------- 1 file changed, 87 deletions(-) diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index 5790aa4fabeb..1f777a1a8192 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -698,18 +698,6 @@ static int imx335_update_test_pattern(struct imx335 *imx335, u32 pattern_index) return ret; } -/** - * imx335_set_ctrl() - Set subdevice control - * @ctrl: pointer to v4l2_ctrl structure - * - * Supported controls: - * - V4L2_CID_VBLANK - * - cluster controls: - * - V4L2_CID_ANALOGUE_GAIN - * - V4L2_CID_EXPOSURE - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx335 *imx335 = @@ -800,14 +788,6 @@ static int imx335_get_format_code(struct imx335 *imx335, u32 code) return imx335_mbus_codes[0]; } -/** - * imx335_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @code: V4L2 sub-device code enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -820,14 +800,6 @@ static int imx335_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -/** - * imx335_enum_frame_size() - Enumerate V4L2 sub-device frame sizes - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fsize: V4L2 sub-device size enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fsize) @@ -871,14 +843,6 @@ static void imx335_fill_pad_format(struct imx335 *imx335, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -/** - * imx335_set_pad_format() - Set subdevice pad format - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -923,13 +887,6 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd, return ret; } -/** - * imx335_init_state() - Initialize sub-device state - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { @@ -947,14 +904,6 @@ static int imx335_init_state(struct v4l2_subdev *sd, return imx335_set_pad_format(sd, sd_state, &fmt); } -/** - * imx335_get_selection() - Selection API - * @sd: pointer to imx335 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @sel: V4L2 selection info - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) @@ -1011,15 +960,6 @@ static int imx335_set_framefmt(struct imx335 *imx335) return ret; } -/** - * imx335_enable_streams() - Enable sensor streams - * @sd: V4L2 subdevice - * @state: V4L2 subdevice state - * @pad: The pad to enable - * @streams_mask: Bitmask of streams to enable - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) @@ -1097,15 +1037,6 @@ static int imx335_enable_streams(struct v4l2_subdev *sd, return ret; } -/** - * imx335_disable_streams() - Disable sensor streams - * @sd: V4L2 subdevice - * @state: V4L2 subdevice state - * @pad: The pad to disable - * @streams_mask: Bitmask of streams to disable - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_disable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) @@ -1299,12 +1230,6 @@ static int imx335_power_on(struct device *dev) return ret; } -/** - * imx335_power_off() - Sensor power off sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1430,12 +1355,6 @@ static int imx335_init_controls(struct imx335 *imx335) return 0; } -/** - * imx335_probe() - I2C client device binding - * @client: pointer to i2c client device - * - * Return: 0 if successful, error code otherwise. - */ static int imx335_probe(struct i2c_client *client) { struct imx335 *imx335; @@ -1530,12 +1449,6 @@ static int imx335_probe(struct i2c_client *client) return ret; } -/** - * imx335_remove() - I2C client device unbinding - * @client: pointer to I2C client device - * - * Return: 0 if successful, error code otherwise. - */ static void imx335_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); From 63652fab67450ff3e334975fae509fd464be224b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Apr 2026 00:44:28 +0300 Subject: [PATCH 092/394] media: imx412: Remove redundant kernel-doc comments Remove kernel-doc comments from regular callback functions. These comments have no information value. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- drivers/media/i2c/imx412.c | 82 -------------------------------------- 1 file changed, 82 deletions(-) diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index e25e0a9ff65c..2705af2f16c0 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -570,18 +570,6 @@ static int imx412_update_exp_gain(struct imx412 *imx412, u32 exposure, u32 gain) return ret; } -/** - * imx412_set_ctrl() - Set subdevice control - * @ctrl: pointer to v4l2_ctrl structure - * - * Supported controls: - * - V4L2_CID_VBLANK - * - cluster controls: - * - V4L2_CID_ANALOGUE_GAIN - * - V4L2_CID_EXPOSURE - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx412 *imx412 = @@ -634,14 +622,6 @@ static const struct v4l2_ctrl_ops imx412_ctrl_ops = { .s_ctrl = imx412_set_ctrl, }; -/** - * imx412_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @code: V4L2 sub-device code enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -654,14 +634,6 @@ static int imx412_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -/** - * imx412_enum_frame_size() - Enumerate V4L2 sub-device frame sizes - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fsize: V4L2 sub-device size enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fsize) @@ -701,14 +673,6 @@ static void imx412_fill_pad_format(struct imx412 *imx412, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -/** - * imx412_get_pad_format() - Get subdevice pad format - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -731,14 +695,6 @@ static int imx412_get_pad_format(struct v4l2_subdev *sd, return 0; } -/** - * imx412_set_pad_format() - Set subdevice pad format - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -768,13 +724,6 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd, return ret; } -/** - * imx412_init_state() - Initialize sub-device state - * @sd: pointer to imx412 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { @@ -840,13 +789,6 @@ static int imx412_stop_streaming(struct imx412 *imx412) 1, IMX412_MODE_STANDBY); } -/** - * imx412_set_stream() - Enable sensor streaming - * @sd: pointer to imx412 subdevice - * @enable: set to enable sensor streaming - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_set_stream(struct v4l2_subdev *sd, int enable) { struct imx412 *imx412 = to_imx412(sd); @@ -1010,12 +952,6 @@ static const struct v4l2_subdev_internal_ops imx412_internal_ops = { .init_state = imx412_init_state, }; -/** - * imx412_power_on() - Sensor power on sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_power_on(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1053,12 +989,6 @@ static int imx412_power_on(struct device *dev) return ret; } -/** - * imx412_power_off() - Sensor power off sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1159,12 +1089,6 @@ static int imx412_init_controls(struct imx412 *imx412) return 0; } -/** - * imx412_probe() - I2C client device binding - * @client: pointer to i2c client device - * - * Return: 0 if successful, error code otherwise. - */ static int imx412_probe(struct i2c_client *client) { struct imx412 *imx412; @@ -1254,12 +1178,6 @@ static int imx412_probe(struct i2c_client *client) return ret; } -/** - * imx412_remove() - I2C client device unbinding - * @client: pointer to I2C client device - * - * Return: 0 if successful, error code otherwise. - */ static void imx412_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); From 5fba5f916c158ffeb82627dabbe52f48fb79dcbe Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Apr 2026 00:44:42 +0300 Subject: [PATCH 093/394] media: ov9282: Remove redundant kernel-doc comments Remove kernel-doc comments from regular callback functions. These comments have no information value. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- drivers/media/i2c/ov9282.c | 67 -------------------------------------- 1 file changed, 67 deletions(-) diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index 2167fb73ea41..5b6f897a74fc 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -586,18 +586,6 @@ static u32 ov9282_flash_duration_to_us(struct ov9282 *ov9282, u32 value) return DIV_ROUND_UP(value * frame_width, OV9282_STROBE_SPAN_FACTOR); } -/** - * ov9282_set_ctrl() - Set subdevice control - * @ctrl: pointer to v4l2_ctrl structure - * - * Supported controls: - * - V4L2_CID_VBLANK - * - cluster controls: - * - V4L2_CID_ANALOGUE_GAIN - * - V4L2_CID_EXPOSURE - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov9282 *ov9282 = @@ -704,14 +692,6 @@ static const struct v4l2_ctrl_ops ov9282_ctrl_ops = { .try_ctrl = ov9282_try_ctrl, }; -/** - * ov9282_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes - * @sd: pointer to ov9282 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @code: V4L2 sub-device code enumeration need to be filled - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -780,14 +760,6 @@ static void ov9282_fill_pad_format(struct ov9282 *ov9282, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -/** - * ov9282_get_pad_format() - Get subdevice pad format - * @sd: pointer to ov9282 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -807,14 +779,6 @@ static int ov9282_get_pad_format(struct v4l2_subdev *sd, return 0; } -/** - * ov9282_set_pad_format() - Set subdevice pad format - * @sd: pointer to ov9282 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * @fmt: V4L2 sub-device format need to be set - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -852,13 +816,6 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd, return ret; } -/** - * ov9282_init_state() - Initialize sub-device state - * @sd: pointer to ov9282 V4L2 sub-device structure - * @sd_state: V4L2 sub-device configuration - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { @@ -1157,12 +1114,6 @@ static const struct v4l2_subdev_internal_ops ov9282_internal_ops = { .init_state = ov9282_init_state, }; -/** - * ov9282_power_on() - Sensor power on sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_power_on(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1206,12 +1157,6 @@ static int ov9282_power_on(struct device *dev) return ret; } -/** - * ov9282_power_off() - Sensor power off sequence - * @dev: pointer to i2c device - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1333,12 +1278,6 @@ static int ov9282_init_controls(struct ov9282 *ov9282) return 0; } -/** - * ov9282_probe() - I2C client device binding - * @client: pointer to i2c client device - * - * Return: 0 if successful, error code otherwise. - */ static int ov9282_probe(struct i2c_client *client) { struct ov9282 *ov9282; @@ -1435,12 +1374,6 @@ static int ov9282_probe(struct i2c_client *client) return ret; } -/** - * ov9282_remove() - I2C client device unbinding - * @client: pointer to I2C client device - * - * Return: 0 if successful, error code otherwise. - */ static void ov9282_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); From e11fb2d3a4dfd32b5f50e89892799729ee1a8049 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 3 Apr 2026 00:44:54 +0300 Subject: [PATCH 094/394] media: tvp514x: Remove redundant kernel-doc comments Remove kernel-doc comments from regular callback functions. These comments have no information value. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- drivers/media/i2c/tvp514x.c | 55 +------------------------------------ 1 file changed, 1 insertion(+), 54 deletions(-) diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index f9c9c80c33ac..7af8f37646d6 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -686,13 +686,6 @@ static int tvp514x_s_routing(struct v4l2_subdev *sd, return 0; } -/** - * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl - * @ctrl: pointer to v4l2_ctrl structure - * - * If the requested control is supported, sets the control's current - * value in HW. Otherwise, returns -EINVAL if the control is not supported. - */ static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); @@ -789,13 +782,6 @@ tvp514x_set_frame_interval(struct v4l2_subdev *sd, return 0; } -/** - * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream - * @sd: pointer to standard V4L2 sub-device structure - * @enable: streaming enable or disable - * - * Sets streaming to enable or disable, if possible. - */ static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable) { int err = 0; @@ -850,14 +836,6 @@ static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = { .s_ctrl = tvp514x_s_ctrl, }; -/** - * tvp514x_enum_mbus_code() - V4L2 decoder interface handler for enum_mbus_code - * @sd: pointer to standard V4L2 sub-device structure - * @sd_state: subdev state - * @code: pointer to v4l2_subdev_mbus_code_enum structure - * - * Enumertaes mbus codes supported - */ static int tvp514x_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -877,14 +855,6 @@ static int tvp514x_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -/** - * tvp514x_get_pad_format() - V4L2 decoder interface handler for get pad format - * @sd: pointer to standard V4L2 sub-device structure - * @sd_state: subdev state - * @format: pointer to v4l2_subdev_format structure - * - * Retrieves pad format which is active or tried based on requirement - */ static int tvp514x_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) @@ -909,14 +879,6 @@ static int tvp514x_get_pad_format(struct v4l2_subdev *sd, return 0; } -/** - * tvp514x_set_pad_format() - V4L2 decoder interface handler for set pad format - * @sd: pointer to standard V4L2 sub-device structure - * @sd_state: subdev state - * @fmt: pointer to v4l2_subdev_format structure - * - * Set pad format for the output pad - */ static int tvp514x_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) @@ -1014,15 +976,7 @@ tvp514x_get_pdata(struct i2c_client *client) return pdata; } -/** - * tvp514x_probe() - decoder driver i2c probe handler - * @client: i2c driver client device structure - * - * Register decoder as an i2c client device and V4L2 - * device. - */ -static int -tvp514x_probe(struct i2c_client *client) +static int tvp514x_probe(struct i2c_client *client) { struct tvp514x_platform_data *pdata = tvp514x_get_pdata(client); struct tvp514x_decoder *decoder; @@ -1113,13 +1067,6 @@ tvp514x_probe(struct i2c_client *client) return ret; } -/** - * tvp514x_remove() - decoder driver i2c remove handler - * @client: i2c driver client device structure - * - * Unregister decoder as an i2c client device and V4L2 - * device. Complement of tvp514x_probe(). - */ static void tvp514x_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); From ef56c563c4b756c6ab7ee02ea5d3b7cb7b70748c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 6 Feb 2026 11:38:18 +0200 Subject: [PATCH 095/394] media: Documentation: Improve LINK_FREQ documentation Add a reference to the LINK_FREQ control and clarify the meaning of the control as for C-PHY the matter is less obvious. Signed-off-by: Sakari Ailus Reviewed-by: Mirela Rabulea Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- Documentation/driver-api/media/tx-rx.rst | 3 ++- .../userspace-api/media/v4l/ext-ctrls-image-process.rst | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/driver-api/media/tx-rx.rst b/Documentation/driver-api/media/tx-rx.rst index 22e1b13ecde9..7df2407817b3 100644 --- a/Documentation/driver-api/media/tx-rx.rst +++ b/Documentation/driver-api/media/tx-rx.rst @@ -93,7 +93,8 @@ where * - variable or constant - description * - link_freq - - The value of the ``V4L2_CID_LINK_FREQ`` integer64 menu item. + - The value of the :ref:`V4L2_CID_LINK_FREQ ` integer64 + menu item. * - nr_of_lanes - Number of data lanes used on the CSI-2 link. * - 2 diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst index 6d516f041ca2..57c2baf34acf 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst @@ -24,7 +24,10 @@ Image Process Control IDs .. _v4l2-cid-link-freq: ``V4L2_CID_LINK_FREQ (integer menu)`` - The frequency of the data bus (e.g. parallel or CSI-2). + The fundamental frequency of the operating symbol rate (serial interfaces + such as CSI-2) or the sampling rate (parallel interfaces such as DVP or + Bt.565) of the data interface. For CSI-2, the frequency is equal to + _1 / (2 * UI)_. .. _v4l2-cid-pixel-rate: From 1155f8e0ccb36570caa2aabe5f00e617deed3a59 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 25 Aug 2025 13:07:50 +0300 Subject: [PATCH 096/394] media: v4l2-subdev: Refactor returning routes Refactor returning the routes by adding a new function that essentially does a memcopy and sets the number of the routes in the routing table. Signed-off-by: Sakari Ailus Reviewed-by: Michael Riesch Reviewed-by: Laurent Pinchart --- drivers/media/v4l2-core/v4l2-subdev.c | 33 ++++++++++++--------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index d00d27d49060..2e927ec336e5 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -629,6 +629,18 @@ subdev_ioctl_get_state(struct v4l2_subdev *sd, struct v4l2_subdev_fh *subdev_fh, v4l2_subdev_get_unlocked_active_state(sd); } +static void v4l2_subdev_copy_routes(struct v4l2_subdev_routing *routing, + const struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route *routes = + (struct v4l2_subdev_route *)(uintptr_t)routing->routes; + u32 copy_routes = min(routing->len_routes, state->routing.num_routes); + + memcpy(routes, state->routing.routes, sizeof(*routes) * copy_routes); + + routing->num_routes = state->routing.num_routes; +} + static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, struct v4l2_subdev_state *state) { @@ -1000,7 +1012,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, case VIDIOC_SUBDEV_G_ROUTING: { struct v4l2_subdev_routing *routing = arg; - struct v4l2_subdev_krouting *krouting; if (!v4l2_subdev_enable_streams_api) return -ENOIOCTLCMD; @@ -1010,13 +1021,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, memset(routing->reserved, 0, sizeof(routing->reserved)); - krouting = &state->routing; - - memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, - krouting->routes, - min(krouting->num_routes, routing->len_routes) * - sizeof(*krouting->routes)); - routing->num_routes = krouting->num_routes; + v4l2_subdev_copy_routes(routing, state); return 0; } @@ -1084,11 +1089,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, * the routing table. */ if (!v4l2_subdev_has_op(sd, pad, set_routing)) { - memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, - state->routing.routes, - min(state->routing.num_routes, routing->len_routes) * - sizeof(*state->routing.routes)); - routing->num_routes = state->routing.num_routes; + v4l2_subdev_copy_routes(routing, state); return 0; } @@ -1102,11 +1103,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, if (rval < 0) return rval; - memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, - state->routing.routes, - min(state->routing.num_routes, routing->len_routes) * - sizeof(*state->routing.routes)); - routing->num_routes = state->routing.num_routes; + v4l2_subdev_copy_routes(routing, state); return 0; } From bc1ba628e37c93cf2abeb2c79716f49087f8a024 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 5 Feb 2026 10:19:35 +0200 Subject: [PATCH 097/394] media: v4l2-subdev: Allow accessing routes with STREAMS client capability Disable access to routes when the STREAMS client capability bit isn't set. Routes aren't relevant otherwise anyway. Signed-off-by: Sakari Ailus Reviewed-by: Jacopo Mondi Reviewed-by: Mirela Rabulea Reviewed-by: Michael Riesch Reviewed-by: Laurent Pinchart --- drivers/media/v4l2-core/v4l2-subdev.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 2e927ec336e5..e9f81b9be9e2 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1019,6 +1019,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) return -ENOIOCTLCMD; + if (!client_supports_streams) + return -EINVAL; + memset(routing->reserved, 0, sizeof(routing->reserved)); v4l2_subdev_copy_routes(routing, state); @@ -1040,6 +1043,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) return -ENOIOCTLCMD; + if (!client_supports_streams) + return -EINVAL; + if (routing->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) return -EPERM; From f078966ca1fb1b3865d8e6bbe2705cfd277fc637 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 23 Mar 2026 09:53:52 +0000 Subject: [PATCH 098/394] media: uvcvideo: Fix sequence number when no EOF If the driver could not detect the EOF, the sequence number is increased twice: 1) When we enter uvc_video_decode_start() with the old buffer and FID has flipped => We return -EAGAIN and last_fid is not flipped 2) When we enter uvc_video_decode_start() with the new buffer. Fix this issue by moving the new frame detection logic earlier in uvc_video_decode_start(). This also has some nice side affects: - The error status from the new packet will no longer get propagated to the previous frame-buffer. - uvc_video_clock_decode() will no longer update the previous frame buf->stf with info from the new packet. - uvc_video_clock_decode() and uvc_video_stats_decode() will no longer get called twice for the same packet. Cc: stable@kernel.org Fixes: 650b95feee35 ("[media] uvcvideo: Generate discontinuous sequence numbers when frames are lost") Reported-by: Hans de Goede Closes: https://lore.kernel.org/linux-media/CANiDSCuj4cPuB5_v2xyvAagA5FjoN8V5scXiFFOeD3aKDMqkCg@mail.gmail.com/T/#me39fb134e8c2c085567a31548c3403eb639625e4 Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 92 ++++++++++++++++--------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index f6c8e3223796..3182f5e9d9a0 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1168,6 +1168,53 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, header_len = data[0]; fid = data[1] & UVC_STREAM_FID; + /* + * Mark the buffer as done if we're at the beginning of a new frame. + * End of frame detection is better implemented by checking the EOF + * bit (FID bit toggling is delayed by one frame compared to the EOF + * bit), but some devices don't set the bit at end of frame (and the + * last payload can be lost anyway). We thus must check if the FID has + * been toggled. + * + * stream->last_fid is initialized to -1, and buf->bytesused to 0, + * so the first isochronous frame will never trigger an end of frame + * detection. + * + * Empty buffers (bytesused == 0) don't trigger end of frame detection + * as it doesn't make sense to return an empty buffer. This also + * avoids detecting end of frame conditions at FID toggling if the + * previous payload had the EOF bit set. + */ + if (fid != stream->last_fid && buf && buf->bytesused != 0) { + uvc_dbg(stream->dev, FRAME, + "Frame complete (FID bit toggled)\n"); + buf->state = UVC_BUF_STATE_READY; + + return -EAGAIN; + } + + /* + * Some cameras, when running two parallel streams (one MJPEG alongside + * another non-MJPEG stream), are known to lose the EOF packet for a frame. + * We can detect the end of a frame by checking for a new SOI marker, as + * the SOI always lies on the packet boundary between two frames for + * these devices. + */ + if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF && + (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG || + stream->cur_format->fcc == V4L2_PIX_FMT_JPEG) && + buf && buf->bytesused != 0) { + const u8 *packet = data + header_len; + + if (len >= header_len + 2 && + packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI) { + buf->state = UVC_BUF_STATE_READY; + buf->error = 1; + stream->last_fid ^= UVC_STREAM_FID; + return -EAGAIN; + } + } + /* * Increase the sequence number regardless of any buffer states, so * that discontinuous sequence numbers always indicate lost frames. @@ -1224,51 +1271,6 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, buf->state = UVC_BUF_STATE_ACTIVE; } - /* - * Mark the buffer as done if we're at the beginning of a new frame. - * End of frame detection is better implemented by checking the EOF - * bit (FID bit toggling is delayed by one frame compared to the EOF - * bit), but some devices don't set the bit at end of frame (and the - * last payload can be lost anyway). We thus must check if the FID has - * been toggled. - * - * stream->last_fid is initialized to -1, so the first isochronous - * frame will never trigger an end of frame detection. - * - * Empty buffers (bytesused == 0) don't trigger end of frame detection - * as it doesn't make sense to return an empty buffer. This also - * avoids detecting end of frame conditions at FID toggling if the - * previous payload had the EOF bit set. - */ - if (fid != stream->last_fid && buf->bytesused != 0) { - uvc_dbg(stream->dev, FRAME, - "Frame complete (FID bit toggled)\n"); - buf->state = UVC_BUF_STATE_READY; - return -EAGAIN; - } - - /* - * Some cameras, when running two parallel streams (one MJPEG alongside - * another non-MJPEG stream), are known to lose the EOF packet for a frame. - * We can detect the end of a frame by checking for a new SOI marker, as - * the SOI always lies on the packet boundary between two frames for - * these devices. - */ - if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF && - (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG || - stream->cur_format->fcc == V4L2_PIX_FMT_JPEG)) { - const u8 *packet = data + header_len; - - if (len >= header_len + 2 && - packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI && - buf->bytesused != 0) { - buf->state = UVC_BUF_STATE_READY; - buf->error = 1; - stream->last_fid ^= UVC_STREAM_FID; - return -EAGAIN; - } - } - stream->last_fid = fid; return header_len; From 2f24ac8dd87983a55f0498898f34a5f2b735b802 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 23 Mar 2026 09:53:53 +0000 Subject: [PATCH 099/394] media: uvcvideo: Fix buffer sequence in frame gaps In UVC, the FID flips with every frame. For every FID flip, we increase the stream sequence number. Now, if a FID flips multiple times and there is no data transferred between the flips, the buffer sequence number will be set to the value of the stream sequence number after the first flip. Userspace uses the buffer sequence number to determine if there have been missing frames. With the current behaviour, userspace will think that the gap is in the wrong location. This patch modifies uvc_video_decode_start() to provide the correct buffer sequence number and timestamp. Cc: stable@kernel.org Fixes: 650b95feee35 ("[media] uvcvideo: Generate discontinuous sequence numbers when frames are lost") Signed-off-by: Ricardo Ribalda Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 3182f5e9d9a0..62db4db4e565 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1223,6 +1223,19 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, stream->sequence++; if (stream->sequence) uvc_video_stats_update(stream); + + /* + * On a FID flip initialize sequence number and timestamp. + * + * The driver already takes care of injecting FID flips for + * UVC_QUIRK_STREAM_NO_FID and UVC_QUIRK_MJPEG_NO_EOF. + */ + if (buf) { + buf->buf.field = V4L2_FIELD_NONE; + buf->buf.sequence = stream->sequence; + buf->buf.vb2_buf.timestamp = + ktime_to_ns(uvc_video_get_time()); + } } uvc_video_clock_decode(stream, buf, data, len); @@ -1263,10 +1276,6 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, return -ENODATA; } - buf->buf.field = V4L2_FIELD_NONE; - buf->buf.sequence = stream->sequence; - buf->buf.vb2_buf.timestamp = ktime_to_ns(uvc_video_get_time()); - /* TODO: Handle PTS and SCR. */ buf->state = UVC_BUF_STATE_ACTIVE; } From a84391b77351bbca92ea9db2be1f44e17f2eda6d Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 16 Mar 2026 13:34:44 +0000 Subject: [PATCH 100/394] media: uvcvideo: Import standard controls from uvcdynctrl The uvcdynctrl tool from libwebcam: https://sourceforge.net/projects/libwebcam/ maps proprietary controls into v4l2 controls using the UVCIOC_CTRL_MAP ioctl. The tool has not been updated for 10+ years now, and there is no reason for the UVC driver to not do the mapping by itself. This patch adds the mappings from the uvcdynctrl into the driver. Hopefully this effort can help in deprecating the UVCIOC_CTRL_MAP ioctl. Some background about UVCIOC_CTRL_MAP (thanks Laurent for the context): ``` this was envisioned as the base of a vibrant ecosystem where a large number of vendors would submit XML files that describe their XU control mappings, at a pace faster than could be supported by adding XU mappings to the driver. This vision failed to materialize and the tool has not been updated for 10+ years now. There is no reason to believe the situation will change. ``` During the porting, the following mappings where NOT imported because they were not using standard v4l2 IDs. It is recommended that userspace moves to UVCIOC_CTRL_QUERY for non standard controls. { .id = V4L2_CID_FLASH_MODE, .entity = UVC_GUID_SIS_LED_HW_CONTROL, .selector = 4, .size = 4, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_MENU, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, .menu_mask = 0x3, .menu_mapping = { 0x20, 0x22 }, .menu_names = { "Off", "On" }, }, { .id = V4L2_CID_FLASH_FREQUENCY, .entity = UVC_GUID_SIS_LED_HW_CONTROL, .selector = 4, .size = 8, .offset = 16, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, }, { .id = V4L2_CID_LED1_MODE, .entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1, .selector = 1, .size = 8, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_MENU, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, .menu_mask = 0xF, .menu_mapping = { 0, 1, 2, 3 }, .menu_names = { "Off", "On", "Blinking", "Auto" }, }, { .id = V4L2_CID_LED1_FREQUENCY, .entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1, .selector = 1, .size = 8, .offset = 16, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, }, { .id = V4L2_CID_DISABLE_PROCESSING, .entity = UVC_GUID_LOGITECH_VIDEO_PIPE_V1, .selector = 5, .size = 8, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, }, { .id = V4L2_CID_RAW_BITS_PER_PIXEL, .entity = UVC_GUID_LOGITECH_VIDEO_PIPE_V1, .selector = 8, .size = 8, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, }, { .id = V4L2_CID_LED1_MODE, .entity = UVC_GUID_LOGITECH_PERIPHERAL, .selector = 0x09, .size = 2, .offset = 8, .v4l2_type = V4L2_CTRL_TYPE_MENU, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, .menu_mask = 0xF, .menu_mapping = { 0, 1, 2, 3 }, .menu_names = { "Off", "On", "Blink", "Auto" }, }, { .id = V4L2_CID_LED1_FREQUENCY, .entity = UVC_GUID_LOGITECH_PERIPHERAL, .selector = 0x09, .size = 8, .offset = 24, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, }, This script has been used to generate the mappings. They were then reformatted manually to follow the driver style. import sys import uuid import re import xml.etree.ElementTree as ET def get_namespace(root): return re.match(r"\{.*\}", root.tag).group(0) def get_single_guid(ns, constant): id = constant.find(ns + "id").text value = constant.find(ns + "value").text return (id, value) def get_constants(ns, root): out = dict() for constant in root.iter(ns + "constant"): attr = constant.attrib if attr["type"] == "integer": id, value = get_single_guid(ns, constant) if id in out: print(f"dupe constant {id}") out[id] = value return out def get_guids(ns, root): out = dict() for constant in root.iter(ns + "constant"): attr = constant.attrib if attr["type"] == "guid": id, value = get_single_guid(ns, constant) if id in out: print(f"dupe guid {id}") out[id] = value return out def get_single_control(ns, control): out = {} for id in "entity", "selector", "index", "size", "description": v = control.find(ns + id) if v is None and id == "description": continue out[id] = v.text reqs = set() for r in control.find(ns + "requests"): reqs.add(r.text) out["requests"] = reqs return (control.attrib["id"], out) def get_controls(ns, root): out = dict() for control in root.iter(ns + "control"): id, value = get_single_control(ns, control) if id in out: print(f"Dupe control id {id}") out[id] = value return out def get_single_mapping(ns, mapping): out = {} out["name"] = mapping.find(ns + "name").text uvc = mapping.find(ns + "uvc") for id in "size", "offset", "uvc_type": out[id] = uvc.find(ns + id).text out["control_ref"] = uvc.find(ns + "control_ref").attrib["idref"] v4l2 = mapping.find(ns + "v4l2") for id in "id", "v4l2_type": out[id] = v4l2.find(ns + id).text menu = {} for entry in v4l2.iter(ns + "menu_entry"): menu[entry.attrib["name"]] = entry.attrib["value"] if menu: out["menu"] = menu return out def get_mapping(ns, root): out = [] for control in root.iter(ns + "mapping"): mapping = get_single_mapping(ns, control) out += [mapping] return out def print_guids(guids): for g in guids: print(f"#define {g} \\") u_bytes = uuid.UUID(guids[g]).bytes_le u_bytes = [f"0x{b:02x}" for b in u_bytes] print("\t{ " + ", ".join(u_bytes) + " }") def print_flags(flags): get_range = {"GET_MIN", "GET_DEF", "GET_MAX", "GET_CUR", "GET_RES"} if get_range.issubset(flags): flags -= get_range flags.add("GET_RANGE") flags = list(flags) flags.sort() out = "" for f in flags[:-1]: out += f"UVC_CTRL_FLAG_{f}\n\t\t\t\t| " out += f"UVC_CTRL_FLAG_{flags[-1]}" return out def print_description(desc): print("/*") for line in desc.strip().splitlines(): print(f" * {line.strip()}") print("*/") def print_controls(controls, cons): for id in controls: c = controls[id] if "description" in c: print_description(c["description"]) print( f"""\t{{ \t\t.entity\t\t= {c["entity"]}, \t\t.selector\t= {cons[c["selector"]]}, \t\t.index\t\t= {c["index"]}, \t\t.size\t\t= {c["size"]}, \t\t.flags\t\t= {print_flags(c["requests"])}, \t}},""" ) def menu_mapping_txt(menu): out = f"\n\t\t.menu_mask\t= 0x{((1< Cc: Martin Rubli Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_ctrl.c | 174 +++++++++++++++++++++++++++++++ include/linux/usb/uvc.h | 6 ++ 2 files changed, 180 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index b9a3d9257a11..b6e020b41671 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -385,6 +385,99 @@ static const struct uvc_control_info uvc_ctrls[] = { | UVC_CTRL_FLAG_GET_RANGE | UVC_CTRL_FLAG_RESTORE, }, + /* + * Allows the control of pan/tilt motor movements for camera models + * that support mechanical pan/tilt. + * + * Bits 0 to 15 control pan, bits 16 to 31 control tilt. + * The unit of the pan/tilt values is 1/64th of a degree and the + * resolution is 1 degree. + */ + { + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 1, + .index = 0, + .size = 4, + .flags = UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_SET_CUR, + }, + /* + * Reset the pan/tilt motors to their original position for camera + * models that support mechanical pan/tilt. + * + * Setting bit 0 resets the pan position. + * Setting bit 1 resets the tilt position. + * + * Both bits can be set at the same time to reset both, pan and tilt, + * at the same time. + */ + { + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 2, + .index = 1, + .size = 1, + .flags = UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_SET_CUR, + }, + /* + * Allows the control of focus motor movements for camera models that + * support mechanical focus. + * + * Bits 0 to 7 allow selection of the desired lens position. + * There are no physical units, instead, the focus range is spread over + * 256 logical units with 0 representing infinity focus and 255 being + * macro focus. + */ + { + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 3, + .index = 2, + .size = 6, + .flags = UVC_CTRL_FLAG_GET_CUR + | UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_SET_CUR, + }, + /* + * Allows the control of pan/tilt motor movements for camera models + * that support mechanical pan/tilt. + * + * Bits 0 to 15 control pan, bits 16 to 31 control tilt. + */ + { + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 1, + .index = 0, + .size = 4, + .flags = UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_SET_CUR, + }, + /* + * Reset the pan/tilt motors to their original position for camera + * models that support mechanical pan/tilt. + * + * Setting bit 0 resets the pan position. + * Setting bit 1 resets the tilt position. + */ + { + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 2, + .index = 1, + .size = 1, + .flags = UVC_CTRL_FLAG_GET_DEF + | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_MIN + | UVC_CTRL_FLAG_GET_RES + | UVC_CTRL_FLAG_SET_CUR, + }, }; static const u32 uvc_control_classes[] = { @@ -1009,6 +1102,87 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { .menu_mask = BIT(V4L2_COLORFX_VIVID) | BIT(V4L2_COLORFX_NONE), }, + { + .id = V4L2_CID_PAN_RELATIVE, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 1, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_TILT_RELATIVE, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 1, + .size = 16, + .offset = 16, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_PAN_RESET, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 2, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BUTTON, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_TILT_RESET, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 2, + .size = 1, + .offset = 1, + .v4l2_type = V4L2_CTRL_TYPE_BUTTON, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_PAN_RELATIVE, + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 1, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_TILT_RELATIVE, + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 1, + .size = 16, + .offset = 16, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_PAN_RESET, + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 2, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BUTTON, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_TILT_RESET, + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 2, + .size = 1, + .offset = 1, + .v4l2_type = V4L2_CTRL_TYPE_BUTTON, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_FOCUS_ABSOLUTE, + .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1, + .selector = 3, + .size = 8, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, }; /* ------------------------------------------------------------------------ diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h index 05bfebab42b6..84f35c6f4d6e 100644 --- a/include/linux/usb/uvc.h +++ b/include/linux/usb/uvc.h @@ -43,6 +43,12 @@ #define UVC_GUID_MSXU_1_5 \ {0xdc, 0x95, 0x3f, 0x0f, 0x32, 0x26, 0x4e, 0x4c, \ 0x92, 0xc9, 0xa0, 0x47, 0x82, 0xf4, 0x3b, 0xc8} +#define UVC_GUID_LOGITECH_MOTOR_CONTROL_V1 \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x56 } +#define UVC_GUID_LOGITECH_PERIPHERAL \ + {0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \ + 0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd } /* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#222-extension-unit-controls */ #define UVC_MSXU_CONTROL_FOCUS 0x01 From 85355f7268af2e1cdb771842de59ac6be42863d5 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 16 Mar 2026 13:34:45 +0000 Subject: [PATCH 101/394] media: uvcvideo: Announce deprecation intentions for UVCIOC_CTRL_MAP The UVCIOC_CTRL_MAP lets userspace create a mapping for a custom control. This mapping is usually created by the uvcdynctrl userspace utility. We would like to get the mappings into the driver instead. Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- Documentation/userspace-api/media/drivers/uvcvideo.rst | 2 ++ drivers/media/usb/uvc/uvc_v4l2.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst index dbb30ad389ae..b09d2f8ba66e 100644 --- a/Documentation/userspace-api/media/drivers/uvcvideo.rst +++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst @@ -109,6 +109,8 @@ IOCTL reference UVCIOC_CTRL_MAP - Map a UVC control to a V4L2 control ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +**This IOCTL is deprecated and will be eventually removed** + Argument: struct uvc_xu_control_mapping **Description**: diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index cda1697204ea..fa852b789193 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -1049,6 +1049,8 @@ static long uvc_ioctl_default(struct file *file, void *priv, bool valid_prio, switch (cmd) { /* Dynamic controls. */ case UVCIOC_CTRL_MAP: + pr_warn_once("uvcvideo: " DEPRECATED + "UVCIOC_CTRL_MAP ioctl will be eventually removed.\n"); return uvc_ioctl_xu_ctrl_map(chain, arg); case UVCIOC_CTRL_QUERY: @@ -1163,6 +1165,8 @@ static long uvc_v4l2_compat_ioctl32(struct file *file, switch (cmd) { case UVCIOC_CTRL_MAP32: + pr_warn_once("uvcvideo: " DEPRECATED + "UVCIOC_CTRL_MAP32 ioctl will be eventually removed.\n"); ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up); if (ret) break; From 02f93b4f940c66b99258cce954dcaeda7b3295a8 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 16 Mar 2026 13:34:46 +0000 Subject: [PATCH 102/394] media: uvcvideo: Introduce allow_privacy_override module parameter Some camera modules have XU controls that can configure the behaviour of the privacy LED. Block mapping of those controls, unless the module is configured with a new parameter: allow_privacy_override. Signed-off-by: Ricardo Ribalda [johannes.goede@oss.qualcomm.com: Remove deprecation warning from param] Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_ctrl.c | 38 ++++++++++++++++++++++++++++++ drivers/media/usb/uvc/uvc_driver.c | 4 ++++ drivers/media/usb/uvc/uvc_v4l2.c | 7 ++++++ drivers/media/usb/uvc/uvcvideo.h | 2 ++ include/linux/usb/uvc.h | 4 ++++ 5 files changed, 55 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index b6e020b41671..3ca108b83f1d 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -3001,6 +3001,35 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev, return ret; } +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector) +{ + /* + * This list is not exhaustive, it is a best effort to block access to + * non documented controls that can affect user's privacy. + */ + struct privacy_control { + u8 entity[16]; + u8 selector; + } privacy_control[] = { + { + .entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1, + .selector = 1, + }, + { + .entity = UVC_GUID_LOGITECH_PERIPHERAL, + .selector = 9, + }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(privacy_control); i++) + if (!memcmp(entity, privacy_control[i].entity, 16) && + selector == privacy_control[i].selector) + return true; + + return false; +} + int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control_query *xqry) { @@ -3045,6 +3074,15 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, return -ENOENT; } + if (uvc_ctrl_is_privacy_control(entity->guid, xqry->selector) && + !uvc_allow_privacy_override_param) { + dev_warn_once(&chain->dev->intf->dev, + "Privacy related controls can only be accessed if module parameter allow_privacy_override is true\n"); + uvc_dbg(chain->dev, CONTROL, "Blocking access to privacy related Control %pUl/%u\n", + entity->guid, xqry->selector); + return -EACCES; + } + if (mutex_lock_interruptible(&chain->ctrl_mutex)) return -ERESTARTSYS; diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 31b4ac3b48c1..e289cc71ba98 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -36,6 +36,7 @@ unsigned int uvc_no_drop_param = 1; static unsigned int uvc_quirks_param = -1; unsigned int uvc_dbg_param; unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; +bool uvc_allow_privacy_override_param; static struct usb_driver uvc_driver; @@ -2504,6 +2505,9 @@ module_param_named(trace, uvc_dbg_param, uint, 0644); MODULE_PARM_DESC(trace, "Trace level bitmask"); module_param_named(timeout, uvc_timeout_param, uint, 0644); MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); +module_param_named(allow_privacy_override, uvc_allow_privacy_override_param, bool, 0644); +MODULE_PARM_DESC(allow_privacy_override, + "Allow access to privacy related controls"); /* ------------------------------------------------------------------------ * Driver initialization and cleanup diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index fa852b789193..644c2f1cd8e6 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -133,6 +133,13 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain, return -EINVAL; } + if (uvc_ctrl_is_privacy_control(xmap->entity, xmap->selector) && + !uvc_allow_privacy_override_param) { + dev_warn_once(&chain->dev->intf->dev, + "Privacy related controls can only be mapped if module parameter allow_privacy_override is true\n"); + return -EACCES; + } + map = kzalloc_obj(*map); if (map == NULL) return -ENOMEM; diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 0a0c01b2420f..4ba35727e954 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -666,6 +666,7 @@ extern unsigned int uvc_no_drop_param; extern unsigned int uvc_dbg_param; extern unsigned int uvc_timeout_param; extern unsigned int uvc_hw_timestamps_param; +extern bool uvc_allow_privacy_override_param; #define uvc_dbg(_dev, flag, fmt, ...) \ do { \ @@ -791,6 +792,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control_query *xqry); void uvc_ctrl_cleanup_fh(struct uvc_fh *handle); +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector); /* Utility functions */ struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h index 84f35c6f4d6e..99b070ab860f 100644 --- a/include/linux/usb/uvc.h +++ b/include/linux/usb/uvc.h @@ -49,6 +49,10 @@ #define UVC_GUID_LOGITECH_PERIPHERAL \ {0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \ 0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd } +#define UVC_GUID_LOGITECH_USER_HW_CONTROL_V1 \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f } + /* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#222-extension-unit-controls */ #define UVC_MSXU_CONTROL_FOCUS 0x01 From 817998f863981b8b3f9829798dc4ec9fa275bdaf Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 13 Mar 2026 18:13:02 +0800 Subject: [PATCH 103/394] media: qcom: camss: Fix csid IRQ offset for sa8775p Fix BUF_DONE_IRQ_STATUS_RDI_OFFSET calculation for csid lite on sa8775p platform. The offset should be 0 for csid lite on sa8775p, Signed-off-by: Wenmeng Liu Reviewed-by: Bryan O'Donoghue Fixes: ed03e99de0fa ("media: qcom: camss: Add support for CSID 690") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-csid-gen3.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen3.c b/drivers/media/platform/qcom/camss/camss-csid-gen3.c index 664245cf6eb0..bd059243790e 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen3.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen3.c @@ -48,9 +48,9 @@ #define IS_CSID_690(csid) ((csid->camss->res->version == CAMSS_8775P) \ || (csid->camss->res->version == CAMSS_8300)) #define CSID_BUF_DONE_IRQ_STATUS 0x8C -#define BUF_DONE_IRQ_STATUS_RDI_OFFSET (csid_is_lite(csid) ?\ - 1 : (IS_CSID_690(csid) ?\ - 13 : 14)) +#define BUF_DONE_IRQ_STATUS_RDI_OFFSET (csid_is_lite(csid) ? \ + ((IS_CSID_690(csid) ? 0 : 1)) : \ + ((IS_CSID_690(csid) ? 13 : 14))) #define CSID_BUF_DONE_IRQ_MASK 0x90 #define CSID_BUF_DONE_IRQ_CLEAR 0x94 #define CSID_BUF_DONE_IRQ_SET 0x98 From 27c6817bbc0edfe11669d0185e7ae5dc7a113edb Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 13 Mar 2026 18:13:03 +0800 Subject: [PATCH 104/394] media: qcom: camss: Fix csid clock configuration for sa8775p Fix the mismatch between clock list and clock rate table for CSID lite instances. The current implementation has 5 clocks defined but only 2 are actually needed (vfe_lite_csid and vfe_lite_cphy_rx), while the clock rate table doesn't match this configuration. Update both clock list and rate table to maintain consistency: - Remove unused clocks: cpas_vfe_lite, vfe_lite_ahb, vfe_lite - Update clock rate table to match the remaining two clocks Signed-off-by: Wenmeng Liu Reviewed-by: Bryan O'Donoghue Fixes: ed03e99de0fa ("media: qcom: camss: Add support for CSID 690") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss.c | 40 +++++++++-------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 00b87fd9afbd..cb0134718985 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -3598,12 +3598,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID2 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite0" }, .interrupt = { "csid_lite0" }, @@ -3617,12 +3615,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID3 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite1" }, .interrupt = { "csid_lite1" }, @@ -3636,12 +3632,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID4 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite2" }, .interrupt = { "csid_lite2" }, @@ -3655,12 +3649,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID5 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite3" }, .interrupt = { "csid_lite3" }, @@ -3674,12 +3666,10 @@ static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID6 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", - "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" }, .clock_rate = { - { 0, 0, 400000000, 400000000, 0}, - { 0, 0, 400000000, 480000000, 0} + { 400000000, 480000000 }, + { 400000000, 480000000 } }, .reg = { "csid_lite4" }, .interrupt = { "csid_lite4" }, From 924f45e58b1fefca0e6accd45a508029436e112e Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 13 Mar 2026 18:13:04 +0800 Subject: [PATCH 105/394] media: qcom: camss: Add missing clocks for VFE lite on sa8775p Add missing required clocks (cpas_ahb and camnoc_axi) for VFE lite instances on sa8775p platform. These clocks are necessary for proper VFE lite operation: Reviewed-by: Bryan O'Donoghue Signed-off-by: Wenmeng Liu Fixes: e7b59e1d06fb ("media: qcom: camss: Add support for VFE 690") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss.c | 40 ++++++++++++++--------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index cb0134718985..9335636d7c4d 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -3742,15 +3742,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE2 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite0" }, .interrupt = { "vfe_lite0" }, @@ -3765,15 +3767,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE3 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite1" }, .interrupt = { "vfe_lite1" }, @@ -3788,15 +3792,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE4 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite2" }, .interrupt = { "vfe_lite2" }, @@ -3811,15 +3817,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE5 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite3" }, .interrupt = { "vfe_lite3" }, @@ -3834,15 +3842,17 @@ static const struct camss_subdev_resources vfe_res_8775p[] = { /* VFE6 (lite) */ { .regulators = {}, - .clock = { "cpas_vfe_lite", "vfe_lite_ahb", + .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx", - "vfe_lite"}, + "vfe_lite", "camnoc_axi"}, .clock_rate = { - { 0, 0, 0, 0 }, + { 0 }, + { 0 }, { 300000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 400000000, 400000000, 400000000, 400000000 }, { 480000000, 600000000, 600000000, 600000000 }, + { 400000000 }, }, .reg = { "vfe_lite4" }, .interrupt = { "vfe_lite4" }, From 797c1cbf672f372d6a464df0dcedf476fc715969 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 20 Mar 2026 16:18:24 +0100 Subject: [PATCH 106/394] media: qcom: camss: avoid format string warning clang-22 warns about csiphy_match_clock_name() taking a variable format string that is not checked against the 'int index' argument: drivers/media/platform/qcom/camss/camss-csiphy.c:566:44: error: diagnostic behavior may be improved by adding the 'format(printf, 2, 3)' attribute to the declaration of 'csiphy_match_clock_name' [-Werror,-Wmissing-format-attribute] 561 | static bool csiphy_match_clock_name(const char *clock_name, const char *format, | __attribute__((format(printf, 2, 3))) 562 | int index) 563 | { 564 | char name[16]; /* csiphyXXX_timer\0 */ 565 | 566 | snprintf(name, sizeof(name), format, index); | ^ drivers/media/platform/qcom/camss/camss-csiphy.c:561:13: note: 'csiphy_match_clock_name' declared here 561 | static bool csiphy_match_clock_name(const char *clock_name, const char *format, | ^ Change the function to use a snprintf() style format string that allows this to be checked at the call site. Signed-off-by: Arnd Bergmann Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-csiphy.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 62623393f414..78a1b568dbae 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -558,12 +558,16 @@ static int csiphy_init_formats(struct v4l2_subdev *sd, return csiphy_set_format(sd, fh ? fh->state : NULL, &format); } -static bool csiphy_match_clock_name(const char *clock_name, const char *format, - int index) +static bool __printf(2, 3) +csiphy_match_clock_name(const char *clock_name, const char *format, ...) { char name[16]; /* csiphyXXX_timer\0 */ + va_list args; + + va_start(args, format); + vsnprintf(name, sizeof(name), format, args); + va_end(args); - snprintf(name, sizeof(name), format, index); return !strcmp(clock_name, name); } From 983f4496b95eab85997054511a6c59575d4120b1 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Mon, 16 Feb 2026 09:54:19 +0100 Subject: [PATCH 107/394] dt-bindings: media: camss: Add qcom,sm6350-camss Add bindings for the Camera Subsystem on the SM6350 SoC. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Luca Weiss Reviewed-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Signed-off-by: Bryan O'Donoghue --- .../bindings/media/qcom,sm6350-camss.yaml | 471 ++++++++++++++++++ 1 file changed, 471 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml diff --git a/Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml new file mode 100644 index 000000000000..96974d90d8c4 --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml @@ -0,0 +1,471 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/qcom,sm6350-camss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SM6350 Camera Subsystem (CAMSS) + +maintainers: + - Luca Weiss + +description: + The CAMSS IP is a CSI decoder and ISP present on Qualcomm platforms. + +properties: + compatible: + const: qcom,sm6350-camss + + reg: + maxItems: 24 + + reg-names: + items: + - const: csid0 + - const: csid1 + - const: csid2 + - const: csid_lite + - const: csiphy0 + - const: csiphy1 + - const: csiphy2 + - const: csiphy3 + - const: vfe0 + - const: vfe1 + - const: vfe2 + - const: vfe_lite + - const: a5_csr + - const: a5_qgic + - const: a5_sierra + - const: bps + - const: camnoc + - const: core_top_csr_tcsr + - const: cpas_cdm + - const: cpas_top + - const: ipe + - const: jpeg_dma + - const: jpeg_enc + - const: lrme + + clocks: + maxItems: 39 + + clock-names: + items: + - const: cam_axi + - const: soc_ahb + - const: camnoc_axi + - const: core_ahb + - const: cpas_ahb + - const: csiphy0 + - const: csiphy0_timer + - const: csiphy1 + - const: csiphy1_timer + - const: csiphy2 + - const: csiphy2_timer + - const: csiphy3 + - const: csiphy3_timer + - const: vfe0_axi + - const: vfe0 + - const: vfe0_cphy_rx + - const: vfe0_csid + - const: vfe1_axi + - const: vfe1 + - const: vfe1_cphy_rx + - const: vfe1_csid + - const: vfe2_axi + - const: vfe2 + - const: vfe2_cphy_rx + - const: vfe2_csid + - const: vfe_lite + - const: vfe_lite_cphy_rx + - const: vfe_lite_csid + - const: bps + - const: bps_ahb + - const: bps_areg + - const: bps_axi + - const: icp + - const: ipe0 + - const: ipe0_ahb + - const: ipe0_areg + - const: ipe0_axi + - const: jpeg + - const: lrme + + interrupts: + maxItems: 18 + + interrupt-names: + items: + - const: csid0 + - const: csid1 + - const: csid2 + - const: csid_lite + - const: csiphy0 + - const: csiphy1 + - const: csiphy2 + - const: csiphy3 + - const: vfe0 + - const: vfe1 + - const: vfe2 + - const: vfe_lite + - const: a5 + - const: cpas + - const: cpas_cdm + - const: jpeg_dma + - const: jpeg_enc + - const: lrme + + interconnects: + maxItems: 4 + + interconnect-names: + items: + - const: ahb + - const: hf_mnoc + - const: sf_mnoc + - const: sf_icp_mnoc + + iommus: + maxItems: 14 + + power-domains: + maxItems: 6 + + power-domain-names: + items: + - const: ife0 + - const: ife1 + - const: ife2 + - const: top + - const: bps + - const: ipe + + vdd-csiphy0-0p9-supply: + description: + Phandle to a 0.9V regulator supply to CSIPHY0. + + vdd-csiphy0-1p25-supply: + description: + Phandle to a 1.25V regulator supply to CSIPHY0. + + vdd-csiphy1-0p9-supply: + description: + Phandle to a 0.9V regulator supply to CSIPHY1. + + vdd-csiphy1-1p25-supply: + description: + Phandle to a 1.25V regulator supply to CSIPHY1. + + vdd-csiphy2-0p9-supply: + description: + Phandle to a 0.9V regulator supply to CSIPHY2. + + vdd-csiphy2-1p25-supply: + description: + Phandle to a 1.25V regulator supply to CSIPHY2. + + vdd-csiphy3-0p9-supply: + description: + Phandle to a 0.9V regulator supply to CSIPHY3. + + vdd-csiphy3-1p25-supply: + description: + Phandle to a 1.25V regulator supply to CSIPHY3. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + description: + CSI input ports. + + patternProperties: + "^port@[0-3]$": + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + + description: + Input port for receiving CSI data from a CSIPHY. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 4 + + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + + required: + - data-lanes + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - interrupts + - interrupt-names + - interconnects + - interconnect-names + - iommus + - power-domains + - power-domain-names + - ports + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + #include + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + isp@acb3000 { + compatible = "qcom,sm6350-camss"; + + reg = <0x0 0x0acb3000 0x0 0x1000>, + <0x0 0x0acba000 0x0 0x1000>, + <0x0 0x0acc1000 0x0 0x1000>, + <0x0 0x0acc8000 0x0 0x1000>, + <0x0 0x0ac65000 0x0 0x1000>, + <0x0 0x0ac66000 0x0 0x1000>, + <0x0 0x0ac67000 0x0 0x1000>, + <0x0 0x0ac68000 0x0 0x1000>, + <0x0 0x0acaf000 0x0 0x4000>, + <0x0 0x0acb6000 0x0 0x4000>, + <0x0 0x0acbd000 0x0 0x4000>, + <0x0 0x0acc4000 0x0 0x4000>, + <0x0 0x0ac18000 0x0 0x3000>, + <0x0 0x0ac00000 0x0 0x6000>, + <0x0 0x0ac10000 0x0 0x8000>, + <0x0 0x0ac6f000 0x0 0x8000>, + <0x0 0x0ac42000 0x0 0x4600>, + <0x0 0x01fc0000 0x0 0x40000>, + <0x0 0x0ac48000 0x0 0x1000>, + <0x0 0x0ac40000 0x0 0x1000>, + <0x0 0x0ac87000 0x0 0xa000>, + <0x0 0x0ac52000 0x0 0x4000>, + <0x0 0x0ac4e000 0x0 0x4000>, + <0x0 0x0ac6b000 0x0 0xa00>; + reg-names = "csid0", + "csid1", + "csid2", + "csid_lite", + "csiphy0", + "csiphy1", + "csiphy2", + "csiphy3", + "vfe0", + "vfe1", + "vfe2", + "vfe_lite", + "a5_csr", + "a5_qgic", + "a5_sierra", + "bps", + "camnoc", + "core_top_csr_tcsr", + "cpas_cdm", + "cpas_top", + "ipe", + "jpeg_dma", + "jpeg_enc", + "lrme"; + + clocks = <&gcc GCC_CAMERA_AXI_CLK>, + <&camcc CAMCC_SOC_AHB_CLK>, + <&camcc CAMCC_CAMNOC_AXI_CLK>, + <&camcc CAMCC_CORE_AHB_CLK>, + <&camcc CAMCC_CPAS_AHB_CLK>, + <&camcc CAMCC_CSIPHY0_CLK>, + <&camcc CAMCC_CSI0PHYTIMER_CLK>, + <&camcc CAMCC_CSIPHY1_CLK>, + <&camcc CAMCC_CSI1PHYTIMER_CLK>, + <&camcc CAMCC_CSIPHY2_CLK>, + <&camcc CAMCC_CSI2PHYTIMER_CLK>, + <&camcc CAMCC_CSIPHY3_CLK>, + <&camcc CAMCC_CSI3PHYTIMER_CLK>, + <&camcc CAMCC_IFE_0_AXI_CLK>, + <&camcc CAMCC_IFE_0_CLK>, + <&camcc CAMCC_IFE_0_CPHY_RX_CLK>, + <&camcc CAMCC_IFE_0_CSID_CLK>, + <&camcc CAMCC_IFE_1_AXI_CLK>, + <&camcc CAMCC_IFE_1_CLK>, + <&camcc CAMCC_IFE_1_CPHY_RX_CLK>, + <&camcc CAMCC_IFE_1_CSID_CLK>, + <&camcc CAMCC_IFE_2_AXI_CLK>, + <&camcc CAMCC_IFE_2_CLK>, + <&camcc CAMCC_IFE_2_CPHY_RX_CLK>, + <&camcc CAMCC_IFE_2_CSID_CLK>, + <&camcc CAMCC_IFE_LITE_CLK>, + <&camcc CAMCC_IFE_LITE_CPHY_RX_CLK>, + <&camcc CAMCC_IFE_LITE_CSID_CLK>, + <&camcc CAMCC_BPS_CLK>, + <&camcc CAMCC_BPS_AHB_CLK>, + <&camcc CAMCC_BPS_AREG_CLK>, + <&camcc CAMCC_BPS_AXI_CLK>, + <&camcc CAMCC_ICP_CLK>, + <&camcc CAMCC_IPE_0_CLK>, + <&camcc CAMCC_IPE_0_AHB_CLK>, + <&camcc CAMCC_IPE_0_AREG_CLK>, + <&camcc CAMCC_IPE_0_AXI_CLK>, + <&camcc CAMCC_JPEG_CLK>, + <&camcc CAMCC_LRME_CLK>; + clock-names = "cam_axi", + "soc_ahb", + "camnoc_axi", + "core_ahb", + "cpas_ahb", + "csiphy0", + "csiphy0_timer", + "csiphy1", + "csiphy1_timer", + "csiphy2", + "csiphy2_timer", + "csiphy3", + "csiphy3_timer", + "vfe0_axi", + "vfe0", + "vfe0_cphy_rx", + "vfe0_csid", + "vfe1_axi", + "vfe1", + "vfe1_cphy_rx", + "vfe1_csid", + "vfe2_axi", + "vfe2", + "vfe2_cphy_rx", + "vfe2_csid", + "vfe_lite", + "vfe_lite_cphy_rx", + "vfe_lite_csid", + "bps", + "bps_ahb", + "bps_areg", + "bps_axi", + "icp", + "ipe0", + "ipe0_ahb", + "ipe0_areg", + "ipe0_axi", + "jpeg", + "lrme"; + + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "csid0", + "csid1", + "csid2", + "csid_lite", + "csiphy0", + "csiphy1", + "csiphy2", + "csiphy3", + "vfe0", + "vfe1", + "vfe2", + "vfe_lite", + "a5", + "cpas", + "cpas_cdm", + "jpeg_dma", + "jpeg_enc", + "lrme"; + + interconnects = <&gem_noc MASTER_AMPSS_M0 QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, + <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_CAMNOC_SF QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_CAMNOC_ICP QCOM_ICC_TAG_ALWAYS + &clk_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "ahb", + "hf_mnoc", + "sf_mnoc", + "sf_icp_mnoc"; + + iommus = <&apps_smmu 0x820 0xc0>, + <&apps_smmu 0x840 0x0>, + <&apps_smmu 0x860 0xc0>, + <&apps_smmu 0x880 0x0>, + <&apps_smmu 0xc40 0x20>, + <&apps_smmu 0xc60 0x20>, + <&apps_smmu 0xc80 0x0>, + <&apps_smmu 0xca2 0x0>, + <&apps_smmu 0xcc0 0x20>, + <&apps_smmu 0xce0 0x20>, + <&apps_smmu 0xd00 0x20>, + <&apps_smmu 0xd20 0x20>, + <&apps_smmu 0xd40 0x20>, + <&apps_smmu 0xd60 0x20>; + + power-domains = <&camcc IFE_0_GDSC>, + <&camcc IFE_1_GDSC>, + <&camcc IFE_2_GDSC>, + <&camcc TITAN_TOP_GDSC>, + <&camcc BPS_GDSC>, + <&camcc IPE_0_GDSC>; + power-domain-names = "ife0", + "ife1", + "ife2", + "top", + "bps", + "ipe"; + + vdd-csiphy0-0p9-supply = <&vreg_l18a>; + vdd-csiphy0-1p25-supply = <&vreg_l22a>; + vdd-csiphy1-0p9-supply = <&vreg_l18a>; + vdd-csiphy1-1p25-supply = <&vreg_l22a>; + vdd-csiphy2-0p9-supply = <&vreg_l18a>; + vdd-csiphy2-1p25-supply = <&vreg_l22a>; + vdd-csiphy3-0p9-supply = <&vreg_l18a>; + vdd-csiphy3-1p25-supply = <&vreg_l22a>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csiphy0_ep: endpoint { + data-lanes = <0 1 2 3>; + bus-type = ; + remote-endpoint = <&sensor_ep>; + }; + }; + }; + }; + }; From f1db6810902d70767180b550614a936f0613c0a2 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Mon, 16 Feb 2026 09:54:20 +0100 Subject: [PATCH 108/394] media: qcom: camss: Add SM6350 support Add the necessary support for CAMSS on the SM6350 SoC. Reviewed-by: Bryan O'Donoghue Signed-off-by: Luca Weiss Reviewed-by: Vladimir Zapolskiy Signed-off-by: Bryan O'Donoghue --- .../qcom/camss/camss-csiphy-3ph-1-0.c | 125 +++++++++ drivers/media/platform/qcom/camss/camss-vfe.c | 2 + drivers/media/platform/qcom/camss/camss.c | 261 ++++++++++++++++++ drivers/media/platform/qcom/camss/camss.h | 1 + 4 files changed, 389 insertions(+) diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c index 415483274552..dac8d2ecf799 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c @@ -399,6 +399,126 @@ csiphy_lane_regs lane_regs_sm8250[] = { {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, }; +/* GEN2 1.2.3 2PH */ +static const struct +csiphy_lane_regs lane_regs_sm6350[] = { + {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0904, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0910, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0900, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0908, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0904, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x002c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0034, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0010, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x001c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0028, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x003c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0004, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0020, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0038, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x005c, 0xc0, 0x00, CSIPHY_SKEW_CAL}, + {0x0060, 0x0d, 0x00, CSIPHY_SKEW_CAL}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0730, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c84, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c90, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c80, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c88, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c84, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x072c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0734, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0710, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x071c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x073c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0704, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0720, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0708, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x070c, 0xff, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0738, 0x1f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0230, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a10, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a00, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a08, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0a04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x022c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0234, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0210, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x021c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0228, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x023c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0200, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0204, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0220, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0208, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0238, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x025c, 0xc0, 0x00, CSIPHY_SKEW_CAL}, + {0x0260, 0x0d, 0x00, CSIPHY_SKEW_CAL}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b10, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b00, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b08, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0b04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x042c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0434, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0410, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x041c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0428, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x043c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0400, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0404, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0420, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0408, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0438, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x045c, 0xc0, 0x00, CSIPHY_SKEW_CAL}, + {0x0460, 0x0d, 0x00, CSIPHY_SKEW_CAL}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0630, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c10, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c00, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c08, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0c04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x062c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0634, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0610, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x061c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0628, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x063c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0600, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0604, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0620, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0608, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0638, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x065c, 0xc0, 0x00, CSIPHY_SKEW_CAL}, + {0x0660, 0x0d, 0x00, CSIPHY_SKEW_CAL}, + {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS}, +}; + /* 14nm 2PH v 2.0.1 2p5Gbps 4 lane DPHY mode */ static const struct csiphy_lane_regs lane_regs_qcm2290[] = { @@ -1011,6 +1131,7 @@ static bool csiphy_is_gen2(u32 version) switch (version) { case CAMSS_2290: case CAMSS_6150: + case CAMSS_6350: case CAMSS_7280: case CAMSS_8250: case CAMSS_8280XP: @@ -1105,6 +1226,10 @@ static int csiphy_init(struct csiphy_device *csiphy) regs->lane_regs = &lane_regs_qcm2290[0]; regs->lane_array_size = ARRAY_SIZE(lane_regs_qcm2290); break; + case CAMSS_6350: + regs->lane_regs = &lane_regs_sm6350[0]; + regs->lane_array_size = ARRAY_SIZE(lane_regs_sm6350); + break; case CAMSS_7280: case CAMSS_8250: regs->lane_regs = &lane_regs_sm8250[0]; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 5baf0e3d4bc4..7dc937d018f6 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -343,6 +343,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, case CAMSS_660: case CAMSS_2290: case CAMSS_6150: + case CAMSS_6350: case CAMSS_7280: case CAMSS_8x96: case CAMSS_8250: @@ -2003,6 +2004,7 @@ static int vfe_bpl_align(struct vfe_device *vfe) switch (vfe->camss->res->version) { case CAMSS_6150: + case CAMSS_6350: case CAMSS_7280: case CAMSS_8250: case CAMSS_8280XP: diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 9335636d7c4d..506d84dd3b97 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -1703,6 +1703,253 @@ static const struct resources_icc icc_res_sm6150[] = { }, }; +static const struct camss_subdev_resources csiphy_res_sm6350[] = { + /* CSIPHY0 */ + { + .regulators = { + { .supply = "vdd-csiphy0-0p9", .init_load_uA = 80000 }, + { .supply = "vdd-csiphy0-1p25", .init_load_uA = 80000 }, + }, + .clock = { "csiphy0", "csiphy0_timer" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy0" }, + .interrupt = { "csiphy0" }, + .csiphy = { + .id = 0, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY1 */ + { + .regulators = { + { .supply = "vdd-csiphy1-0p9", .init_load_uA = 80000 }, + { .supply = "vdd-csiphy1-1p25", .init_load_uA = 80000 }, + }, + .clock = { "csiphy1", "csiphy1_timer" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy1" }, + .interrupt = { "csiphy1" }, + .csiphy = { + .id = 1, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY2 */ + { + .regulators = { + { .supply = "vdd-csiphy2-0p9", .init_load_uA = 80000 }, + { .supply = "vdd-csiphy2-1p25", .init_load_uA = 80000 }, + }, + .clock = { "csiphy2", "csiphy2_timer" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy2" }, + .interrupt = { "csiphy2" }, + .csiphy = { + .id = 2, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY3 */ + { + .regulators = { + { .supply = "vdd-csiphy3-0p9", .init_load_uA = 80000 }, + { .supply = "vdd-csiphy3-1p25", .init_load_uA = 80000 }, + }, + .clock = { "csiphy3", "csiphy3_timer" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 300000000 } }, + .reg = { "csiphy3" }, + .interrupt = { "csiphy3" }, + .csiphy = { + .id = 3, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + } +}; + +static const struct camss_subdev_resources csid_res_sm6350[] = { + /* CSID0 */ + { + .regulators = {}, + .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" }, + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID1 */ + { + .regulators = {}, + .clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" }, + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID2 */ + { + .regulators = {}, + .clock = { "vfe2_csid", "vfe2_cphy_rx", "vfe2" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 } }, + .reg = { "csid2" }, + .interrupt = { "csid2" }, + .csid = { + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID3 (lite) */ + { + .regulators = {}, + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite" }, + .clock_rate = { { 300000000, 384000000, 400000000 }, + { 0 }, + { 400000000, 480000000 } }, + .reg = { "csid_lite" }, + .interrupt = { "csid_lite" }, + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + } +}; + +static const struct camss_subdev_resources vfe_res_sm6350[] = { + /* VFE0 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "camnoc_axi", "vfe0", + "vfe0_axi", "cam_axi", "soc_ahb" }, + .clock_rate = { { 19200000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" }, + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife0", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE1 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "camnoc_axi", "vfe1", + "vfe1_axi", "cam_axi", "soc_ahb" }, + .clock_rate = { { 19200000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe1" }, + .interrupt = { "vfe1" }, + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife1", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE2 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "camnoc_axi", "vfe2", + "vfe2_axi", "cam_axi", "soc_ahb" }, + .clock_rate = { { 19200000 }, + { 0 }, + { 320000000, 404000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe2" }, + .interrupt = { "vfe2" }, + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife2", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE3 (lite) */ + { + .regulators = {}, + .clock = { "cpas_ahb", "camnoc_axi", "vfe_lite", + "cam_axi", "soc_ahb" }, + .clock_rate = { { 19200000 }, + { 0 }, + { 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe_lite" }, + .interrupt = { "vfe_lite" }, + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, +}; + +static const struct resources_icc icc_res_sm6350[] = { + { + .name = "ahb", + .icc_bw_tbl.avg = 0, + .icc_bw_tbl.peak = 300000, + }, + { + .name = "hf_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, + { + .name = "sf_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, + { + .name = "sf_icp_mnoc", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, +}; + static const struct camss_subdev_resources csiphy_res_8250[] = { /* CSIPHY0 */ { @@ -5233,6 +5480,19 @@ static const struct camss_resources sm6150_resources = { .vfe_num = ARRAY_SIZE(vfe_res_sm6150), }; +static const struct camss_resources sm6350_resources = { + .version = CAMSS_6350, + .pd_name = "top", + .csiphy_res = csiphy_res_sm6350, + .csid_res = csid_res_sm6350, + .vfe_res = vfe_res_sm6350, + .icc_res = icc_res_sm6350, + .icc_path_num = ARRAY_SIZE(icc_res_sm6350), + .csiphy_num = ARRAY_SIZE(csiphy_res_sm6350), + .csid_num = ARRAY_SIZE(csid_res_sm6350), + .vfe_num = ARRAY_SIZE(vfe_res_sm6350), +}; + static const struct camss_resources sm8250_resources = { .version = CAMSS_8250, .pd_name = "top", @@ -5329,6 +5589,7 @@ static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,sdm670-camss", .data = &sdm670_resources }, { .compatible = "qcom,sdm845-camss", .data = &sdm845_resources }, { .compatible = "qcom,sm6150-camss", .data = &sm6150_resources }, + { .compatible = "qcom,sm6350-camss", .data = &sm6350_resources }, { .compatible = "qcom,sm8250-camss", .data = &sm8250_resources }, { .compatible = "qcom,sm8550-camss", .data = &sm8550_resources }, { .compatible = "qcom,sm8650-camss", .data = &sm8650_resources }, diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index 6d048414c919..d323c105d185 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -81,6 +81,7 @@ enum camss_version { CAMSS_660, CAMSS_2290, CAMSS_6150, + CAMSS_6350, CAMSS_7280, CAMSS_8x16, CAMSS_8x39, From 70b193987c8c025618b6dea7303e7972053e9313 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Tue, 17 Mar 2026 18:05:45 +0800 Subject: [PATCH 109/394] media: qcom: camss: Add common TPG support Introduce a new common Test Pattern Generator (TPG) implementation for Qualcomm CAMSS. This module provides a generic interface for pattern generation that can be reused by multiple platforms. Unlike CSID-integrated TPG, this TPG acts as a standalone block that emulates both CSIPHY and sensor behavior, enabling flexible test patterns without external hardware. Signed-off-by: Wenmeng Liu Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # Dell Inpsiron14p Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/Makefile | 11 +- drivers/media/platform/qcom/camss/camss-tpg.c | 519 ++++++++++++++++++ drivers/media/platform/qcom/camss/camss-tpg.h | 118 ++++ drivers/media/platform/qcom/camss/camss.h | 5 + 4 files changed, 648 insertions(+), 5 deletions(-) create mode 100644 drivers/media/platform/qcom/camss/camss-tpg.c create mode 100644 drivers/media/platform/qcom/camss/camss-tpg.h diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index 5e349b491513..d747aa7db3c1 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -10,10 +10,13 @@ qcom-camss-objs += \ camss-csid-680.o \ camss-csid-gen2.o \ camss-csid-gen3.o \ + camss-csiphy.o \ camss-csiphy-2ph-1-0.o \ camss-csiphy-3ph-1-0.o \ - camss-csiphy.o \ + camss-format.o \ camss-ispif.o \ + camss-tpg.o \ + camss-vfe.o \ camss-vfe-4-1.o \ camss-vfe-4-7.o \ camss-vfe-4-8.o \ @@ -21,11 +24,9 @@ qcom-camss-objs += \ camss-vfe-340.o \ camss-vfe-480.o \ camss-vfe-680.o \ - camss-vfe-gen3.o \ camss-vfe-gen1.o \ + camss-vfe-gen3.o \ camss-vfe-vbif.o \ - camss-vfe.o \ - camss-video.o \ - camss-format.o \ + camss-video.o obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom-camss.o diff --git a/drivers/media/platform/qcom/camss/camss-tpg.c b/drivers/media/platform/qcom/camss/camss-tpg.c new file mode 100644 index 000000000000..c5b75132add4 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-tpg.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * + * Qualcomm MSM Camera Subsystem - TPG Module + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "camss-tpg.h" +#include "camss.h" + +static const struct tpg_format_info formats_gen1[] = { + { + MEDIA_BUS_FMT_SBGGR8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + MIPI_CSI2_DT_RAW12, + ENCODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + MIPI_CSI2_DT_RAW12, + ENCODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + MIPI_CSI2_DT_RAW12, + ENCODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + MIPI_CSI2_DT_RAW12, + ENCODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + }, + { + MEDIA_BUS_FMT_Y8_1X8, + MIPI_CSI2_DT_RAW8, + ENCODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + MIPI_CSI2_DT_RAW10, + ENCODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + }, +}; + +const struct tpg_formats tpg_formats_gen1 = { + .nformats = ARRAY_SIZE(formats_gen1), + .formats = formats_gen1 +}; + +const struct tpg_format_info *tpg_get_fmt_entry(const struct tpg_format_info *formats, + unsigned int nformats, + u32 code) +{ + unsigned int i; + + for (i = 0; i < nformats; i++) + if (code == formats[i].code) + return &formats[i]; + + return ERR_PTR(-EINVAL); +} + +static int tpg_set_clock_rates(struct tpg_device *tpg) +{ + struct device *dev = tpg->camss->dev; + int i, ret; + + for (i = 0; i < tpg->nclocks; i++) { + struct camss_clock *clock = &tpg->clock[i]; + long round_rate; + + if (clock->freq) { + round_rate = clk_round_rate(clock->clk, clock->freq[0]); + if (round_rate < 0) { + dev_err(dev, "clk round rate failed: %ld\n", + round_rate); + return -EINVAL; + } + + ret = clk_set_rate(clock->clk, round_rate); + if (ret < 0) { + dev_err(dev, "clk set rate failed: %d\n", ret); + return ret; + } + } + } + + return 0; +} + +static int tpg_set_power(struct v4l2_subdev *sd, int on) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + struct device *dev = tpg->camss->dev; + + if (on) { + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + ret = tpg_set_clock_rates(tpg); + if (ret < 0) { + pm_runtime_put_sync(dev); + return ret; + } + + ret = camss_enable_clocks(tpg->nclocks, tpg->clock, dev); + if (ret < 0) { + pm_runtime_put_sync(dev); + return ret; + } + + tpg->res->hw_ops->reset(tpg); + + tpg->res->hw_ops->hw_version(tpg); + } else { + camss_disable_clocks(tpg->nclocks, tpg->clock); + + pm_runtime_put_sync(dev); + } + + return 0; +} + +static int tpg_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + int ret; + + if (enable) { + ret = v4l2_ctrl_handler_setup(&tpg->ctrls); + if (ret < 0) { + dev_err(tpg->camss->dev, + "could not sync v4l2 controls: %d\n", ret); + return ret; + } + } + + return tpg->res->hw_ops->configure_stream(tpg, enable); +} + +static struct v4l2_mbus_framefmt * +__tpg_get_format(struct tpg_device *tpg, + struct v4l2_subdev_state *sd_state, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_state_get_format(sd_state, + pad); + + return &tpg->fmt; +} + +static void tpg_try_format(struct tpg_device *tpg, + struct v4l2_mbus_framefmt *fmt) +{ + unsigned int i; + + for (i = 0; i < tpg->res->formats->nformats; i++) + if (tpg->res->formats->formats[i].code == fmt->code) + break; + + if (i >= tpg->res->formats->nformats) + fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8; + + fmt->width = clamp_t(u32, fmt->width, TPG_MIN_WIDTH, TPG_MAX_WIDTH); + fmt->height = clamp_t(u32, fmt->height, TPG_MIN_HEIGHT, TPG_MAX_HEIGHT); + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +static int tpg_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + + if (code->index >= tpg->res->formats->nformats) + return -EINVAL; + + code->code = tpg->res->formats->formats[code->index].code; + + return 0; +} + +static int tpg_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + unsigned int i; + + if (fse->index != 0) + return -EINVAL; + + for (i = 0; i < tpg->res->formats->nformats; i++) + if (tpg->res->formats->formats[i].code == fse->code) + break; + + if (i >= tpg->res->formats->nformats) + return -EINVAL; + + fse->min_width = TPG_MIN_WIDTH; + fse->min_height = TPG_MIN_HEIGHT; + fse->max_width = TPG_MAX_WIDTH; + fse->max_height = TPG_MAX_HEIGHT; + + return 0; +} + +static int tpg_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __tpg_get_format(tpg, sd_state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +static int tpg_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct tpg_device *tpg = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __tpg_get_format(tpg, sd_state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + tpg_try_format(tpg, &fmt->format); + *format = fmt->format; + + return 0; +} + +static int tpg_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format = { + .pad = MSM_TPG_PAD_SRC, + .which = fh ? V4L2_SUBDEV_FORMAT_TRY : + V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .width = 1920, + .height = 1080, + } + }; + + return tpg_set_format(sd, fh ? fh->state : NULL, &format); +} + +static int tpg_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tpg_device *tpg = container_of(ctrl->handler, + struct tpg_device, ctrls); + int ret = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + ret = tpg->res->hw_ops->configure_testgen_pattern(tpg, ctrl->val); + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops tpg_ctrl_ops = { + .s_ctrl = tpg_s_ctrl, +}; + +int msm_tpg_subdev_init(struct camss *camss, + struct tpg_device *tpg, + const struct camss_subdev_resources *res, u8 id) +{ + struct platform_device *pdev; + struct device *dev; + int i, j; + + dev = camss->dev; + pdev = to_platform_device(dev); + + tpg->camss = camss; + tpg->id = id; + tpg->res = &res->tpg; + tpg->res->hw_ops->subdev_init(tpg); + + tpg->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); + if (IS_ERR(tpg->base)) + return PTR_ERR(tpg->base); + + tpg->nclocks = 0; + while (res->clock[tpg->nclocks]) + tpg->nclocks++; + + if (!tpg->nclocks) + return 0; + + tpg->clock = devm_kcalloc(dev, tpg->nclocks, + sizeof(*tpg->clock), GFP_KERNEL); + if (!tpg->clock) + return -ENOMEM; + + for (i = 0; i < tpg->nclocks; i++) { + struct camss_clock *clock = &tpg->clock[i]; + + clock->clk = devm_clk_get(dev, res->clock[i]); + if (IS_ERR(clock->clk)) + return PTR_ERR(clock->clk); + + clock->name = res->clock[i]; + + clock->nfreqs = 0; + while (res->clock_rate[i][clock->nfreqs]) + clock->nfreqs++; + + if (!clock->nfreqs) { + clock->freq = NULL; + continue; + } + + clock->freq = devm_kcalloc(dev, clock->nfreqs, + sizeof(*clock->freq), GFP_KERNEL); + if (!clock->freq) + return -ENOMEM; + + for (j = 0; j < clock->nfreqs; j++) + clock->freq[j] = res->clock_rate[i][j]; + } + + return 0; +} + +static int tpg_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + if (flags & MEDIA_LNK_FL_ENABLED) + if (media_pad_remote_pad_first(local)) + return -EBUSY; + + return 0; +} + +static const struct v4l2_subdev_core_ops tpg_core_ops = { + .s_power = tpg_set_power, +}; + +static const struct v4l2_subdev_video_ops tpg_video_ops = { + .s_stream = tpg_set_stream, +}; + +static const struct v4l2_subdev_pad_ops tpg_pad_ops = { + .enum_mbus_code = tpg_enum_mbus_code, + .enum_frame_size = tpg_enum_frame_size, + .get_fmt = tpg_get_format, + .set_fmt = tpg_set_format, +}; + +static const struct v4l2_subdev_ops tpg_v4l2_ops = { + .core = &tpg_core_ops, + .video = &tpg_video_ops, + .pad = &tpg_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops tpg_v4l2_internal_ops = { + .open = tpg_init_formats, +}; + +static const struct media_entity_operations tpg_media_ops = { + .link_setup = tpg_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +int msm_tpg_register_entity(struct tpg_device *tpg, + struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd = &tpg->subdev; + struct device *dev = tpg->camss->dev; + int ret; + + v4l2_subdev_init(sd, &tpg_v4l2_ops); + sd->internal_ops = &tpg_v4l2_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", + "msm_tpg", tpg->id); + sd->grp_id = TPG_GRP_ID; + v4l2_set_subdevdata(sd, tpg); + + ret = v4l2_ctrl_handler_init(&tpg->ctrls, 1); + if (ret < 0) { + dev_err(dev, "Failed to init ctrl handler: %d\n", ret); + return ret; + } + + tpg->testgen_mode = v4l2_ctrl_new_std_menu_items(&tpg->ctrls, + &tpg_ctrl_ops, V4L2_CID_TEST_PATTERN, + tpg->testgen.nmodes, 0, 0, + tpg->testgen.modes); + if (tpg->ctrls.error) { + dev_err(dev, "Failed to init ctrl: %d\n", tpg->ctrls.error); + ret = tpg->ctrls.error; + goto free_ctrl; + } + + tpg->subdev.ctrl_handler = &tpg->ctrls; + + ret = tpg_init_formats(sd, NULL); + if (ret < 0) { + dev_err(dev, "Failed to init format: %d\n", ret); + goto free_ctrl; + } + + tpg->pad.flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.ops = &tpg_media_ops; + ret = media_entity_pads_init(&sd->entity, 1, &tpg->pad); + if (ret < 0) { + dev_err(dev, "Failed to init media entity: %d\n", ret); + goto free_ctrl; + } + + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret < 0) { + dev_err(dev, "Failed to register subdev: %d\n", ret); + media_entity_cleanup(&sd->entity); + goto free_ctrl; + } + + return 0; + +free_ctrl: + v4l2_ctrl_handler_free(&tpg->ctrls); + + return ret; +} + +void msm_tpg_unregister_entity(struct tpg_device *tpg) +{ + v4l2_device_unregister_subdev(&tpg->subdev); + media_entity_cleanup(&tpg->subdev.entity); + v4l2_ctrl_handler_free(&tpg->ctrls); +} diff --git a/drivers/media/platform/qcom/camss/camss-tpg.h b/drivers/media/platform/qcom/camss/camss-tpg.h new file mode 100644 index 000000000000..7fb35a97dd06 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-tpg.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * camss-tpg.h + * + * Qualcomm MSM Camera Subsystem - TPG Module + * + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#ifndef QC_MSM_CAMSS_TPG_H +#define QC_MSM_CAMSS_TPG_H + +#include +#include +#include +#include +#include +#include +#include + +#define ENCODE_FORMAT_UNCOMPRESSED_8_BIT 0x1 +#define ENCODE_FORMAT_UNCOMPRESSED_10_BIT 0x2 +#define ENCODE_FORMAT_UNCOMPRESSED_12_BIT 0x3 +#define ENCODE_FORMAT_UNCOMPRESSED_14_BIT 0x4 +#define ENCODE_FORMAT_UNCOMPRESSED_16_BIT 0x5 +#define ENCODE_FORMAT_UNCOMPRESSED_20_BIT 0x6 +#define ENCODE_FORMAT_UNCOMPRESSED_24_BIT 0x7 + +#define MSM_TPG_PAD_SRC 0 +#define MSM_TPG_ACTIVE_VC 0 +#define MSM_TPG_ACTIVE_DT 0 + +#define TPG_MIN_WIDTH 1 +#define TPG_MIN_HEIGHT 1 +#define TPG_MAX_WIDTH 8191 +#define TPG_MAX_HEIGHT 8191 + +#define TPG_GRP_ID 0 + +enum tpg_testgen_mode { + TPG_PAYLOAD_MODE_DISABLED = 0, + TPG_PAYLOAD_MODE_INCREMENTING = 1, + TPG_PAYLOAD_MODE_ALTERNATING_55_AA = 2, + TPG_PAYLOAD_MODE_RANDOM = 5, + TPG_PAYLOAD_MODE_USER_SPECIFIED = 6, + TPG_PAYLOAD_MODE_COLOR_BARS = 9, + TPG_PAYLOAD_MODE_NUM_SUPPORTED_GEN1 = 9, +}; + +struct tpg_testgen_config { + enum tpg_testgen_mode mode; + const char * const*modes; + u8 nmodes; +}; + +struct tpg_format_info { + u32 code; + u8 data_type; + u8 encode_format; + u8 bpp; +}; + +struct tpg_formats { + unsigned int nformats; + const struct tpg_format_info *formats; +}; + +struct tpg_device; + +struct tpg_hw_ops { + int (*configure_stream)(struct tpg_device *tpg, u8 enable); + int (*configure_testgen_pattern)(struct tpg_device *tpg, s32 val); + u32 (*hw_version)(struct tpg_device *tpg); + int (*reset)(struct tpg_device *tpg); + void (*subdev_init)(struct tpg_device *tpg); +}; + +struct tpg_subdev_resources { + u8 lane_cnt; + const struct tpg_formats *formats; + const struct tpg_hw_ops *hw_ops; +}; + +struct tpg_device { + struct camss *camss; + u8 id; + struct v4l2_subdev subdev; + struct media_pad pad; + void __iomem *base; + struct camss_clock *clock; + int nclocks; + struct tpg_testgen_config testgen; + struct v4l2_mbus_framefmt fmt; + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *testgen_mode; + const struct tpg_subdev_resources *res; + u32 hw_version; +}; + +struct camss_subdev_resources; + +const struct tpg_format_info *tpg_get_fmt_entry(const struct tpg_format_info *formats, + unsigned int nformats, + u32 code); + +int msm_tpg_subdev_init(struct camss *camss, + struct tpg_device *tpg, + const struct camss_subdev_resources *res, u8 id); + +int msm_tpg_register_entity(struct tpg_device *tpg, + struct v4l2_device *v4l2_dev); + +void msm_tpg_unregister_entity(struct tpg_device *tpg); + +extern const struct tpg_formats tpg_formats_gen1; + +extern const struct tpg_hw_ops tpg_ops_gen1; + +#endif /* QC_MSM_CAMSS_TPG_H */ diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index d323c105d185..93d691c8ac63 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -21,6 +21,7 @@ #include "camss-csid.h" #include "camss-csiphy.h" #include "camss-ispif.h" +#include "camss-tpg.h" #include "camss-vfe.h" #include "camss-format.h" @@ -52,6 +53,7 @@ struct camss_subdev_resources { char *interrupt[CAMSS_RES_MAX]; union { struct csiphy_subdev_resources csiphy; + struct tpg_subdev_resources tpg; struct csid_subdev_resources csid; struct vfe_subdev_resources vfe; }; @@ -106,6 +108,7 @@ struct camss_resources { enum camss_version version; const char *pd_name; const struct camss_subdev_resources *csiphy_res; + const struct camss_subdev_resources *tpg_res; const struct camss_subdev_resources *csid_res; const struct camss_subdev_resources *ispif_res; const struct camss_subdev_resources *vfe_res; @@ -113,6 +116,7 @@ struct camss_resources { const struct resources_icc *icc_res; const unsigned int icc_path_num; const unsigned int csiphy_num; + const unsigned int tpg_num; const unsigned int csid_num; const unsigned int vfe_num; }; @@ -123,6 +127,7 @@ struct camss { struct media_device media_dev; struct device *dev; struct csiphy_device *csiphy; + struct tpg_device *tpg; struct csid_device *csid; struct ispif_device *ispif; struct vfe_device *vfe; From 4b14db418b6e9fe4cb1f50120bb26f73d3a1573f Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Tue, 17 Mar 2026 18:05:46 +0800 Subject: [PATCH 110/394] media: qcom: camss: Add link support for TPG TPG is connected to the csid as an entity, the link needs to be adapted. Signed-off-by: Wenmeng Liu Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # Dell Inpsiron14p Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/camss/camss-csid.c | 45 ++++++++++----- .../media/platform/qcom/camss/camss-csid.h | 1 + .../media/platform/qcom/camss/camss-csiphy.c | 1 + .../media/platform/qcom/camss/camss-csiphy.h | 2 + drivers/media/platform/qcom/camss/camss.c | 55 +++++++++++++++++++ 5 files changed, 90 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index ed1820488c98..48459b46a981 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -35,6 +35,8 @@ #define HW_VERSION_REVISION 16 #define HW_VERSION_GENERATION 28 +#define LANE_CFG_BITWIDTH 4 + #define MSM_CSID_NAME "msm_csid" const char * const csid_testgen_modes[] = { @@ -1215,18 +1217,22 @@ void msm_csid_get_csid_id(struct media_entity *entity, u8 *id) } /* - * csid_get_lane_assign - Calculate CSI2 lane assign configuration parameter - * @lane_cfg - CSI2 lane configuration + * csid_get_lane_assign - Calculate lane assign by csiphy/tpg lane num + * @lane_cfg: CSI2 lane configuration + * @num_lanes: lane num * * Return lane assign */ -static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg) +static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg, int num_lanes) { u32 lane_assign = 0; + int pos; int i; - for (i = 0; i < lane_cfg->num_data; i++) - lane_assign |= lane_cfg->data[i].pos << (i * 4); + for (i = 0; i < num_lanes; i++) { + pos = lane_cfg ? lane_cfg->data[i].pos : i; + lane_assign |= pos << (i * LANE_CFG_BITWIDTH); + } return lane_assign; } @@ -1251,6 +1257,7 @@ static int csid_link_setup(struct media_entity *entity, if ((local->flags & MEDIA_PAD_FL_SINK) && (flags & MEDIA_LNK_FL_ENABLED)) { struct v4l2_subdev *sd; + struct tpg_device *tpg; struct csid_device *csid; struct csiphy_device *csiphy; struct csiphy_lanes_cfg *lane_cfg; @@ -1265,18 +1272,28 @@ static int csid_link_setup(struct media_entity *entity, return -EBUSY; sd = media_entity_to_v4l2_subdev(remote->entity); - csiphy = v4l2_get_subdevdata(sd); + if (sd->grp_id == TPG_GRP_ID) { + tpg = v4l2_get_subdevdata(sd); - /* If a sensor is not linked to CSIPHY */ - /* do no allow a link from CSIPHY to CSID */ - if (!csiphy->cfg.csi2) - return -EPERM; + csid->phy.lane_cnt = tpg->res->lane_cnt; + csid->phy.csiphy_id = tpg->id; + csid->phy.lane_assign = csid_get_lane_assign(NULL, csid->phy.lane_cnt); + csid->tpg_linked = true; + } else { + csiphy = v4l2_get_subdevdata(sd); - csid->phy.csiphy_id = csiphy->id; + /* If a sensor is not linked to CSIPHY */ + /* do no allow a link from CSIPHY to CSID */ + if (!csiphy->cfg.csi2) + return -EPERM; - lane_cfg = &csiphy->cfg.csi2->lane_cfg; - csid->phy.lane_cnt = lane_cfg->num_data; - csid->phy.lane_assign = csid_get_lane_assign(lane_cfg); + csid->phy.csiphy_id = csiphy->id; + + lane_cfg = &csiphy->cfg.csi2->lane_cfg; + csid->phy.lane_cnt = lane_cfg->num_data; + csid->phy.lane_assign = csid_get_lane_assign(lane_cfg, lane_cfg->num_data); + csid->tpg_linked = false; + } } /* Decide which virtual channels to enable based on which source pads are enabled */ if (local->flags & MEDIA_PAD_FL_SOURCE) { diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index aedc96ed84b2..5296b10f6bac 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -161,6 +161,7 @@ struct csid_device { int num_supplies; struct completion reset_complete; struct csid_testgen_config testgen; + bool tpg_linked; struct csid_phy_config phy; struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM]; struct v4l2_ctrl_handler ctrls; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 78a1b568dbae..539ac4888b60 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -793,6 +793,7 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", MSM_CSIPHY_NAME, csiphy->id); + sd->grp_id = CSIPHY_GRP_ID; v4l2_set_subdevdata(sd, csiphy); ret = csiphy_init_formats(sd, NULL); diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index 2d5054819df7..9d9657b82f74 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -21,6 +21,8 @@ #define MSM_CSIPHY_PAD_SRC 1 #define MSM_CSIPHY_PADS_NUM 2 +#define CSIPHY_GRP_ID 1 + struct csiphy_lane { u8 pos; u8 pol; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 506d84dd3b97..bd351ceadda7 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -4748,6 +4748,19 @@ static int camss_init_subdevices(struct camss *camss) } } + if (camss->tpg) { + for (i = 0; i < camss->res->tpg_num; i++) { + ret = msm_tpg_subdev_init(camss, &camss->tpg[i], + &res->tpg_res[i], i); + if (ret < 0) { + dev_err(camss->dev, + "Failed to init tpg%d sub-device: %d\n", + i, ret); + return ret; + } + } + } + /* note: SM8250 requires VFE to be initialized before CSID */ for (i = 0; i < camss->res->vfe_num; i++) { ret = msm_vfe_subdev_init(camss, &camss->vfe[i], @@ -4836,6 +4849,23 @@ static int camss_link_entities(struct camss *camss) } } + for (i = 0; i < camss->res->tpg_num; i++) { + for (j = 0; j < camss->res->csid_num; j++) { + ret = media_create_pad_link(&camss->tpg[i].subdev.entity, + MSM_TPG_PAD_SRC, + &camss->csid[j].subdev.entity, + MSM_CSID_PAD_SINK, + 0); + if (ret < 0) { + camss_link_err(camss, + camss->tpg[i].subdev.entity.name, + camss->csid[j].subdev.entity.name, + ret); + return ret; + } + } + } + if (camss->ispif) { for (i = 0; i < camss->res->csid_num; i++) { for (j = 0; j < camss->ispif->line_num; j++) { @@ -4940,6 +4970,19 @@ static int camss_register_entities(struct camss *camss) } } + if (camss->tpg) { + for (i = 0; i < camss->res->tpg_num; i++) { + ret = msm_tpg_register_entity(&camss->tpg[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register tpg%d entity: %d\n", + i, ret); + goto err_reg_tpg; + } + } + } + for (i = 0; i < camss->res->csid_num; i++) { ret = msm_csid_register_entity(&camss->csid[i], &camss->v4l2_dev); @@ -4983,6 +5026,13 @@ static int camss_register_entities(struct camss *camss) for (i--; i >= 0; i--) msm_csid_unregister_entity(&camss->csid[i]); + i = camss->res->tpg_num; +err_reg_tpg: + if (camss->tpg) { + for (i--; i >= 0; i--) + msm_tpg_unregister_entity(&camss->tpg[i]); + } + i = camss->res->csiphy_num; err_reg_csiphy: for (i--; i >= 0; i--) @@ -5004,6 +5054,11 @@ static void camss_unregister_entities(struct camss *camss) for (i = 0; i < camss->res->csiphy_num; i++) msm_csiphy_unregister_entity(&camss->csiphy[i]); + if (camss->tpg) { + for (i = 0; i < camss->res->tpg_num; i++) + msm_tpg_unregister_entity(&camss->tpg[i]); + } + for (i = 0; i < camss->res->csid_num; i++) msm_csid_unregister_entity(&camss->csid[i]); From 51fe835c485beaebe041d441d2be9840882bb152 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Tue, 17 Mar 2026 18:05:47 +0800 Subject: [PATCH 111/394] media: qcom: camss: tpg: Add TPG support for multiple targets Add support for TPG found on LeMans, Monaco, Hamoa. Signed-off-by: Wenmeng Liu Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # Dell Inpsiron14p Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/Makefile | 1 + .../platform/qcom/camss/camss-csid-680.c | 14 +- .../platform/qcom/camss/camss-csid-gen3.c | 14 +- .../platform/qcom/camss/camss-tpg-gen1.c | 231 ++++++++++++++++++ drivers/media/platform/qcom/camss/camss.c | 117 ++++++++- 5 files changed, 371 insertions(+), 6 deletions(-) create mode 100644 drivers/media/platform/qcom/camss/camss-tpg-gen1.c diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index d747aa7db3c1..27898b3cc7d3 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -16,6 +16,7 @@ qcom-camss-objs += \ camss-format.o \ camss-ispif.o \ camss-tpg.o \ + camss-tpg-gen1.o \ camss-vfe.o \ camss-vfe-4-1.o \ camss-vfe-4-7.o \ diff --git a/drivers/media/platform/qcom/camss/camss-csid-680.c b/drivers/media/platform/qcom/camss/camss-csid-680.c index 3ad3a174bcfb..2e4547455d22 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-680.c +++ b/drivers/media/platform/qcom/camss/camss-csid-680.c @@ -103,6 +103,8 @@ #define CSI2_RX_CFG0_PHY_NUM_SEL 20 #define CSI2_RX_CFG0_PHY_SEL_BASE_IDX 1 #define CSI2_RX_CFG0_PHY_TYPE_SEL 24 +#define CSI2_RX_CFG0_TPG_MUX_EN BIT(27) +#define CSI2_RX_CFG0_TPG_MUX_SEL GENMASK(29, 28) #define CSID_CSI2_RX_CFG1 0x204 #define CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN BIT(0) @@ -185,10 +187,20 @@ static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy, int vc) { u32 val; + struct camss *camss; + camss = csid->camss; val = (phy->lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES; val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL; - val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) << CSI2_RX_CFG0_PHY_NUM_SEL; + + if (camss->tpg && csid->tpg_linked && + camss->tpg[phy->csiphy_id].testgen.mode != TPG_PAYLOAD_MODE_DISABLED) { + val |= FIELD_PREP(CSI2_RX_CFG0_TPG_MUX_SEL, phy->csiphy_id + 1); + val |= CSI2_RX_CFG0_TPG_MUX_EN; + } else { + val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) + << CSI2_RX_CFG0_PHY_NUM_SEL; + } writel(val, csid->base + CSID_CSI2_RX_CFG0); diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen3.c b/drivers/media/platform/qcom/camss/camss-csid-gen3.c index bd059243790e..222765fe7914 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen3.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen3.c @@ -66,6 +66,8 @@ #define CSI2_RX_CFG0_VC_MODE 3 #define CSI2_RX_CFG0_DL0_INPUT_SEL 4 #define CSI2_RX_CFG0_PHY_NUM_SEL 20 +#define CSI2_RX_CFG0_TPG_MUX_EN BIT(27) +#define CSI2_RX_CFG0_TPG_MUX_SEL GENMASK(29, 28) #define CSID_CSI2_RX_CFG1 0x204 #define CSI2_RX_CFG1_ECC_CORRECTION_EN BIT(0) @@ -109,10 +111,20 @@ static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy, int vc) { int val; + struct camss *camss; + camss = csid->camss; val = (phy->lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES; val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL; - val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) << CSI2_RX_CFG0_PHY_NUM_SEL; + + if (camss->tpg && csid->tpg_linked && + camss->tpg[phy->csiphy_id].testgen.mode != TPG_PAYLOAD_MODE_DISABLED) { + val |= FIELD_PREP(CSI2_RX_CFG0_TPG_MUX_SEL, phy->csiphy_id + 1); + val |= CSI2_RX_CFG0_TPG_MUX_EN; + } else { + val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) + << CSI2_RX_CFG0_PHY_NUM_SEL; + } writel(val, csid->base + CSID_CSI2_RX_CFG0); diff --git a/drivers/media/platform/qcom/camss/camss-tpg-gen1.c b/drivers/media/platform/qcom/camss/camss-tpg-gen1.c new file mode 100644 index 000000000000..d29de5f93c18 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-tpg-gen1.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * + * Qualcomm MSM Camera Subsystem - TPG (Test Pattern Generator) Module + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +#include +#include + +#include "camss-tpg.h" +#include "camss.h" + +/* TPG global registers */ +#define TPG_HW_VERSION 0x0 +# define HW_VERSION_STEPPING GENMASK(15, 0) +# define HW_VERSION_REVISION GENMASK(27, 16) +# define HW_VERSION_GENERATION GENMASK(31, 28) + +#define TPG_HW_VER(gen, rev, step) \ + (((u32)(gen) << 28) | ((u32)(rev) << 16) | (u32)(step)) + +#define TPG_HW_VER_2_0_0 TPG_HW_VER(2, 0, 0) +#define TPG_HW_VER_2_1_0 TPG_HW_VER(2, 1, 0) + +#define TPG_HW_STATUS 0x4 + +#define TPG_CTRL 0x64 +# define TPG_CTRL_TEST_EN BIT(0) +# define TPG_CTRL_PHY_SEL BIT(3) +# define TPG_CTRL_NUM_ACTIVE_LANES GENMASK(5, 4) +# define TPG_CTRL_VC_DT_PATTERN_ID GENMASK(8, 6) +# define TPG_CTRL_OVERLAP_SHDR_EN BIT(10) +# define TPG_CTRL_NUM_ACTIVE_VC GENMASK(31, 30) + +#define TPG_CLEAR 0x1F4 + +/* TPG VC-based registers */ +#define TPG_VC_n_GAIN_CFG(n) (0x60 + (n) * 0x60) + +#define TPG_VC_n_CFG0(n) (0x68 + (n) * 0x60) +# define TPG_VC_n_CFG0_VC_NUM GENMASK(4, 0) +# define TPG_VC_n_CFG0_NUM_ACTIVE_DT GENMASK(9, 8) +# define TPG_VC_n_CFG0_NUM_BATCH GENMASK(15, 12) +# define TPG_VC_n_CFG0_NUM_FRAMES GENMASK(31, 16) + +#define TPG_VC_n_LSFR_SEED(n) (0x6C + (n) * 0x60) +#define TPG_VC_n_HBI_CFG(n) (0x70 + (n) * 0x60) +#define TPG_VC_n_VBI_CFG(n) (0x74 + (n) * 0x60) + +#define TPG_VC_n_COLOR_BARS_CFG(n) (0x78 + (n) * 0x60) +# define TPG_VC_n_COLOR_BARS_CFG_PIX_PATTERN GENMASK(2, 0) +# define TPG_VC_n_COLOR_BARS_CFG_QCFA_EN BIT(3) +# define TPG_VC_n_COLOR_BARS_CFG_SPLIT_EN BIT(4) +# define TPG_VC_n_COLOR_BARS_CFG_NOISE_EN BIT(5) +# define TPG_VC_n_COLOR_BARS_CFG_ROTATE_PERIOD GENMASK(13, 8) +# define TPG_VC_n_COLOR_BARS_CFG_XCFA_EN BIT(16) +# define TPG_VC_n_COLOR_BARS_CFG_SIZE_X GENMASK(26, 24) +# define TPG_VC_n_COLOR_BARS_CFG_SIZE_Y GENMASK(30, 28) + +/* TPG DT-based registers */ +#define TPG_VC_m_DT_n_CFG_0(m, n) (0x7C + (m) * 0x60 + (n) * 0xC) +# define TPG_VC_m_DT_n_CFG_0_FRAME_HEIGHT GENMASK(15, 0) +# define TPG_VC_m_DT_n_CFG_0_FRAME_WIDTH GENMASK(31, 16) + +#define TPG_VC_m_DT_n_CFG_1(m, n) (0x80 + (m) * 0x60 + (n) * 0xC) +# define TPG_VC_m_DT_n_CFG_1_DATA_TYPE GENMASK(5, 0) +# define TPG_VC_m_DT_n_CFG_1_ECC_XOR_MASK GENMASK(13, 8) +# define TPG_VC_m_DT_n_CFG_1_CRC_XOR_MASK GENMASK(31, 16) + +#define TPG_VC_m_DT_n_CFG_2(m, n) (0x84 + (m) * 0x60 + (n) * 0xC) +# define TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE GENMASK(3, 0) +/* v2.0.0: USER[19:4], ENC[23:20] */ +# define TPG_V2_0_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD GENMASK(19, 4) +# define TPG_V2_0_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT GENMASK(23, 20) +/* v2.1.0: USER[27:4], ENC[31:28] */ +# define TPG_V2_1_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD GENMASK(27, 4) +# define TPG_V2_1_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT GENMASK(31, 28) + +#define TPG_HBI_PCT_DEFAULT 545 /* 545% */ +#define TPG_VBI_PCT_DEFAULT 10 /* 10% */ +#define PERCENT_BASE 100 + +/* Default user-specified payload for TPG test generator. + * Keep consistent with CSID TPG default: 0xBE. + */ +#define TPG_USER_SPECIFIED_PAYLOAD_DEFAULT 0xBE +#define TPG_LFSR_SEED_DEFAULT 0x12345678 +#define TPG_COLOR_BARS_CFG_STANDARD \ + FIELD_PREP(TPG_VC_n_COLOR_BARS_CFG_ROTATE_PERIOD, 0xA) + +static const char * const testgen_payload_modes[] = { + [TPG_PAYLOAD_MODE_DISABLED] = "Disabled", + [TPG_PAYLOAD_MODE_INCREMENTING] = "Incrementing", + [TPG_PAYLOAD_MODE_ALTERNATING_55_AA] = "Alternating 0x55/0xAA", + [TPG_PAYLOAD_MODE_RANDOM] = "Pseudo-random Data", + [TPG_PAYLOAD_MODE_USER_SPECIFIED] = "User Specified", + [TPG_PAYLOAD_MODE_COLOR_BARS] = "Color bars", +}; + +static int tpg_stream_on(struct tpg_device *tpg) +{ + struct tpg_testgen_config *tg = &tpg->testgen; + struct v4l2_mbus_framefmt *input_format; + const struct tpg_format_info *format; + u8 payload_mode = (tg->mode > TPG_PAYLOAD_MODE_DISABLED) ? + tg->mode - 1 : 0; + u8 lane_cnt = tpg->res->lane_cnt; + u8 vc, dt, last_vc = 0; + u32 val; + + for (vc = 0; vc <= MSM_TPG_ACTIVE_VC; vc++) { + last_vc = vc; + + input_format = &tpg->fmt; + format = tpg_get_fmt_entry(tpg->res->formats->formats, + tpg->res->formats->nformats, + input_format->code); + if (IS_ERR(format)) + return -EINVAL; + + /* VC configuration */ + val = FIELD_PREP(TPG_VC_n_CFG0_NUM_ACTIVE_DT, MSM_TPG_ACTIVE_DT) | + FIELD_PREP(TPG_VC_n_CFG0_NUM_FRAMES, 0); + writel(val, tpg->base + TPG_VC_n_CFG0(vc)); + + writel(TPG_LFSR_SEED_DEFAULT, tpg->base + TPG_VC_n_LSFR_SEED(vc)); + + val = DIV_ROUND_UP(input_format->width * format->bpp * TPG_HBI_PCT_DEFAULT, + BITS_PER_BYTE * lane_cnt * PERCENT_BASE); + writel(val, tpg->base + TPG_VC_n_HBI_CFG(vc)); + + val = input_format->height * TPG_VBI_PCT_DEFAULT / PERCENT_BASE; + writel(val, tpg->base + TPG_VC_n_VBI_CFG(vc)); + + writel(TPG_COLOR_BARS_CFG_STANDARD, tpg->base + TPG_VC_n_COLOR_BARS_CFG(vc)); + + /* DT configuration */ + for (dt = 0; dt <= MSM_TPG_ACTIVE_DT; dt++) { + val = FIELD_PREP(TPG_VC_m_DT_n_CFG_0_FRAME_HEIGHT, + input_format->height & 0xffff) | + FIELD_PREP(TPG_VC_m_DT_n_CFG_0_FRAME_WIDTH, + input_format->width & 0xffff); + writel(val, tpg->base + TPG_VC_m_DT_n_CFG_0(vc, dt)); + + val = FIELD_PREP(TPG_VC_m_DT_n_CFG_1_DATA_TYPE, format->data_type); + writel(val, tpg->base + TPG_VC_m_DT_n_CFG_1(vc, dt)); + + if (tpg->hw_version == TPG_HW_VER_2_0_0) { + val = FIELD_PREP(TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE, payload_mode) | + FIELD_PREP(TPG_V2_0_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD, + TPG_USER_SPECIFIED_PAYLOAD_DEFAULT) | + FIELD_PREP(TPG_V2_0_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT, + format->encode_format); + } else if (tpg->hw_version >= TPG_HW_VER_2_1_0) { + val = FIELD_PREP(TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE, payload_mode) | + FIELD_PREP(TPG_V2_1_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD, + TPG_USER_SPECIFIED_PAYLOAD_DEFAULT) | + FIELD_PREP(TPG_V2_1_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT, + format->encode_format); + } + writel(val, tpg->base + TPG_VC_m_DT_n_CFG_2(vc, dt)); + } + } + + /* Global TPG control */ + val = FIELD_PREP(TPG_CTRL_TEST_EN, 1) | + FIELD_PREP(TPG_CTRL_NUM_ACTIVE_LANES, lane_cnt - 1) | + FIELD_PREP(TPG_CTRL_NUM_ACTIVE_VC, last_vc); + writel(val, tpg->base + TPG_CTRL); + + return 0; +} + +static int tpg_reset(struct tpg_device *tpg) +{ + writel(0, tpg->base + TPG_CTRL); + writel(1, tpg->base + TPG_CLEAR); + + return 0; +} + +static void tpg_stream_off(struct tpg_device *tpg) +{ + tpg_reset(tpg); +} + +static int tpg_configure_stream(struct tpg_device *tpg, u8 enable) +{ + if (enable) + return tpg_stream_on(tpg); + + tpg_stream_off(tpg); + + return 0; +} + +static int tpg_configure_testgen_pattern(struct tpg_device *tpg, s32 val) +{ + if (val >= 0 && val <= TPG_PAYLOAD_MODE_COLOR_BARS) + tpg->testgen.mode = val; + + return 0; +} + +static u32 tpg_hw_version(struct tpg_device *tpg) +{ + u32 hw_version = readl(tpg->base + TPG_HW_VERSION); + + tpg->hw_version = hw_version; + dev_dbg(tpg->camss->dev, "tpg HW Version = %u.%u.%u\n", + (u32)FIELD_GET(HW_VERSION_GENERATION, hw_version), + (u32)FIELD_GET(HW_VERSION_REVISION, hw_version), + (u32)FIELD_GET(HW_VERSION_STEPPING, hw_version)); + + return hw_version; +} + +static void tpg_subdev_init(struct tpg_device *tpg) +{ + tpg->testgen.modes = testgen_payload_modes; + tpg->testgen.nmodes = TPG_PAYLOAD_MODE_NUM_SUPPORTED_GEN1; +} + +const struct tpg_hw_ops tpg_ops_gen1 = { + .configure_stream = tpg_configure_stream, + .configure_testgen_pattern = tpg_configure_testgen_pattern, + .hw_version = tpg_hw_version, + .reset = tpg_reset, + .subdev_init = tpg_subdev_init, +}; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index bd351ceadda7..2123f6388e3d 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -3806,6 +3806,54 @@ static const struct camss_subdev_resources csiphy_res_8775p[] = { }, }; +static const struct camss_subdev_resources tpg_res_8775p[] = { + /* TPG0 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "tpg0" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, + /* TPG1 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "tpg1" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, + /* TPG2 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "tpg2" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, +}; + static const struct camss_subdev_resources csid_res_8775p[] = { /* CSID0 */ { @@ -4210,6 +4258,54 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, }; +static const struct camss_subdev_resources tpg_res_x1e80100[] = { + /* TPG0 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csid_csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "csitpg0" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, + /* TPG1 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csid_csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "csitpg1" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, + /* TPG2 */ + { + .regulators = {}, + .clock = { "cpas_ahb", "csid_csiphy_rx" }, + .clock_rate = { + { 0 }, + { 400000000 }, + }, + .reg = { "csitpg2" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, +}; + static const struct camss_subdev_resources csid_res_x1e80100[] = { /* CSID0 */ { @@ -4323,7 +4419,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = { .clock = {"camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", "cpas_fast_ahb", "cpas_vfe0", "vfe0_fast_ahb", "vfe0" }, - .clock_rate = { { 0 }, + .clock_rate = { { 400000000 }, { 0 }, { 0 }, { 0 }, @@ -4347,7 +4443,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = { .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", "cpas_fast_ahb", "cpas_vfe1", "vfe1_fast_ahb", "vfe1" }, - .clock_rate = { { 0 }, + .clock_rate = { { 400000000 }, { 0 }, { 0 }, { 0 }, @@ -4371,7 +4467,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = { .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", "vfe_lite_ahb", "cpas_vfe_lite", "vfe_lite", "vfe_lite_csid" }, - .clock_rate = { { 0 }, + .clock_rate = { { 400000000 }, { 0 }, { 0 }, { 0 }, @@ -4394,7 +4490,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = { .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", "vfe_lite_ahb", "cpas_vfe_lite", "vfe_lite", "vfe_lite_csid" }, - .clock_rate = { { 0 }, + .clock_rate = { { 400000000 }, { 0 }, { 0 }, { 0 }, @@ -5277,6 +5373,13 @@ static int camss_probe(struct platform_device *pdev) if (!camss->csiphy) return -ENOMEM; + if (camss->res->tpg_num > 0) { + camss->tpg = devm_kcalloc(dev, camss->res->tpg_num, + sizeof(*camss->tpg), GFP_KERNEL); + if (!camss->tpg) + return -ENOMEM; + } + camss->csid = devm_kcalloc(dev, camss->res->csid_num, sizeof(*camss->csid), GFP_KERNEL); if (!camss->csid) @@ -5466,11 +5569,13 @@ static const struct camss_resources qcs8300_resources = { .version = CAMSS_8300, .pd_name = "top", .csiphy_res = csiphy_res_8300, + .tpg_res = tpg_res_8775p, .csid_res = csid_res_8775p, .csid_wrapper_res = &csid_wrapper_res_sm8550, .vfe_res = vfe_res_8775p, .icc_res = icc_res_qcs8300, .csiphy_num = ARRAY_SIZE(csiphy_res_8300), + .tpg_num = ARRAY_SIZE(tpg_res_8775p), .csid_num = ARRAY_SIZE(csid_res_8775p), .vfe_num = ARRAY_SIZE(vfe_res_8775p), .icc_path_num = ARRAY_SIZE(icc_res_qcs8300), @@ -5480,11 +5585,13 @@ static const struct camss_resources sa8775p_resources = { .version = CAMSS_8775P, .pd_name = "top", .csiphy_res = csiphy_res_8775p, + .tpg_res = tpg_res_8775p, .csid_res = csid_res_8775p, .csid_wrapper_res = &csid_wrapper_res_sm8550, .vfe_res = vfe_res_8775p, .icc_res = icc_res_sa8775p, .csiphy_num = ARRAY_SIZE(csiphy_res_8775p), + .tpg_num = ARRAY_SIZE(tpg_res_8775p), .csid_num = ARRAY_SIZE(csid_res_8775p), .vfe_num = ARRAY_SIZE(vfe_res_8775p), .icc_path_num = ARRAY_SIZE(icc_res_sa8775p), @@ -5620,12 +5727,14 @@ static const struct camss_resources x1e80100_resources = { .version = CAMSS_X1E80100, .pd_name = "top", .csiphy_res = csiphy_res_x1e80100, + .tpg_res = tpg_res_x1e80100, .csid_res = csid_res_x1e80100, .vfe_res = vfe_res_x1e80100, .csid_wrapper_res = &csid_wrapper_res_x1e80100, .icc_res = icc_res_x1e80100, .icc_path_num = ARRAY_SIZE(icc_res_x1e80100), .csiphy_num = ARRAY_SIZE(csiphy_res_x1e80100), + .tpg_num = ARRAY_SIZE(tpg_res_x1e80100), .csid_num = ARRAY_SIZE(csid_res_x1e80100), .vfe_num = ARRAY_SIZE(vfe_res_x1e80100), }; From c97e797a64cdfe4acecff831b4285418d2815893 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Thu, 19 Mar 2026 17:09:02 +0800 Subject: [PATCH 112/394] media: qcom: camss: vfe: fix PIX subdev naming on VFE lite VFE lite hardware does not provide a functional PIX path, but after the per sub-device type resource changes the PIX subdev name is still assigned unconditionally. Only assign the PIX subdev name on non-lite VFE variants to avoid exposing a misleading device name. Fixes: ae44829a4a97 ("media: qcom: camss: Add per sub-device type resources") Signed-off-by: Wenmeng Liu Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-vfe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 7dc937d018f6..cc9a9685f6b5 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -2055,7 +2055,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe, v4l2_subdev_init(sd, &vfe_v4l2_ops); sd->internal_ops = &vfe_v4l2_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - if (i == VFE_LINE_PIX) + if (i == VFE_LINE_PIX && vfe->res->is_lite == false) snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s", MSM_VFE_NAME, vfe->id, "pix"); else From 49a2cc83540c6abc81bb137d0e8da3b59b1c2fcf Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 25 Feb 2026 16:14:46 +0100 Subject: [PATCH 113/394] MAINTAINERS: add myself as a CAMSS patch reviewer Add myself as a reviewer of Qualcomm CAMSS subsystem patches and delete inactive maintainers (Todor & Robert). Signed-off-by: Loic Poulain Reviewed-by: Vladimir Zapolskiy Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- MAINTAINERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 81d53481d3f7..5c66788998e8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21834,10 +21834,9 @@ F: drivers/bluetooth/btqcomsmd.c F: drivers/bluetooth/hci_qca.c QUALCOMM CAMERA SUBSYSTEM DRIVER -M: Robert Foss -M: Todor Tomov M: Bryan O'Donoghue R: Vladimir Zapolskiy +R: Loic Poulain L: linux-media@vger.kernel.org S: Maintained F: Documentation/admin-guide/media/qcom_camss.rst From 5837c36e012924709c0aaa997c5c8efc898f6a75 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Thu, 26 Feb 2026 10:08:50 +0100 Subject: [PATCH 114/394] media: qcom: camss: Add debug message to camss-video format check Add a debug trace to video_check_format() to log both the subdev-reported format and the format requested by the video node. This makes it easier to diagnose mismatches between subdev output and the negotiated V4L2 pixel format, as well as issues related to plane count, resolution, or field settings. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-video.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 831486e14754..f52d8e84f970 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -215,6 +215,12 @@ static int video_check_format(struct camss_video *video) if (ret < 0) return ret; + dev_dbg(video->camss->dev, + "%s: format is (%ux%u %p4cc/%up field:%u), trying (%ux%u %p4cc/%up field:%u)", + video->vdev.name, sd_pix->width, sd_pix->height, &sd_pix->pixelformat, + sd_pix->num_planes, sd_pix->field, pix->width, pix->height, &pix->pixelformat, + pix->num_planes, pix->field); + if (pix->pixelformat != sd_pix->pixelformat || pix->height != sd_pix->height || pix->width != sd_pix->width || From 91978c39ee90f0549a2a74e2e56a3e09c5ce6877 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 13 Mar 2026 20:51:50 +0100 Subject: [PATCH 115/394] media: qcom: camss: Add per-format BPL alignment helper Add camss_format_get_bpl_alignment(), a helper that returns the bytes-per-line (BPL) alignment requirement for a given CAMSS format. Different RAW Bayer packing schemes impose different BPL alignment constraints (e.g. RAW10 requires multiples of 5 bytes, RAW12 multiples of 3 bytes, RAW14 multiples of 7 bytes, etc.). Centralizing this logic makes the alignment rules explicit and avoids duplicating them across the pipeline. This will allow PIX paths and buffer preparation code to correctly round up BPL values to hardware-required boundaries. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-format.c | 14 ++++++++++++++ drivers/media/platform/qcom/camss/camss-format.h | 1 + 2 files changed, 15 insertions(+) diff --git a/drivers/media/platform/qcom/camss/camss-format.c b/drivers/media/platform/qcom/camss/camss-format.c index 4a3d5549615c..52cb01306ee0 100644 --- a/drivers/media/platform/qcom/camss/camss-format.c +++ b/drivers/media/platform/qcom/camss/camss-format.c @@ -7,8 +7,10 @@ * Copyright (c) 2023, The Linux Foundation. All rights reserved. * Copyright (c) 2023 Qualcomm Technologies, Inc. */ +#include #include #include +#include #include "camss-format.h" @@ -33,6 +35,18 @@ u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nf return formats[0].mbus_bpp; } +/* + * camss_format_get_bpl_alignment - Retrieve required BPL alignment for a given format. + * @format: a pointer to the format + * + * Return the required alignment, in bytes. + */ +unsigned int camss_format_get_bpl_alignment(const struct camss_format_info *format) +{ + /* Minimal number of bytes required to keep the line length an integer number of pixels */ + return lcm_not_zero(format->mbus_bpp, BITS_PER_BYTE) / BITS_PER_BYTE; +} + /* * camss_format_find_code - Find a format code in an array * @code: a pointer to media bus format codes array diff --git a/drivers/media/platform/qcom/camss/camss-format.h b/drivers/media/platform/qcom/camss/camss-format.h index 923a48c9c3fb..4f87ac8c4975 100644 --- a/drivers/media/platform/qcom/camss/camss-format.h +++ b/drivers/media/platform/qcom/camss/camss-format.h @@ -55,6 +55,7 @@ struct camss_formats { }; u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nformats, u32 code); +unsigned int camss_format_get_bpl_alignment(const struct camss_format_info *f); u32 camss_format_find_code(u32 *code, unsigned int n_code, unsigned int index, u32 req_code); int camss_format_find_format(u32 code, u32 pixelformat, const struct camss_format_info *formats, unsigned int nformats); From ac437a96b7e4ecf92c59f296f8eb678dda018fc8 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 13 Mar 2026 20:51:51 +0100 Subject: [PATCH 116/394] media: qcom: camss: Use proper BPL alignment helper and non-power-of-two rounding Bytes-per-line (BPL) alignment in CAMSS currently uses ALIGN(), which only works correctly for power-of-two values. Some RAW Bayer packing formats (e.g. RAW10/12/14) require non-power-of-two alignment such as 3, 5, or 7-byte multiples, so ALIGN() produces incorrect results. Introduce the use of roundup() with the per-format alignment returned by camss_format_get_bpl_alignment() when no hardware alignment is enforced (video->bpl_alignment). Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-video.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index f52d8e84f970..0852eb6f1315 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -47,6 +47,9 @@ static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus, unsigned int i; u32 bytesperline; + if (!alignment) + alignment = camss_format_get_bpl_alignment(f); + memset(pix, 0, sizeof(*pix)); v4l2_fill_pix_format_mplane(pix, mbus); pix->pixelformat = f->pixelformat; @@ -54,7 +57,7 @@ static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus, for (i = 0; i < pix->num_planes; i++) { bytesperline = pix->width / f->hsub[i].numerator * f->hsub[i].denominator * f->bpp[i] / 8; - bytesperline = ALIGN(bytesperline, alignment); + bytesperline = roundup(bytesperline, alignment); pix->plane_fmt[i].bytesperline = bytesperline; pix->plane_fmt[i].sizeimage = pix->height / f->vsub[i].numerator * f->vsub[i].denominator * @@ -459,6 +462,7 @@ static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) { + unsigned int alignment = video->bpl_alignment; struct v4l2_pix_format_mplane *pix_mp; const struct camss_format_info *fi; struct v4l2_plane_pix_format *p; @@ -491,6 +495,9 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) width = pix_mp->width; height = pix_mp->height; + if (!alignment) + alignment = camss_format_get_bpl_alignment(fi); + memset(pix_mp, 0, sizeof(*pix_mp)); pix_mp->pixelformat = fi->pixelformat; @@ -500,7 +507,7 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) for (i = 0; i < pix_mp->num_planes; i++) { bpl = pix_mp->width / fi->hsub[i].numerator * fi->hsub[i].denominator * fi->bpp[i] / 8; - bpl = ALIGN(bpl, video->bpl_alignment); + bpl = roundup(bpl, alignment); pix_mp->plane_fmt[i].bytesperline = bpl; pix_mp->plane_fmt[i].sizeimage = pix_mp->height / fi->vsub[i].numerator * fi->vsub[i].denominator * bpl; @@ -525,7 +532,7 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) lines = p->sizeimage / p->bytesperline; if (p->bytesperline < bytesperline[i]) - p->bytesperline = ALIGN(bytesperline[i], 8); + p->bytesperline = roundup(bytesperline[i], alignment); if (p->sizeimage < p->bytesperline * lines) p->sizeimage = p->bytesperline * lines; From e458c14072d95698637f1630b9d972509dccd8d5 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 13 Mar 2026 20:51:52 +0100 Subject: [PATCH 117/394] media: qcom: camss: vfe: Make PIX BPL alignment format-based on CAMSS_2290 Split the VFE bytes-per-line (BPL) alignment logic into separate helpers for RDI and PIX paths. RDI is usually aligned on RDI write engine bus constraint such as 64-bit or 128-bit. But PIX engine is usually (at least on platform I looked at) based on pixel format. On CAMSS_2290, PIX BPL alignment is set to 0 to indicate that the alignment must be derived from the pixel format. This allows the pipeline to use camss_format_get_bpl_alignment(). For other platforms, retain the legacy PIX default (16 bytes), until PIX is properly tested/enabled. A future improvement would be to remove platform-specific conditionals from the VFE code and move the alignment requirements into the per-platform VFE resource data. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue [bod: Fixed straggling newlines] Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-vfe.c | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index cc9a9685f6b5..319d19158988 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -1998,7 +1998,7 @@ static const struct media_entity_operations vfe_media_ops = { .link_validate = v4l2_subdev_link_validate, }; -static int vfe_bpl_align(struct vfe_device *vfe) +static int vfe_bpl_align_rdi(struct vfe_device *vfe) { int ret = 8; @@ -2023,6 +2023,24 @@ static int vfe_bpl_align(struct vfe_device *vfe) return ret; } +static int vfe_bpl_align_pix(struct vfe_device *vfe) +{ + int ret = 16; + + switch (vfe->camss->res->version) { + case CAMSS_2290: + /* The alignment/bpl depends solely on the pixel format and is + * computed dynamically in camss_format_get_bpl_alignment(). + */ + ret = 0; + break; + default: + break; + } + + return ret; +} + /* * msm_vfe_register_entities - Register subdev node for VFE module * @vfe: VFE device @@ -2089,11 +2107,12 @@ int msm_vfe_register_entities(struct vfe_device *vfe, } video_out->ops = &vfe->video_ops; - video_out->bpl_alignment = vfe_bpl_align(vfe); - video_out->line_based = 0; if (i == VFE_LINE_PIX) { - video_out->bpl_alignment = 16; + video_out->bpl_alignment = vfe_bpl_align_pix(vfe); video_out->line_based = 1; + } else { + video_out->bpl_alignment = vfe_bpl_align_rdi(vfe); + video_out->line_based = 0; } video_out->nformats = vfe->line[i].nformats; From 93ea81d16570442dbca04d2e2563ae8c3e65fa1b Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 7 Apr 2026 11:34:51 +0100 Subject: [PATCH 118/394] media: qcom: camss: Fix RDI streaming for CSID 680 Fix streaming to RDI1 and RDI2. csid->phy.en_vc contains a bitmask of enabled CSID ports not virtual channels. We cycle through the number of available CSID ports and test this value against the vc_en bitmask. We then use the passed value both as an index to the port configuration macros and as a virtual channel index. This is a very broken pattern. Reviewing the initial introduction of VC support it states that you can only map one CSID to one VFE. This is true however each CSID has multiple sources which can sink inside of the VFE - for example there is a "pixel" path for bayer stats which sources @ CSID(x):3 and sinks on VFE(x):pix. That is CSID port # 3 should drive VFE port #3. With our current setup only a sensor which drives virtual channel number #3 could possibly enable that setup. This is deeply wrong the virtual channel has no relevance to hooking CSID to VFE, a fact that is proven after this patch is applied allowing RDI0,RDI1 and RDI2 to function with VC0 whereas before only RDI1 worked. Another way the current model breaks is the DT field. A sensor driving different data-types on the same VC would not be able to separate the VC:DT pair to separate RDI outputs, thus breaking another feature of VCs in the MIPI data-stream. Default the VC back to zero. A follow on series will implement subdev streams to actually enable VCs without breaking CSID source to VFE sink. Fixes: 253314b20408 ("media: qcom: camss: Add CSID 680 support") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/camss/camss-csid-680.c | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-680.c b/drivers/media/platform/qcom/camss/camss-csid-680.c index 2e4547455d22..345a67c8fb94 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-680.c +++ b/drivers/media/platform/qcom/camss/camss-csid-680.c @@ -231,9 +231,9 @@ static void __csid_configure_top(struct csid_device *csid) CSID_TOP_IO_PATH_CFG0(csid->id)); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -245,28 +245,28 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 lane_cnt = 4; val = 0; - writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port)); /* * DT_ID is a two bit bitfield that is concatenated with * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - dt_id = vc & 0x03; + dt_id = port & 0x03; /* note: for non-RDI path, this should be format->decode_format */ val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; val |= format->data_type << RDI_CFG0_DATA_TYPE; val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; val |= dt_id << RDI_CFG0_DT_ID; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); val = RDI_CFG1_TIMESTAMP_STB_FRAME; val |= RDI_CFG1_BYTE_CNTR_EN; @@ -277,23 +277,23 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= RDI_CFG1_CROP_V_EN; val |= RDI_CFG1_PACKING_MIPI; - writel(val, csid->base + CSID_RDI_CFG1(vc)); + writel(val, csid->base + CSID_RDI_CFG1(port)); val = 0; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port)); val = 1; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port)); val = 0; - writel(val, csid->base + CSID_RDI_CTRL(vc)); + writel(val, csid->base + CSID_RDI_CTRL(port)); - val = readl(csid->base + CSID_RDI_CFG0(vc)); + val = readl(csid->base + CSID_RDI_CFG0(port)); if (enable) val |= RDI_CFG0_ENABLE; else val &= ~RDI_CFG0_ENABLE; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) @@ -302,11 +302,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) __csid_configure_top(csid); - /* Loop through all enabled VCs and configure stream for each */ + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { if (csid->phy.en_vc & BIT(i)) { - __csid_configure_rdi_stream(csid, enable, i); - __csid_configure_rx(csid, &csid->phy, i); + __csid_configure_rdi_stream(csid, enable, i, 0); + __csid_configure_rx(csid, &csid->phy, 0); __csid_ctrl_rdi(csid, enable, i); } } From cc1c35619c9895f918f9e388e3caa6ab2ff2fbe7 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 7 Apr 2026 11:34:52 +0100 Subject: [PATCH 119/394] media: qcom: camss: Fix RDI streaming for CSID 340 Fix streaming from CSIDn RDI1 and RDI2 to VFEn RDI1 and RDI2. A pattern we have replicated throughout CAMSS where we use the VC number to populate both the VC fields and port fields of the CSID means that in practice only VC = 0 on CSIDn:RDI0 to VFEn:RDI0 works. Fix that for CSID 340 by separating VC and port. Fix to VC zero as a bugfix we will look to properly populate the VC field with follow on patches later. Fixes: f0fc808a466a ("media: qcom: camss: Add CSID 340 support") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/camss/camss-csid-340.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c index 2b50f9b96a34..0231985746ed 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-340.c +++ b/drivers/media/platform/qcom/camss/camss-csid-340.c @@ -74,9 +74,9 @@ static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) writel_relaxed(!!enable, csid->base + CSID_RDI_CTRL(rdi)); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -88,14 +88,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - dt_id = vc & 0x03; + dt_id = port & 0x03; val = CSID_RDI_CFG0_DECODE_FORMAT_NOP; /* only for RDI path */ val |= FIELD_PREP(CSID_RDI_CFG0_DT_MASK, format->data_type); @@ -105,10 +105,11 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 if (enable) val |= CSID_RDI_CFG0_ENABLE; - dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x vc=%u)\n", - csid->id, enable ? "enable" : "disable", format->data_type, vc); + dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x port=%u vc=%u)\n", + csid->id, enable ? "enable" : "disable", format->data_type, + port, vc); - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) @@ -117,9 +118,10 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) __csid_configure_rx(csid, &csid->phy); + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { if (csid->phy.en_vc & BIT(i)) { - __csid_configure_rdi_stream(csid, enable, i); + __csid_configure_rdi_stream(csid, enable, i, 0); __csid_ctrl_rdi(csid, enable, i); } } From 618765634cefbdddafa84f07f82e9dd05b86cb9c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 7 Apr 2026 11:34:53 +0100 Subject: [PATCH 120/394] media: qcom: camss: Fix RDI streaming for CSID GEN2 Fix streaming from CSIDn RDI1 and RDI2 to VFEn RDI1 and RDI2. A pattern we have replicated throughout CAMSS where we use the VC number to populate both the VC fields and port fields of the CSID means that in practice only VC = 0 on CSIDn:RDI0 to VFEn:RDI0 works. Fix that for CSID gen2 by separating VC and port. Fix to VC zero as a bugfix we will look to properly populate the VC field with follow on patches later. Fixes: 729fc005c8e2 ("media: qcom: camss: Split testgen, RDI and RX for CSID 170") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/camss/camss-csid-gen2.c | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c index 2a1746dcc1c5..eadcb2f7e3aa 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c @@ -203,10 +203,10 @@ static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) writel_relaxed(val, csid->base + CSID_RDI_CTRL(rdi)); } -static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 port, u8 vc) { struct csid_testgen_config *tg = &csid->testgen; - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -253,10 +253,10 @@ static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc) writel_relaxed(val, csid->base + CSID_TPG_CTRL); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -267,14 +267,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - u8 dt_id = vc & 0x03; + u8 dt_id = port & 0x03; val = 1 << RDI_CFG0_BYTE_CNTR_EN; val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN; @@ -284,56 +284,57 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= format->data_type << RDI_CFG0_DATA_TYPE; val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; val |= dt_id << RDI_CFG0_DT_ID; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG0(port)); /* CSID_TIMESTAMP_STB_POST_IRQ */ val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL; - writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG1(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(port)); val = 1; - writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc)); + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(port)); val = 0; - writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CTRL(port)); - val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc)); + val = readl_relaxed(csid->base + CSID_RDI_CFG0(port)); val |= enable << RDI_CFG0_ENABLE; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + writel_relaxed(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) { struct csid_testgen_config *tg = &csid->testgen; u8 i; - /* Loop through all enabled VCs and configure stream for each */ + + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) if (csid->phy.en_vc & BIT(i)) { if (tg->enabled) - __csid_configure_testgen(csid, enable, i); + __csid_configure_testgen(csid, enable, i, 0); - __csid_configure_rdi_stream(csid, enable, i); - __csid_configure_rx(csid, &csid->phy, i); + __csid_configure_rdi_stream(csid, enable, i, 0); + __csid_configure_rx(csid, &csid->phy, 0); __csid_ctrl_rdi(csid, enable, i); } } From ad136e52634d5a393a9dc93383f8ea1e898faf37 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 7 Apr 2026 11:34:54 +0100 Subject: [PATCH 121/394] media: qcom: camss: Fix RDI streaming for CSID GEN3 Fix streaming from CSIDn RDI1 and RDI2 to VFEn RDI1 and RDI2. A pattern we have replicated throughout CAMSS where we use the VC number to populate both the VC fields and port fields of the CSID means that in practice only VC = 0 on CSIDn:RDI0 to VFEn:RDI0 works. Fix that for CSID gen3 by separating VC and port. Fix to VC zero as a bugfix we will look to properly populate the VC field with follow on patches later. Fixes: d96fe1808dcc ("media: qcom: camss: Add CSID 780 support") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/camss/camss-csid-gen3.c | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen3.c b/drivers/media/platform/qcom/camss/camss-csid-gen3.c index 222765fe7914..0fdbf75fb27d 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen3.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen3.c @@ -157,12 +157,12 @@ static void __csid_configure_wrapper(struct csid_device *csid) writel(val, csid->camss->csid_wrapper_base + CSID_IO_PATH_CFG0(csid->id)); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { u32 val; u8 lane_cnt = csid->phy.lane_cnt; /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); @@ -175,14 +175,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(vc) + * CSID_RDI_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 * * CID : VC 3:0 << 2 | DT_ID 1:0 */ - u8 dt_id = vc & 0x03; + u8 dt_id = port & 0x03; val = RDI_CFG0_TIMESTAMP_EN; val |= RDI_CFG0_TIMESTAMP_STB_SEL; @@ -192,7 +192,7 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= format->data_type << RDI_CFG0_DT; val |= dt_id << RDI_CFG0_DT_ID; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); val = RDI_CFG1_PACKING_FORMAT_MIPI; val |= RDI_CFG1_PIX_STORE; @@ -201,22 +201,22 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val |= RDI_CFG1_CROP_H_EN; val |= RDI_CFG1_CROP_V_EN; - writel(val, csid->base + CSID_RDI_CFG1(vc)); + writel(val, csid->base + CSID_RDI_CFG1(port)); val = 0; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port)); val = 1; - writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port)); val = 0; - writel(val, csid->base + CSID_RDI_CTRL(vc)); + writel(val, csid->base + CSID_RDI_CTRL(port)); - val = readl(csid->base + CSID_RDI_CFG0(vc)); + val = readl(csid->base + CSID_RDI_CFG0(port)); if (enable) val |= RDI_CFG0_EN; - writel(val, csid->base + CSID_RDI_CFG0(vc)); + writel(val, csid->base + CSID_RDI_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) @@ -225,11 +225,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) __csid_configure_wrapper(csid); - /* Loop through all enabled VCs and configure stream for each */ + /* Loop through all enabled ports and configure a stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) if (csid->phy.en_vc & BIT(i)) { - __csid_configure_rdi_stream(csid, enable, i); - __csid_configure_rx(csid, &csid->phy, i); + __csid_configure_rdi_stream(csid, enable, i, 0); + __csid_configure_rx(csid, &csid->phy, 0); __csid_ctrl_rdi(csid, enable, i); } } From c43207553867c150963308418a88dcd59bb2a3f0 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 25 Jan 2026 13:30:05 +0200 Subject: [PATCH 122/394] media: iris: retrieve UBWC platform configuration Specifying UBWC data in each driver doesn't scale and is prone to errors. Request UBWC data from the central database in preparation to using it through the rest of the driver. Reviewed-by: Bryan O'Donoghue Reviewed-by: Konrad Dybcio Reviewed-by: Dikshita Agarwal Tested-by: Wangao Wang Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/Kconfig | 1 + drivers/media/platform/qcom/iris/iris_core.h | 4 ++++ drivers/media/platform/qcom/iris/iris_probe.c | 5 +++++ 3 files changed, 10 insertions(+) diff --git a/drivers/media/platform/qcom/iris/Kconfig b/drivers/media/platform/qcom/iris/Kconfig index 3c803a05305a..39b06de6c3e6 100644 --- a/drivers/media/platform/qcom/iris/Kconfig +++ b/drivers/media/platform/qcom/iris/Kconfig @@ -5,6 +5,7 @@ config VIDEO_QCOM_IRIS select V4L2_MEM2MEM_DEV select QCOM_MDT_LOADER if ARCH_QCOM select QCOM_SCM + select QCOM_UBWC_CONFIG select VIDEOBUF2_DMA_CONTIG help This is a V4L2 driver for Qualcomm iris video accelerator diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h index fb194c967ad4..d10a03aa5685 100644 --- a/drivers/media/platform/qcom/iris/iris_core.h +++ b/drivers/media/platform/qcom/iris/iris_core.h @@ -30,6 +30,8 @@ enum domain_type { DECODER = BIT(1), }; +struct qcom_ubwc_cfg_data; + /** * struct iris_core - holds core parameters valid for all instances * @@ -52,6 +54,7 @@ enum domain_type { * @resets: table of iris reset clocks * @controller_resets: table of controller reset clocks * @iris_platform_data: a structure for platform data + * @ubwc_cfg: UBWC configuration for the platform * @state: current state of core * @iface_q_table_daddr: device address for interface queue table memory * @sfr_daddr: device address for SFR (Sub System Failure Reason) register memory @@ -95,6 +98,7 @@ struct iris_core { struct reset_control_bulk_data *resets; struct reset_control_bulk_data *controller_resets; const struct iris_platform_data *iris_platform_data; + const struct qcom_ubwc_cfg_data *ubwc_cfg; enum iris_core_state state; dma_addr_t iface_q_table_daddr; dma_addr_t sfr_daddr; diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index ddaacda523ec..492f85f518eb 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "iris_core.h" #include "iris_ctrls.h" @@ -244,6 +245,10 @@ static int iris_probe(struct platform_device *pdev) core->iris_platform_data = of_device_get_match_data(core->dev); + core->ubwc_cfg = qcom_ubwc_config_get_data(); + if (IS_ERR(core->ubwc_cfg)) + return PTR_ERR(core->ubwc_cfg); + ret = devm_request_threaded_irq(core->dev, core->irq, iris_hfi_isr, iris_hfi_isr_handler, IRQF_TRIGGER_HIGH, "iris", core); if (ret) From 0c2bc74465c2298cbbcd0af0d0946b7b038c5e2d Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 25 Jan 2026 13:30:06 +0200 Subject: [PATCH 123/394] media: iris: don't specify min_acc_length in the source code The min_acc length can be calculated from the platform UBWC configuration. Use the freshly introduced helper and calculate min_acc length based on the platform UBWC configuration instead of specifying it directly in the source. Reviewed-by: Bryan O'Donoghue Reviewed-by: Konrad Dybcio Reviewed-by: Dikshita Agarwal Tested-by: Wangao Wang Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c | 6 +++++- drivers/media/platform/qcom/iris/iris_platform_common.h | 1 - drivers/media/platform/qcom/iris/iris_platform_gen2.c | 1 - 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c index d77fa29f44fc..aa4520b27739 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c @@ -3,6 +3,9 @@ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ +#include +#include + #include "iris_hfi_common.h" #include "iris_hfi_gen2.h" #include "iris_hfi_gen2_packet.h" @@ -120,6 +123,7 @@ static void iris_hfi_gen2_create_packet(struct iris_hfi_header *hdr, u32 pkt_typ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_header *hdr) { + const struct qcom_ubwc_cfg_data *ubwc = core->ubwc_cfg; u32 payload = 0; iris_hfi_gen2_create_header(hdr, 0, core->header_id++); @@ -146,7 +150,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->mal_length; + payload = qcom_ubwc_min_acc_length_64b(ubwc) ? 64 : 32; iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_MAL_LENGTH, HFI_HOST_FLAGS_NONE, diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 5a489917580e..08a9529e599b 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -77,7 +77,6 @@ struct tz_cp_config { struct ubwc_config_data { u32 max_channels; - u32 mal_length; u32 highest_bank_bit; u32 bank_swzl_level; u32 bank_swz2_level; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 5da90d47f9c6..01c6ffa7e084 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -792,7 +792,6 @@ static const char * const sm8550_opp_clk_table[] = { static struct ubwc_config_data ubwc_config_sm8550 = { .max_channels = 8, - .mal_length = 32, .highest_bank_bit = 16, .bank_swzl_level = 0, .bank_swz2_level = 1, From 68b41e1aaeb18b5e970a86f3c52264fdedbbc423 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 25 Jan 2026 13:30:07 +0200 Subject: [PATCH 124/394] media: iris: don't specify highest_bank_bit in the source code The highest_bank_bit param is specified both in the Iris driver and in the platform UBWC config. Use the platform UBWC configuration instead of specifying it directly in the source. Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Tested-by: Wangao Wang Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c | 2 +- drivers/media/platform/qcom/iris/iris_platform_common.h | 1 - drivers/media/platform/qcom/iris/iris_platform_gen2.c | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c index aa4520b27739..6dc0cbaa9c19 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c @@ -160,7 +160,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->highest_bank_bit; + payload = ubwc->highest_bank_bit; iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_HBB, HFI_HOST_FLAGS_NONE, diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 08a9529e599b..5639eb5a75b6 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -77,7 +77,6 @@ struct tz_cp_config { struct ubwc_config_data { u32 max_channels; - u32 highest_bank_bit; u32 bank_swzl_level; u32 bank_swz2_level; u32 bank_swz3_level; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 01c6ffa7e084..bdeb92e0b7bc 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -792,7 +792,6 @@ static const char * const sm8550_opp_clk_table[] = { static struct ubwc_config_data ubwc_config_sm8550 = { .max_channels = 8, - .highest_bank_bit = 16, .bank_swzl_level = 0, .bank_swz2_level = 1, .bank_swz3_level = 1, From 8f1a4226dfbdd665a5a9ee28f1e33343cc2b30d7 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 25 Jan 2026 13:30:08 +0200 Subject: [PATCH 125/394] media: iris: don't specify ubwc_swizzle in the source code The UBWC swizzle is specified both in the Iris driver and in the platform UBWC config. Use the platform UBWC configuration instead of specifying it directly in the source. Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Tested-by: Wangao Wang Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c | 6 +++--- drivers/media/platform/qcom/iris/iris_platform_common.h | 3 --- drivers/media/platform/qcom/iris/iris_platform_gen2.c | 3 --- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c index 6dc0cbaa9c19..a4d9efdbb43b 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c @@ -170,7 +170,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->bank_swzl_level; + payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL1); iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_BANK_SWZL_LEVEL1, HFI_HOST_FLAGS_NONE, @@ -180,7 +180,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->bank_swz2_level; + payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL2); iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_BANK_SWZL_LEVEL2, HFI_HOST_FLAGS_NONE, @@ -190,7 +190,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->bank_swz3_level; + payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL3); iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_BANK_SWZL_LEVEL3, HFI_HOST_FLAGS_NONE, diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 5639eb5a75b6..e217f15ef028 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -77,9 +77,6 @@ struct tz_cp_config { struct ubwc_config_data { u32 max_channels; - u32 bank_swzl_level; - u32 bank_swz2_level; - u32 bank_swz3_level; u32 bank_spreading; }; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index bdeb92e0b7bc..8072f430bd26 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -792,9 +792,6 @@ static const char * const sm8550_opp_clk_table[] = { static struct ubwc_config_data ubwc_config_sm8550 = { .max_channels = 8, - .bank_swzl_level = 0, - .bank_swz2_level = 1, - .bank_swz3_level = 1, .bank_spreading = 1, }; From 838dcd80db90b66e7d989b425c3c3574868f7ca4 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 25 Jan 2026 13:30:09 +0200 Subject: [PATCH 126/394] media: iris: don't specify bank_spreading in the source code The UBWC bank spreading is specified both in the Iris driver and in the platform UBWC config. Use the platform UBWC configuration instead of specifying it directly in the source. Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Tested-by: Wangao Wang Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c | 2 +- drivers/media/platform/qcom/iris/iris_platform_common.h | 1 - drivers/media/platform/qcom/iris/iris_platform_gen2.c | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c index a4d9efdbb43b..a49394b92768 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c @@ -200,7 +200,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->bank_spreading; + payload = qcom_ubwc_bank_spread(ubwc); iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_BANK_SPREADING, HFI_HOST_FLAGS_NONE, diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index e217f15ef028..07c58cf3a14a 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -77,7 +77,6 @@ struct tz_cp_config { struct ubwc_config_data { u32 max_channels; - u32 bank_spreading; }; struct platform_inst_caps { diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 8072f430bd26..4e617176dee4 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -792,7 +792,6 @@ static const char * const sm8550_opp_clk_table[] = { static struct ubwc_config_data ubwc_config_sm8550 = { .max_channels = 8, - .bank_spreading = 1, }; static const struct tz_cp_config tz_cp_config_sm8550[] = { From 59d1b40ce3d76480cee6389a0015f1ca0f49c336 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 25 Jan 2026 13:30:10 +0200 Subject: [PATCH 127/394] media: iris: don't specify max_channels in the source code The UBWC max_channels spreading is specified in the Iris driver, but it also can be calculated from the platform UBWC config. Use the platform UBWC configuration instead of specifying it directly in the source. Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Tested-by: Wangao Wang Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c | 2 +- drivers/media/platform/qcom/iris/iris_platform_common.h | 1 - drivers/media/platform/qcom/iris/iris_platform_gen2.c | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c index a49394b92768..0d05dd2afc07 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c @@ -140,7 +140,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade &payload, sizeof(u32)); - payload = core->iris_platform_data->ubwc_config->max_channels; + payload = qcom_ubwc_macrotile_mode(ubwc) ? 8 : 4; iris_hfi_gen2_create_packet(hdr, HFI_PROP_UBWC_MAX_CHANNELS, HFI_HOST_FLAGS_NONE, diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 07c58cf3a14a..e8b5446dce76 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -76,7 +76,6 @@ struct tz_cp_config { }; struct ubwc_config_data { - u32 max_channels; }; struct platform_inst_caps { diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 4e617176dee4..05b1dd11abce 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -791,7 +791,6 @@ static const char * const sm8550_opp_clk_table[] = { }; static struct ubwc_config_data ubwc_config_sm8550 = { - .max_channels = 8, }; static const struct tz_cp_config tz_cp_config_sm8550[] = { From 28d2ab064dfd7a349bbe14d211f3c95a4efde370 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 25 Jan 2026 13:30:11 +0200 Subject: [PATCH 128/394] media: iris: drop remnants of UBWC configuration Now as all UBWC configuration bits were migrated to be used or derived from the global UBWC platform-specific data, drop the unused struct and field definitions. Reviewed-by: Konrad Dybcio Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Tested-by: Wangao Wang Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_platform_common.h | 4 ---- drivers/media/platform/qcom/iris/iris_platform_gen2.c | 7 ------- 2 files changed, 11 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index e8b5446dce76..f42e1798747c 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -75,9 +75,6 @@ struct tz_cp_config { u32 cp_nonpixel_size; }; -struct ubwc_config_data { -}; - struct platform_inst_caps { u32 min_frame_width; u32 max_frame_width; @@ -241,7 +238,6 @@ struct iris_platform_data { u32 tz_cp_config_data_size; u32 core_arch; u32 hw_response_timeout; - struct ubwc_config_data *ubwc_config; u32 num_vpp_pipe; bool no_aon; u32 max_session_count; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 05b1dd11abce..a526b50a1cd3 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -790,9 +790,6 @@ static const char * const sm8550_opp_clk_table[] = { NULL, }; -static struct ubwc_config_data ubwc_config_sm8550 = { -}; - static const struct tz_cp_config tz_cp_config_sm8550[] = { { .cp_start = 0, @@ -949,7 +946,6 @@ const struct iris_platform_data sm8550_data = { .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, - .ubwc_config = &ubwc_config_sm8550, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, @@ -1054,7 +1050,6 @@ const struct iris_platform_data sm8650_data = { .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, - .ubwc_config = &ubwc_config_sm8550, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, @@ -1150,7 +1145,6 @@ const struct iris_platform_data sm8750_data = { .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, - .ubwc_config = &ubwc_config_sm8550, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, @@ -1250,7 +1244,6 @@ const struct iris_platform_data qcs8300_data = { .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, - .ubwc_config = &ubwc_config_sm8550, .num_vpp_pipe = 2, .max_session_count = 16, .max_core_mbpf = ((4096 * 2176) / 256) * 4, From ab128251dace7f234f69a51bbe3d0b40ed9814c6 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 9 Feb 2026 03:32:16 +0200 Subject: [PATCH 129/394] media: dt-bindings: qcom,sm8250-venus: sort out power domains First of all, on SM8250 Iris (ex-Venus) core needs to scale clocks which are powered by the MMCX domain. Add MMCX domain to the list of the power domain to be used on this platform. Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Acked-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bryan O'Donoghue --- .../devicetree/bindings/media/qcom,sm8250-venus.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml index da54493220c9..43a10d9f664e 100644 --- a/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml @@ -22,7 +22,7 @@ properties: power-domains: minItems: 2 - maxItems: 3 + maxItems: 4 power-domain-names: minItems: 2 @@ -30,6 +30,7 @@ properties: - const: venus - const: vcodec0 - const: mx + - const: mmcx clocks: maxItems: 3 @@ -114,8 +115,12 @@ examples: interrupts = ; power-domains = <&videocc MVS0C_GDSC>, <&videocc MVS0_GDSC>, - <&rpmhpd RPMHPD_MX>; - power-domain-names = "venus", "vcodec0", "mx"; + <&rpmhpd RPMHPD_MX>, + <&rpmhpd RPMHPD_MMCX>; + power-domain-names = "venus", + "vcodec0", + "mx", + "mmcx"; clocks = <&gcc GCC_VIDEO_AXI0_CLK>, <&videocc VIDEO_CC_MVS0C_CLK>, From 4170e2f25ce40b69f3827fcdabf395380095e136 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 9 Feb 2026 03:32:17 +0200 Subject: [PATCH 130/394] media: iris: scale MMCX power domain on SM8250 On SM8250 most of the video clocks are powered by the MMCX domain, while the PLL is powered on by the MX domain. Extend the driver to support scaling both power domains, while keeping compatibility with the existing DTs, which define only the MX domain. Fixes: 79865252acb6 ("media: iris: enable video driver probe of SM8250 SoC") Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_platform_gen1.c | 2 +- drivers/media/platform/qcom/iris/iris_probe.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index df8e6bf9430e..aa71f7f53ee3 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -281,7 +281,7 @@ static const struct bw_info sm8250_bw_table_dec[] = { static const char * const sm8250_pmdomain_table[] = { "venus", "vcodec0" }; -static const char * const sm8250_opp_pd_table[] = { "mx" }; +static const char * const sm8250_opp_pd_table[] = { "mx", "mmcx" }; static const struct platform_clk_data sm8250_clk_table[] = { {IRIS_AXI_CLK, "iface" }, diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index 492f85f518eb..511c8b56cf5d 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -65,6 +65,13 @@ static int iris_init_power_domains(struct iris_core *core) return ret; ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, &core->opp_pmdomain_tbl); + /* backwards compatibility for incomplete ABI SM8250 */ + if (ret == -ENODEV && + of_device_is_compatible(core->dev->of_node, "qcom,sm8250-venus")) { + iris_opp_pd_data.num_pd_names--; + ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, + &core->opp_pmdomain_tbl); + } if (ret < 0) return ret; From f45fd3dbb5f6d60a8b31d5cc4affaf1ef40163b3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 9 Feb 2026 03:32:18 +0200 Subject: [PATCH 131/394] media: venus: scale MMCX power domain on SM8250 On SM8250 most of the video clocks are powered by the MMCX domain, while the PLL is powered on by the MX domain. Extend the driver to support scaling both power domains, while keeping compatibility with the existing DTs, which define only the MX domain. Fixes: 0aeabfa29a9c ("media: venus: core: add sm8250 DT compatible and resource data") Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/venus/core.c | 7 ++++++- drivers/media/platform/qcom/venus/core.h | 1 + drivers/media/platform/qcom/venus/pm_helpers.c | 8 +++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 7e639760c41d..00fb6806c129 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -882,6 +882,7 @@ static const struct venus_resources sdm845_res_v2 = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" }, .vcodec_pmdomains_num = 3, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 2, .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, @@ -933,6 +934,7 @@ static const struct venus_resources sc7180_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_4XX, .vpu_version = VPU_VERSION_AR50, @@ -991,7 +993,8 @@ static const struct venus_resources sm8250_res = { .vcodec_clks_num = 1, .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, - .opp_pmdomain = (const char *[]) { "mx" }, + .opp_pmdomain = (const char *[]) { "mx", "mmcx" }, + .opp_pmdomain_num = 2, .vcodec_num = 1, .max_load = 7833600, .hfi_version = HFI_VERSION_6XX, @@ -1053,6 +1056,7 @@ static const struct venus_resources sc7280_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_6XX, .vpu_version = VPU_VERSION_IRIS2_1, @@ -1100,6 +1104,7 @@ static const struct venus_resources qcm2290_res = { .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, .opp_pmdomain = (const char *[]) { "cx" }, + .opp_pmdomain_num = 1, .vcodec_num = 1, .hfi_version = HFI_VERSION_4XX, .vpu_version = VPU_VERSION_AR50_LITE, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 7506f5d0f609..70e7b40affa9 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -83,6 +83,7 @@ struct venus_resources { const char **vcodec_pmdomains; unsigned int vcodec_pmdomains_num; const char **opp_pmdomain; + unsigned int opp_pmdomain_num; unsigned int vcodec_num; const char * const resets[VIDC_RESETS_NUM_MAX]; unsigned int resets_num; diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index f0269524ac70..14a4e8311a64 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -887,7 +887,7 @@ static int vcodec_domains_get(struct venus_core *core) }; struct dev_pm_domain_attach_data opp_pd_data = { .pd_names = res->opp_pmdomain, - .num_pd_names = 1, + .num_pd_names = res->opp_pmdomain_num, .pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP, }; @@ -904,6 +904,12 @@ static int vcodec_domains_get(struct venus_core *core) /* Attach the power domain for setting performance state */ ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain); + /* backwards compatibility for incomplete ABI SM8250 */ + if (ret == -ENODEV && + of_device_is_compatible(dev->of_node, "qcom,sm8250-venus")) { + opp_pd_data.num_pd_names--; + ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain); + } if (ret < 0) return ret; From ab4e6a16f97eb74c3ce6cef1b5acd07c08cf6629 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Mar 2026 22:19:53 +0200 Subject: [PATCH 132/394] media: dt-bindings: qcom,sc7280-venus: drop non-PAS support The only users of the non-PAS setup on SC7280 platform are the ChromeOS devices, which were cancelled before reaching end users. Iris, the alternative driver for the same hardware, does not support non-PAS setup. It is expected that in future both Venus and Iris devices will use different ABI for non-PAS (EL2) setup. In order to declare only the future-proof hardware description drop support for non-PAS setup from the SC7280 Venus schema (breaking almost non-existing SC7280 ChromeOS devices). The dropped iommus entry reflects the extra stream, which should not be treated in the same way as the main one (which doesn't match the usage described by the iommus definition). Reviewed-by: Krzysztof Kozlowski Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../devicetree/bindings/media/qcom,sc7280-venus.yaml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml index 413c5b4ee650..9725fcb761dc 100644 --- a/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml @@ -43,8 +43,7 @@ properties: - const: vcodec_bus iommus: - minItems: 1 - maxItems: 2 + maxItems: 1 interconnects: maxItems: 2 @@ -120,12 +119,7 @@ examples: <&mmss_noc MASTER_VIDEO_P0 0 &mc_virt SLAVE_EBI1 0>; interconnect-names = "cpu-cfg", "video-mem"; - iommus = <&apps_smmu 0x2180 0x20>, - <&apps_smmu 0x2184 0x20>; + iommus = <&apps_smmu 0x2180 0x20>; memory-region = <&video_mem>; - - video-firmware { - iommus = <&apps_smmu 0x21a2 0x0>; - }; }; From cc59dbb862ac3bb07f4b3f77a423569a9153fe7b Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Mar 2026 22:19:54 +0200 Subject: [PATCH 133/394] media: dt-bindings: qcom-sc7180-venus: move video-firmware here As SC7180 is the only remaining user of the non-TZ / non-PAS setup which uses the video-firmware subnode, move its definition from the common schema to the SC7180-specific one. These properties do not accurately describe the hardware. Future platforms that are going to support non-TZ setup will use different semantics and different DT ABI (using the iommu-map property). Reviewed-by: Vikash Garodia Reviewed-by: Krzysztof Kozlowski Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../bindings/media/qcom,sc7180-venus.yaml | 15 +++++++++++++++ .../bindings/media/qcom,venus-common.yaml | 15 --------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml index bfd8b1ad4731..b21bed314848 100644 --- a/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml @@ -91,6 +91,21 @@ properties: deprecated: true additionalProperties: false + video-firmware: + type: object + additionalProperties: false + + description: | + Firmware subnode is needed when the platform does not + have TrustZone. + + properties: + iommus: + maxItems: 1 + + required: + - iommus + required: - compatible - power-domain-names diff --git a/Documentation/devicetree/bindings/media/qcom,venus-common.yaml b/Documentation/devicetree/bindings/media/qcom,venus-common.yaml index 3153d91f9d18..59a3fde846d2 100644 --- a/Documentation/devicetree/bindings/media/qcom,venus-common.yaml +++ b/Documentation/devicetree/bindings/media/qcom,venus-common.yaml @@ -47,21 +47,6 @@ properties: minItems: 1 maxItems: 4 - video-firmware: - type: object - additionalProperties: false - - description: | - Firmware subnode is needed when the platform does not - have TrustZone. - - properties: - iommus: - maxItems: 1 - - required: - - iommus - required: - reg - clocks From 8f100f5896e1ccec3802dafd0f719627733c8183 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Mar 2026 22:19:56 +0200 Subject: [PATCH 134/394] media: qcom: venus: flip the venus/iris switch With the Iris and Venus driver having more or less feature parity for "HFI 6xx" platforms and with Iris gaining support for SC7280, flip the switch. Use Iris by default for SM8250 and SC7280, the platforms which are supported by both drivers, and use Venus only if Iris is not compiled at all. Use IS_ENABLED to strip out the code and data structures which are used by the disabled platforms. Reviewed-by: Konrad Dybcio Reviewed-by: Vikash Garodia Signed-off-by: Dmitry Baryshkov [bod: Moved two conditional compats inside of one ifdef for ci] [bod: Changed IS_V6(core) (0) to ((void)(core), 0) for ci] Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/Makefile | 5 +---- drivers/media/platform/qcom/iris/iris_probe.c | 2 -- drivers/media/platform/qcom/venus/core.c | 6 +++++- drivers/media/platform/qcom/venus/core.h | 11 +++++++++++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile index 2abbd3aeb4af..2fde45f81727 100644 --- a/drivers/media/platform/qcom/iris/Makefile +++ b/drivers/media/platform/qcom/iris/Makefile @@ -10,6 +10,7 @@ qcom-iris-objs += iris_buffer.o \ iris_hfi_gen2_packet.o \ iris_hfi_gen2_response.o \ iris_hfi_queue.o \ + iris_platform_gen1.o \ iris_platform_gen2.o \ iris_power.o \ iris_probe.o \ @@ -26,8 +27,4 @@ qcom-iris-objs += iris_buffer.o \ iris_vpu_buffer.o \ iris_vpu_common.o \ -ifeq ($(CONFIG_VIDEO_QCOM_VENUS),) -qcom-iris-objs += iris_platform_gen1.o -endif - obj-$(CONFIG_VIDEO_QCOM_IRIS) += qcom-iris.o diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index 511c8b56cf5d..baa13cc5c209 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -364,7 +364,6 @@ static const struct of_device_id iris_dt_match[] = { .compatible = "qcom,qcs8300-iris", .data = &qcs8300_data, }, -#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_VENUS)) { .compatible = "qcom,sc7280-venus", .data = &sc7280_data, @@ -373,7 +372,6 @@ static const struct of_device_id iris_dt_match[] = { .compatible = "qcom,sm8250-venus", .data = &sm8250_data, }, -#endif { .compatible = "qcom,sm8550-iris", .data = &sm8550_data, diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 00fb6806c129..a87e8afb23df 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -951,6 +951,7 @@ static const struct venus_resources sc7180_res = { .enc_nodename = "video-encoder", }; +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) static const struct freq_tbl sm8250_freq_table[] = { { 0, 444000000 }, { 0, 366000000 }, @@ -1073,6 +1074,7 @@ static const struct venus_resources sc7280_res = { .dec_nodename = "video-decoder", .enc_nodename = "video-encoder", }; +#endif static const struct bw_tbl qcm2290_bw_table_dec[] = { { 352800, 597000, 0, 746000, 0 }, /* 1080p@30 + 720p@30 */ @@ -1130,11 +1132,13 @@ static const struct of_device_id venus_dt_match[] = { { .compatible = "qcom,msm8998-venus", .data = &msm8998_res, }, { .compatible = "qcom,qcm2290-venus", .data = &qcm2290_res, }, { .compatible = "qcom,sc7180-venus", .data = &sc7180_res, }, - { .compatible = "qcom,sc7280-venus", .data = &sc7280_res, }, { .compatible = "qcom,sdm660-venus", .data = &sdm660_res, }, { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, }, { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, }, +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) + { .compatible = "qcom,sc7280-venus", .data = &sc7280_res, }, { .compatible = "qcom,sm8250-venus", .data = &sm8250_res, }, +#endif { } }; MODULE_DEVICE_TABLE(of, venus_dt_match); diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 70e7b40affa9..03804c30808e 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -54,8 +54,10 @@ enum vpu_version { VPU_VERSION_AR50, VPU_VERSION_AR50_LITE, VPU_VERSION_IRIS1, +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) VPU_VERSION_IRIS2, VPU_VERSION_IRIS2_1, +#endif }; struct firmware_version { @@ -526,13 +528,22 @@ struct venus_inst { #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) #define IS_V3(core) ((core)->res->hfi_version == HFI_VERSION_3XX) #define IS_V4(core) ((core)->res->hfi_version == HFI_VERSION_4XX) +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) #define IS_V6(core) ((core)->res->hfi_version == HFI_VERSION_6XX) +#else +#define IS_V6(core) (((void)(core), 0)) +#endif #define IS_AR50(core) ((core)->res->vpu_version == VPU_VERSION_AR50) #define IS_AR50_LITE(core) ((core)->res->vpu_version == VPU_VERSION_AR50_LITE) #define IS_IRIS1(core) ((core)->res->vpu_version == VPU_VERSION_IRIS1) +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)) #define IS_IRIS2(core) ((core)->res->vpu_version == VPU_VERSION_IRIS2) #define IS_IRIS2_1(core) ((core)->res->vpu_version == VPU_VERSION_IRIS2_1) +#else +#define IS_IRIS2(core) (((void)(core), 0)) +#define IS_IRIS2_1(core) (((void)(core), 0)) +#endif static inline bool is_lite(struct venus_core *core) { From 335cdc4c43a03c43b8a0073478e7dac4055aaa15 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:02 +0200 Subject: [PATCH 135/394] media: qcom: iris: drop pas_id from the iris_platform_data struct The PAS ID, the authentication service ID, used by the Iris is a constant and it is not expected to change anytime. Drop it from the platform data and use the constant instead. Reviewed-by: Dikshita Agarwal Reviewed-by: Konrad Dybcio Reviewed-by: Vikash Garodia Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_firmware.c | 11 ++++++----- .../media/platform/qcom/iris/iris_platform_common.h | 2 -- drivers/media/platform/qcom/iris/iris_platform_gen1.c | 2 -- drivers/media/platform/qcom/iris/iris_platform_gen2.c | 4 ---- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c index 5f408024e967..bc6c5c3e00c3 100644 --- a/drivers/media/platform/qcom/iris/iris_firmware.c +++ b/drivers/media/platform/qcom/iris/iris_firmware.c @@ -12,11 +12,12 @@ #include "iris_core.h" #include "iris_firmware.h" +#define IRIS_PAS_ID 9 + #define MAX_FIRMWARE_NAME_SIZE 128 static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name) { - u32 pas_id = core->iris_platform_data->pas_id; const struct firmware *firmware = NULL; struct device *dev = core->dev; struct resource res; @@ -53,7 +54,7 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name) } ret = qcom_mdt_load(dev, firmware, fw_name, - pas_id, mem_virt, mem_phys, res_size, NULL); + IRIS_PAS_ID, mem_virt, mem_phys, res_size, NULL); memunmap(mem_virt); err_release_fw: @@ -79,7 +80,7 @@ int iris_fw_load(struct iris_core *core) return -ENOMEM; } - ret = qcom_scm_pas_auth_and_reset(core->iris_platform_data->pas_id); + ret = qcom_scm_pas_auth_and_reset(IRIS_PAS_ID); if (ret) { dev_err(core->dev, "auth and reset failed: %d\n", ret); return ret; @@ -93,7 +94,7 @@ int iris_fw_load(struct iris_core *core) cp_config->cp_nonpixel_size); if (ret) { dev_err(core->dev, "qcom_scm_mem_protect_video_var failed: %d\n", ret); - qcom_scm_pas_shutdown(core->iris_platform_data->pas_id); + qcom_scm_pas_shutdown(IRIS_PAS_ID); return ret; } } @@ -103,7 +104,7 @@ int iris_fw_load(struct iris_core *core) int iris_fw_unload(struct iris_core *core) { - return qcom_scm_pas_shutdown(core->iris_platform_data->pas_id); + return qcom_scm_pas_shutdown(IRIS_PAS_ID); } int iris_set_hw_state(struct iris_core *core, bool resume) diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index f42e1798747c..e4eefc646c7f 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -12,7 +12,6 @@ struct iris_core; struct iris_inst; -#define IRIS_PAS_ID 9 #define HW_RESPONSE_TIMEOUT_VALUE (1000) /* milliseconds */ #define AUTOSUSPEND_DELAY_VALUE (HW_RESPONSE_TIMEOUT_VALUE + 500) /* milliseconds */ @@ -226,7 +225,6 @@ struct iris_platform_data { unsigned int controller_rst_tbl_size; u64 dma_mask; const char *fwname; - u32 pas_id; struct iris_fmt *inst_iris_fmts; u32 inst_iris_fmts_size; struct platform_inst_caps *inst_caps; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index aa71f7f53ee3..07ed572e895b 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -360,7 +360,6 @@ const struct iris_platform_data sm8250_data = { /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu-1.0/venus.mbn", - .pas_id = IRIS_PAS_ID, .inst_iris_fmts = platform_fmts_sm8250_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), .inst_caps = &platform_inst_cap_sm8250, @@ -413,7 +412,6 @@ const struct iris_platform_data sc7280_data = { /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu20_p1.mbn", - .pas_id = IRIS_PAS_ID, .inst_iris_fmts = platform_fmts_sm8250_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), .inst_caps = &platform_inst_cap_sm8250, diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index a526b50a1cd3..1f23ddb972f0 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -934,7 +934,6 @@ const struct iris_platform_data sm8550_data = { /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu30_p4.mbn", - .pas_id = IRIS_PAS_ID, .inst_iris_fmts = platform_fmts_sm8550_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_sm8550, @@ -1038,7 +1037,6 @@ const struct iris_platform_data sm8650_data = { /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu33_p4.mbn", - .pas_id = IRIS_PAS_ID, .inst_iris_fmts = platform_fmts_sm8550_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_sm8550, @@ -1133,7 +1131,6 @@ const struct iris_platform_data sm8750_data = { /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu35_p4.mbn", - .pas_id = IRIS_PAS_ID, .inst_iris_fmts = platform_fmts_sm8550_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_sm8550, @@ -1232,7 +1229,6 @@ const struct iris_platform_data qcs8300_data = { /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu30_p4_s6.mbn", - .pas_id = IRIS_PAS_ID, .inst_iris_fmts = platform_fmts_sm8550_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_qcs8300, From 8550eebfca38d3232fcb8c6641ea8543c78bee3c Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:03 +0200 Subject: [PATCH 136/394] media: qcom: iris: use common set_preset_registers function The set_preset_registers is (currently) common to all supported devices. Extract it to a iris_vpu_common.c and call it directly from iris_vpu_power_on(). Later, if any of the devices requires special handling, it can be sorted out separately. Reviewed-by: Dikshita Agarwal Reviewed-by: Vikash Garodia Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_platform_common.h | 1 - drivers/media/platform/qcom/iris/iris_platform_gen1.c | 7 ------- drivers/media/platform/qcom/iris/iris_platform_gen2.c | 9 --------- drivers/media/platform/qcom/iris/iris_vpu_common.c | 7 ++++++- drivers/media/platform/qcom/iris/iris_vpu_common.h | 2 ++ 5 files changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index e4eefc646c7f..d7106902698c 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -207,7 +207,6 @@ struct iris_platform_data { struct iris_inst *(*get_instance)(void); u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type); const struct vpu_ops *vpu_ops; - void (*set_preset_registers)(struct iris_core *core); const struct icc_info *icc_tbl; unsigned int icc_tbl_size; const struct bw_info *bw_tbl_dec; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index 07ed572e895b..ed07d1b00e43 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -260,11 +260,6 @@ static struct platform_inst_caps platform_inst_cap_sm8250 = { .max_operating_rate = MAXIMUM_FPS, }; -static void iris_set_sm8250_preset_registers(struct iris_core *core) -{ - writel(0x0, core->reg_base + 0xB0088); -} - static const struct icc_info sm8250_icc_table[] = { { "cpu-cfg", 1000, 1000 }, { "video-mem", 1000, 15000000 }, @@ -343,7 +338,6 @@ const struct iris_platform_data sm8250_data = { .init_hfi_response_ops = iris_hfi_gen1_response_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, - .set_preset_registers = iris_set_sm8250_preset_registers, .icc_tbl = sm8250_icc_table, .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table), .clk_rst_tbl = sm8250_clk_reset_table, @@ -397,7 +391,6 @@ const struct iris_platform_data sc7280_data = { .init_hfi_response_ops = iris_hfi_gen1_response_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, - .set_preset_registers = iris_set_sm8250_preset_registers, .icc_tbl = sm8250_icc_table, .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table), .bw_tbl_dec = sc7280_bw_table_dec, diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 1f23ddb972f0..c84d4399f84d 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -756,11 +756,6 @@ static struct platform_inst_caps platform_inst_cap_sm8550 = { .max_operating_rate = MAXIMUM_FPS, }; -static void iris_set_sm8550_preset_registers(struct iris_core *core) -{ - writel(0x0, core->reg_base + 0xB0088); -} - static const struct icc_info sm8550_icc_table[] = { { "cpu-cfg", 1000, 1000 }, { "video-mem", 1000, 15000000 }, @@ -917,7 +912,6 @@ const struct iris_platform_data sm8550_data = { .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu3_ops, - .set_preset_registers = iris_set_sm8550_preset_registers, .icc_tbl = sm8550_icc_table, .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), .clk_rst_tbl = sm8550_clk_reset_table, @@ -1018,7 +1012,6 @@ const struct iris_platform_data sm8650_data = { .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu33_ops, - .set_preset_registers = iris_set_sm8550_preset_registers, .icc_tbl = sm8550_icc_table, .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), .clk_rst_tbl = sm8650_clk_reset_table, @@ -1114,7 +1107,6 @@ const struct iris_platform_data sm8750_data = { .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu35_ops, - .set_preset_registers = iris_set_sm8550_preset_registers, .icc_tbl = sm8550_icc_table, .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), .clk_rst_tbl = sm8750_clk_reset_table, @@ -1212,7 +1204,6 @@ const struct iris_platform_data qcs8300_data = { .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu3_ops, - .set_preset_registers = iris_set_sm8550_preset_registers, .icc_tbl = sm8550_icc_table, .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), .clk_rst_tbl = sm8550_clk_reset_table, diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index 548e5f1727fd..faabf53126f3 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -468,7 +468,7 @@ int iris_vpu_power_on(struct iris_core *core) iris_opp_set_rate(core->dev, freq); - core->iris_platform_data->set_preset_registers(core); + iris_vpu_set_preset_registers(core); iris_vpu_interrupt_init(core); core->intr_status = 0; @@ -485,3 +485,8 @@ int iris_vpu_power_on(struct iris_core *core) return ret; } + +void iris_vpu_set_preset_registers(struct iris_core *core) +{ + writel(0x0, core->reg_base + 0xb0088); +} diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h b/drivers/media/platform/qcom/iris/iris_vpu_common.h index f6dffc613b82..07728c4c72b6 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.h +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h @@ -39,4 +39,6 @@ int iris_vpu35_vpu4x_power_on_controller(struct iris_core *core); void iris_vpu35_vpu4x_program_bootup_registers(struct iris_core *core); u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_size); +void iris_vpu_set_preset_registers(struct iris_core *core); + #endif From c61331d2409b5501a4641adfee6597c1e79c34be Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:04 +0200 Subject: [PATCH 137/394] media: qcom: iris: don't use function indirection in gen2-specific code To note that iris_set_num_comv() is gen2-internal, rename it to iris_hfi_gen2_set_num_comv() and then stop using hfi_ops indirection to set session property (like other functions in this file do). Reviewed-by: Dikshita Agarwal Reviewed-by: Konrad Dybcio Reviewed-by: Vikash Garodia Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/iris/iris_hfi_gen2_command.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index 30bfd90d423b..e4f25b7f5d04 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -1205,7 +1205,7 @@ static u32 iris_hfi_gen2_buf_type_from_driver(u32 domain, enum iris_buffer_type } } -static int iris_set_num_comv(struct iris_inst *inst) +static int iris_hfi_gen2_set_num_comv(struct iris_inst *inst) { struct platform_inst_caps *caps; struct iris_core *core = inst->core; @@ -1220,12 +1220,12 @@ static int iris_set_num_comv(struct iris_inst *inst) num_comv = (inst->codec == V4L2_PIX_FMT_AV1) ? NUM_COMV_AV1 : caps->num_comv; - return core->hfi_ops->session_set_property(inst, - HFI_PROP_COMV_BUFFER_COUNT, - HFI_HOST_FLAGS_NONE, - HFI_PORT_BITSTREAM, - HFI_PAYLOAD_U32, - &num_comv, sizeof(u32)); + return iris_hfi_gen2_session_set_property(inst, + HFI_PROP_COMV_BUFFER_COUNT, + HFI_HOST_FLAGS_NONE, + HFI_PORT_BITSTREAM, + HFI_PAYLOAD_U32, + &num_comv, sizeof(u32)); } static void iris_hfi_gen2_get_buffer(u32 domain, struct iris_buffer *buffer, @@ -1257,7 +1257,7 @@ static int iris_hfi_gen2_session_queue_buffer(struct iris_inst *inst, struct iri iris_hfi_gen2_get_buffer(inst->domain, buffer, &hfi_buffer); if (buffer->type == BUF_COMV) { - ret = iris_set_num_comv(inst); + ret = iris_hfi_gen2_set_num_comv(inst); if (ret) return ret; } From 35da0884068226ca3a53371dbf685db6e0d74658 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:05 +0200 Subject: [PATCH 138/394] media: qcom: iris: split HFI session ops from core ops Calling HFI instance-specific ops should not require double indirection through the core ops. Split instance-specific ops to a separate struct, keep a pointer to it in struct iris_inst and set it directly in the get_instance function. Reviewed-by: Dikshita Agarwal Reviewed-by: Vikash Garodia Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/iris/iris_buffer.c | 4 +- .../media/platform/qcom/iris/iris_common.c | 8 ++-- drivers/media/platform/qcom/iris/iris_ctrls.c | 46 +++++++++---------- .../platform/qcom/iris/iris_hfi_common.h | 3 ++ .../qcom/iris/iris_hfi_gen1_command.c | 23 +++++++--- .../qcom/iris/iris_hfi_gen2_command.c | 17 +++++-- .../media/platform/qcom/iris/iris_instance.h | 4 ++ drivers/media/platform/qcom/iris/iris_vb2.c | 2 +- drivers/media/platform/qcom/iris/iris_vdec.c | 6 +-- drivers/media/platform/qcom/iris/iris_venc.c | 4 +- drivers/media/platform/qcom/iris/iris_vidc.c | 2 +- 11 files changed, 72 insertions(+), 47 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index 9151f43bc6b9..f55b7c608116 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -404,7 +404,7 @@ int iris_create_internal_buffers(struct iris_inst *inst, u32 plane) int iris_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; int ret; ret = hfi_ops->session_queue_buf(inst, buf); @@ -572,7 +572,7 @@ int iris_destroy_dequeued_internal_buffers(struct iris_inst *inst, u32 plane) static int iris_release_internal_buffers(struct iris_inst *inst, enum iris_buffer_type buffer_type) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct iris_buffers *buffers = &inst->buffers[buffer_type]; struct iris_buffer *buffer, *next; int ret; diff --git a/drivers/media/platform/qcom/iris/iris_common.c b/drivers/media/platform/qcom/iris/iris_common.c index 7f1c7fe144f7..25836561bcf3 100644 --- a/drivers/media/platform/qcom/iris/iris_common.c +++ b/drivers/media/platform/qcom/iris/iris_common.c @@ -48,7 +48,7 @@ void iris_set_ts_metadata(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) int iris_process_streamon_input(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_inst_sub_state set_sub_state = 0; int ret; @@ -90,7 +90,7 @@ int iris_process_streamon_input(struct iris_inst *inst) int iris_process_streamon_output(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_inst_sub_state clear_sub_state = 0; bool drain_active, drc_active, first_ipsc; int ret = 0; @@ -189,7 +189,7 @@ static void iris_flush_deferred_buffers(struct iris_inst *inst, static void iris_kill_session(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; if (!inst->session_id) return; @@ -200,7 +200,7 @@ static void iris_kill_session(struct iris_inst *inst) int iris_session_streamoff(struct iris_inst *inst, u32 plane) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_buffer_type buffer_type; int ret; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index 3cec957580f5..5a24aa869b2d 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -399,7 +399,7 @@ static u32 iris_get_port_info(struct iris_inst *inst, int iris_set_u32_enum(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_value = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -412,7 +412,7 @@ int iris_set_u32_enum(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_u32(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_value = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -425,7 +425,7 @@ int iris_set_u32(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct v4l2_format *inp_f = inst->fmt_src; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 height = inp_f->fmt.pix_mp.height; @@ -446,7 +446,7 @@ int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id int iris_set_pipe(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 work_route = inst->fw_caps[PIPE].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -459,7 +459,7 @@ int iris_set_pipe(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) int iris_set_profile(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id, hfi_value; if (inst->codec == V4L2_PIX_FMT_H264) { @@ -479,7 +479,7 @@ int iris_set_profile(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_ int iris_set_level(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id, hfi_value; if (inst->codec == V4L2_PIX_FMT_H264) { @@ -499,7 +499,7 @@ int iris_set_level(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id int iris_set_profile_level_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; struct hfi_profile_level pl; @@ -520,7 +520,7 @@ int iris_set_profile_level_gen1(struct iris_inst *inst, enum platform_inst_fw_ca int iris_set_header_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 header_mode = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 hfi_val; @@ -539,7 +539,7 @@ int iris_set_header_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_ int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 prepend_sps_pps = inst->fw_caps[PREPEND_SPSPPS_TO_IDR].value; u32 header_mode = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -561,7 +561,7 @@ int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_ int iris_set_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 entropy_mode = inst->fw_caps[ENTROPY_MODE].value; u32 bitrate = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -586,7 +586,7 @@ int iris_set_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_ int iris_set_peak_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 rc_mode = inst->fw_caps[BITRATE_MODE].value; u32 peak_bitrate = inst->fw_caps[cap_id].value; u32 bitrate = inst->fw_caps[BITRATE].value; @@ -613,7 +613,7 @@ int iris_set_peak_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type int iris_set_bitrate_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 bitrate_mode = inst->fw_caps[BITRATE_MODE].value; u32 frame_rc = inst->fw_caps[FRAME_RC_ENABLE].value; u32 frame_skip = inst->fw_caps[FRAME_SKIP_MODE].value; @@ -640,7 +640,7 @@ int iris_set_bitrate_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap int iris_set_bitrate_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 bitrate_mode = inst->fw_caps[BITRATE_MODE].value; u32 frame_rc = inst->fw_caps[FRAME_RC_ENABLE].value; u32 frame_skip = inst->fw_caps[FRAME_SKIP_MODE].value; @@ -667,7 +667,7 @@ int iris_set_bitrate_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap int iris_set_entropy_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 entropy_mode = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 hfi_val; @@ -687,7 +687,7 @@ int iris_set_entropy_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap int iris_set_entropy_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 entropy_mode = inst->fw_caps[cap_id].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 profile; @@ -712,7 +712,7 @@ int iris_set_entropy_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap int iris_set_min_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 i_qp_enable = 0, p_qp_enable = 0, b_qp_enable = 0; u32 i_frame_qp = 0, p_frame_qp = 0, b_frame_qp = 0; u32 min_qp_enable = 0, client_qp_enable = 0; @@ -776,7 +776,7 @@ int iris_set_min_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_i int iris_set_max_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 i_qp_enable = 0, p_qp_enable = 0, b_qp_enable = 0; u32 max_qp_enable = 0, client_qp_enable; u32 i_frame_qp, p_frame_qp, b_frame_qp; @@ -841,7 +841,7 @@ int iris_set_max_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_i int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 i_qp_enable = 0, p_qp_enable = 0, b_qp_enable = 0, client_qp_enable; u32 i_frame_qp, p_frame_qp, b_frame_qp; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -902,7 +902,7 @@ int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct hfi_quantization_range_v2 range; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; @@ -923,7 +923,7 @@ int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 hfi_val; @@ -953,7 +953,7 @@ int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; u32 hfi_val = HFI_DISABLE_FLIP; @@ -972,7 +972,7 @@ int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct vb2_queue *q = v4l2_m2m_get_dst_vq(inst->m2m_ctx); u32 ir_period = inst->fw_caps[cap_id].value; u32 ir_type = 0; @@ -998,7 +998,7 @@ int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type ca int iris_set_properties(struct iris_inst *inst, u32 plane) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct platform_inst_fw_cap *cap; int ret; u32 i; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h index 3edb5ae582b4..18684ada78b2 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_common.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h @@ -110,6 +110,9 @@ struct iris_hfi_command_ops { int (*sys_image_version)(struct iris_core *core); int (*sys_interframe_powercollapse)(struct iris_core *core); int (*sys_pc_prep)(struct iris_core *core); +}; + +struct iris_hfi_session_ops { int (*session_set_config_params)(struct iris_inst *inst, u32 plane); int (*session_set_property)(struct iris_inst *inst, u32 packet_type, u32 flag, u32 plane, u32 payload_type, diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index e42d17653c2c..a28b0c7ebbad 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -1063,11 +1063,7 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p return 0; } -static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = { - .sys_init = iris_hfi_gen1_sys_init, - .sys_image_version = iris_hfi_gen1_sys_image_version, - .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse, - .sys_pc_prep = iris_hfi_gen1_sys_pc_prep, +static const struct iris_hfi_session_ops iris_hfi_gen1_session_ops = { .session_open = iris_hfi_gen1_session_open, .session_set_config_params = iris_hfi_gen1_session_set_config_params, .session_set_property = iris_hfi_gen1_session_set_property, @@ -1080,6 +1076,13 @@ static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = { .session_close = iris_hfi_gen1_session_close, }; +static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = { + .sys_init = iris_hfi_gen1_sys_init, + .sys_image_version = iris_hfi_gen1_sys_image_version, + .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse, + .sys_pc_prep = iris_hfi_gen1_sys_pc_prep, +}; + void iris_hfi_gen1_command_ops_init(struct iris_core *core) { core->hfi_ops = &iris_hfi_gen1_command_ops; @@ -1087,5 +1090,13 @@ void iris_hfi_gen1_command_ops_init(struct iris_core *core) struct iris_inst *iris_hfi_gen1_get_instance(void) { - return kzalloc_obj(struct iris_inst); + struct iris_inst *out; + + out = kzalloc_obj(*out); + if (!out) + return NULL; + + out->hfi_session_ops = &iris_hfi_gen1_session_ops; + + return out; } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index e4f25b7f5d04..ffb70fd9499c 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -1300,11 +1300,7 @@ static int iris_hfi_gen2_session_release_buffer(struct iris_inst *inst, struct i inst_hfi_gen2->packet->size); } -static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = { - .sys_init = iris_hfi_gen2_sys_init, - .sys_image_version = iris_hfi_gen2_sys_image_version, - .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse, - .sys_pc_prep = iris_hfi_gen2_sys_pc_prep, +static const struct iris_hfi_session_ops iris_hfi_gen2_session_ops = { .session_open = iris_hfi_gen2_session_open, .session_set_config_params = iris_hfi_gen2_session_set_config_params, .session_set_property = iris_hfi_gen2_session_set_property, @@ -1319,6 +1315,13 @@ static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = { .session_close = iris_hfi_gen2_session_close, }; +static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = { + .sys_init = iris_hfi_gen2_sys_init, + .sys_image_version = iris_hfi_gen2_sys_image_version, + .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse, + .sys_pc_prep = iris_hfi_gen2_sys_pc_prep, +}; + void iris_hfi_gen2_command_ops_init(struct iris_core *core) { core->hfi_ops = &iris_hfi_gen2_command_ops; @@ -1330,6 +1333,10 @@ struct iris_inst *iris_hfi_gen2_get_instance(void) /* The allocation is intentionally larger than struct iris_inst. */ out = kzalloc_obj(*out); + if (!out) + return NULL; + + out->inst.hfi_session_ops = &iris_hfi_gen2_session_ops; return &out->inst; } diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index 16965150f427..352af99699dd 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -15,6 +15,8 @@ #define DEFAULT_WIDTH 320 #define DEFAULT_HEIGHT 240 +struct iris_hfi_session_ops; + enum iris_fmt_type_out { IRIS_FMT_H264, IRIS_FMT_HEVC, @@ -38,6 +40,7 @@ struct iris_fmt { * @list: used for attach an instance to the core * @core: pointer to core structure * @session_id: id of current video session + * @hfi_session_ops: iris HFI session ops * @ctx_q_lock: lock to serialize queues related ioctls * @lock: lock to seralise forward and reverse threads * @fh: reference of v4l2 file handler @@ -80,6 +83,7 @@ struct iris_inst { struct list_head list; struct iris_core *core; u32 session_id; + const struct iris_hfi_session_ops *hfi_session_ops; struct mutex ctx_q_lock;/* lock to serialize queues related ioctls */ struct mutex lock; /* lock to serialize forward and reverse threads */ struct v4l2_fh fh; diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c index bf0b8400996e..a2ea2d67f60d 100644 --- a/drivers/media/platform/qcom/iris/iris_vb2.c +++ b/drivers/media/platform/qcom/iris/iris_vb2.c @@ -129,7 +129,7 @@ int iris_vb2_queue_setup(struct vb2_queue *q, if (!inst->once_per_session_set) { inst->once_per_session_set = true; - ret = core->hfi_ops->session_open(inst); + ret = inst->hfi_session_ops->session_open(inst); if (ret) { ret = -EINVAL; dev_err(core->dev, "session open failed\n"); diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index 719217399a30..ccda3b9fb845 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -374,7 +374,7 @@ int iris_vdec_streamon_input(struct iris_inst *inst) int iris_vdec_streamon_output(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; int ret; ret = hfi_ops->session_set_config_params(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); @@ -434,7 +434,7 @@ int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) int iris_vdec_start_cmd(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_inst_sub_state clear_sub_state = 0; struct vb2_queue *dst_vq; int ret; @@ -497,7 +497,7 @@ int iris_vdec_start_cmd(struct iris_inst *inst) int iris_vdec_stop_cmd(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; int ret; ret = hfi_ops->session_drain(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); diff --git a/drivers/media/platform/qcom/iris/iris_venc.c b/drivers/media/platform/qcom/iris/iris_venc.c index aa27b22704eb..aeed756ee9ca 100644 --- a/drivers/media/platform/qcom/iris/iris_venc.c +++ b/drivers/media/platform/qcom/iris/iris_venc.c @@ -581,7 +581,7 @@ int iris_venc_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) int iris_venc_start_cmd(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; enum iris_inst_sub_state clear_sub_state = 0; struct vb2_queue *dst_vq; int ret; @@ -623,7 +623,7 @@ int iris_venc_start_cmd(struct iris_inst *inst) int iris_venc_stop_cmd(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; int ret; ret = hfi_ops->session_drain(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index bd38d84c9cc7..7e03d63578e1 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -224,7 +224,7 @@ int iris_open(struct file *filp) static void iris_session_close(struct iris_inst *inst) { - const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; bool wait_for_response = true; int ret; From d8a6a63372b86fa2152c633a384514585a9ce6ae Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:06 +0200 Subject: [PATCH 139/394] media: qcom: iris: merge hfi_response_ops and hfi_command_ops There is little point in having two different structures for HFI-related core ops. Merge both of them into the new iris_hfi_ops structure. Reviewed-by: Dikshita Agarwal Reviewed-by: Vikash Garodia Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_core.h | 6 ++---- drivers/media/platform/qcom/iris/iris_hfi_common.c | 6 +++--- drivers/media/platform/qcom/iris/iris_hfi_common.h | 8 +++----- drivers/media/platform/qcom/iris/iris_hfi_gen1.h | 4 ++-- .../media/platform/qcom/iris/iris_hfi_gen1_command.c | 8 +++++--- .../platform/qcom/iris/iris_hfi_gen1_response.c | 11 +---------- drivers/media/platform/qcom/iris/iris_hfi_gen2.h | 4 ++-- .../media/platform/qcom/iris/iris_hfi_gen2_command.c | 8 +++++--- .../platform/qcom/iris/iris_hfi_gen2_response.c | 11 +---------- .../media/platform/qcom/iris/iris_platform_common.h | 3 +-- .../media/platform/qcom/iris/iris_platform_gen1.c | 6 ++---- .../media/platform/qcom/iris/iris_platform_gen2.c | 12 ++++-------- drivers/media/platform/qcom/iris/iris_probe.c | 3 +-- drivers/media/platform/qcom/iris/iris_vpu_common.c | 2 +- 14 files changed, 33 insertions(+), 59 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h index d10a03aa5685..1592681640ab 100644 --- a/drivers/media/platform/qcom/iris/iris_core.h +++ b/drivers/media/platform/qcom/iris/iris_core.h @@ -68,8 +68,7 @@ struct qcom_ubwc_cfg_data; * @header_id: id of packet header * @packet_id: id of packet * @power: a structure for clock and bw information - * @hfi_ops: iris hfi command ops - * @hfi_response_ops: iris hfi response ops + * @hfi_sys_ops: iris HFI system ops * @core_init_done: structure of signal completion for system response * @intr_status: interrupt status * @sys_error_handler: a delayed work for handling system fatal error @@ -112,8 +111,7 @@ struct iris_core { u32 header_id; u32 packet_id; struct iris_core_power power; - const struct iris_hfi_command_ops *hfi_ops; - const struct iris_hfi_response_ops *hfi_response_ops; + const struct iris_hfi_sys_ops *hfi_sys_ops; struct completion core_init_done; u32 intr_status; struct delayed_work sys_error_handler; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.c b/drivers/media/platform/qcom/iris/iris_hfi_common.c index 92112eb16c11..ad8e4ecb8605 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_common.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_common.c @@ -76,7 +76,7 @@ u32 iris_hfi_get_v4l2_matrix_coefficients(u32 hfi_coefficients) int iris_hfi_core_init(struct iris_core *core) { - const struct iris_hfi_command_ops *hfi_ops = core->hfi_ops; + const struct iris_hfi_sys_ops *hfi_ops = core->hfi_sys_ops; int ret; ret = hfi_ops->sys_init(core); @@ -109,7 +109,7 @@ irqreturn_t iris_hfi_isr_handler(int irq, void *data) iris_vpu_clear_interrupt(core); mutex_unlock(&core->lock); - core->hfi_response_ops->hfi_response_handler(core); + core->hfi_sys_ops->sys_hfi_response_handler(core); if (!iris_vpu_watchdog(core, core->intr_status)) enable_irq(irq); @@ -144,7 +144,7 @@ int iris_hfi_pm_suspend(struct iris_core *core) int iris_hfi_pm_resume(struct iris_core *core) { - const struct iris_hfi_command_ops *ops = core->hfi_ops; + const struct iris_hfi_sys_ops *ops = core->hfi_sys_ops; int ret; ret = iris_vpu_power_on(core); diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h index 18684ada78b2..9aa84a1d8f95 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_common.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h @@ -105,11 +105,13 @@ struct iris_hfi_prop_type_handle { int (*handle)(struct iris_inst *inst, u32 plane); }; -struct iris_hfi_command_ops { +struct iris_hfi_sys_ops { int (*sys_init)(struct iris_core *core); int (*sys_image_version)(struct iris_core *core); int (*sys_interframe_powercollapse)(struct iris_core *core); int (*sys_pc_prep)(struct iris_core *core); + + void (*sys_hfi_response_handler)(struct iris_core *core); }; struct iris_hfi_session_ops { @@ -129,10 +131,6 @@ struct iris_hfi_session_ops { int (*session_close)(struct iris_inst *inst); }; -struct iris_hfi_response_ops { - void (*hfi_response_handler)(struct iris_core *core); -}; - struct hfi_subscription_params { u32 bitstream_resolution; u32 crop_offsets[2]; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h index 19b8e9054a75..38e9d262d7df 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h @@ -9,8 +9,8 @@ struct iris_core; struct iris_inst; -void iris_hfi_gen1_command_ops_init(struct iris_core *core); -void iris_hfi_gen1_response_ops_init(struct iris_core *core); +void iris_hfi_gen1_sys_ops_init(struct iris_core *core); +void iris_hfi_gen1_response_handler(struct iris_core *core); struct iris_inst *iris_hfi_gen1_get_instance(void); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index a28b0c7ebbad..26b7feb05d15 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -1076,16 +1076,18 @@ static const struct iris_hfi_session_ops iris_hfi_gen1_session_ops = { .session_close = iris_hfi_gen1_session_close, }; -static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = { +static const struct iris_hfi_sys_ops iris_hfi_gen1_sys_ops = { .sys_init = iris_hfi_gen1_sys_init, .sys_image_version = iris_hfi_gen1_sys_image_version, .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse, .sys_pc_prep = iris_hfi_gen1_sys_pc_prep, + + .sys_hfi_response_handler = iris_hfi_gen1_response_handler, }; -void iris_hfi_gen1_command_ops_init(struct iris_core *core) +void iris_hfi_gen1_sys_ops_init(struct iris_core *core) { - core->hfi_ops = &iris_hfi_gen1_command_ops; + core->hfi_sys_ops = &iris_hfi_gen1_sys_ops; } struct iris_inst *iris_hfi_gen1_get_instance(void) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c index 8e864c239e29..bfd7495bf44f 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c @@ -688,7 +688,7 @@ static void iris_hfi_gen1_flush_debug_queue(struct iris_core *core, u8 *packet) } } -static void iris_hfi_gen1_response_handler(struct iris_core *core) +void iris_hfi_gen1_response_handler(struct iris_core *core) { memset(core->response_packet, 0, sizeof(struct hfi_pkt_hdr)); while (!iris_hfi_queue_msg_read(core, core->response_packet)) { @@ -698,12 +698,3 @@ static void iris_hfi_gen1_response_handler(struct iris_core *core) iris_hfi_gen1_flush_debug_queue(core, core->response_packet); } - -static const struct iris_hfi_response_ops iris_hfi_gen1_response_ops = { - .hfi_response_handler = iris_hfi_gen1_response_handler, -}; - -void iris_hfi_gen1_response_ops_init(struct iris_core *core) -{ - core->hfi_response_ops = &iris_hfi_gen1_response_ops; -} diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h index b9d3749a10ef..6cc6d9890c12 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h @@ -34,8 +34,8 @@ struct iris_inst_hfi_gen2 { struct hfi_subscription_params dst_subcr_params; }; -void iris_hfi_gen2_command_ops_init(struct iris_core *core); -void iris_hfi_gen2_response_ops_init(struct iris_core *core); +void iris_hfi_gen2_sys_ops_init(struct iris_core *core); +void iris_hfi_gen2_response_handler(struct iris_core *core); struct iris_inst *iris_hfi_gen2_get_instance(void); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index ffb70fd9499c..0c98d680bf09 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -1315,16 +1315,18 @@ static const struct iris_hfi_session_ops iris_hfi_gen2_session_ops = { .session_close = iris_hfi_gen2_session_close, }; -static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = { +static const struct iris_hfi_sys_ops iris_hfi_gen2_sys_ops = { .sys_init = iris_hfi_gen2_sys_init, .sys_image_version = iris_hfi_gen2_sys_image_version, .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse, .sys_pc_prep = iris_hfi_gen2_sys_pc_prep, + + .sys_hfi_response_handler = iris_hfi_gen2_response_handler, }; -void iris_hfi_gen2_command_ops_init(struct iris_core *core) +void iris_hfi_gen2_sys_ops_init(struct iris_core *core) { - core->hfi_ops = &iris_hfi_gen2_command_ops; + core->hfi_sys_ops = &iris_hfi_gen2_sys_ops; } struct iris_inst *iris_hfi_gen2_get_instance(void) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c index 8e19f61bbbf9..c350d231265e 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c @@ -977,7 +977,7 @@ static void iris_hfi_gen2_flush_debug_queue(struct iris_core *core, u8 *packet) } } -static void iris_hfi_gen2_response_handler(struct iris_core *core) +void iris_hfi_gen2_response_handler(struct iris_core *core) { if (iris_vpu_watchdog(core, core->intr_status)) { struct iris_hfi_packet pkt = {.type = HFI_SYS_ERROR_WD_TIMEOUT}; @@ -997,12 +997,3 @@ static void iris_hfi_gen2_response_handler(struct iris_core *core) iris_hfi_gen2_flush_debug_queue(core, core->response_packet); } - -static const struct iris_hfi_response_ops iris_hfi_gen2_response_ops = { - .hfi_response_handler = iris_hfi_gen2_response_handler, -}; - -void iris_hfi_gen2_response_ops_init(struct iris_core *core) -{ - core->hfi_response_ops = &iris_hfi_gen2_response_ops; -} diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index d7106902698c..6b76a9046f9a 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -202,8 +202,7 @@ enum platform_pm_domain_type { }; struct iris_platform_data { - void (*init_hfi_command_ops)(struct iris_core *core); - void (*init_hfi_response_ops)(struct iris_core *core); + void (*init_hfi_ops)(struct iris_core *core); struct iris_inst *(*get_instance)(void); u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type); const struct vpu_ops *vpu_ops; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index ed07d1b00e43..dc74da04771b 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -334,8 +334,7 @@ static const u32 sm8250_enc_ip_int_buf_tbl[] = { const struct iris_platform_data sm8250_data = { .get_instance = iris_hfi_gen1_get_instance, - .init_hfi_command_ops = &iris_hfi_gen1_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen1_response_ops_init, + .init_hfi_ops = &iris_hfi_gen1_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, .icc_tbl = sm8250_icc_table, @@ -387,8 +386,7 @@ const struct iris_platform_data sm8250_data = { const struct iris_platform_data sc7280_data = { .get_instance = iris_hfi_gen1_get_instance, - .init_hfi_command_ops = &iris_hfi_gen1_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen1_response_ops_init, + .init_hfi_ops = &iris_hfi_gen1_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, .icc_tbl = sm8250_icc_table, diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index c84d4399f84d..19e99e1c2aff 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -908,8 +908,7 @@ static const u32 sm8550_enc_op_int_buf_tbl[] = { const struct iris_platform_data sm8550_data = { .get_instance = iris_hfi_gen2_get_instance, - .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, + .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu3_ops, .icc_tbl = sm8550_icc_table, @@ -1008,8 +1007,7 @@ const struct iris_platform_data sm8550_data = { */ const struct iris_platform_data sm8650_data = { .get_instance = iris_hfi_gen2_get_instance, - .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, + .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu33_ops, .icc_tbl = sm8550_icc_table, @@ -1103,8 +1101,7 @@ const struct iris_platform_data sm8650_data = { const struct iris_platform_data sm8750_data = { .get_instance = iris_hfi_gen2_get_instance, - .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, + .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu35_ops, .icc_tbl = sm8550_icc_table, @@ -1200,8 +1197,7 @@ const struct iris_platform_data sm8750_data = { */ const struct iris_platform_data qcs8300_data = { .get_instance = iris_hfi_gen2_get_instance, - .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, - .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, + .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu3_ops, .icc_tbl = sm8550_icc_table, diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index baa13cc5c209..fa561f6a736c 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -264,8 +264,7 @@ static int iris_probe(struct platform_device *pdev) disable_irq_nosync(core->irq); iris_init_ops(core); - core->iris_platform_data->init_hfi_command_ops(core); - core->iris_platform_data->init_hfi_response_ops(core); + core->iris_platform_data->init_hfi_ops(core); ret = iris_init_resources(core); if (ret) diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index faabf53126f3..dbce5aeba06c 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -149,7 +149,7 @@ int iris_vpu_prepare_pc(struct iris_core *core) if (!wfi_status || !idle_status) goto skip_power_off; - ret = core->hfi_ops->sys_pc_prep(core); + ret = core->hfi_sys_ops->sys_pc_prep(core); if (ret) goto skip_power_off; From c8f12e754ee4dba209f86ba5e9156cfb69712711 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:07 +0200 Subject: [PATCH 140/394] media: qcom: iris: move get_instance to iris_hfi_sys_ops The get_instance() is a callback tightly connected to the HFI implementation. Move it into the new iris_hfi_sys_ops structure, merging all core callbacks into a single vtable. Reviewed-by: Dikshita Agarwal Reviewed-by: Vikash Garodia Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/iris/iris_hfi_common.h | 2 ++ .../media/platform/qcom/iris/iris_hfi_gen1.h | 2 -- .../qcom/iris/iris_hfi_gen1_command.c | 32 ++++++++++--------- .../media/platform/qcom/iris/iris_hfi_gen2.h | 1 - .../qcom/iris/iris_hfi_gen2_command.c | 32 ++++++++++--------- .../platform/qcom/iris/iris_platform_common.h | 1 - .../platform/qcom/iris/iris_platform_gen1.c | 2 -- .../platform/qcom/iris/iris_platform_gen2.c | 4 --- drivers/media/platform/qcom/iris/iris_vidc.c | 2 +- 9 files changed, 37 insertions(+), 41 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h index 9aa84a1d8f95..a27447eb2519 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_common.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h @@ -112,6 +112,8 @@ struct iris_hfi_sys_ops { int (*sys_pc_prep)(struct iris_core *core); void (*sys_hfi_response_handler)(struct iris_core *core); + + struct iris_inst *(*sys_get_instance)(void); }; struct iris_hfi_session_ops { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h index 38e9d262d7df..c37adf65055a 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h @@ -7,10 +7,8 @@ #define __IRIS_HFI_GEN1_H__ struct iris_core; -struct iris_inst; void iris_hfi_gen1_sys_ops_init(struct iris_core *core); void iris_hfi_gen1_response_handler(struct iris_core *core); -struct iris_inst *iris_hfi_gen1_get_instance(void); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 26b7feb05d15..0017ade4adbd 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -1076,21 +1076,7 @@ static const struct iris_hfi_session_ops iris_hfi_gen1_session_ops = { .session_close = iris_hfi_gen1_session_close, }; -static const struct iris_hfi_sys_ops iris_hfi_gen1_sys_ops = { - .sys_init = iris_hfi_gen1_sys_init, - .sys_image_version = iris_hfi_gen1_sys_image_version, - .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse, - .sys_pc_prep = iris_hfi_gen1_sys_pc_prep, - - .sys_hfi_response_handler = iris_hfi_gen1_response_handler, -}; - -void iris_hfi_gen1_sys_ops_init(struct iris_core *core) -{ - core->hfi_sys_ops = &iris_hfi_gen1_sys_ops; -} - -struct iris_inst *iris_hfi_gen1_get_instance(void) +static struct iris_inst *iris_hfi_gen1_get_instance(void) { struct iris_inst *out; @@ -1102,3 +1088,19 @@ struct iris_inst *iris_hfi_gen1_get_instance(void) return out; } + +static const struct iris_hfi_sys_ops iris_hfi_gen1_sys_ops = { + .sys_init = iris_hfi_gen1_sys_init, + .sys_image_version = iris_hfi_gen1_sys_image_version, + .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse, + .sys_pc_prep = iris_hfi_gen1_sys_pc_prep, + + .sys_hfi_response_handler = iris_hfi_gen1_response_handler, + + .sys_get_instance = iris_hfi_gen1_get_instance, +}; + +void iris_hfi_gen1_sys_ops_init(struct iris_core *core) +{ + core->hfi_sys_ops = &iris_hfi_gen1_sys_ops; +} diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h index 6cc6d9890c12..21ab58e0aa84 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h @@ -36,6 +36,5 @@ struct iris_inst_hfi_gen2 { void iris_hfi_gen2_sys_ops_init(struct iris_core *core); void iris_hfi_gen2_response_handler(struct iris_core *core); -struct iris_inst *iris_hfi_gen2_get_instance(void); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index 0c98d680bf09..639b75fca1ab 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -1315,21 +1315,7 @@ static const struct iris_hfi_session_ops iris_hfi_gen2_session_ops = { .session_close = iris_hfi_gen2_session_close, }; -static const struct iris_hfi_sys_ops iris_hfi_gen2_sys_ops = { - .sys_init = iris_hfi_gen2_sys_init, - .sys_image_version = iris_hfi_gen2_sys_image_version, - .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse, - .sys_pc_prep = iris_hfi_gen2_sys_pc_prep, - - .sys_hfi_response_handler = iris_hfi_gen2_response_handler, -}; - -void iris_hfi_gen2_sys_ops_init(struct iris_core *core) -{ - core->hfi_sys_ops = &iris_hfi_gen2_sys_ops; -} - -struct iris_inst *iris_hfi_gen2_get_instance(void) +static struct iris_inst *iris_hfi_gen2_get_instance(void) { struct iris_inst_hfi_gen2 *out; @@ -1342,3 +1328,19 @@ struct iris_inst *iris_hfi_gen2_get_instance(void) return &out->inst; } + +static const struct iris_hfi_sys_ops iris_hfi_gen2_sys_ops = { + .sys_init = iris_hfi_gen2_sys_init, + .sys_image_version = iris_hfi_gen2_sys_image_version, + .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse, + .sys_pc_prep = iris_hfi_gen2_sys_pc_prep, + + .sys_hfi_response_handler = iris_hfi_gen2_response_handler, + + .sys_get_instance = iris_hfi_gen2_get_instance, +}; + +void iris_hfi_gen2_sys_ops_init(struct iris_core *core) +{ + core->hfi_sys_ops = &iris_hfi_gen2_sys_ops; +} diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 6b76a9046f9a..d1daef2d874b 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -203,7 +203,6 @@ enum platform_pm_domain_type { struct iris_platform_data { void (*init_hfi_ops)(struct iris_core *core); - struct iris_inst *(*get_instance)(void); u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type); const struct vpu_ops *vpu_ops; const struct icc_info *icc_tbl; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index dc74da04771b..9925a893b404 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -333,7 +333,6 @@ static const u32 sm8250_enc_ip_int_buf_tbl[] = { }; const struct iris_platform_data sm8250_data = { - .get_instance = iris_hfi_gen1_get_instance, .init_hfi_ops = &iris_hfi_gen1_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, @@ -385,7 +384,6 @@ const struct iris_platform_data sm8250_data = { }; const struct iris_platform_data sc7280_data = { - .get_instance = iris_hfi_gen1_get_instance, .init_hfi_ops = &iris_hfi_gen1_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 19e99e1c2aff..10a972f96cbe 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -907,7 +907,6 @@ static const u32 sm8550_enc_op_int_buf_tbl[] = { }; const struct iris_platform_data sm8550_data = { - .get_instance = iris_hfi_gen2_get_instance, .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu3_ops, @@ -1006,7 +1005,6 @@ const struct iris_platform_data sm8550_data = { * - fwname to "qcom/vpu/vpu33_p4.mbn" */ const struct iris_platform_data sm8650_data = { - .get_instance = iris_hfi_gen2_get_instance, .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu33_ops, @@ -1100,7 +1098,6 @@ const struct iris_platform_data sm8650_data = { }; const struct iris_platform_data sm8750_data = { - .get_instance = iris_hfi_gen2_get_instance, .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu35_ops, @@ -1196,7 +1193,6 @@ const struct iris_platform_data sm8750_data = { * - inst_caps to platform_inst_cap_qcs8300 */ const struct iris_platform_data qcs8300_data = { - .get_instance = iris_hfi_gen2_get_instance, .init_hfi_ops = iris_hfi_gen2_sys_ops_init, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu3_ops, diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index 7e03d63578e1..ecd8a20fedbf 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -156,7 +156,7 @@ int iris_open(struct file *filp) pm_runtime_put_sync(core->dev); - inst = core->iris_platform_data->get_instance(); + inst = core->hfi_sys_ops->sys_get_instance(); if (!inst) return -ENOMEM; From 10175bca76e1497fd76fd662d33ab199f7160bb1 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:08 +0200 Subject: [PATCH 141/394] media: qcom: iris: drop hw_response_timeout_val from platform data The HW response time is a constant between platforms. Remove it from the iris_platform_data structure and use it directly. Suggested-by: Vikash Garodia Reviewed-by: Vikash Garodia Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_core.c | 3 +-- drivers/media/platform/qcom/iris/iris_platform_common.h | 1 - drivers/media/platform/qcom/iris/iris_platform_gen1.c | 2 -- drivers/media/platform/qcom/iris/iris_platform_gen2.c | 4 ---- drivers/media/platform/qcom/iris/iris_utils.c | 5 +---- 5 files changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_core.c b/drivers/media/platform/qcom/iris/iris_core.c index 8406c48d635b..e6141012cd3d 100644 --- a/drivers/media/platform/qcom/iris/iris_core.c +++ b/drivers/media/platform/qcom/iris/iris_core.c @@ -28,14 +28,13 @@ void iris_core_deinit(struct iris_core *core) static int iris_wait_for_system_response(struct iris_core *core) { - u32 hw_response_timeout_val = core->iris_platform_data->hw_response_timeout; int ret; if (core->state == IRIS_CORE_ERROR) return -EIO; ret = wait_for_completion_timeout(&core->core_init_done, - msecs_to_jiffies(hw_response_timeout_val)); + msecs_to_jiffies(HW_RESPONSE_TIMEOUT_VALUE)); if (!ret) { core->state = IRIS_CORE_ERROR; return -ETIMEDOUT; diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index d1daef2d874b..e8a219023aaa 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -232,7 +232,6 @@ struct iris_platform_data { const struct tz_cp_config *tz_cp_config_data; u32 tz_cp_config_data_size; u32 core_arch; - u32 hw_response_timeout; u32 num_vpp_pipe; bool no_aon; u32 max_session_count; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index 9925a893b404..6ed4c4ae4056 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -361,7 +361,6 @@ const struct iris_platform_data sm8250_data = { .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), .tz_cp_config_data = tz_cp_config_sm8250, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K, @@ -410,7 +409,6 @@ const struct iris_platform_data sc7280_data = { .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), .tz_cp_config_data = tz_cp_config_sm8250, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 1, .no_aon = true, .max_session_count = 16, diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 10a972f96cbe..abe523db45c2 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -936,7 +936,6 @@ const struct iris_platform_data sm8550_data = { .tz_cp_config_data = tz_cp_config_sm8550, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, @@ -1036,7 +1035,6 @@ const struct iris_platform_data sm8650_data = { .tz_cp_config_data = tz_cp_config_sm8550, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, @@ -1127,7 +1125,6 @@ const struct iris_platform_data sm8750_data = { .tz_cp_config_data = tz_cp_config_sm8550, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, @@ -1222,7 +1219,6 @@ const struct iris_platform_data qcs8300_data = { .tz_cp_config_data = tz_cp_config_sm8550, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, - .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 2, .max_session_count = 16, .max_core_mbpf = ((4096 * 2176) / 256) * 4, diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c index cfc5b576ec56..29b07d88507e 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.c +++ b/drivers/media/platform/qcom/iris/iris_utils.c @@ -55,16 +55,13 @@ void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type, int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush) { - struct iris_core *core = inst->core; - u32 hw_response_timeout_val; struct completion *done; int ret; - hw_response_timeout_val = core->iris_platform_data->hw_response_timeout; done = is_flush ? &inst->flush_completion : &inst->completion; mutex_unlock(&inst->lock); - ret = wait_for_completion_timeout(done, msecs_to_jiffies(hw_response_timeout_val)); + ret = wait_for_completion_timeout(done, msecs_to_jiffies(HW_RESPONSE_TIMEOUT_VALUE)); mutex_lock(&inst->lock); if (!ret) { iris_inst_change_state(inst, IRIS_INST_ERROR); From 95faed0b9f516b693860865fc54c2a708946a96d Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:09 +0200 Subject: [PATCH 142/394] media: qcom: iris: split firmware_data from raw platform data Having firmware-related fields in platform data results in the tying platform data to the HFI firmware data rather than the actual hardware. For example, SM8450 uses Gen2 firmware, so currently its platform data should be placed next to the other gen2 platforms, although it has the VPU2.0 core, similar to the one found on SM8250 and SC7280 and so the hardware-specific platform data is also close to those devices. Split firmware data to a separate struct, separating hardware-related data from the firmware interfaces. Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/iris/iris_buffer.c | 82 +++--- drivers/media/platform/qcom/iris/iris_core.h | 2 + drivers/media/platform/qcom/iris/iris_ctrls.c | 8 +- .../qcom/iris/iris_hfi_gen1_command.c | 8 +- .../qcom/iris/iris_hfi_gen2_command.c | 66 ++--- .../platform/qcom/iris/iris_platform_common.h | 91 ++++--- .../platform/qcom/iris/iris_platform_gen1.c | 67 ++--- .../platform/qcom/iris/iris_platform_gen2.c | 248 +++--------------- drivers/media/platform/qcom/iris/iris_probe.c | 3 +- drivers/media/platform/qcom/iris/iris_vidc.c | 10 +- .../platform/qcom/iris/iris_vpu_common.c | 2 +- 11 files changed, 211 insertions(+), 376 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index f55b7c608116..fbe136360aa1 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -301,31 +301,31 @@ static void iris_fill_internal_buf_info(struct iris_inst *inst, void iris_get_internal_buffers(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; const u32 *internal_buf_type; u32 internal_buffer_count, i; if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; for (i = 0; i < internal_buffer_count; i++) iris_fill_internal_buf_info(inst, internal_buf_type[i]); } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - internal_buffer_count = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; for (i = 0; i < internal_buffer_count; i++) iris_fill_internal_buf_info(inst, internal_buf_type[i]); } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; for (i = 0; i < internal_buffer_count; i++) iris_fill_internal_buf_info(inst, internal_buf_type[i]); } else { - internal_buf_type = platform_data->enc_op_int_buf_tbl; - internal_buffer_count = platform_data->enc_op_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_op_int_buf_tbl; + internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size; for (i = 0; i < internal_buffer_count; i++) iris_fill_internal_buf_info(inst, internal_buf_type[i]); } @@ -366,7 +366,7 @@ static int iris_create_internal_buffer(struct iris_inst *inst, int iris_create_internal_buffers(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; u32 internal_buffer_count, i, j; struct iris_buffers *buffers; const u32 *internal_buf_type; @@ -374,19 +374,19 @@ int iris_create_internal_buffers(struct iris_inst *inst, u32 plane) if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - internal_buffer_count = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->enc_op_int_buf_tbl; - internal_buffer_count = platform_data->enc_op_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_op_int_buf_tbl; + internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size; } } @@ -442,7 +442,7 @@ int iris_queue_internal_deferred_buffers(struct iris_inst *inst, enum iris_buffe int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; struct iris_buffer *buffer, *next; struct iris_buffers *buffers; const u32 *internal_buf_type; @@ -451,19 +451,19 @@ int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane) if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - internal_buffer_count = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->enc_op_int_buf_tbl; - internal_buffer_count = platform_data->enc_op_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_op_int_buf_tbl; + internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size; } } @@ -501,7 +501,7 @@ int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buf static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool force) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; struct iris_buffer *buf, *next; struct iris_buffers *buffers; const u32 *internal_buf_type; @@ -510,19 +510,19 @@ static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - len = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + len = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - len = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + len = firmware_data->dec_op_int_buf_tbl_size; } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - len = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + len = firmware_data->enc_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->enc_op_int_buf_tbl; - len = platform_data->enc_op_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_op_int_buf_tbl; + len = firmware_data->enc_op_int_buf_tbl_size; } } @@ -593,17 +593,17 @@ static int iris_release_internal_buffers(struct iris_inst *inst, static int iris_release_input_internal_buffers(struct iris_inst *inst) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; const u32 *internal_buf_type; u32 internal_buffer_count, i; int ret; if (inst->domain == DECODER) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->enc_ip_int_buf_tbl; - internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; } for (i = 0; i < internal_buffer_count; i++) { diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h index 1592681640ab..e0ca245c8c63 100644 --- a/drivers/media/platform/qcom/iris/iris_core.h +++ b/drivers/media/platform/qcom/iris/iris_core.h @@ -54,6 +54,7 @@ struct qcom_ubwc_cfg_data; * @resets: table of iris reset clocks * @controller_resets: table of controller reset clocks * @iris_platform_data: a structure for platform data + * @iris_firmware_data: a pointer to the firmware (or HFI) specific data * @ubwc_cfg: UBWC configuration for the platform * @state: current state of core * @iface_q_table_daddr: device address for interface queue table memory @@ -97,6 +98,7 @@ struct iris_core { struct reset_control_bulk_data *resets; struct reset_control_bulk_data *controller_resets; const struct iris_platform_data *iris_platform_data; + const struct iris_firmware_data *iris_firmware_data; const struct qcom_ubwc_cfg_data *ubwc_cfg; enum iris_core_state state; dma_addr_t iface_q_table_daddr; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index 5a24aa869b2d..ef7adac3764d 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -332,8 +332,8 @@ void iris_session_init_caps(struct iris_core *core) const struct platform_inst_fw_cap *caps; u32 i, num_cap, cap_id; - caps = core->iris_platform_data->inst_fw_caps_dec; - num_cap = core->iris_platform_data->inst_fw_caps_dec_size; + caps = core->iris_firmware_data->inst_fw_caps_dec; + num_cap = core->iris_firmware_data->inst_fw_caps_dec_size; for (i = 0; i < num_cap; i++) { cap_id = caps[i].cap_id; @@ -360,8 +360,8 @@ void iris_session_init_caps(struct iris_core *core) } } - caps = core->iris_platform_data->inst_fw_caps_enc; - num_cap = core->iris_platform_data->inst_fw_caps_enc_size; + caps = core->iris_firmware_data->inst_fw_caps_enc; + num_cap = core->iris_firmware_data->inst_fw_caps_enc_size; for (i = 0; i < num_cap; i++) { cap_id = caps[i].cap_id; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 0017ade4adbd..3fb90a466a64 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -1033,8 +1033,8 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p }; if (inst->domain == DECODER) { - config_params = core->iris_platform_data->dec_input_config_params_default; - config_params_size = core->iris_platform_data->dec_input_config_params_default_size; + config_params = core->iris_firmware_data->dec_input_config_params_default; + config_params_size = core->iris_firmware_data->dec_input_config_params_default_size; if (V4L2_TYPE_IS_OUTPUT(plane)) { handler = vdec_prop_type_handle_inp_arr; handler_size = ARRAY_SIZE(vdec_prop_type_handle_inp_arr); @@ -1043,8 +1043,8 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p handler_size = ARRAY_SIZE(vdec_prop_type_handle_out_arr); } } else { - config_params = core->iris_platform_data->enc_input_config_params; - config_params_size = core->iris_platform_data->enc_input_config_params_size; + config_params = core->iris_firmware_data->enc_input_config_params; + config_params_size = core->iris_firmware_data->enc_input_config_params_size; handler = venc_prop_type_handle_inp_arr; handler_size = ARRAY_SIZE(venc_prop_type_handle_inp_arr); } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index 639b75fca1ab..c90b22a75bc5 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -601,7 +601,7 @@ static int iris_hfi_gen2_set_super_block(struct iris_inst *inst, u32 plane) static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *pdata = inst->core->iris_platform_data; + const struct iris_firmware_data *fdata = inst->core->iris_firmware_data; u32 config_params_size = 0, i, j; const u32 *config_params = NULL; int ret; @@ -630,31 +630,31 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { if (inst->codec == V4L2_PIX_FMT_H264) { - config_params = pdata->dec_input_config_params_default; - config_params_size = pdata->dec_input_config_params_default_size; + config_params = fdata->dec_input_config_params_default; + config_params_size = fdata->dec_input_config_params_default_size; } else if (inst->codec == V4L2_PIX_FMT_HEVC) { - config_params = pdata->dec_input_config_params_hevc; - config_params_size = pdata->dec_input_config_params_hevc_size; + config_params = fdata->dec_input_config_params_hevc; + config_params_size = fdata->dec_input_config_params_hevc_size; } else if (inst->codec == V4L2_PIX_FMT_VP9) { - config_params = pdata->dec_input_config_params_vp9; - config_params_size = pdata->dec_input_config_params_vp9_size; + config_params = fdata->dec_input_config_params_vp9; + config_params_size = fdata->dec_input_config_params_vp9_size; } else if (inst->codec == V4L2_PIX_FMT_AV1) { - config_params = pdata->dec_input_config_params_av1; - config_params_size = pdata->dec_input_config_params_av1_size; + config_params = fdata->dec_input_config_params_av1; + config_params_size = fdata->dec_input_config_params_av1_size; } else { return -EINVAL; } } else { - config_params = pdata->dec_output_config_params; - config_params_size = pdata->dec_output_config_params_size; + config_params = fdata->dec_output_config_params; + config_params_size = fdata->dec_output_config_params_size; } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - config_params = pdata->enc_input_config_params; - config_params_size = pdata->enc_input_config_params_size; + config_params = fdata->enc_input_config_params; + config_params_size = fdata->enc_input_config_params_size; } else { - config_params = pdata->enc_output_config_params; - config_params_size = pdata->enc_output_config_params_size; + config_params = fdata->enc_output_config_params; + config_params_size = fdata->enc_output_config_params_size; } } @@ -849,24 +849,24 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan switch (inst->codec) { case V4L2_PIX_FMT_H264: - change_param = core->iris_platform_data->dec_input_config_params_default; + change_param = core->iris_firmware_data->dec_input_config_params_default; change_param_size = - core->iris_platform_data->dec_input_config_params_default_size; + core->iris_firmware_data->dec_input_config_params_default_size; break; case V4L2_PIX_FMT_HEVC: - change_param = core->iris_platform_data->dec_input_config_params_hevc; + change_param = core->iris_firmware_data->dec_input_config_params_hevc; change_param_size = - core->iris_platform_data->dec_input_config_params_hevc_size; + core->iris_firmware_data->dec_input_config_params_hevc_size; break; case V4L2_PIX_FMT_VP9: - change_param = core->iris_platform_data->dec_input_config_params_vp9; + change_param = core->iris_firmware_data->dec_input_config_params_vp9; change_param_size = - core->iris_platform_data->dec_input_config_params_vp9_size; + core->iris_firmware_data->dec_input_config_params_vp9_size; break; case V4L2_PIX_FMT_AV1: - change_param = core->iris_platform_data->dec_input_config_params_av1; + change_param = core->iris_firmware_data->dec_input_config_params_av1; change_param_size = - core->iris_platform_data->dec_input_config_params_av1_size; + core->iris_firmware_data->dec_input_config_params_av1_size; break; } @@ -996,29 +996,29 @@ static int iris_hfi_gen2_subscribe_property(struct iris_inst *inst, u32 plane) return 0; if (V4L2_TYPE_IS_OUTPUT(plane)) { - subscribe_prop_size = core->iris_platform_data->dec_input_prop_size; - subcribe_prop = core->iris_platform_data->dec_input_prop; + subscribe_prop_size = core->iris_firmware_data->dec_input_prop_size; + subcribe_prop = core->iris_firmware_data->dec_input_prop; } else { switch (inst->codec) { case V4L2_PIX_FMT_H264: - subcribe_prop = core->iris_platform_data->dec_output_prop_avc; + subcribe_prop = core->iris_firmware_data->dec_output_prop_avc; subscribe_prop_size = - core->iris_platform_data->dec_output_prop_avc_size; + core->iris_firmware_data->dec_output_prop_avc_size; break; case V4L2_PIX_FMT_HEVC: - subcribe_prop = core->iris_platform_data->dec_output_prop_hevc; + subcribe_prop = core->iris_firmware_data->dec_output_prop_hevc; subscribe_prop_size = - core->iris_platform_data->dec_output_prop_hevc_size; + core->iris_firmware_data->dec_output_prop_hevc_size; break; case V4L2_PIX_FMT_VP9: - subcribe_prop = core->iris_platform_data->dec_output_prop_vp9; + subcribe_prop = core->iris_firmware_data->dec_output_prop_vp9; subscribe_prop_size = - core->iris_platform_data->dec_output_prop_vp9_size; + core->iris_firmware_data->dec_output_prop_vp9_size; break; case V4L2_PIX_FMT_AV1: - subcribe_prop = core->iris_platform_data->dec_output_prop_av1; + subcribe_prop = core->iris_firmware_data->dec_output_prop_av1; subscribe_prop_size = - core->iris_platform_data->dec_output_prop_av1_size; + core->iris_firmware_data->dec_output_prop_av1_size; break; } } diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index e8a219023aaa..5af6d9f49f01 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -201,9 +201,61 @@ enum platform_pm_domain_type { IRIS_APV_HW_POWER_DOMAIN, }; -struct iris_platform_data { +struct iris_firmware_data { void (*init_hfi_ops)(struct iris_core *core); + + u32 core_arch; + + const struct platform_inst_fw_cap *inst_fw_caps_dec; + u32 inst_fw_caps_dec_size; + const struct platform_inst_fw_cap *inst_fw_caps_enc; + u32 inst_fw_caps_enc_size; + + const u32 *dec_input_config_params_default; + unsigned int dec_input_config_params_default_size; + const u32 *dec_input_config_params_hevc; + unsigned int dec_input_config_params_hevc_size; + const u32 *dec_input_config_params_vp9; + unsigned int dec_input_config_params_vp9_size; + const u32 *dec_input_config_params_av1; + unsigned int dec_input_config_params_av1_size; + const u32 *dec_output_config_params; + unsigned int dec_output_config_params_size; + const u32 *enc_input_config_params; + unsigned int enc_input_config_params_size; + const u32 *enc_output_config_params; + unsigned int enc_output_config_params_size; + + const u32 *dec_input_prop; + unsigned int dec_input_prop_size; + const u32 *dec_output_prop_avc; + unsigned int dec_output_prop_avc_size; + const u32 *dec_output_prop_hevc; + unsigned int dec_output_prop_hevc_size; + const u32 *dec_output_prop_vp9; + unsigned int dec_output_prop_vp9_size; + const u32 *dec_output_prop_av1; + unsigned int dec_output_prop_av1_size; + + const u32 *dec_ip_int_buf_tbl; + unsigned int dec_ip_int_buf_tbl_size; + const u32 *dec_op_int_buf_tbl; + unsigned int dec_op_int_buf_tbl_size; + const u32 *enc_ip_int_buf_tbl; + unsigned int enc_ip_int_buf_tbl_size; + const u32 *enc_op_int_buf_tbl; + unsigned int enc_op_int_buf_tbl_size; +}; + +struct iris_platform_data { + /* + * XXX: remove firmware_data pointer and consider moving + * get_vpu_buffer_size pointer once we have platforms supporting both + * firmware kinds. + */ + const struct iris_firmware_data *firmware_data; u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type); + const struct vpu_ops *vpu_ops; const struct icc_info *icc_tbl; unsigned int icc_tbl_size; @@ -225,13 +277,8 @@ struct iris_platform_data { struct iris_fmt *inst_iris_fmts; u32 inst_iris_fmts_size; struct platform_inst_caps *inst_caps; - const struct platform_inst_fw_cap *inst_fw_caps_dec; - u32 inst_fw_caps_dec_size; - const struct platform_inst_fw_cap *inst_fw_caps_enc; - u32 inst_fw_caps_enc_size; const struct tz_cp_config *tz_cp_config_data; u32 tz_cp_config_data_size; - u32 core_arch; u32 num_vpp_pipe; bool no_aon; u32 max_session_count; @@ -239,38 +286,6 @@ struct iris_platform_data { u32 max_core_mbpf; /* max number of macroblocks per second supported */ u32 max_core_mbps; - const u32 *dec_input_config_params_default; - unsigned int dec_input_config_params_default_size; - const u32 *dec_input_config_params_hevc; - unsigned int dec_input_config_params_hevc_size; - const u32 *dec_input_config_params_vp9; - unsigned int dec_input_config_params_vp9_size; - const u32 *dec_input_config_params_av1; - unsigned int dec_input_config_params_av1_size; - const u32 *dec_output_config_params; - unsigned int dec_output_config_params_size; - const u32 *enc_input_config_params; - unsigned int enc_input_config_params_size; - const u32 *enc_output_config_params; - unsigned int enc_output_config_params_size; - const u32 *dec_input_prop; - unsigned int dec_input_prop_size; - const u32 *dec_output_prop_avc; - unsigned int dec_output_prop_avc_size; - const u32 *dec_output_prop_hevc; - unsigned int dec_output_prop_hevc_size; - const u32 *dec_output_prop_vp9; - unsigned int dec_output_prop_vp9_size; - const u32 *dec_output_prop_av1; - unsigned int dec_output_prop_av1_size; - const u32 *dec_ip_int_buf_tbl; - unsigned int dec_ip_int_buf_tbl_size; - const u32 *dec_op_int_buf_tbl; - unsigned int dec_op_int_buf_tbl_size; - const u32 *enc_ip_int_buf_tbl; - unsigned int enc_ip_int_buf_tbl_size; - const u32 *enc_op_int_buf_tbl; - unsigned int enc_op_int_buf_tbl_size; }; #endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index 6ed4c4ae4056..8875f90d487e 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -332,8 +332,33 @@ static const u32 sm8250_enc_ip_int_buf_tbl[] = { BUF_SCRATCH_2, }; -const struct iris_platform_data sm8250_data = { +const struct iris_firmware_data iris_hfi_gen1_data = { .init_hfi_ops = &iris_hfi_gen1_sys_ops_init, + + .inst_fw_caps_dec = inst_fw_cap_sm8250_dec, + .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec), + .inst_fw_caps_enc = inst_fw_cap_sm8250_enc, + .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), + + .dec_input_config_params_default = + sm8250_vdec_input_config_param_default, + .dec_input_config_params_default_size = + ARRAY_SIZE(sm8250_vdec_input_config_param_default), + .enc_input_config_params = sm8250_venc_input_config_param, + .enc_input_config_params_size = + ARRAY_SIZE(sm8250_venc_input_config_param), + + .dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl, + .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_ip_int_buf_tbl), + .dec_op_int_buf_tbl = sm8250_dec_op_int_buf_tbl, + .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_op_int_buf_tbl), + + .enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl, + .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl), +}; + +const struct iris_platform_data sm8250_data = { + .firmware_data = &iris_hfi_gen1_data, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, .icc_tbl = sm8250_icc_table, @@ -355,35 +380,16 @@ const struct iris_platform_data sm8250_data = { .inst_iris_fmts = platform_fmts_sm8250_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), .inst_caps = &platform_inst_cap_sm8250, - .inst_fw_caps_dec = inst_fw_cap_sm8250_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec), - .inst_fw_caps_enc = inst_fw_cap_sm8250_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), .tz_cp_config_data = tz_cp_config_sm8250, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K, .max_core_mbps = ((7680 * 4320) / 256) * 60, - .dec_input_config_params_default = - sm8250_vdec_input_config_param_default, - .dec_input_config_params_default_size = - ARRAY_SIZE(sm8250_vdec_input_config_param_default), - .enc_input_config_params = sm8250_venc_input_config_param, - .enc_input_config_params_size = - ARRAY_SIZE(sm8250_venc_input_config_param), - - .dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl, - .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_ip_int_buf_tbl), - .dec_op_int_buf_tbl = sm8250_dec_op_int_buf_tbl, - .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_op_int_buf_tbl), - - .enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl, - .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl), }; const struct iris_platform_data sc7280_data = { - .init_hfi_ops = &iris_hfi_gen1_sys_ops_init, + .firmware_data = &iris_hfi_gen1_data, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu2_ops, .icc_tbl = sm8250_icc_table, @@ -403,10 +409,6 @@ const struct iris_platform_data sc7280_data = { .inst_iris_fmts = platform_fmts_sm8250_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), .inst_caps = &platform_inst_cap_sm8250, - .inst_fw_caps_dec = inst_fw_cap_sm8250_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec), - .inst_fw_caps_enc = inst_fw_cap_sm8250_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), .tz_cp_config_data = tz_cp_config_sm8250, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), .num_vpp_pipe = 1, @@ -415,19 +417,4 @@ const struct iris_platform_data sc7280_data = { .max_core_mbpf = 4096 * 2176 / 256 * 2 + 1920 * 1088 / 256, /* max spec for SC7280 is 4096x2176@60fps */ .max_core_mbps = 4096 * 2176 / 256 * 60, - .dec_input_config_params_default = - sm8250_vdec_input_config_param_default, - .dec_input_config_params_default_size = - ARRAY_SIZE(sm8250_vdec_input_config_param_default), - .enc_input_config_params = sm8250_venc_input_config_param, - .enc_input_config_params_size = - ARRAY_SIZE(sm8250_venc_input_config_param), - - .dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl, - .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_ip_int_buf_tbl), - .dec_op_int_buf_tbl = sm8250_dec_op_int_buf_tbl, - .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_op_int_buf_tbl), - - .enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl, - .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl), }; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index abe523db45c2..05fbab276100 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -906,40 +906,16 @@ static const u32 sm8550_enc_op_int_buf_tbl[] = { BUF_SCRATCH_2, }; -const struct iris_platform_data sm8550_data = { +const struct iris_firmware_data iris_hfi_gen2_data = { .init_hfi_ops = iris_hfi_gen2_sys_ops_init, - .get_vpu_buffer_size = iris_vpu_buf_size, - .vpu_ops = &iris_vpu3_ops, - .icc_tbl = sm8550_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), - .clk_rst_tbl = sm8550_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), - .bw_tbl_dec = sm8550_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), - .pmdomain_tbl = sm8550_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), - .opp_pd_tbl = sm8550_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), - .clk_tbl = sm8550_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), - .opp_clk_tbl = sm8550_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu30_p4.mbn", - .inst_iris_fmts = platform_fmts_sm8550_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), - .inst_caps = &platform_inst_cap_sm8550, + + .core_arch = VIDEO_ARCH_LX, + .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), - .tz_cp_config_data = tz_cp_config_sm8550, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .core_arch = VIDEO_ARCH_LX, - .num_vpp_pipe = 4, - .max_session_count = 16, - .max_core_mbpf = NUM_MBS_8K * 2, - .max_core_mbps = ((7680 * 4320) / 256) * 60, + .dec_input_config_params_default = sm8550_vdec_input_config_params_default, .dec_input_config_params_default_size = @@ -996,6 +972,37 @@ const struct iris_platform_data sm8550_data = { .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; +const struct iris_platform_data sm8550_data = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .vpu_ops = &iris_vpu3_ops, + .icc_tbl = sm8550_icc_table, + .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), + .clk_rst_tbl = sm8550_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), + .bw_tbl_dec = sm8550_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), + .pmdomain_tbl = sm8550_pmdomain_table, + .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), + .opp_pd_tbl = sm8550_opp_pd_table, + .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), + .clk_tbl = sm8550_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), + .opp_clk_tbl = sm8550_opp_clk_table, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu30_p4.mbn", + .inst_iris_fmts = platform_fmts_sm8550_dec, + .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), + .inst_caps = &platform_inst_cap_sm8550, + .tz_cp_config_data = tz_cp_config_sm8550, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), + .num_vpp_pipe = 4, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; + /* * Shares most of SM8550 data except: * - vpu_ops to iris_vpu33_ops @@ -1004,7 +1011,7 @@ const struct iris_platform_data sm8550_data = { * - fwname to "qcom/vpu/vpu33_p4.mbn" */ const struct iris_platform_data sm8650_data = { - .init_hfi_ops = iris_hfi_gen2_sys_ops_init, + .firmware_data = &iris_hfi_gen2_data, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu33_ops, .icc_tbl = sm8550_icc_table, @@ -1028,75 +1035,16 @@ const struct iris_platform_data sm8650_data = { .inst_iris_fmts = platform_fmts_sm8550_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_sm8550, - .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), - .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), .tz_cp_config_data = tz_cp_config_sm8550, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .core_arch = VIDEO_ARCH_LX, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, .max_core_mbps = ((7680 * 4320) / 256) * 60, - .dec_input_config_params_default = - sm8550_vdec_input_config_params_default, - .dec_input_config_params_default_size = - ARRAY_SIZE(sm8550_vdec_input_config_params_default), - .dec_input_config_params_hevc = - sm8550_vdec_input_config_param_hevc, - .dec_input_config_params_hevc_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_hevc), - .dec_input_config_params_vp9 = - sm8550_vdec_input_config_param_vp9, - .dec_input_config_params_vp9_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), - .dec_input_config_params_av1 = - sm8550_vdec_input_config_param_av1, - .dec_input_config_params_av1_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_av1), - .dec_output_config_params = - sm8550_vdec_output_config_params, - .dec_output_config_params_size = - ARRAY_SIZE(sm8550_vdec_output_config_params), - - .enc_input_config_params = - sm8550_venc_input_config_params, - .enc_input_config_params_size = - ARRAY_SIZE(sm8550_venc_input_config_params), - .enc_output_config_params = - sm8550_venc_output_config_params, - .enc_output_config_params_size = - ARRAY_SIZE(sm8550_venc_output_config_params), - - .dec_input_prop = sm8550_vdec_subscribe_input_properties, - .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties), - .dec_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc, - .dec_output_prop_avc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc), - .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc, - .dec_output_prop_hevc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc), - .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, - .dec_output_prop_vp9_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), - .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1, - .dec_output_prop_av1_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1), - - .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, - .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), - .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl, - .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl), - - .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl, - .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl), - .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, - .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; const struct iris_platform_data sm8750_data = { - .init_hfi_ops = iris_hfi_gen2_sys_ops_init, + .firmware_data = &iris_hfi_gen2_data, .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu35_ops, .icc_tbl = sm8550_icc_table, @@ -1118,71 +1066,12 @@ const struct iris_platform_data sm8750_data = { .inst_iris_fmts = platform_fmts_sm8550_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_sm8550, - .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), - .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), .tz_cp_config_data = tz_cp_config_sm8550, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .core_arch = VIDEO_ARCH_LX, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, .max_core_mbps = ((7680 * 4320) / 256) * 60, - .dec_input_config_params_default = - sm8550_vdec_input_config_params_default, - .dec_input_config_params_default_size = - ARRAY_SIZE(sm8550_vdec_input_config_params_default), - .dec_input_config_params_hevc = - sm8550_vdec_input_config_param_hevc, - .dec_input_config_params_hevc_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_hevc), - .dec_input_config_params_vp9 = - sm8550_vdec_input_config_param_vp9, - .dec_input_config_params_vp9_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), - .dec_input_config_params_av1 = - sm8550_vdec_input_config_param_av1, - .dec_input_config_params_av1_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_av1), - .dec_output_config_params = - sm8550_vdec_output_config_params, - .dec_output_config_params_size = - ARRAY_SIZE(sm8550_vdec_output_config_params), - - .enc_input_config_params = - sm8550_venc_input_config_params, - .enc_input_config_params_size = - ARRAY_SIZE(sm8550_venc_input_config_params), - .enc_output_config_params = - sm8550_venc_output_config_params, - .enc_output_config_params_size = - ARRAY_SIZE(sm8550_venc_output_config_params), - - .dec_input_prop = sm8550_vdec_subscribe_input_properties, - .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties), - .dec_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc, - .dec_output_prop_avc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc), - .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc, - .dec_output_prop_hevc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc), - .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, - .dec_output_prop_vp9_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), - .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1, - .dec_output_prop_av1_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1), - - .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, - .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), - .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl, - .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl), - - .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl, - .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl), - .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, - .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; /* @@ -1190,7 +1079,7 @@ const struct iris_platform_data sm8750_data = { * - inst_caps to platform_inst_cap_qcs8300 */ const struct iris_platform_data qcs8300_data = { - .init_hfi_ops = iris_hfi_gen2_sys_ops_init, + .firmware_data = &iris_hfi_gen2_data, .get_vpu_buffer_size = iris_vpu_buf_size, .vpu_ops = &iris_vpu3_ops, .icc_tbl = sm8550_icc_table, @@ -1212,69 +1101,10 @@ const struct iris_platform_data qcs8300_data = { .inst_iris_fmts = platform_fmts_sm8550_dec, .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_qcs8300, - .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, - .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), - .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, - .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), .tz_cp_config_data = tz_cp_config_sm8550, .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .core_arch = VIDEO_ARCH_LX, .num_vpp_pipe = 2, .max_session_count = 16, .max_core_mbpf = ((4096 * 2176) / 256) * 4, .max_core_mbps = (((3840 * 2176) / 256) * 120), - .dec_input_config_params_default = - sm8550_vdec_input_config_params_default, - .dec_input_config_params_default_size = - ARRAY_SIZE(sm8550_vdec_input_config_params_default), - .dec_input_config_params_hevc = - sm8550_vdec_input_config_param_hevc, - .dec_input_config_params_hevc_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_hevc), - .dec_input_config_params_vp9 = - sm8550_vdec_input_config_param_vp9, - .dec_input_config_params_vp9_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), - .dec_input_config_params_av1 = - sm8550_vdec_input_config_param_av1, - .dec_input_config_params_av1_size = - ARRAY_SIZE(sm8550_vdec_input_config_param_av1), - .dec_output_config_params = - sm8550_vdec_output_config_params, - .dec_output_config_params_size = - ARRAY_SIZE(sm8550_vdec_output_config_params), - - .enc_input_config_params = - sm8550_venc_input_config_params, - .enc_input_config_params_size = - ARRAY_SIZE(sm8550_venc_input_config_params), - .enc_output_config_params = - sm8550_venc_output_config_params, - .enc_output_config_params_size = - ARRAY_SIZE(sm8550_venc_output_config_params), - - .dec_input_prop = sm8550_vdec_subscribe_input_properties, - .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties), - .dec_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc, - .dec_output_prop_avc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc), - .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc, - .dec_output_prop_hevc_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc), - .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, - .dec_output_prop_vp9_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), - .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1, - .dec_output_prop_av1_size = - ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1), - - .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, - .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), - .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl, - .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl), - - .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl, - .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl), - .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, - .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index fa561f6a736c..dd87504c2e67 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -251,6 +251,7 @@ static int iris_probe(struct platform_device *pdev) return core->irq; core->iris_platform_data = of_device_get_match_data(core->dev); + core->iris_firmware_data = core->iris_platform_data->firmware_data; core->ubwc_cfg = qcom_ubwc_config_get_data(); if (IS_ERR(core->ubwc_cfg)) @@ -264,7 +265,7 @@ static int iris_probe(struct platform_device *pdev) disable_irq_nosync(core->irq); iris_init_ops(core); - core->iris_platform_data->init_hfi_ops(core); + core->iris_firmware_data->init_hfi_ops(core); ret = iris_init_resources(core); if (ret) diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index ecd8a20fedbf..807c9a20b6ba 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -243,7 +243,7 @@ static void iris_session_close(struct iris_inst *inst) static void iris_check_num_queued_internal_buffers(struct iris_inst *inst, u32 plane) { - const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; struct iris_buffer *buf, *next; struct iris_buffers *buffers; const u32 *internal_buf_type; @@ -251,11 +251,11 @@ static void iris_check_num_queued_internal_buffers(struct iris_inst *inst, u32 p u32 count = 0; if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = platform_data->dec_ip_int_buf_tbl; - internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = platform_data->dec_op_int_buf_tbl; - internal_buffer_count = platform_data->dec_op_int_buf_tbl_size; + internal_buf_type = firmware_data->dec_op_int_buf_tbl; + internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; } for (i = 0; i < internal_buffer_count; i++) { diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index dbce5aeba06c..c6cfc1d9fd9e 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -63,7 +63,7 @@ static void iris_vpu_setup_ucregion_memory_map(struct iris_core *core) writel(QTBL_ENABLE, core->reg_base + QTBL_INFO); if (core->sfr_daddr) { - value = (u32)core->sfr_daddr + core->iris_platform_data->core_arch; + value = (u32)core->sfr_daddr + core->iris_firmware_data->core_arch; writel(value, core->reg_base + SFR_ADDR); } From 53a5e095636acbab817a7fb98a67ce76cac59fdf Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:10 +0200 Subject: [PATCH 143/394] media: qcom: iris: split platform data from firmware data Finalize the logical separation of the software and hardware interface descriptions by moving hardware properties to the files specific to the particular VPU version. Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/Makefile | 6 +- .../{iris_platform_gen1.c => iris_hfi_gen1.c} | 134 ----------- .../{iris_platform_gen2.c => iris_hfi_gen2.c} | 214 ------------------ .../platform/qcom/iris/iris_platform_common.h | 3 + .../platform/qcom/iris/iris_platform_sm8250.h | 29 +++ .../platform/qcom/iris/iris_platform_sm8550.h | 31 +++ .../platform/qcom/iris/iris_platform_vpu2.c | 124 ++++++++++ .../platform/qcom/iris/iris_platform_vpu3x.c | 204 +++++++++++++++++ 8 files changed, 395 insertions(+), 350 deletions(-) rename drivers/media/platform/qcom/iris/{iris_platform_gen1.c => iris_hfi_gen1.c} (67%) rename drivers/media/platform/qcom/iris/{iris_platform_gen2.c => iris_hfi_gen2.c} (77%) create mode 100644 drivers/media/platform/qcom/iris/iris_platform_sm8250.h create mode 100644 drivers/media/platform/qcom/iris/iris_platform_sm8550.h create mode 100644 drivers/media/platform/qcom/iris/iris_platform_vpu2.c create mode 100644 drivers/media/platform/qcom/iris/iris_platform_vpu3x.c diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile index 2fde45f81727..48e415cbc439 100644 --- a/drivers/media/platform/qcom/iris/Makefile +++ b/drivers/media/platform/qcom/iris/Makefile @@ -4,14 +4,16 @@ qcom-iris-objs += iris_buffer.o \ iris_ctrls.o \ iris_firmware.o \ iris_hfi_common.o \ + iris_hfi_gen1.o \ iris_hfi_gen1_command.o \ iris_hfi_gen1_response.o \ + iris_hfi_gen2.o \ iris_hfi_gen2_command.o \ iris_hfi_gen2_packet.o \ iris_hfi_gen2_response.o \ iris_hfi_queue.o \ - iris_platform_gen1.o \ - iris_platform_gen2.o \ + iris_platform_vpu2.o \ + iris_platform_vpu3x.o \ iris_power.o \ iris_probe.o \ iris_resources.o \ diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c similarity index 67% rename from drivers/media/platform/qcom/iris/iris_platform_gen1.c rename to drivers/media/platform/qcom/iris/iris_hfi_gen1.c index 8875f90d487e..60f51a1ba941 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c @@ -3,38 +3,16 @@ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ -#include "iris_core.h" #include "iris_ctrls.h" #include "iris_platform_common.h" -#include "iris_resources.h" #include "iris_hfi_gen1.h" #include "iris_hfi_gen1_defines.h" #include "iris_vpu_buffer.h" -#include "iris_vpu_common.h" -#include "iris_instance.h" - -#include "iris_platform_sc7280.h" #define BITRATE_MIN 32000 #define BITRATE_MAX 160000000 -#define BITRATE_PEAK_DEFAULT (BITRATE_DEFAULT * 2) #define BITRATE_STEP 100 -static struct iris_fmt platform_fmts_sm8250_dec[] = { - [IRIS_FMT_H264] = { - .pixfmt = V4L2_PIX_FMT_H264, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_HEVC] = { - .pixfmt = V4L2_PIX_FMT_HEVC, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_VP9] = { - .pixfmt = V4L2_PIX_FMT_VP9, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, -}; - static struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = { { .cap_id = PIPE, @@ -248,56 +226,6 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { }, }; -static struct platform_inst_caps platform_inst_cap_sm8250 = { - .min_frame_width = 128, - .max_frame_width = 8192, - .min_frame_height = 128, - .max_frame_height = 8192, - .max_mbpf = 138240, - .mb_cycles_vsp = 25, - .mb_cycles_vpp = 200, - .max_frame_rate = MAXIMUM_FPS, - .max_operating_rate = MAXIMUM_FPS, -}; - -static const struct icc_info sm8250_icc_table[] = { - { "cpu-cfg", 1000, 1000 }, - { "video-mem", 1000, 15000000 }, -}; - -static const char * const sm8250_clk_reset_table[] = { "bus", "core" }; - -static const struct bw_info sm8250_bw_table_dec[] = { - { ((4096 * 2160) / 256) * 60, 2403000 }, - { ((4096 * 2160) / 256) * 30, 1224000 }, - { ((1920 * 1080) / 256) * 60, 812000 }, - { ((1920 * 1080) / 256) * 30, 416000 }, -}; - -static const char * const sm8250_pmdomain_table[] = { "venus", "vcodec0" }; - -static const char * const sm8250_opp_pd_table[] = { "mx", "mmcx" }; - -static const struct platform_clk_data sm8250_clk_table[] = { - {IRIS_AXI_CLK, "iface" }, - {IRIS_CTRL_CLK, "core" }, - {IRIS_HW_CLK, "vcodec0_core" }, -}; - -static const char * const sm8250_opp_clk_table[] = { - "vcodec0_core", - NULL, -}; - -static const struct tz_cp_config tz_cp_config_sm8250[] = { - { - .cp_start = 0, - .cp_size = 0x25800000, - .cp_nonpixel_start = 0x01000000, - .cp_nonpixel_size = 0x24800000, - }, -}; - static const u32 sm8250_vdec_input_config_param_default[] = { HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE, HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT, @@ -356,65 +284,3 @@ const struct iris_firmware_data iris_hfi_gen1_data = { .enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl, .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl), }; - -const struct iris_platform_data sm8250_data = { - .firmware_data = &iris_hfi_gen1_data, - .get_vpu_buffer_size = iris_vpu_buf_size, - .vpu_ops = &iris_vpu2_ops, - .icc_tbl = sm8250_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table), - .clk_rst_tbl = sm8250_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8250_clk_reset_table), - .bw_tbl_dec = sm8250_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec), - .pmdomain_tbl = sm8250_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8250_pmdomain_table), - .opp_pd_tbl = sm8250_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8250_opp_pd_table), - .clk_tbl = sm8250_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8250_clk_table), - .opp_clk_tbl = sm8250_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu-1.0/venus.mbn", - .inst_iris_fmts = platform_fmts_sm8250_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), - .inst_caps = &platform_inst_cap_sm8250, - .tz_cp_config_data = tz_cp_config_sm8250, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), - .num_vpp_pipe = 4, - .max_session_count = 16, - .max_core_mbpf = NUM_MBS_8K, - .max_core_mbps = ((7680 * 4320) / 256) * 60, -}; - -const struct iris_platform_data sc7280_data = { - .firmware_data = &iris_hfi_gen1_data, - .get_vpu_buffer_size = iris_vpu_buf_size, - .vpu_ops = &iris_vpu2_ops, - .icc_tbl = sm8250_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table), - .bw_tbl_dec = sc7280_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sc7280_bw_table_dec), - .pmdomain_tbl = sm8250_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8250_pmdomain_table), - .opp_pd_tbl = sc7280_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sc7280_opp_pd_table), - .clk_tbl = sc7280_clk_table, - .clk_tbl_size = ARRAY_SIZE(sc7280_clk_table), - .opp_clk_tbl = sc7280_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu20_p1.mbn", - .inst_iris_fmts = platform_fmts_sm8250_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), - .inst_caps = &platform_inst_cap_sm8250, - .tz_cp_config_data = tz_cp_config_sm8250, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), - .num_vpp_pipe = 1, - .no_aon = true, - .max_session_count = 16, - .max_core_mbpf = 4096 * 2176 / 256 * 2 + 1920 * 1088 / 256, - /* max spec for SC7280 is 4096x2176@60fps */ - .max_core_mbps = 4096 * 2176 / 256 * 60, -}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c similarity index 77% rename from drivers/media/platform/qcom/iris/iris_platform_gen2.c rename to drivers/media/platform/qcom/iris/iris_hfi_gen2.c index 05fbab276100..ce8490d64854 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -4,40 +4,15 @@ * Copyright (c) 2025 Linaro Ltd */ -#include "iris_core.h" #include "iris_ctrls.h" #include "iris_hfi_gen2.h" #include "iris_hfi_gen2_defines.h" #include "iris_platform_common.h" #include "iris_vpu_buffer.h" -#include "iris_vpu_common.h" - -#include "iris_platform_qcs8300.h" -#include "iris_platform_sm8650.h" -#include "iris_platform_sm8750.h" #define VIDEO_ARCH_LX 1 #define BITRATE_MAX 245000000 -static struct iris_fmt platform_fmts_sm8550_dec[] = { - [IRIS_FMT_H264] = { - .pixfmt = V4L2_PIX_FMT_H264, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_HEVC] = { - .pixfmt = V4L2_PIX_FMT_HEVC, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_VP9] = { - .pixfmt = V4L2_PIX_FMT_VP9, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_AV1] = { - .pixfmt = V4L2_PIX_FMT_AV1, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, -}; - static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { { .cap_id = PROFILE_H264, @@ -742,58 +717,6 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { }, }; -static struct platform_inst_caps platform_inst_cap_sm8550 = { - .min_frame_width = 96, - .max_frame_width = 8192, - .min_frame_height = 96, - .max_frame_height = 8192, - .max_mbpf = (8192 * 4352) / 256, - .mb_cycles_vpp = 200, - .mb_cycles_fw = 489583, - .mb_cycles_fw_vpp = 66234, - .num_comv = 0, - .max_frame_rate = MAXIMUM_FPS, - .max_operating_rate = MAXIMUM_FPS, -}; - -static const struct icc_info sm8550_icc_table[] = { - { "cpu-cfg", 1000, 1000 }, - { "video-mem", 1000, 15000000 }, -}; - -static const char * const sm8550_clk_reset_table[] = { "bus" }; - -static const struct bw_info sm8550_bw_table_dec[] = { - { ((4096 * 2160) / 256) * 60, 1608000 }, - { ((4096 * 2160) / 256) * 30, 826000 }, - { ((1920 * 1080) / 256) * 60, 567000 }, - { ((1920 * 1080) / 256) * 30, 294000 }, -}; - -static const char * const sm8550_pmdomain_table[] = { "venus", "vcodec0" }; - -static const char * const sm8550_opp_pd_table[] = { "mxc", "mmcx" }; - -static const struct platform_clk_data sm8550_clk_table[] = { - {IRIS_AXI_CLK, "iface" }, - {IRIS_CTRL_CLK, "core" }, - {IRIS_HW_CLK, "vcodec0_core" }, -}; - -static const char * const sm8550_opp_clk_table[] = { - "vcodec0_core", - NULL, -}; - -static const struct tz_cp_config tz_cp_config_sm8550[] = { - { - .cp_start = 0, - .cp_size = 0x25800000, - .cp_nonpixel_start = 0x01000000, - .cp_nonpixel_size = 0x24800000, - }, -}; - static const u32 sm8550_vdec_input_config_params_default[] = { HFI_PROP_BITSTREAM_RESOLUTION, HFI_PROP_CROP_OFFSETS, @@ -971,140 +894,3 @@ const struct iris_firmware_data iris_hfi_gen2_data = { .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; - -const struct iris_platform_data sm8550_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu_buf_size, - .vpu_ops = &iris_vpu3_ops, - .icc_tbl = sm8550_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), - .clk_rst_tbl = sm8550_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), - .bw_tbl_dec = sm8550_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), - .pmdomain_tbl = sm8550_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), - .opp_pd_tbl = sm8550_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), - .clk_tbl = sm8550_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), - .opp_clk_tbl = sm8550_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu30_p4.mbn", - .inst_iris_fmts = platform_fmts_sm8550_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), - .inst_caps = &platform_inst_cap_sm8550, - .tz_cp_config_data = tz_cp_config_sm8550, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .num_vpp_pipe = 4, - .max_session_count = 16, - .max_core_mbpf = NUM_MBS_8K * 2, - .max_core_mbps = ((7680 * 4320) / 256) * 60, -}; - -/* - * Shares most of SM8550 data except: - * - vpu_ops to iris_vpu33_ops - * - clk_rst_tbl to sm8650_clk_reset_table - * - controller_rst_tbl to sm8650_controller_reset_table - * - fwname to "qcom/vpu/vpu33_p4.mbn" - */ -const struct iris_platform_data sm8650_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu33_buf_size, - .vpu_ops = &iris_vpu33_ops, - .icc_tbl = sm8550_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), - .clk_rst_tbl = sm8650_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8650_clk_reset_table), - .controller_rst_tbl = sm8650_controller_reset_table, - .controller_rst_tbl_size = ARRAY_SIZE(sm8650_controller_reset_table), - .bw_tbl_dec = sm8550_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), - .pmdomain_tbl = sm8550_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), - .opp_pd_tbl = sm8550_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), - .clk_tbl = sm8550_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), - .opp_clk_tbl = sm8550_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu33_p4.mbn", - .inst_iris_fmts = platform_fmts_sm8550_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), - .inst_caps = &platform_inst_cap_sm8550, - .tz_cp_config_data = tz_cp_config_sm8550, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .num_vpp_pipe = 4, - .max_session_count = 16, - .max_core_mbpf = NUM_MBS_8K * 2, - .max_core_mbps = ((7680 * 4320) / 256) * 60, -}; - -const struct iris_platform_data sm8750_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu33_buf_size, - .vpu_ops = &iris_vpu35_ops, - .icc_tbl = sm8550_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), - .clk_rst_tbl = sm8750_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8750_clk_reset_table), - .bw_tbl_dec = sm8550_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), - .pmdomain_tbl = sm8550_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), - .opp_pd_tbl = sm8550_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), - .clk_tbl = sm8750_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8750_clk_table), - .opp_clk_tbl = sm8550_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu35_p4.mbn", - .inst_iris_fmts = platform_fmts_sm8550_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), - .inst_caps = &platform_inst_cap_sm8550, - .tz_cp_config_data = tz_cp_config_sm8550, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .num_vpp_pipe = 4, - .max_session_count = 16, - .max_core_mbpf = NUM_MBS_8K * 2, - .max_core_mbps = ((7680 * 4320) / 256) * 60, -}; - -/* - * Shares most of SM8550 data except: - * - inst_caps to platform_inst_cap_qcs8300 - */ -const struct iris_platform_data qcs8300_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu_buf_size, - .vpu_ops = &iris_vpu3_ops, - .icc_tbl = sm8550_icc_table, - .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), - .clk_rst_tbl = sm8550_clk_reset_table, - .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), - .bw_tbl_dec = sm8550_bw_table_dec, - .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec), - .pmdomain_tbl = sm8550_pmdomain_table, - .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table), - .opp_pd_tbl = sm8550_opp_pd_table, - .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), - .clk_tbl = sm8550_clk_table, - .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), - .opp_clk_tbl = sm8550_opp_clk_table, - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu30_p4_s6.mbn", - .inst_iris_fmts = platform_fmts_sm8550_dec, - .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), - .inst_caps = &platform_inst_cap_qcs8300, - .tz_cp_config_data = tz_cp_config_sm8550, - .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), - .num_vpp_pipe = 2, - .max_session_count = 16, - .max_core_mbpf = ((4096 * 2176) / 256) * 4, - .max_core_mbps = (((3840 * 2176) / 256) * 120), -}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 5af6d9f49f01..6dfead673393 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -40,6 +40,9 @@ enum pipe_type { PIPE_4 = 4, }; +extern const struct iris_firmware_data iris_hfi_gen1_data; +extern const struct iris_firmware_data iris_hfi_gen2_data; + extern const struct iris_platform_data qcs8300_data; extern const struct iris_platform_data sc7280_data; extern const struct iris_platform_data sm8250_data; diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8250.h b/drivers/media/platform/qcom/iris/iris_platform_sm8250.h new file mode 100644 index 000000000000..50306043eb8e --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_sm8250.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __IRIS_PLATFORM_SM8250_H__ +#define __IRIS_PLATFORM_SM8250_H__ + +static const struct bw_info sm8250_bw_table_dec[] = { + { ((4096 * 2160) / 256) * 60, 2403000 }, + { ((4096 * 2160) / 256) * 30, 1224000 }, + { ((1920 * 1080) / 256) * 60, 812000 }, + { ((1920 * 1080) / 256) * 30, 416000 }, +}; + +static const char * const sm8250_opp_pd_table[] = { "mx", "mmcx" }; + +static const struct platform_clk_data sm8250_clk_table[] = { + {IRIS_AXI_CLK, "iface" }, + {IRIS_CTRL_CLK, "core" }, + {IRIS_HW_CLK, "vcodec0_core" }, +}; + +static const char * const sm8250_opp_clk_table[] = { + "vcodec0_core", + NULL, +}; + +#endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8550.h b/drivers/media/platform/qcom/iris/iris_platform_sm8550.h new file mode 100644 index 000000000000..a9d9709c2e35 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_sm8550.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __IRIS_PLATFORM_SM8550_H__ +#define __IRIS_PLATFORM_SM8550_H__ + +static const char * const sm8550_clk_reset_table[] = { "bus" }; + +static const struct platform_clk_data sm8550_clk_table[] = { + {IRIS_AXI_CLK, "iface" }, + {IRIS_CTRL_CLK, "core" }, + {IRIS_HW_CLK, "vcodec0_core" }, +}; + +static struct platform_inst_caps platform_inst_cap_sm8550 = { + .min_frame_width = 96, + .max_frame_width = 8192, + .min_frame_height = 96, + .max_frame_height = 8192, + .max_mbpf = (8192 * 4352) / 256, + .mb_cycles_vpp = 200, + .mb_cycles_fw = 489583, + .mb_cycles_fw_vpp = 66234, + .num_comv = 0, + .max_frame_rate = MAXIMUM_FPS, + .max_operating_rate = MAXIMUM_FPS, +}; + +#endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c new file mode 100644 index 000000000000..ab2a19aa9c36 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "iris_core.h" +#include "iris_ctrls.h" +#include "iris_platform_common.h" +#include "iris_resources.h" +#include "iris_hfi_gen1.h" +#include "iris_hfi_gen1_defines.h" +#include "iris_vpu_buffer.h" +#include "iris_vpu_common.h" +#include "iris_instance.h" + +#include "iris_platform_sc7280.h" +#include "iris_platform_sm8250.h" + +static struct iris_fmt iris_fmts_vpu2_dec[] = { + [IRIS_FMT_H264] = { + .pixfmt = V4L2_PIX_FMT_H264, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_HEVC] = { + .pixfmt = V4L2_PIX_FMT_HEVC, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_VP9] = { + .pixfmt = V4L2_PIX_FMT_VP9, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, +}; + +static struct platform_inst_caps platform_inst_cap_vpu2 = { + .min_frame_width = 128, + .max_frame_width = 8192, + .min_frame_height = 128, + .max_frame_height = 8192, + .max_mbpf = 138240, + .mb_cycles_vsp = 25, + .mb_cycles_vpp = 200, + .max_frame_rate = MAXIMUM_FPS, + .max_operating_rate = MAXIMUM_FPS, +}; + +static const struct icc_info iris_icc_info_vpu2[] = { + { "cpu-cfg", 1000, 1000 }, + { "video-mem", 1000, 15000000 }, +}; + +static const char * const iris_clk_reset_table_vpu2[] = { "bus", "core" }; + +static const char * const iris_pmdomain_table_vpu2[] = { "venus", "vcodec0" }; + +static const struct tz_cp_config tz_cp_config_vpu2[] = { + { + .cp_start = 0, + .cp_size = 0x25800000, + .cp_nonpixel_start = 0x01000000, + .cp_nonpixel_size = 0x24800000, + }, +}; + +const struct iris_platform_data sc7280_data = { + .firmware_data = &iris_hfi_gen1_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .vpu_ops = &iris_vpu2_ops, + .icc_tbl = iris_icc_info_vpu2, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2), + .bw_tbl_dec = sc7280_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sc7280_bw_table_dec), + .pmdomain_tbl = iris_pmdomain_table_vpu2, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu2), + .opp_pd_tbl = sc7280_opp_pd_table, + .opp_pd_tbl_size = ARRAY_SIZE(sc7280_opp_pd_table), + .clk_tbl = sc7280_clk_table, + .clk_tbl_size = ARRAY_SIZE(sc7280_clk_table), + .opp_clk_tbl = sc7280_opp_clk_table, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu20_p1.mbn", + .inst_iris_fmts = iris_fmts_vpu2_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec), + .inst_caps = &platform_inst_cap_vpu2, + .tz_cp_config_data = tz_cp_config_vpu2, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu2), + .num_vpp_pipe = 1, + .no_aon = true, + .max_session_count = 16, + .max_core_mbpf = 4096 * 2176 / 256 * 2 + 1920 * 1088 / 256, + /* max spec for SC7280 is 4096x2176@60fps */ + .max_core_mbps = 4096 * 2176 / 256 * 60, +}; + +const struct iris_platform_data sm8250_data = { + .firmware_data = &iris_hfi_gen1_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .vpu_ops = &iris_vpu2_ops, + .icc_tbl = iris_icc_info_vpu2, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2), + .clk_rst_tbl = iris_clk_reset_table_vpu2, + .clk_rst_tbl_size = ARRAY_SIZE(iris_clk_reset_table_vpu2), + .bw_tbl_dec = sm8250_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec), + .pmdomain_tbl = iris_pmdomain_table_vpu2, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu2), + .opp_pd_tbl = sm8250_opp_pd_table, + .opp_pd_tbl_size = ARRAY_SIZE(sm8250_opp_pd_table), + .clk_tbl = sm8250_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8250_clk_table), + .opp_clk_tbl = sm8250_opp_clk_table, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu-1.0/venus.mbn", + .inst_iris_fmts = iris_fmts_vpu2_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec), + .inst_caps = &platform_inst_cap_vpu2, + .tz_cp_config_data = tz_cp_config_vpu2, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu2), + .num_vpp_pipe = 4, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c new file mode 100644 index 000000000000..c2496aa0f851 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2025 Linaro Ltd + */ + +#include "iris_core.h" +#include "iris_ctrls.h" +#include "iris_hfi_gen2.h" +#include "iris_hfi_gen2_defines.h" +#include "iris_platform_common.h" +#include "iris_vpu_buffer.h" +#include "iris_vpu_common.h" + +#include "iris_platform_qcs8300.h" +#include "iris_platform_sm8550.h" +#include "iris_platform_sm8650.h" +#include "iris_platform_sm8750.h" + +static struct iris_fmt iris_fmts_vpu3x_dec[] = { + [IRIS_FMT_H264] = { + .pixfmt = V4L2_PIX_FMT_H264, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_HEVC] = { + .pixfmt = V4L2_PIX_FMT_HEVC, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_VP9] = { + .pixfmt = V4L2_PIX_FMT_VP9, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_AV1] = { + .pixfmt = V4L2_PIX_FMT_AV1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, +}; + +static const struct icc_info iris_icc_info_vpu3x[] = { + { "cpu-cfg", 1000, 1000 }, + { "video-mem", 1000, 15000000 }, +}; + +static const struct bw_info iris_bw_table_dec_vpu3x[] = { + { ((4096 * 2160) / 256) * 60, 1608000 }, + { ((4096 * 2160) / 256) * 30, 826000 }, + { ((1920 * 1080) / 256) * 60, 567000 }, + { ((1920 * 1080) / 256) * 30, 294000 }, +}; + +static const char * const iris_pmdomain_table_vpu3x[] = { "venus", "vcodec0" }; + +static const char * const iris_opp_pd_table_vpu3x[] = { "mxc", "mmcx" }; + +static const char * const iris_opp_clk_table_vpu3x[] = { + "vcodec0_core", + NULL, +}; + +static const struct tz_cp_config tz_cp_config_vpu3[] = { + { + .cp_start = 0, + .cp_size = 0x25800000, + .cp_nonpixel_start = 0x01000000, + .cp_nonpixel_size = 0x24800000, + }, +}; + +/* + * Shares most of SM8550 data except: + * - inst_caps to platform_inst_cap_qcs8300 + */ +const struct iris_platform_data qcs8300_data = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .vpu_ops = &iris_vpu3_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8550_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = sm8550_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), + .opp_clk_tbl = iris_opp_clk_table_vpu3x, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu30_p4_s6.mbn", + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_qcs8300, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 2, + .max_session_count = 16, + .max_core_mbpf = ((4096 * 2176) / 256) * 4, + .max_core_mbps = (((3840 * 2176) / 256) * 120), +}; + +const struct iris_platform_data sm8550_data = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .vpu_ops = &iris_vpu3_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8550_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = sm8550_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), + .opp_clk_tbl = iris_opp_clk_table_vpu3x, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu30_p4.mbn", + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_sm8550, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 4, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; + +/* + * Shares most of SM8550 data except: + * - vpu_ops to iris_vpu33_ops + * - clk_rst_tbl to sm8650_clk_reset_table + * - controller_rst_tbl to sm8650_controller_reset_table + * - fwname to "qcom/vpu/vpu33_p4.mbn" + */ +const struct iris_platform_data sm8650_data = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu33_buf_size, + .vpu_ops = &iris_vpu33_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8650_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8650_clk_reset_table), + .controller_rst_tbl = sm8650_controller_reset_table, + .controller_rst_tbl_size = ARRAY_SIZE(sm8650_controller_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = sm8550_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), + .opp_clk_tbl = iris_opp_clk_table_vpu3x, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu33_p4.mbn", + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_sm8550, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 4, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; + +const struct iris_platform_data sm8750_data = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu33_buf_size, + .vpu_ops = &iris_vpu35_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8750_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8750_clk_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = sm8750_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8750_clk_table), + .opp_clk_tbl = iris_opp_clk_table_vpu3x, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu35_p4.mbn", + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_sm8550, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 4, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; From 96360a894197c54066b7e79de4b91c37dba39547 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:11 +0200 Subject: [PATCH 144/394] media: qcom: iris: use new firmware name for SM8250 The linux-firmware is providing the vpuNN_pM.mbn firmware for SM8250 since August of 2024. Stop using the legacy firmware name (vpu-1.0/venus.mbn) and switch to the standard firmware name schema (vpu/vpu20_p4.mbn). Reviewed-by: Vikash Garodia Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_platform_vpu2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c index ab2a19aa9c36..692fbc2aab56 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c @@ -111,7 +111,7 @@ const struct iris_platform_data sm8250_data = { .opp_clk_tbl = sm8250_opp_clk_table, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu-1.0/venus.mbn", + .fwname = "qcom/vpu/vpu20_p4.mbn", .inst_iris_fmts = iris_fmts_vpu2_dec, .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec), .inst_caps = &platform_inst_cap_vpu2, From 2c7fe9db118e29f541a6811594b4316a287b3512 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 29 Mar 2026 02:33:12 +0200 Subject: [PATCH 145/394] media: qcom: iris: extract firmware description data In preparation to adding support for several firmware revisions to be used for a platform, extract the firmware description data. It incorporates firmware name, HFI ops and buffer requirements of the particular firmware build. Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov [bod: Made struct iris_firmware_desc into static consts to pass media CI] Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/iris/iris_buffer.c | 2 +- drivers/media/platform/qcom/iris/iris_core.h | 2 + .../media/platform/qcom/iris/iris_firmware.c | 2 +- .../qcom/iris/iris_hfi_gen1_command.c | 2 +- .../platform/qcom/iris/iris_platform_common.h | 17 ++++---- .../platform/qcom/iris/iris_platform_vpu2.c | 20 ++++++--- .../platform/qcom/iris/iris_platform_vpu3x.c | 41 +++++++++++++------ drivers/media/platform/qcom/iris/iris_probe.c | 3 +- 8 files changed, 59 insertions(+), 30 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index fbe136360aa1..ef7f6f931557 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -295,7 +295,7 @@ static void iris_fill_internal_buf_info(struct iris_inst *inst, { struct iris_buffers *buffers = &inst->buffers[buffer_type]; - buffers->size = inst->core->iris_platform_data->get_vpu_buffer_size(inst, buffer_type); + buffers->size = inst->core->iris_firmware_desc->get_vpu_buffer_size(inst, buffer_type); buffers->min_count = iris_vpu_buf_count(inst, buffer_type); } diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h index e0ca245c8c63..24da60448cf2 100644 --- a/drivers/media/platform/qcom/iris/iris_core.h +++ b/drivers/media/platform/qcom/iris/iris_core.h @@ -55,6 +55,7 @@ struct qcom_ubwc_cfg_data; * @controller_resets: table of controller reset clocks * @iris_platform_data: a structure for platform data * @iris_firmware_data: a pointer to the firmware (or HFI) specific data + * @iris_firmware_desc: a pointer to the firmware-specific descriptive data * @ubwc_cfg: UBWC configuration for the platform * @state: current state of core * @iface_q_table_daddr: device address for interface queue table memory @@ -99,6 +100,7 @@ struct iris_core { struct reset_control_bulk_data *controller_resets; const struct iris_platform_data *iris_platform_data; const struct iris_firmware_data *iris_firmware_data; + const struct iris_firmware_desc *iris_firmware_desc; const struct qcom_ubwc_cfg_data *ubwc_cfg; enum iris_core_state state; dma_addr_t iface_q_table_daddr; diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c index bc6c5c3e00c3..1a476146d758 100644 --- a/drivers/media/platform/qcom/iris/iris_firmware.c +++ b/drivers/media/platform/qcom/iris/iris_firmware.c @@ -72,7 +72,7 @@ int iris_fw_load(struct iris_core *core) ret = of_property_read_string_index(core->dev->of_node, "firmware-name", 0, &fwpath); if (ret) - fwpath = core->iris_platform_data->fwname; + fwpath = core->iris_firmware_desc->fwname; ret = iris_load_fw_to_memory(core, fwpath); if (ret) { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 3fb90a466a64..83373862655f 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -918,7 +918,7 @@ static int iris_hfi_gen1_set_bufsize(struct iris_inst *inst, u32 plane) if (iris_split_mode_enabled(inst)) { bufsz.type = HFI_BUFFER_OUTPUT; - bufsz.size = inst->core->iris_platform_data->get_vpu_buffer_size(inst, BUF_DPB); + bufsz.size = inst->core->iris_firmware_desc->get_vpu_buffer_size(inst, BUF_DPB); ret = hfi_gen1_set_property(inst, ptype, &bufsz, sizeof(bufsz)); if (ret) diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 6dfead673393..6a108173be35 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -250,14 +250,18 @@ struct iris_firmware_data { unsigned int enc_op_int_buf_tbl_size; }; -struct iris_platform_data { - /* - * XXX: remove firmware_data pointer and consider moving - * get_vpu_buffer_size pointer once we have platforms supporting both - * firmware kinds. - */ +struct iris_firmware_desc { const struct iris_firmware_data *firmware_data; u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type); + const char *fwname; +}; + +struct iris_platform_data { + /* + * XXX: replace with gen1 / gen2 pointers once we have platforms + * supporting both firmware kinds. + */ + const struct iris_firmware_desc *firmware_desc; const struct vpu_ops *vpu_ops; const struct icc_info *icc_tbl; @@ -276,7 +280,6 @@ struct iris_platform_data { const char * const *controller_rst_tbl; unsigned int controller_rst_tbl_size; u64 dma_mask; - const char *fwname; struct iris_fmt *inst_iris_fmts; u32 inst_iris_fmts_size; struct platform_inst_caps *inst_caps; diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c index 692fbc2aab56..41986af8313b 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c @@ -16,6 +16,18 @@ #include "iris_platform_sc7280.h" #include "iris_platform_sm8250.h" +static const struct iris_firmware_desc iris_vpu20_p1_gen1_desc = { + .firmware_data = &iris_hfi_gen1_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu20_p1.mbn", +}; + +static const struct iris_firmware_desc iris_vpu20_p4_gen1_desc = { + .firmware_data = &iris_hfi_gen1_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu20_p4.mbn", +}; + static struct iris_fmt iris_fmts_vpu2_dec[] = { [IRIS_FMT_H264] = { .pixfmt = V4L2_PIX_FMT_H264, @@ -62,8 +74,7 @@ static const struct tz_cp_config tz_cp_config_vpu2[] = { }; const struct iris_platform_data sc7280_data = { - .firmware_data = &iris_hfi_gen1_data, - .get_vpu_buffer_size = iris_vpu_buf_size, + .firmware_desc = &iris_vpu20_p1_gen1_desc, .vpu_ops = &iris_vpu2_ops, .icc_tbl = iris_icc_info_vpu2, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2), @@ -78,7 +89,6 @@ const struct iris_platform_data sc7280_data = { .opp_clk_tbl = sc7280_opp_clk_table, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu20_p1.mbn", .inst_iris_fmts = iris_fmts_vpu2_dec, .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec), .inst_caps = &platform_inst_cap_vpu2, @@ -93,8 +103,7 @@ const struct iris_platform_data sc7280_data = { }; const struct iris_platform_data sm8250_data = { - .firmware_data = &iris_hfi_gen1_data, - .get_vpu_buffer_size = iris_vpu_buf_size, + .firmware_desc = &iris_vpu20_p4_gen1_desc, .vpu_ops = &iris_vpu2_ops, .icc_tbl = iris_icc_info_vpu2, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2), @@ -111,7 +120,6 @@ const struct iris_platform_data sm8250_data = { .opp_clk_tbl = sm8250_opp_clk_table, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu20_p4.mbn", .inst_iris_fmts = iris_fmts_vpu2_dec, .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec), .inst_caps = &platform_inst_cap_vpu2, diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c index c2496aa0f851..c249ff827541 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c @@ -17,6 +17,30 @@ #include "iris_platform_sm8650.h" #include "iris_platform_sm8750.h" +static const struct iris_firmware_desc iris_vpu30_p4_s6_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu30_p4_s6.mbn", +}; + +static const struct iris_firmware_desc iris_vpu30_p4_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu30_p4.mbn", +}; + +static const struct iris_firmware_desc iris_vpu33_p4_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu33_buf_size, + .fwname = "qcom/vpu/vpu33_p4.mbn", +}; + +static const struct iris_firmware_desc iris_vpu35_p4_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu33_buf_size, + .fwname = "qcom/vpu/vpu35_p4.mbn", +}; + static struct iris_fmt iris_fmts_vpu3x_dec[] = { [IRIS_FMT_H264] = { .pixfmt = V4L2_PIX_FMT_H264, @@ -71,8 +95,7 @@ static const struct tz_cp_config tz_cp_config_vpu3[] = { * - inst_caps to platform_inst_cap_qcs8300 */ const struct iris_platform_data qcs8300_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu_buf_size, + .firmware_desc = &iris_vpu30_p4_s6_gen2_desc, .vpu_ops = &iris_vpu3_ops, .icc_tbl = iris_icc_info_vpu3x, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), @@ -89,7 +112,6 @@ const struct iris_platform_data qcs8300_data = { .opp_clk_tbl = iris_opp_clk_table_vpu3x, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu30_p4_s6.mbn", .inst_iris_fmts = iris_fmts_vpu3x_dec, .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), .inst_caps = &platform_inst_cap_qcs8300, @@ -102,8 +124,7 @@ const struct iris_platform_data qcs8300_data = { }; const struct iris_platform_data sm8550_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu_buf_size, + .firmware_desc = &iris_vpu30_p4_gen2_desc, .vpu_ops = &iris_vpu3_ops, .icc_tbl = iris_icc_info_vpu3x, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), @@ -120,7 +141,6 @@ const struct iris_platform_data sm8550_data = { .opp_clk_tbl = iris_opp_clk_table_vpu3x, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu30_p4.mbn", .inst_iris_fmts = iris_fmts_vpu3x_dec, .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), .inst_caps = &platform_inst_cap_sm8550, @@ -137,11 +157,9 @@ const struct iris_platform_data sm8550_data = { * - vpu_ops to iris_vpu33_ops * - clk_rst_tbl to sm8650_clk_reset_table * - controller_rst_tbl to sm8650_controller_reset_table - * - fwname to "qcom/vpu/vpu33_p4.mbn" */ const struct iris_platform_data sm8650_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu33_buf_size, + .firmware_desc = &iris_vpu33_p4_gen2_desc, .vpu_ops = &iris_vpu33_ops, .icc_tbl = iris_icc_info_vpu3x, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), @@ -160,7 +178,6 @@ const struct iris_platform_data sm8650_data = { .opp_clk_tbl = iris_opp_clk_table_vpu3x, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu33_p4.mbn", .inst_iris_fmts = iris_fmts_vpu3x_dec, .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), .inst_caps = &platform_inst_cap_sm8550, @@ -173,8 +190,7 @@ const struct iris_platform_data sm8650_data = { }; const struct iris_platform_data sm8750_data = { - .firmware_data = &iris_hfi_gen2_data, - .get_vpu_buffer_size = iris_vpu33_buf_size, + .firmware_desc = &iris_vpu35_p4_gen2_desc, .vpu_ops = &iris_vpu35_ops, .icc_tbl = iris_icc_info_vpu3x, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), @@ -191,7 +207,6 @@ const struct iris_platform_data sm8750_data = { .opp_clk_tbl = iris_opp_clk_table_vpu3x, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu35_p4.mbn", .inst_iris_fmts = iris_fmts_vpu3x_dec, .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), .inst_caps = &platform_inst_cap_sm8550, diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index dd87504c2e67..d36f0c0e785b 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -251,7 +251,8 @@ static int iris_probe(struct platform_device *pdev) return core->irq; core->iris_platform_data = of_device_get_match_data(core->dev); - core->iris_firmware_data = core->iris_platform_data->firmware_data; + core->iris_firmware_desc = core->iris_platform_data->firmware_desc; + core->iris_firmware_data = core->iris_firmware_desc->firmware_data; core->ubwc_cfg = qcom_ubwc_config_get_data(); if (IS_ERR(core->ubwc_cfg)) From ba07fd2f5742e8b0d965003b34c7a44eb4f67e53 Mon Sep 17 00:00:00 2001 From: Ben Hoff Date: Sun, 10 May 2026 19:50:36 -0400 Subject: [PATCH 146/394] media: pci: add AVMatrix HWS capture driver Add an in-tree AVMatrix HWS PCIe capture driver. The driver supports up to four HDMI inputs and exposes the video capture path through V4L2 with vb2-dma-contig streaming, DV timings, and per-input controls. Audio support is intentionally omitted from this submission. This patch also adds the MAINTAINERS entry for the new driver. This driver is derived from a GPL out-of-tree driver. Changes since v6: - v6 accidently contained legacy history, resubmitting with latest - remove an unused mode-change label reported by W=1 Changes since v5: - keep queue_setup() and alloc_sizeimage on the logical sizeimage value - drop the dead queue_setup() fallback that rebuilt pix.sizeimage on the fly - clarify that hws_calc_sizeimage() models the packed-YUYV-only path - add Assisted-by attribution for Codex Changes since v4: - replace plain 64-bit elapsed-time divisions in debug logging with div_u64() so i386 module builds do not emit __udivdi3 references Changes since v3: - fold the MAINTAINERS update into this patch so per-patch CI sees the new file pattern - wrap the validation text for checkpatch Changes since v2: - keep scratch DMA allocation on a single probe-owned path - avoid double-freeing V4L2 control handlers on register unwind - drop the extra per-node resolution sysfs ABI - turn live geometry changes into explicit SOURCE_CHANGE renegotiation - report live DV timings and reject attempts to retime a live source - stop advertising RESOLUTION source changes for fps-only updates - keep live fps state across harmless S_FMT restarts - stop exposing an unvalidated DV RX power-present signal - clean the imported sources for checkpatch and W=1 builds Validation: - build-tested with W=1 against a local kernel build tree - compiled the driver with ARCH=i386 allmodconfig and verified the resulting hws_pci.o, hws_video.o, and hws.o do not reference __udivdi3 - v4l2-compliance 1.33.0-5459 from v4l-utils commit 4a0d2c3b4f523406cb9a6f4c541ef14f72f19f3d on /dev/video2: 48 tests succeeded, 0 failed, 1 warning DV_RX_POWER_PRESENT is intentionally left unsupported in this revision because current hardware evidence does not expose a validated receiver-side power-detect signal distinct from active video presence. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202604020522.z22eZuW8-lkp@intel.com/ Assisted-by: Codex:gpt-5.5 Signed-off-by: Ben Hoff Signed-off-by: Hans Verkuil --- MAINTAINERS | 6 + drivers/media/pci/Kconfig | 1 + drivers/media/pci/Makefile | 1 + drivers/media/pci/hws/Kconfig | 11 + drivers/media/pci/hws/Makefile | 4 + drivers/media/pci/hws/hws.h | 173 +++ drivers/media/pci/hws/hws_irq.c | 269 +++++ drivers/media/pci/hws/hws_irq.h | 10 + drivers/media/pci/hws/hws_pci.c | 865 ++++++++++++++ drivers/media/pci/hws/hws_reg.h | 136 +++ drivers/media/pci/hws/hws_v4l2_ioctl.c | 919 +++++++++++++++ drivers/media/pci/hws/hws_v4l2_ioctl.h | 36 + drivers/media/pci/hws/hws_video.c | 1490 ++++++++++++++++++++++++ drivers/media/pci/hws/hws_video.h | 29 + 14 files changed, 3950 insertions(+) create mode 100644 drivers/media/pci/hws/Kconfig create mode 100644 drivers/media/pci/hws/Makefile create mode 100644 drivers/media/pci/hws/hws.h create mode 100644 drivers/media/pci/hws/hws_irq.c create mode 100644 drivers/media/pci/hws/hws_irq.h create mode 100644 drivers/media/pci/hws/hws_pci.c create mode 100644 drivers/media/pci/hws/hws_reg.h create mode 100644 drivers/media/pci/hws/hws_v4l2_ioctl.c create mode 100644 drivers/media/pci/hws/hws_v4l2_ioctl.h create mode 100644 drivers/media/pci/hws/hws_video.c create mode 100644 drivers/media/pci/hws/hws_video.h diff --git a/MAINTAINERS b/MAINTAINERS index 5c66788998e8..4e054ce1cfaa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4317,6 +4317,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml F: drivers/iio/adc/hx711.c +AVMATRIX HWS CAPTURE DRIVER +M: Ben Hoff +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/pci/hws/ + AWINIC AW99706 WLED BACKLIGHT DRIVER M: Junjie Cao S: Maintained diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index eebb16c58f3d..bfdb200f85a3 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -13,6 +13,7 @@ if MEDIA_PCI_SUPPORT if MEDIA_CAMERA_SUPPORT comment "Media capture support" +source "drivers/media/pci/hws/Kconfig" source "drivers/media/pci/mgb4/Kconfig" source "drivers/media/pci/solo6x10/Kconfig" source "drivers/media/pci/tw5864/Kconfig" diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index 02763ad88511..c4508b6723a9 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_DT3155) += dt3155/ +obj-$(CONFIG_VIDEO_HWS) += hws/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_MGB4) += mgb4/ obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ diff --git a/drivers/media/pci/hws/Kconfig b/drivers/media/pci/hws/Kconfig new file mode 100644 index 000000000000..ab0bbf49ca71 --- /dev/null +++ b/drivers/media/pci/hws/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_HWS + tristate "AVMatrix HWS PCIe capture devices" + depends on PCI && VIDEO_DEV + select VIDEOBUF2_DMA_CONTIG + help + Enable support for AVMatrix HWS series multi-channel PCIe capture + devices that provide HDMI video capture. + + To compile this driver as a module, choose M here: the module + will be named hws. diff --git a/drivers/media/pci/hws/Makefile b/drivers/media/pci/hws/Makefile new file mode 100644 index 000000000000..f9c7dc4f2d8d --- /dev/null +++ b/drivers/media/pci/hws/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +hws-objs := hws_pci.o hws_irq.o hws_video.o hws_v4l2_ioctl.o + +obj-$(CONFIG_VIDEO_HWS) += hws.o diff --git a/drivers/media/pci/hws/hws.h b/drivers/media/pci/hws/hws.h new file mode 100644 index 000000000000..8fbe1fe27844 --- /dev/null +++ b/drivers/media/pci/hws/hws.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef HWS_PCIE_H +#define HWS_PCIE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "hws_reg.h" + +struct hwsmem_param { + u32 index; + u32 type; + u32 status; +}; + +struct hws_pix_state { + u32 width; + u32 height; + u32 fourcc; /* V4L2_PIX_FMT_* (YUYV only here) */ + u32 bytesperline; /* stride */ + u32 sizeimage; /* full frame */ + enum v4l2_field field; /* V4L2_FIELD_NONE or INTERLACED */ + enum v4l2_colorspace colorspace; /* e.g., REC709 */ + enum v4l2_ycbcr_encoding ycbcr_enc; /* V4L2_YCBCR_ENC_DEFAULT */ + enum v4l2_quantization quantization; /* V4L2_QUANTIZATION_LIM_RANGE */ + enum v4l2_xfer_func xfer_func; /* V4L2_XFER_FUNC_DEFAULT */ + bool interlaced; /* cached hardware state */ + u32 half_size; /* hardware half-frame size */ +}; + +#define UNSET (-1U) + +struct hws_pcie_dev; +struct hws_adapter; +struct hws_video; + +struct hwsvideo_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; + int slot; +}; + +struct hws_video { + /* Linkage */ + struct hws_pcie_dev *parent; + struct video_device *video_device; + + struct vb2_queue buffer_queue; + struct list_head capture_queue; + struct hwsvideo_buffer *active; + struct hwsvideo_buffer *next_prepared; + + /* Locking */ + struct mutex state_lock; + spinlock_t irq_lock; /* Protects capture_queue and active buffers. */ + + /* Indices */ + int channel_index; + + /* Color controls */ + int current_brightness; + int current_contrast; + int current_saturation; + int current_hue; + + /* V4L2 controls */ + struct v4l2_ctrl_handler control_handler; + struct v4l2_ctrl *ctrl_brightness; + struct v4l2_ctrl *ctrl_contrast; + struct v4l2_ctrl *ctrl_saturation; + struct v4l2_ctrl *ctrl_hue; + + /* Capture queue status */ + struct hws_pix_state pix; + struct v4l2_dv_timings cur_dv_timings; /* last configured/notified DV timings */ + u32 current_fps; /* Hz, updated by mode changes, not by read-only queries */ + + /* Per-channel capture state */ + bool cap_active; + bool stop_requested; + u8 last_buf_half_toggle; + bool half_seen; + atomic_t sequence_number; + u32 queued_count; + + /* Timeout and error handling */ + u32 timeout_count; + u32 error_count; + + bool window_valid; + u32 last_dma_hi; + u32 last_dma_page; + u32 last_pci_addr; + u32 last_half16; + + /* Misc counters */ + int signal_loss_cnt; +}; + +static inline void hws_set_current_dv_timings(struct hws_video *vid, + u32 width, u32 height, + bool interlaced) +{ + if (!vid) + return; + + vid->cur_dv_timings = (struct v4l2_dv_timings) { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = width, + .height = height, + .interlaced = interlaced, + }, + }; +} + +struct hws_scratch_dma { + void *cpu; + dma_addr_t dma; + size_t size; +}; + +struct hws_pcie_dev { + /* Core objects */ + struct pci_dev *pdev; + struct hws_video video[MAX_VID_CHANNELS]; + + /* BAR and workqueues */ + void __iomem *bar0_base; + + /* Device identity and capabilities */ + u16 vendor_id; + u16 device_id; + u16 device_ver; + u16 hw_ver; + u32 sub_ver; + u32 port_id; + /* Tri-state support flag used by set_video_format_size(). */ + u32 support_yv12; + u32 max_hw_video_buf_sz; + u8 max_channels; + u8 cur_max_video_ch; + bool start_run; + + bool buf_allocated; + + /* V4L2 framework objects */ + struct v4l2_device v4l2_device; + + /* Kernel thread */ + struct task_struct *main_task; + struct hws_scratch_dma scratch_vid[MAX_VID_CHANNELS]; + + bool suspended; + int irq; + + /* Error flags */ + int pci_lost; +}; + +#endif diff --git a/drivers/media/pci/hws/hws_irq.c b/drivers/media/pci/hws/hws_irq.c new file mode 100644 index 000000000000..eebb4b8a5cd5 --- /dev/null +++ b/drivers/media/pci/hws/hws_irq.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hws_irq.h" +#include "hws_reg.h" +#include "hws_video.h" +#include "hws.h" + +#define MAX_INT_LOOPS 100 + +static bool hws_toggle_debug; +module_param_named(toggle_debug, hws_toggle_debug, bool, 0644); +MODULE_PARM_DESC(toggle_debug, + "Read toggle registers in IRQ handler for debug logging"); + +static int hws_arm_next(struct hws_pcie_dev *hws, u32 ch) +{ + struct hws_video *v = &hws->video[ch]; + unsigned long flags; + struct hwsvideo_buffer *buf; + + dev_dbg(&hws->pdev->dev, + "arm_next(ch=%u): stop=%d cap=%d queued=%d\n", + ch, READ_ONCE(v->stop_requested), READ_ONCE(v->cap_active), + !list_empty(&v->capture_queue)); + + if (READ_ONCE(hws->suspended)) { + dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): suspended\n", ch); + return -EBUSY; + } + + if (READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active)) { + dev_dbg(&hws->pdev->dev, + "arm_next(ch=%u): stop=%d cap=%d -> cancel\n", ch, + v->stop_requested, v->cap_active); + return -ECANCELED; + } + + spin_lock_irqsave(&v->irq_lock, flags); + if (list_empty(&v->capture_queue)) { + spin_unlock_irqrestore(&v->irq_lock, flags); + dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): queue empty\n", ch); + return -EAGAIN; + } + + buf = list_first_entry(&v->capture_queue, struct hwsvideo_buffer, list); + list_del_init(&buf->list); /* keep buffer safe for later cleanup */ + if (v->queued_count) + v->queued_count--; + v->active = buf; + spin_unlock_irqrestore(&v->irq_lock, flags); + dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): picked buffer %p\n", ch, + buf); + + /* Publish descriptor(s) before doorbell/MMIO kicks. */ + wmb(); + + /* Avoid MMIO during suspend */ + if (READ_ONCE(hws->suspended)) { + unsigned long f; + + dev_dbg(&hws->pdev->dev, + "arm_next(ch=%u): suspended after pick\n", ch); + spin_lock_irqsave(&v->irq_lock, f); + if (v->active) { + list_add(&buf->list, &v->capture_queue); + v->queued_count++; + v->active = NULL; + } + spin_unlock_irqrestore(&v->irq_lock, f); + return -EBUSY; + } + + /* Also program the DMA address register directly */ + { + dma_addr_t dma_addr = + vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + hws_program_dma_for_addr(hws, ch, dma_addr); + iowrite32(lower_32_bits(dma_addr), + hws->bar0_base + HWS_REG_DMA_ADDR(ch)); + } + + dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): programmed buffer %p\n", ch, + buf); + spin_lock_irqsave(&v->irq_lock, flags); + hws_prime_next_locked(v); + spin_unlock_irqrestore(&v->irq_lock, flags); + return 0; +} + +static void hws_video_handle_vdone(struct hws_video *v) +{ + struct hws_pcie_dev *hws = v->parent; + unsigned int ch = v->channel_index; + struct hwsvideo_buffer *done; + unsigned long flags; + bool promoted = false; + + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): stop=%d cap=%d active=%p\n", + ch, READ_ONCE(v->stop_requested), READ_ONCE(v->cap_active), + v->active); + + int ret; + + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): entry stop=%d cap=%d\n", ch, + v->stop_requested, v->cap_active); + if (READ_ONCE(hws->suspended)) + return; + + if (READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active)) + return; + + spin_lock_irqsave(&v->irq_lock, flags); + done = v->active; + if (done && v->next_prepared) { + v->active = v->next_prepared; + v->next_prepared = NULL; + promoted = true; + } + spin_unlock_irqrestore(&v->irq_lock, flags); + + /* 1) Complete the buffer the HW just finished (if any) */ + if (done) { + struct vb2_v4l2_buffer *vb2v = &done->vb; + size_t expected = v->pix.sizeimage; + size_t plane_size = vb2_plane_size(&vb2v->vb2_buf, 0); + + if (expected > plane_size) { + dev_warn_ratelimited(&hws->pdev->dev, + "bh_video(ch=%u): sizeimage %zu > plane %zu, dropping seq=%u\n", + ch, expected, plane_size, + (u32)atomic_read(&v->sequence_number) + 1); + vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_ERROR); + goto arm_next; + } + vb2_set_plane_payload(&vb2v->vb2_buf, 0, expected); + + dma_rmb(); /* device writes visible before userspace sees it */ + + vb2v->sequence = (u32)atomic_inc_return(&v->sequence_number); + vb2v->vb2_buf.timestamp = ktime_get_ns(); + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): DONE buf=%p seq=%u half_seen=%d toggle=%u\n", + ch, done, vb2v->sequence, v->half_seen, + v->last_buf_half_toggle); + + if (!promoted) + v->active = NULL; /* channel no longer owns this buffer */ + vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_DONE); + } + + if (READ_ONCE(hws->suspended)) + return; + + if (promoted) { + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): promoted pre-armed buffer active=%p\n", + ch, v->active); + spin_lock_irqsave(&v->irq_lock, flags); + hws_prime_next_locked(v); + spin_unlock_irqrestore(&v->irq_lock, flags); + return; + } + +arm_next: + /* 2) Immediately arm the next queued buffer (if present) */ + ret = hws_arm_next(hws, ch); + if (ret == -EAGAIN) { + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): no queued buffer to arm\n", ch); + return; + } + dev_dbg(&hws->pdev->dev, + "bh_video(ch=%u): armed next buffer, active=%p\n", ch, + v->active); + /* On success the engine now points at v->active's DMA address */ +} + +irqreturn_t hws_irq_handler(int irq, void *info) +{ + struct hws_pcie_dev *pdx = info; + u32 int_state; + + dev_dbg(&pdx->pdev->dev, "irq: entry\n"); + if (pdx->bar0_base) { + dev_dbg(&pdx->pdev->dev, + "irq: INT_EN=0x%08x INT_STATUS=0x%08x\n", + readl(pdx->bar0_base + INT_EN_REG_BASE), + readl(pdx->bar0_base + HWS_REG_INT_STATUS)); + } + + /* Fast path: if suspended, quietly ack and exit */ + if (READ_ONCE(pdx->suspended)) { + int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + if (int_state) { + writel(int_state, pdx->bar0_base + HWS_REG_INT_STATUS); + (void)readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + } + return int_state ? IRQ_HANDLED : IRQ_NONE; + } + int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + if (!int_state || int_state == 0xFFFFFFFF) { + dev_dbg(&pdx->pdev->dev, + "irq: spurious or device-gone int_state=0x%08x\n", + int_state); + return IRQ_NONE; + } + dev_dbg(&pdx->pdev->dev, "irq: entry INT_STATUS=0x%08x\n", int_state); + + /* Loop until all pending bits are serviced (max 100 iterations) */ + for (u32 cnt = 0; int_state && cnt < MAX_INT_LOOPS; ++cnt) { + for (unsigned int ch = 0; ch < pdx->cur_max_video_ch; ++ch) { + u32 vbit = HWS_INT_VDONE_BIT(ch); + + if (!(int_state & vbit)) + continue; + + if (READ_ONCE(pdx->video[ch].cap_active) && + !READ_ONCE(pdx->video[ch].stop_requested)) { + if (hws_toggle_debug) { + u32 toggle = + readl_relaxed(pdx->bar0_base + + HWS_REG_VBUF_TOGGLE(ch)) & 0x01; + WRITE_ONCE(pdx->video[ch].last_buf_half_toggle, + toggle); + } + dma_rmb(); + WRITE_ONCE(pdx->video[ch].half_seen, true); + dev_dbg(&pdx->pdev->dev, + "irq: VDONE ch=%u toggle=%u handling inline (cap=%d)\n", + ch, + READ_ONCE(pdx->video[ch].last_buf_half_toggle), + READ_ONCE(pdx->video[ch].cap_active)); + hws_video_handle_vdone(&pdx->video[ch]); + } else { + dev_dbg(&pdx->pdev->dev, + "irq: VDONE ch=%u ignored (cap=%d stop=%d)\n", + ch, + READ_ONCE(pdx->video[ch].cap_active), + READ_ONCE(pdx->video[ch].stop_requested)); + } + + writel(vbit, pdx->bar0_base + HWS_REG_INT_STATUS); + (void)readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + } + + /* Re-read in case new interrupt bits popped while processing */ + int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); + dev_dbg(&pdx->pdev->dev, + "irq: loop cnt=%u new INT_STATUS=0x%08x\n", cnt, + int_state); + if (cnt + 1 == MAX_INT_LOOPS) + dev_warn_ratelimited(&pdx->pdev->dev, + "IRQ storm? status=0x%08x\n", + int_state); + } + + return IRQ_HANDLED; +} diff --git a/drivers/media/pci/hws/hws_irq.h b/drivers/media/pci/hws/hws_irq.h new file mode 100644 index 000000000000..a42867aa0c46 --- /dev/null +++ b/drivers/media/pci/hws/hws_irq.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef HWS_INTERRUPT_H +#define HWS_INTERRUPT_H + +#include +#include "hws.h" + +irqreturn_t hws_irq_handler(int irq, void *info); + +#endif /* HWS_INTERRUPT_H */ diff --git a/drivers/media/pci/hws/hws_pci.c b/drivers/media/pci/hws/hws_pci.c new file mode 100644 index 000000000000..30bb7d34465b --- /dev/null +++ b/drivers/media/pci/hws/hws_pci.c @@ -0,0 +1,865 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hws.h" +#include "hws_reg.h" +#include "hws_video.h" +#include "hws_irq.h" +#include "hws_v4l2_ioctl.h" + +#define DRV_NAME "hws" +#define HWS_BUSY_POLL_DELAY_US 10 +#define HWS_BUSY_POLL_TIMEOUT_US 1000000 + +static unsigned long long hws_elapsed_us(u64 start_ns) +{ + return div_u64(ktime_get_mono_fast_ns() - start_ns, 1000); +} + +/* register layout inside HWS_REG_DEVICE_INFO */ +#define DEVINFO_VER GENMASK(7, 0) +#define DEVINFO_SUBVER GENMASK(15, 8) +#define DEVINFO_YV12 GENMASK(31, 28) +#define DEVINFO_HWKEY GENMASK(27, 24) +#define DEVINFO_PORTID GENMASK(25, 24) /* low 2 bits of HW-key */ + +#define MAKE_ENTRY(__vend, __chip, __subven, __subdev, __configptr) \ + { .vendor = (__vend), \ + .device = (__chip), \ + .subvendor = (__subven), \ + .subdevice = (__subdev), \ + .driver_data = (unsigned long)(__configptr) } + +/* + * PCI IDs for HWS family cards. + * + * The subsystem IDs are fixed at 0x8888:0x0007 for this family. Some boards + * enumerate with vendor ID 0x8888 or 0x1f33. Exact SKU names are not fully + * pinned down yet; update these comments when vendor documentation or INF + * strings are available. + */ +static const struct pci_device_id hws_pci_table[] = { + /* HWS family, SKU unknown. */ + MAKE_ENTRY(0x8888, 0x9534, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x1F33, 0x8534, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x1F33, 0x8554, 0x8888, 0x0007, NULL), + + /* HWS 2x2 HDMI family. */ + MAKE_ENTRY(0x8888, 0x8524, 0x8888, 0x0007, NULL), + /* HWS 2x2 SDI family. */ + MAKE_ENTRY(0x1F33, 0x6524, 0x8888, 0x0007, NULL), + + /* HWS X4 HDMI family. */ + MAKE_ENTRY(0x8888, 0x8504, 0x8888, 0x0007, NULL), + /* HWS X4 SDI family. */ + MAKE_ENTRY(0x8888, 0x6504, 0x8888, 0x0007, NULL), + + /* HWS family, SKU unknown. */ + MAKE_ENTRY(0x8888, 0x8532, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x8888, 0x8512, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x8888, 0x8501, 0x8888, 0x0007, NULL), + MAKE_ENTRY(0x1F33, 0x6502, 0x8888, 0x0007, NULL), + + /* HWS X4 HDMI family (alternate vendor ID). */ + MAKE_ENTRY(0x1F33, 0x8504, 0x8888, 0x0007, NULL), + /* HWS 2x2 HDMI family (alternate vendor ID). */ + MAKE_ENTRY(0x1F33, 0x8524, 0x8888, 0x0007, NULL), + + {} +}; + +static void enable_pcie_relaxed_ordering(struct pci_dev *dev) +{ + pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN); +} + +static void hws_configure_hardware_capabilities(struct hws_pcie_dev *hdev) +{ + u16 id = hdev->device_id; + + /* select per-chip channel counts */ + switch (id) { + case 0x9534: + case 0x6524: + case 0x8524: + case 0x8504: + case 0x6504: + hdev->cur_max_video_ch = 4; + break; + case 0x8532: + hdev->cur_max_video_ch = 2; + break; + case 0x8512: + case 0x6502: + hdev->cur_max_video_ch = 2; + break; + case 0x8501: + hdev->cur_max_video_ch = 1; + break; + default: + hdev->cur_max_video_ch = 4; + break; + } + + /* universal buffer capacity */ + hdev->max_hw_video_buf_sz = MAX_MM_VIDEO_SIZE; + + /* decide hardware-version and program DMA max size if needed */ + if (hdev->device_ver > 121) { + if (id == 0x8501 && hdev->device_ver == 122) { + hdev->hw_ver = 0; + } else { + hdev->hw_ver = 1; + u32 dma_max = (u32)(MAX_VIDEO_SCALER_SIZE / 16); + + writel(dma_max, hdev->bar0_base + HWS_REG_DMA_MAX_SIZE); + /* readback to flush posted MMIO write */ + (void)readl(hdev->bar0_base + HWS_REG_DMA_MAX_SIZE); + } + } else { + hdev->hw_ver = 0; + } +} + +static void hws_stop_device(struct hws_pcie_dev *hws); + +static void hws_log_lifecycle_snapshot(struct hws_pcie_dev *hws, + const char *action, + const char *phase) +{ + struct device *dev; + u32 int_en, int_status, vcap, sys_status, dec_mode; + + if (!hws || !hws->pdev) + return; + + dev = &hws->pdev->dev; + if (!hws->bar0_base) { + dev_dbg(dev, + "lifecycle:%s:%s bar0-unmapped suspended=%d start_run=%d pci_lost=%d irq=%d\n", + action, phase, READ_ONCE(hws->suspended), hws->start_run, + hws->pci_lost, hws->irq); + return; + } + + int_en = readl(hws->bar0_base + INT_EN_REG_BASE); + int_status = readl(hws->bar0_base + HWS_REG_INT_STATUS); + vcap = readl(hws->bar0_base + HWS_REG_VCAP_ENABLE); + sys_status = readl(hws->bar0_base + HWS_REG_SYS_STATUS); + dec_mode = readl(hws->bar0_base + HWS_REG_DEC_MODE); + + dev_dbg(dev, + "lifecycle:%s:%s suspended=%d start_run=%d pci_lost=%d irq=%d INT_EN=0x%08x INT_STATUS=0x%08x VCAP=0x%08x SYS=0x%08x DEC=0x%08x\n", + action, phase, READ_ONCE(hws->suspended), hws->start_run, + hws->pci_lost, hws->irq, int_en, int_status, vcap, + sys_status, dec_mode); +} + +static int read_chip_id(struct hws_pcie_dev *hdev) +{ + u32 reg; + /* mirror PCI IDs for later switches */ + hdev->device_id = hdev->pdev->device; + hdev->vendor_id = hdev->pdev->vendor; + + reg = readl(hdev->bar0_base + HWS_REG_DEVICE_INFO); + + hdev->device_ver = FIELD_GET(DEVINFO_VER, reg); + hdev->sub_ver = FIELD_GET(DEVINFO_SUBVER, reg); + hdev->support_yv12 = FIELD_GET(DEVINFO_YV12, reg); + hdev->port_id = FIELD_GET(DEVINFO_PORTID, reg); + + hdev->max_hw_video_buf_sz = MAX_MM_VIDEO_SIZE; + hdev->max_channels = 4; + hdev->buf_allocated = false; + hdev->main_task = NULL; + hdev->start_run = false; + hdev->pci_lost = 0; + + writel(0x00, hdev->bar0_base + HWS_REG_DEC_MODE); + writel(0x10, hdev->bar0_base + HWS_REG_DEC_MODE); + + hws_configure_hardware_capabilities(hdev); + + dev_info(&hdev->pdev->dev, + "chip detected: ver=%u subver=%u port=%u yv12=%u\n", + hdev->device_ver, hdev->sub_ver, hdev->port_id, + hdev->support_yv12); + + return 0; +} + +static int main_ks_thread_handle(void *data) +{ + struct hws_pcie_dev *pdx = data; + + set_freezable(); + + while (!kthread_should_stop()) { + /* If we're suspending, don't touch hardware; just sleep/freeze. */ + if (READ_ONCE(pdx->suspended)) { + try_to_freeze(); + schedule_timeout_interruptible(msecs_to_jiffies(1000)); + continue; + } + + /* avoid MMIO when suspended (guarded above) */ + check_video_format(pdx); + + try_to_freeze(); /* cooperate with freezer each loop */ + + /* Sleep 1s or until signaled to wake/stop */ + schedule_timeout_interruptible(msecs_to_jiffies(1000)); + } + + dev_dbg(&pdx->pdev->dev, "%s: exiting\n", __func__); + return 0; +} + +static void hws_stop_kthread_action(void *data) +{ + struct hws_pcie_dev *hws = data; + struct task_struct *t; + u64 start_ns; + + if (!hws) + return; + + t = READ_ONCE(hws->main_task); + if (!IS_ERR_OR_NULL(t)) { + start_ns = ktime_get_mono_fast_ns(); + dev_dbg(&hws->pdev->dev, + "lifecycle:kthread-stop:begin task=%s[%d]\n", + t->comm, t->pid); + WRITE_ONCE(hws->main_task, NULL); + kthread_stop(t); + dev_dbg(&hws->pdev->dev, + "lifecycle:kthread-stop:done (%lluus)\n", + hws_elapsed_us(start_ns)); + } +} + +static int hws_alloc_seed_buffers(struct hws_pcie_dev *hws) +{ + int ch; + /* 64 KiB is plenty for a safe dummy; hardware needs 64-byte alignment. */ + const size_t need = ALIGN(64 * 1024, 64); + + for (ch = 0; ch < hws->cur_max_video_ch; ch++) { +#if defined(CONFIG_HAS_DMA) /* normal on PCIe platforms */ + void *cpu = dma_alloc_coherent(&hws->pdev->dev, need, + &hws->scratch_vid[ch].dma, + GFP_KERNEL); +#else + void *cpu = NULL; +#endif + if (!cpu) { + dev_warn(&hws->pdev->dev, + "scratch: dma_alloc_coherent failed ch=%d\n", ch); + /* not fatal: free earlier ones and continue without seeding */ + while (--ch >= 0) { + if (hws->scratch_vid[ch].cpu) + dma_free_coherent(&hws->pdev->dev, + hws->scratch_vid[ch].size, + hws->scratch_vid[ch].cpu, + hws->scratch_vid[ch].dma); + hws->scratch_vid[ch].cpu = NULL; + hws->scratch_vid[ch].size = 0; + } + return -ENOMEM; + } + hws->scratch_vid[ch].cpu = cpu; + hws->scratch_vid[ch].size = need; + } + return 0; +} + +static void hws_free_seed_buffers(struct hws_pcie_dev *hws) +{ + int ch; + + for (ch = 0; ch < hws->cur_max_video_ch; ch++) { + if (hws->scratch_vid[ch].cpu) { + dma_free_coherent(&hws->pdev->dev, + hws->scratch_vid[ch].size, + hws->scratch_vid[ch].cpu, + hws->scratch_vid[ch].dma); + hws->scratch_vid[ch].cpu = NULL; + hws->scratch_vid[ch].size = 0; + } + } +} + +static void hws_seed_channel(struct hws_pcie_dev *hws, int ch) +{ + dma_addr_t paddr = hws->scratch_vid[ch].dma; + u32 lo = lower_32_bits(paddr); + u32 hi = upper_32_bits(paddr); + u32 pci_addr = lo & PCI_E_BAR_ADD_LOWMASK; + + lo &= PCI_E_BAR_ADD_MASK; + + /* Program 64-bit BAR remap entry for this channel (table @ 0x208 + ch * 8) */ + writel_relaxed(hi, hws->bar0_base + + PCI_ADDR_TABLE_BASE + 0x208 + ch * 8); + writel_relaxed(lo, hws->bar0_base + + PCI_ADDR_TABLE_BASE + 0x208 + ch * 8 + + PCIE_BARADDROFSIZE); + + /* Program capture engine per-channel base/half */ + writel_relaxed((ch + 1) * PCIEBAR_AXI_BASE + pci_addr, + hws->bar0_base + CVBS_IN_BUF_BASE + + ch * PCIE_BARADDROFSIZE); + + /* Half size: use either the current format's half or half of scratch. */ + { + u32 half = hws->video[ch].pix.half_size ? + hws->video[ch].pix.half_size : + (u32)(hws->scratch_vid[ch].size / 2); + + writel_relaxed(half / 16, + hws->bar0_base + CVBS_IN_BUF_BASE2 + + ch * PCIE_BARADDROFSIZE); + } + + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); /* flush posted writes */ +} + +static void hws_seed_all_channels(struct hws_pcie_dev *hws) +{ + int ch; + + for (ch = 0; ch < hws->cur_max_video_ch; ch++) { + if (hws->scratch_vid[ch].cpu) + hws_seed_channel(hws, ch); + } +} + +static void hws_irq_mask_gate(struct hws_pcie_dev *hws) +{ + writel(0x00000000, hws->bar0_base + INT_EN_REG_BASE); + (void)readl(hws->bar0_base + INT_EN_REG_BASE); +} + +static void hws_irq_unmask_gate(struct hws_pcie_dev *hws) +{ + writel(HWS_INT_EN_MASK, hws->bar0_base + INT_EN_REG_BASE); + (void)readl(hws->bar0_base + INT_EN_REG_BASE); +} + +static void hws_irq_clear_pending(struct hws_pcie_dev *hws) +{ + u32 st = readl(hws->bar0_base + HWS_REG_INT_STATUS); + + if (st) { + writel(st, hws->bar0_base + HWS_REG_INT_STATUS); /* W1C */ + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); + } +} + +static void hws_block_hotpaths(struct hws_pcie_dev *hws) +{ + WRITE_ONCE(hws->suspended, true); + if (hws->irq >= 0) + disable_irq(hws->irq); + + if (!hws->bar0_base) + return; + + hws_irq_mask_gate(hws); + hws_irq_clear_pending(hws); +} + +static int hws_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) +{ + struct hws_pcie_dev *hws; + int i, ret, irq; + unsigned long irqf = 0; + bool v4l2_registered = false; + + /* devres-backed device object */ + hws = devm_kzalloc(&pdev->dev, sizeof(*hws), GFP_KERNEL); + if (!hws) + return -ENOMEM; + + hws->pdev = pdev; + hws->irq = -1; + hws->suspended = false; + pci_set_drvdata(pdev, hws); + + /* 1) Enable device + bus mastering (managed) */ + ret = pcim_enable_device(pdev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "pcim_enable_device\n"); + pci_set_master(pdev); + + /* 2) Map BAR0 (managed) */ + ret = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); + if (ret) + return dev_err_probe(&pdev->dev, ret, "pcim_iomap_regions BAR0\n"); + hws->bar0_base = pcim_iomap_table(pdev)[0]; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + dev_warn(&pdev->dev, + "64-bit DMA mask unavailable, falling back to 32-bit (%d)\n", + ret); + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "No suitable DMA configuration\n"); + } else { + dev_dbg(&pdev->dev, "Using 64-bit DMA mask\n"); + } + + /* 3) Apply optional PCIe tuning. */ + enable_pcie_relaxed_ordering(pdev); +#ifdef CONFIG_ARCH_TI816X + pcie_set_readrq(pdev, 128); +#endif + + /* 4) Identify chip & capabilities */ + read_chip_id(hws); + dev_info(&pdev->dev, "Device VID=0x%04x DID=0x%04x\n", + pdev->vendor, pdev->device); + hws_init_video_sys(hws, false); + + /* 5) Init channels (video state, locks, vb2, ctrls) */ + for (i = 0; i < hws->max_channels; i++) { + ret = hws_video_init_channel(hws, i); + if (ret) { + dev_err(&pdev->dev, "video channel init failed (ch=%d)\n", i); + goto err_unwind_channels; + } + } + + /* 6) Allocate scratch DMA and seed BAR table + channel base/half (legacy SetDMAAddress) */ + ret = hws_alloc_seed_buffers(hws); + if (!ret) + hws_seed_all_channels(hws); + + /* 7) Start-run sequence. */ + hws_init_video_sys(hws, false); + + /* A) Force legacy INTx; legacy used request_irq(pdev->irq, ..., IRQF_SHARED) */ + pci_intx(pdev, 1); + irqf = IRQF_SHARED; + irq = pdev->irq; + hws->irq = irq; + dev_info(&pdev->dev, "IRQ mode: legacy INTx (shared), irq=%d\n", irq); + + /* B) Mask the device's global/bridge gate (INT_EN_REG_BASE) */ + hws_irq_mask_gate(hws); + + /* C) Clear any sticky pending interrupt status (W1C) before we arm the line */ + hws_irq_clear_pending(hws); + + /* D) Request the legacy shared interrupt line (no vectors/MSI/MSI-X) */ + ret = devm_request_irq(&pdev->dev, irq, hws_irq_handler, irqf, + dev_name(&pdev->dev), hws); + if (ret) { + dev_err(&pdev->dev, "request_irq(%d) failed: %d\n", irq, ret); + goto err_unwind_channels; + } + + /* E) Set the global interrupt enable bit in main control register */ + { + u32 ctl_reg = readl(hws->bar0_base + HWS_REG_CTL); + + ctl_reg |= HWS_CTL_IRQ_ENABLE_BIT; + writel(ctl_reg, hws->bar0_base + HWS_REG_CTL); + (void)readl(hws->bar0_base + HWS_REG_CTL); /* flush write */ + dev_info(&pdev->dev, "Global IRQ enable bit set in control register\n"); + } + + /* F) Open the global gate just like legacy did */ + hws_irq_unmask_gate(hws); + dev_info(&pdev->dev, "INT_EN_GATE readback=0x%08x\n", + readl(hws->bar0_base + INT_EN_REG_BASE)); + + /* 11) Register V4L2 */ + ret = hws_video_register(hws); + if (ret) { + dev_err(&pdev->dev, "video_register: %d\n", ret); + goto err_unwind_channels; + } + v4l2_registered = true; + + /* 12) Background monitor thread (managed) */ + hws->main_task = kthread_run(main_ks_thread_handle, hws, "hws-mon"); + if (IS_ERR(hws->main_task)) { + ret = PTR_ERR(hws->main_task); + hws->main_task = NULL; + dev_err(&pdev->dev, "kthread_run: %d\n", ret); + goto err_unregister_va; + } + ret = devm_add_action_or_reset(&pdev->dev, hws_stop_kthread_action, hws); + if (ret) { + dev_err(&pdev->dev, "devm_add_action kthread_stop: %d\n", ret); + goto err_unregister_va; /* reset already stopped the thread */ + } + + /* 13) Final: show the line is armed */ + dev_info(&pdev->dev, "irq handler installed on irq=%d\n", irq); + return 0; + +err_unregister_va: + hws_stop_device(hws); + hws_video_unregister(hws); + hws_free_seed_buffers(hws); + return ret; +err_unwind_channels: + hws_free_seed_buffers(hws); + if (!v4l2_registered) { + while (--i >= 0) + hws_video_cleanup_channel(hws, i); + } + return ret; +} + +static int hws_check_busy(struct hws_pcie_dev *pdx) +{ + void __iomem *reg = pdx->bar0_base + HWS_REG_SYS_STATUS; + u32 val; + int ret; + + /* poll until !(val & BUSY_BIT), sleeping HWS_BUSY_POLL_DELAY_US between reads */ + ret = readl_poll_timeout(reg, val, !(val & HWS_SYS_DMA_BUSY_BIT), + HWS_BUSY_POLL_DELAY_US, + HWS_BUSY_POLL_TIMEOUT_US); + if (ret) { + dev_err(&pdx->pdev->dev, + "SYS_STATUS busy bit never cleared (0x%08x)\n", val); + return -ETIMEDOUT; + } + + return 0; +} + +static void hws_stop_dsp(struct hws_pcie_dev *hws) +{ + u32 status; + + /* Read the decoder mode/status register */ + status = readl(hws->bar0_base + HWS_REG_DEC_MODE); + dev_dbg(&hws->pdev->dev, "%s: status=0x%08x\n", __func__, status); + + /* If the device looks unplugged/stuck, bail out */ + if (status == 0xFFFFFFFF) + return; + + /* Tell the DSP to stop */ + writel(0x10, hws->bar0_base + HWS_REG_DEC_MODE); + + if (hws_check_busy(hws)) + dev_warn(&hws->pdev->dev, "DSP busy timeout on stop\n"); + /* Disable video capture engine in the DSP */ + writel(0x0, hws->bar0_base + HWS_REG_VCAP_ENABLE); +} + +/* Publish stop so ISR/BH will not touch video buffers anymore. */ +static void hws_publish_stop_flags(struct hws_pcie_dev *hws) +{ + unsigned int i; + + for (i = 0; i < hws->cur_max_video_ch; ++i) { + struct hws_video *v = &hws->video[i]; + + WRITE_ONCE(v->cap_active, false); + WRITE_ONCE(v->stop_requested, true); + } + + smp_wmb(); /* make flags visible before we touch MMIO/queues */ +} + +/* Drain engines + ISR/BH after flags are published. */ +static void hws_drain_after_stop(struct hws_pcie_dev *hws) +{ + u32 ackmask = 0; + unsigned int i; + u64 start_ns = ktime_get_mono_fast_ns(); + + /* Mask device enables: no new DMA starts. */ + writel(0x0, hws->bar0_base + HWS_REG_VCAP_ENABLE); + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); /* flush */ + + /* Let any in-flight DMAs finish (best-effort). */ + (void)hws_check_busy(hws); + + /* Ack any latched VDONE. */ + for (i = 0; i < hws->cur_max_video_ch; ++i) + ackmask |= HWS_INT_VDONE_BIT(i); + if (ackmask) { + writel(ackmask, hws->bar0_base + HWS_REG_INT_STATUS); + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); + } + + /* Ensure no hard IRQ is still running. */ + if (hws->irq >= 0) + synchronize_irq(hws->irq); + + dev_dbg(&hws->pdev->dev, "lifecycle:drain-after-stop:done (%lluus)\n", + hws_elapsed_us(start_ns)); +} + +static void hws_stop_device(struct hws_pcie_dev *hws) +{ + u32 status = readl(hws->bar0_base + HWS_REG_SYS_STATUS); + u64 start_ns = ktime_get_mono_fast_ns(); + bool live = status != 0xFFFFFFFF; + + dev_dbg(&hws->pdev->dev, "%s: status=0x%08x\n", __func__, status); + if (!live) { + hws->pci_lost = true; + goto out; + } + hws_log_lifecycle_snapshot(hws, "stop-device", "begin"); + + /* Make ISR/BH a no-op, then drain engines/IRQ. */ + hws_publish_stop_flags(hws); + hws_drain_after_stop(hws); + + /* 1) Stop the on-board DSP */ + hws_stop_dsp(hws); + +out: + hws->start_run = false; + if (live) + hws_log_lifecycle_snapshot(hws, "stop-device", "end"); + else + dev_dbg(&hws->pdev->dev, "lifecycle:stop-device:device-lost\n"); + dev_dbg(&hws->pdev->dev, "lifecycle:stop-device:done (%lluus)\n", + hws_elapsed_us(start_ns)); + dev_dbg(&hws->pdev->dev, "%s: complete\n", __func__); +} + +static int hws_quiesce_for_transition(struct hws_pcie_dev *hws, + const char *action, + bool stop_thread) +{ + struct device *dev = &hws->pdev->dev; + u64 start_ns = ktime_get_mono_fast_ns(); + u64 step_ns; + int vret; + + hws_log_lifecycle_snapshot(hws, action, "begin"); + + step_ns = ktime_get_mono_fast_ns(); + hws_block_hotpaths(hws); + dev_dbg(dev, "lifecycle:%s:block-hotpaths (%lluus)\n", action, + hws_elapsed_us(step_ns)); + hws_log_lifecycle_snapshot(hws, action, "blocked"); + + if (stop_thread) { + step_ns = ktime_get_mono_fast_ns(); + hws_stop_kthread_action(hws); + dev_dbg(dev, "lifecycle:%s:stop-kthread (%lluus)\n", action, + hws_elapsed_us(step_ns)); + } + + step_ns = ktime_get_mono_fast_ns(); + vret = hws_video_quiesce(hws, action); + dev_dbg(dev, "lifecycle:%s:video-quiesce ret=%d (%lluus)\n", action, + vret, hws_elapsed_us(step_ns)); + if (vret) + dev_warn(dev, "lifecycle:%s video quiesce returned %d\n", + action, vret); + + step_ns = ktime_get_mono_fast_ns(); + hws_stop_device(hws); + dev_dbg(dev, "lifecycle:%s:stop-device (%lluus)\n", action, + hws_elapsed_us(step_ns)); + hws_log_lifecycle_snapshot(hws, action, "end"); + dev_dbg(dev, "lifecycle:%s:quiesce-done ret=%d (%lluus)\n", action, + vret, hws_elapsed_us(start_ns)); + + return vret; +} + +static void hws_remove(struct pci_dev *pdev) +{ + struct hws_pcie_dev *hws = pci_get_drvdata(pdev); + u64 start_ns; + + if (!hws) + return; + + start_ns = ktime_get_mono_fast_ns(); + dev_info(&pdev->dev, "lifecycle:remove begin\n"); + hws_log_lifecycle_snapshot(hws, "remove", "begin"); + + /* Stop the monitor thread before tearing down V4L2/vb2 objects. */ + hws_block_hotpaths(hws); + hws_stop_kthread_action(hws); + + /* Stop hardware and capture cleanly. */ + hws_stop_device(hws); + + /* Unregister V4L2 resources. */ + hws_video_unregister(hws); + + /* Release seeded DMA buffers */ + hws_free_seed_buffers(hws); + /* kthread is stopped by the devm action registered in probe. */ + hws_log_lifecycle_snapshot(hws, "remove", "end"); + dev_info(&pdev->dev, "lifecycle:remove done (%lluus)\n", + hws_elapsed_us(start_ns)); +} + +#ifdef CONFIG_PM_SLEEP +static int hws_pm_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct hws_pcie_dev *hws = pci_get_drvdata(pdev); + int vret; + u64 start_ns = ktime_get_mono_fast_ns(); + u64 step_ns; + + dev_info(dev, "lifecycle:pm_suspend begin\n"); + vret = hws_quiesce_for_transition(hws, "pm_suspend", false); + + step_ns = ktime_get_mono_fast_ns(); + pci_save_state(pdev); + pci_clear_master(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + dev_dbg(dev, "lifecycle:pm_suspend:pci-d3hot (%lluus)\n", + hws_elapsed_us(step_ns)); + dev_info(dev, "lifecycle:pm_suspend done ret=%d (%lluus)\n", vret, + hws_elapsed_us(start_ns)); + + return 0; +} + +static int hws_pm_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct hws_pcie_dev *hws = pci_get_drvdata(pdev); + int ret; + u64 start_ns = ktime_get_mono_fast_ns(); + u64 step_ns; + + dev_info(dev, "lifecycle:pm_resume begin\n"); + + /* Back to D0 and re-enable the function */ + step_ns = ktime_get_mono_fast_ns(); + pci_set_power_state(pdev, PCI_D0); + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(dev, "pci_enable_device: %d\n", ret); + return ret; + } + pci_restore_state(pdev); + pci_set_master(pdev); + dev_dbg(dev, "lifecycle:pm_resume:pci-enable (%lluus)\n", + hws_elapsed_us(step_ns)); + + /* Reapply any PCIe tuning lost across D3 */ + enable_pcie_relaxed_ordering(pdev); + + /* Reinitialize chip-side capabilities / registers */ + step_ns = ktime_get_mono_fast_ns(); + read_chip_id(hws); + /* Re-seed BAR remaps/DMA windows and restart the capture core */ + hws_seed_all_channels(hws); + hws_init_video_sys(hws, true); + hws_irq_clear_pending(hws); + dev_dbg(dev, "lifecycle:pm_resume:chip-reinit (%lluus)\n", + hws_elapsed_us(step_ns)); + + /* IRQs can be re-enabled now that MMIO is sane */ + step_ns = ktime_get_mono_fast_ns(); + if (hws->irq >= 0) + enable_irq(hws->irq); + + WRITE_ONCE(hws->suspended, false); + dev_dbg(dev, "lifecycle:pm_resume:irq-unsuspend (%lluus)\n", + hws_elapsed_us(step_ns)); + + /* vb2: nothing mandatory; userspace will STREAMON again when ready */ + step_ns = ktime_get_mono_fast_ns(); + hws_video_pm_resume(hws); + dev_dbg(dev, "lifecycle:pm_resume:video-resume (%lluus)\n", + hws_elapsed_us(step_ns)); + hws_log_lifecycle_snapshot(hws, "pm_resume", "end"); + dev_info(dev, "lifecycle:pm_resume done (%lluus)\n", + hws_elapsed_us(start_ns)); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(hws_pm_ops, hws_pm_suspend, hws_pm_resume); +# define HWS_PM_OPS (&hws_pm_ops) +#else +# define HWS_PM_OPS NULL +#endif + +static void hws_shutdown(struct pci_dev *pdev) +{ + struct hws_pcie_dev *hws = pci_get_drvdata(pdev); + int vret = 0; + u64 start_ns = ktime_get_mono_fast_ns(); + u64 step_ns; + + if (!hws) + return; + + dev_info(&pdev->dev, "lifecycle:pci_shutdown begin\n"); + vret = hws_quiesce_for_transition(hws, "pci_shutdown", true); + + step_ns = ktime_get_mono_fast_ns(); + pci_clear_master(pdev); + dev_dbg(&pdev->dev, "lifecycle:pci_shutdown:clear-master (%lluus)\n", + hws_elapsed_us(step_ns)); + dev_info(&pdev->dev, "lifecycle:pci_shutdown done ret=%d (%lluus)\n", + vret, hws_elapsed_us(start_ns)); +} + +static struct pci_driver hws_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = hws_pci_table, + .probe = hws_probe, + .remove = hws_remove, + .shutdown = hws_shutdown, + .driver = { + .pm = HWS_PM_OPS, + }, +}; + +MODULE_DEVICE_TABLE(pci, hws_pci_table); + +static int __init pcie_hws_init(void) +{ + return pci_register_driver(&hws_pci_driver); +} + +static void __exit pcie_hws_exit(void) +{ + pci_unregister_driver(&hws_pci_driver); +} + +module_init(pcie_hws_init); +module_exit(pcie_hws_exit); + +MODULE_DESCRIPTION(DRV_NAME); +MODULE_AUTHOR("Ben Hoff "); +MODULE_AUTHOR("Sales "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("DMA_BUF"); diff --git a/drivers/media/pci/hws/hws_reg.h b/drivers/media/pci/hws/hws_reg.h new file mode 100644 index 000000000000..e4fb4af44434 --- /dev/null +++ b/drivers/media/pci/hws/hws_reg.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _HWS_PCIE_REG_H +#define _HWS_PCIE_REG_H + +#include +#include + +#define XDMA_CHANNEL_NUM_MAX (1) +#define MAX_NUM_ENGINES (XDMA_CHANNEL_NUM_MAX * 2) + +#define PCIE_BARADDROFSIZE 4u + +#define PCI_BUS_ACCESS_BASE 0x00000000U +#define INT_EN_REG_BASE (PCI_BUS_ACCESS_BASE + 0x0134U) +#define PCIEBR_EN_REG_BASE (PCI_BUS_ACCESS_BASE + 0x0148U) +#define PCIE_INT_DEC_REG_BASE (PCI_BUS_ACCESS_BASE + 0x0138U) + +#define HWS_INT_EN_MASK 0x0003FFFFU + +#define PCIEBAR_AXI_BASE 0x20000000U + +#define CTL_REG_ACC_BASE 0x0 +#define PCI_ADDR_TABLE_BASE CTL_REG_ACC_BASE + +#define CVBS_IN_BASE 0x00004000U +#define CVBS_IN_BUF_BASE (CVBS_IN_BASE + (16U * PCIE_BARADDROFSIZE)) +#define CVBS_IN_BUF_BASE2 (CVBS_IN_BASE + (50U * PCIE_BARADDROFSIZE)) + +/* 2 Mib */ +#define MAX_L_VIDEO_SIZE 0x200000U + +#define PCI_E_BAR_PAGE_SIZE 0x20000000 +#define PCI_E_BAR_ADD_MASK 0xE0000000 +#define PCI_E_BAR_ADD_LOWMASK 0x1FFFFFFF + +#define MAX_VID_CHANNELS 4 + +#define MAX_MM_VIDEO_SIZE SZ_4M + +#define MAX_VIDEO_HW_W 1920 +#define MAX_VIDEO_HW_H 1080 +#define MAX_VIDEO_SCALER_SIZE (1920U * 1080U * 2U) + +#define MIN_VAMP_BRIGHTNESS_UNITS 0 +#define MAX_VAMP_BRIGHTNESS_UNITS 0xff + +#define MIN_VAMP_CONTRAST_UNITS 0 +#define MAX_VAMP_CONTRAST_UNITS 0xff + +#define MIN_VAMP_SATURATION_UNITS 0 +#define MAX_VAMP_SATURATION_UNITS 0xff + +#define MIN_VAMP_HUE_UNITS 0 +#define MAX_VAMP_HUE_UNITS 0xff + +#define HWS_BRIGHTNESS_DEFAULT 0x80 +#define HWS_CONTRAST_DEFAULT 0x80 +#define HWS_SATURATION_DEFAULT 0x80 +#define HWS_HUE_DEFAULT 0x00 + +/* Core/global status. */ +#define HWS_REG_SYS_STATUS (CVBS_IN_BASE + 0 * PCIE_BARADDROFSIZE) +/* bit3: DMA busy, bit2: int, ... */ + +#define HWS_SYS_DMA_BUSY_BIT BIT(3) /* 0x08 = DMA busy flag */ + +#define HWS_REG_DEC_MODE (CVBS_IN_BASE + 0 * PCIE_BARADDROFSIZE) +/* Main control register */ +#define HWS_REG_CTL (CVBS_IN_BASE + 4 * PCIE_BARADDROFSIZE) +#define HWS_CTL_IRQ_ENABLE_BIT BIT(0) /* Global interrupt enable bit */ +/* Write 0x00 to fully reset decoder, + * set bit 31=1 to "start run", + * low byte=0x13 selects YUYV/BT.709/etc, + * in ReadChipId() we also write 0x00 and 0x10 here for chip-ID sequencing. + */ + +/* Per-channel done flags. */ +#define HWS_REG_INT_STATUS (CVBS_IN_BASE + 1 * PCIE_BARADDROFSIZE) +#define HWS_SYS_BUSY_BIT BIT(2) /* matches old 0x04 test */ + +/* Capture enable switches. */ +/* bit0-3: CH0-CH3 video enable */ +#define HWS_REG_VCAP_ENABLE (CVBS_IN_BASE + 2 * PCIE_BARADDROFSIZE) +/* bits0-3: signal present, bits8-11: interlace */ +#define HWS_REG_ACTIVE_STATUS (CVBS_IN_BASE + 5 * PCIE_BARADDROFSIZE) +/* bits0-3: HDCP detected */ +#define HWS_REG_HDCP_STATUS (CVBS_IN_BASE + 8 * PCIE_BARADDROFSIZE) +#define HWS_REG_DMA_MAX_SIZE (CVBS_IN_BASE + 9 * PCIE_BARADDROFSIZE) + +/* Buffer addresses (written once during init/reset). */ +/* Base of host-visible buffer. */ +#define HWS_REG_VBUF1_ADDR (CVBS_IN_BASE + 25 * PCIE_BARADDROFSIZE) +/* Per-channel DMA address. */ +#define HWS_REG_DMA_ADDR(ch) (CVBS_IN_BASE + (26 + (ch)) * PCIE_BARADDROFSIZE) + +/* Per-channel live buffer toggles (read-only). */ +#define HWS_REG_VBUF_TOGGLE(ch) (CVBS_IN_BASE + (32 + (ch)) * PCIE_BARADDROFSIZE) +/* + * Returns 0 or 1 = which half of the video ring the DMA engine is + * currently filling for channel *ch* (0-3). + */ + +/* Per-interrupt bits (video 0-3). */ +#define HWS_INT_VDONE_BIT(ch) BIT(ch) /* 0x01,0x02,0x04,0x08 */ + +#define HWS_REG_INT_ACK (CVBS_IN_BASE + 0x4000 + 1 * PCIE_BARADDROFSIZE) + +/* 16-bit W | 16-bit H. */ +#define HWS_REG_IN_RES(ch) (CVBS_IN_BASE + (90 + (ch) * 2) * PCIE_BARADDROFSIZE) +/* B|C|H|S packed bytes. */ +#define HWS_REG_BCHS(ch) (CVBS_IN_BASE + (91 + (ch) * 2) * PCIE_BARADDROFSIZE) + +/* Input fps. */ +#define HWS_REG_FRAME_RATE(ch) (CVBS_IN_BASE + (110 + (ch)) * PCIE_BARADDROFSIZE) +/* Programmed out W|H. */ +#define HWS_REG_OUT_RES(ch) (CVBS_IN_BASE + (120 + (ch)) * PCIE_BARADDROFSIZE) +/* Programmed out fps. */ +#define HWS_REG_OUT_FRAME_RATE(ch) (CVBS_IN_BASE + (130 + (ch)) * PCIE_BARADDROFSIZE) + +/* Device version/port ID/subversion register. */ +#define HWS_REG_DEVICE_INFO (CVBS_IN_BASE + 88 * PCIE_BARADDROFSIZE) +/* + * Reading this 32-bit word returns: + * bits 7:0 = "device version" + * bits 15:8 = "device sub-version" + * bits 23:24 = "HW key / port ID" etc. + * bits 31:28 = "support YV12" flags + */ + +/* Convenience aliases for individual channels. */ +#define HWS_REG_VBUF_TOGGLE_CH0 HWS_REG_VBUF_TOGGLE(0) +#define HWS_REG_VBUF_TOGGLE_CH1 HWS_REG_VBUF_TOGGLE(1) +#define HWS_REG_VBUF_TOGGLE_CH2 HWS_REG_VBUF_TOGGLE(2) +#define HWS_REG_VBUF_TOGGLE_CH3 HWS_REG_VBUF_TOGGLE(3) + +#endif /* _HWS_PCIE_REG_H */ diff --git a/drivers/media/pci/hws/hws_v4l2_ioctl.c b/drivers/media/pci/hws/hws_v4l2_ioctl.c new file mode 100644 index 000000000000..0303e311ee4a --- /dev/null +++ b/drivers/media/pci/hws/hws_v4l2_ioctl.c @@ -0,0 +1,919 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "hws.h" +#include "hws_reg.h" +#include "hws_video.h" +#include "hws_v4l2_ioctl.h" + +struct hws_dv_mode { + struct v4l2_dv_timings timings; + u32 refresh_hz; +}; + +static const struct hws_dv_mode * +hws_find_dv_by_wh(u32 w, u32 h, bool interlaced); +static const struct hws_dv_mode * +hws_find_dv_by_wh_fps(u32 w, u32 h, bool interlaced, u32 fps); +static u32 hws_get_live_fps(struct hws_video *vid); +static u32 hws_input_status(struct hws_video *vid); +static int hws_fill_dv_timings(u32 w, u32 h, bool interlace, u32 fps, + struct v4l2_dv_timings *timings); + +static const struct hws_dv_mode hws_dv_modes[] = { + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1920, + .height = 1080, + .hfrontporch = 88, + .hsync = 44, + .hbackporch = 148, + .vfrontporch = 4, + .vsync = 5, + .vbackporch = 36, + .pixelclock = 148500000, + .polarities = V4L2_DV_VSYNC_POS_POL | + V4L2_DV_HSYNC_POS_POL, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1920, + .height = 1080, + .hfrontporch = 88, + .hsync = 44, + .hbackporch = 148, + .vfrontporch = 4, + .vsync = 5, + .vbackporch = 36, + .pixelclock = 74250000, + .polarities = V4L2_DV_VSYNC_POS_POL | + V4L2_DV_HSYNC_POS_POL, + .interlaced = 0, + }, + }, + 30, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1280, + .height = 720, + .hfrontporch = 110, + .hsync = 40, + .hbackporch = 220, + .vfrontporch = 5, + .vsync = 5, + .vbackporch = 20, + .pixelclock = 74250000, + .polarities = V4L2_DV_VSYNC_POS_POL | + V4L2_DV_HSYNC_POS_POL, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 720, + .height = 480, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 720, + .height = 576, + .interlaced = 0, + }, + }, + 50, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 800, + .height = 600, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 640, + .height = 480, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1024, + .height = 768, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1280, + .height = 768, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1280, + .height = 800, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1280, + .height = 1024, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1360, + .height = 768, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1440, + .height = 900, + .interlaced = 0, + }, + }, + 60, + }, + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1680, + .height = 1050, + .interlaced = 0, + }, + }, + 60, + }, + /* Portrait */ + { + { + .type = V4L2_DV_BT_656_1120, + .bt = { + .width = 1080, + .height = 1920, + .interlaced = 0, + }, + }, + 60, + }, +}; + +static const size_t hws_dv_modes_cnt = ARRAY_SIZE(hws_dv_modes); + +/* YUYV: 16 bpp; align to 64 as you did elsewhere */ +static inline u32 hws_calc_bpl_yuyv(u32 w) { return ALIGN(w * 2, 64); } +static inline u32 hws_calc_size_yuyv(u32 w, u32 h) { return hws_calc_bpl_yuyv(w) * h; } +static inline u32 hws_calc_half_size(u32 sizeimage) +{ + return sizeimage / 2; +} + +static inline void hws_hw_write_bchs(struct hws_pcie_dev *hws, unsigned int ch, + u8 br, u8 co, u8 hu, u8 sa) +{ + u32 packed = (sa << 24) | (hu << 16) | (co << 8) | br; + + if (!hws || !hws->bar0_base || ch >= hws->max_channels) + return; + writel_relaxed(packed, hws->bar0_base + HWS_REG_BCHS(ch)); + (void)readl(hws->bar0_base + HWS_REG_BCHS(ch)); /* post write */ +} + +/* Helper: find a supported DV mode by W/H + interlace flag */ +static const struct hws_dv_mode * +hws_match_supported_dv(const struct v4l2_dv_timings *req) +{ + const struct v4l2_bt_timings *bt; + u32 fps; + + if (!req || req->type != V4L2_DV_BT_656_1120) + return NULL; + + bt = &req->bt; + fps = 0; + if (bt->pixelclock) { + u32 total_w = bt->width + bt->hfrontporch + bt->hsync + + bt->hbackporch; + u32 total_h = bt->height + bt->vfrontporch + bt->vsync + + bt->vbackporch; + + if (total_w && total_h) + fps = DIV_ROUND_CLOSEST_ULL((u64)bt->pixelclock, + (u64)total_w * total_h); + } + if (fps) { + const struct hws_dv_mode *exact = + hws_find_dv_by_wh_fps(bt->width, bt->height, + !!bt->interlaced, fps); + if (exact) + return exact; + } + return hws_find_dv_by_wh(bt->width, bt->height, !!bt->interlaced); +} + +/* Helper: find a supported DV mode by W/H + interlace flag */ +static const struct hws_dv_mode * +hws_find_dv_by_wh(u32 w, u32 h, bool interlaced) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) { + const struct hws_dv_mode *t = &hws_dv_modes[i]; + const struct v4l2_bt_timings *bt = &t->timings.bt; + + if (t->timings.type != V4L2_DV_BT_656_1120) + continue; + + if (bt->width == w && bt->height == h && + !!bt->interlaced == interlaced) + return t; + } + return NULL; +} + +static const struct hws_dv_mode * +hws_find_dv_by_wh_fps(u32 w, u32 h, bool interlaced, u32 fps) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) { + const struct hws_dv_mode *t = &hws_dv_modes[i]; + const struct v4l2_bt_timings *bt = &t->timings.bt; + + if (t->timings.type != V4L2_DV_BT_656_1120) + continue; + + if (bt->width == w && bt->height == h && + !!bt->interlaced == interlaced && + t->refresh_hz == fps) + return t; + } + return NULL; +} + +static bool hws_get_live_dv_geometry(struct hws_video *vid, + u32 *w, u32 *h, bool *interlaced) +{ + struct hws_pcie_dev *pdx; + u32 reg; + + if (!vid) + return false; + + pdx = vid->parent; + if (!pdx || !pdx->bar0_base) + return false; + + reg = readl(pdx->bar0_base + HWS_REG_IN_RES(vid->channel_index)); + if (!reg || reg == 0xFFFFFFFF) + return false; + + if (w) + *w = reg & 0xFFFF; + if (h) + *h = (reg >> 16) & 0xFFFF; + if (interlaced) { + reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS); + *interlaced = !!(reg & BIT(8 + vid->channel_index)); + } + return true; +} + +static u32 hws_get_live_fps(struct hws_video *vid) +{ + struct hws_pcie_dev *pdx; + u32 fps; + + if (!vid) + return 0; + + pdx = vid->parent; + if (!pdx || !pdx->bar0_base) + return 0; + + fps = readl(pdx->bar0_base + HWS_REG_FRAME_RATE(vid->channel_index)); + if (!fps || fps == 0xFFFFFFFF || fps > 240) + return 0; + + return fps; +} + +static u32 hws_pick_fps_from_mode(u32 w, u32 h, bool interlaced) +{ + const struct hws_dv_mode *m = hws_find_dv_by_wh(w, h, interlaced); + + if (m && m->refresh_hz) + return m->refresh_hz; + /* Fallback to a sane default */ + return 60; +} + +static int hws_fill_dv_timings(u32 w, u32 h, bool interlace, u32 fps, + struct v4l2_dv_timings *timings) +{ + const struct hws_dv_mode *m; + + m = fps ? hws_find_dv_by_wh_fps(w, h, interlace, fps) : NULL; + if (!m) + m = hws_find_dv_by_wh(w, h, interlace); + if (!m) + return -ENOLINK; + + *timings = m->timings; + return 0; +} + +static u32 hws_input_status(struct hws_video *vid) +{ + struct hws_pcie_dev *pdx; + u32 reg; + + if (!vid) + return V4L2_IN_ST_NO_SIGNAL; + + pdx = vid->parent; + if (!pdx || !pdx->bar0_base) + return V4L2_IN_ST_NO_SIGNAL; + + reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS); + if (reg == 0xffffffff) + return V4L2_IN_ST_NO_SIGNAL; + + return (reg & BIT(vid->channel_index)) ? 0 : V4L2_IN_ST_NO_SIGNAL; +} + +/* Query the *current detected* DV timings on the input. + * If you have a real hardware detector, call it here; otherwise we + * derive from the cached pix state and map to the closest supported DV mode. + */ +int hws_vidioc_query_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct hws_video *vid = video_drvdata(file); + u32 w, h; + u32 fps; + bool interlace; + + if (!timings) + return -EINVAL; + + w = vid->pix.width; + h = vid->pix.height; + interlace = vid->pix.interlaced; + hws_get_live_dv_geometry(vid, &w, &h, &interlace); + fps = hws_get_live_fps(vid); + if (!fps) + fps = vid->current_fps ? vid->current_fps : + hws_pick_fps_from_mode(w, h, interlace); + + return hws_fill_dv_timings(w, h, interlace, fps, timings); +} + +/* Enumerate the Nth supported DV timings from our static table. */ +int hws_vidioc_enum_dv_timings(struct file *file, void *fh, + struct v4l2_enum_dv_timings *edv) +{ + struct hws_video *vid = video_drvdata(file); + const struct hws_dv_mode *m; + u32 w, h; + u32 fps; + bool interlace; + + if (!edv) + return -EINVAL; + + if (edv->pad) + return -EINVAL; + + w = 0; + h = 0; + interlace = false; + if (hws_get_live_dv_geometry(vid, &w, &h, &interlace)) { + fps = hws_get_live_fps(vid); + if (!fps) + fps = vid->current_fps ? vid->current_fps : + hws_pick_fps_from_mode(w, h, interlace); + m = fps ? hws_find_dv_by_wh_fps(w, h, interlace, fps) : NULL; + if (!m) + m = hws_find_dv_by_wh(w, h, interlace); + if (m) { + if (edv->index) + return -EINVAL; + edv->timings = m->timings; + return 0; + } + } + + if (edv->index >= hws_dv_modes_cnt) + return -EINVAL; + + edv->timings = hws_dv_modes[edv->index].timings; + return 0; +} + +/* Get the *currently configured* DV timings. */ +int hws_vidioc_g_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct hws_video *vid = video_drvdata(file); + u32 w, h; + u32 fps; + bool interlace; + + if (!timings) + return -EINVAL; + + w = vid->pix.width; + h = vid->pix.height; + interlace = vid->pix.interlaced; + if (hws_get_live_dv_geometry(vid, &w, &h, &interlace)) { + fps = hws_get_live_fps(vid); + if (!fps) + fps = vid->current_fps ? vid->current_fps : + hws_pick_fps_from_mode(w, h, interlace); + return hws_fill_dv_timings(w, h, interlace, fps, timings); + } + + *timings = vid->cur_dv_timings; + return 0; +} + +static inline void hws_set_colorimetry_state(struct hws_pix_state *p) +{ + bool sd = p->height <= 576; + + p->colorspace = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; + p->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + p->quantization = V4L2_QUANTIZATION_FULL_RANGE; + p->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +/* Set DV timings: must match one of our supported modes. + * If buffers are queued and this implies a size change, we reject with -EBUSY. + * Otherwise we update pix state and (optionally) reprogram the HW. + */ +int hws_vidioc_s_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct hws_video *vid = video_drvdata(file); + const struct hws_dv_mode *m; + const struct v4l2_bt_timings *bt; + u32 new_w, new_h; + bool interlaced; + int ret = 0; + unsigned long was_busy; + u32 live_w, live_h; + u32 live_fps; + bool live_interlaced; + bool live_present; + + if (!timings) + return -EINVAL; + + m = hws_match_supported_dv(timings); + if (!m) + return -EINVAL; + + bt = &m->timings.bt; + if (bt->interlaced) + return -EINVAL; /* only progressive modes are advertised */ + new_w = bt->width; + new_h = bt->height; + interlaced = false; + + lockdep_assert_held(&vid->state_lock); + live_present = hws_get_live_dv_geometry(vid, &live_w, &live_h, + &live_interlaced); + + /* If vb2 has active buffers and size would change, reject. */ + was_busy = vb2_is_busy(&vid->buffer_queue); + if (was_busy && + (new_w != vid->pix.width || new_h != vid->pix.height || + interlaced != vid->pix.interlaced)) { + ret = -EBUSY; + return ret; + } + + /* When a live input signal is present, the receiver owns the timing. + * Allow setting the already-active timings so v4l2-compliance can + * round-trip them, but reject attempts to retime the live source. + */ + if (live_present) { + live_fps = hws_get_live_fps(vid); + if (!live_fps) + live_fps = vid->current_fps ? vid->current_fps : + hws_pick_fps_from_mode(live_w, live_h, + live_interlaced); + if (live_w == new_w && live_h == new_h && + live_interlaced == interlaced && + m->refresh_hz == live_fps) + return 0; + return -EBUSY; + } + + /* Update software pixel state (and recalc sizes) */ + vid->pix.width = new_w; + vid->pix.height = new_h; + vid->pix.field = interlaced ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_NONE; + vid->pix.interlaced = interlaced; + vid->pix.fourcc = V4L2_PIX_FMT_YUYV; + + hws_set_colorimetry_state(&vid->pix); + + /* Recompute stride, sizeimage, and half_size. */ + vid->pix.bytesperline = hws_calc_bpl_yuyv(new_w); + vid->pix.sizeimage = hws_calc_size_yuyv(new_w, new_h); + vid->pix.half_size = hws_calc_half_size(vid->pix.sizeimage); + vid->cur_dv_timings = m->timings; + vid->current_fps = m->refresh_hz; + return ret; +} + +/* Report DV timings capability: advertise BT.656/1120 with + * the min/max WxH derived from our table and basic progressive support. + */ +int hws_vidioc_dv_timings_cap(struct file *file, void *fh, + struct v4l2_dv_timings_cap *cap) +{ + u32 min_w = ~0U, min_h = ~0U; + u32 max_w = 0, max_h = 0; + size_t i, n = 0; + + if (!cap) + return -EINVAL; + + memset(cap, 0, sizeof(*cap)); + cap->type = V4L2_DV_BT_656_1120; + + for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) { + const struct v4l2_bt_timings *bt = &hws_dv_modes[i].timings.bt; + + if (hws_dv_modes[i].timings.type != V4L2_DV_BT_656_1120) + continue; + n++; + + if (bt->width < min_w) + min_w = bt->width; + if (bt->height < min_h) + min_h = bt->height; + if (bt->width > max_w) + max_w = bt->width; + if (bt->height > max_h) + max_h = bt->height; + } + + /* If the table was empty, fail gracefully. */ + if (!n || min_w == U32_MAX) + return -ENODATA; + + cap->bt.min_width = min_w; + cap->bt.max_width = max_w; + cap->bt.min_height = min_h; + cap->bt.max_height = max_h; + + /* We support both CEA-861- and VESA-style modes in the list. */ + cap->bt.standards = + V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT; + + /* Only progressive modes are advertised. */ + cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE; + + /* Leave pixelclock/porch limits unconstrained (0) for now. */ + return 0; +} + +static int hws_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct hws_video *vid = + container_of(ctrl->handler, struct hws_video, control_handler); + struct hws_pcie_dev *pdx = vid->parent; + bool program = false; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + vid->current_brightness = ctrl->val; + program = true; + break; + case V4L2_CID_CONTRAST: + vid->current_contrast = ctrl->val; + program = true; + break; + case V4L2_CID_SATURATION: + vid->current_saturation = ctrl->val; + program = true; + break; + case V4L2_CID_HUE: + vid->current_hue = ctrl->val; + program = true; + break; + default: + return -EINVAL; + } + + if (program) { + hws_hw_write_bchs(pdx, vid->channel_index, + (u8)vid->current_brightness, + (u8)vid->current_contrast, + (u8)vid->current_hue, + (u8)vid->current_saturation); + } + return 0; +} + +const struct v4l2_ctrl_ops hws_ctrl_ops = { + .s_ctrl = hws_s_ctrl, +}; + +int hws_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + struct hws_video *vid = video_drvdata(file); + int vi_index = vid->channel_index + 1; /* keep it simple */ + + strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + snprintf(cap->card, sizeof(cap->card), + "AVMatrix HWS Capture %d", vi_index); + return 0; +} + +int hws_vidioc_enum_fmt_vid_cap(struct file *file, void *priv_fh, struct v4l2_fmtdesc *f) +{ + if (f->index != 0) + return -EINVAL; /* only one format */ + + f->pixelformat = V4L2_PIX_FMT_YUYV; + return 0; +} + +int hws_vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct hws_video *vid = video_drvdata(file); + + fmt->fmt.pix.width = vid->pix.width; + fmt->fmt.pix.height = vid->pix.height; + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + fmt->fmt.pix.field = vid->pix.field; + fmt->fmt.pix.bytesperline = vid->pix.bytesperline; + fmt->fmt.pix.sizeimage = vid->pix.sizeimage; + fmt->fmt.pix.colorspace = vid->pix.colorspace; + fmt->fmt.pix.ycbcr_enc = vid->pix.ycbcr_enc; + fmt->fmt.pix.quantization = vid->pix.quantization; + fmt->fmt.pix.xfer_func = vid->pix.xfer_func; + return 0; +} + +static inline void hws_set_colorimetry_fmt(struct v4l2_pix_format *p) +{ + bool sd = p->height <= 576; + + p->colorspace = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; + p->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + p->quantization = V4L2_QUANTIZATION_FULL_RANGE; + p->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +int hws_vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct hws_video *vid = file ? video_drvdata(file) : NULL; + struct hws_pcie_dev *pdev = vid ? vid->parent : NULL; + struct v4l2_pix_format *pix = &f->fmt.pix; + u32 req_w = pix->width, req_h = pix->height; + u32 w, h, min_bpl, bpl; + size_t size; /* wider than u32 for overflow check */ + size_t max_frame = pdev ? pdev->max_hw_video_buf_sz : MAX_MM_VIDEO_SIZE; + + /* Only YUYV */ + pix->pixelformat = V4L2_PIX_FMT_YUYV; + + /* Defaults then clamp */ + w = (req_w ? req_w : 640); + h = (req_h ? req_h : 480); + if (w > MAX_VIDEO_HW_W) + w = MAX_VIDEO_HW_W; + if (h > MAX_VIDEO_HW_H) + h = MAX_VIDEO_HW_H; + if (!w) + w = 640; /* hard fallback in case macros are odd */ + if (!h) + h = 480; + + /* Field policy */ + pix->field = V4L2_FIELD_NONE; + + /* Stride policy for packed 16bpp, 64B align */ + min_bpl = ALIGN(w * 2, 64); + + /* Bound requested bpl to something sane, then align */ + bpl = pix->bytesperline; + if (bpl < min_bpl) { + bpl = min_bpl; + } else { + /* Cap at 16x width to avoid silly values that overflow sizeimage */ + u32 max_bpl = ALIGN(w * 2 * 16, 64); + + if (bpl > max_bpl) + bpl = max_bpl; + bpl = ALIGN(bpl, 64); + } + if (h && max_frame) { + size_t max_bpl_hw = max_frame / h; + + if (max_bpl_hw < min_bpl) + return -ERANGE; + max_bpl_hw = rounddown(max_bpl_hw, 64); + if (!max_bpl_hw) + return -ERANGE; + if (bpl > max_bpl_hw) { + if (pdev) + dev_dbg(&pdev->pdev->dev, + "try_fmt: clamp bpl %u -> %zu due to hw buf cap %zu\n", + bpl, max_bpl_hw, max_frame); + bpl = (u32)max_bpl_hw; + } + } + size = (size_t)bpl * (size_t)h; + if (size > max_frame) + return -ERANGE; + + pix->width = w; + pix->height = h; + pix->bytesperline = bpl; + pix->sizeimage = (u32)size; /* logical size, not page-aligned */ + + hws_set_colorimetry_fmt(pix); + if (pdev) + dev_dbg(&pdev->pdev->dev, + "try_fmt: w=%u h=%u bpl=%u size=%u field=%u\n", + pix->width, pix->height, pix->bytesperline, + pix->sizeimage, pix->field); + return 0; +} + +int hws_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + struct hws_video *vid = video_drvdata(file); + int ret; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* Normalize the request */ + ret = hws_vidioc_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + /* Don't allow buffer layout changes while buffers are queued. */ + if (vb2_is_busy(&vid->buffer_queue)) { + if (f->fmt.pix.width != vid->pix.width || + f->fmt.pix.height != vid->pix.height || + f->fmt.pix.bytesperline != vid->pix.bytesperline) + return -EBUSY; + } + + /* Apply to driver state */ + vid->pix.width = f->fmt.pix.width; + vid->pix.height = f->fmt.pix.height; + vid->pix.fourcc = V4L2_PIX_FMT_YUYV; + vid->pix.field = f->fmt.pix.field; + vid->pix.colorspace = f->fmt.pix.colorspace; + vid->pix.ycbcr_enc = f->fmt.pix.ycbcr_enc; + vid->pix.quantization = f->fmt.pix.quantization; + vid->pix.xfer_func = f->fmt.pix.xfer_func; + + /* Update negotiated buffer sizes. */ + vid->pix.bytesperline = f->fmt.pix.bytesperline; /* aligned */ + vid->pix.sizeimage = f->fmt.pix.sizeimage; /* logical */ + vid->pix.half_size = hws_calc_half_size(vid->pix.sizeimage); + vid->pix.interlaced = false; + /* S_FMT negotiates buffer layout only. Keep detector-owned DV timing + * state unchanged so a harmless restart cannot clobber the live FPS. + */ + /* Or: + * hws_calc_sizeimage(vid, vid->pix.width, vid->pix.height, false); + */ + + dev_dbg(&vid->parent->pdev->dev, + "s_fmt: w=%u h=%u bpl=%u size=%u\n", + vid->pix.width, vid->pix.height, vid->pix.bytesperline, + vid->pix.sizeimage); + + return 0; +} + +int hws_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *param) +{ + struct hws_video *vid = video_drvdata(file); + u32 fps; + + if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + fps = hws_get_live_fps(vid); + if (!fps) + fps = vid->current_fps ? vid->current_fps : 60; + + /* HDMI receivers report the detected frame period, they don't set it. */ + param->parm.capture.capability = 0; + param->parm.capture.capturemode = 0; + param->parm.capture.timeperframe.numerator = 1; + param->parm.capture.timeperframe.denominator = fps; + param->parm.capture.extendedmode = 0; + param->parm.capture.readbuffers = 0; + + return 0; +} + +int hws_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct hws_video *vid = video_drvdata(file); + + if (input->index) + return -EINVAL; + input->type = V4L2_INPUT_TYPE_CAMERA; + strscpy(input->name, KBUILD_MODNAME, sizeof(input->name)); + input->capabilities = V4L2_IN_CAP_DV_TIMINGS; + input->status = hws_input_status(vid); + + return 0; +} + +int hws_vidioc_g_input(struct file *file, void *priv, unsigned int *index) +{ + *index = 0; + return 0; +} + +int hws_vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + return i ? -EINVAL : 0; +} diff --git a/drivers/media/pci/hws/hws_v4l2_ioctl.h b/drivers/media/pci/hws/hws_v4l2_ioctl.h new file mode 100644 index 000000000000..53044f78d6fa --- /dev/null +++ b/drivers/media/pci/hws/hws_v4l2_ioctl.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef HWS_V4L2_IOCTL_H +#define HWS_V4L2_IOCTL_H + +#include +#include + +extern const struct v4l2_ctrl_ops hws_ctrl_ops; + +int hws_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap); +int hws_vidioc_enum_fmt_vid_cap(struct file *file, void *priv_fh, struct v4l2_fmtdesc *f); +int hws_vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt); +int hws_vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f); +int hws_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorms); +int hws_vidioc_s_std(struct file *file, void *priv, v4l2_std_id tvnorms); +int hws_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *setfps); +int hws_vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i); +int hws_vidioc_g_input(struct file *file, void *priv, unsigned int *i); +int hws_vidioc_s_input(struct file *file, void *priv, unsigned int i); +int hws_vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *a); +int hws_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a); +int hws_vidioc_dv_timings_cap(struct file *file, void *fh, + struct v4l2_dv_timings_cap *cap); +int hws_vidioc_s_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings); + +int hws_vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a); +int hws_vidioc_g_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings); +int hws_vidioc_enum_dv_timings(struct file *file, void *fh, + struct v4l2_enum_dv_timings *edv); +int hws_vidioc_query_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings); +int hws_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f); + +#endif diff --git a/drivers/media/pci/hws/hws_video.c b/drivers/media/pci/hws/hws_video.c new file mode 100644 index 000000000000..18e4bc6901d3 --- /dev/null +++ b/drivers/media/pci/hws/hws_video.c @@ -0,0 +1,1490 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "hws.h" +#include "hws_reg.h" +#include "hws_video.h" +#include "hws_irq.h" +#include "hws_v4l2_ioctl.h" + +#define HWS_REMAP_SLOT_OFF(ch) (0x208 + (ch) * 8) /* one 64-bit slot per ch */ +#define HWS_BUF_BASE_OFF(ch) (CVBS_IN_BUF_BASE + (ch) * PCIE_BARADDROFSIZE) +#define HWS_HALF_SZ_OFF(ch) (CVBS_IN_BUF_BASE2 + (ch) * PCIE_BARADDROFSIZE) + +static void update_live_resolution(struct hws_pcie_dev *pdx, unsigned int ch, + bool interlace); +static bool hws_read_active_state(struct hws_pcie_dev *pdx, unsigned int ch, + bool *interlace); +static void handle_hwv2_path(struct hws_pcie_dev *hws, unsigned int ch); +static void handle_legacy_path(struct hws_pcie_dev *hws, unsigned int ch); +static u32 hws_calc_sizeimage(struct hws_video *v, u16 w, u16 h, + bool interlaced); + +/* DMA helper functions */ +static void hws_program_dma_window(struct hws_video *vid, dma_addr_t dma); +static struct hwsvideo_buffer * +hws_take_queued_buffer_locked(struct hws_video *vid); + +static unsigned long long hws_elapsed_us(u64 start_ns) +{ + return div_u64(ktime_get_mono_fast_ns() - start_ns, 1000); +} + +static inline bool list_node_unlinked(const struct list_head *n) +{ + return n->next == LIST_POISON1 || n->prev == LIST_POISON2; +} + +static bool dma_window_verify; +module_param_named(dma_window_verify, dma_window_verify, bool, 0644); +MODULE_PARM_DESC(dma_window_verify, + "Read back DMA window registers after programming (debug)"); + +void hws_set_dma_doorbell(struct hws_pcie_dev *hws, unsigned int ch, + dma_addr_t dma, const char *tag) +{ + iowrite32(lower_32_bits(dma), hws->bar0_base + HWS_REG_DMA_ADDR(ch)); + dev_dbg(&hws->pdev->dev, "dma_doorbell ch%u: dma=0x%llx tag=%s\n", ch, + (u64)dma, tag ? tag : ""); +} + +static void hws_program_dma_window(struct hws_video *vid, dma_addr_t dma) +{ + const u32 addr_mask = PCI_E_BAR_ADD_MASK; + const u32 addr_low_mask = PCI_E_BAR_ADD_LOWMASK; + struct hws_pcie_dev *hws = vid->parent; + unsigned int ch = vid->channel_index; + u32 table_off = HWS_REMAP_SLOT_OFF(ch); + u32 lo = lower_32_bits(dma); + u32 hi = upper_32_bits(dma); + u32 pci_addr = lo & addr_low_mask; + u32 page_lo = lo & addr_mask; + + bool wrote = false; + + /* Remap entry only when DMA crosses into a new 512 MB page */ + if (!vid->window_valid || vid->last_dma_hi != hi || + vid->last_dma_page != page_lo) { + writel(hi, hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off); + writel(page_lo, + hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off + + PCIE_BARADDROFSIZE); + vid->last_dma_hi = hi; + vid->last_dma_page = page_lo; + wrote = true; + } + + /* Base pointer only needs low 29 bits */ + if (!vid->window_valid || vid->last_pci_addr != pci_addr) { + writel((ch + 1) * PCIEBAR_AXI_BASE + pci_addr, + hws->bar0_base + HWS_BUF_BASE_OFF(ch)); + vid->last_pci_addr = pci_addr; + wrote = true; + } + + /* Half-size only changes when resolution changes */ + if (!vid->window_valid || vid->last_half16 != vid->pix.half_size / 16) { + writel(vid->pix.half_size / 16, + hws->bar0_base + HWS_HALF_SZ_OFF(ch)); + vid->last_half16 = vid->pix.half_size / 16; + wrote = true; + } + + vid->window_valid = true; + + if (dma_window_verify && wrote) { + u32 r_hi = + readl(hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off); + u32 r_lo = + readl(hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off + + PCIE_BARADDROFSIZE); + u32 r_base = readl(hws->bar0_base + HWS_BUF_BASE_OFF(ch)); + u32 r_half = readl(hws->bar0_base + HWS_HALF_SZ_OFF(ch)); + + dev_dbg(&hws->pdev->dev, + "ch%u remap verify: hi=0x%08x page_lo=0x%08x exp_page=0x%08x base=0x%08x exp_base=0x%08x half16B=0x%08x exp_half=0x%08x\n", + ch, r_hi, r_lo, page_lo, r_base, + (ch + 1) * PCIEBAR_AXI_BASE + pci_addr, r_half, + vid->pix.half_size / 16); + } else if (wrote) { + /* Flush posted writes before arming DMA */ + readl_relaxed(hws->bar0_base + HWS_HALF_SZ_OFF(ch)); + } +} + +static struct hwsvideo_buffer * +hws_take_queued_buffer_locked(struct hws_video *vid) +{ + struct hwsvideo_buffer *buf; + + if (!vid || list_empty(&vid->capture_queue)) + return NULL; + + buf = list_first_entry(&vid->capture_queue, + struct hwsvideo_buffer, list); + list_del_init(&buf->list); + if (vid->queued_count) + vid->queued_count--; + return buf; +} + +void hws_prime_next_locked(struct hws_video *vid) +{ + struct hws_pcie_dev *hws; + struct hwsvideo_buffer *next; + dma_addr_t dma; + + if (!vid) + return; + + hws = vid->parent; + if (!hws || !hws->bar0_base) + return; + + if (!READ_ONCE(vid->cap_active) || !vid->active || vid->next_prepared) + return; + + next = hws_take_queued_buffer_locked(vid); + if (!next) + return; + + vid->next_prepared = next; + dma = vb2_dma_contig_plane_dma_addr(&next->vb.vb2_buf, 0); + hws_program_dma_for_addr(hws, vid->channel_index, dma); + iowrite32(lower_32_bits(dma), + hws->bar0_base + HWS_REG_DMA_ADDR(vid->channel_index)); + dev_dbg(&hws->pdev->dev, + "ch%u pre-armed next buffer %p dma=0x%llx\n", + vid->channel_index, next, (u64)dma); +} + +static bool hws_force_no_signal_frame(struct hws_video *v, const char *tag) +{ + struct hws_pcie_dev *hws; + unsigned long flags; + struct hwsvideo_buffer *buf = NULL, *next = NULL; + bool have_next = false; + bool doorbell = false; + + if (!v) + return false; + hws = v->parent; + if (!hws || READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active)) + return false; + spin_lock_irqsave(&v->irq_lock, flags); + if (v->active) { + buf = v->active; + v->active = NULL; + buf->slot = 0; + } else if (!list_empty(&v->capture_queue)) { + buf = list_first_entry(&v->capture_queue, + struct hwsvideo_buffer, list); + list_del_init(&buf->list); + if (v->queued_count) + v->queued_count--; + buf->slot = 0; + } + if (v->next_prepared) { + next = v->next_prepared; + v->next_prepared = NULL; + next->slot = 0; + v->active = next; + have_next = true; + } else if (!list_empty(&v->capture_queue)) { + next = list_first_entry(&v->capture_queue, + struct hwsvideo_buffer, list); + list_del_init(&next->list); + if (v->queued_count) + v->queued_count--; + next->slot = 0; + v->active = next; + have_next = true; + } else { + v->active = NULL; + } + spin_unlock_irqrestore(&v->irq_lock, flags); + if (!buf) + return false; + /* Complete buffer with a neutral frame so dequeuers keep running. */ + { + struct vb2_v4l2_buffer *vb2v = &buf->vb; + void *dst = vb2_plane_vaddr(&vb2v->vb2_buf, 0); + + if (dst) + memset(dst, 0x10, v->pix.sizeimage); + vb2_set_plane_payload(&vb2v->vb2_buf, 0, v->pix.sizeimage); + vb2v->sequence = (u32)atomic_inc_return(&v->sequence_number); + vb2v->vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_DONE); + } + if (have_next && next) { + dma_addr_t dma = + vb2_dma_contig_plane_dma_addr(&next->vb.vb2_buf, 0); + hws_program_dma_for_addr(hws, v->channel_index, dma); + hws_set_dma_doorbell(hws, v->channel_index, dma, + tag ? tag : "nosignal_zero"); + doorbell = true; + } + if (doorbell) { + wmb(); /* ensure descriptors visible before enabling capture */ + hws_enable_video_capture(hws, v->channel_index, true); + } + return true; +} + +static int hws_ctrls_init(struct hws_video *vid) +{ + struct v4l2_ctrl_handler *hdl = &vid->control_handler; + + /* Create BCHS controls. */ + v4l2_ctrl_handler_init(hdl, 4); + + vid->ctrl_brightness = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, + V4L2_CID_BRIGHTNESS, + MIN_VAMP_BRIGHTNESS_UNITS, + MAX_VAMP_BRIGHTNESS_UNITS, 1, + HWS_BRIGHTNESS_DEFAULT); + + vid->ctrl_contrast = + v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, V4L2_CID_CONTRAST, + MIN_VAMP_CONTRAST_UNITS, MAX_VAMP_CONTRAST_UNITS, + 1, HWS_CONTRAST_DEFAULT); + + vid->ctrl_saturation = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, + V4L2_CID_SATURATION, + MIN_VAMP_SATURATION_UNITS, + MAX_VAMP_SATURATION_UNITS, 1, + HWS_SATURATION_DEFAULT); + + vid->ctrl_hue = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, V4L2_CID_HUE, + MIN_VAMP_HUE_UNITS, + MAX_VAMP_HUE_UNITS, 1, + HWS_HUE_DEFAULT); + if (hdl->error) { + int err = hdl->error; + + v4l2_ctrl_handler_free(hdl); + return err; + } + return 0; +} + +int hws_video_init_channel(struct hws_pcie_dev *pdev, int ch) +{ + struct hws_video *vid; + + /* basic sanity */ + if (!pdev || ch < 0 || ch >= pdev->max_channels) + return -EINVAL; + + vid = &pdev->video[ch]; + + /* hard reset the per-channel struct (safe here since we init everything next) */ + memset(vid, 0, sizeof(*vid)); + + /* identity */ + vid->parent = pdev; + vid->channel_index = ch; + + /* locks & lists */ + mutex_init(&vid->state_lock); + spin_lock_init(&vid->irq_lock); + INIT_LIST_HEAD(&vid->capture_queue); + atomic_set(&vid->sequence_number, 0); + vid->active = NULL; + + /* DMA watchdog removed; retain counters for diagnostics */ + vid->timeout_count = 0; + vid->error_count = 0; + + vid->queued_count = 0; + vid->window_valid = false; + + /* Default format. */ + vid->pix.width = 1920; + vid->pix.height = 1080; + vid->pix.fourcc = V4L2_PIX_FMT_YUYV; + vid->pix.bytesperline = ALIGN(vid->pix.width * 2, 64); + vid->pix.sizeimage = vid->pix.bytesperline * vid->pix.height; + vid->pix.field = V4L2_FIELD_NONE; + vid->pix.colorspace = V4L2_COLORSPACE_REC709; + vid->pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + vid->pix.quantization = V4L2_QUANTIZATION_FULL_RANGE; + vid->pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; + vid->pix.interlaced = false; + vid->pix.half_size = vid->pix.sizeimage / 2; + hws_set_current_dv_timings(vid, vid->pix.width, + vid->pix.height, vid->pix.interlaced); + vid->current_fps = 60; + + /* color controls default (mid-scale) */ + vid->current_brightness = 0x80; + vid->current_contrast = 0x80; + vid->current_saturation = 0x80; + vid->current_hue = 0x80; + + /* capture state */ + vid->cap_active = false; + vid->stop_requested = false; + vid->last_buf_half_toggle = 0; + vid->half_seen = false; + vid->signal_loss_cnt = 0; + + /* Create BCHS + DV power-present as modern controls */ + { + int err = hws_ctrls_init(vid); + + if (err) { + dev_err(&pdev->pdev->dev, + "v4l2 ctrl init failed on ch%d: %d\n", ch, err); + return err; + } + } + + return 0; +} + +static void hws_video_drain_queue_locked(struct hws_video *vid) +{ + /* Return in-flight first */ + if (vid->active) { + vb2_buffer_done(&vid->active->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vid->active = NULL; + } + + /* Then everything queued */ + while (!list_empty(&vid->capture_queue)) { + struct hwsvideo_buffer *b = + list_first_entry(&vid->capture_queue, + struct hwsvideo_buffer, + list); + list_del_init(&b->list); + vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } +} + +static void hws_video_release_registration(struct hws_video *vid) +{ + if (vid->buffer_queue.ops) { + vb2_queue_release(&vid->buffer_queue); + vid->buffer_queue.ops = NULL; + } + + if (!vid->video_device) + return; + + if (video_is_registered(vid->video_device)) + vb2_video_unregister_device(vid->video_device); + else + video_device_release(vid->video_device); + vid->video_device = NULL; +} + +static void hws_video_collect_done_locked(struct hws_video *vid, + struct list_head *done) +{ + struct hwsvideo_buffer *b; + + if (vid->active) { + if (!list_node_unlinked(&vid->active->list)) { + list_move_tail(&vid->active->list, done); + } else { + INIT_LIST_HEAD(&vid->active->list); + list_add_tail(&vid->active->list, done); + } + vid->active = NULL; + } + + if (vid->next_prepared) { + if (!list_node_unlinked(&vid->next_prepared->list)) { + list_move_tail(&vid->next_prepared->list, done); + } else { + INIT_LIST_HEAD(&vid->next_prepared->list); + list_add_tail(&vid->next_prepared->list, done); + } + vid->next_prepared = NULL; + } + + while (!list_empty(&vid->capture_queue)) { + b = list_first_entry(&vid->capture_queue, struct hwsvideo_buffer, + list); + list_move_tail(&b->list, done); + } + + vid->queued_count = 0; +} + +void hws_video_cleanup_channel(struct hws_pcie_dev *pdev, int ch) +{ + struct hws_video *vid; + unsigned long flags; + + if (!pdev || ch < 0 || ch >= pdev->max_channels) + return; + + vid = &pdev->video[ch]; + + /* 1) Stop HW best-effort for this channel */ + hws_enable_video_capture(vid->parent, vid->channel_index, false); + + /* 2) Flip software state so IRQ/BH will be no-ops if they run */ + WRITE_ONCE(vid->stop_requested, true); + WRITE_ONCE(vid->cap_active, false); + + /* 3) Ensure the IRQ handler finished any in-flight completions */ + if (vid->parent && vid->parent->irq >= 0) + synchronize_irq(vid->parent->irq); + + /* 4) Drain SW capture queue & in-flight under lock */ + spin_lock_irqsave(&vid->irq_lock, flags); + hws_video_drain_queue_locked(vid); + spin_unlock_irqrestore(&vid->irq_lock, flags); + + /* 5) Release VB2 queue if initialized */ + hws_video_release_registration(vid); + + /* 6) Free V4L2 controls */ + v4l2_ctrl_handler_free(&vid->control_handler); + + /* 8) Reset simple state; do not memset the whole struct here. */ + mutex_destroy(&vid->state_lock); + INIT_LIST_HEAD(&vid->capture_queue); + vid->active = NULL; + vid->stop_requested = false; + vid->last_buf_half_toggle = 0; + vid->half_seen = false; + vid->signal_loss_cnt = 0; +} + +/* Convenience cast */ +static inline struct hwsvideo_buffer *to_hwsbuf(struct vb2_buffer *vb) +{ + return container_of(to_vb2_v4l2_buffer(vb), struct hwsvideo_buffer, vb); +} + +static int hws_buf_init(struct vb2_buffer *vb) +{ + struct hwsvideo_buffer *b = to_hwsbuf(vb); + + INIT_LIST_HEAD(&b->list); + return 0; +} + +static void hws_buf_finish(struct vb2_buffer *vb) +{ + /* vb2 core handles cache maintenance for dma-contig buffers */ + (void)vb; +} + +static void hws_buf_cleanup(struct vb2_buffer *vb) +{ + struct hwsvideo_buffer *b = to_hwsbuf(vb); + + if (!list_empty(&b->list)) + list_del_init(&b->list); +} + +void hws_program_dma_for_addr(struct hws_pcie_dev *hws, unsigned int ch, + dma_addr_t dma) +{ + struct hws_video *vid = &hws->video[ch]; + + hws_program_dma_window(vid, dma); +} + +void hws_enable_video_capture(struct hws_pcie_dev *hws, unsigned int chan, + bool on) +{ + u32 status; + + if (!hws || hws->pci_lost || chan >= hws->max_channels) + return; + + status = readl(hws->bar0_base + HWS_REG_VCAP_ENABLE); + status = on ? (status | BIT(chan)) : (status & ~BIT(chan)); + writel(status, hws->bar0_base + HWS_REG_VCAP_ENABLE); + (void)readl(hws->bar0_base + HWS_REG_VCAP_ENABLE); + + WRITE_ONCE(hws->video[chan].cap_active, on); + + dev_dbg(&hws->pdev->dev, "vcap %s ch%u (reg=0x%08x)\n", + on ? "ON" : "OFF", chan, status); +} + +static void hws_seed_dma_windows(struct hws_pcie_dev *hws) +{ + const u32 addr_mask = PCI_E_BAR_ADD_MASK; + const u32 addr_low_mask = PCI_E_BAR_ADD_LOWMASK; + u32 table = 0x208; /* one 64-bit entry per channel */ + unsigned int ch; + + if (!hws || !hws->bar0_base) + return; + + /* If cur_max_video_ch is not set yet, default to max_channels. */ + if (!hws->cur_max_video_ch || hws->cur_max_video_ch > hws->max_channels) + hws->cur_max_video_ch = hws->max_channels; + + for (ch = 0; ch < hws->cur_max_video_ch; ch++, table += 8) { + if (!hws->scratch_vid[ch].cpu) + continue; + + /* Program 64-bit BAR remap entry for this channel */ + { + dma_addr_t p = hws->scratch_vid[ch].dma; + u32 lo = lower_32_bits(p) & addr_mask; + u32 hi = upper_32_bits(p); + u32 pci_addr_low = lower_32_bits(p) & addr_low_mask; + + writel_relaxed(hi, + hws->bar0_base + PCI_ADDR_TABLE_BASE + + table); + writel_relaxed(lo, + hws->bar0_base + PCI_ADDR_TABLE_BASE + + table + PCIE_BARADDROFSIZE); + + /* Per-channel AXI base + PCI low */ + writel_relaxed((ch + 1) * PCIEBAR_AXI_BASE + + pci_addr_low, + hws->bar0_base + CVBS_IN_BUF_BASE + + ch * PCIE_BARADDROFSIZE); + + /* Half-frame length in /16 units. + * Prefer the current channel's computed half_size if available. + * Fall back to half of the probe-owned scratch buffer. + */ + { + u32 half_bytes = hws->video[ch].pix.half_size ? + hws->video[ch].pix.half_size : + (u32)(hws->scratch_vid[ch].size / 2); + writel_relaxed(half_bytes / 16, + hws->bar0_base + + CVBS_IN_BUF_BASE2 + + ch * PCIE_BARADDROFSIZE); + } + } + } + + /* Post writes so device sees them before we move on */ + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); +} + +static void hws_ack_all_irqs(struct hws_pcie_dev *hws) +{ + u32 st = readl(hws->bar0_base + HWS_REG_INT_STATUS); + + if (st) { + writel(st, hws->bar0_base + HWS_REG_INT_STATUS); /* W1C */ + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); + } +} + +static void hws_open_irq_fabric(struct hws_pcie_dev *hws) +{ + /* Route all sources to vector 0. */ + writel(0x00000000, hws->bar0_base + PCIE_INT_DEC_REG_BASE); + (void)readl(hws->bar0_base + PCIE_INT_DEC_REG_BASE); + + /* Enable the PCIe bridge. */ + writel(0x00000001, hws->bar0_base + PCIEBR_EN_REG_BASE); + (void)readl(hws->bar0_base + PCIEBR_EN_REG_BASE); + + /* Open the global/bridge gate (legacy 0x3FFFF) */ + writel(HWS_INT_EN_MASK, hws->bar0_base + INT_EN_REG_BASE); + (void)readl(hws->bar0_base + INT_EN_REG_BASE); +} + +void hws_init_video_sys(struct hws_pcie_dev *hws, bool enable) +{ + int i; + + if (hws->start_run && !enable) + return; + + /* 1) reset the decoder mode register to 0 */ + writel(0x00000000, hws->bar0_base + HWS_REG_DEC_MODE); + hws_seed_dma_windows(hws); + + /* 3) on a full reset, clear all per-channel status and indices */ + if (!enable) { + for (i = 0; i < hws->max_channels; i++) { + /* helpers to arm/disable capture engines */ + hws_enable_video_capture(hws, i, false); + } + } + + /* 4) Start run: set bit31, wait a bit, then program low 24 bits. */ + writel(0x80000000, hws->bar0_base + HWS_REG_DEC_MODE); + writel(0x80FFFFFF, hws->bar0_base + HWS_REG_DEC_MODE); + writel(0x13, hws->bar0_base + HWS_REG_DEC_MODE); + hws_ack_all_irqs(hws); + hws_open_irq_fabric(hws); + /* 6) record that we're now running */ + hws->start_run = true; +} + +int hws_check_card_status(struct hws_pcie_dev *hws) +{ + u32 status; + + if (!hws || !hws->bar0_base) + return -ENODEV; + + status = readl(hws->bar0_base + HWS_REG_SYS_STATUS); + + /* Common device-missing pattern. */ + if (status == 0xFFFFFFFF) { + hws->pci_lost = true; + dev_err(&hws->pdev->dev, "PCIe device not responding\n"); + return -ENODEV; + } + + /* If RUN/READY bit (bit0) is not set, reinitialize the video core. */ + if (!(status & BIT(0))) { + dev_dbg(&hws->pdev->dev, + "SYS_STATUS not ready (0x%08x), reinitializing\n", + status); + hws_init_video_sys(hws, true); + } + + return 0; +} + +void check_video_format(struct hws_pcie_dev *pdx) +{ + int i; + + for (i = 0; i < pdx->cur_max_video_ch; i++) { + bool interlace = false; + + if (!hws_read_active_state(pdx, i, &interlace)) { + /* No active video; optionally feed neutral frames to keep streaming. */ + if (pdx->video[i].signal_loss_cnt == 0) + pdx->video[i].signal_loss_cnt = 1; + if (READ_ONCE(pdx->video[i].cap_active)) + hws_force_no_signal_frame(&pdx->video[i], + "monitor_nosignal"); + } else { + if (pdx->hw_ver > 0) + handle_hwv2_path(pdx, i); + else + /* Legacy path stub; see handle_legacy_path() comment. */ + handle_legacy_path(pdx, i); + + update_live_resolution(pdx, i, interlace); + pdx->video[i].signal_loss_cnt = 0; + } + } +} + +static inline void hws_write_if_diff(struct hws_pcie_dev *hws, u32 reg_off, + u32 new_val) +{ + void __iomem *addr; + u32 old; + + if (!hws || !hws->bar0_base) + return; + + addr = hws->bar0_base + reg_off; + + old = readl(addr); + /* Treat all-ones as device gone; avoid writing garbage. */ + if (old == 0xFFFFFFFF) { + hws->pci_lost = true; + return; + } + + if (old != new_val) { + writel(new_val, addr); + /* Post the write on some bridges / enforce ordering. */ + (void)readl(addr); + } +} + +static bool hws_read_active_state(struct hws_pcie_dev *pdx, unsigned int ch, + bool *interlace) +{ + u32 reg; + bool active; + + if (ch >= pdx->cur_max_video_ch) + return false; + + reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS); + active = !!(reg & BIT(ch)); + if (interlace) + *interlace = !!(reg & BIT(8 + ch)); + return active; +} + +/* Modern hardware path: keep HW registers in sync with current per-channel + * software state. + */ +static void handle_hwv2_path(struct hws_pcie_dev *hws, unsigned int ch) +{ + struct hws_video *vid; + u32 reg, in_fps, cur_out_res, want_out_res; + + if (!hws || !hws->bar0_base || ch >= hws->max_channels) + return; + + vid = &hws->video[ch]; + + /* 1) Input frame rate (read-only; log or export via debugfs if wanted) */ + in_fps = readl(hws->bar0_base + HWS_REG_FRAME_RATE(ch)); + /* dev_dbg(&hws->pdev->dev, "ch%u input fps=%u\n", ch, in_fps); */ + (void)in_fps; + + /* 2) Output resolution programming. + * For now, mirror the current format to OUT_RES. + */ + want_out_res = (vid->pix.height << 16) | vid->pix.width; + cur_out_res = readl(hws->bar0_base + HWS_REG_OUT_RES(ch)); + if (cur_out_res != want_out_res) + hws_write_if_diff(hws, HWS_REG_OUT_RES(ch), want_out_res); + + /* 3) Output FPS: only program if you actually track a target. + * Example heuristic (disabled by default): + * + * u32 out_fps = (vid->fmt_curr.height >= 1080) ? 60 : 30; + * hws_write_if_diff(hws, HWS_REG_OUT_FRAME_RATE(ch), out_fps); + */ + + /* 4) BCHS controls: pack from per-channel current_* fields */ + reg = readl(hws->bar0_base + HWS_REG_BCHS(ch)); + { + u8 br = reg & 0xFF; + u8 co = (reg >> 8) & 0xFF; + u8 hu = (reg >> 16) & 0xFF; + u8 sa = (reg >> 24) & 0xFF; + + if (br != vid->current_brightness || + co != vid->current_contrast || hu != vid->current_hue || + sa != vid->current_saturation) { + u32 packed = (vid->current_saturation << 24) | + (vid->current_hue << 16) | + (vid->current_contrast << 8) | + vid->current_brightness; + hws_write_if_diff(hws, HWS_REG_BCHS(ch), packed); + } + } + + /* 5) HDCP detect: read only (no cache field in your structs today) */ + reg = readl(hws->bar0_base + HWS_REG_HDCP_STATUS); + /* bool hdcp = !!(reg & BIT(ch)); // use if you later add a field/control */ +} + +static void handle_legacy_path(struct hws_pcie_dev *hws, unsigned int ch) +{ + /* + * Legacy (hw_ver == 0) expected behavior: + * - A per-channel SW FPS accumulator incremented on each VDONE. + * - A once-per-second poll mapped the count to discrete FPS: + * >55*2 => 60, >45*2 => 50, >25*2 => 30, >20*2 => 25, else 60, + * then reset the accumulator to 0. + * - The *2 factor assumed VDONE fired per-field; if legacy VDONE is + * per-frame, drop the factor. + * + * Current code keeps this path as a no-op; vid->current_fps stays at the + * default or mode-derived value. If accurate legacy FPS reporting is + * needed (V4L2 g_parm/timeperframe), reintroduce the accumulator in the + * IRQ path and perform the mapping/reset here. + * + * No-op by default. If you introduce a SW FPS accumulator, map it here. + * + * Example skeleton: + * + * u32 sw_rate = READ_ONCE(hws->sw_fps[ch]); // incremented elsewhere + * if (sw_rate > THRESHOLD) { + * u32 fps = pick_fps_from_rate(sw_rate); + * hws_write_if_diff(hws, HWS_REG_OUT_FRAME_RATE(ch), fps); + * WRITE_ONCE(hws->sw_fps[ch], 0); + * } + */ + (void)hws; + (void)ch; +} + +static void hws_video_apply_mode_change(struct hws_pcie_dev *pdx, + unsigned int ch, u16 w, u16 h, + bool interlaced, u32 fps) +{ + struct hws_video *v = &pdx->video[ch]; + unsigned long flags; + bool queue_busy; + bool geometry_changed; + struct list_head done; + struct hwsvideo_buffer *b, *tmp; + + if (!pdx || !pdx->bar0_base) + return; + if (ch >= pdx->max_channels) + return; + if (!w || !h || w > MAX_VIDEO_HW_W || + (!interlaced && h > MAX_VIDEO_HW_H) || + (interlaced && (h * 2) > MAX_VIDEO_HW_H)) + return; + if (!fps || fps == 0xFFFFFFFF || fps > 240) + fps = (h == 576) ? 50 : 60; + + geometry_changed = w != v->pix.width || h != v->pix.height || + interlaced != v->pix.interlaced; + if (!geometry_changed && fps == v->current_fps) + return; + + if (!geometry_changed) { + /* Refresh cached live timing state, but don't emit a resolution + * change event when only the frame rate changes. + */ + mutex_lock(&v->state_lock); + v->pix.interlaced = interlaced; + v->pix.field = interlaced ? V4L2_FIELD_INTERLACED : + V4L2_FIELD_NONE; + hws_set_current_dv_timings(v, w, h, interlaced); + v->current_fps = fps; + mutex_unlock(&v->state_lock); + return; + } + + if (!mutex_trylock(&v->state_lock)) + return; + + INIT_LIST_HEAD(&done); + queue_busy = vb2_is_busy(&v->buffer_queue); + + WRITE_ONCE(v->stop_requested, true); + WRITE_ONCE(v->cap_active, false); + /* Publish software stop first so the IRQ completion path sees the stop + * before we touch MMIO or the lists. Pairs with READ_ONCE() checks in the + * VDONE handler and hws_arm_next() to prevent completions while modes + * change. + */ + smp_wmb(); + + hws_enable_video_capture(pdx, ch, false); + readl(pdx->bar0_base + HWS_REG_INT_STATUS); + + if (v->parent && v->parent->irq >= 0) + synchronize_irq(v->parent->irq); + + spin_lock_irqsave(&v->irq_lock, flags); + hws_video_collect_done_locked(v, &done); + spin_unlock_irqrestore(&v->irq_lock, flags); + + /* Update software pixel state */ + v->pix.width = w; + v->pix.height = h; + v->pix.interlaced = interlaced; + hws_set_current_dv_timings(v, w, h, interlaced); + v->current_fps = fps; + + hws_calc_sizeimage(v, w, h, interlaced); + v->window_valid = false; + + /* Geometry changes require userspace renegotiation once buffers exist. + * Emit SOURCE_CHANGE, mark the queue in error, and let userspace + * STREAMOFF/REQBUFS/STREAMON rather than trying to restart capture + * with partially drained in-flight state. + */ + if (queue_busy) { + struct v4l2_event ev = { + .type = V4L2_EVENT_SOURCE_CHANGE, + }; + + ev.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION; + v4l2_event_queue(v->video_device, &ev); + vb2_queue_error(&v->buffer_queue); + } else { + WRITE_ONCE(v->stop_requested, false); + } + + /* Program HW with new resolution */ + hws_write_if_diff(pdx, HWS_REG_OUT_RES(ch), (h << 16) | w); + + /* Legacy half-buffer programming */ + writel(v->pix.half_size / 16, + pdx->bar0_base + CVBS_IN_BUF_BASE2 + ch * PCIE_BARADDROFSIZE); + (void)readl(pdx->bar0_base + CVBS_IN_BUF_BASE2 + + ch * PCIE_BARADDROFSIZE); + + /* Reset per-channel toggles/counters */ + WRITE_ONCE(v->last_buf_half_toggle, 0); + atomic_set(&v->sequence_number, 0); + + mutex_unlock(&v->state_lock); + + list_for_each_entry_safe(b, tmp, &done, list) { + list_del_init(&b->list); + vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } +} + +static void update_live_resolution(struct hws_pcie_dev *pdx, unsigned int ch, + bool interlace) +{ + u32 reg = readl(pdx->bar0_base + HWS_REG_IN_RES(ch)); + u32 fps = readl(pdx->bar0_base + HWS_REG_FRAME_RATE(ch)); + u16 res_w = reg & 0xFFFF; + u16 res_h = (reg >> 16) & 0xFFFF; + struct hws_video *vid = &pdx->video[ch]; + bool geometry_changed; + bool fps_changed; + + bool within_hw = (res_w <= MAX_VIDEO_HW_W) && + ((!interlace && res_h <= MAX_VIDEO_HW_H) || + (interlace && (res_h * 2) <= MAX_VIDEO_HW_H)); + + if (!within_hw) + return; + + geometry_changed = res_w != vid->pix.width || + res_h != vid->pix.height || + interlace != vid->pix.interlaced; + fps_changed = fps && fps != 0xFFFFFFFF && fps <= 240 && + fps != vid->current_fps; + + if (geometry_changed || fps_changed) + hws_video_apply_mode_change(pdx, ch, res_w, res_h, interlace, + fps); +} + +static int hws_open(struct file *file) +{ + return v4l2_fh_open(file); +} + +static const struct v4l2_file_operations hws_fops = { + .owner = THIS_MODULE, + .open = hws_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int hws_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + +static const struct v4l2_ioctl_ops hws_ioctl_fops = { + /* Core caps/info */ + .vidioc_querycap = hws_vidioc_querycap, + + /* Pixel format: still needed to report YUYV etc. */ + .vidioc_enum_fmt_vid_cap = hws_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = hws_vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = hws_vidioc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = hws_vidioc_try_fmt_vid_cap, + + /* Buffer queueing / streaming */ + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + /* Inputs */ + .vidioc_enum_input = hws_vidioc_enum_input, + .vidioc_g_input = hws_vidioc_g_input, + .vidioc_s_input = hws_vidioc_s_input, + + /* DV timings (HDMI/DVI/VESA modes) */ + .vidioc_query_dv_timings = hws_vidioc_query_dv_timings, + .vidioc_enum_dv_timings = hws_vidioc_enum_dv_timings, + .vidioc_g_dv_timings = hws_vidioc_g_dv_timings, + .vidioc_s_dv_timings = hws_vidioc_s_dv_timings, + .vidioc_dv_timings_cap = hws_vidioc_dv_timings_cap, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = hws_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_g_parm = hws_vidioc_g_parm, +}; + +static u32 hws_calc_sizeimage(struct hws_video *v, u16 w, u16 h, + bool interlaced) +{ + /* HWS captures packed YUYV only; stride is 16 bpp aligned to 64 bytes. */ + u32 lines = h; /* full frame lines for sizeimage */ + u32 bytesperline = ALIGN(w * 2, 64); + u32 sizeimage, half0; + + /* publish into pix, since we now carry these in-state */ + v->pix.bytesperline = bytesperline; + sizeimage = bytesperline * lines; + + half0 = sizeimage / 2; + + v->pix.sizeimage = sizeimage; + v->pix.half_size = half0; /* first half; second = sizeimage - half0 */ + v->pix.field = interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; + + return v->pix.sizeimage; +} + +static int hws_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct hws_video *vid = q->drv_priv; + + if (*nplanes) { + if (sizes[0] < vid->pix.sizeimage) + return -EINVAL; + } else { + *nplanes = 1; + sizes[0] = vid->pix.sizeimage; + } + + return 0; +} + +static int hws_buffer_prepare(struct vb2_buffer *vb) +{ + struct hws_video *vid = vb->vb2_queue->drv_priv; + struct hws_pcie_dev *hws = vid->parent; + size_t need = vid->pix.sizeimage; + dma_addr_t dma_addr; + + if (vb2_plane_size(vb, 0) < need) + return -EINVAL; + + /* Validate DMA address alignment */ + dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + if (dma_addr & 0x3F) { /* 64-byte alignment required */ + dev_err(&hws->pdev->dev, + "Buffer DMA address 0x%llx not 64-byte aligned\n", + (unsigned long long)dma_addr); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, need); + return 0; +} + +static void hws_buffer_queue(struct vb2_buffer *vb) +{ + struct hws_video *vid = vb->vb2_queue->drv_priv; + struct hwsvideo_buffer *buf = to_hwsbuf(vb); + struct hws_pcie_dev *hws = vid->parent; + unsigned long flags; + + dev_dbg(&hws->pdev->dev, + "buffer_queue(ch=%u): vb=%p sizeimage=%u q_active=%d\n", + vid->channel_index, vb, vid->pix.sizeimage, + READ_ONCE(vid->cap_active)); + + /* Initialize buffer slot */ + buf->slot = 0; + + spin_lock_irqsave(&vid->irq_lock, flags); + list_add_tail(&buf->list, &vid->capture_queue); + vid->queued_count++; + + /* If streaming and no in-flight buffer, prime HW immediately */ + if (READ_ONCE(vid->cap_active) && !vid->active) { + dma_addr_t dma_addr; + + dev_dbg(&hws->pdev->dev, + "buffer_queue(ch=%u): priming first vb=%p\n", + vid->channel_index, &buf->vb.vb2_buf); + list_del_init(&buf->list); + vid->queued_count--; + vid->active = buf; + + dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + hws_program_dma_for_addr(vid->parent, vid->channel_index, + dma_addr); + iowrite32(lower_32_bits(dma_addr), + hws->bar0_base + HWS_REG_DMA_ADDR(vid->channel_index)); + + wmb(); /* ensure descriptors visible before enabling capture */ + hws_enable_video_capture(hws, vid->channel_index, true); + hws_prime_next_locked(vid); + } else if (READ_ONCE(vid->cap_active) && vid->active) { + hws_prime_next_locked(vid); + } + spin_unlock_irqrestore(&vid->irq_lock, flags); +} + +static int hws_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct hws_video *v = q->drv_priv; + struct hws_pcie_dev *hws = v->parent; + struct hwsvideo_buffer *to_program = NULL; /* local copy */ + struct vb2_buffer *prog_vb2 = NULL; + unsigned long flags; + int ret; + + dev_dbg(&hws->pdev->dev, "start_streaming: ch=%u count=%u\n", + v->channel_index, count); + + ret = hws_check_card_status(hws); + if (ret) { + struct hwsvideo_buffer *b, *tmp; + unsigned long f; + LIST_HEAD(queued); + + spin_lock_irqsave(&v->irq_lock, f); + if (v->active) { + list_add_tail(&v->active->list, &queued); + v->active = NULL; + } + if (v->next_prepared) { + list_add_tail(&v->next_prepared->list, &queued); + v->next_prepared = NULL; + } + while (!list_empty(&v->capture_queue)) { + b = list_first_entry(&v->capture_queue, + struct hwsvideo_buffer, list); + list_move_tail(&b->list, &queued); + } + spin_unlock_irqrestore(&v->irq_lock, f); + + list_for_each_entry_safe(b, tmp, &queued, list) { + list_del_init(&b->list); + vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } + return ret; + } + (void)hws_read_active_state(hws, v->channel_index, + &v->pix.interlaced); + + lockdep_assert_held(&v->state_lock); + /* init per-stream state */ + WRITE_ONCE(v->stop_requested, false); + WRITE_ONCE(v->cap_active, true); + WRITE_ONCE(v->half_seen, false); + WRITE_ONCE(v->last_buf_half_toggle, 0); + + /* Try to prime a buffer, but it's OK if none are queued yet */ + spin_lock_irqsave(&v->irq_lock, flags); + if (!v->active && !list_empty(&v->capture_queue)) { + to_program = list_first_entry(&v->capture_queue, + struct hwsvideo_buffer, list); + list_del_init(&to_program->list); + v->queued_count--; + v->active = to_program; + prog_vb2 = &to_program->vb.vb2_buf; + dev_dbg(&hws->pdev->dev, + "start_streaming: ch=%u took buffer %p\n", + v->channel_index, to_program); + } + spin_unlock_irqrestore(&v->irq_lock, flags); + + /* Only program/enable HW if we actually have a buffer */ + if (to_program) { + if (!prog_vb2) + prog_vb2 = &to_program->vb.vb2_buf; + { + dma_addr_t dma_addr; + + dma_addr = vb2_dma_contig_plane_dma_addr(prog_vb2, 0); + hws_program_dma_for_addr(hws, v->channel_index, dma_addr); + iowrite32(lower_32_bits(dma_addr), + hws->bar0_base + + HWS_REG_DMA_ADDR(v->channel_index)); + dev_dbg(&hws->pdev->dev, + "start_streaming: ch=%u programmed buffer %p dma=0x%08x\n", + v->channel_index, to_program, + lower_32_bits(dma_addr)); + (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); + } + + wmb(); /* ensure descriptors visible before enabling capture */ + hws_enable_video_capture(hws, v->channel_index, true); + { + unsigned long pf; + + spin_lock_irqsave(&v->irq_lock, pf); + hws_prime_next_locked(v); + spin_unlock_irqrestore(&v->irq_lock, pf); + } + } else { + dev_dbg(&hws->pdev->dev, + "start_streaming: ch=%u no buffer yet (will arm on QBUF)\n", + v->channel_index); + } + + return 0; +} + +static void hws_log_video_state(struct hws_video *v, const char *action, + const char *phase) +{ + struct hws_pcie_dev *hws = v->parent; + unsigned long flags; + unsigned int queued = 0; + unsigned int tracked = 0; + unsigned int seq = 0; + struct hwsvideo_buffer *b; + bool streaming = vb2_is_streaming(&v->buffer_queue); + bool cap_active; + bool stop_requested; + struct hwsvideo_buffer *active; + struct hwsvideo_buffer *next_prepared; + + spin_lock_irqsave(&v->irq_lock, flags); + list_for_each_entry(b, &v->capture_queue, list) + queued++; + cap_active = READ_ONCE(v->cap_active); + stop_requested = READ_ONCE(v->stop_requested); + active = v->active; + next_prepared = v->next_prepared; + tracked = v->queued_count; + seq = (u32)atomic_read(&v->sequence_number); + spin_unlock_irqrestore(&v->irq_lock, flags); + + dev_dbg(&hws->pdev->dev, + "video:%s:%s ch=%u streaming=%d cap=%d stop=%d active=%p next=%p queued=%u tracked=%u seq=%u\n", + action, phase, v->channel_index, streaming, cap_active, + stop_requested, active, next_prepared, queued, tracked, seq); +} + +static void hws_stop_streaming(struct vb2_queue *q) +{ + struct hws_video *v = q->drv_priv; + struct hws_pcie_dev *hws = v->parent; + unsigned long flags; + struct hwsvideo_buffer *b, *tmp; + LIST_HEAD(done); + unsigned int done_cnt = 0; + u64 start_ns = ktime_get_mono_fast_ns(); + + hws_log_video_state(v, "streamoff", "begin"); + + /* 1) Quiesce SW/HW first */ + lockdep_assert_held(&v->state_lock); + WRITE_ONCE(v->cap_active, false); + WRITE_ONCE(v->stop_requested, true); + + hws_enable_video_capture(v->parent, v->channel_index, false); + + /* 2) Collect in-flight + queued under the IRQ lock */ + spin_lock_irqsave(&v->irq_lock, flags); + hws_video_collect_done_locked(v, &done); + spin_unlock_irqrestore(&v->irq_lock, flags); + + /* 3) Complete outside the lock */ + list_for_each_entry_safe(b, tmp, &done, list) { + /* Unlink from 'done' before completing */ + list_del_init(&b->list); + vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR); + done_cnt++; + } + dev_dbg(&hws->pdev->dev, + "video:streamoff:done ch=%u completed=%u (%lluus)\n", + v->channel_index, done_cnt, hws_elapsed_us(start_ns)); + hws_log_video_state(v, "streamoff", "end"); +} + +static const struct vb2_ops hwspcie_video_qops = { + .queue_setup = hws_queue_setup, + .buf_prepare = hws_buffer_prepare, + .buf_init = hws_buf_init, + .buf_finish = hws_buf_finish, + .buf_cleanup = hws_buf_cleanup, + .buf_queue = hws_buffer_queue, + .start_streaming = hws_start_streaming, + .stop_streaming = hws_stop_streaming, +}; + +int hws_video_register(struct hws_pcie_dev *dev) +{ + int i, ret; + + ret = v4l2_device_register(&dev->pdev->dev, &dev->v4l2_device); + if (ret) { + dev_err(&dev->pdev->dev, "v4l2_device_register failed: %d\n", + ret); + return ret; + } + + for (i = 0; i < dev->cur_max_video_ch; i++) { + struct hws_video *ch = &dev->video[i]; + struct video_device *vdev; + struct vb2_queue *q; + + /* hws_video_init_channel() should have set: + * - ch->parent, ch->channel_index + * - locks (state_lock, irq_lock) + * - capture_queue (INIT_LIST_HEAD) + * - control_handler + controls + * - fmt_curr (width/height) + * Do not reinitialize any of those here. + */ + + vdev = video_device_alloc(); + if (!vdev) { + dev_err(&dev->pdev->dev, + "video_device_alloc ch%u failed\n", i); + ret = -ENOMEM; + goto err_unwind; + } + ch->video_device = vdev; + + /* Basic V4L2 node setup */ + snprintf(vdev->name, sizeof(vdev->name), "%s-hdmi%u", + KBUILD_MODNAME, i); + vdev->v4l2_dev = &dev->v4l2_device; + vdev->fops = &hws_fops; + vdev->ioctl_ops = &hws_ioctl_fops; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + vdev->lock = &ch->state_lock; /* serialize file ops */ + vdev->ctrl_handler = &ch->control_handler; + vdev->vfl_dir = VFL_DIR_RX; + vdev->release = video_device_release; + if (ch->control_handler.error) { + ret = ch->control_handler.error; + goto err_unwind; + } + video_set_drvdata(vdev, ch); + + /* vb2 queue init (dma-contig) */ + q = &ch->buffer_queue; + memset(q, 0, sizeof(*q)); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = ch; + q->buf_struct_size = sizeof(struct hwsvideo_buffer); + q->ops = &hwspcie_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &ch->state_lock; + q->min_queued_buffers = 1; + q->dev = &dev->pdev->dev; + + ret = vb2_queue_init(q); + vdev->queue = q; + if (ret) { + dev_err(&dev->pdev->dev, + "vb2_queue_init ch%u failed: %d\n", i, ret); + goto err_unwind; + } + + /* Make controls live (no-op if none or already set up) */ + if (ch->control_handler.error) { + ret = ch->control_handler.error; + dev_err(&dev->pdev->dev, + "ctrl handler ch%u error: %d\n", i, ret); + goto err_unwind; + } + v4l2_ctrl_handler_setup(&ch->control_handler); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(&dev->pdev->dev, + "video_register_device ch%u failed: %d\n", i, + ret); + goto err_unwind; + } + } + + return 0; + +err_unwind: + for (; i >= 0; i--) { + struct hws_video *ch = &dev->video[i]; + + hws_video_release_registration(ch); + } + v4l2_device_unregister(&dev->v4l2_device); + return ret; +} + +void hws_video_unregister(struct hws_pcie_dev *dev) +{ + int i; + + if (!dev) + return; + + for (i = 0; i < dev->cur_max_video_ch; i++) { + struct hws_video *ch = &dev->video[i]; + + hws_video_release_registration(ch); + v4l2_ctrl_handler_free(&ch->control_handler); + } + v4l2_device_unregister(&dev->v4l2_device); +} + +int hws_video_quiesce(struct hws_pcie_dev *hws, const char *reason) +{ + int i, ret = 0; + u64 start_ns = ktime_get_mono_fast_ns(); + + dev_dbg(&hws->pdev->dev, "video:%s:begin channels=%u\n", reason, + hws->cur_max_video_ch); + for (i = 0; i < hws->cur_max_video_ch; i++) { + struct hws_video *vid = &hws->video[i]; + struct vb2_queue *q = &vid->buffer_queue; + u64 ch_start_ns = ktime_get_mono_fast_ns(); + bool streaming; + + if (!q || !q->ops) { + dev_dbg(&hws->pdev->dev, + "video:%s:ch=%d skipped queue-unavailable\n", + reason, i); + continue; + } + + streaming = vb2_is_streaming(q); + hws_log_video_state(vid, reason, "channel"); + if (streaming) { + /* Stop via vb2, which runs .stop_streaming. */ + int r = vb2_streamoff(q, q->type); + + dev_dbg(&hws->pdev->dev, + "video:%s:ch=%d streamoff ret=%d (%lluus)\n", + reason, i, r, hws_elapsed_us(ch_start_ns)); + if (r && !ret) + ret = r; + } else { + dev_dbg(&hws->pdev->dev, + "video:%s:ch=%d idle (%lluus)\n", + reason, i, hws_elapsed_us(ch_start_ns)); + } + } + dev_dbg(&hws->pdev->dev, "video:%s:done ret=%d (%lluus)\n", reason, + ret, hws_elapsed_us(start_ns)); + return ret; +} + +void hws_video_pm_resume(struct hws_pcie_dev *hws) +{ + /* Nothing mandatory to do here for vb2; userspace will STREAMON + * again when ready. + */ +} diff --git a/drivers/media/pci/hws/hws_video.h b/drivers/media/pci/hws/hws_video.h new file mode 100644 index 000000000000..4feaf5b2f5a9 --- /dev/null +++ b/drivers/media/pci/hws/hws_video.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef HWS_VIDEO_H +#define HWS_VIDEO_H + +struct hws_video; + +int hws_video_register(struct hws_pcie_dev *dev); +void hws_video_unregister(struct hws_pcie_dev *dev); +void hws_enable_video_capture(struct hws_pcie_dev *hws, + unsigned int chan, + bool on); +void hws_prime_next_locked(struct hws_video *vid); + +int hws_video_init_channel(struct hws_pcie_dev *pdev, int ch); +void hws_video_cleanup_channel(struct hws_pcie_dev *pdev, int ch); +void check_video_format(struct hws_pcie_dev *pdx); +int hws_check_card_status(struct hws_pcie_dev *hws); +void hws_init_video_sys(struct hws_pcie_dev *hws, bool enable); + +void hws_program_dma_for_addr(struct hws_pcie_dev *hws, + unsigned int ch, + dma_addr_t dma); +void hws_set_dma_doorbell(struct hws_pcie_dev *hws, unsigned int ch, + dma_addr_t dma, const char *tag); + +int hws_video_quiesce(struct hws_pcie_dev *hws, const char *reason); +void hws_video_pm_resume(struct hws_pcie_dev *hws); + +#endif From 07f38a4942efc6937dfb412592705c1c54258e43 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 6 May 2026 18:53:24 -0700 Subject: [PATCH 147/394] media: gspca: use module_usb_driver() Nothing interesting happens in _init and _exit. Just use the macro to simplify the code slightly. Signed-off-by: Rosen Penev Signed-off-by: Hans Verkuil --- drivers/media/usb/gspca/touptek.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/media/usb/gspca/touptek.c b/drivers/media/usb/gspca/touptek.c index dde311c25d9b..734644d928ab 100644 --- a/drivers/media/usb/gspca/touptek.c +++ b/drivers/media/usb/gspca/touptek.c @@ -709,19 +709,4 @@ static struct usb_driver sd_driver = { #endif }; -static int __init sd_mod_init(void) -{ - int ret; - - ret = usb_register(&sd_driver); - if (ret < 0) - return ret; - return 0; -} -static void __exit sd_mod_exit(void) -{ - usb_deregister(&sd_driver); -} - -module_init(sd_mod_init); -module_exit(sd_mod_exit); +module_usb_driver(sd_driver); From d5b50055338e131a1a99f923ebb0361974a00f36 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Thu, 7 May 2026 02:22:13 +0000 Subject: [PATCH 148/394] media: tegra-video: vi: fix invalid u32 return value in format lookup tegra_get_format_fourcc_by_idx() returns a u32 but uses -EINVAL to signal an out-of-bounds index. This results in a large unsigned value being returned, which may be interpreted as a valid fourcc. Returning 0 is not a valid fourcc either. This condition should never happen, so use WARN_ON_ONCE() to catch unexpected out-of-bounds access and return a valid fallback format instead. Suggested-by: Hans Verkuil Fixes: 3d8a97eabef0 ("media: tegra-video: Add Tegra210 Video input driver") Cc: stable@vger.kernel.org Reviewed-by: Luca Ceresoli Signed-off-by: Hungyu Lin Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/vi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index f14cdc7b5211..456134a9e8cf 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -80,8 +80,8 @@ static int tegra_get_format_idx_by_code(struct tegra_vi *vi, static u32 tegra_get_format_fourcc_by_idx(struct tegra_vi *vi, unsigned int index) { - if (index >= vi->soc->nformats) - return -EINVAL; + if (WARN_ON_ONCE(index >= vi->soc->nformats)) + return vi->soc->video_formats[0].fourcc; return vi->soc->video_formats[index].fourcc; } From 5328dd0ce2d5db943393c9222dbcd2bd41fcba35 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:06 +0000 Subject: [PATCH 149/394] media: v4l2-dev: Add range check for vdev->minor If the fixed minor ranges are not properly set we could end up in a situation where the calculated minor is invalid. Add a check for this in the code to make it more robust. This check also fixes the following false positive smatch warning: drivers/media/v4l2-core/v4l2-dev.c:1036 __video_register_device() error: buffer overflow 'video_devices' 256 <= 288 drivers/media/v4l2-core/v4l2-dev.c:1043 __video_register_device() error: buffer overflow 'video_devices' 256 <= 288 drivers/media/v4l2-core/v4l2-dev.c:1101 __video_register_device() error: buffer overflow 'video_devices' 256 <= 288 Reviewed-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-dev.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 6ce623a1245a..5516b2bbb08f 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -1032,6 +1032,11 @@ int __video_register_device(struct video_device *vdev, vdev->minor = i + minor_offset; vdev->num = nr; + if (WARN_ON(vdev->minor >= VIDEO_NUM_DEVICES)) { + mutex_unlock(&videodev_lock); + return -EINVAL; + } + /* Should not happen since we thought this minor was free */ if (WARN_ON(video_devices[vdev->minor])) { mutex_unlock(&videodev_lock); From 508afd8060e83182e5c2f88385b77f598f6a3d0d Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:07 +0000 Subject: [PATCH 150/394] media: i2c: mt9p031: Rewrite assignment to make smatch happy The current code makes smatch a bit uncomfortable: drivers/media/i2c/mt9p031.c:799 mt9p031_s_ctrl() warn: assigning (-1952) to unsigned variable 'data' Probably because smatch is not clever enough (yet). Do a simple rewrite to make sure that smatch understands what we are doing here. Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/i2c/mt9p031.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index ea5d43d925ff..8dc57eeba606 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -796,7 +796,8 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) data = (1 << 6) | (ctrl->val >> 1); } else { ctrl->val &= ~7; - data = ((ctrl->val - 64) << 5) | (1 << 6) | 32; + data = ((ctrl->val - 64) >> 3) & 0x7f; + data = (data << 8) | (1 << 6) | 32; } return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data); From ddf9d68a368d8cae5510923a99bd2d9a7275a091 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:08 +0000 Subject: [PATCH 151/394] media: i2c: adv7604: Add range checks for chip info If the driver's chip information is invalid we can end up accessing an invalid memory region. This fixes the following false positive smatch errors: drivers/media/i2c/adv7604.c:3672 adv76xx_probe() error: buffer overflow 'state->pads' 7 <= 4294967294 drivers/media/i2c/adv7604.c:3673 adv76xx_probe() error: buffer overflow 'state->pads' 7 <= u32max Reviewed-by: Hans Verkuil Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/i2c/adv7604.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 67116a4ef134..ae75982fb514 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -3668,6 +3668,12 @@ static int adv76xx_probe(struct i2c_client *client) state->source_pad = state->info->num_dv_ports + (state->info->has_afe ? 2 : 0); + if (WARN_ON(state->source_pad >= ADV76XX_PAD_MAX)) { + err = -EINVAL; + v4l2_err(sd, "invalid chip info\n"); + goto err_i2c; + } + for (i = 0; i < state->source_pad; ++i) state->pads[i].flags = MEDIA_PAD_FL_SINK; state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE; From 79aef69bb0903616f4867f0168aea717a11c439c Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:09 +0000 Subject: [PATCH 152/394] media: chips-media: wave5: Add range checks for dec_output_info If the driver's dec_output_info contains invalid data the driver can write in invalid memory. Add a range check for that. This fixes this smatch error: drivers/media/platform/chips-media/wave5/wave5-vpuapi.c:588 wave5_vpu_dec_get_output_info() error: buffer overflow 'inst->frame_buf' 64 <= 127 Signed-off-by: Ricardo Ribalda Reviewed-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../media/platform/chips-media/wave5/wave5-vpuapi.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c index d26ffc942219..f77abd5e122a 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c @@ -584,8 +584,15 @@ int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_i p_dec_info->num_of_decoding_fbs : p_dec_info->num_of_display_fbs; if (info->index_frame_display >= 0 && - info->index_frame_display < (int)max_dec_index) - info->disp_frame = inst->frame_buf[val + info->index_frame_display]; + info->index_frame_display < (int)max_dec_index) { + u32 idx = val + info->index_frame_display; + + if (WARN_ON(idx >= MAX_REG_FRAME)) { + ret = -EINVAL; + goto err_out; + } + info->disp_frame = inst->frame_buf[idx]; + } info->rd_ptr = p_dec_info->stream_rd_ptr; info->wr_ptr = p_dec_info->stream_wr_ptr; From c32fe4c4918c9aa49f61359e3b42619c4d8686de Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:10 +0000 Subject: [PATCH 153/394] media: staging: ipu3-imgu: Add range check for imgu_css_cfg_acc_stripe If the driver's stripe information is invalid it can result in an integer underflow. Add a range check to avoid this kind of error. This patch fixes the following smatch error: drivers/staging/media/ipu3/ipu3-css-params.c:1792 imgu_css_cfg_acc_stripe() warn: 'acc->stripe.bds_out_stripes[0]->width - 2 * f' 4294967168 can't fit into 65535 'acc->stripe.bds_out_stripes[1]->offset' Cc: stable@vger.kernel.org Fixes: e11110a5b744 ("media: staging/intel-ipu3: css: Compute and program ccs") Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/staging/media/ipu3/ipu3-css-params.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/ipu3/ipu3-css-params.c b/drivers/staging/media/ipu3/ipu3-css-params.c index 2c48d57a3180..92cce31e35c5 100644 --- a/drivers/staging/media/ipu3/ipu3-css-params.c +++ b/drivers/staging/media/ipu3/ipu3-css-params.c @@ -1770,6 +1770,8 @@ static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe, acc->stripe.bds_out_stripes[0].width = ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, f); } else { + u32 offset; + /* Image processing is divided into two stripes */ acc->stripe.bds_out_stripes[0].width = acc->stripe.bds_out_stripes[1].width = @@ -1788,8 +1790,10 @@ static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe, acc->stripe.bds_out_stripes[1].width += f; } /* Overlap between stripes is IPU3_UAPI_ISP_VEC_ELEMS * 4 */ - acc->stripe.bds_out_stripes[1].offset = - acc->stripe.bds_out_stripes[0].width - 2 * f; + offset = acc->stripe.bds_out_stripes[0].width - 2 * f; + if (offset > 65535) + return -EINVAL; + acc->stripe.bds_out_stripes[1].offset = offset; } acc->stripe.effective_stripes[0].height = From 9724164f71974a2a44a5e026614fbcc05bab6d91 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 7 May 2026 20:58:11 +0000 Subject: [PATCH 154/394] media: amlogic-c3: Add validations for ae and awb config Avoid invalid memory access if the zones_num is bigger than zone_weight. This patch fixes the following smatch errors: drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:111 c3_isp_params_awb_wt() error: buffer overflow 'cfg->zone_weight' 768 <= u32max drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:111 c3_isp_params_awb_wt() error: buffer overflow 'cfg->zone_weight' 768 <= u32max drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:227 c3_isp_params_ae_wt() error: buffer overflow 'cfg->zone_weight' 255 <= u32max drivers/media/platform/amlogic/c3/isp/c3-isp-params.c:227 c3_isp_params_ae_wt() error: buffer overflow 'cfg->zone_weight' 255 <= u32max Cc: stable@vger.kernel.org Fixes: fb2e135208f3 ("media: platform: Add C3 ISP driver") Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Reviewed-by: Keke Li Signed-off-by: Hans Verkuil --- drivers/media/platform/amlogic/c3/isp/c3-isp-params.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c index 6f9ca7a7dd88..aec3eed0e443 100644 --- a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c +++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c @@ -104,6 +104,8 @@ static void c3_isp_params_awb_wt(struct c3_isp_device *isp, c3_isp_write(isp, ISP_AWB_BLK_WT_ADDR, 0); zones_num = cfg->horiz_zones_num * cfg->vert_zones_num; + if (zones_num > C3_ISP_AWB_MAX_ZONES) + zones_num = C3_ISP_AWB_MAX_ZONES; /* Need to write 8 weights at once */ for (i = 0; i < zones_num / 8; i++) { @@ -220,6 +222,8 @@ static void c3_isp_params_ae_wt(struct c3_isp_device *isp, c3_isp_write(isp, ISP_AE_BLK_WT_ADDR, 0); zones_num = cfg->horiz_zones_num * cfg->vert_zones_num; + if (zones_num > C3_ISP_AE_MAX_ZONES) + zones_num = C3_ISP_AE_MAX_ZONES; /* Need to write 8 weights at once */ for (i = 0; i < zones_num / 8; i++) { From 56384b486b80ce4a2bc93689aae49995f908f90d Mon Sep 17 00:00:00 2001 From: Arash Golgol Date: Sat, 9 May 2026 19:40:13 +0330 Subject: [PATCH 155/394] media: video-i2c: use vb2_video_unregister_device on driver removal The driver uses vb2_fop_release() as its file release operation, so vb2_video_unregister_device() should be used instead of video_unregister_device() during driver removal. This ensures that the vb2 queue is properly disconnected before the video device is unregistered. Signed-off-by: Arash Golgol Signed-off-by: Hans Verkuil --- drivers/media/i2c/video-i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index fef3993f4e2d..9c47f4aaa359 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -888,7 +888,7 @@ static void video_i2c_remove(struct i2c_client *client) if (data->chip->set_power) data->chip->set_power(data, false); - video_unregister_device(&data->vdev); + vb2_video_unregister_device(&data->vdev); } #ifdef CONFIG_PM From bc4574c265ed738849e46d942617100580fcedd2 Mon Sep 17 00:00:00 2001 From: Arash Golgol Date: Sun, 10 May 2026 07:04:23 +0330 Subject: [PATCH 156/394] media: video-i2c: fix buffer queue ordering Queued buffers are added to the tail of vid_cap_active in buffer_queue(), but the capture kthread also retrieves buffers from the tail of the list. This makes the queue behave as LIFO instead of FIFO when multiple buffers are queued. Fix this by retrieving buffers from the head of the list. Signed-off-by: Arash Golgol Signed-off-by: Hans Verkuil --- drivers/media/i2c/video-i2c.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 9c47f4aaa359..2b23914717d1 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -454,8 +454,9 @@ static int video_i2c_thread_vid_cap(void *priv) spin_lock(&data->slock); if (!list_empty(&data->vid_cap_active)) { - vid_cap_buf = list_last_entry(&data->vid_cap_active, - struct video_i2c_buffer, list); + vid_cap_buf = list_first_entry(&data->vid_cap_active, + struct video_i2c_buffer, + list); list_del(&vid_cap_buf->list); } From 5fec5c8f11fdb65cfa0276f3877e8b8c1c8c50f7 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 11 May 2026 16:32:38 +0200 Subject: [PATCH 157/394] media: rzv2h-ivc: Add myself as co-maintainer Add myself as co-maintainer of the RZ/V2H(P) IVC block. Signed-off-by: Jacopo Mondi Acked-by: Daniel Scally Signed-off-by: Hans Verkuil --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4e054ce1cfaa..4b5b66d5114e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22756,6 +22756,7 @@ F: drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c RENESAS RZ/V2H(P) INPUT VIDEO CONTROL BLOCK DRIVER M: Daniel Scally +M: Jacopo Mondi L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/renesas,r9a09g057-ivc.yaml From 04344d0b4929caa94c0df72f767752aa0935ef5d Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:06 +0300 Subject: [PATCH 158/394] media: airspy: Return queued buffers on start_streaming() failure The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. airspy_start_streaming() returned -ENODEV early when the USB device had been disconnected (s->udev == NULL) without returning any buffers that buf_queue() had already accepted. Take v4l2_lock first and jump to the existing err_clear_bit label, which already drains s->queued_bufs via vb2_buffer_done(..., VB2_BUF_STATE_QUEUED) before unlocking. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: 634fe5033951 ("[media] airspy: AirSpy SDR driver") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/usb/airspy/airspy.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 8f6b721ba107..57edb42463e8 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -522,11 +522,13 @@ static int airspy_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(s->dev, "\n"); - if (!s->udev) - return -ENODEV; - mutex_lock(&s->v4l2_lock); + if (!s->udev) { + ret = -ENODEV; + goto err_clear_bit; + } + s->sequence = 0; set_bit(POWER_ON, &s->flags); From 7201c17786a498497bca57752883b90914d405ac Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:07 +0300 Subject: [PATCH 159/394] media: msi2500: Return queued buffers on start_streaming() failure The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. msi2500_start_streaming() had five error paths that all hit this trap and were further tangled by ret-overwriting between calls: - -ENODEV when the USB device was already disconnected - -ERESTARTSYS when mutex_lock_interruptible() was interrupted - msi2500_set_usb_adc() failure: ret was silently overwritten by the next call (msi2500_isoc_init), so the error was lost entirely - msi2500_isoc_init() failure: cleanup_queued_bufs was called, but the function then fell through to msi2500_ctrl_msg() and again masked the original error by overwriting ret - msi2500_ctrl_msg(CMD_START_STREAMING) failure: no cleanup at all, leaving isoc URBs submitted with no way for the driver to consume them Consolidate the error paths into a small goto chain. Every failure now stops the function, drains the queued-buffer list, and returns the real error code. The ctrl_msg failure path also rolls back the preceding msi2500_isoc_init() via msi2500_isoc_cleanup() before unlocking and draining. The cleanup helper takes a vb2_buffer_state argument so that the start_streaming error paths can pass VB2_BUF_STATE_QUEUED (as expected by userspace on start_streaming failure) while stop_streaming keeps its existing VB2_BUF_STATE_ERROR semantics. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: 977e444f59ad ("[media] Mirics MSi3101 SDR Dongle driver") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/usb/msi2500/msi2500.c | 32 +++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 1ff98956b680..0614087c3c3c 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -541,7 +541,8 @@ static int msi2500_isoc_init(struct msi2500_dev *dev) } /* Must be called with vb_queue_lock hold */ -static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev) +static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev, + enum vb2_buffer_state state) { unsigned long flags; @@ -554,7 +555,7 @@ static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev) buf = list_entry(dev->queued_bufs.next, struct msi2500_frame_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, state); } spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); } @@ -830,25 +831,40 @@ static int msi2500_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(dev->dev, "\n"); - if (!dev->udev) - return -ENODEV; + if (!dev->udev) { + ret = -ENODEV; + goto err_cleanup; + } - if (mutex_lock_interruptible(&dev->v4l2_lock)) - return -ERESTARTSYS; + if (mutex_lock_interruptible(&dev->v4l2_lock)) { + ret = -ERESTARTSYS; + goto err_cleanup; + } /* wake-up tuner */ v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 1); ret = msi2500_set_usb_adc(dev); + if (ret) + goto err_unlock_cleanup; ret = msi2500_isoc_init(dev); if (ret) - msi2500_cleanup_queued_bufs(dev); + goto err_unlock_cleanup; ret = msi2500_ctrl_msg(dev, CMD_START_STREAMING, 0); + if (ret) + goto err_isoc_cleanup; mutex_unlock(&dev->v4l2_lock); + return 0; +err_isoc_cleanup: + msi2500_isoc_cleanup(dev); +err_unlock_cleanup: + mutex_unlock(&dev->v4l2_lock); +err_cleanup: + msi2500_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); return ret; } @@ -863,7 +879,7 @@ static void msi2500_stop_streaming(struct vb2_queue *vq) if (dev->udev) msi2500_isoc_cleanup(dev); - msi2500_cleanup_queued_bufs(dev); + msi2500_cleanup_queued_bufs(dev, VB2_BUF_STATE_ERROR); /* according to tests, at least 700us delay is required */ msleep(20); From 975b2ee20e569d47821e4f6c9761b4664d48a6a4 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:08 +0300 Subject: [PATCH 160/394] media: pwc: Return queued buffers on start_streaming() failure The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. pwc's start_streaming() had two early returns that hit this trap: -ENODEV when the USB device was already disconnected, and -ERESTARTSYS when mutex_lock_interruptible() was interrupted by a signal. Call the existing pwc_cleanup_queued_bufs() helper with VB2_BUF_STATE_QUEUED before returning (matching the state already used by the pwc_isoc_init() error path in the same function). This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: ceede9fa8939 ("[media] pwc: Fix locking") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/usb/pwc/pwc-if.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index c416e2fc5754..59b99ac8fcb6 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -710,11 +710,15 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) struct pwc_device *pdev = vb2_get_drv_priv(vq); int r; - if (!pdev->udev) + if (!pdev->udev) { + pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED); return -ENODEV; + } - if (mutex_lock_interruptible(&pdev->v4l2_lock)) + if (mutex_lock_interruptible(&pdev->v4l2_lock)) { + pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED); return -ERESTARTSYS; + } /* Turn on camera and set LEDS on */ pwc_camera_power(pdev, 1); pwc_set_leds(pdev, leds[0], leds[1]); From 33ca0aab6f4bd90921fc1395478f38f72c4d19af Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:09 +0300 Subject: [PATCH 161/394] media: rtl2832_sdr: Return queued buffers on start_streaming() failure The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. rtl2832_sdr_start_streaming() had multiple error paths that hit this trap: two direct early returns (-ENODEV, -ERESTARTSYS), plus six `goto err` paths covering subdev s_power, tuner setup, ADC setup, stream-buffer allocation, urb allocation, and urb submission failures. None of them returned the queued buffers. The original function had no distinct success exit and fell straight through into the err label, which previously only did mutex_unlock and "return ret". Adding queued-buffer cleanup at err must therefore be paired with an explicit success return; otherwise every successful start would also drain the buffer queue and kill streaming. Add that success return, then add rtl2832_sdr_cleanup_queued_bufs() at the err label and before each early return. The cleanup helper takes a vb2_buffer_state argument so that the start_streaming error paths can pass VB2_BUF_STATE_QUEUED (as expected by userspace on start_streaming failure) while stop_streaming keeps its existing VB2_BUF_STATE_ERROR semantics. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). The err label still does not roll back power_ctrl(), frontend_ctrl(), the POWER_ON flag, or stream/URB allocations that may have happened before the failing step. Those are pre-existing leaks of a different class and are not addressed here. Fixes: 771138920eaf ("[media] rtl2832_sdr: Realtek RTL2832 SDR driver module") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/rtl2832_sdr.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 422d1a7b5456..c564485e3bbb 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -399,7 +399,8 @@ static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_dev *dev) } /* Must be called with vb_queue_lock hold */ -static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev) +static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev, + enum vb2_buffer_state state) { struct platform_device *pdev = dev->pdev; unsigned long flags; @@ -413,7 +414,7 @@ static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev) buf = list_entry(dev->queued_bufs.next, struct rtl2832_sdr_frame_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, state); } spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); } @@ -855,11 +856,15 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) dev_dbg(&pdev->dev, "\n"); - if (!dev->udev) + if (!dev->udev) { + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); return -ENODEV; + } - if (mutex_lock_interruptible(&dev->v4l2_lock)) + if (mutex_lock_interruptible(&dev->v4l2_lock)) { + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); return -ERESTARTSYS; + } if (d->props->power_ctrl) d->props->power_ctrl(d, 1); @@ -900,7 +905,11 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) goto err; + mutex_unlock(&dev->v4l2_lock); + return 0; + err: + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); mutex_unlock(&dev->v4l2_lock); return ret; @@ -920,7 +929,7 @@ static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq) rtl2832_sdr_kill_urbs(dev); rtl2832_sdr_free_urbs(dev); rtl2832_sdr_free_stream_bufs(dev); - rtl2832_sdr_cleanup_queued_bufs(dev); + rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_ERROR); rtl2832_sdr_unset_adc(dev); /* sleep tuner */ From ffc8eec06378a340d708c889184ab3e14b57d540 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:10 +0300 Subject: [PATCH 162/394] media: stm32-dcmipp: Return queued buffers on start_streaming() failure The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. dcmipp_bytecap_start_streaming() returned -EINVAL when the source subdevice could not be resolved from the media graph, before pm_runtime_resume_and_get() and media_pipeline_start() had been called. The remaining error paths already converge on the err_buffer_done label, which calls dcmipp_bytecap_all_buffers_done(..., VB2_BUF_STATE_QUEUED). Jump to that label directly: the intermediate err_pm_put / err_media_pipeline_stop labels are skipped, which is correct because nothing they would undo has happened yet. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: 28e0f3772296 ("media: stm32-dcmipp: STM32 DCMIPP camera interface driver") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- .../media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c index a42f43d19f9e..f0e809458489 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c @@ -401,8 +401,10 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq, */ if (!vcap->s_subdev) { pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - return -EINVAL; + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) { + ret = -EINVAL; + goto err_buffer_done; + } vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity); vcap->s_subdev_pad_nb = pad->index; } From bbba3e260a62810a717b4442a3bb96d0ec0f6309 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Mon, 11 May 2026 20:12:11 +0300 Subject: [PATCH 163/394] media: sun4i-csi: Return queued buffers on start_streaming() failure The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak. sun4i_csi_start_streaming() returned -EINVAL when no matching CSI format could be found, before any setup (scratch buffer allocation, pipeline start) had been performed. The remaining error paths already converge on the err_clear_dma_queue label, which calls return_all_buffers(..., VB2_BUF_STATE_QUEUED) under csi->qlock. Jump to that label directly: the intermediate err_disable_device / err_disable_pipeline / err_free_scratch_buffer labels are skipped, which is correct because nothing they would undo has happened yet. This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo: Return queued buffers on start_streaming() failure"). Fixes: 577bbf23b758 ("media: sunxi: Add A10 CSI driver") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c index e911c7f7acc5..4781db21c205 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c @@ -234,8 +234,10 @@ static int sun4i_csi_start_streaming(struct vb2_queue *vq, unsigned int count) int ret; csi_fmt = sun4i_csi_find_format(&csi->fmt.pixelformat, NULL); - if (!csi_fmt) - return -EINVAL; + if (!csi_fmt) { + ret = -EINVAL; + goto err_clear_dma_queue; + } dev_dbg(csi->dev, "Starting capture\n"); From 813dacdda8f2e05b1309a4942e91531333b296ed Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 12 May 2026 08:46:15 +0200 Subject: [PATCH 164/394] media: visl: check if ctx->tpg_str_buf allocation failed The result of ctx->tpg_str_buf = kzalloc(TPG_STR_BUF_SZ, GFP_KERNEL); was never checked. Add this. Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/visl/visl-core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/test-drivers/visl/visl-core.c b/drivers/media/test-drivers/visl/visl-core.c index 127ab18bce99..5e9bdd425d4a 100644 --- a/drivers/media/test-drivers/visl/visl-core.c +++ b/drivers/media/test-drivers/visl/visl-core.c @@ -339,6 +339,10 @@ static int visl_open(struct file *file) } ctx->tpg_str_buf = kzalloc(TPG_STR_BUF_SZ, GFP_KERNEL); + if (!ctx->tpg_str_buf) { + rc = -ENOMEM; + goto free_ctx; + } v4l2_fh_init(&ctx->fh, video_devdata(file)); ctx->dev = dev; From 10f943b12e7cb338da00f10e129043ae27b33af4 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 12 May 2026 08:54:26 +0200 Subject: [PATCH 165/394] media: rzg2l-cru: Add MAINTAINERS entry The CRU was missing a maintainer entry. Add it. Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4b5b66d5114e..63389fea5d15 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22688,6 +22688,15 @@ S: Supported F: Documentation/devicetree/bindings/timer/renesas,rz-mtu3.yaml F: drivers/counter/rz-mtu3-cnt.c +RENESAS RZ/G2L / RZ/V2H(P) CRU +M: Lad Prabhakar +M: Jacopo Mondi +L: linux-renesas-soc@vger.kernel.org +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/renesas,rzg2l-cru.yaml +F: drivers/media/platform/renesas/rzg2l-cru/ + RENESAS RZ/T2H / RZ/N2H A/D DRIVER M: Cosmin Tanislav L: linux-iio@vger.kernel.org From ae04f36f752ad51343389d1ea8433dc8ff4ffa1e Mon Sep 17 00:00:00 2001 From: Antti Laakso Date: Thu, 19 Mar 2026 17:50:30 +0200 Subject: [PATCH 166/394] platform/x86: int3472: Match MSI laptop board name Ensure MSI system is correct by checking board name too. Signed-off-by: Antti Laakso Reviewed-by: Daniel Scally Signed-off-by: Sakari Ailus --- drivers/platform/x86/intel/int3472/tps68470_board_data.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c index c1ddbf9a82c0..e65067358301 100644 --- a/drivers/platform/x86/intel/int3472/tps68470_board_data.c +++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c @@ -404,6 +404,7 @@ static const struct dmi_system_id int3472_tps68470_board_data_table[] = { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 14 AI+ Evo C2VMG"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-14N3"), }, .driver_data = (void *)&msi_p14_ai_evo_tps68470_board_data, }, From 621e4f762ed63830d162e7cc994ebab8758fa355 Mon Sep 17 00:00:00 2001 From: Antti Laakso Date: Thu, 19 Mar 2026 17:50:31 +0200 Subject: [PATCH 167/394] platform/x86: int3472: Add more MSI AI evo laptops The MSI prestige AI EVO 13 and 16 have the same camera configuration as model 14. Use the same platform data for all. Signed-off-by: Antti Laakso Reviewed-by: Daniel Scally [Sakari Ailus: Use user-reported board name for model 16.] Signed-off-by: Sakari Ailus --- .../x86/intel/int3472/tps68470_board_data.c | 52 ++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c index e65067358301..28c9e12c85bf 100644 --- a/drivers/platform/x86/intel/int3472/tps68470_board_data.c +++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c @@ -238,7 +238,7 @@ static struct regulator_consumer_supply ovti5675_dvdd_consumer_supplies[] = { REGULATOR_SUPPLY("dvdd", "i2c-OVTI5675:00"), }; -static const struct regulator_init_data msi_p14_ai_evo_tps68470_core_reg_init_data = { +static const struct regulator_init_data msi_prestige_ai_evo_tps68470_core_reg_init_data = { .constraints = { .min_uV = 1200000, .max_uV = 1200000, @@ -249,7 +249,7 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_core_reg_init_da .consumer_supplies = ovti5675_dvdd_consumer_supplies, }; -static const struct regulator_init_data msi_p14_ai_evo_tps68470_ana_reg_init_data = { +static const struct regulator_init_data msi_prestige_ai_evo_tps68470_ana_reg_init_data = { .constraints = { .min_uV = 2815200, .max_uV = 2815200, @@ -260,7 +260,7 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_ana_reg_init_dat .consumer_supplies = ovti5675_avdd_consumer_supplies, }; -static const struct regulator_init_data msi_p14_ai_evo_tps68470_vio_reg_init_data = { +static const struct regulator_init_data msi_prestige_ai_evo_tps68470_vio_reg_init_data = { .constraints = { .min_uV = 1800600, .max_uV = 1800600, @@ -269,7 +269,7 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_vio_reg_init_dat }, }; -static const struct regulator_init_data msi_p14_ai_evo_tps68470_vsio_reg_init_data = { +static const struct regulator_init_data msi_prestige_ai_evo_tps68470_vsio_reg_init_data = { .constraints = { .min_uV = 1800600, .max_uV = 1800600, @@ -280,12 +280,12 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_vsio_reg_init_da .consumer_supplies = ovti5675_dovdd_consumer_supplies, }; -static const struct tps68470_regulator_platform_data msi_p14_ai_evo_tps68470_pdata = { +static const struct tps68470_regulator_platform_data msi_prestige_ai_evo_tps68470_pdata = { .reg_init_data = { - [TPS68470_CORE] = &msi_p14_ai_evo_tps68470_core_reg_init_data, - [TPS68470_ANA] = &msi_p14_ai_evo_tps68470_ana_reg_init_data, - [TPS68470_VIO] = &msi_p14_ai_evo_tps68470_vio_reg_init_data, - [TPS68470_VSIO] = &msi_p14_ai_evo_tps68470_vsio_reg_init_data, + [TPS68470_CORE] = &msi_prestige_ai_evo_tps68470_core_reg_init_data, + [TPS68470_ANA] = &msi_prestige_ai_evo_tps68470_ana_reg_init_data, + [TPS68470_VIO] = &msi_prestige_ai_evo_tps68470_vio_reg_init_data, + [TPS68470_VSIO] = &msi_prestige_ai_evo_tps68470_vsio_reg_init_data, }, }; @@ -315,7 +315,7 @@ static struct gpiod_lookup_table dell_7212_int3479_gpios = { } }; -static struct gpiod_lookup_table msi_p14_ai_evo_ovti5675_gpios = { +static struct gpiod_lookup_table msi_prestige_ai_evo_ovti5675_gpios = { .dev_id = "i2c-OVTI5675:00", .table = { GPIO_LOOKUP("tps68470-gpio", 9, "reset", GPIO_ACTIVE_LOW), @@ -323,13 +323,13 @@ static struct gpiod_lookup_table msi_p14_ai_evo_ovti5675_gpios = { } }; -static const struct property_entry msi_p14_ai_evo_gpio_props[] = { +static const struct property_entry msi_prestige_ai_evo_gpio_props[] = { PROPERTY_ENTRY_BOOL("daisy-chain-enable"), { } }; -static const struct software_node msi_p14_ai_evo_tps68470_gpio_swnode = { - .properties = msi_p14_ai_evo_gpio_props, +static const struct software_node msi_prestige_ai_evo_tps68470_gpio_swnode = { + .properties = msi_prestige_ai_evo_gpio_props, }; static const struct int3472_tps68470_board_data surface_go_tps68470_board_data = { @@ -361,13 +361,13 @@ static const struct int3472_tps68470_board_data dell_7212_tps68470_board_data = }, }; -static const struct int3472_tps68470_board_data msi_p14_ai_evo_tps68470_board_data = { +static const struct int3472_tps68470_board_data msi_prestige_ai_evo_tps68470_board_data = { .dev_name = "i2c-INT3472:06", - .tps68470_regulator_pdata = &msi_p14_ai_evo_tps68470_pdata, - .tps68470_gpio_swnode = &msi_p14_ai_evo_tps68470_gpio_swnode, + .tps68470_regulator_pdata = &msi_prestige_ai_evo_tps68470_pdata, + .tps68470_gpio_swnode = &msi_prestige_ai_evo_tps68470_gpio_swnode, .n_gpiod_lookups = 1, .tps68470_gpio_lookup_tables = { - &msi_p14_ai_evo_ovti5675_gpios, + &msi_prestige_ai_evo_ovti5675_gpios, }, }; @@ -400,13 +400,29 @@ static const struct dmi_system_id int3472_tps68470_board_data_table[] = { }, .driver_data = (void *)&dell_7212_tps68470_board_data, }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 13 AI+ Evo A2VMG"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-13Q3"), + }, + .driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data, + }, { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 14 AI+ Evo C2VMG"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-14N3"), }, - .driver_data = (void *)&msi_p14_ai_evo_tps68470_board_data, + .driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data, + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 16 AI+ Evo B2VMG"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-15A3"), + }, + .driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data, }, { } }; From 38547c3d871c46f46e98cc6d10c496984e112978 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 6 May 2026 09:48:43 +0300 Subject: [PATCH 168/394] media: i2c: lm3560: Fix v4l2 subdev registration The existing driver does not call media subdev registration, making it invisible to the media framework. Since the LM3560 supports two independent LEDs, register each LED as a separate media entity. Because registering LEDs before device initialization may cause access attempts before the hardware is ready, lm3560_init_device has been moved before the subdevice initializations. An additional helper, lm3560_subdev_cleanup, was added to release LED0 if the initialization of LED1 fails, and to deregister both LEDs in the remove function. Signed-off-by: Svyatoslav Ryhel Signed-off-by: Sakari Ailus --- drivers/media/i2c/lm3560.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index f4cc844f4e3c..edfb07587cab 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -364,8 +364,15 @@ static int lm3560_subdev_init(struct lm3560_flash *flash, goto err_out; flash->subdev_led[led_no].entity.function = MEDIA_ENT_F_FLASH; - return rval; + rval = v4l2_async_register_subdev(&flash->subdev_led[led_no]); + if (rval < 0) { + dev_err(flash->dev, "failed to register V4L2 subdev"); + goto error_out_media; + } + return rval; +error_out_media: + media_entity_cleanup(&flash->subdev_led[led_no].entity); err_out: v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]); return rval; @@ -391,6 +398,14 @@ static int lm3560_init_device(struct lm3560_flash *flash) return rval; } +static void lm3560_subdev_cleanup(struct lm3560_flash *flash, + enum lm3560_led_id led_no) +{ + v4l2_async_unregister_subdev(&flash->subdev_led[led_no]); + v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]); + media_entity_cleanup(&flash->subdev_led[led_no].entity); +} + static int lm3560_probe(struct i2c_client *client) { struct lm3560_flash *flash; @@ -425,17 +440,19 @@ static int lm3560_probe(struct i2c_client *client) flash->dev = &client->dev; mutex_init(&flash->lock); + rval = lm3560_init_device(flash); + if (rval < 0) + return rval; + rval = lm3560_subdev_init(flash, LM3560_LED0, "lm3560-led0"); if (rval < 0) return rval; rval = lm3560_subdev_init(flash, LM3560_LED1, "lm3560-led1"); - if (rval < 0) - return rval; - - rval = lm3560_init_device(flash); - if (rval < 0) + if (rval < 0) { + lm3560_subdev_cleanup(flash, LM3560_LED0); return rval; + } i2c_set_clientdata(client, flash); @@ -447,11 +464,8 @@ static void lm3560_remove(struct i2c_client *client) struct lm3560_flash *flash = i2c_get_clientdata(client); unsigned int i; - for (i = LM3560_LED0; i < LM3560_LED_MAX; i++) { - v4l2_device_unregister_subdev(&flash->subdev_led[i]); - v4l2_ctrl_handler_free(&flash->ctrls_led[i]); - media_entity_cleanup(&flash->subdev_led[i].entity); - } + for (i = LM3560_LED0; i < LM3560_LED_MAX; i++) + lm3560_subdev_cleanup(flash, i); } static const struct i2c_device_id lm3560_id_table[] = { From ace46508bac913f9d0f4b84b49392445ae911fb3 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 6 May 2026 09:48:44 +0300 Subject: [PATCH 169/394] media: i2c: lm3560: Optimize mutex lock usage Pass the device's own mutex lock to the control handler so that the media framework can handle control access instead of managing it manually. The lock must be common to both sub-devices since they share same hardware, so the individual sub-device locks will not work here. Signed-off-by: Svyatoslav Ryhel Signed-off-by: Sakari Ailus --- drivers/media/i2c/lm3560.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index edfb07587cab..5b568ed9536b 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -162,14 +162,12 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no); int rval = -EINVAL; - mutex_lock(&flash->lock); - if (ctrl->id == V4L2_CID_FLASH_FAULT) { s32 fault = 0; unsigned int reg_val; rval = regmap_read(flash->regmap, REG_FLAG, ®_val); if (rval < 0) - goto out; + return rval; if (reg_val & FAULT_SHORT_CIRCUIT) fault |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; if (reg_val & FAULT_OVERTEMP) @@ -179,8 +177,6 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) ctrl->cur.val = fault; } -out: - mutex_unlock(&flash->lock); return rval; } @@ -190,8 +186,6 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) u8 tout_bits; int rval = -EINVAL; - mutex_lock(&flash->lock); - switch (ctrl->id) { case V4L2_CID_FLASH_LED_MODE: flash->led_mode = ctrl->val; @@ -202,14 +196,12 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) case V4L2_CID_FLASH_STROBE_SOURCE: rval = regmap_update_bits(flash->regmap, REG_CONFIG1, 0x04, (ctrl->val) << 2); - if (rval < 0) - goto err_out; break; case V4L2_CID_FLASH_STROBE: if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { rval = -EBUSY; - goto err_out; + break; } flash->led_mode = V4L2_FLASH_LED_MODE_FLASH; rval = lm3560_mode_ctrl(flash); @@ -218,7 +210,7 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) case V4L2_CID_FLASH_STROBE_STOP: if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { rval = -EBUSY; - goto err_out; + break; } flash->led_mode = V4L2_FLASH_LED_MODE_NONE; rval = lm3560_mode_ctrl(flash); @@ -239,8 +231,6 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) break; } -err_out: - mutex_unlock(&flash->lock); return rval; } @@ -328,6 +318,8 @@ static int lm3560_init_controls(struct lm3560_flash *flash, if (fault != NULL) fault->flags |= V4L2_CTRL_FLAG_VOLATILE; + hdl->lock = &flash->lock; + if (hdl->error) return hdl->error; @@ -363,6 +355,7 @@ static int lm3560_subdev_init(struct lm3560_flash *flash, if (rval < 0) goto err_out; flash->subdev_led[led_no].entity.function = MEDIA_ENT_F_FLASH; + flash->subdev_led[led_no].state_lock = &flash->lock; rval = v4l2_async_register_subdev(&flash->subdev_led[led_no]); if (rval < 0) { From 34fc7eedc3a0e150086024c450218aba6efaf4cd Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 6 May 2026 09:48:45 +0300 Subject: [PATCH 170/394] media: i2c: lm3560: Convert to use OF bindings Since there are no users of this driver via platform data, remove the platform data support and switch to using Device Tree bindings. Converting to Device Tree assumes dynamic and independent registration of LEDs. To monitor the configured LEDs, a bitmap has been added. This makes LED cleanup more robust and less context dependent. Signed-off-by: Svyatoslav Ryhel Signed-off-by: Sakari Ailus --- drivers/media/i2c/lm3560.c | 141 ++++++++++++++++++++++++++----------- include/media/i2c/lm3560.h | 15 ---- 2 files changed, 100 insertions(+), 56 deletions(-) diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index 5b568ed9536b..609fd0339345 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -9,11 +9,14 @@ * Ldd-Mlp */ +#include #include #include #include #include +#include #include +#include #include #include #include @@ -43,22 +46,33 @@ enum led_enable { * struct lm3560_flash * * @dev: pointer to &struct device - * @pdata: platform data * @regmap: reg. map for i2c * @lock: muxtex for serial access. * @led_mode: V4L2 LED mode * @ctrls_led: V4L2 controls * @subdev_led: V4L2 subdev + * @led_id: LED status holder + * @peak: peak current + * @max_flash_timeout: flash timeout + * @max_flash_brt: flash mode led brightness + * @max_torch_brt: torch mode led brightness */ struct lm3560_flash { struct device *dev; - struct lm3560_platform_data *pdata; struct regmap *regmap; struct mutex lock; enum v4l2_flash_led_mode led_mode; struct v4l2_ctrl_handler ctrls_led[LM3560_LED_MAX]; struct v4l2_subdev subdev_led[LM3560_LED_MAX]; + + DECLARE_BITMAP(led_id, LM3560_LED_MAX); + + enum lm3560_peak_current peak; + u32 max_flash_timeout; + + u32 max_flash_brt[LM3560_LED_MAX]; + u32 max_torch_brt[LM3560_LED_MAX]; }; #define to_lm3560_flash(_ctrl, _no) \ @@ -269,8 +283,8 @@ static int lm3560_init_controls(struct lm3560_flash *flash, enum lm3560_led_id led_no) { struct v4l2_ctrl *fault; - u32 max_flash_brt = flash->pdata->max_flash_brt[led_no]; - u32 max_torch_brt = flash->pdata->max_torch_brt[led_no]; + u32 max_flash_brt = flash->max_flash_brt[led_no]; + u32 max_torch_brt = flash->max_torch_brt[led_no]; struct v4l2_ctrl_handler *hdl = &flash->ctrls_led[led_no]; const struct v4l2_ctrl_ops *ops = &lm3560_led_ctrl_ops[led_no]; @@ -295,9 +309,9 @@ static int lm3560_init_controls(struct lm3560_flash *flash, /* flash strobe timeout */ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT, LM3560_FLASH_TOUT_MIN, - flash->pdata->max_flash_timeout, + flash->max_flash_timeout, LM3560_FLASH_TOUT_STEP, - flash->pdata->max_flash_timeout); + flash->max_flash_timeout); /* flash brt */ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY, @@ -339,15 +353,18 @@ static const struct regmap_config lm3560_regmap = { }; static int lm3560_subdev_init(struct lm3560_flash *flash, - enum lm3560_led_id led_no, char *led_name) + enum lm3560_led_id led_no, + struct fwnode_handle *fwnode) { struct i2c_client *client = to_i2c_client(flash->dev); int rval; v4l2_i2c_subdev_init(&flash->subdev_led[led_no], client, &lm3560_ops); flash->subdev_led[led_no].flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - strscpy(flash->subdev_led[led_no].name, led_name, - sizeof(flash->subdev_led[led_no].name)); + snprintf(flash->subdev_led[led_no].name, + sizeof(flash->subdev_led[led_no].name), + "lm3560-led%d", led_no); + flash->subdev_led[led_no].fwnode = fwnode_handle_get(fwnode); rval = lm3560_init_controls(flash, led_no); if (rval) goto err_out; @@ -378,7 +395,7 @@ static int lm3560_init_device(struct lm3560_flash *flash) /* set peak current */ rval = regmap_update_bits(flash->regmap, - REG_FLASH_TOUT, 0x60, flash->pdata->peak); + REG_FLASH_TOUT, 0x60, flash->peak); if (rval < 0) return rval; /* output disable */ @@ -391,18 +408,21 @@ static int lm3560_init_device(struct lm3560_flash *flash) return rval; } -static void lm3560_subdev_cleanup(struct lm3560_flash *flash, - enum lm3560_led_id led_no) +static void lm3560_subdev_cleanup(struct lm3560_flash *flash) { - v4l2_async_unregister_subdev(&flash->subdev_led[led_no]); - v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]); - media_entity_cleanup(&flash->subdev_led[led_no].entity); + int led_no; + + for_each_set_bit(led_no, flash->led_id, LM3560_LED_MAX) { + v4l2_async_unregister_subdev(&flash->subdev_led[led_no]); + v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]); + media_entity_cleanup(&flash->subdev_led[led_no].entity); + } } static int lm3560_probe(struct i2c_client *client) { struct lm3560_flash *flash; - struct lm3560_platform_data *pdata = dev_get_platdata(&client->dev); + u32 peak_ua; int rval; flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL); @@ -415,36 +435,69 @@ static int lm3560_probe(struct i2c_client *client) return rval; } - /* if there is no platform data, use chip default value */ - if (pdata == NULL) { - pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); - if (pdata == NULL) - return -ENODEV; - pdata->peak = LM3560_PEAK_3600mA; - pdata->max_flash_timeout = LM3560_FLASH_TOUT_MAX; - /* led 1 */ - pdata->max_flash_brt[LM3560_LED0] = LM3560_FLASH_BRT_MAX; - pdata->max_torch_brt[LM3560_LED0] = LM3560_TORCH_BRT_MAX; - /* led 2 */ - pdata->max_flash_brt[LM3560_LED1] = LM3560_FLASH_BRT_MAX; - pdata->max_torch_brt[LM3560_LED1] = LM3560_TORCH_BRT_MAX; - } - flash->pdata = pdata; flash->dev = &client->dev; mutex_init(&flash->lock); + bitmap_zero(flash->led_id, LM3560_LED_MAX); + + flash->peak = LM3560_PEAK_1600mA; + rval = device_property_read_u32(flash->dev, + "ti,peak-current-microamp", &peak_ua); + if (!rval) { + switch (peak_ua) { + case 1600000: + flash->peak = LM3560_PEAK_1600mA; + break; + case 2300000: + flash->peak = LM3560_PEAK_2300mA; + break; + case 3000000: + flash->peak = LM3560_PEAK_3000mA; + break; + case 3600000: + flash->peak = LM3560_PEAK_3600mA; + break; + default: + return -EINVAL; + } + } + + flash->max_flash_timeout = LM3560_FLASH_TOUT_MIN * 1000; + device_property_read_u32(flash->dev, "flash-max-timeout-us", + &flash->max_flash_timeout); + flash->max_flash_timeout /= 1000; + rval = lm3560_init_device(flash); if (rval < 0) return rval; - rval = lm3560_subdev_init(flash, LM3560_LED0, "lm3560-led0"); - if (rval < 0) - return rval; + device_for_each_child_node_scoped(flash->dev, node) { + u32 reg; - rval = lm3560_subdev_init(flash, LM3560_LED1, "lm3560-led1"); - if (rval < 0) { - lm3560_subdev_cleanup(flash, LM3560_LED0); - return rval; + rval = fwnode_property_read_u32(node, "reg", ®); + if (rval < 0) + /* We care only about nodes with reg property */ + continue; + + if (reg == LM3560_LED0 || reg == LM3560_LED1) { + flash->max_flash_brt[reg] = LM3560_FLASH_BRT_MIN; + fwnode_property_read_u32(node, "flash-max-microamp", + &flash->max_flash_brt[reg]); + + flash->max_torch_brt[reg] = LM3560_TORCH_BRT_MIN; + fwnode_property_read_u32(node, "led-max-microamp", + &flash->max_torch_brt[reg]); + + rval = lm3560_subdev_init(flash, reg, node); + if (rval < 0) { + lm3560_subdev_cleanup(flash); + return dev_err_probe(flash->dev, rval, + "failed to register led%d\n", + reg); + } + + set_bit(reg, flash->led_id); + } } i2c_set_clientdata(client, flash); @@ -455,12 +508,17 @@ static int lm3560_probe(struct i2c_client *client) static void lm3560_remove(struct i2c_client *client) { struct lm3560_flash *flash = i2c_get_clientdata(client); - unsigned int i; - for (i = LM3560_LED0; i < LM3560_LED_MAX; i++) - lm3560_subdev_cleanup(flash, i); + lm3560_subdev_cleanup(flash); } +static const struct of_device_id lm3560_of_match[] = { + { .compatible = "ti,lm3559" }, + { .compatible = "ti,lm3560" }, + { } +}; +MODULE_DEVICE_TABLE(of, lm3560_of_match); + static const struct i2c_device_id lm3560_id_table[] = { { LM3559_NAME }, { LM3560_NAME }, @@ -473,6 +531,7 @@ static struct i2c_driver lm3560_i2c_driver = { .driver = { .name = LM3560_NAME, .pm = NULL, + .of_match_table = lm3560_of_match, }, .probe = lm3560_probe, .remove = lm3560_remove, diff --git a/include/media/i2c/lm3560.h b/include/media/i2c/lm3560.h index 770d8c72c94a..b56c1ff8fd49 100644 --- a/include/media/i2c/lm3560.h +++ b/include/media/i2c/lm3560.h @@ -66,19 +66,4 @@ enum lm3560_peak_current { LM3560_PEAK_3600mA = 0x60 }; -/* struct lm3560_platform_data - * - * @peak : peak current - * @max_flash_timeout: flash timeout - * @max_flash_brt: flash mode led brightness - * @max_torch_brt: torch mode led brightness - */ -struct lm3560_platform_data { - enum lm3560_peak_current peak; - - u32 max_flash_timeout; - u32 max_flash_brt[LM3560_LED_MAX]; - u32 max_torch_brt[LM3560_LED_MAX]; -}; - #endif /* __LM3560_H__ */ From af5fa4896e8528fa664ef2735910e8c73daccae1 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 6 May 2026 09:48:46 +0300 Subject: [PATCH 171/394] media: i2c: lm3560: Add support for PM features Add support for power management features to better control the LM3560 within the media framework. To achieve the desired PM support, the HWEN GPIO and VIN power supply were added and configured into power on/off sequences. Added PM operations along with the PM configuration setup. Signed-off-by: Svyatoslav Ryhel Signed-off-by: Sakari Ailus --- drivers/media/i2c/lm3560.c | 117 ++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index 609fd0339345..49030bc0f441 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -12,12 +12,15 @@ #include #include #include +#include #include #include #include #include +#include #include #include +#include #include #include #include @@ -48,6 +51,8 @@ enum led_enable { * @dev: pointer to &struct device * @regmap: reg. map for i2c * @lock: muxtex for serial access. + * @hwen_gpio: line connected to HWEN pin + * @vin_supply: line connected to IN supply (2.5V - 5.5V) * @led_mode: V4L2 LED mode * @ctrls_led: V4L2 controls * @subdev_led: V4L2 subdev @@ -62,6 +67,9 @@ struct lm3560_flash { struct regmap *regmap; struct mutex lock; + struct gpio_desc *hwen_gpio; + struct regulator *vin_supply; + enum v4l2_flash_led_mode led_mode; struct v4l2_ctrl_handler ctrls_led[LM3560_LED_MAX]; struct v4l2_subdev subdev_led[LM3560_LED_MAX]; @@ -176,12 +184,17 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no); int rval = -EINVAL; + if (!pm_runtime_get_if_active(flash->dev)) + return 0; + if (ctrl->id == V4L2_CID_FLASH_FAULT) { s32 fault = 0; unsigned int reg_val; rval = regmap_read(flash->regmap, REG_FLAG, ®_val); - if (rval < 0) + if (rval < 0) { + pm_runtime_put(flash->dev); return rval; + } if (reg_val & FAULT_SHORT_CIRCUIT) fault |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; if (reg_val & FAULT_OVERTEMP) @@ -191,6 +204,8 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) ctrl->cur.val = fault; } + pm_runtime_put(flash->dev); + return rval; } @@ -200,6 +215,9 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) u8 tout_bits; int rval = -EINVAL; + if (!pm_runtime_get_if_active(flash->dev)) + return 0; + switch (ctrl->id) { case V4L2_CID_FLASH_LED_MODE: flash->led_mode = ctrl->val; @@ -245,6 +263,8 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) break; } + pm_runtime_put(flash->dev); + return rval; } @@ -408,6 +428,38 @@ static int lm3560_init_device(struct lm3560_flash *flash) return rval; } +static int __maybe_unused lm3560_power_off(struct device *dev) +{ + struct lm3560_flash *flash = dev_get_drvdata(dev); + + gpiod_set_value_cansleep(flash->hwen_gpio, 0); + regulator_disable(flash->vin_supply); + + return 0; +} + +static int __maybe_unused lm3560_power_on(struct device *dev) +{ + struct lm3560_flash *flash = dev_get_drvdata(dev); + int rval; + + rval = regulator_enable(flash->vin_supply); + if (rval < 0) { + dev_err(flash->dev, "failed to enable vin power supply\n"); + return rval; + } + + gpiod_set_value_cansleep(flash->hwen_gpio, 1); + + rval = lm3560_init_device(flash); + if (rval < 0) { + lm3560_power_off(dev); + return rval; + } + + return 0; +} + static void lm3560_subdev_cleanup(struct lm3560_flash *flash) { int led_no; @@ -440,6 +492,17 @@ static int lm3560_probe(struct i2c_client *client) bitmap_zero(flash->led_id, LM3560_LED_MAX); + flash->hwen_gpio = devm_gpiod_get_optional(flash->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(flash->hwen_gpio)) + return dev_err_probe(flash->dev, PTR_ERR(flash->hwen_gpio), + "failed to get hwen gpio\n"); + + flash->vin_supply = devm_regulator_get(flash->dev, "vin"); + if (IS_ERR(flash->vin_supply)) + return dev_err_probe(flash->dev, PTR_ERR(flash->vin_supply), + "failed to get vin-supply\n"); + flash->peak = LM3560_PEAK_1600mA; rval = device_property_read_u32(flash->dev, "ti,peak-current-microamp", &peak_ua); @@ -467,9 +530,19 @@ static int lm3560_probe(struct i2c_client *client) &flash->max_flash_timeout); flash->max_flash_timeout /= 1000; + rval = regulator_enable(flash->vin_supply); + if (rval < 0) + return dev_err_probe(flash->dev, rval, + "failed to enable vin power supply\n"); + + gpiod_set_value_cansleep(flash->hwen_gpio, 1); + rval = lm3560_init_device(flash); if (rval < 0) - return rval; + goto error_disable; + + pm_runtime_set_active(flash->dev); + pm_runtime_enable(flash->dev); device_for_each_child_node_scoped(flash->dev, node) { u32 reg; @@ -490,10 +563,10 @@ static int lm3560_probe(struct i2c_client *client) rval = lm3560_subdev_init(flash, reg, node); if (rval < 0) { - lm3560_subdev_cleanup(flash); - return dev_err_probe(flash->dev, rval, - "failed to register led%d\n", - reg); + dev_err(flash->dev, + "failed to register led%d: %d\n", + reg, rval); + goto error_clean; } set_bit(reg, flash->led_id); @@ -502,7 +575,23 @@ static int lm3560_probe(struct i2c_client *client) i2c_set_clientdata(client, flash); + pm_runtime_set_autosuspend_delay(flash->dev, 1000); + pm_runtime_use_autosuspend(flash->dev); + pm_runtime_idle(flash->dev); + return 0; + +error_clean: + pm_runtime_disable(flash->dev); + pm_runtime_set_suspended(flash->dev); + + lm3560_subdev_cleanup(flash); + +error_disable: + gpiod_set_value_cansleep(flash->hwen_gpio, 0); + regulator_disable(flash->vin_supply); + + return rval; } static void lm3560_remove(struct i2c_client *client) @@ -510,8 +599,22 @@ static void lm3560_remove(struct i2c_client *client) struct lm3560_flash *flash = i2c_get_clientdata(client); lm3560_subdev_cleanup(flash); + + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) { + lm3560_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + } } +static const struct dev_pm_ops lm3560_pm_ops = { + SET_RUNTIME_PM_OPS(lm3560_power_off, lm3560_power_on, NULL) +}; + static const struct of_device_id lm3560_of_match[] = { { .compatible = "ti,lm3559" }, { .compatible = "ti,lm3560" }, @@ -530,7 +633,7 @@ MODULE_DEVICE_TABLE(i2c, lm3560_id_table); static struct i2c_driver lm3560_i2c_driver = { .driver = { .name = LM3560_NAME, - .pm = NULL, + .pm = pm_ptr(&lm3560_pm_ops), .of_match_table = lm3560_of_match, }, .probe = lm3560_probe, From dd9a02fc75cefc84024eb658c9e528cc97ca4eda Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 6 May 2026 09:48:47 +0300 Subject: [PATCH 172/394] media: i2c: lm3560: Add proper support for LM3559 The LM3559 is very similar to the LM3560, but it operates at much lower currents. This may result in incorrect current selection if LM3560 ranges are applied to the LM3559. Implement driver data matching to use device-specific current configurations. Since the driver no longer supports platform data and device configuration is performed more granularly, move the remaining enums from the header into the driver file and remove the header. Signed-off-by: Svyatoslav Ryhel Signed-off-by: Sakari Ailus --- drivers/media/i2c/lm3560.c | 120 ++++++++++++++++++++++++++++++------- include/media/i2c/lm3560.h | 69 --------------------- 2 files changed, 100 insertions(+), 89 deletions(-) delete mode 100644 include/media/i2c/lm3560.h diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index 49030bc0f441..46d316a26e50 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -22,9 +22,9 @@ #include #include #include -#include #include #include +#include /* registers definitions */ #define REG_ENABLE 0x10 @@ -39,12 +39,44 @@ #define FAULT_OVERTEMP (1<<1) #define FAULT_SHORT_CIRCUIT (1<<2) +#define LM3560_FLASH_TOUT_MIN 32 +#define LM3560_FLASH_TOUT_STEP 32 +#define LM3560_FLASH_TOUT_MAX 1024 +#define LM3560_FLASH_TOUT_ms_TO_REG(a) \ + ((a) < LM3560_FLASH_TOUT_MIN ? 0 : \ + (((a) - LM3560_FLASH_TOUT_MIN) / LM3560_FLASH_TOUT_STEP)) +#define LM3560_FLASH_TOUT_REG_TO_ms(a) \ + ((a) * LM3560_FLASH_TOUT_STEP + LM3560_FLASH_TOUT_MIN) + +enum lm3560_led_id { + LM3560_LED0 = 0, + LM3560_LED1, + LM3560_LED_MAX +}; + +enum lm3560_peak_current { + LM3560_PEAK_1600mA = 0x00, + LM3560_PEAK_2300mA = 0x20, + LM3560_PEAK_3000mA = 0x40, + LM3560_PEAK_3600mA = 0x60 +}; + enum led_enable { MODE_SHDN = 0x0, MODE_TORCH = 0x2, MODE_FLASH = 0x3, }; +struct lm3560_flash_config { + u32 flash_brt_min_ua; + u32 flash_brt_max_ua; + u32 flash_brt_step_ua; + + u32 torch_brt_min_ua; + u32 torch_brt_max_ua; + u32 torch_brt_step_ua; +}; + /** * struct lm3560_flash * @@ -61,6 +93,7 @@ enum led_enable { * @max_flash_timeout: flash timeout * @max_flash_brt: flash mode led brightness * @max_torch_brt: torch mode led brightness + * @config: device specific current configuration */ struct lm3560_flash { struct device *dev; @@ -81,6 +114,8 @@ struct lm3560_flash { u32 max_flash_brt[LM3560_LED_MAX]; u32 max_torch_brt[LM3560_LED_MAX]; + + const struct lm3560_flash_config *config; }; #define to_lm3560_flash(_ctrl, _no) \ @@ -136,15 +171,20 @@ static int lm3560_enable_ctrl(struct lm3560_flash *flash, static int lm3560_torch_brt_ctrl(struct lm3560_flash *flash, enum lm3560_led_id led_no, unsigned int brt) { + const struct lm3560_flash_config *config = flash->config; int rval; - u8 br_bits; + u32 br_bits; - if (brt < LM3560_TORCH_BRT_MIN) + if (brt < config->torch_brt_min_ua) return lm3560_enable_ctrl(flash, led_no, false); else rval = lm3560_enable_ctrl(flash, led_no, true); - br_bits = LM3560_TORCH_BRT_uA_TO_REG(brt); + br_bits = clamp(brt, config->torch_brt_min_ua, + config->torch_brt_max_ua); + br_bits = (br_bits - config->torch_brt_min_ua) / + config->torch_brt_step_ua; + if (led_no == LM3560_LED0) rval = regmap_update_bits(flash->regmap, REG_TORCH_BR, 0x07, br_bits); @@ -159,15 +199,20 @@ static int lm3560_torch_brt_ctrl(struct lm3560_flash *flash, static int lm3560_flash_brt_ctrl(struct lm3560_flash *flash, enum lm3560_led_id led_no, unsigned int brt) { + const struct lm3560_flash_config *config = flash->config; int rval; - u8 br_bits; + u32 br_bits; - if (brt < LM3560_FLASH_BRT_MIN) + if (brt < config->flash_brt_min_ua) return lm3560_enable_ctrl(flash, led_no, false); else rval = lm3560_enable_ctrl(flash, led_no, true); - br_bits = LM3560_FLASH_BRT_uA_TO_REG(brt); + br_bits = clamp(brt, config->flash_brt_min_ua, + config->flash_brt_max_ua); + br_bits = (br_bits - config->flash_brt_min_ua) / + config->flash_brt_step_ua; + if (led_no == LM3560_LED0) rval = regmap_update_bits(flash->regmap, REG_FLASH_BR, 0x0f, br_bits); @@ -307,6 +352,7 @@ static int lm3560_init_controls(struct lm3560_flash *flash, u32 max_torch_brt = flash->max_torch_brt[led_no]; struct v4l2_ctrl_handler *hdl = &flash->ctrls_led[led_no]; const struct v4l2_ctrl_ops *ops = &lm3560_led_ctrl_ops[led_no]; + const struct lm3560_flash_config *config = flash->config; v4l2_ctrl_handler_init(hdl, 8); @@ -335,13 +381,13 @@ static int lm3560_init_controls(struct lm3560_flash *flash, /* flash brt */ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY, - LM3560_FLASH_BRT_MIN, max_flash_brt, - LM3560_FLASH_BRT_STEP, max_flash_brt); + config->flash_brt_min_ua, max_flash_brt, + config->flash_brt_step_ua, max_flash_brt); /* torch brt */ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY, - LM3560_TORCH_BRT_MIN, max_torch_brt, - LM3560_TORCH_BRT_STEP, max_torch_brt); + config->torch_brt_min_ua, max_torch_brt, + config->torch_brt_step_ua, max_torch_brt); /* fault */ fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, 0, @@ -481,6 +527,10 @@ static int lm3560_probe(struct i2c_client *client) if (flash == NULL) return -ENOMEM; + flash->config = device_get_match_data(&client->dev); + if (!flash->config) + return -ENODEV; + flash->regmap = devm_regmap_init_i2c(client, &lm3560_regmap); if (IS_ERR(flash->regmap)) { rval = PTR_ERR(flash->regmap); @@ -507,16 +557,26 @@ static int lm3560_probe(struct i2c_client *client) rval = device_property_read_u32(flash->dev, "ti,peak-current-microamp", &peak_ua); if (!rval) { + /* + * LM3559 has lower peak current limit, but + * bit configuration matches LM3560. + * Correct current restrictions are enforced + * by the LM3560 schema. + */ switch (peak_ua) { + case 1400000: case 1600000: flash->peak = LM3560_PEAK_1600mA; break; + case 2100000: case 2300000: flash->peak = LM3560_PEAK_2300mA; break; + case 2700000: case 3000000: flash->peak = LM3560_PEAK_3000mA; break; + case 3200000: case 3600000: flash->peak = LM3560_PEAK_3600mA; break; @@ -545,6 +605,7 @@ static int lm3560_probe(struct i2c_client *client) pm_runtime_enable(flash->dev); device_for_each_child_node_scoped(flash->dev, node) { + const struct lm3560_flash_config *config = flash->config; u32 reg; rval = fwnode_property_read_u32(node, "reg", ®); @@ -553,11 +614,11 @@ static int lm3560_probe(struct i2c_client *client) continue; if (reg == LM3560_LED0 || reg == LM3560_LED1) { - flash->max_flash_brt[reg] = LM3560_FLASH_BRT_MIN; + flash->max_flash_brt[reg] = config->flash_brt_min_ua; fwnode_property_read_u32(node, "flash-max-microamp", &flash->max_flash_brt[reg]); - flash->max_torch_brt[reg] = LM3560_TORCH_BRT_MIN; + flash->max_torch_brt[reg] = config->torch_brt_min_ua; fwnode_property_read_u32(node, "led-max-microamp", &flash->max_torch_brt[reg]); @@ -615,24 +676,43 @@ static const struct dev_pm_ops lm3560_pm_ops = { SET_RUNTIME_PM_OPS(lm3560_power_off, lm3560_power_on, NULL) }; +static const struct lm3560_flash_config lm3559_config = { + .flash_brt_min_ua = 56250, + .flash_brt_max_ua = 900000, + .flash_brt_step_ua = 56250, + + .torch_brt_min_ua = 28125, + .torch_brt_max_ua = 225000, + .torch_brt_step_ua = 28125, +}; + +static const struct lm3560_flash_config lm3560_config = { + .flash_brt_min_ua = 62500, + .flash_brt_max_ua = 1000000, + .flash_brt_step_ua = 62500, + + .torch_brt_min_ua = 31250, + .torch_brt_max_ua = 250000, + .torch_brt_step_ua = 31250, +}; + static const struct of_device_id lm3560_of_match[] = { - { .compatible = "ti,lm3559" }, - { .compatible = "ti,lm3560" }, + { .compatible = "ti,lm3559", .data = &lm3559_config }, + { .compatible = "ti,lm3560", .data = &lm3560_config }, { } }; MODULE_DEVICE_TABLE(of, lm3560_of_match); static const struct i2c_device_id lm3560_id_table[] = { - { LM3559_NAME }, - { LM3560_NAME }, - {} + { "lm3559", (kernel_ulong_t)&lm3559_config }, + { "lm3560", (kernel_ulong_t)&lm3560_config }, + { } }; - MODULE_DEVICE_TABLE(i2c, lm3560_id_table); static struct i2c_driver lm3560_i2c_driver = { .driver = { - .name = LM3560_NAME, + .name = "lm3560", .pm = pm_ptr(&lm3560_pm_ops), .of_match_table = lm3560_of_match, }, diff --git a/include/media/i2c/lm3560.h b/include/media/i2c/lm3560.h deleted file mode 100644 index b56c1ff8fd49..000000000000 --- a/include/media/i2c/lm3560.h +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * include/media/i2c/lm3560.h - * - * Copyright (C) 2013 Texas Instruments - * - * Contact: Daniel Jeong - * Ldd-Mlp - */ - -#ifndef __LM3560_H__ -#define __LM3560_H__ - -#include - -#define LM3559_NAME "lm3559" -#define LM3560_NAME "lm3560" -#define LM3560_I2C_ADDR (0x53) - -/* FLASH Brightness - * min 62500uA, step 62500uA, max 1000000uA - */ -#define LM3560_FLASH_BRT_MIN 62500 -#define LM3560_FLASH_BRT_STEP 62500 -#define LM3560_FLASH_BRT_MAX 1000000 -#define LM3560_FLASH_BRT_uA_TO_REG(a) \ - ((a) < LM3560_FLASH_BRT_MIN ? 0 : \ - (((a) - LM3560_FLASH_BRT_MIN) / LM3560_FLASH_BRT_STEP)) -#define LM3560_FLASH_BRT_REG_TO_uA(a) \ - ((a) * LM3560_FLASH_BRT_STEP + LM3560_FLASH_BRT_MIN) - -/* FLASH TIMEOUT DURATION - * min 32ms, step 32ms, max 1024ms - */ -#define LM3560_FLASH_TOUT_MIN 32 -#define LM3560_FLASH_TOUT_STEP 32 -#define LM3560_FLASH_TOUT_MAX 1024 -#define LM3560_FLASH_TOUT_ms_TO_REG(a) \ - ((a) < LM3560_FLASH_TOUT_MIN ? 0 : \ - (((a) - LM3560_FLASH_TOUT_MIN) / LM3560_FLASH_TOUT_STEP)) -#define LM3560_FLASH_TOUT_REG_TO_ms(a) \ - ((a) * LM3560_FLASH_TOUT_STEP + LM3560_FLASH_TOUT_MIN) - -/* TORCH BRT - * min 31250uA, step 31250uA, max 250000uA - */ -#define LM3560_TORCH_BRT_MIN 31250 -#define LM3560_TORCH_BRT_STEP 31250 -#define LM3560_TORCH_BRT_MAX 250000 -#define LM3560_TORCH_BRT_uA_TO_REG(a) \ - ((a) < LM3560_TORCH_BRT_MIN ? 0 : \ - (((a) - LM3560_TORCH_BRT_MIN) / LM3560_TORCH_BRT_STEP)) -#define LM3560_TORCH_BRT_REG_TO_uA(a) \ - ((a) * LM3560_TORCH_BRT_STEP + LM3560_TORCH_BRT_MIN) - -enum lm3560_led_id { - LM3560_LED0 = 0, - LM3560_LED1, - LM3560_LED_MAX -}; - -enum lm3560_peak_current { - LM3560_PEAK_1600mA = 0x00, - LM3560_PEAK_2300mA = 0x20, - LM3560_PEAK_3000mA = 0x40, - LM3560_PEAK_3600mA = 0x60 -}; - -#endif /* __LM3560_H__ */ From d97d13c24d7893abcfb80d38630ce74daaa1434c Mon Sep 17 00:00:00 2001 From: Alessandro Baldi Date: Sun, 17 May 2026 11:14:15 +0200 Subject: [PATCH 173/394] media: imon: Add iMON VFD HID OEM v1.2 key mappings Add Vol+/Vol-/Mute panel button mappings for iMON VFD HID OEM v1.2. This version differs in the codes that generate the KEY_VOLUMEUP, KEY_VOLUMEDOWN and KEY_MUTE events. Signed-off-by: Alessandro Baldi Signed-off-by: Sean Young --- drivers/media/rc/imon.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 9bb27ba8240f..049a73b5f882 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -290,6 +290,10 @@ static const struct imon_usb_dev_descr imon_OEM_VFD = { { 0x000100000000ffeell, KEY_VOLUMEUP }, { 0x010000000000ffeell, KEY_VOLUMEDOWN }, { 0x000000000100ffeell, KEY_MUTE }, + /* iMON VFD HID OEM v1.2 */ + { 0x000000000a00ffeell, KEY_VOLUMEUP }, + { 0x000000000b00ffeell, KEY_VOLUMEDOWN }, + { 0x000000000c00ffeell, KEY_MUTE }, /* 0xffdc iMON MCE VFD */ { 0x00010000ffffffeell, KEY_VOLUMEUP }, { 0x01000000ffffffeell, KEY_VOLUMEDOWN }, From cd4ce68875f406b0cf3f5a94c0fd22989689222f Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Tue, 30 Dec 2025 18:09:15 +0100 Subject: [PATCH 174/394] media: rzg2l-cru: Skip ICnMC configuration when ICnSVC is used When the CRU is configured to use ICnSVC for virtual channel mapping, as on the RZ/{G3E, V2H/P} SoC, the ICnMC register must not be programmed. Return early after setting up ICnSVC to avoid overriding the ICnMC register, which is not applicable in this mode. This prevents unintended register programming when ICnSVC is enabled. Cc: stable@vger.kernel.org Fixes: 3c5ca0a48bb0 ("media: rzg2l-cru: Drop function pointer to configure CSI") Signed-off-by: Tommaso Merciai [Rework to not break image format programming] Signed-off-by: Jacopo Mondi Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-cru-regs.h | 1 + .../platform/renesas/rzg2l-cru/rzg2l-video.c | 17 +++++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h index a5a57369ef0e..10e62f2646d0 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h @@ -60,6 +60,7 @@ #define ICnMC_CSCTHR BIT(5) #define ICnMC_INF(x) ((x) << 16) #define ICnMC_VCSEL(x) ((x) << 22) +#define ICnMC_VCSEL_MASK GENMASK(23, 22) #define ICnMC_INF_MASK GENMASK(21, 16) #define ICnMS_IA BIT(2) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 162e2ace6931..6aea7c244df1 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -262,19 +262,24 @@ static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, u8 csi_vc) { const struct rzg2l_cru_info *info = cru->info; - u32 icnmc = ICnMC_INF(ip_fmt->datatype); + u32 icnmc = rzg2l_cru_read(cru, info->image_conv) & ~(ICnMC_INF_MASK | + ICnMC_VCSEL_MASK); + icnmc |= ICnMC_INF(ip_fmt->datatype); + /* + * VC filtering goes through SVC register on G3E/V2H. + * + * FIXME: virtual channel filtering is likely broken and only VC=0 + * works. + */ if (cru->info->regs[ICnSVC]) { rzg2l_cru_write(cru, ICnSVCNUM, csi_vc); rzg2l_cru_write(cru, ICnSVC, ICnSVC_SVC0(0) | ICnSVC_SVC1(1) | ICnSVC_SVC2(2) | ICnSVC_SVC3(3)); + } else { + icnmc |= ICnMC_VCSEL(csi_vc); } - icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK; - - /* Set virtual channel CSI2 */ - icnmc |= ICnMC_VCSEL(csi_vc); - rzg2l_cru_write(cru, info->image_conv, icnmc); } From 8f383bbded25a8fa9f5344eeaa1e197a5cb8fe65 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Tue, 30 Dec 2025 18:09:16 +0100 Subject: [PATCH 175/394] media: rzg2l-cru: Use only frame end interrupts On RZ/G3E the CRU driver relies on the frame end interrupt to detect the completion of an active frame transfer when stopping DMA. Update the driver to enable only frame end interrupts (CRUnIE2_FExE), dropping the usage of the frame start interrupts, which is not required for this operations flow. Fix the interrupt status handling in the DMA stopping state by checking the correct frame end status bits (FExS) instead of the frame start one (FSxS). Add a dedicated CRUnINTS2_FExS() macro to reflect the actual register bit layout and drop the now unused CRUnIE2_FSxE() and CRUnINTS2_FSxS() macros. This ensures that DMA stopping is triggered by the intended frame end events and avoids incorrect interrupt handling. Signed-off-by: Tommaso Merciai Signed-off-by: Jacopo Mondi Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil --- .../media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h | 3 +-- drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 9 ++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h index 10e62f2646d0..23cb50ee8e57 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h @@ -14,12 +14,11 @@ #define CRUnIE_EFE BIT(17) -#define CRUnIE2_FSxE(x) BIT(((x) * 3)) #define CRUnIE2_FExE(x) BIT(((x) * 3) + 1) #define CRUnINTS_SFS BIT(16) -#define CRUnINTS2_FSxS(x) BIT(((x) * 3)) +#define CRUnINTS2_FExS(x) BIT(((x) * 3) + 1) #define CRUnRST_VRESETN BIT(0) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 6aea7c244df1..98b6afbc708d 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -440,7 +440,6 @@ static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru) void rzg3e_cru_enable_interrupts(struct rzg2l_cru_dev *cru) { - rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FSxE(cru->svc_channel)); rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FExE(cru->svc_channel)); } @@ -700,10 +699,10 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) } if (cru->state == RZG2L_CRU_DMA_STOPPING) { - if (irq_status & CRUnINTS2_FSxS(0) || - irq_status & CRUnINTS2_FSxS(1) || - irq_status & CRUnINTS2_FSxS(2) || - irq_status & CRUnINTS2_FSxS(3)) + if (irq_status & CRUnINTS2_FExS(0) || + irq_status & CRUnINTS2_FExS(1) || + irq_status & CRUnINTS2_FExS(2) || + irq_status & CRUnINTS2_FExS(3)) dev_dbg(cru->dev, "IRQ while state stopping\n"); return IRQ_HANDLED; } From 66b3f340e8cc447c4d769680f2c9b0a4cdea0a2c Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 09:24:06 +0100 Subject: [PATCH 176/394] media: rzg2l-cru: Modernize locking usage with guards Use more modern lock guard constructs to express the locking sequences in the rzg2l driver. Also include spinlock.h and mutex.h that were previously missing. Signed-off-by: Jacopo Mondi Reviewed-by: Lad Prabhakar Tested-by: Tommaso Merciai Reviewed-by: Tommaso Merciai Reviewed-by: Daniel Scally Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-cru.h | 2 ++ .../platform/renesas/rzg2l-cru/rzg2l-video.c | 31 ++++++------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 3a200db15730..be4a9a4953d1 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -9,7 +9,9 @@ #define __RZG2L_CRU__ #include +#include #include +#include #include #include diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 98b6afbc708d..0739169f4439 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -110,10 +110,10 @@ static void return_unused_buffers(struct rzg2l_cru_dev *cru, enum vb2_buffer_state state) { struct rzg2l_cru_buffer *buf, *node; - unsigned long flags; unsigned int i; - spin_lock_irqsave(&cru->qlock, flags); + guard(spinlock_irqsave)(&cru->qlock); + for (i = 0; i < cru->num_buf; i++) { if (cru->queue_buf[i]) { vb2_buffer_done(&cru->queue_buf[i]->vb2_buf, @@ -126,7 +126,6 @@ static void return_unused_buffers(struct rzg2l_cru_dev *cru, vb2_buffer_done(&buf->vb.vb2_buf, state); list_del(&buf->list); } - spin_unlock_irqrestore(&cru->qlock, flags); } static int rzg2l_cru_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, @@ -165,13 +164,9 @@ static void rzg2l_cru_buffer_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vb->vb2_queue); - unsigned long flags; - - spin_lock_irqsave(&cru->qlock, flags); + guard(spinlock_irqsave)(&cru->qlock); list_add_tail(to_buf_list(vbuf), &cru->buf_list); - - spin_unlock_irqrestore(&cru->qlock, flags); } static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, @@ -465,7 +460,6 @@ void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru) int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) { struct v4l2_mbus_framefmt *fmt = rzg2l_cru_ip_get_src_fmt(cru); - unsigned long flags; u8 csi_vc; int ret; @@ -475,7 +469,7 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) csi_vc = ret; cru->svc_channel = csi_vc; - spin_lock_irqsave(&cru->qlock, flags); + guard(spinlock_irqsave)(&cru->qlock); /* Select a video input */ rzg2l_cru_write(cru, CRUnCTRL, CRUnCTRL_VINSEL(0)); @@ -492,7 +486,6 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) /* Initialize image convert */ ret = rzg2l_cru_initialize_image_conv(cru, fmt, csi_vc); if (ret) { - spin_unlock_irqrestore(&cru->qlock, flags); return ret; } @@ -502,8 +495,6 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) /* Enable image processing reception */ rzg2l_cru_write(cru, ICnEN, ICnEN_ICEN); - spin_unlock_irqrestore(&cru->qlock, flags); - return 0; } @@ -573,16 +564,15 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) { struct rzg2l_cru_dev *cru = data; unsigned int handled = 0; - unsigned long flags; u32 irq_status; u32 amnmbs; int slot; - spin_lock_irqsave(&cru->qlock, flags); + guard(spinlock_irqsave)(&cru->qlock); irq_status = rzg2l_cru_read(cru, CRUnINTS); if (!irq_status) - goto done; + return IRQ_RETVAL(handled); handled = 1; @@ -591,14 +581,14 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ if (cru->state == RZG2L_CRU_DMA_STOPPED) { dev_dbg(cru->dev, "IRQ while state stopped\n"); - goto done; + return IRQ_RETVAL(handled); } /* Increase stop retries if capture status is 'RZG2L_CRU_DMA_STOPPING' */ if (cru->state == RZG2L_CRU_DMA_STOPPING) { if (irq_status & CRUnINTS_SFS) dev_dbg(cru->dev, "IRQ while state stopping\n"); - goto done; + return IRQ_RETVAL(handled); } /* Prepare for capture and update state */ @@ -621,7 +611,7 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) if (cru->state == RZG2L_CRU_DMA_STARTING) { if (slot != 0) { dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); - goto done; + return IRQ_RETVAL(handled); } dev_dbg(cru->dev, "Capture start synced!\n"); @@ -646,9 +636,6 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) /* Prepare for next frame */ rzg2l_cru_fill_hw_slot(cru, slot); -done: - spin_unlock_irqrestore(&cru->qlock, flags); - return IRQ_RETVAL(handled); } From 4acf167fa3696a1f3abfeb7ddcafb2a3e084a638 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 09:33:27 +0100 Subject: [PATCH 177/394] media: rzg2l-cru: Use proper guard() in irq handler The irq handler uses a scoped_guard() that covers the whole function body. Replace it with a more appropriate guard() and reduce the indentation. Signed-off-by: Jacopo Mondi Reviewed-by: Lad Prabhakar Tested-by: Tommaso Merciai Reviewed-by: Tommaso Merciai Reviewed-by: Daniel Scally Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-video.c | 116 +++++++++--------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 0739169f4439..75928b0f48be 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -670,70 +670,70 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) u32 irq_status; int slot; - scoped_guard(spinlock, &cru->qlock) { - irq_status = rzg2l_cru_read(cru, CRUnINTS2); - if (!irq_status) - return IRQ_NONE; + guard(spinlock)(&cru->qlock); - dev_dbg(cru->dev, "CRUnINTS2 0x%x\n", irq_status); + irq_status = rzg2l_cru_read(cru, CRUnINTS2); + if (!irq_status) + return IRQ_NONE; - rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2)); + dev_dbg(cru->dev, "CRUnINTS2 0x%x\n", irq_status); - /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ - if (cru->state == RZG2L_CRU_DMA_STOPPED) { - dev_dbg(cru->dev, "IRQ while state stopped\n"); - return IRQ_HANDLED; - } + rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2)); - if (cru->state == RZG2L_CRU_DMA_STOPPING) { - if (irq_status & CRUnINTS2_FExS(0) || - irq_status & CRUnINTS2_FExS(1) || - irq_status & CRUnINTS2_FExS(2) || - irq_status & CRUnINTS2_FExS(3)) - dev_dbg(cru->dev, "IRQ while state stopping\n"); - return IRQ_HANDLED; - } - - slot = rzg3e_cru_get_current_slot(cru); - if (slot < 0) - return IRQ_HANDLED; - - dev_dbg(cru->dev, "Current written slot: %d\n", slot); - cru->buf_addr[slot] = 0; - - /* - * To hand buffers back in a known order to userspace start - * to capture first from slot 0. - */ - if (cru->state == RZG2L_CRU_DMA_STARTING) { - if (slot != 0) { - dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); - return IRQ_HANDLED; - } - dev_dbg(cru->dev, "Capture start synced!\n"); - cru->state = RZG2L_CRU_DMA_RUNNING; - } - - /* Capture frame */ - if (cru->queue_buf[slot]) { - struct vb2_v4l2_buffer *buf = cru->queue_buf[slot]; - - buf->field = cru->format.field; - buf->sequence = cru->sequence; - buf->vb2_buf.timestamp = ktime_get_ns(); - vb2_buffer_done(&buf->vb2_buf, VB2_BUF_STATE_DONE); - cru->queue_buf[slot] = NULL; - } else { - /* Scratch buffer was used, dropping frame. */ - dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); - } - - cru->sequence++; - - /* Prepare for next frame */ - rzg2l_cru_fill_hw_slot(cru, slot); + /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ + if (cru->state == RZG2L_CRU_DMA_STOPPED) { + dev_dbg(cru->dev, "IRQ while state stopped\n"); + return IRQ_HANDLED; } + if (cru->state == RZG2L_CRU_DMA_STOPPING) { + if (irq_status & CRUnINTS2_FExS(0) || + irq_status & CRUnINTS2_FExS(1) || + irq_status & CRUnINTS2_FExS(2) || + irq_status & CRUnINTS2_FExS(3)) + dev_dbg(cru->dev, "IRQ while state stopping\n"); + return IRQ_HANDLED; + } + + slot = rzg3e_cru_get_current_slot(cru); + if (slot < 0) + return IRQ_HANDLED; + + dev_dbg(cru->dev, "Current written slot: %d\n", slot); + cru->buf_addr[slot] = 0; + + /* + * To hand buffers back in a known order to userspace start + * to capture first from slot 0. + */ + if (cru->state == RZG2L_CRU_DMA_STARTING) { + if (slot != 0) { + dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); + return IRQ_HANDLED; + } + dev_dbg(cru->dev, "Capture start synced!\n"); + cru->state = RZG2L_CRU_DMA_RUNNING; + } + + /* Capture frame */ + if (cru->queue_buf[slot]) { + struct vb2_v4l2_buffer *buf = cru->queue_buf[slot]; + + buf->field = cru->format.field; + buf->sequence = cru->sequence; + buf->vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&buf->vb2_buf, VB2_BUF_STATE_DONE); + cru->queue_buf[slot] = NULL; + } else { + /* Scratch buffer was used, dropping frame. */ + dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); + } + + cru->sequence++; + + /* Prepare for next frame */ + rzg2l_cru_fill_hw_slot(cru, slot); + return IRQ_HANDLED; } From d921f21e27626b6684d8c8222925abafae4cfd74 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 12 Feb 2026 15:38:11 +0100 Subject: [PATCH 178/394] media: rzg2l-cru: Remove locking from start/stop routines The start/stop streaming routines do not need to lock the whole function body against possible concurrent accesses to the CRU buffers or hardware registers. The stop function starts by disabling interrupts, and only this portion needs to be protected not to race against a possible IRQ. Once interrupts are disabled, nothing in the video device driver can race and once the peripheral has been disabled we can release all pending buffers. Signed-off-by: Jacopo Mondi Reviewed-by: Lad Prabhakar Reviewed-by: Daniel Scally Tested-by: Tommaso Merciai Reviewed-by: Tommaso Merciai Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-video.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 75928b0f48be..96c71f1357f8 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -340,23 +340,19 @@ bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru) void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) { unsigned int retries = 0; - unsigned long flags; u32 icnms; - spin_lock_irqsave(&cru->qlock, flags); - - /* Disable and clear the interrupt */ - cru->info->disable_interrupts(cru); + scoped_guard(spinlock_irq, &cru->qlock) { + /* Disable and clear the interrupt */ + cru->info->disable_interrupts(cru); + } /* Stop the operation of image conversion */ rzg2l_cru_write(cru, ICnEN, 0); /* Wait for streaming to stop */ - while ((rzg2l_cru_read(cru, ICnMS) & ICnMS_IA) && retries++ < RZG2L_RETRIES) { - spin_unlock_irqrestore(&cru->qlock, flags); + while ((rzg2l_cru_read(cru, ICnMS) & ICnMS_IA) && retries++ < RZG2L_RETRIES) msleep(RZG2L_TIMEOUT_MS); - spin_lock_irqsave(&cru->qlock, flags); - } icnms = rzg2l_cru_read(cru, ICnMS) & ICnMS_IA; if (icnms) @@ -400,8 +396,6 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) /* Resets the image processing module */ rzg2l_cru_write(cru, CRUnRST, 0); - - spin_unlock_irqrestore(&cru->qlock, flags); } static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru) @@ -469,8 +463,6 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) csi_vc = ret; cru->svc_channel = csi_vc; - guard(spinlock_irqsave)(&cru->qlock); - /* Select a video input */ rzg2l_cru_write(cru, CRUnCTRL, CRUnCTRL_VINSEL(0)); From 84a4664d8c57318757f4cb86998a7e592b0dc376 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 12 Feb 2026 15:38:38 +0100 Subject: [PATCH 179/394] media: rzg2l-cru: Do not use irqsave when not needed The return_unused_buffers() and rzg2l_cru_buffer_queue() functions are never called from an interrupt context, hence they do not need to use the irqsave version of the spinlock primitives. Signed-off-by: Jacopo Mondi Tested-by: Tommaso Merciai Reviewed-by: Tommaso Merciai Reviewed-by: Daniel Scally Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 96c71f1357f8..b2d1b6a4aaaa 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -112,7 +112,7 @@ static void return_unused_buffers(struct rzg2l_cru_dev *cru, struct rzg2l_cru_buffer *buf, *node; unsigned int i; - guard(spinlock_irqsave)(&cru->qlock); + guard(spinlock_irq)(&cru->qlock); for (i = 0; i < cru->num_buf; i++) { if (cru->queue_buf[i]) { @@ -165,7 +165,7 @@ static void rzg2l_cru_buffer_queue(struct vb2_buffer *vb) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vb->vb2_queue); - guard(spinlock_irqsave)(&cru->qlock); + guard(spinlock_irq)(&cru->qlock); list_add_tail(to_buf_list(vbuf), &cru->buf_list); } From a8de479fabce2d0a63d958e3ff30a053c1b3c4bb Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 09:35:35 +0100 Subject: [PATCH 180/394] media: rzg2l-cru: Remove wrong locking comment A function documented as "need to hold qlock before calling" actually takes the lock itself. Drop the comment and prepare to replace it with proper annotations where appropriate. Signed-off-by: Jacopo Mondi Reviewed-by: Tommaso Merciai Reviewed-by: Daniel Scally Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index b2d1b6a4aaaa..c49131587679 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -105,7 +105,6 @@ __rzg2l_cru_read_constant(struct rzg2l_cru_dev *cru, u32 offset) __rzg2l_cru_read_constant(cru, offset) : \ __rzg2l_cru_read(cru, offset)) -/* Need to hold qlock before calling */ static void return_unused_buffers(struct rzg2l_cru_dev *cru, enum vb2_buffer_state state) { From c3e0b2e34ef1f177a459ead54d1f66123149ec46 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 09:17:23 +0100 Subject: [PATCH 181/394] media: rz2gl-cru: Introduce a spinlock for hw operations The CRU driver uses a single spinlock to protect the buffers queue and the hardware operations. This single spinlock is held for the whole duration of the interrupt handler, causing all other driver's operations to freeze. Under heavy system stress conditions with userspace not providing buffers fast enough, this causes loss of frames. Prepare to re-work the driver locking by introducing (but not using yet) a new spinlock to protect the hardware registers programming. Signed-off-by: Jacopo Mondi Reviewed-by: Tommaso Merciai Reviewed-by: Daniel Scally Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h | 10 +++++++--- drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index be4a9a4953d1..12d574182eb8 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -111,7 +111,6 @@ struct rzg2l_cru_info { * @v4l2_dev: V4L2 device * @num_buf: Holds the current number of buffers enabled * @svc_channel: SVC0/1/2/3 to use for RZ/G3E - * @buf_addr: Memory addresses where current video data is written. * @notifier: V4L2 asynchronous subdevs notifier * * @ip: Image processing subdev info @@ -120,6 +119,10 @@ struct rzg2l_cru_info { * @mdev_lock: protects the count, notifier and csi members * @pad: media pad for the video device entity * + * @hw_lock: protects the slot counter, hardware programming of + * slot addresses and the @buf_addr[] list + * @buf_addr: Memory addresses where current video data is written + * * @lock: protects @queue * @queue: vb2 buffers queue * @scratch: cpu address for scratch buffer @@ -149,8 +152,6 @@ struct rzg2l_cru_dev { u8 num_buf; u8 svc_channel; - dma_addr_t buf_addr[RZG2L_CRU_HW_BUFFER_DEFAULT]; - struct v4l2_async_notifier notifier; struct rzg2l_cru_ip ip; @@ -159,6 +160,9 @@ struct rzg2l_cru_dev { struct mutex mdev_lock; struct media_pad pad; + spinlock_t hw_lock; + dma_addr_t buf_addr[RZG2L_CRU_HW_BUFFER_DEFAULT]; + struct mutex lock; struct vb2_queue queue; void *scratch; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index c49131587679..b6616d54f8a3 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -843,6 +843,7 @@ int rzg2l_cru_dma_register(struct rzg2l_cru_dev *cru) mutex_init(&cru->lock); INIT_LIST_HEAD(&cru->buf_list); + spin_lock_init(&cru->hw_lock); spin_lock_init(&cru->qlock); cru->state = RZG2L_CRU_DMA_STOPPED; From ffd977914e162fa73aa1200c998fb555221ece20 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 09:50:10 +0100 Subject: [PATCH 182/394] media: rzg2l-cru: Split hw locking from buffers Split the locking between a spinlock dedicated to protect the hardware slots programming (hw_lock) and one lock (qlock) to protect the queue of buffers submitted by userspace. Do not rework the locking strategy yet but start simply by splitting the locking in two. Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-video.c | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index b6616d54f8a3..5769dbcbd084 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -111,19 +111,21 @@ static void return_unused_buffers(struct rzg2l_cru_dev *cru, struct rzg2l_cru_buffer *buf, *node; unsigned int i; - guard(spinlock_irq)(&cru->qlock); - - for (i = 0; i < cru->num_buf; i++) { - if (cru->queue_buf[i]) { - vb2_buffer_done(&cru->queue_buf[i]->vb2_buf, - state); - cru->queue_buf[i] = NULL; + scoped_guard(spinlock_irq, &cru->hw_lock) { + for (i = 0; i < cru->num_buf; i++) { + if (cru->queue_buf[i]) { + vb2_buffer_done(&cru->queue_buf[i]->vb2_buf, + state); + cru->queue_buf[i] = NULL; + } } } - list_for_each_entry_safe(buf, node, &cru->buf_list, list) { - vb2_buffer_done(&buf->vb.vb2_buf, state); - list_del(&buf->list); + scoped_guard(spinlock_irq, &cru->qlock) { + list_for_each_entry_safe(buf, node, &cru->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); + } } } @@ -203,6 +205,8 @@ static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot) dev_dbg(cru->dev, "Filling HW slot: %d\n", slot); + guard(spinlock)(&cru->qlock); + if (list_empty(&cru->buf_list)) { cru->queue_buf[slot] = NULL; phys_addr = cru->scratch_phys; @@ -341,7 +345,7 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) unsigned int retries = 0; u32 icnms; - scoped_guard(spinlock_irq, &cru->qlock) { + scoped_guard(spinlock_irq, &cru->hw_lock) { /* Disable and clear the interrupt */ cru->info->disable_interrupts(cru); } @@ -559,7 +563,7 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) u32 amnmbs; int slot; - guard(spinlock_irqsave)(&cru->qlock); + guard(spinlock_irqsave)(&cru->hw_lock); irq_status = rzg2l_cru_read(cru, CRUnINTS); if (!irq_status) @@ -661,7 +665,7 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) u32 irq_status; int slot; - guard(spinlock)(&cru->qlock); + guard(spinlock)(&cru->hw_lock); irq_status = rzg2l_cru_read(cru, CRUnINTS2); if (!irq_status) From f067b1c9c25079c182aebf50d33b8bc8c49e3a1b Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 09:55:00 +0100 Subject: [PATCH 183/394] media: rzg2l-cru: Manually track active slot number The CRU cycles over the hardware slots where the destination address for the next frame has to be programmed. The RZ/G2L version of the IP has a register that tells which is the last used slot by the hardware but, unfortunately, such register is not available on RZ/G3E and RZ/V2H(P). The driver currently compares the value of the AMnMADRSL/H register which report "the memory address which the current video data was written to" and compares it with the address programmed in the slots. This heuristic requires a bit of book keeping and proper locking. As the driver handles the FrameEnd interrupt, it's way easier to keep track of the slot that has been used by ourselves with a driver variable. Signed-off-by: Jacopo Mondi Tested-by: Tommaso Merciai Reviewed-by: Tommaso Merciai Reviewed-by: Daniel Scally Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-cru.h | 6 ++-- .../platform/renesas/rzg2l-cru/rzg2l-video.c | 31 ++----------------- 2 files changed, 7 insertions(+), 30 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 12d574182eb8..25f17069585c 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -119,9 +119,10 @@ struct rzg2l_cru_info { * @mdev_lock: protects the count, notifier and csi members * @pad: media pad for the video device entity * - * @hw_lock: protects the slot counter, hardware programming of - * slot addresses and the @buf_addr[] list + * @hw_lock: protects the @active_slot counter, hardware programming + * of slot addresses and the @buf_addr[] list * @buf_addr: Memory addresses where current video data is written + * @active_slot: The slot in use * * @lock: protects @queue * @queue: vb2 buffers queue @@ -162,6 +163,7 @@ struct rzg2l_cru_dev { spinlock_t hw_lock; dma_addr_t buf_addr[RZG2L_CRU_HW_BUFFER_DEFAULT]; + unsigned int active_slot; struct mutex lock; struct vb2_queue queue; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 5769dbcbd084..b02940369a18 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -634,31 +634,6 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) return IRQ_RETVAL(handled); } -static int rzg3e_cru_get_current_slot(struct rzg2l_cru_dev *cru) -{ - u64 amnmadrs; - int slot; - - /* - * When AMnMADRSL is read, AMnMADRSH of the higher-order - * address also latches the address. - * - * AMnMADRSH must be read after AMnMADRSL has been read. - */ - amnmadrs = rzg2l_cru_read(cru, AMnMADRSL); - amnmadrs |= (u64)rzg2l_cru_read(cru, AMnMADRSH) << 32; - - /* Ensure amnmadrs is within this buffer range */ - for (slot = 0; slot < cru->num_buf; slot++) { - if (amnmadrs >= cru->buf_addr[slot] && - amnmadrs < cru->buf_addr[slot] + cru->format.sizeimage) - return slot; - } - - dev_err(cru->dev, "Invalid MB address 0x%llx (out of range)\n", amnmadrs); - return -EINVAL; -} - irqreturn_t rzg3e_cru_irq(int irq, void *data) { struct rzg2l_cru_dev *cru = data; @@ -690,9 +665,8 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) return IRQ_HANDLED; } - slot = rzg3e_cru_get_current_slot(cru); - if (slot < 0) - return IRQ_HANDLED; + slot = cru->active_slot; + cru->active_slot = (cru->active_slot + 1) % cru->num_buf; dev_dbg(cru->dev, "Current written slot: %d\n", slot); cru->buf_addr[slot] = 0; @@ -769,6 +743,7 @@ static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count goto assert_presetn; } + cru->active_slot = 0; cru->sequence = 0; ret = rzg2l_cru_set_stream(cru, 1); From 1b0e65a1bd7e2673ae40f0adc07349936a17d461 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 10:57:11 +0100 Subject: [PATCH 184/394] media: rz2gl-cru: Return pending buffers in order Buffers are programmed into slots in queueing order. When returning pending buffers we can't simply start from the first slot but we should actually iterate slots starting from the one is use. The rzg3e_cru_irq() handler already uses 'active_slot', make rzg2l_cru_irq() use it as well to know where to start iterating from. As the pattern of iterating over slots in order will be used for slots programming in the next patches, provide an helper macro to do that. While at it, rename return_unused_buffers() to rzg2l_cru_return_buffers(). Signed-off-by: Jacopo Mondi Tested-by: Tommaso Merciai Reviewed-by: Daniel Scally Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-video.c | 68 +++++++++++++------ 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index b02940369a18..715628878b90 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -42,6 +42,24 @@ struct rzg2l_cru_buffer { #define to_buf_list(vb2_buffer) \ (&container_of(vb2_buffer, struct rzg2l_cru_buffer, vb)->list) +/* + * The CRU hardware cycles over its slots when transferring frames. All drivers + * structure that contains programming data for the slots, such as the memory + * destination addresses have to be iterated as they were circular buffers. + * + * Provide here utilities to iterate over slots and the associated data. + */ +static inline unsigned int rzg2l_cru_slot_next(struct rzg2l_cru_dev *cru, + unsigned int slot) +{ + return (slot + 1) % cru->num_buf; +} + +/* Start cycling on cru slots from the one after 'start'. */ +#define for_each_cru_slot_from(cru, slot, start) \ + for ((slot) = rzg2l_cru_slot_next((cru), (start)); \ + (slot) != (start); (slot) = rzg2l_cru_slot_next((cru), (slot))) + /* ----------------------------------------------------------------------------- * DMA operations */ @@ -105,27 +123,35 @@ __rzg2l_cru_read_constant(struct rzg2l_cru_dev *cru, u32 offset) __rzg2l_cru_read_constant(cru, offset) : \ __rzg2l_cru_read(cru, offset)) -static void return_unused_buffers(struct rzg2l_cru_dev *cru, - enum vb2_buffer_state state) +static void rzg2l_cru_return_buffers(struct rzg2l_cru_dev *cru, + enum vb2_buffer_state state) { struct rzg2l_cru_buffer *buf, *node; - unsigned int i; scoped_guard(spinlock_irq, &cru->hw_lock) { - for (i = 0; i < cru->num_buf; i++) { - if (cru->queue_buf[i]) { - vb2_buffer_done(&cru->queue_buf[i]->vb2_buf, - state); - cru->queue_buf[i] = NULL; - } + /* Return the buffer in progress first, if not completed yet. */ + unsigned int slot = cru->active_slot; + + if (cru->queue_buf[slot]) { + vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, state); + cru->queue_buf[slot] = NULL; + } + + /* Return all the pending buffers after the active one. */ + for_each_cru_slot_from(cru, slot, cru->active_slot) { + if (!cru->queue_buf[slot]) + continue; + + vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, state); + cru->queue_buf[slot] = NULL; } } - scoped_guard(spinlock_irq, &cru->qlock) { - list_for_each_entry_safe(buf, node, &cru->buf_list, list) { - vb2_buffer_done(&buf->vb.vb2_buf, state); - list_del(&buf->list); - } + guard(spinlock_irq)(&cru->qlock); + + list_for_each_entry_safe(buf, node, &cru->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); } } @@ -588,16 +614,16 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) /* Prepare for capture and update state */ amnmbs = rzg2l_cru_read(cru, AMnMBS); - slot = amnmbs & AMnMBS_MBSTS; + cru->active_slot = amnmbs & AMnMBS_MBSTS; /* * AMnMBS.MBSTS indicates the destination of Memory Bank (MB). * Recalculate to get the current transfer complete MB. */ - if (slot == 0) + if (cru->active_slot == 0) slot = cru->num_buf - 1; else - slot--; + slot = cru->active_slot - 1; /* * To hand buffers back in a known order to userspace start @@ -666,7 +692,7 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) } slot = cru->active_slot; - cru->active_slot = (cru->active_slot + 1) % cru->num_buf; + cru->active_slot = rzg2l_cru_slot_next(cru, cru->active_slot); dev_dbg(cru->dev, "Current written slot: %d\n", slot); cru->buf_addr[slot] = 0; @@ -737,7 +763,7 @@ static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count cru->scratch = dma_alloc_coherent(cru->dev, cru->format.sizeimage, &cru->scratch_phys, GFP_KERNEL); if (!cru->scratch) { - return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); + rzg2l_cru_return_buffers(cru, VB2_BUF_STATE_QUEUED); dev_err(cru->dev, "Failed to allocate scratch buffer\n"); ret = -ENOMEM; goto assert_presetn; @@ -748,7 +774,7 @@ static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count ret = rzg2l_cru_set_stream(cru, 1); if (ret) { - return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); + rzg2l_cru_return_buffers(cru, VB2_BUF_STATE_QUEUED); goto out; } @@ -785,7 +811,7 @@ static void rzg2l_cru_stop_streaming_vq(struct vb2_queue *vq) dma_free_coherent(cru->dev, cru->format.sizeimage, cru->scratch, cru->scratch_phys); - return_unused_buffers(cru, VB2_BUF_STATE_ERROR); + rzg2l_cru_return_buffers(cru, VB2_BUF_STATE_ERROR); reset_control_assert(cru->presetn); clk_disable_unprepare(cru->vclk); From 145b8d16cc4a450d2f890935dc7617b05caf566c Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Thu, 18 Sep 2025 12:02:06 +0100 Subject: [PATCH 185/394] media: rzg2l-cru: Rework rzg2l_cru_fill_hw_slot() The current implementation of rzg2l_cru_fill_hw_slot() results in the artificial loss of frames. At present whenever a frame-complete IRQ is received the driver fills the hardware slot that was just written to with the address of the next buffer in the driver's queue. If the queue is empty, that hardware slot's address is set to the address of the scratch buffer to enable the capture loop to keep running. There is a minimum of a two-frame delay before that slot will be written to however, and in the intervening period userspace may queue more buffers which could be used. To resolve the issue rework rzg2l_cru_fill_hw_slot() so that it iteratively fills all slots from the queue which currently do not have a buffer assigned, until the queue is empty. The scratch buffer is only resorted to in the event that the queue is empty and the next slot that will be written to does not already have a buffer assigned. Signed-off-by: Daniel Scally Signed-off-by: Jacopo Mondi Tested-by: Tommaso Merciai Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-video.c | 64 +++++++++++-------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 715628878b90..c375cf9e261e 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -214,47 +214,52 @@ static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, } /* - * Moves a buffer from the queue to the HW slot. If no buffer is - * available use the scratch buffer. The scratch buffer is never - * returned to userspace, its only function is to enable the capture - * loop to keep running. + * Move as many buffers as possible from the queue to HW slots If no buffer is + * available use the scratch buffer. The scratch buffer is never returned to + * userspace, its only function is to enable the capture loop to keep running. + * + * @cru: the CRU device + * @slot: the slot that has just completed */ static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot) { - struct vb2_v4l2_buffer *vbuf; struct rzg2l_cru_buffer *buf; + struct vb2_v4l2_buffer *vbuf; + unsigned int next_slot; dma_addr_t phys_addr; - /* A already populated slot shall never be overwritten. */ - if (WARN_ON(cru->queue_buf[slot])) - return; + lockdep_assert_held(&cru->hw_lock); - dev_dbg(cru->dev, "Filling HW slot: %d\n", slot); + /* Find the next slot which hasn't a valid address programmed. */ + for_each_cru_slot_from(cru, next_slot, slot) { + if (cru->queue_buf[next_slot]) + continue; - guard(spinlock)(&cru->qlock); + scoped_guard(spinlock_irqsave, &cru->qlock) { + buf = list_first_entry_or_null(&cru->buf_list, + struct rzg2l_cru_buffer, list); + if (buf) + list_del_init(&buf->list); + } + + if (!buf) { + /* Direct frames to the scratch buffer. */ + phys_addr = cru->scratch_phys; + cru->queue_buf[next_slot] = NULL; + rzg2l_cru_set_slot_addr(cru, next_slot, phys_addr); + return; + } - if (list_empty(&cru->buf_list)) { - cru->queue_buf[slot] = NULL; - phys_addr = cru->scratch_phys; - } else { - /* Keep track of buffer we give to HW */ - buf = list_entry(cru->buf_list.next, - struct rzg2l_cru_buffer, list); vbuf = &buf->vb; - list_del_init(to_buf_list(vbuf)); - cru->queue_buf[slot] = vbuf; - - /* Setup DMA */ + cru->queue_buf[next_slot] = vbuf; phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); + rzg2l_cru_set_slot_addr(cru, next_slot, phys_addr); } - - rzg2l_cru_set_slot_addr(cru, slot, phys_addr); } static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) { const struct rzg2l_cru_info *info = cru->info; - unsigned int slot; u32 amnaxiattr; /* @@ -263,8 +268,14 @@ static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) */ rzg2l_cru_write(cru, AMnMBVALID, AMnMBVALID_MBVALID(cru->num_buf - 1)); - for (slot = 0; slot < cru->num_buf; slot++) - rzg2l_cru_fill_hw_slot(cru, slot); + /* + * Program slot#0 with the first available buffer, if any. Pass to the + * function 'num_buf - 1' as rzg2l_cru_fill_hw_slot() calculates which + * is the next slot to program. + */ + scoped_guard(spinlock_irq, &cru->hw_lock) { + rzg2l_cru_fill_hw_slot(cru, cru->num_buf - 1); + } if (info->has_stride) { u32 stride = cru->format.bytesperline; @@ -695,7 +706,6 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) cru->active_slot = rzg2l_cru_slot_next(cru, cru->active_slot); dev_dbg(cru->dev, "Current written slot: %d\n", slot); - cru->buf_addr[slot] = 0; /* * To hand buffers back in a known order to userspace start From 85d8820d486939a5b3398de4ed34388c7d36f251 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 11 Feb 2026 15:27:37 +0100 Subject: [PATCH 186/394] media: rzg2l-cru: Remove the 'state' variable The cru driver uses a 'state' variable for debugging purpose in the interrupt handler. The state is used to detect invalid usage conditions that are not meant to happen unless the driver has a bug in handling the stop and start conditions. Remove the state variable which seems to be a debugging leftover. Signed-off-by: Jacopo Mondi Reviewed-by: Daniel Scally Tested-by: Tommaso Merciai Signed-off-by: Hans Verkuil --- .../platform/renesas/rzg2l-cru/rzg2l-cru.h | 15 ---- .../platform/renesas/rzg2l-cru/rzg2l-video.c | 71 +------------------ 2 files changed, 3 insertions(+), 83 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 25f17069585c..5bf334e173d2 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -38,20 +38,6 @@ enum rzg2l_csi2_pads { struct rzg2l_cru_dev; -/** - * enum rzg2l_cru_dma_state - DMA states - * @RZG2L_CRU_DMA_STOPPED: No operation in progress - * @RZG2L_CRU_DMA_STARTING: Capture starting up - * @RZG2L_CRU_DMA_RUNNING: Operation in progress have buffers - * @RZG2L_CRU_DMA_STOPPING: Stopping operation - */ -enum rzg2l_cru_dma_state { - RZG2L_CRU_DMA_STOPPED = 0, - RZG2L_CRU_DMA_STARTING, - RZG2L_CRU_DMA_RUNNING, - RZG2L_CRU_DMA_STOPPING, -}; - struct rzg2l_cru_csi { struct v4l2_async_connection *asd; struct v4l2_subdev *subdev; @@ -174,7 +160,6 @@ struct rzg2l_cru_dev { struct vb2_v4l2_buffer *queue_buf[RZG2L_CRU_HW_BUFFER_MAX]; struct list_head buf_list; unsigned int sequence; - enum rzg2l_cru_dma_state state; struct v4l2_pix_format format; }; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index c375cf9e261e..fe8ab1ffea81 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -398,8 +398,6 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) if (icnms) dev_err(cru->dev, "Failed stop HW, something is seriously broken\n"); - cru->state = RZG2L_CRU_DMA_STOPPED; - /* Wait until the FIFO becomes empty */ for (retries = 5; retries > 0; retries--) { if (cru->info->fifo_empty(cru)) @@ -587,8 +585,6 @@ static int rzg2l_cru_set_stream(struct rzg2l_cru_dev *cru, int on) static void rzg2l_cru_stop_streaming(struct rzg2l_cru_dev *cru) { - cru->state = RZG2L_CRU_DMA_STOPPING; - rzg2l_cru_set_stream(cru, 0); } @@ -600,8 +596,6 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) u32 amnmbs; int slot; - guard(spinlock_irqsave)(&cru->hw_lock); - irq_status = rzg2l_cru_read(cru, CRUnINTS); if (!irq_status) return IRQ_RETVAL(handled); @@ -610,20 +604,9 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS)); - /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ - if (cru->state == RZG2L_CRU_DMA_STOPPED) { - dev_dbg(cru->dev, "IRQ while state stopped\n"); - return IRQ_RETVAL(handled); - } + /* Calculate slot and prepare for new capture. */ + guard(spinlock_irqsave)(&cru->hw_lock); - /* Increase stop retries if capture status is 'RZG2L_CRU_DMA_STOPPING' */ - if (cru->state == RZG2L_CRU_DMA_STOPPING) { - if (irq_status & CRUnINTS_SFS) - dev_dbg(cru->dev, "IRQ while state stopping\n"); - return IRQ_RETVAL(handled); - } - - /* Prepare for capture and update state */ amnmbs = rzg2l_cru_read(cru, AMnMBS); cru->active_slot = amnmbs & AMnMBS_MBSTS; @@ -636,20 +619,6 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) else slot = cru->active_slot - 1; - /* - * To hand buffers back in a known order to userspace start - * to capture first from slot 0. - */ - if (cru->state == RZG2L_CRU_DMA_STARTING) { - if (slot != 0) { - dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); - return IRQ_RETVAL(handled); - } - - dev_dbg(cru->dev, "Capture start synced!\n"); - cru->state = RZG2L_CRU_DMA_RUNNING; - } - /* Capture frame */ if (cru->queue_buf[slot]) { cru->queue_buf[slot]->field = cru->format.field; @@ -677,49 +646,18 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) u32 irq_status; int slot; - guard(spinlock)(&cru->hw_lock); - irq_status = rzg2l_cru_read(cru, CRUnINTS2); if (!irq_status) return IRQ_NONE; - dev_dbg(cru->dev, "CRUnINTS2 0x%x\n", irq_status); - rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2)); - /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ - if (cru->state == RZG2L_CRU_DMA_STOPPED) { - dev_dbg(cru->dev, "IRQ while state stopped\n"); - return IRQ_HANDLED; - } - - if (cru->state == RZG2L_CRU_DMA_STOPPING) { - if (irq_status & CRUnINTS2_FExS(0) || - irq_status & CRUnINTS2_FExS(1) || - irq_status & CRUnINTS2_FExS(2) || - irq_status & CRUnINTS2_FExS(3)) - dev_dbg(cru->dev, "IRQ while state stopping\n"); - return IRQ_HANDLED; - } - + guard(spinlock)(&cru->hw_lock); slot = cru->active_slot; cru->active_slot = rzg2l_cru_slot_next(cru, cru->active_slot); dev_dbg(cru->dev, "Current written slot: %d\n", slot); - /* - * To hand buffers back in a known order to userspace start - * to capture first from slot 0. - */ - if (cru->state == RZG2L_CRU_DMA_STARTING) { - if (slot != 0) { - dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); - return IRQ_HANDLED; - } - dev_dbg(cru->dev, "Capture start synced!\n"); - cru->state = RZG2L_CRU_DMA_RUNNING; - } - /* Capture frame */ if (cru->queue_buf[slot]) { struct vb2_v4l2_buffer *buf = cru->queue_buf[slot]; @@ -788,7 +726,6 @@ static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count goto out; } - cru->state = RZG2L_CRU_DMA_STARTING; dev_dbg(cru->dev, "Starting to capture\n"); return 0; @@ -861,8 +798,6 @@ int rzg2l_cru_dma_register(struct rzg2l_cru_dev *cru) spin_lock_init(&cru->hw_lock); spin_lock_init(&cru->qlock); - cru->state = RZG2L_CRU_DMA_STOPPED; - for (i = 0; i < RZG2L_CRU_HW_BUFFER_MAX; i++) cru->queue_buf[i] = NULL; From acb6a00aa3055f13f817829b68661e8d490babe3 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 31 Mar 2026 10:03:47 +0200 Subject: [PATCH 187/394] media: rzg2l-cru: Remove debug printouts from irq Using dev_dbg() in irq handlers to debug per-frame events is marginally useful and possibly not the best idea, as using printk-based helpers introduce latencies that impact the drivers operations. If any tracing/debugging has to be performed around frame events in interrupt handlers, the tracing subsystem offers better alternatives. Drop dev_dgb() calls from the CRU interrupt handlers. Reviewed-by: Daniel Scally Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index fe8ab1ffea81..b6990466165e 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -627,9 +627,6 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, VB2_BUF_STATE_DONE); cru->queue_buf[slot] = NULL; - } else { - /* Scratch buffer was used, dropping frame. */ - dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); } cru->sequence++; @@ -656,8 +653,6 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) slot = cru->active_slot; cru->active_slot = rzg2l_cru_slot_next(cru, cru->active_slot); - dev_dbg(cru->dev, "Current written slot: %d\n", slot); - /* Capture frame */ if (cru->queue_buf[slot]) { struct vb2_v4l2_buffer *buf = cru->queue_buf[slot]; @@ -667,9 +662,6 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data) buf->vb2_buf.timestamp = ktime_get_ns(); vb2_buffer_done(&buf->vb2_buf, VB2_BUF_STATE_DONE); cru->queue_buf[slot] = NULL; - } else { - /* Scratch buffer was used, dropping frame. */ - dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); } cru->sequence++; From 8cb2d88fa5526898a48881956b62c6618635d157 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 26 Mar 2026 16:07:41 +0100 Subject: [PATCH 188/394] media: rzg2l-cru: Simplify irq return value handling The rzg2l_cru_irq() irq handler uses a local variable to store the handler return value. Simplify it by using IRQ_NONE and IRQ_HANDLED. Signed-off-by: Jacopo Mondi Reviewed-by: Tommaso Merciai Reviewed-by: Daniel Scally Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index b6990466165e..5185a547461d 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -591,16 +591,13 @@ static void rzg2l_cru_stop_streaming(struct rzg2l_cru_dev *cru) irqreturn_t rzg2l_cru_irq(int irq, void *data) { struct rzg2l_cru_dev *cru = data; - unsigned int handled = 0; u32 irq_status; u32 amnmbs; int slot; irq_status = rzg2l_cru_read(cru, CRUnINTS); if (!irq_status) - return IRQ_RETVAL(handled); - - handled = 1; + return IRQ_NONE; rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS)); @@ -634,7 +631,7 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data) /* Prepare for next frame */ rzg2l_cru_fill_hw_slot(cru, slot); - return IRQ_RETVAL(handled); + return IRQ_HANDLED; } irqreturn_t rzg3e_cru_irq(int irq, void *data) From b4e6ddce16b8ae0ddbec7c96c675779260ad4697 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Sat, 28 Mar 2026 17:34:51 +0100 Subject: [PATCH 189/394] 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 --- .../renesas/rzv2h-ivc/rzv2h-ivc-video.c | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) 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 b167f1bab7ef..932fed38cf3f 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 @@ static int rzv2h_ivc_start_streaming(struct vb2_queue *q, unsigned int count) 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); From e398a06907913d11096348fd220b986110ce300a Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 18 Nov 2025 10:04:30 +0800 Subject: [PATCH 190/394] media: mali-c55: Remove unneeded semicolon Remove unnecessary semicolons reported by Coccinelle/coccicheck and the semantic patch at scripts/coccinelle/misc/semicolon.cocci. Signed-off-by: Chen Ni Reviewed-by: Daniel Scally Reviewed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c index c1a562cd214e..25dc3226d081 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -458,7 +458,7 @@ static int mali_c55_media_frameworks_init(struct mali_c55 *mali_c55) if (ret) { dev_err(mali_c55->dev, "failed to register V4L2 device\n"); goto err_unregister_media_device; - }; + } mali_c55->notifier.ops = &mali_c55_notifier_ops; v4l2_async_nf_init(&mali_c55->notifier, &mali_c55->v4l2_dev); From 5a11410b3de199d4c1d23f453982777e9c0396f8 Mon Sep 17 00:00:00 2001 From: "jempty.liang" Date: Tue, 13 Jan 2026 07:57:22 +0000 Subject: [PATCH 191/394] media: mali-c55: Initialise dev for tpg/rsz/isp subdevs The subdevices registered by the Mali-C55 driver do not have their 'struct device *dev' member initialized. This is visibile when looking at debug message, as in example: "(NULL device *): collect_streams: sub-device 'mali-c55 tpg' does not support streams" Fix this by initializing the *dev field for each subdevice registered by the Mali-C55 driver. Signed-off-by: jempty.liang Reviewed-by: Daniel Scally Reviewed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-isp.c | 1 + drivers/media/platform/arm/mali-c55/mali-c55-resizer.c | 1 + drivers/media/platform/arm/mali-c55/mali-c55-tpg.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c index 4c0fd1ec741c..dfa5dbe68c46 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c @@ -583,6 +583,7 @@ int mali_c55_register_isp(struct mali_c55 *mali_c55) sd->entity.ops = &mali_c55_isp_media_ops; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; sd->internal_ops = &mali_c55_isp_internal_ops; + sd->dev = mali_c55->dev; strscpy(sd->name, MALI_C55_DRIVER_NAME " isp", sizeof(sd->name)); isp->pads[MALI_C55_ISP_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK | diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c index a8d739af74b6..c4f46651dcee 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c @@ -1070,6 +1070,7 @@ static int mali_c55_register_resizer(struct mali_c55 *mali_c55, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; sd->internal_ops = &mali_c55_resizer_internal_ops; + sd->dev = mali_c55->dev; rsz->pads[MALI_C55_RSZ_SINK_PAD].flags = MEDIA_PAD_FL_SINK; rsz->pads[MALI_C55_RSZ_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE; diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c b/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c index 1af5d2759a83..894f4cf377af 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c @@ -370,6 +370,7 @@ int mali_c55_register_tpg(struct mali_c55 *mali_c55) sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; sd->internal_ops = &mali_c55_tpg_internal_ops; + sd->dev = mali_c55->dev; strscpy(sd->name, MALI_C55_DRIVER_NAME " tpg", sizeof(sd->name)); pad->flags = MEDIA_PAD_FL_SOURCE; From ff57ee85854280c6c0662559ed287591b19fc1d5 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Thu, 15 Jan 2026 10:35:16 +0800 Subject: [PATCH 192/394] media: mali-c55: core: Remove redundant dev_err() The platform_get_irq_byname() function already prints an error message internally upon failure using dev_err_probe(). Therefore, the explicit dev_err() is redundant and results in duplicate error logs. Remove the redundant dev_err() call to clean up the error path. Signed-off-by: Chen Ni Reviewed-by: Daniel Scally Reviewed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c index 25dc3226d081..6505d3be9807 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -833,7 +833,6 @@ static int mali_c55_probe(struct platform_device *pdev) mali_c55->irqnum = platform_get_irq(pdev, 0); if (mali_c55->irqnum < 0) { ret = mali_c55->irqnum; - dev_err(dev, "failed to get interrupt\n"); goto err_deinit_media_frameworks; } From 94c6402e423d36a2bd6f62055a65a0d439d84da7 Mon Sep 17 00:00:00 2001 From: Alper Ak Date: Sat, 7 Feb 2026 12:18:22 +0300 Subject: [PATCH 193/394] media: mali-c55: Fix possible ERR_PTR in enable_streams The media_pad_remote_pad_unique() function returns either a valid pointer or an ERR_PTR() on failure (-ENOTUNIQ if multiple links are enabled, -ENOLINK if no connected pad is found). The return value was assigned directly to isp->remote_src and dereferenced in the next line without checking for errors, which could lead to an ERR_PTR dereference. Add proper error checking with IS_ERR() before dereferencing the pointer. Also set isp->remote_src to NULL on error to maintain consistency with other error paths in the function. Cc: stable@vger.kernel.org Fixes: d5f281f3dd29 ("media: mali-c55: Add Mali-C55 ISP driver") Signed-off-by: Alper Ak Reviewed-by: Jacopo Mondi Reviewed-by: Daniel Scally Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-isp.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c index dfa5dbe68c46..e128adf6ee37 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c @@ -333,6 +333,13 @@ static int mali_c55_isp_enable_streams(struct v4l2_subdev *sd, sink_pad = &isp->pads[MALI_C55_ISP_PAD_SINK_VIDEO]; isp->remote_src = media_pad_remote_pad_unique(sink_pad); + if (IS_ERR(isp->remote_src)) { + ret = PTR_ERR(isp->remote_src); + dev_err(mali_c55->dev, "Failed to get remote source pad: %d\n", ret); + isp->remote_src = NULL; + return ret; + } + src_sd = media_entity_to_v4l2_subdev(isp->remote_src->entity); isp->frame_sequence = 0; From 38e3509dd98d7e970db87e13f8ec7412852a6967 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 15:14:50 +0000 Subject: [PATCH 194/394] media: mali-c55: Add missing of_reserved_mem_device_release() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mali_c55_probe() calls of_reserved_mem_device_init() to associate reserved memory regions with the device. This function allocates a struct rmem_assigned_device and adds it to a global linked list, which must be explicitly released via of_reserved_mem_device_release() — there is no devm variant of this API. However, neither the probe error paths nor mali_c55_remove() called of_reserved_mem_device_release(). Any probe failure after the of_reserved_mem_device_init() call, as well as every normal device removal, leaked the reserved memory association on the global list. Fix this by adding an err_release_mem label at the end of the probe error chain and calling of_reserved_mem_device_release() in mali_c55_remove(). The remove teardown order is also corrected to call mali_c55_media_frameworks_deinit() before kfree(), mirroring the probe init order in reverse. Cc: stable@vger.kernel.org Fixes: d5f281f3dd29 ("media: mali-c55: Add Mali-C55 ISP driver") Signed-off-by: David Carlier Reviewed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-core.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c index 6505d3be9807..305b8cbb9d1c 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -806,8 +806,10 @@ static int mali_c55_probe(struct platform_device *pdev) vb2_dma_contig_set_max_seg_size(dev, UINT_MAX); ret = __mali_c55_power_on(mali_c55); - if (ret) - return dev_err_probe(dev, ret, "failed to power on\n"); + if (ret) { + dev_err_probe(dev, ret, "failed to power on\n"); + goto err_release_mem; + } ret = mali_c55_check_hwcfg(mali_c55); if (ret) @@ -845,6 +847,8 @@ static int mali_c55_probe(struct platform_device *pdev) kfree(mali_c55->context.registers); err_power_off: __mali_c55_power_off(mali_c55); +err_release_mem: + of_reserved_mem_device_release(dev); return ret; } @@ -853,8 +857,9 @@ static void mali_c55_remove(struct platform_device *pdev) { struct mali_c55 *mali_c55 = platform_get_drvdata(pdev); - kfree(mali_c55->context.registers); mali_c55_media_frameworks_deinit(mali_c55); + kfree(mali_c55->context.registers); + of_reserved_mem_device_release(&pdev->dev); } static const struct of_device_id mali_c55_of_match[] = { From 2c9b9bcc2569f52e366ec71ca012542e161f1f8d Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 15:14:51 +0000 Subject: [PATCH 195/394] media: mali-c55: Power-off the peripheral in remove() The Mali C55 driver doesn't depend on PM. For this reason, if pm_runtime is not compiled in it is required to manually power-off the peripheral during the driver's remove() handler. Also pm_runtime_enable() is called during probe but mali_c55_remove() never calls pm_runtime_disable(), leaving the device's runtime PM state enabled after the driver is unbound. Manually power-off the peripheral in remove() if the peripheral has not been suspended using runtime_pm and disable runtime pm. Cc: stable@vger.kernel.org Fixes: d5f281f3dd29 ("media: mali-c55: Add Mali-C55 ISP driver") Signed-off-by: David Carlier Reviewed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c index 305b8cbb9d1c..00aed62c63d9 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -858,6 +858,11 @@ static void mali_c55_remove(struct platform_device *pdev) struct mali_c55 *mali_c55 = platform_get_drvdata(pdev); mali_c55_media_frameworks_deinit(mali_c55); + if (!pm_runtime_suspended(&pdev->dev)) { + __mali_c55_power_off(mali_c55); + pm_runtime_set_suspended(&pdev->dev); + } + pm_runtime_disable(&pdev->dev); kfree(mali_c55->context.registers); of_reserved_mem_device_release(&pdev->dev); } From a1db83cc6f7e88a166c77d9060507ec01d617784 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 28 Mar 2026 15:14:52 +0000 Subject: [PATCH 196/394] media: mali-c55: Disable pm_runtime on probe error When mali_c55_media_frameworks_init() fails, the goto target jumps to err_free_context_registers, skipping pm_runtime_disable() despite pm_runtime having already been enabled earlier in the function. Fix this by adding an err_pm_runtime_disable label and redirecting the frameworks init failure to it, so pm_runtime is properly unwound on that error path. The runtime PM status is also set back to suspended before disabling, to undo the pm_runtime_set_active() from probe. Cc: stable@vger.kernel.org Fixes: d5f281f3dd29 ("media: mali-c55: Add Mali-C55 ISP driver") Signed-off-by: David Carlier Reviewed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c index 00aed62c63d9..ee4a4267415e 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -828,7 +828,7 @@ static int mali_c55_probe(struct platform_device *pdev) ret = mali_c55_media_frameworks_init(mali_c55); if (ret) - goto err_free_context_registers; + goto err_pm_runtime_disable; pm_runtime_idle(&pdev->dev); @@ -842,8 +842,9 @@ static int mali_c55_probe(struct platform_device *pdev) err_deinit_media_frameworks: mali_c55_media_frameworks_deinit(mali_c55); +err_pm_runtime_disable: + pm_runtime_set_suspended(&pdev->dev); pm_runtime_disable(&pdev->dev); -err_free_context_registers: kfree(mali_c55->context.registers); err_power_off: __mali_c55_power_off(mali_c55); From 65efd1314dbcb37e4ab47974095ae97f90751f30 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 May 2026 16:40:32 +0200 Subject: [PATCH 197/394] media: include/uapi/linux/cec*.h: add CEC LIP support Add support for the new Latency Indication Protocol feature. This adds the opcodes and the wrapper functions. Signed-off-by: Hans Verkuil Link: https://patch.msgid.link/8dac7c99b7eab4fcc11d76d50dd08fb4448672f4.1779115235.git.hverkuil+cisco@kernel.org Signed-off-by: Mauro Carvalho Chehab Message-ID: <8dac7c99b7eab4fcc11d76d50dd08fb4448672f4.1779115235.git.hverkuil+cisco@kernel.org> --- include/uapi/linux/cec-funcs.h | 182 +++++++++++++++++++++++++++++++++ include/uapi/linux/cec.h | 24 +++++ 2 files changed, 206 insertions(+) diff --git a/include/uapi/linux/cec-funcs.h b/include/uapi/linux/cec-funcs.h index 189ecf0e13cd..ba4b47de9bf0 100644 --- a/include/uapi/linux/cec-funcs.h +++ b/include/uapi/linux/cec-funcs.h @@ -1701,6 +1701,188 @@ static inline void cec_ops_request_current_latency(const struct cec_msg *msg, } +/* Latency Indication Protocol Feature */ +/* Only for CEC 2.0 and up */ +static inline void cec_msg_request_lip_support(struct cec_msg *msg, + int reply, __u16 phys_addr) +{ + msg->len = 4; + msg->msg[1] = CEC_MSG_REQUEST_LIP_SUPPORT; + msg->msg[2] = phys_addr >> 8; + msg->msg[3] = phys_addr & 0xff; + msg->reply = reply ? CEC_MSG_REPORT_LIP_SUPPORT : 0; +} + +static inline void cec_ops_request_lip_support(const struct cec_msg *msg, + __u16 *phys_addr) +{ + *phys_addr = (msg->msg[2] << 8) | msg->msg[3]; +} + +static inline void cec_msg_report_lip_support(struct cec_msg *msg, __u32 sqid) +{ + msg->len = 6; + msg->msg[1] = CEC_MSG_REPORT_LIP_SUPPORT; + msg->msg[2] = sqid >> 24; + msg->msg[3] = (sqid >> 16) & 0xff; + msg->msg[4] = (sqid >> 8) & 0xff; + msg->msg[5] = sqid & 0xff; +} + +static inline void cec_ops_report_lip_support(const struct cec_msg *msg, + __u32 *sqid) +{ + *sqid = (msg->msg[2] << 24) | (msg->msg[3] << 16) | + (msg->msg[4] << 8) | msg->msg[5]; +} + +static inline void cec_msg_request_audio_and_video_latency(struct cec_msg *msg, + int reply, __u8 video_format, + __u8 hdr_format, __u8 vrr_format, + __u8 audio_format, + __u8 audio_format_extension) +{ + msg->len = 6; + msg->msg[1] = CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY; + msg->msg[2] = video_format; + msg->msg[3] = hdr_format; + msg->msg[4] = vrr_format; + msg->msg[5] = audio_format; + if (audio_format >= 1 && audio_format <= 31) { + msg->msg[6] = audio_format_extension; + msg->len++; + } + msg->reply = reply ? CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY : 0; +} + +static inline void cec_ops_request_audio_and_video_latency(const struct cec_msg *msg, + __u8 *video_format, + __u8 *hdr_format, + __u8 *vrr_format, + __u8 *audio_format, + __u8 *audio_format_extension) +{ + *video_format = msg->msg[2]; + *hdr_format = msg->msg[3]; + *vrr_format = msg->msg[4]; + *audio_format = msg->msg[5]; + *audio_format_extension = msg->len > 6 ? msg->msg[6] : 0; +} + +static inline void cec_msg_report_audio_and_video_latency(struct cec_msg *msg, + __u16 video_latency, + __u16 audio_latency) +{ + msg->len = 6; + msg->msg[1] = CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY; + msg->msg[2] = video_latency >> 8; + msg->msg[3] = video_latency & 0xff; + msg->msg[4] = audio_latency >> 8; + msg->msg[5] = audio_latency & 0xff; +} + +static inline void cec_ops_report_audio_and_video_latency(const struct cec_msg *msg, + __u16 *video_latency, + __u16 *audio_latency) +{ + *video_latency = (msg->msg[2] << 8) | msg->msg[3]; + *audio_latency = (msg->msg[4] << 8) | msg->msg[5]; +} + +static inline void cec_msg_request_audio_latency(struct cec_msg *msg, + int reply, + __u8 audio_format, + __u8 audio_format_extension) +{ + msg->len = 3; + msg->msg[1] = CEC_MSG_REQUEST_AUDIO_LATENCY; + msg->msg[2] = audio_format; + if (audio_format >= 1 && audio_format <= 31) { + msg->msg[3] = audio_format_extension; + msg->len++; + } + msg->reply = reply ? CEC_MSG_REPORT_AUDIO_LATENCY : 0; +} + +static inline void cec_ops_request_audio_latency(const struct cec_msg *msg, + __u8 *audio_format, + __u8 *audio_format_extension) +{ + *audio_format = msg->msg[2]; + *audio_format_extension = msg->len > 3 ? msg->msg[3] : 0; +} + +static inline void cec_msg_report_audio_latency(struct cec_msg *msg, + __u16 audio_latency) +{ + msg->len = 4; + msg->msg[1] = CEC_MSG_REPORT_AUDIO_LATENCY; + msg->msg[2] = audio_latency >> 8; + msg->msg[3] = audio_latency & 0xff; +} + +static inline void cec_ops_report_audio_latency(const struct cec_msg *msg, + __u16 *audio_latency) +{ + *audio_latency = (msg->msg[2] << 8) | msg->msg[3]; +} + +static inline void cec_msg_request_video_latency(struct cec_msg *msg, + int reply, __u8 video_format, + __u8 hdr_format, + __u8 vrr_format) +{ + msg->len = 5; + msg->msg[1] = CEC_MSG_REQUEST_VIDEO_LATENCY; + msg->msg[2] = video_format; + msg->msg[3] = hdr_format; + msg->msg[4] = vrr_format; + msg->reply = reply ? CEC_MSG_REPORT_VIDEO_LATENCY : 0; +} + +static inline void cec_ops_request_video_latency(const struct cec_msg *msg, + __u8 *video_format, + __u8 *hdr_format, + __u8 *vrr_format) +{ + *video_format = msg->msg[2]; + *hdr_format = msg->msg[3]; + *vrr_format = msg->msg[4]; +} + +static inline void cec_msg_report_video_latency(struct cec_msg *msg, + __u16 video_latency) +{ + msg->len = 4; + msg->msg[1] = CEC_MSG_REPORT_VIDEO_LATENCY; + msg->msg[2] = video_latency >> 8; + msg->msg[3] = video_latency & 0xff; +} + +static inline void cec_ops_report_video_latency(const struct cec_msg *msg, + __u16 *video_latency) +{ + *video_latency = (msg->msg[2] << 8) | msg->msg[3]; +} + +static inline void cec_msg_update_sqid(struct cec_msg *msg, __u32 sqid) +{ + msg->len = 6; + msg->msg[1] = CEC_MSG_UPDATE_SQID; + msg->msg[2] = sqid >> 24; + msg->msg[3] = (sqid >> 16) & 0xff; + msg->msg[4] = (sqid >> 8) & 0xff; + msg->msg[5] = sqid & 0xff; +} + +static inline void cec_ops_update_sqid(const struct cec_msg *msg, + __u32 *sqid) +{ + *sqid = (msg->msg[2] << 24) | (msg->msg[3] << 16) | + (msg->msg[4] << 8) | msg->msg[5]; +} + + /* Capability Discovery and Control Feature */ static inline void cec_msg_cdc_hec_inquire_state(struct cec_msg *msg, __u16 phys_addr1, diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index b2af1dddd4d7..75bc9e4e0350 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -1104,6 +1104,30 @@ struct cec_event { #define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY 3 +/* Latency Indication Protocol Feature */ +#define CEC_MSG_REQUEST_LIP_SUPPORT 0x50 +#define CEC_MSG_REPORT_LIP_SUPPORT 0x51 +#define CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY 0x52 +/* HDR Format Operand (hdr_format) */ +#define CEC_OP_HDR_FORMAT_GAMMA_SDR 0 +#define CEC_OP_HDR_FORMAT_GAMMA_HDR 1 +#define CEC_OP_HDR_FORMAT_PQ 2 +#define CEC_OP_HDR_FORMAT_HLG 3 +#define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_1 8 +#define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_2 9 +#define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_4 11 +#define CEC_OP_HDR_FORMAT_DV_SINK_LED 16 +#define CEC_OP_HDR_FORMAT_DV_SOURCE_LED 17 +#define CEC_OP_HDR_FORMAT_HDR10PLUS 24 +#define CEC_OP_HDR_FORMAT_ETSI_TS_103_433 32 +#define CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY 0x53 +#define CEC_MSG_REQUEST_AUDIO_LATENCY 0x54 +#define CEC_MSG_REPORT_AUDIO_LATENCY 0x55 +#define CEC_MSG_REQUEST_VIDEO_LATENCY 0x56 +#define CEC_MSG_REPORT_VIDEO_LATENCY 0x57 +#define CEC_MSG_UPDATE_SQID 0x58 + + /* Capability Discovery and Control Feature */ #define CEC_MSG_CDC_MESSAGE 0xf8 /* Ethernet-over-HDMI: nobody ever does this... */ From 300b8963c2f68a70588b086ac9137c2fae9caed5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 May 2026 16:40:33 +0200 Subject: [PATCH 198/394] Documentation: media: add CEC opcodes Add the new opcodes to cec.h.rst.exceptions to avoid documentation build failures. Signed-off-by: Hans Verkuil Link: https://patch.msgid.link/afe5d8f6cdf2dc030011eaf9acd8f68847d1b8cf.1779115235.git.hverkuil+cisco@kernel.org Signed-off-by: Mauro Carvalho Chehab Message-ID: --- .../media/cec/cec.h.rst.exceptions | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Documentation/userspace-api/media/cec/cec.h.rst.exceptions b/Documentation/userspace-api/media/cec/cec.h.rst.exceptions index 65e8be062bdb..5d6f7747c023 100644 --- a/Documentation/userspace-api/media/cec/cec.h.rst.exceptions +++ b/Documentation/userspace-api/media/cec/cec.h.rst.exceptions @@ -524,6 +524,29 @@ ignore define CEC_OP_AUD_OUT_COMPENSATED_DELAY ignore define CEC_OP_AUD_OUT_COMPENSATED_NO_DELAY ignore define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY +ignore define CEC_MSG_REQUEST_LIP_SUPPORT +ignore define CEC_MSG_REPORT_LIP_SUPPORT +ignore define CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY + +ignore define CEC_OP_HDR_FORMAT_GAMMA_SDR +ignore define CEC_OP_HDR_FORMAT_GAMMA_HDR +ignore define CEC_OP_HDR_FORMAT_PQ +ignore define CEC_OP_HDR_FORMAT_HLG +ignore define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_1 +ignore define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_2 +ignore define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_4 +ignore define CEC_OP_HDR_FORMAT_DV_SINK_LED +ignore define CEC_OP_HDR_FORMAT_DV_SOURCE_LED +ignore define CEC_OP_HDR_FORMAT_HDR10PLUS +ignore define CEC_OP_HDR_FORMAT_ETSI_TS_103_433 + +ignore define CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY +ignore define CEC_MSG_REQUEST_AUDIO_LATENCY +ignore define CEC_MSG_REPORT_AUDIO_LATENCY +ignore define CEC_MSG_REQUEST_VIDEO_LATENCY +ignore define CEC_MSG_REPORT_VIDEO_LATENCY +ignore define CEC_MSG_UPDATE_SQID + ignore define CEC_MSG_CDC_MESSAGE ignore define CEC_MSG_CDC_HEC_INQUIRE_STATE From b300f96cb8cbda56c645e0816948803ea9674433 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 May 2026 16:40:34 +0200 Subject: [PATCH 199/394] media: cec: core: add LIP support Add support for the new CEC LIP opcodes. Signed-off-by: Hans Verkuil Link: https://patch.msgid.link/9fb27f4a2133cf46b8e0fc126c2d1ef8b1bc30e1.1779115235.git.hverkuil+cisco@kernel.org Signed-off-by: Mauro Carvalho Chehab Message-ID: <9fb27f4a2133cf46b8e0fc126c2d1ef8b1bc30e1.1779115235.git.hverkuil+cisco@kernel.org> --- drivers/media/cec/core/cec-adap.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 8f7244ac1d43..a90cb84a4b4d 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1098,6 +1098,15 @@ static const u8 cec_msg_size[256] = { [CEC_MSG_REQUEST_CURRENT_LATENCY] = 4 | BCAST, [CEC_MSG_REPORT_CURRENT_LATENCY] = 6 | BCAST, [CEC_MSG_CDC_MESSAGE] = 2 | BCAST, + [CEC_MSG_REQUEST_LIP_SUPPORT] = 4 | DIRECTED, + [CEC_MSG_REPORT_LIP_SUPPORT] = 6 | DIRECTED, + [CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY] = 6 | DIRECTED, + [CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY] = 6 | DIRECTED, + [CEC_MSG_REQUEST_AUDIO_LATENCY] = 3 | DIRECTED, + [CEC_MSG_REPORT_AUDIO_LATENCY] = 4 | DIRECTED, + [CEC_MSG_REQUEST_VIDEO_LATENCY] = 5 | DIRECTED, + [CEC_MSG_REPORT_VIDEO_LATENCY] = 4 | DIRECTED, + [CEC_MSG_UPDATE_SQID] = 6 | DIRECTED, }; /* Called by the CEC adapter if a message is received */ From 79500c929de313550554e604d22f3202955cea20 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 May 2026 16:40:35 +0200 Subject: [PATCH 200/394] media: include/uapi/linux/cec*: clarify which msgs are CEC 2.0 Drop comments about CEC 2.0 from cec-funcs.h. In cec.h clearly comment messages that are CEC 2.0 specific as such. Also rename references to HDMI 2.0 to CEC 2.0. The messages were marked as CEC 2.0 only. That is wrong, these messages are explicitly allowed for any CEC version. Signed-off-by: Hans Verkuil Link: https://patch.msgid.link/098789ee233f777d5ad87a72f09a997373c98d25.1779115235.git.hverkuil+cisco@kernel.org Signed-off-by: Mauro Carvalho Chehab Message-ID: <098789ee233f777d5ad87a72f09a997373c98d25.1779115235.git.hverkuil+cisco@kernel.org> --- include/uapi/linux/cec.h | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index 75bc9e4e0350..81a05c9c0706 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -742,7 +742,7 @@ struct cec_event { #define CEC_OP_PRIM_DEVTYPE_PROCESSOR 7 #define CEC_MSG_SET_MENU_LANGUAGE 0x32 -#define CEC_MSG_REPORT_FEATURES 0xa6 /* HDMI 2.0 */ +#define CEC_MSG_REPORT_FEATURES 0xa6 /* CEC 2.0 */ /* All Device Types Operand (all_device_types) */ #define CEC_OP_ALL_DEVTYPE_TV 0x80 #define CEC_OP_ALL_DEVTYPE_RECORD 0x40 @@ -777,7 +777,7 @@ struct cec_event { #define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX 0x02 #define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_VOLUME_LEVEL 0x01 -#define CEC_MSG_GIVE_FEATURES 0xa5 /* HDMI 2.0 */ +#define CEC_MSG_GIVE_FEATURES 0xa5 /* CEC 2.0 */ /* Deck Control Feature */ @@ -1067,7 +1067,7 @@ struct cec_event { #define CEC_OP_AUD_FMT_ID_CEA861 0 #define CEC_OP_AUD_FMT_ID_CEA861_CXT 1 -#define CEC_MSG_SET_AUDIO_VOLUME_LEVEL 0x73 +#define CEC_MSG_SET_AUDIO_VOLUME_LEVEL 0x73 /* CEC 2.0 */ /* Audio Rate Control Feature */ #define CEC_MSG_SET_AUDIO_RATE 0x9a @@ -1091,7 +1091,6 @@ struct cec_event { /* Dynamic Audio Lipsync Feature */ -/* Only for CEC 2.0 and up */ #define CEC_MSG_REQUEST_CURRENT_LATENCY 0xa7 #define CEC_MSG_REPORT_CURRENT_LATENCY 0xa8 /* Low Latency Mode Operand (low_latency_mode) */ @@ -1105,9 +1104,9 @@ struct cec_event { /* Latency Indication Protocol Feature */ -#define CEC_MSG_REQUEST_LIP_SUPPORT 0x50 -#define CEC_MSG_REPORT_LIP_SUPPORT 0x51 -#define CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY 0x52 +#define CEC_MSG_REQUEST_LIP_SUPPORT 0x50 /* CEC 2.0 */ +#define CEC_MSG_REPORT_LIP_SUPPORT 0x51 /* CEC 2.0 */ +#define CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY 0x52 /* CEC 2.0 */ /* HDR Format Operand (hdr_format) */ #define CEC_OP_HDR_FORMAT_GAMMA_SDR 0 #define CEC_OP_HDR_FORMAT_GAMMA_HDR 1 @@ -1120,12 +1119,12 @@ struct cec_event { #define CEC_OP_HDR_FORMAT_DV_SOURCE_LED 17 #define CEC_OP_HDR_FORMAT_HDR10PLUS 24 #define CEC_OP_HDR_FORMAT_ETSI_TS_103_433 32 -#define CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY 0x53 -#define CEC_MSG_REQUEST_AUDIO_LATENCY 0x54 -#define CEC_MSG_REPORT_AUDIO_LATENCY 0x55 -#define CEC_MSG_REQUEST_VIDEO_LATENCY 0x56 -#define CEC_MSG_REPORT_VIDEO_LATENCY 0x57 -#define CEC_MSG_UPDATE_SQID 0x58 +#define CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY 0x53 /* CEC 2.0 */ +#define CEC_MSG_REQUEST_AUDIO_LATENCY 0x54 /* CEC 2.0 */ +#define CEC_MSG_REPORT_AUDIO_LATENCY 0x55 /* CEC 2.0 */ +#define CEC_MSG_REQUEST_VIDEO_LATENCY 0x56 /* CEC 2.0 */ +#define CEC_MSG_REPORT_VIDEO_LATENCY 0x57 /* CEC 2.0 */ +#define CEC_MSG_UPDATE_SQID 0x58 /* CEC 2.0 */ /* Capability Discovery and Control Feature */ From d5a047e957a754462706fc10c0e648bcc4dcc8e6 Mon Sep 17 00:00:00 2001 From: Arun T Date: Fri, 15 May 2026 23:15:10 +0530 Subject: [PATCH 201/394] platform/x86: int3472: Rename daisy-chain GPIO props to generic Rename the MSI-specific daisy-chain GPIO properties and software node to generic names so they can be reused by other platforms that also require daisy-chain GPIO configuration for TPS68470. Signed-off-by: Arun T Signed-off-by: Sakari Ailus --- drivers/platform/x86/intel/int3472/tps68470_board_data.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c index 28c9e12c85bf..4358dc601923 100644 --- a/drivers/platform/x86/intel/int3472/tps68470_board_data.c +++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c @@ -323,13 +323,13 @@ static struct gpiod_lookup_table msi_prestige_ai_evo_ovti5675_gpios = { } }; -static const struct property_entry msi_prestige_ai_evo_gpio_props[] = { +static const struct property_entry int3472_tps68470_daisy_chain_gpio_props[] = { PROPERTY_ENTRY_BOOL("daisy-chain-enable"), { } }; -static const struct software_node msi_prestige_ai_evo_tps68470_gpio_swnode = { - .properties = msi_prestige_ai_evo_gpio_props, +static const struct software_node int3472_tps68470_daisy_chain_gpio_swnode = { + .properties = int3472_tps68470_daisy_chain_gpio_props, }; static const struct int3472_tps68470_board_data surface_go_tps68470_board_data = { @@ -364,7 +364,7 @@ static const struct int3472_tps68470_board_data dell_7212_tps68470_board_data = static const struct int3472_tps68470_board_data msi_prestige_ai_evo_tps68470_board_data = { .dev_name = "i2c-INT3472:06", .tps68470_regulator_pdata = &msi_prestige_ai_evo_tps68470_pdata, - .tps68470_gpio_swnode = &msi_prestige_ai_evo_tps68470_gpio_swnode, + .tps68470_gpio_swnode = &int3472_tps68470_daisy_chain_gpio_swnode, .n_gpiod_lookups = 1, .tps68470_gpio_lookup_tables = { &msi_prestige_ai_evo_ovti5675_gpios, From cbc6cd55ae5fb6e15bd761f588d7a3057b0c54f8 Mon Sep 17 00:00:00 2001 From: Arun T Date: Fri, 15 May 2026 23:15:11 +0530 Subject: [PATCH 202/394] platform/x86: int3472: Add TPS68470 board data for intel nvl The Intel NVL platform uses IPU8 powered by a TPS68470 PMIC, requiring board data to configure the GPIOs and regulators for proper camera sensor operation. Signed-off-by: Arun T Reviewed-by: Daniel Scally Reviewed-by: Hans de Goede Signed-off-by: Sakari Ailus --- .../x86/intel/int3472/tps68470_board_data.c | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c index 4358dc601923..c53542465fb4 100644 --- a/drivers/platform/x86/intel/int3472/tps68470_board_data.c +++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c @@ -289,6 +289,86 @@ static const struct tps68470_regulator_platform_data msi_prestige_ai_evo_tps6847 }, }; +/* Settings for Intel NVL platform */ + +static struct regulator_consumer_supply ovti13b1_core_consumer_supplies[] = { + REGULATOR_SUPPLY("dvdd", "i2c-OVTI13B1:01"), +}; + +static struct regulator_consumer_supply ovti13b1_ana_consumer_supplies[] = { + REGULATOR_SUPPLY("avdd", "i2c-OVTI13B1:01"), +}; + +static struct regulator_consumer_supply ovti13b1_vcm_consumer_supplies[] = { + REGULATOR_SUPPLY("vcc", "i2c-OVTI13B1:01-VCM"), +}; + +static struct regulator_consumer_supply ovti13b1_vsio_consumer_supplies[] = { + REGULATOR_SUPPLY("dovdd", "i2c-OVTI13B1:01"), +}; + +static const struct regulator_init_data intel_nvl_tps68470_core_reg_init_data = { + .constraints = { + .min_uV = 1200000, + .max_uV = 1200000, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ovti13b1_core_consumer_supplies), + .consumer_supplies = ovti13b1_core_consumer_supplies, +}; + +static const struct regulator_init_data intel_nvl_tps68470_ana_reg_init_data = { + .constraints = { + .min_uV = 2815200, + .max_uV = 2815200, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ovti13b1_ana_consumer_supplies), + .consumer_supplies = ovti13b1_ana_consumer_supplies, +}; +static const struct regulator_init_data intel_nvl_tps68470_vcm_reg_init_data = { + .constraints = { + .min_uV = 2815200, + .max_uV = 2815200, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ovti13b1_vcm_consumer_supplies), + .consumer_supplies = ovti13b1_vcm_consumer_supplies, +}; + +/* Ensure the always-on VIO regulator has the same voltage as VSIO */ +static const struct regulator_init_data intel_nvl_tps68470_vio_reg_init_data = { + .constraints = { + .min_uV = 1800600, + .max_uV = 1800600, + .apply_uV = true, + .always_on = true, + }, +}; +static const struct regulator_init_data intel_nvl_tps68470_vsio_reg_init_data = { + .constraints = { + .min_uV = 1800600, + .max_uV = 1800600, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ovti13b1_vsio_consumer_supplies), + .consumer_supplies = ovti13b1_vsio_consumer_supplies, +}; + +static const struct tps68470_regulator_platform_data intel_nvl_tps68470_pdata = { + .reg_init_data = { + [TPS68470_CORE] = &intel_nvl_tps68470_core_reg_init_data, + [TPS68470_ANA] = &intel_nvl_tps68470_ana_reg_init_data, + [TPS68470_VCM] = &intel_nvl_tps68470_vcm_reg_init_data, + [TPS68470_VIO] = &intel_nvl_tps68470_vio_reg_init_data, + [TPS68470_VSIO] = &intel_nvl_tps68470_vsio_reg_init_data, + }, +}; + static struct gpiod_lookup_table surface_go_int347a_gpios = { .dev_id = "i2c-INT347A:00", .table = { @@ -323,6 +403,14 @@ static struct gpiod_lookup_table msi_prestige_ai_evo_ovti5675_gpios = { } }; +static struct gpiod_lookup_table intel_nvl_tps68470_gpios = { + .dev_id = "i2c-OVTI13B1:01", + .table = { + GPIO_LOOKUP("tps68470-gpio", 9, "reset", GPIO_ACTIVE_LOW), + { } + } +}; + static const struct property_entry int3472_tps68470_daisy_chain_gpio_props[] = { PROPERTY_ENTRY_BOOL("daisy-chain-enable"), { } @@ -371,6 +459,16 @@ static const struct int3472_tps68470_board_data msi_prestige_ai_evo_tps68470_boa }, }; +static const struct int3472_tps68470_board_data intel_nvl_tps68470_board_data = { + .dev_name = "i2c-INT3472:04", + .tps68470_regulator_pdata = &intel_nvl_tps68470_pdata, + .tps68470_gpio_swnode = &int3472_tps68470_daisy_chain_gpio_swnode, + .n_gpiod_lookups = 1, + .tps68470_gpio_lookup_tables = { + &intel_nvl_tps68470_gpios, + }, +}; + static const struct dmi_system_id int3472_tps68470_board_data_table[] = { { .matches = { @@ -424,6 +522,13 @@ static const struct dmi_system_id int3472_tps68470_board_data_table[] = { }, .driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data, }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Nova Lake Client Platform"), + }, + .driver_data = (void *)&intel_nvl_tps68470_board_data, + }, { } }; From 03529c85f4e94950b8de3df7336c253b587febe9 Mon Sep 17 00:00:00 2001 From: Arun T Date: Fri, 15 May 2026 23:15:12 +0530 Subject: [PATCH 203/394] media: ov13b10: Support multiple regulators The OV13B10 sensor driver currently handles a single regulator called avdd, however the sensor can be supplied by up to three regulators. Update the driver to handle all of them together using the regulator bulk API. Signed-off-by: Arun T Reviewed-by: Hans de Goede Signed-off-by: Sakari Ailus --- drivers/media/i2c/ov13b10.c | 47 ++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c index 5421874732bc..b0d34141a13a 100644 --- a/drivers/media/i2c/ov13b10.c +++ b/drivers/media/i2c/ov13b10.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -699,6 +700,12 @@ static const struct ov13b10_mode supported_2_lanes_modes[] = { }, }; +static const char * const ov13b10_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + struct ov13b10 { struct device *dev; @@ -708,7 +715,7 @@ struct ov13b10 { struct v4l2_ctrl_handler ctrl_handler; struct clk *img_clk; - struct regulator *avdd; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov13b10_supply_names)]; struct gpio_desc *reset; /* V4L2 Controls */ @@ -1194,9 +1201,8 @@ static int ov13b10_power_off(struct device *dev) struct ov13b10 *ov13b10 = to_ov13b10(sd); gpiod_set_value_cansleep(ov13b10->reset, 1); - - if (ov13b10->avdd) - regulator_disable(ov13b10->avdd); + regulator_bulk_disable(ARRAY_SIZE(ov13b10_supply_names), + ov13b10->supplies); clk_disable_unprepare(ov13b10->img_clk); @@ -1214,14 +1220,12 @@ static int ov13b10_power_on(struct device *dev) dev_err(dev, "failed to enable imaging clock: %d", ret); return ret; } - - if (ov13b10->avdd) { - ret = regulator_enable(ov13b10->avdd); - if (ret < 0) { - dev_err(dev, "failed to enable avdd: %d", ret); - clk_disable_unprepare(ov13b10->img_clk); - return ret; - } + ret = regulator_bulk_enable(ARRAY_SIZE(ov13b10_supply_names), + ov13b10->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators\n"); + clk_disable_unprepare(ov13b10->img_clk); + return ret; } gpiod_set_value_cansleep(ov13b10->reset, 0); @@ -1475,7 +1479,8 @@ static int ov13b10_get_pm_resources(struct ov13b10 *ov13b) unsigned long freq; int ret; - ov13b->reset = devm_gpiod_get_optional(ov13b->dev, "reset", GPIOD_OUT_LOW); + ov13b->reset = devm_gpiod_get_optional(ov13b->dev, "reset", + GPIOD_OUT_LOW); if (IS_ERR(ov13b->reset)) return dev_err_probe(ov13b->dev, PTR_ERR(ov13b->reset), "failed to get reset gpio\n"); @@ -1491,15 +1496,15 @@ static int ov13b10_get_pm_resources(struct ov13b10 *ov13b) "external clock %lu is not supported\n", freq); - ov13b->avdd = devm_regulator_get_optional(ov13b->dev, "avdd"); - if (IS_ERR(ov13b->avdd)) { - ret = PTR_ERR(ov13b->avdd); - ov13b->avdd = NULL; - if (ret != -ENODEV) - return dev_err_probe(ov13b->dev, ret, - "failed to get avdd regulator\n"); - } + for (unsigned int i = 0; i < ARRAY_SIZE(ov13b10_supply_names); i++) + ov13b->supplies[i].supply = ov13b10_supply_names[i]; + ret = devm_regulator_bulk_get(ov13b->dev, + ARRAY_SIZE(ov13b10_supply_names), + ov13b->supplies); + if (ret) + return dev_err_probe(ov13b->dev, ret, + "failed to get regulators\n"); return 0; } From 5d8004bd279579452d7fe9f38396414fc87e1109 Mon Sep 17 00:00:00 2001 From: Bin Du Date: Tue, 12 May 2026 11:21:14 +0800 Subject: [PATCH 204/394] media: platform: amd: isp4: drop stale list reinit before free Newer Smatch snapshots no longer report the false positive around isp4if_send_fw_cmd(), so the extra list reinitialization before kfree() is no longer needed. Drop the stale list reinit and keep the cleanup path simpler. Signed-off-by: Bin Du Signed-off-by: Sakari Ailus --- drivers/media/platform/amd/isp4/isp4_interface.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c index 15f14eddd683..8d73f66bb42c 100644 --- a/drivers/media/platform/amd/isp4/isp4_interface.c +++ b/drivers/media/platform/amd/isp4/isp4_interface.c @@ -459,12 +459,6 @@ static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, ele = NULL; free_ele: - /* - * The response handler or the timeout/error path must dequeue the - * command element before we own the final reference. - */ - if (ele) - INIT_LIST_HEAD(&ele->list); kfree(ele); return ret; } From e62bb5abdcc0dc57ff09c4db961784582e61cd9b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 15 May 2026 11:12:06 +0200 Subject: [PATCH 205/394] media: platform: amd: add DRM_AMDGPU dependency With DRM_AMDGPU=m and DRM_AMD_ISP=y, it is possible to configura VIDEO_AMD_ISP4_CAPTURE as built-in, but that fails to link: aarch64-linux-ld: drivers/media/platform/amd/isp4/isp4_interface.o: in function `isp4if_gpu_mem_alloc.isra.0': isp4_interface.c:(.text+0x1d0): undefined reference to `isp_kernel_buffer_alloc' aarch64-linux-ld: drivers/media/platform/amd/isp4/isp4_interface.o: in function `isp4if_dealloc_fw_gpumem': isp4_interface.c:(.text+0x26c): undefined reference to `isp_kernel_buffer_free' Add a dependency on the tristate DRM_AMDGPU symbol in addition to the boolean DRM_AMD_ISP=y, so this can only be built-in if the ISP driver is also linked into the kernel itself. Fixes: 9a54c285630c ("media: platform: amd: Introduce amd isp4 capture driver") Signed-off-by: Arnd Bergmann Reviewed-by: Bin Du Tested-by: Bin Du Signed-off-by: Sakari Ailus --- drivers/media/platform/amd/isp4/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/amd/isp4/Kconfig b/drivers/media/platform/amd/isp4/Kconfig index 55dd2dc453a2..9d1927af1cb8 100644 --- a/drivers/media/platform/amd/isp4/Kconfig +++ b/drivers/media/platform/amd/isp4/Kconfig @@ -2,7 +2,9 @@ config VIDEO_AMD_ISP4_CAPTURE tristate "AMD ISP4 and camera driver" - depends on DRM_AMD_ISP && VIDEO_DEV && HAS_DMA + depends on DRM_AMDGPU && DRM_AMD_ISP + depends on HAS_DMA + depends on VIDEO_DEV select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS select VIDEOBUF2_V4L2 From 906e410dcffbbd99fb4081abab817a830033aa28 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Wed, 13 May 2026 08:42:44 +0300 Subject: [PATCH 206/394] media: pwc: Drain fill_buf on start_streaming() failure pwc_isoc_init() submits its isochronous URBs with usb_submit_urb(.., GFP_KERNEL) in a loop. After the first URB is submitted, its completion handler pwc_isoc_handler() can run on another CPU before the loop finishes: start_streaming() pwc_isoc_init() usb_submit_urb(urbs[0], GFP_KERNEL) pwc_isoc_handler(urbs[0]) pdev->fill_buf = pwc_get_next_fill_buf(pdev) usb_submit_urb(urbs[i>0], ..) -> fails pwc_isoc_cleanup(pdev) /* kills URBs */ return ret; pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED) pwc_get_next_fill_buf() detaches a buffer from pdev->queued_bufs and stores it in pdev->fill_buf. The error path in start_streaming() only drains pdev->queued_bufs, so the buffer parked in pdev->fill_buf is leaked. vb2_start_streaming() then triggers WARN_ON(owned_by_drv_count). stop_streaming() already handles this since commit 80b0963e1698 ("[media] pwc: fix WARN_ON"), which added the fill_buf drain in the teardown path but not in the start_streaming() error path. Mirror that handling on failure so start_streaming() returns with no buffer owned by the driver. Issue identified by automated review of the INV-003 series at https://sashiko.dev/ Fixes: 885fe18f5542 ("[media] pwc: Replace private buffer management code with videobuf2") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/usb/pwc/pwc-if.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 59b99ac8fcb6..e2884b04d952 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -730,6 +730,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) pwc_camera_power(pdev, 0); /* And cleanup any queued bufs!! */ pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED); + if (pdev->fill_buf) { + vb2_buffer_done(&pdev->fill_buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); + pdev->fill_buf = NULL; + } } mutex_unlock(&pdev->v4l2_lock); From 436a693af04ffb889aaf87cb69ec1f2b21d3569c Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 13 May 2026 16:02:37 +0900 Subject: [PATCH 207/394] media: radio-si476x: Unregister v4l2_device on probe failure si476x_radio_probe() registers radio->v4l2dev before allocating the V4L2 controls and before registering the video device. If any of those later steps fails, probe returns through the exit label after freeing only the control handler. A failed probe does not call si476x_radio_remove(), so the v4l2_device_unregister() there is not reached. This leaves the parent device reference taken by v4l2_device_register() behind on the error path. Unregister the V4L2 device in the probe error path after freeing the controls. Fixes: b879a9c2a755 ("[media] v4l2: Add a V4L2 driver for SI476X MFD") Cc: stable@vger.kernel.org Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Signed-off-by: Hans Verkuil --- drivers/media/radio/radio-si476x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c index 9980346cb5ea..bfe89782dce4 100644 --- a/drivers/media/radio/radio-si476x.c +++ b/drivers/media/radio/radio-si476x.c @@ -1493,6 +1493,7 @@ static int si476x_radio_probe(struct platform_device *pdev) return 0; exit: v4l2_ctrl_handler_free(radio->videodev.ctrl_handler); + v4l2_device_unregister(&radio->v4l2dev); return rval; } From ffdb7c4ecaed0325cbc20d78f601fec235c49487 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Wed, 13 May 2026 19:38:06 +0300 Subject: [PATCH 208/394] media: staging: atmel-isc: Remove driver atmel-isc has been in staging pending removal since 2022. Hence remove now. Signed-off-by: Eugen Hristev Signed-off-by: Hans Verkuil --- MAINTAINERS | 2 - drivers/staging/media/Kconfig | 4 - drivers/staging/media/Makefile | 1 - .../staging/media/deprecated/atmel/Kconfig | 47 - .../staging/media/deprecated/atmel/Makefile | 8 - drivers/staging/media/deprecated/atmel/TODO | 34 - .../media/deprecated/atmel/atmel-isc-base.c | 2008 ----------------- .../media/deprecated/atmel/atmel-isc-clk.c | 311 --- .../media/deprecated/atmel/atmel-isc-regs.h | 413 ---- .../media/deprecated/atmel/atmel-isc.h | 362 --- .../deprecated/atmel/atmel-sama5d2-isc.c | 644 ------ .../deprecated/atmel/atmel-sama7g5-isc.c | 607 ----- 12 files changed, 4441 deletions(-) delete mode 100644 drivers/staging/media/deprecated/atmel/Kconfig delete mode 100644 drivers/staging/media/deprecated/atmel/Makefile delete mode 100644 drivers/staging/media/deprecated/atmel/TODO delete mode 100644 drivers/staging/media/deprecated/atmel/atmel-isc-base.c delete mode 100644 drivers/staging/media/deprecated/atmel/atmel-isc-clk.c delete mode 100644 drivers/staging/media/deprecated/atmel/atmel-isc-regs.h delete mode 100644 drivers/staging/media/deprecated/atmel/atmel-isc.h delete mode 100644 drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c delete mode 100644 drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c diff --git a/MAINTAINERS b/MAINTAINERS index 63389fea5d15..3124500ff432 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17395,8 +17395,6 @@ F: Documentation/devicetree/bindings/media/atmel,isc.yaml F: Documentation/devicetree/bindings/media/microchip,xisc.yaml F: drivers/media/platform/microchip/microchip-isc* F: drivers/media/platform/microchip/microchip-sama*-isc* -F: drivers/staging/media/deprecated/atmel/atmel-isc* -F: drivers/staging/media/deprecated/atmel/atmel-sama*-isc* F: include/linux/atmel-isc-media.h MICROCHIP ISI DRIVER diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 1aa31bddf970..06cbc5c548ca 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -50,8 +50,4 @@ menuconfig STAGING_MEDIA_DEPRECATED If in doubt, say N here. -if STAGING_MEDIA_DEPRECATED -source "drivers/staging/media/deprecated/atmel/Kconfig" -endif - endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 6f78b0edde1e..6fd7179733d8 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += deprecated/atmel/ obj-$(CONFIG_INTEL_ATOMISP) += atomisp/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ obj-$(CONFIG_VIDEO_MAX96712) += max96712/ diff --git a/drivers/staging/media/deprecated/atmel/Kconfig b/drivers/staging/media/deprecated/atmel/Kconfig deleted file mode 100644 index 418841ea5a0d..000000000000 --- a/drivers/staging/media/deprecated/atmel/Kconfig +++ /dev/null @@ -1,47 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only - -comment "Atmel media platform drivers" - -config VIDEO_ATMEL_ISC - tristate "ATMEL Image Sensor Controller (ISC) support (DEPRECATED)" - depends on V4L_PLATFORM_DRIVERS - depends on VIDEO_DEV && COMMON_CLK - depends on ARCH_AT91 || COMPILE_TEST - depends on !VIDEO_MICROCHIP_ISC_BASE || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select VIDEOBUF2_DMA_CONTIG - select REGMAP_MMIO - select V4L2_FWNODE - select VIDEO_ATMEL_ISC_BASE - help - This module makes the ATMEL Image Sensor Controller available - as a v4l2 device. - - This driver is deprecated and is scheduled for removal by - the beginning of 2026. See the TODO file for more information. - -config VIDEO_ATMEL_XISC - tristate "ATMEL eXtended Image Sensor Controller (XISC) support (DEPRECATED)" - depends on V4L_PLATFORM_DRIVERS - depends on VIDEO_DEV && COMMON_CLK - depends on ARCH_AT91 || COMPILE_TEST - depends on !VIDEO_MICROCHIP_ISC_BASE || COMPILE_TEST - select VIDEOBUF2_DMA_CONTIG - select REGMAP_MMIO - select V4L2_FWNODE - select VIDEO_ATMEL_ISC_BASE - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - This module makes the ATMEL eXtended Image Sensor Controller - available as a v4l2 device. - - This driver is deprecated and is scheduled for removal by - the beginning of 2026. See the TODO file for more information. - -config VIDEO_ATMEL_ISC_BASE - tristate - default n - help - ATMEL ISC and XISC common code base. diff --git a/drivers/staging/media/deprecated/atmel/Makefile b/drivers/staging/media/deprecated/atmel/Makefile deleted file mode 100644 index 34eaeeac5bba..000000000000 --- a/drivers/staging/media/deprecated/atmel/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -atmel-isc-objs = atmel-sama5d2-isc.o -atmel-xisc-objs = atmel-sama7g5-isc.o -atmel-isc-common-objs = atmel-isc-base.o atmel-isc-clk.o - -obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += atmel-isc-common.o -obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel-isc.o -obj-$(CONFIG_VIDEO_ATMEL_XISC) += atmel-xisc.o diff --git a/drivers/staging/media/deprecated/atmel/TODO b/drivers/staging/media/deprecated/atmel/TODO deleted file mode 100644 index 71691df07a80..000000000000 --- a/drivers/staging/media/deprecated/atmel/TODO +++ /dev/null @@ -1,34 +0,0 @@ -The Atmel ISC driver is not compliant with media controller specification. -In order to evolve this driver, it has to move to media controller, to -support enhanced features and future products which embed it. -The move to media controller involves several changes which are -not backwards compatible with the current usability of the driver. - -The best example is the way the format is propagated from the top video -driver /dev/videoX down to the sensor. - -In a simple configuration sensor ==> isc , the isc just calls subdev s_fmt -and controls the sensor directly. This is achieved by having a lot of code -inside the driver that will query the subdev at probe time and make a list -of formats which are usable. -Basically the user has nothing to configure, as the isc will handle -everything at the top level. This is an easy way to capture, but also comes -with the drawback of lack of flexibility. -In a more complicated pipeline -sensor ==> controller 1 ==> controller 2 ==> isc -this will not be achievable, as controller 1 and controller 2 might be -media-controller configurable, and will not propagate the formats down to -the sensor. - -After discussions with the media maintainers, the decision is to move -Atmel ISC to staging as-is, to keep the Kconfig symbols and the users -to the driver in staging. Thus, all the existing users of the non -media-controller paradigm will continue to be happy and use the old config -way. - -The new driver was added in the media subsystem with a different -symbol, with the conversion to media controller done, and new users -of the driver will be able to use all the new features. - -The replacement driver is named VIDEO_MICROCHIP_ISC or -VIDEO_MICROCHIP_XISC depending on the product flavor. diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c deleted file mode 100644 index fb9ee8547392..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c +++ /dev/null @@ -1,2008 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Microchip Image Sensor Controller (ISC) common driver base - * - * Copyright (C) 2016-2019 Microchip Technology, Inc. - * - * Author: Songjun Wu - * Author: Eugen Hristev - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "atmel-isc-regs.h" -#include "atmel-isc.h" - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "debug level (0-2)"); - -static unsigned int sensor_preferred = 1; -module_param(sensor_preferred, uint, 0644); -MODULE_PARM_DESC(sensor_preferred, - "Sensor is preferred to output the specified format (1-on 0-off), default 1"); - -#define ISC_IS_FORMAT_RAW(mbus_code) \ - (((mbus_code) & 0xf000) == 0x3000) - -#define ISC_IS_FORMAT_GREY(mbus_code) \ - (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \ - (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8))) - -static inline void isc_update_v4l2_ctrls(struct isc_device *isc) -{ - struct isc_ctrls *ctrls = &isc->ctrls; - - /* In here we set the v4l2 controls w.r.t. our pipeline config */ - v4l2_ctrl_s_ctrl(isc->r_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]); - v4l2_ctrl_s_ctrl(isc->b_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]); - v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]); - v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]); - - v4l2_ctrl_s_ctrl(isc->r_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_R]); - v4l2_ctrl_s_ctrl(isc->b_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_B]); - v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GR]); - v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GB]); -} - -static inline void isc_update_awb_ctrls(struct isc_device *isc) -{ - struct isc_ctrls *ctrls = &isc->ctrls; - - /* In here we set our actual hw pipeline config */ - - regmap_write(isc->regmap, ISC_WB_O_RGR, - ((ctrls->offset[ISC_HIS_CFG_MODE_R])) | - ((ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); - regmap_write(isc->regmap, ISC_WB_O_BGB, - ((ctrls->offset[ISC_HIS_CFG_MODE_B])) | - ((ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16)); - regmap_write(isc->regmap, ISC_WB_G_RGR, - ctrls->gain[ISC_HIS_CFG_MODE_R] | - (ctrls->gain[ISC_HIS_CFG_MODE_GR] << 16)); - regmap_write(isc->regmap, ISC_WB_G_BGB, - ctrls->gain[ISC_HIS_CFG_MODE_B] | - (ctrls->gain[ISC_HIS_CFG_MODE_GB] << 16)); -} - -static inline void isc_reset_awb_ctrls(struct isc_device *isc) -{ - unsigned int c; - - for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { - /* gains have a fixed point at 9 decimals */ - isc->ctrls.gain[c] = 1 << 9; - /* offsets are in 2's complements */ - isc->ctrls.offset[c] = 0; - } -} - - -static int isc_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct isc_device *isc = vb2_get_drv_priv(vq); - unsigned int size = isc->fmt.fmt.pix.sizeimage; - - if (*nplanes) - return sizes[0] < size ? -EINVAL : 0; - - *nplanes = 1; - sizes[0] = size; - - return 0; -} - -static int isc_buffer_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue); - unsigned long size = isc->fmt.fmt.pix.sizeimage; - - if (vb2_plane_size(vb, 0) < size) { - v4l2_err(&isc->v4l2_dev, "buffer too small (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -EINVAL; - } - - vb2_set_plane_payload(vb, 0, size); - - vbuf->field = isc->fmt.fmt.pix.field; - - return 0; -} - -static void isc_crop_pfe(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 h, w; - - h = isc->fmt.fmt.pix.height; - w = isc->fmt.fmt.pix.width; - - /* - * In case the sensor is not RAW, it will output a pixel (12-16 bits) - * with two samples on the ISC Data bus (which is 8-12) - * ISC will count each sample, so, we need to multiply these values - * by two, to get the real number of samples for the required pixels. - */ - if (!ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) { - h <<= 1; - w <<= 1; - } - - /* - * We limit the column/row count that the ISC will output according - * to the configured resolution that we want. - * This will avoid the situation where the sensor is misconfigured, - * sending more data, and the ISC will just take it and DMA to memory, - * causing corruption. - */ - regmap_write(regmap, ISC_PFE_CFG1, - (ISC_PFE_CFG1_COLMIN(0) & ISC_PFE_CFG1_COLMIN_MASK) | - (ISC_PFE_CFG1_COLMAX(w - 1) & ISC_PFE_CFG1_COLMAX_MASK)); - - regmap_write(regmap, ISC_PFE_CFG2, - (ISC_PFE_CFG2_ROWMIN(0) & ISC_PFE_CFG2_ROWMIN_MASK) | - (ISC_PFE_CFG2_ROWMAX(h - 1) & ISC_PFE_CFG2_ROWMAX_MASK)); - - regmap_update_bits(regmap, ISC_PFE_CFG0, - ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN, - ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN); -} - -static void isc_start_dma(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 sizeimage = isc->fmt.fmt.pix.sizeimage; - u32 dctrl_dview; - dma_addr_t addr0; - - addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0); - regmap_write(regmap, ISC_DAD0 + isc->offsets.dma, addr0); - - switch (isc->config.fourcc) { - case V4L2_PIX_FMT_YUV420: - regmap_write(regmap, ISC_DAD1 + isc->offsets.dma, - addr0 + (sizeimage * 2) / 3); - regmap_write(regmap, ISC_DAD2 + isc->offsets.dma, - addr0 + (sizeimage * 5) / 6); - break; - case V4L2_PIX_FMT_YUV422P: - regmap_write(regmap, ISC_DAD1 + isc->offsets.dma, - addr0 + sizeimage / 2); - regmap_write(regmap, ISC_DAD2 + isc->offsets.dma, - addr0 + (sizeimage * 3) / 4); - break; - default: - break; - } - - dctrl_dview = isc->config.dctrl_dview; - - regmap_write(regmap, ISC_DCTRL + isc->offsets.dma, - dctrl_dview | ISC_DCTRL_IE_IS); - spin_lock(&isc->awb_lock); - regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE); - spin_unlock(&isc->awb_lock); -} - -static void isc_set_pipeline(struct isc_device *isc, u32 pipeline) -{ - struct regmap *regmap = isc->regmap; - struct isc_ctrls *ctrls = &isc->ctrls; - u32 val, bay_cfg; - const u32 *gamma; - unsigned int i; - - /* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */ - for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) { - val = pipeline & BIT(i) ? 1 : 0; - regmap_field_write(isc->pipeline[i], val); - } - - if (!pipeline) - return; - - bay_cfg = isc->config.sd_format->cfa_baycfg; - - regmap_write(regmap, ISC_WB_CFG, bay_cfg); - isc_update_awb_ctrls(isc); - isc_update_v4l2_ctrls(isc); - - regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL); - - gamma = &isc->gamma_table[ctrls->gamma_index][0]; - regmap_bulk_write(regmap, ISC_GAM_BENTRY, gamma, GAMMA_ENTRIES); - regmap_bulk_write(regmap, ISC_GAM_GENTRY, gamma, GAMMA_ENTRIES); - regmap_bulk_write(regmap, ISC_GAM_RENTRY, gamma, GAMMA_ENTRIES); - - isc->config_dpc(isc); - isc->config_csc(isc); - isc->config_cbc(isc); - isc->config_cc(isc); - isc->config_gam(isc); -} - -static int isc_update_profile(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 sr; - int counter = 100; - - regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO); - - regmap_read(regmap, ISC_CTRLSR, &sr); - while ((sr & ISC_CTRL_UPPRO) && counter--) { - usleep_range(1000, 2000); - regmap_read(regmap, ISC_CTRLSR, &sr); - } - - if (counter < 0) { - v4l2_warn(&isc->v4l2_dev, "Time out to update profile\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static void isc_set_histogram(struct isc_device *isc, bool enable) -{ - struct regmap *regmap = isc->regmap; - struct isc_ctrls *ctrls = &isc->ctrls; - - if (enable) { - regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his, - ISC_HIS_CFG_MODE_GR | - (isc->config.sd_format->cfa_baycfg - << ISC_HIS_CFG_BAYSEL_SHIFT) | - ISC_HIS_CFG_RAR); - regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his, - ISC_HIS_CTRL_EN); - regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE); - ctrls->hist_id = ISC_HIS_CFG_MODE_GR; - isc_update_profile(isc); - regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); - - ctrls->hist_stat = HIST_ENABLED; - } else { - regmap_write(regmap, ISC_INTDIS, ISC_INT_HISDONE); - regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his, - ISC_HIS_CTRL_DIS); - - ctrls->hist_stat = HIST_DISABLED; - } -} - -static int isc_configure(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 pfe_cfg0, dcfg, mask, pipeline; - struct isc_subdev_entity *subdev = isc->current_subdev; - - pfe_cfg0 = isc->config.sd_format->pfe_cfg0_bps; - pipeline = isc->config.bits_pipeline; - - dcfg = isc->config.dcfg_imode | isc->dcfg; - - pfe_cfg0 |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE; - mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW | - ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW | - ISC_PFE_CFG0_MODE_MASK | ISC_PFE_CFG0_CCIR_CRC | - ISC_PFE_CFG0_CCIR656 | ISC_PFE_CFG0_MIPI; - - regmap_update_bits(regmap, ISC_PFE_CFG0, mask, pfe_cfg0); - - isc->config_rlp(isc); - - regmap_write(regmap, ISC_DCFG + isc->offsets.dma, dcfg); - - /* Set the pipeline */ - isc_set_pipeline(isc, pipeline); - - /* - * The current implemented histogram is available for RAW R, B, GB, GR - * channels. We need to check if sensor is outputting RAW BAYER - */ - if (isc->ctrls.awb && - ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) - isc_set_histogram(isc, true); - else - isc_set_histogram(isc, false); - - /* Update profile */ - return isc_update_profile(isc); -} - -static int isc_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct isc_device *isc = vb2_get_drv_priv(vq); - struct regmap *regmap = isc->regmap; - struct isc_buffer *buf; - unsigned long flags; - int ret; - - /* Enable stream on the sub device */ - ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1); - if (ret && ret != -ENOIOCTLCMD) { - v4l2_err(&isc->v4l2_dev, "stream on failed in subdev %d\n", - ret); - goto err_start_stream; - } - - ret = pm_runtime_resume_and_get(isc->dev); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, "RPM resume failed in subdev %d\n", - ret); - goto err_pm_get; - } - - ret = isc_configure(isc); - if (unlikely(ret)) - goto err_configure; - - /* Enable DMA interrupt */ - regmap_write(regmap, ISC_INTEN, ISC_INT_DDONE); - - spin_lock_irqsave(&isc->dma_queue_lock, flags); - - isc->sequence = 0; - isc->stop = false; - reinit_completion(&isc->comp); - - isc->cur_frm = list_first_entry(&isc->dma_queue, - struct isc_buffer, list); - list_del(&isc->cur_frm->list); - - isc_crop_pfe(isc); - isc_start_dma(isc); - - spin_unlock_irqrestore(&isc->dma_queue_lock, flags); - - /* if we streaming from RAW, we can do one-shot white balance adj */ - if (ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) - v4l2_ctrl_activate(isc->do_wb_ctrl, true); - - return 0; - -err_configure: - pm_runtime_put_sync(isc->dev); -err_pm_get: - v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); - -err_start_stream: - spin_lock_irqsave(&isc->dma_queue_lock, flags); - list_for_each_entry(buf, &isc->dma_queue, list) - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - INIT_LIST_HEAD(&isc->dma_queue); - spin_unlock_irqrestore(&isc->dma_queue_lock, flags); - - return ret; -} - -static void isc_stop_streaming(struct vb2_queue *vq) -{ - struct isc_device *isc = vb2_get_drv_priv(vq); - unsigned long flags; - struct isc_buffer *buf; - int ret; - - mutex_lock(&isc->awb_mutex); - v4l2_ctrl_activate(isc->do_wb_ctrl, false); - - isc->stop = true; - - /* Wait until the end of the current frame */ - if (isc->cur_frm && !wait_for_completion_timeout(&isc->comp, 5 * HZ)) - v4l2_err(&isc->v4l2_dev, - "Timeout waiting for end of the capture\n"); - - mutex_unlock(&isc->awb_mutex); - - /* Disable DMA interrupt */ - regmap_write(isc->regmap, ISC_INTDIS, ISC_INT_DDONE); - - pm_runtime_put_sync(isc->dev); - - /* Disable stream on the sub device */ - ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); - if (ret && ret != -ENOIOCTLCMD) - v4l2_err(&isc->v4l2_dev, "stream off failed in subdev\n"); - - /* Release all active buffers */ - spin_lock_irqsave(&isc->dma_queue_lock, flags); - if (unlikely(isc->cur_frm)) { - vb2_buffer_done(&isc->cur_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - isc->cur_frm = NULL; - } - list_for_each_entry(buf, &isc->dma_queue, list) - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - INIT_LIST_HEAD(&isc->dma_queue); - spin_unlock_irqrestore(&isc->dma_queue_lock, flags); -} - -static void isc_buffer_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct isc_buffer *buf = container_of(vbuf, struct isc_buffer, vb); - struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue); - unsigned long flags; - - spin_lock_irqsave(&isc->dma_queue_lock, flags); - if (!isc->cur_frm && list_empty(&isc->dma_queue) && - vb2_start_streaming_called(vb->vb2_queue)) { - isc->cur_frm = buf; - isc_start_dma(isc); - } else - list_add_tail(&buf->list, &isc->dma_queue); - spin_unlock_irqrestore(&isc->dma_queue_lock, flags); -} - -static struct isc_format *find_format_by_fourcc(struct isc_device *isc, - unsigned int fourcc) -{ - unsigned int num_formats = isc->num_user_formats; - struct isc_format *fmt; - unsigned int i; - - for (i = 0; i < num_formats; i++) { - fmt = isc->user_formats[i]; - if (fmt->fourcc == fourcc) - return fmt; - } - - return NULL; -} - -static const struct vb2_ops isc_vb2_ops = { - .queue_setup = isc_queue_setup, - .buf_prepare = isc_buffer_prepare, - .start_streaming = isc_start_streaming, - .stop_streaming = isc_stop_streaming, - .buf_queue = isc_buffer_queue, -}; - -static int isc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - strscpy(cap->driver, "microchip-isc", sizeof(cap->driver)); - strscpy(cap->card, "Atmel Image Sensor Controller", sizeof(cap->card)); - - return 0; -} - -static int isc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct isc_device *isc = video_drvdata(file); - u32 index = f->index; - u32 i, supported_index; - - if (index < isc->controller_formats_size) { - f->pixelformat = isc->controller_formats[index].fourcc; - return 0; - } - - index -= isc->controller_formats_size; - - supported_index = 0; - - for (i = 0; i < isc->formats_list_size; i++) { - if (!ISC_IS_FORMAT_RAW(isc->formats_list[i].mbus_code) || - !isc->formats_list[i].sd_support) - continue; - if (supported_index == index) { - f->pixelformat = isc->formats_list[i].fourcc; - return 0; - } - supported_index++; - } - - return -EINVAL; -} - -static int isc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct isc_device *isc = video_drvdata(file); - - *fmt = isc->fmt; - - return 0; -} - -/* - * Checks the current configured format, if ISC can output it, - * considering which type of format the ISC receives from the sensor - */ -static int isc_try_validate_formats(struct isc_device *isc) -{ - int ret; - bool bayer = false, yuv = false, rgb = false, grey = false; - - /* all formats supported by the RLP module are OK */ - switch (isc->try_config.fourcc) { - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SGBRG8: - case V4L2_PIX_FMT_SGRBG8: - case V4L2_PIX_FMT_SRGGB8: - case V4L2_PIX_FMT_SBGGR10: - case V4L2_PIX_FMT_SGBRG10: - case V4L2_PIX_FMT_SGRBG10: - case V4L2_PIX_FMT_SRGGB10: - case V4L2_PIX_FMT_SBGGR12: - case V4L2_PIX_FMT_SGBRG12: - case V4L2_PIX_FMT_SGRBG12: - case V4L2_PIX_FMT_SRGGB12: - ret = 0; - bayer = true; - break; - - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YUV422P: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - ret = 0; - yuv = true; - break; - - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_ABGR32: - case V4L2_PIX_FMT_XBGR32: - case V4L2_PIX_FMT_ARGB444: - case V4L2_PIX_FMT_ARGB555: - ret = 0; - rgb = true; - break; - case V4L2_PIX_FMT_GREY: - case V4L2_PIX_FMT_Y10: - case V4L2_PIX_FMT_Y16: - ret = 0; - grey = true; - break; - default: - /* any other different formats are not supported */ - ret = -EINVAL; - } - v4l2_dbg(1, debug, &isc->v4l2_dev, - "Format validation, requested rgb=%u, yuv=%u, grey=%u, bayer=%u\n", - rgb, yuv, grey, bayer); - - /* we cannot output RAW if we do not receive RAW */ - if ((bayer) && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) - return -EINVAL; - - /* we cannot output GREY if we do not receive RAW/GREY */ - if (grey && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code) && - !ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code)) - return -EINVAL; - - return ret; -} - -/* - * Configures the RLP and DMA modules, depending on the output format - * configured for the ISC. - * If direct_dump == true, just dump raw data 8/16 bits depending on format. - */ -static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump) -{ - isc->try_config.rlp_cfg_mode = 0; - - switch (isc->try_config.fourcc) { - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SGBRG8: - case V4L2_PIX_FMT_SGRBG8: - case V4L2_PIX_FMT_SRGGB8: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 8; - isc->try_config.bpp_v4l2 = 8; - break; - case V4L2_PIX_FMT_SBGGR10: - case V4L2_PIX_FMT_SGBRG10: - case V4L2_PIX_FMT_SGRBG10: - case V4L2_PIX_FMT_SRGGB10: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT10; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_SBGGR12: - case V4L2_PIX_FMT_SGBRG12: - case V4L2_PIX_FMT_SGRBG12: - case V4L2_PIX_FMT_SRGGB12: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT12; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_RGB565: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_RGB565; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_ARGB444: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB444; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_ARGB555: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB555; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_ABGR32: - case V4L2_PIX_FMT_XBGR32: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB32; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 32; - isc->try_config.bpp_v4l2 = 32; - break; - case V4L2_PIX_FMT_YUV420: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC420P; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR; - isc->try_config.bpp = 12; - isc->try_config.bpp_v4l2 = 8; /* only first plane */ - break; - case V4L2_PIX_FMT_YUV422P: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC422P; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 8; /* only first plane */ - break; - case V4L2_PIX_FMT_YUYV: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_YUYV; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_UYVY: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_UYVY; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_VYUY: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_VYUY; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - case V4L2_PIX_FMT_GREY: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY8; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 8; - isc->try_config.bpp_v4l2 = 8; - break; - case V4L2_PIX_FMT_Y16: - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY10 | ISC_RLP_CFG_LSH; - fallthrough; - case V4L2_PIX_FMT_Y10: - isc->try_config.rlp_cfg_mode |= ISC_RLP_CFG_MODE_DATY10; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - isc->try_config.bpp = 16; - isc->try_config.bpp_v4l2 = 16; - break; - default: - return -EINVAL; - } - - if (direct_dump) { - isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8; - isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; - isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; - return 0; - } - - return 0; -} - -/* - * Configuring pipeline modules, depending on which format the ISC outputs - * and considering which format it has as input from the sensor. - */ -static int isc_try_configure_pipeline(struct isc_device *isc) -{ - switch (isc->try_config.fourcc) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_ARGB555: - case V4L2_PIX_FMT_ARGB444: - case V4L2_PIX_FMT_ABGR32: - case V4L2_PIX_FMT_XBGR32: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - WB_ENABLE | GAM_ENABLES | DPC_BLCENABLE | - CC_ENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - case V4L2_PIX_FMT_YUV420: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - CSC_ENABLE | GAM_ENABLES | WB_ENABLE | - SUB420_ENABLE | SUB422_ENABLE | CBC_ENABLE | - DPC_BLCENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - case V4L2_PIX_FMT_YUV422P: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - CSC_ENABLE | WB_ENABLE | GAM_ENABLES | - SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - CSC_ENABLE | WB_ENABLE | GAM_ENABLES | - SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - case V4L2_PIX_FMT_GREY: - case V4L2_PIX_FMT_Y16: - /* if sensor format is RAW, we convert inside ISC */ - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { - isc->try_config.bits_pipeline = CFA_ENABLE | - CSC_ENABLE | WB_ENABLE | GAM_ENABLES | - CBC_ENABLE | DPC_BLCENABLE; - } else { - isc->try_config.bits_pipeline = 0x0; - } - break; - default: - if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) - isc->try_config.bits_pipeline = WB_ENABLE | DPC_BLCENABLE; - else - isc->try_config.bits_pipeline = 0x0; - } - - /* Tune the pipeline to product specific */ - isc->adapt_pipeline(isc); - - return 0; -} - -static void isc_try_fse(struct isc_device *isc, - struct v4l2_subdev_state *sd_state) -{ - struct v4l2_rect *try_crop = - v4l2_subdev_state_get_crop(sd_state, 0); - struct v4l2_subdev_frame_size_enum fse = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - int ret; - - /* - * If we do not know yet which format the subdev is using, we cannot - * do anything. - */ - if (!isc->try_config.sd_format) - return; - - fse.code = isc->try_config.sd_format->mbus_code; - - ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size, - sd_state, &fse); - /* - * Attempt to obtain format size from subdev. If not available, - * just use the maximum ISC can receive. - */ - if (ret) { - try_crop->width = isc->max_width; - try_crop->height = isc->max_height; - } else { - try_crop->width = fse.max_width; - try_crop->height = fse.max_height; - } -} - -static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f, - u32 *code) -{ - int i; - struct isc_format *sd_fmt = NULL, *direct_fmt = NULL; - struct v4l2_pix_format *pixfmt = &f->fmt.pix; - struct v4l2_subdev_pad_config pad_cfg = {}; - struct v4l2_subdev_state pad_state = { - .pads = &pad_cfg, - }; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - u32 mbus_code; - int ret; - bool rlp_dma_direct_dump = false; - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - /* Step 1: find a RAW format that is supported */ - for (i = 0; i < isc->num_user_formats; i++) { - if (ISC_IS_FORMAT_RAW(isc->user_formats[i]->mbus_code)) { - sd_fmt = isc->user_formats[i]; - break; - } - } - /* Step 2: We can continue with this RAW format, or we can look - * for better: maybe sensor supports directly what we need. - */ - direct_fmt = find_format_by_fourcc(isc, pixfmt->pixelformat); - - /* Step 3: We have both. We decide given the module parameter which - * one to use. - */ - if (direct_fmt && sd_fmt && sensor_preferred) - sd_fmt = direct_fmt; - - /* Step 4: we do not have RAW but we have a direct format. Use it. */ - if (direct_fmt && !sd_fmt) - sd_fmt = direct_fmt; - - /* Step 5: if we are using a direct format, we need to package - * everything as 8 bit data and just dump it - */ - if (sd_fmt == direct_fmt) - rlp_dma_direct_dump = true; - - /* Step 6: We have no format. This can happen if the userspace - * requests some weird/invalid format. - * In this case, default to whatever we have - */ - if (!sd_fmt && !direct_fmt) { - sd_fmt = isc->user_formats[isc->num_user_formats - 1]; - v4l2_dbg(1, debug, &isc->v4l2_dev, - "Sensor not supporting %.4s, using %.4s\n", - (char *)&pixfmt->pixelformat, (char *)&sd_fmt->fourcc); - } - - if (!sd_fmt) { - ret = -EINVAL; - goto isc_try_fmt_err; - } - - /* Step 7: Print out what we decided for debugging */ - v4l2_dbg(1, debug, &isc->v4l2_dev, - "Preferring to have sensor using format %.4s\n", - (char *)&sd_fmt->fourcc); - - /* Step 8: at this moment we decided which format the subdev will use */ - isc->try_config.sd_format = sd_fmt; - - /* Limit to Atmel ISC hardware capabilities */ - if (pixfmt->width > isc->max_width) - pixfmt->width = isc->max_width; - if (pixfmt->height > isc->max_height) - pixfmt->height = isc->max_height; - - /* - * The mbus format is the one the subdev outputs. - * The pixels will be transferred in this format Sensor -> ISC - */ - mbus_code = sd_fmt->mbus_code; - - /* - * Validate formats. If the required format is not OK, default to raw. - */ - - isc->try_config.fourcc = pixfmt->pixelformat; - - if (isc_try_validate_formats(isc)) { - pixfmt->pixelformat = isc->try_config.fourcc = sd_fmt->fourcc; - /* Re-try to validate the new format */ - ret = isc_try_validate_formats(isc); - if (ret) - goto isc_try_fmt_err; - } - - ret = isc_try_configure_rlp_dma(isc, rlp_dma_direct_dump); - if (ret) - goto isc_try_fmt_err; - - ret = isc_try_configure_pipeline(isc); - if (ret) - goto isc_try_fmt_err; - - /* Obtain frame sizes if possible to have crop requirements ready */ - isc_try_fse(isc, &pad_state); - - v4l2_fill_mbus_format(&format.format, pixfmt, mbus_code); - ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt, - &pad_state, &format); - if (ret < 0) - goto isc_try_fmt_subdev_err; - - v4l2_fill_pix_format(pixfmt, &format.format); - - /* Limit to Atmel ISC hardware capabilities */ - if (pixfmt->width > isc->max_width) - pixfmt->width = isc->max_width; - if (pixfmt->height > isc->max_height) - pixfmt->height = isc->max_height; - - pixfmt->field = V4L2_FIELD_NONE; - pixfmt->bytesperline = (pixfmt->width * isc->try_config.bpp_v4l2) >> 3; - pixfmt->sizeimage = ((pixfmt->width * isc->try_config.bpp) >> 3) * - pixfmt->height; - - if (code) - *code = mbus_code; - - return 0; - -isc_try_fmt_err: - v4l2_err(&isc->v4l2_dev, "Could not find any possible format for a working pipeline\n"); -isc_try_fmt_subdev_err: - memset(&isc->try_config, 0, sizeof(isc->try_config)); - - return ret; -} - -static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) -{ - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - u32 mbus_code = 0; - int ret; - - ret = isc_try_fmt(isc, f, &mbus_code); - if (ret) - return ret; - - v4l2_fill_mbus_format(&format.format, &f->fmt.pix, mbus_code); - ret = v4l2_subdev_call(isc->current_subdev->sd, pad, - set_fmt, NULL, &format); - if (ret < 0) - return ret; - - /* Limit to Atmel ISC hardware capabilities */ - if (f->fmt.pix.width > isc->max_width) - f->fmt.pix.width = isc->max_width; - if (f->fmt.pix.height > isc->max_height) - f->fmt.pix.height = isc->max_height; - - isc->fmt = *f; - - if (isc->try_config.sd_format && isc->config.sd_format && - isc->try_config.sd_format != isc->config.sd_format) { - isc->ctrls.hist_stat = HIST_INIT; - isc_reset_awb_ctrls(isc); - isc_update_v4l2_ctrls(isc); - } - /* make the try configuration active */ - isc->config = isc->try_config; - - v4l2_dbg(1, debug, &isc->v4l2_dev, "New ISC configuration in place\n"); - - return 0; -} - -static int isc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct isc_device *isc = video_drvdata(file); - - if (vb2_is_busy(&isc->vb2_vidq)) - return -EBUSY; - - return isc_set_fmt(isc, f); -} - -static int isc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct isc_device *isc = video_drvdata(file); - - return isc_try_fmt(isc, f, NULL); -} - -static int isc_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - if (inp->index != 0) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = 0; - strscpy(inp->name, "Camera", sizeof(inp->name)); - - return 0; -} - -static int isc_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - - return 0; -} - -static int isc_s_input(struct file *file, void *priv, unsigned int i) -{ - if (i > 0) - return -EINVAL; - - return 0; -} - -static int isc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) -{ - struct isc_device *isc = video_drvdata(file); - - return v4l2_g_parm_cap(video_devdata(file), isc->current_subdev->sd, a); -} - -static int isc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) -{ - struct isc_device *isc = video_drvdata(file); - - return v4l2_s_parm_cap(video_devdata(file), isc->current_subdev->sd, a); -} - -static int isc_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct isc_device *isc = video_drvdata(file); - int ret = -EINVAL; - int i; - - if (fsize->index) - return -EINVAL; - - for (i = 0; i < isc->num_user_formats; i++) - if (isc->user_formats[i]->fourcc == fsize->pixel_format) - ret = 0; - - for (i = 0; i < isc->controller_formats_size; i++) - if (isc->controller_formats[i].fourcc == fsize->pixel_format) - ret = 0; - - if (ret) - return ret; - - fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - - fsize->stepwise.min_width = 16; - fsize->stepwise.max_width = isc->max_width; - fsize->stepwise.min_height = 16; - fsize->stepwise.max_height = isc->max_height; - fsize->stepwise.step_width = 1; - fsize->stepwise.step_height = 1; - - return 0; -} - -static const struct v4l2_ioctl_ops isc_ioctl_ops = { - .vidioc_querycap = isc_querycap, - .vidioc_enum_fmt_vid_cap = isc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = isc_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = isc_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = isc_try_fmt_vid_cap, - - .vidioc_enum_input = isc_enum_input, - .vidioc_g_input = isc_g_input, - .vidioc_s_input = isc_s_input, - - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - - .vidioc_g_parm = isc_g_parm, - .vidioc_s_parm = isc_s_parm, - .vidioc_enum_framesizes = isc_enum_framesizes, - - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static int isc_open(struct file *file) -{ - struct isc_device *isc = video_drvdata(file); - struct v4l2_subdev *sd = isc->current_subdev->sd; - int ret; - - if (mutex_lock_interruptible(&isc->lock)) - return -ERESTARTSYS; - - ret = v4l2_fh_open(file); - if (ret < 0) - goto unlock; - - if (!v4l2_fh_is_singular_file(file)) - goto unlock; - - ret = v4l2_subdev_call(sd, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD) { - v4l2_fh_release(file); - goto unlock; - } - - ret = isc_set_fmt(isc, &isc->fmt); - if (ret) { - v4l2_subdev_call(sd, core, s_power, 0); - v4l2_fh_release(file); - } - -unlock: - mutex_unlock(&isc->lock); - return ret; -} - -static int isc_release(struct file *file) -{ - struct isc_device *isc = video_drvdata(file); - struct v4l2_subdev *sd = isc->current_subdev->sd; - bool fh_singular; - int ret; - - mutex_lock(&isc->lock); - - fh_singular = v4l2_fh_is_singular_file(file); - - ret = _vb2_fop_release(file, NULL); - - if (fh_singular) - v4l2_subdev_call(sd, core, s_power, 0); - - mutex_unlock(&isc->lock); - - return ret; -} - -static const struct v4l2_file_operations isc_fops = { - .owner = THIS_MODULE, - .open = isc_open, - .release = isc_release, - .unlocked_ioctl = video_ioctl2, - .read = vb2_fop_read, - .mmap = vb2_fop_mmap, - .poll = vb2_fop_poll, -}; - -irqreturn_t atmel_isc_interrupt(int irq, void *dev_id) -{ - struct isc_device *isc = (struct isc_device *)dev_id; - struct regmap *regmap = isc->regmap; - u32 isc_intsr, isc_intmask, pending; - irqreturn_t ret = IRQ_NONE; - - regmap_read(regmap, ISC_INTSR, &isc_intsr); - regmap_read(regmap, ISC_INTMASK, &isc_intmask); - - pending = isc_intsr & isc_intmask; - - if (likely(pending & ISC_INT_DDONE)) { - spin_lock(&isc->dma_queue_lock); - if (isc->cur_frm) { - struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb; - struct vb2_buffer *vb = &vbuf->vb2_buf; - - vb->timestamp = ktime_get_ns(); - vbuf->sequence = isc->sequence++; - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - isc->cur_frm = NULL; - } - - if (!list_empty(&isc->dma_queue) && !isc->stop) { - isc->cur_frm = list_first_entry(&isc->dma_queue, - struct isc_buffer, list); - list_del(&isc->cur_frm->list); - - isc_start_dma(isc); - } - - if (isc->stop) - complete(&isc->comp); - - ret = IRQ_HANDLED; - spin_unlock(&isc->dma_queue_lock); - } - - if (pending & ISC_INT_HISDONE) { - schedule_work(&isc->awb_work); - ret = IRQ_HANDLED; - } - - return ret; -} -EXPORT_SYMBOL_GPL(atmel_isc_interrupt); - -static void isc_hist_count(struct isc_device *isc, u32 *min, u32 *max) -{ - struct regmap *regmap = isc->regmap; - struct isc_ctrls *ctrls = &isc->ctrls; - u32 *hist_count = &ctrls->hist_count[ctrls->hist_id]; - u32 *hist_entry = &ctrls->hist_entry[0]; - u32 i; - - *min = 0; - *max = HIST_ENTRIES; - - regmap_bulk_read(regmap, ISC_HIS_ENTRY + isc->offsets.his_entry, - hist_entry, HIST_ENTRIES); - - *hist_count = 0; - /* - * we deliberately ignore the end of the histogram, - * the most white pixels - */ - for (i = 1; i < HIST_ENTRIES; i++) { - if (*hist_entry && !*min) - *min = i; - if (*hist_entry) - *max = i; - *hist_count += i * (*hist_entry++); - } - - if (!*min) - *min = 1; - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb: hist_id %u, hist_count %u", - ctrls->hist_id, *hist_count); -} - -static void isc_wb_update(struct isc_ctrls *ctrls) -{ - struct isc_device *isc = container_of(ctrls, struct isc_device, ctrls); - u32 *hist_count = &ctrls->hist_count[0]; - u32 c, offset[4]; - u64 avg = 0; - /* We compute two gains, stretch gain and grey world gain */ - u32 s_gain[4], gw_gain[4]; - - /* - * According to Grey World, we need to set gains for R/B to normalize - * them towards the green channel. - * Thus we want to keep Green as fixed and adjust only Red/Blue - * Compute the average of the both green channels first - */ - avg = (u64)hist_count[ISC_HIS_CFG_MODE_GR] + - (u64)hist_count[ISC_HIS_CFG_MODE_GB]; - avg >>= 1; - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb: green components average %llu\n", avg); - - /* Green histogram is null, nothing to do */ - if (!avg) - return; - - for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { - /* - * the color offset is the minimum value of the histogram. - * we stretch this color to the full range by substracting - * this value from the color component. - */ - offset[c] = ctrls->hist_minmax[c][HIST_MIN_INDEX]; - /* - * The offset is always at least 1. If the offset is 1, we do - * not need to adjust it, so our result must be zero. - * the offset is computed in a histogram on 9 bits (0..512) - * but the offset in register is based on - * 12 bits pipeline (0..4096). - * we need to shift with the 3 bits that the histogram is - * ignoring - */ - ctrls->offset[c] = (offset[c] - 1) << 3; - - /* - * the offset is then taken and converted to 2's complements, - * and must be negative, as we subtract this value from the - * color components - */ - ctrls->offset[c] = -ctrls->offset[c]; - - /* - * the stretch gain is the total number of histogram bins - * divided by the actual range of color component (Max - Min) - * If we compute gain like this, the actual color component - * will be stretched to the full histogram. - * We need to shift 9 bits for precision, we have 9 bits for - * decimals - */ - s_gain[c] = (HIST_ENTRIES << 9) / - (ctrls->hist_minmax[c][HIST_MAX_INDEX] - - ctrls->hist_minmax[c][HIST_MIN_INDEX] + 1); - - /* - * Now we have to compute the gain w.r.t. the average. - * Add/lose gain to the component towards the average. - * If it happens that the component is zero, use the - * fixed point value : 1.0 gain. - */ - if (hist_count[c]) - gw_gain[c] = div_u64(avg << 9, hist_count[c]); - else - gw_gain[c] = 1 << 9; - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb: component %d, s_gain %u, gw_gain %u\n", - c, s_gain[c], gw_gain[c]); - /* multiply both gains and adjust for decimals */ - ctrls->gain[c] = s_gain[c] * gw_gain[c]; - ctrls->gain[c] >>= 9; - - /* make sure we are not out of range */ - ctrls->gain[c] = clamp_val(ctrls->gain[c], 0, GENMASK(12, 0)); - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb: component %d, final gain %u\n", - c, ctrls->gain[c]); - } -} - -static void isc_awb_work(struct work_struct *w) -{ - struct isc_device *isc = - container_of(w, struct isc_device, awb_work); - struct regmap *regmap = isc->regmap; - struct isc_ctrls *ctrls = &isc->ctrls; - u32 hist_id = ctrls->hist_id; - u32 baysel; - unsigned long flags; - u32 min, max; - int ret; - - if (ctrls->hist_stat != HIST_ENABLED) - return; - - isc_hist_count(isc, &min, &max); - - v4l2_dbg(1, debug, &isc->v4l2_dev, - "isc wb mode %d: hist min %u , max %u\n", hist_id, min, max); - - ctrls->hist_minmax[hist_id][HIST_MIN_INDEX] = min; - ctrls->hist_minmax[hist_id][HIST_MAX_INDEX] = max; - - if (hist_id != ISC_HIS_CFG_MODE_B) { - hist_id++; - } else { - isc_wb_update(ctrls); - hist_id = ISC_HIS_CFG_MODE_GR; - } - - ctrls->hist_id = hist_id; - baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT; - - ret = pm_runtime_resume_and_get(isc->dev); - if (ret < 0) - return; - - /* - * only update if we have all the required histograms and controls - * if awb has been disabled, we need to reset registers as well. - */ - if (hist_id == ISC_HIS_CFG_MODE_GR || ctrls->awb == ISC_WB_NONE) { - /* - * It may happen that DMA Done IRQ will trigger while we are - * updating white balance registers here. - * In that case, only parts of the controls have been updated. - * We can avoid that by locking the section. - */ - spin_lock_irqsave(&isc->awb_lock, flags); - isc_update_awb_ctrls(isc); - spin_unlock_irqrestore(&isc->awb_lock, flags); - - /* - * if we are doing just the one time white balance adjustment, - * we are basically done. - */ - if (ctrls->awb == ISC_WB_ONETIME) { - v4l2_info(&isc->v4l2_dev, - "Completed one time white-balance adjustment.\n"); - /* update the v4l2 controls values */ - isc_update_v4l2_ctrls(isc); - ctrls->awb = ISC_WB_NONE; - } - } - regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his, - hist_id | baysel | ISC_HIS_CFG_RAR); - - /* - * We have to make sure the streaming has not stopped meanwhile. - * ISC requires a frame to clock the internal profile update. - * To avoid issues, lock the sequence with a mutex - */ - mutex_lock(&isc->awb_mutex); - - /* streaming is not active anymore */ - if (isc->stop) { - mutex_unlock(&isc->awb_mutex); - return; - } - - isc_update_profile(isc); - - mutex_unlock(&isc->awb_mutex); - - /* if awb has been disabled, we don't need to start another histogram */ - if (ctrls->awb) - regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); - - pm_runtime_put_sync(isc->dev); -} - -static int isc_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct isc_device *isc = container_of(ctrl->handler, - struct isc_device, ctrls.handler); - struct isc_ctrls *ctrls = &isc->ctrls; - - if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) - return 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK; - break; - case V4L2_CID_CONTRAST: - ctrls->contrast = ctrl->val & ISC_CBC_CONTRAST_MASK; - break; - case V4L2_CID_GAMMA: - ctrls->gamma_index = ctrl->val; - break; - default: - return -EINVAL; - } - - return 0; -} - -static const struct v4l2_ctrl_ops isc_ctrl_ops = { - .s_ctrl = isc_s_ctrl, -}; - -static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl) -{ - struct isc_device *isc = container_of(ctrl->handler, - struct isc_device, ctrls.handler); - struct isc_ctrls *ctrls = &isc->ctrls; - - if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) - return 0; - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - if (ctrl->val == 1) - ctrls->awb = ISC_WB_AUTO; - else - ctrls->awb = ISC_WB_NONE; - - /* configure the controls with new values from v4l2 */ - if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new) - ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val; - if (ctrl->cluster[ISC_CTRL_B_GAIN]->is_new) - ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_gain_ctrl->val; - if (ctrl->cluster[ISC_CTRL_GR_GAIN]->is_new) - ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_gain_ctrl->val; - if (ctrl->cluster[ISC_CTRL_GB_GAIN]->is_new) - ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val; - - if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_R] = isc->r_off_ctrl->val; - if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_B] = isc->b_off_ctrl->val; - if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_GR] = isc->gr_off_ctrl->val; - if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val; - - isc_update_awb_ctrls(isc); - - mutex_lock(&isc->awb_mutex); - if (vb2_is_streaming(&isc->vb2_vidq)) { - /* - * If we are streaming, we can update profile to - * have the new settings in place. - */ - isc_update_profile(isc); - } else { - /* - * The auto cluster will activate automatically this - * control. This has to be deactivated when not - * streaming. - */ - v4l2_ctrl_activate(isc->do_wb_ctrl, false); - } - mutex_unlock(&isc->awb_mutex); - - /* if we have autowhitebalance on, start histogram procedure */ - if (ctrls->awb == ISC_WB_AUTO && - vb2_is_streaming(&isc->vb2_vidq) && - ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) - isc_set_histogram(isc, true); - - /* - * for one time whitebalance adjustment, check the button, - * if it's pressed, perform the one time operation. - */ - if (ctrls->awb == ISC_WB_NONE && - ctrl->cluster[ISC_CTRL_DO_WB]->is_new && - !(ctrl->cluster[ISC_CTRL_DO_WB]->flags & - V4L2_CTRL_FLAG_INACTIVE)) { - ctrls->awb = ISC_WB_ONETIME; - isc_set_histogram(isc, true); - v4l2_dbg(1, debug, &isc->v4l2_dev, - "One time white-balance started.\n"); - } - return 0; - } - return 0; -} - -static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl) -{ - struct isc_device *isc = container_of(ctrl->handler, - struct isc_device, ctrls.handler); - struct isc_ctrls *ctrls = &isc->ctrls; - - switch (ctrl->id) { - /* being a cluster, this id will be called for every control */ - case V4L2_CID_AUTO_WHITE_BALANCE: - ctrl->cluster[ISC_CTRL_R_GAIN]->val = - ctrls->gain[ISC_HIS_CFG_MODE_R]; - ctrl->cluster[ISC_CTRL_B_GAIN]->val = - ctrls->gain[ISC_HIS_CFG_MODE_B]; - ctrl->cluster[ISC_CTRL_GR_GAIN]->val = - ctrls->gain[ISC_HIS_CFG_MODE_GR]; - ctrl->cluster[ISC_CTRL_GB_GAIN]->val = - ctrls->gain[ISC_HIS_CFG_MODE_GB]; - - ctrl->cluster[ISC_CTRL_R_OFF]->val = - ctrls->offset[ISC_HIS_CFG_MODE_R]; - ctrl->cluster[ISC_CTRL_B_OFF]->val = - ctrls->offset[ISC_HIS_CFG_MODE_B]; - ctrl->cluster[ISC_CTRL_GR_OFF]->val = - ctrls->offset[ISC_HIS_CFG_MODE_GR]; - ctrl->cluster[ISC_CTRL_GB_OFF]->val = - ctrls->offset[ISC_HIS_CFG_MODE_GB]; - break; - } - return 0; -} - -static const struct v4l2_ctrl_ops isc_awb_ops = { - .s_ctrl = isc_s_awb_ctrl, - .g_volatile_ctrl = isc_g_volatile_awb_ctrl, -}; - -#define ISC_CTRL_OFF(_name, _id, _name_str) \ - static const struct v4l2_ctrl_config _name = { \ - .ops = &isc_awb_ops, \ - .id = _id, \ - .name = _name_str, \ - .type = V4L2_CTRL_TYPE_INTEGER, \ - .flags = V4L2_CTRL_FLAG_SLIDER, \ - .min = -4095, \ - .max = 4095, \ - .step = 1, \ - .def = 0, \ - } - -ISC_CTRL_OFF(isc_r_off_ctrl, ISC_CID_R_OFFSET, "Red Component Offset"); -ISC_CTRL_OFF(isc_b_off_ctrl, ISC_CID_B_OFFSET, "Blue Component Offset"); -ISC_CTRL_OFF(isc_gr_off_ctrl, ISC_CID_GR_OFFSET, "Green Red Component Offset"); -ISC_CTRL_OFF(isc_gb_off_ctrl, ISC_CID_GB_OFFSET, "Green Blue Component Offset"); - -#define ISC_CTRL_GAIN(_name, _id, _name_str) \ - static const struct v4l2_ctrl_config _name = { \ - .ops = &isc_awb_ops, \ - .id = _id, \ - .name = _name_str, \ - .type = V4L2_CTRL_TYPE_INTEGER, \ - .flags = V4L2_CTRL_FLAG_SLIDER, \ - .min = 0, \ - .max = 8191, \ - .step = 1, \ - .def = 512, \ - } - -ISC_CTRL_GAIN(isc_r_gain_ctrl, ISC_CID_R_GAIN, "Red Component Gain"); -ISC_CTRL_GAIN(isc_b_gain_ctrl, ISC_CID_B_GAIN, "Blue Component Gain"); -ISC_CTRL_GAIN(isc_gr_gain_ctrl, ISC_CID_GR_GAIN, "Green Red Component Gain"); -ISC_CTRL_GAIN(isc_gb_gain_ctrl, ISC_CID_GB_GAIN, "Green Blue Component Gain"); - -static int isc_ctrl_init(struct isc_device *isc) -{ - const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops; - struct isc_ctrls *ctrls = &isc->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; - int ret; - - ctrls->hist_stat = HIST_INIT; - isc_reset_awb_ctrls(isc); - - ret = v4l2_ctrl_handler_init(hdl, 13); - if (ret < 0) - return ret; - - /* Initialize product specific controls. For example, contrast */ - isc->config_ctrls(isc, ops); - - ctrls->brightness = 0; - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, isc->gamma_max, 1, - isc->gamma_max); - isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, - V4L2_CID_AUTO_WHITE_BALANCE, - 0, 1, 1, 1); - - /* do_white_balance is a button, so min,max,step,default are ignored */ - isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, - V4L2_CID_DO_WHITE_BALANCE, - 0, 0, 0, 0); - - if (!isc->do_wb_ctrl) { - ret = hdl->error; - v4l2_ctrl_handler_free(hdl); - return ret; - } - - v4l2_ctrl_activate(isc->do_wb_ctrl, false); - - isc->r_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_gain_ctrl, NULL); - isc->b_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_gain_ctrl, NULL); - isc->gr_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_gain_ctrl, NULL); - isc->gb_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_gain_ctrl, NULL); - isc->r_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_off_ctrl, NULL); - isc->b_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_off_ctrl, NULL); - isc->gr_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_off_ctrl, NULL); - isc->gb_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_off_ctrl, NULL); - - /* - * The cluster is in auto mode with autowhitebalance enabled - * and manual mode otherwise. - */ - v4l2_ctrl_auto_cluster(10, &isc->awb_ctrl, 0, true); - - v4l2_ctrl_handler_setup(hdl); - - return 0; -} - -static int isc_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_connection *asd) -{ - struct isc_device *isc = container_of(notifier->v4l2_dev, - struct isc_device, v4l2_dev); - struct isc_subdev_entity *subdev_entity = - container_of(notifier, struct isc_subdev_entity, notifier); - - if (video_is_registered(&isc->video_dev)) { - v4l2_err(&isc->v4l2_dev, "only supports one sub-device.\n"); - return -EBUSY; - } - - subdev_entity->sd = subdev; - - return 0; -} - -static void isc_async_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_connection *asd) -{ - struct isc_device *isc = container_of(notifier->v4l2_dev, - struct isc_device, v4l2_dev); - mutex_destroy(&isc->awb_mutex); - cancel_work_sync(&isc->awb_work); - video_unregister_device(&isc->video_dev); - v4l2_ctrl_handler_free(&isc->ctrls.handler); -} - -static struct isc_format *find_format_by_code(struct isc_device *isc, - unsigned int code, int *index) -{ - struct isc_format *fmt = &isc->formats_list[0]; - unsigned int i; - - for (i = 0; i < isc->formats_list_size; i++) { - if (fmt->mbus_code == code) { - *index = i; - return fmt; - } - - fmt++; - } - - return NULL; -} - -static int isc_formats_init(struct isc_device *isc) -{ - struct isc_format *fmt; - struct v4l2_subdev *subdev = isc->current_subdev->sd; - unsigned int num_fmts, i, j; - u32 list_size = isc->formats_list_size; - struct v4l2_subdev_mbus_code_enum mbus_code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - num_fmts = 0; - while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, - NULL, &mbus_code)) { - mbus_code.index++; - - fmt = find_format_by_code(isc, mbus_code.code, &i); - if (!fmt) { - v4l2_warn(&isc->v4l2_dev, "Mbus code %x not supported\n", - mbus_code.code); - continue; - } - - fmt->sd_support = true; - num_fmts++; - } - - if (!num_fmts) - return -ENXIO; - - isc->num_user_formats = num_fmts; - isc->user_formats = devm_kcalloc(isc->dev, - num_fmts, sizeof(*isc->user_formats), - GFP_KERNEL); - if (!isc->user_formats) - return -ENOMEM; - - fmt = &isc->formats_list[0]; - for (i = 0, j = 0; i < list_size; i++) { - if (fmt->sd_support) - isc->user_formats[j++] = fmt; - fmt++; - } - - return 0; -} - -static int isc_set_default_fmt(struct isc_device *isc) -{ - struct v4l2_format f = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .fmt.pix = { - .width = VGA_WIDTH, - .height = VGA_HEIGHT, - .field = V4L2_FIELD_NONE, - .pixelformat = isc->user_formats[0]->fourcc, - }, - }; - int ret; - - ret = isc_try_fmt(isc, &f, NULL); - if (ret) - return ret; - - isc->fmt = f; - return 0; -} - -static int isc_async_complete(struct v4l2_async_notifier *notifier) -{ - struct isc_device *isc = container_of(notifier->v4l2_dev, - struct isc_device, v4l2_dev); - struct video_device *vdev = &isc->video_dev; - struct vb2_queue *q = &isc->vb2_vidq; - int ret = 0; - - INIT_WORK(&isc->awb_work, isc_awb_work); - - ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, "Failed to register subdev nodes\n"); - return ret; - } - - isc->current_subdev = container_of(notifier, - struct isc_subdev_entity, notifier); - mutex_init(&isc->lock); - mutex_init(&isc->awb_mutex); - - init_completion(&isc->comp); - - /* Initialize videobuf2 queue */ - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - q->drv_priv = isc; - q->buf_struct_size = sizeof(struct isc_buffer); - q->ops = &isc_vb2_ops; - q->mem_ops = &vb2_dma_contig_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &isc->lock; - q->min_queued_buffers = 1; - q->dev = isc->dev; - - ret = vb2_queue_init(q); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, - "vb2_queue_init() failed: %d\n", ret); - goto isc_async_complete_err; - } - - /* Init video dma queues */ - INIT_LIST_HEAD(&isc->dma_queue); - spin_lock_init(&isc->dma_queue_lock); - spin_lock_init(&isc->awb_lock); - - ret = isc_formats_init(isc); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, - "Init format failed: %d\n", ret); - goto isc_async_complete_err; - } - - ret = isc_set_default_fmt(isc); - if (ret) { - v4l2_err(&isc->v4l2_dev, "Could not set default format\n"); - goto isc_async_complete_err; - } - - ret = isc_ctrl_init(isc); - if (ret) { - v4l2_err(&isc->v4l2_dev, "Init isc ctrols failed: %d\n", ret); - goto isc_async_complete_err; - } - - /* Register video device */ - strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); - vdev->release = video_device_release_empty; - vdev->fops = &isc_fops; - vdev->ioctl_ops = &isc_ioctl_ops; - vdev->v4l2_dev = &isc->v4l2_dev; - vdev->vfl_dir = VFL_DIR_RX; - vdev->queue = q; - vdev->lock = &isc->lock; - vdev->ctrl_handler = &isc->ctrls.handler; - vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; - video_set_drvdata(vdev, isc); - - ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - if (ret < 0) { - v4l2_err(&isc->v4l2_dev, - "video_register_device failed: %d\n", ret); - goto isc_async_complete_err; - } - - return 0; - -isc_async_complete_err: - mutex_destroy(&isc->awb_mutex); - mutex_destroy(&isc->lock); - return ret; -} - -const struct v4l2_async_notifier_operations atmel_isc_async_ops = { - .bound = isc_async_bound, - .unbind = isc_async_unbind, - .complete = isc_async_complete, -}; -EXPORT_SYMBOL_GPL(atmel_isc_async_ops); - -void atmel_isc_subdev_cleanup(struct isc_device *isc) -{ - struct isc_subdev_entity *subdev_entity; - - list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { - v4l2_async_nf_unregister(&subdev_entity->notifier); - v4l2_async_nf_cleanup(&subdev_entity->notifier); - } - - INIT_LIST_HEAD(&isc->subdev_entities); -} -EXPORT_SYMBOL_GPL(atmel_isc_subdev_cleanup); - -int atmel_isc_pipeline_init(struct isc_device *isc) -{ - struct device *dev = isc->dev; - struct regmap *regmap = isc->regmap; - struct regmap_field *regs; - unsigned int i; - - /* - * DPCEN-->GDCEN-->BLCEN-->WB-->CFA-->CC--> - * GAM-->VHXS-->CSC-->CBC-->SUB422-->SUB420 - */ - const struct reg_field regfields[ISC_PIPE_LINE_NODE_NUM] = { - REG_FIELD(ISC_DPC_CTRL, 0, 0), - REG_FIELD(ISC_DPC_CTRL, 1, 1), - REG_FIELD(ISC_DPC_CTRL, 2, 2), - REG_FIELD(ISC_WB_CTRL, 0, 0), - REG_FIELD(ISC_CFA_CTRL, 0, 0), - REG_FIELD(ISC_CC_CTRL, 0, 0), - REG_FIELD(ISC_GAM_CTRL, 0, 0), - REG_FIELD(ISC_GAM_CTRL, 1, 1), - REG_FIELD(ISC_GAM_CTRL, 2, 2), - REG_FIELD(ISC_GAM_CTRL, 3, 3), - REG_FIELD(ISC_VHXS_CTRL, 0, 0), - REG_FIELD(ISC_CSC_CTRL + isc->offsets.csc, 0, 0), - REG_FIELD(ISC_CBC_CTRL + isc->offsets.cbc, 0, 0), - REG_FIELD(ISC_SUB422_CTRL + isc->offsets.sub422, 0, 0), - REG_FIELD(ISC_SUB420_CTRL + isc->offsets.sub420, 0, 0), - }; - - for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) { - regs = devm_regmap_field_alloc(dev, regmap, regfields[i]); - if (IS_ERR(regs)) - return PTR_ERR(regs); - - isc->pipeline[i] = regs; - } - - return 0; -} -EXPORT_SYMBOL_GPL(atmel_isc_pipeline_init); - -/* regmap configuration */ -#define ATMEL_ISC_REG_MAX 0xd5c -const struct regmap_config atmel_isc_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .max_register = ATMEL_ISC_REG_MAX, -}; -EXPORT_SYMBOL_GPL(atmel_isc_regmap_config); - -MODULE_AUTHOR("Songjun Wu"); -MODULE_AUTHOR("Eugen Hristev"); -MODULE_DESCRIPTION("Atmel ISC common code base"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c b/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c deleted file mode 100644 index d442b5f4c931..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c +++ /dev/null @@ -1,311 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Microchip Image Sensor Controller (ISC) common clock driver setup - * - * Copyright (C) 2016 Microchip Technology, Inc. - * - * Author: Songjun Wu - * Author: Eugen Hristev - * - */ -#include -#include -#include -#include -#include - -#include "atmel-isc-regs.h" -#include "atmel-isc.h" - -static int isc_wait_clk_stable(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - struct regmap *regmap = isc_clk->regmap; - unsigned long timeout = jiffies + usecs_to_jiffies(1000); - unsigned int status; - - while (time_before(jiffies, timeout)) { - regmap_read(regmap, ISC_CLKSR, &status); - if (!(status & ISC_CLKSR_SIP)) - return 0; - - usleep_range(10, 250); - } - - return -ETIMEDOUT; -} - -static int isc_clk_prepare(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - int ret; - - ret = pm_runtime_resume_and_get(isc_clk->dev); - if (ret < 0) - return ret; - - return isc_wait_clk_stable(hw); -} - -static void isc_clk_unprepare(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - isc_wait_clk_stable(hw); - - pm_runtime_put_sync(isc_clk->dev); -} - -static int isc_clk_enable(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 id = isc_clk->id; - struct regmap *regmap = isc_clk->regmap; - unsigned long flags; - unsigned int status; - - dev_dbg(isc_clk->dev, "ISC CLK: %s, id = %d, div = %d, parent id = %d\n", - __func__, id, isc_clk->div, isc_clk->parent_id); - - spin_lock_irqsave(&isc_clk->lock, flags); - regmap_update_bits(regmap, ISC_CLKCFG, - ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id), - (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) | - (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id))); - - regmap_write(regmap, ISC_CLKEN, ISC_CLK(id)); - spin_unlock_irqrestore(&isc_clk->lock, flags); - - regmap_read(regmap, ISC_CLKSR, &status); - if (status & ISC_CLK(id)) - return 0; - else - return -EINVAL; -} - -static void isc_clk_disable(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 id = isc_clk->id; - unsigned long flags; - - spin_lock_irqsave(&isc_clk->lock, flags); - regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id)); - spin_unlock_irqrestore(&isc_clk->lock, flags); -} - -static int isc_clk_is_enabled(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 status; - int ret; - - ret = pm_runtime_resume_and_get(isc_clk->dev); - if (ret < 0) - return 0; - - regmap_read(isc_clk->regmap, ISC_CLKSR, &status); - - pm_runtime_put_sync(isc_clk->dev); - - return status & ISC_CLK(isc_clk->id) ? 1 : 0; -} - -static unsigned long -isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1); -} - -static int isc_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - long best_rate = -EINVAL; - int best_diff = -1; - unsigned int i, div; - - for (i = 0; i < clk_hw_get_num_parents(hw); i++) { - struct clk_hw *parent; - unsigned long parent_rate; - - parent = clk_hw_get_parent_by_index(hw, i); - if (!parent) - continue; - - parent_rate = clk_hw_get_rate(parent); - if (!parent_rate) - continue; - - for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) { - unsigned long rate; - int diff; - - rate = DIV_ROUND_CLOSEST(parent_rate, div); - diff = abs(req->rate - rate); - - if (best_diff < 0 || best_diff > diff) { - best_rate = rate; - best_diff = diff; - req->best_parent_rate = parent_rate; - req->best_parent_hw = parent; - } - - if (!best_diff || rate < req->rate) - break; - } - - if (!best_diff) - break; - } - - dev_dbg(isc_clk->dev, - "ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n", - __func__, best_rate, - __clk_get_name((req->best_parent_hw)->clk), - req->best_parent_rate); - - if (best_rate < 0) - return best_rate; - - req->rate = best_rate; - - return 0; -} - -static int isc_clk_set_parent(struct clk_hw *hw, u8 index) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - if (index >= clk_hw_get_num_parents(hw)) - return -EINVAL; - - isc_clk->parent_id = index; - - return 0; -} - -static u8 isc_clk_get_parent(struct clk_hw *hw) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - - return isc_clk->parent_id; -} - -static int isc_clk_set_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate) -{ - struct isc_clk *isc_clk = to_isc_clk(hw); - u32 div; - - if (!rate) - return -EINVAL; - - div = DIV_ROUND_CLOSEST(parent_rate, rate); - if (div > (ISC_CLK_MAX_DIV + 1) || !div) - return -EINVAL; - - isc_clk->div = div - 1; - - return 0; -} - -static const struct clk_ops isc_clk_ops = { - .prepare = isc_clk_prepare, - .unprepare = isc_clk_unprepare, - .enable = isc_clk_enable, - .disable = isc_clk_disable, - .is_enabled = isc_clk_is_enabled, - .recalc_rate = isc_clk_recalc_rate, - .determine_rate = isc_clk_determine_rate, - .set_parent = isc_clk_set_parent, - .get_parent = isc_clk_get_parent, - .set_rate = isc_clk_set_rate, -}; - -static int isc_clk_register(struct isc_device *isc, unsigned int id) -{ - struct regmap *regmap = isc->regmap; - struct device_node *np = isc->dev->of_node; - struct isc_clk *isc_clk; - struct clk_init_data init; - const char *clk_name = np->name; - const char *parent_names[3]; - int num_parents; - - if (id == ISC_ISPCK && !isc->ispck_required) - return 0; - - num_parents = of_clk_get_parent_count(np); - if (num_parents < 1 || num_parents > 3) - return -EINVAL; - - if (num_parents > 2 && id == ISC_ISPCK) - num_parents = 2; - - of_clk_parent_fill(np, parent_names, num_parents); - - if (id == ISC_MCK) - of_property_read_string(np, "clock-output-names", &clk_name); - else - clk_name = "isc-ispck"; - - init.parent_names = parent_names; - init.num_parents = num_parents; - init.name = clk_name; - init.ops = &isc_clk_ops; - init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; - - isc_clk = &isc->isc_clks[id]; - isc_clk->hw.init = &init; - isc_clk->regmap = regmap; - isc_clk->id = id; - isc_clk->dev = isc->dev; - spin_lock_init(&isc_clk->lock); - - isc_clk->clk = clk_register(isc->dev, &isc_clk->hw); - if (IS_ERR(isc_clk->clk)) { - dev_err(isc->dev, "%s: clock register fail\n", clk_name); - return PTR_ERR(isc_clk->clk); - } else if (id == ISC_MCK) { - of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk); - } - - return 0; -} - -int atmel_isc_clk_init(struct isc_device *isc) -{ - unsigned int i; - int ret; - - for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) - isc->isc_clks[i].clk = ERR_PTR(-EINVAL); - - for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { - ret = isc_clk_register(isc, i); - if (ret) - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(atmel_isc_clk_init); - -void atmel_isc_clk_cleanup(struct isc_device *isc) -{ - unsigned int i; - - of_clk_del_provider(isc->dev->of_node); - - for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { - struct isc_clk *isc_clk = &isc->isc_clks[i]; - - if (!IS_ERR(isc_clk->clk)) - clk_unregister(isc_clk->clk); - } -} -EXPORT_SYMBOL_GPL(atmel_isc_clk_cleanup); diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h b/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h deleted file mode 100644 index d06b72228d4f..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h +++ /dev/null @@ -1,413 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __ATMEL_ISC_REGS_H -#define __ATMEL_ISC_REGS_H - -#include - -/* ISC Control Enable Register 0 */ -#define ISC_CTRLEN 0x00000000 - -/* ISC Control Disable Register 0 */ -#define ISC_CTRLDIS 0x00000004 - -/* ISC Control Status Register 0 */ -#define ISC_CTRLSR 0x00000008 - -#define ISC_CTRL_CAPTURE BIT(0) -#define ISC_CTRL_UPPRO BIT(1) -#define ISC_CTRL_HISREQ BIT(2) -#define ISC_CTRL_HISCLR BIT(3) - -/* ISC Parallel Front End Configuration 0 Register */ -#define ISC_PFE_CFG0 0x0000000c - -#define ISC_PFE_CFG0_HPOL_LOW BIT(0) -#define ISC_PFE_CFG0_VPOL_LOW BIT(1) -#define ISC_PFE_CFG0_PPOL_LOW BIT(2) -#define ISC_PFE_CFG0_CCIR656 BIT(9) -#define ISC_PFE_CFG0_CCIR_CRC BIT(10) -#define ISC_PFE_CFG0_MIPI BIT(14) - -#define ISC_PFE_CFG0_MODE_PROGRESSIVE (0x0 << 4) -#define ISC_PFE_CFG0_MODE_MASK GENMASK(6, 4) - -#define ISC_PFE_CFG0_BPS_EIGHT (0x4 << 28) -#define ISC_PFG_CFG0_BPS_NINE (0x3 << 28) -#define ISC_PFG_CFG0_BPS_TEN (0x2 << 28) -#define ISC_PFG_CFG0_BPS_ELEVEN (0x1 << 28) -#define ISC_PFG_CFG0_BPS_TWELVE (0x0 << 28) -#define ISC_PFE_CFG0_BPS_MASK GENMASK(30, 28) - -#define ISC_PFE_CFG0_COLEN BIT(12) -#define ISC_PFE_CFG0_ROWEN BIT(13) - -/* ISC Parallel Front End Configuration 1 Register */ -#define ISC_PFE_CFG1 0x00000010 - -#define ISC_PFE_CFG1_COLMIN(v) ((v)) -#define ISC_PFE_CFG1_COLMIN_MASK GENMASK(15, 0) -#define ISC_PFE_CFG1_COLMAX(v) ((v) << 16) -#define ISC_PFE_CFG1_COLMAX_MASK GENMASK(31, 16) - -/* ISC Parallel Front End Configuration 2 Register */ -#define ISC_PFE_CFG2 0x00000014 - -#define ISC_PFE_CFG2_ROWMIN(v) ((v)) -#define ISC_PFE_CFG2_ROWMIN_MASK GENMASK(15, 0) -#define ISC_PFE_CFG2_ROWMAX(v) ((v) << 16) -#define ISC_PFE_CFG2_ROWMAX_MASK GENMASK(31, 16) - -/* ISC Clock Enable Register */ -#define ISC_CLKEN 0x00000018 - -/* ISC Clock Disable Register */ -#define ISC_CLKDIS 0x0000001c - -/* ISC Clock Status Register */ -#define ISC_CLKSR 0x00000020 -#define ISC_CLKSR_SIP BIT(31) - -#define ISC_CLK(n) BIT(n) - -/* ISC Clock Configuration Register */ -#define ISC_CLKCFG 0x00000024 -#define ISC_CLKCFG_DIV_SHIFT(n) ((n)*16) -#define ISC_CLKCFG_DIV_MASK(n) GENMASK(((n)*16 + 7), (n)*16) -#define ISC_CLKCFG_SEL_SHIFT(n) ((n)*16 + 8) -#define ISC_CLKCFG_SEL_MASK(n) GENMASK(((n)*17 + 8), ((n)*16 + 8)) - -/* ISC Interrupt Enable Register */ -#define ISC_INTEN 0x00000028 - -/* ISC Interrupt Disable Register */ -#define ISC_INTDIS 0x0000002c - -/* ISC Interrupt Mask Register */ -#define ISC_INTMASK 0x00000030 - -/* ISC Interrupt Status Register */ -#define ISC_INTSR 0x00000034 - -#define ISC_INT_DDONE BIT(8) -#define ISC_INT_HISDONE BIT(12) - -/* ISC DPC Control Register */ -#define ISC_DPC_CTRL 0x40 - -#define ISC_DPC_CTRL_DPCEN BIT(0) -#define ISC_DPC_CTRL_GDCEN BIT(1) -#define ISC_DPC_CTRL_BLCEN BIT(2) - -/* ISC DPC Config Register */ -#define ISC_DPC_CFG 0x44 - -#define ISC_DPC_CFG_BAYSEL_SHIFT 0 - -#define ISC_DPC_CFG_EITPOL BIT(4) - -#define ISC_DPC_CFG_TA_ENABLE BIT(14) -#define ISC_DPC_CFG_TC_ENABLE BIT(13) -#define ISC_DPC_CFG_TM_ENABLE BIT(12) - -#define ISC_DPC_CFG_RE_MODE BIT(17) - -#define ISC_DPC_CFG_GDCCLP_SHIFT 20 -#define ISC_DPC_CFG_GDCCLP_MASK GENMASK(22, 20) - -#define ISC_DPC_CFG_BLOFF_SHIFT 24 -#define ISC_DPC_CFG_BLOFF_MASK GENMASK(31, 24) - -#define ISC_DPC_CFG_BAYCFG_SHIFT 0 -#define ISC_DPC_CFG_BAYCFG_MASK GENMASK(1, 0) -/* ISC DPC Threshold Median Register */ -#define ISC_DPC_THRESHM 0x48 - -/* ISC DPC Threshold Closest Register */ -#define ISC_DPC_THRESHC 0x4C - -/* ISC DPC Threshold Average Register */ -#define ISC_DPC_THRESHA 0x50 - -/* ISC DPC STatus Register */ -#define ISC_DPC_SR 0x54 - -/* ISC White Balance Control Register */ -#define ISC_WB_CTRL 0x00000058 - -/* ISC White Balance Configuration Register */ -#define ISC_WB_CFG 0x0000005c - -/* ISC White Balance Offset for R, GR Register */ -#define ISC_WB_O_RGR 0x00000060 - -/* ISC White Balance Offset for B, GB Register */ -#define ISC_WB_O_BGB 0x00000064 - -/* ISC White Balance Gain for R, GR Register */ -#define ISC_WB_G_RGR 0x00000068 - -/* ISC White Balance Gain for B, GB Register */ -#define ISC_WB_G_BGB 0x0000006c - -/* ISC Color Filter Array Control Register */ -#define ISC_CFA_CTRL 0x00000070 - -/* ISC Color Filter Array Configuration Register */ -#define ISC_CFA_CFG 0x00000074 -#define ISC_CFA_CFG_EITPOL BIT(4) - -#define ISC_BAY_CFG_GRGR 0x0 -#define ISC_BAY_CFG_RGRG 0x1 -#define ISC_BAY_CFG_GBGB 0x2 -#define ISC_BAY_CFG_BGBG 0x3 - -/* ISC Color Correction Control Register */ -#define ISC_CC_CTRL 0x00000078 - -/* ISC Color Correction RR RG Register */ -#define ISC_CC_RR_RG 0x0000007c - -/* ISC Color Correction RB OR Register */ -#define ISC_CC_RB_OR 0x00000080 - -/* ISC Color Correction GR GG Register */ -#define ISC_CC_GR_GG 0x00000084 - -/* ISC Color Correction GB OG Register */ -#define ISC_CC_GB_OG 0x00000088 - -/* ISC Color Correction BR BG Register */ -#define ISC_CC_BR_BG 0x0000008c - -/* ISC Color Correction BB OB Register */ -#define ISC_CC_BB_OB 0x00000090 - -/* ISC Gamma Correction Control Register */ -#define ISC_GAM_CTRL 0x00000094 - -#define ISC_GAM_CTRL_BIPART BIT(4) - -/* ISC_Gamma Correction Blue Entry Register */ -#define ISC_GAM_BENTRY 0x00000098 - -/* ISC_Gamma Correction Green Entry Register */ -#define ISC_GAM_GENTRY 0x00000198 - -/* ISC_Gamma Correction Green Entry Register */ -#define ISC_GAM_RENTRY 0x00000298 - -/* ISC VHXS Control Register */ -#define ISC_VHXS_CTRL 0x398 - -/* ISC VHXS Source Size Register */ -#define ISC_VHXS_SS 0x39C - -/* ISC VHXS Destination Size Register */ -#define ISC_VHXS_DS 0x3A0 - -/* ISC Vertical Factor Register */ -#define ISC_VXS_FACT 0x3a4 - -/* ISC Horizontal Factor Register */ -#define ISC_HXS_FACT 0x3a8 - -/* ISC Vertical Config Register */ -#define ISC_VXS_CFG 0x3ac - -/* ISC Horizontal Config Register */ -#define ISC_HXS_CFG 0x3b0 - -/* ISC Vertical Tap Register */ -#define ISC_VXS_TAP 0x3b4 - -/* ISC Horizontal Tap Register */ -#define ISC_HXS_TAP 0x434 - -/* Offset for CSC register specific to sama5d2 product */ -#define ISC_SAMA5D2_CSC_OFFSET 0 -/* Offset for CSC register specific to sama7g5 product */ -#define ISC_SAMA7G5_CSC_OFFSET 0x11c - -/* Color Space Conversion Control Register */ -#define ISC_CSC_CTRL 0x00000398 - -/* Color Space Conversion YR YG Register */ -#define ISC_CSC_YR_YG 0x0000039c - -/* Color Space Conversion YB OY Register */ -#define ISC_CSC_YB_OY 0x000003a0 - -/* Color Space Conversion CBR CBG Register */ -#define ISC_CSC_CBR_CBG 0x000003a4 - -/* Color Space Conversion CBB OCB Register */ -#define ISC_CSC_CBB_OCB 0x000003a8 - -/* Color Space Conversion CRR CRG Register */ -#define ISC_CSC_CRR_CRG 0x000003ac - -/* Color Space Conversion CRB OCR Register */ -#define ISC_CSC_CRB_OCR 0x000003b0 - -/* Offset for CBC register specific to sama5d2 product */ -#define ISC_SAMA5D2_CBC_OFFSET 0 -/* Offset for CBC register specific to sama7g5 product */ -#define ISC_SAMA7G5_CBC_OFFSET 0x11c - -/* Contrast And Brightness Control Register */ -#define ISC_CBC_CTRL 0x000003b4 - -/* Contrast And Brightness Configuration Register */ -#define ISC_CBC_CFG 0x000003b8 - -/* Brightness Register */ -#define ISC_CBC_BRIGHT 0x000003bc -#define ISC_CBC_BRIGHT_MASK GENMASK(10, 0) - -/* Contrast Register */ -#define ISC_CBC_CONTRAST 0x000003c0 -#define ISC_CBC_CONTRAST_MASK GENMASK(11, 0) - -/* Hue Register */ -#define ISC_CBCHS_HUE 0x4e0 -/* Saturation Register */ -#define ISC_CBCHS_SAT 0x4e4 - -/* Offset for SUB422 register specific to sama5d2 product */ -#define ISC_SAMA5D2_SUB422_OFFSET 0 -/* Offset for SUB422 register specific to sama7g5 product */ -#define ISC_SAMA7G5_SUB422_OFFSET 0x124 - -/* Subsampling 4:4:4 to 4:2:2 Control Register */ -#define ISC_SUB422_CTRL 0x000003c4 - -/* Offset for SUB420 register specific to sama5d2 product */ -#define ISC_SAMA5D2_SUB420_OFFSET 0 -/* Offset for SUB420 register specific to sama7g5 product */ -#define ISC_SAMA7G5_SUB420_OFFSET 0x124 -/* Subsampling 4:2:2 to 4:2:0 Control Register */ -#define ISC_SUB420_CTRL 0x000003cc - -/* Offset for RLP register specific to sama5d2 product */ -#define ISC_SAMA5D2_RLP_OFFSET 0 -/* Offset for RLP register specific to sama7g5 product */ -#define ISC_SAMA7G5_RLP_OFFSET 0x124 -/* Rounding, Limiting and Packing Configuration Register */ -#define ISC_RLP_CFG 0x000003d0 - -#define ISC_RLP_CFG_MODE_DAT8 0x0 -#define ISC_RLP_CFG_MODE_DAT9 0x1 -#define ISC_RLP_CFG_MODE_DAT10 0x2 -#define ISC_RLP_CFG_MODE_DAT11 0x3 -#define ISC_RLP_CFG_MODE_DAT12 0x4 -#define ISC_RLP_CFG_MODE_DATY8 0x5 -#define ISC_RLP_CFG_MODE_DATY10 0x6 -#define ISC_RLP_CFG_MODE_ARGB444 0x7 -#define ISC_RLP_CFG_MODE_ARGB555 0x8 -#define ISC_RLP_CFG_MODE_RGB565 0x9 -#define ISC_RLP_CFG_MODE_ARGB32 0xa -#define ISC_RLP_CFG_MODE_YYCC 0xb -#define ISC_RLP_CFG_MODE_YYCC_LIMITED 0xc -#define ISC_RLP_CFG_MODE_YCYC 0xd -#define ISC_RLP_CFG_MODE_MASK GENMASK(3, 0) - -#define ISC_RLP_CFG_LSH BIT(5) - -#define ISC_RLP_CFG_YMODE_YUYV (3 << 6) -#define ISC_RLP_CFG_YMODE_YVYU (2 << 6) -#define ISC_RLP_CFG_YMODE_VYUY (0 << 6) -#define ISC_RLP_CFG_YMODE_UYVY (1 << 6) - -#define ISC_RLP_CFG_YMODE_MASK GENMASK(7, 6) - -/* Offset for HIS register specific to sama5d2 product */ -#define ISC_SAMA5D2_HIS_OFFSET 0 -/* Offset for HIS register specific to sama7g5 product */ -#define ISC_SAMA7G5_HIS_OFFSET 0x124 -/* Histogram Control Register */ -#define ISC_HIS_CTRL 0x000003d4 - -#define ISC_HIS_CTRL_EN BIT(0) -#define ISC_HIS_CTRL_DIS 0x0 - -/* Histogram Configuration Register */ -#define ISC_HIS_CFG 0x000003d8 - -#define ISC_HIS_CFG_MODE_GR 0x0 -#define ISC_HIS_CFG_MODE_R 0x1 -#define ISC_HIS_CFG_MODE_GB 0x2 -#define ISC_HIS_CFG_MODE_B 0x3 -#define ISC_HIS_CFG_MODE_Y 0x4 -#define ISC_HIS_CFG_MODE_RAW 0x5 -#define ISC_HIS_CFG_MODE_YCCIR656 0x6 - -#define ISC_HIS_CFG_BAYSEL_SHIFT 4 - -#define ISC_HIS_CFG_RAR BIT(8) - -/* Offset for DMA register specific to sama5d2 product */ -#define ISC_SAMA5D2_DMA_OFFSET 0 -/* Offset for DMA register specific to sama7g5 product */ -#define ISC_SAMA7G5_DMA_OFFSET 0x13c - -/* DMA Configuration Register */ -#define ISC_DCFG 0x000003e0 -#define ISC_DCFG_IMODE_PACKED8 0x0 -#define ISC_DCFG_IMODE_PACKED16 0x1 -#define ISC_DCFG_IMODE_PACKED32 0x2 -#define ISC_DCFG_IMODE_YC422SP 0x3 -#define ISC_DCFG_IMODE_YC422P 0x4 -#define ISC_DCFG_IMODE_YC420SP 0x5 -#define ISC_DCFG_IMODE_YC420P 0x6 -#define ISC_DCFG_IMODE_MASK GENMASK(2, 0) - -#define ISC_DCFG_YMBSIZE_SINGLE (0x0 << 4) -#define ISC_DCFG_YMBSIZE_BEATS4 (0x1 << 4) -#define ISC_DCFG_YMBSIZE_BEATS8 (0x2 << 4) -#define ISC_DCFG_YMBSIZE_BEATS16 (0x3 << 4) -#define ISC_DCFG_YMBSIZE_BEATS32 (0x4 << 4) -#define ISC_DCFG_YMBSIZE_MASK GENMASK(6, 4) - -#define ISC_DCFG_CMBSIZE_SINGLE (0x0 << 8) -#define ISC_DCFG_CMBSIZE_BEATS4 (0x1 << 8) -#define ISC_DCFG_CMBSIZE_BEATS8 (0x2 << 8) -#define ISC_DCFG_CMBSIZE_BEATS16 (0x3 << 8) -#define ISC_DCFG_CMBSIZE_BEATS32 (0x4 << 8) -#define ISC_DCFG_CMBSIZE_MASK GENMASK(10, 8) - -/* DMA Control Register */ -#define ISC_DCTRL 0x000003e4 - -#define ISC_DCTRL_DVIEW_PACKED (0x0 << 1) -#define ISC_DCTRL_DVIEW_SEMIPLANAR (0x1 << 1) -#define ISC_DCTRL_DVIEW_PLANAR (0x2 << 1) -#define ISC_DCTRL_DVIEW_MASK GENMASK(2, 1) - -#define ISC_DCTRL_IE_IS (0x0 << 4) - -/* DMA Descriptor Address Register */ -#define ISC_DNDA 0x000003e8 - -/* DMA Address 0 Register */ -#define ISC_DAD0 0x000003ec - -/* DMA Address 1 Register */ -#define ISC_DAD1 0x000003f4 - -/* DMA Address 2 Register */ -#define ISC_DAD2 0x000003fc - -/* Offset for version register specific to sama5d2 product */ -#define ISC_SAMA5D2_VERSION_OFFSET 0 -#define ISC_SAMA7G5_VERSION_OFFSET 0x13c -/* Version Register */ -#define ISC_VERSION 0x0000040c - -/* Offset for version register specific to sama5d2 product */ -#define ISC_SAMA5D2_HIS_ENTRY_OFFSET 0 -/* Offset for version register specific to sama7g5 product */ -#define ISC_SAMA7G5_HIS_ENTRY_OFFSET 0x14c -/* Histogram Entry */ -#define ISC_HIS_ENTRY 0x00000410 - -#endif diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc.h b/drivers/staging/media/deprecated/atmel/atmel-isc.h deleted file mode 100644 index 31767ea74be6..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-isc.h +++ /dev/null @@ -1,362 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Microchip Image Sensor Controller (ISC) driver header file - * - * Copyright (C) 2016-2019 Microchip Technology, Inc. - * - * Author: Songjun Wu - * Author: Eugen Hristev - * - */ -#ifndef _ATMEL_ISC_H_ - -#include -#include - -#include -#include -#include - -#define ISC_CLK_MAX_DIV 255 - -enum isc_clk_id { - ISC_ISPCK = 0, - ISC_MCK = 1, -}; - -struct isc_clk { - struct clk_hw hw; - struct clk *clk; - struct regmap *regmap; - spinlock_t lock; /* serialize access to clock registers */ - u8 id; - u8 parent_id; - u32 div; - struct device *dev; -}; - -#define to_isc_clk(v) container_of(v, struct isc_clk, hw) - -struct isc_buffer { - struct vb2_v4l2_buffer vb; - struct list_head list; -}; - -struct isc_subdev_entity { - struct v4l2_subdev *sd; - struct v4l2_async_connection *asd; - struct device_node *epn; - struct v4l2_async_notifier notifier; - - u32 pfe_cfg0; - - struct list_head list; -}; - -/* - * struct isc_format - ISC media bus format information - This structure represents the interface between the ISC - and the sensor. It's the input format received by - the ISC. - * @fourcc: Fourcc code for this format - * @mbus_code: V4L2 media bus format code. - * @cfa_baycfg: If this format is RAW BAYER, indicate the type of bayer. - this is either BGBG, RGRG, etc. - * @pfe_cfg0_bps: Number of hardware data lines connected to the ISC - */ - -struct isc_format { - u32 fourcc; - u32 mbus_code; - u32 cfa_baycfg; - - bool sd_support; - u32 pfe_cfg0_bps; -}; - -/* Pipeline bitmap */ -#define DPC_DPCENABLE BIT(0) -#define DPC_GDCENABLE BIT(1) -#define DPC_BLCENABLE BIT(2) -#define WB_ENABLE BIT(3) -#define CFA_ENABLE BIT(4) -#define CC_ENABLE BIT(5) -#define GAM_ENABLE BIT(6) -#define GAM_BENABLE BIT(7) -#define GAM_GENABLE BIT(8) -#define GAM_RENABLE BIT(9) -#define VHXS_ENABLE BIT(10) -#define CSC_ENABLE BIT(11) -#define CBC_ENABLE BIT(12) -#define SUB422_ENABLE BIT(13) -#define SUB420_ENABLE BIT(14) - -#define GAM_ENABLES (GAM_RENABLE | GAM_GENABLE | GAM_BENABLE | GAM_ENABLE) - -/* - * struct fmt_config - ISC format configuration and internal pipeline - This structure represents the internal configuration - of the ISC. - It also holds the format that ISC will present to v4l2. - * @sd_format: Pointer to an isc_format struct that holds the sensor - configuration. - * @fourcc: Fourcc code for this format. - * @bpp: Bytes per pixel in the current format. - * @bpp_v4l2: Bytes per pixel in the current format, for v4l2. - This differs from 'bpp' in the sense that in planar - formats, it refers only to the first plane. - * @rlp_cfg_mode: Configuration of the RLP (rounding, limiting packaging) - * @dcfg_imode: Configuration of the input of the DMA module - * @dctrl_dview: Configuration of the output of the DMA module - * @bits_pipeline: Configuration of the pipeline, which modules are enabled - */ -struct fmt_config { - struct isc_format *sd_format; - - u32 fourcc; - u8 bpp; - u8 bpp_v4l2; - - u32 rlp_cfg_mode; - u32 dcfg_imode; - u32 dctrl_dview; - - u32 bits_pipeline; -}; - -#define HIST_ENTRIES 512 -#define HIST_BAYER (ISC_HIS_CFG_MODE_B + 1) - -enum{ - HIST_INIT = 0, - HIST_ENABLED, - HIST_DISABLED, -}; - -struct isc_ctrls { - struct v4l2_ctrl_handler handler; - - u32 brightness; - u32 contrast; - u8 gamma_index; -#define ISC_WB_NONE 0 -#define ISC_WB_AUTO 1 -#define ISC_WB_ONETIME 2 - u8 awb; - - /* one for each component : GR, R, GB, B */ - u32 gain[HIST_BAYER]; - s32 offset[HIST_BAYER]; - - u32 hist_entry[HIST_ENTRIES]; - u32 hist_count[HIST_BAYER]; - u8 hist_id; - u8 hist_stat; -#define HIST_MIN_INDEX 0 -#define HIST_MAX_INDEX 1 - u32 hist_minmax[HIST_BAYER][2]; -}; - -#define ISC_PIPE_LINE_NODE_NUM 15 - -/* - * struct isc_reg_offsets - ISC device register offsets - * @csc: Offset for the CSC register - * @cbc: Offset for the CBC register - * @sub422: Offset for the SUB422 register - * @sub420: Offset for the SUB420 register - * @rlp: Offset for the RLP register - * @his: Offset for the HIS related registers - * @dma: Offset for the DMA related registers - * @version: Offset for the version register - * @his_entry: Offset for the HIS entries registers - */ -struct isc_reg_offsets { - u32 csc; - u32 cbc; - u32 sub422; - u32 sub420; - u32 rlp; - u32 his; - u32 dma; - u32 version; - u32 his_entry; -}; - -/* - * struct isc_device - ISC device driver data/config struct - * @regmap: Register map - * @hclock: Hclock clock input (refer datasheet) - * @ispck: iscpck clock (refer datasheet) - * @isc_clks: ISC clocks - * @ispck_required: ISC requires ISP Clock initialization - * @dcfg: DMA master configuration, architecture dependent - * - * @dev: Registered device driver - * @v4l2_dev: v4l2 registered device - * @video_dev: registered video device - * - * @vb2_vidq: video buffer 2 video queue - * @dma_queue_lock: lock to serialize the dma buffer queue - * @dma_queue: the queue for dma buffers - * @cur_frm: current isc frame/buffer - * @sequence: current frame number - * @stop: true if isc is not streaming, false if streaming - * @comp: completion reference that signals frame completion - * - * @fmt: current v42l format - * @user_formats: list of formats that are supported and agreed with sd - * @num_user_formats: how many formats are in user_formats - * - * @config: current ISC format configuration - * @try_config: the current ISC try format , not yet activated - * - * @ctrls: holds information about ISC controls - * @do_wb_ctrl: control regarding the DO_WHITE_BALANCE button - * @awb_work: workqueue reference for autowhitebalance histogram - * analysis - * - * @lock: lock for serializing userspace file operations - * with ISC operations - * @awb_mutex: serialize access to streaming status from awb work queue - * @awb_lock: lock for serializing awb work queue operations - * with DMA/buffer operations - * - * @pipeline: configuration of the ISC pipeline - * - * @current_subdev: current subdevice: the sensor - * @subdev_entities: list of subdevice entitites - * - * @gamma_table: pointer to the table with gamma values, has - * gamma_max sets of GAMMA_ENTRIES entries each - * @gamma_max: maximum number of sets of inside the gamma_table - * - * @max_width: maximum frame width, dependent on the internal RAM - * @max_height: maximum frame height, dependent on the internal RAM - * - * @config_dpc: pointer to a function that initializes product - * specific DPC module - * @config_csc: pointer to a function that initializes product - * specific CSC module - * @config_cbc: pointer to a function that initializes product - * specific CBC module - * @config_cc: pointer to a function that initializes product - * specific CC module - * @config_gam: pointer to a function that initializes product - * specific GAMMA module - * @config_rlp: pointer to a function that initializes product - * specific RLP module - * @config_ctrls: pointer to a functoin that initializes product - * specific v4l2 controls. - * - * @adapt_pipeline: pointer to a function that adapts the pipeline bits - * to the product specific pipeline - * - * @offsets: struct holding the product specific register offsets - * @controller_formats: pointer to the array of possible formats that the - * controller can output - * @formats_list: pointer to the array of possible formats that can - * be used as an input to the controller - * @controller_formats_size: size of controller_formats array - * @formats_list_size: size of formats_list array - */ -struct isc_device { - struct regmap *regmap; - struct clk *hclock; - struct clk *ispck; - struct isc_clk isc_clks[2]; - bool ispck_required; - u32 dcfg; - - struct device *dev; - struct v4l2_device v4l2_dev; - struct video_device video_dev; - - struct vb2_queue vb2_vidq; - spinlock_t dma_queue_lock; - struct list_head dma_queue; - struct isc_buffer *cur_frm; - unsigned int sequence; - bool stop; - struct completion comp; - - struct v4l2_format fmt; - struct isc_format **user_formats; - unsigned int num_user_formats; - - struct fmt_config config; - struct fmt_config try_config; - - struct isc_ctrls ctrls; - struct work_struct awb_work; - - struct mutex lock; - struct mutex awb_mutex; - spinlock_t awb_lock; - - struct regmap_field *pipeline[ISC_PIPE_LINE_NODE_NUM]; - - struct isc_subdev_entity *current_subdev; - struct list_head subdev_entities; - - struct { -#define ISC_CTRL_DO_WB 1 -#define ISC_CTRL_R_GAIN 2 -#define ISC_CTRL_B_GAIN 3 -#define ISC_CTRL_GR_GAIN 4 -#define ISC_CTRL_GB_GAIN 5 -#define ISC_CTRL_R_OFF 6 -#define ISC_CTRL_B_OFF 7 -#define ISC_CTRL_GR_OFF 8 -#define ISC_CTRL_GB_OFF 9 - struct v4l2_ctrl *awb_ctrl; - struct v4l2_ctrl *do_wb_ctrl; - struct v4l2_ctrl *r_gain_ctrl; - struct v4l2_ctrl *b_gain_ctrl; - struct v4l2_ctrl *gr_gain_ctrl; - struct v4l2_ctrl *gb_gain_ctrl; - struct v4l2_ctrl *r_off_ctrl; - struct v4l2_ctrl *b_off_ctrl; - struct v4l2_ctrl *gr_off_ctrl; - struct v4l2_ctrl *gb_off_ctrl; - }; - -#define GAMMA_ENTRIES 64 - /* pointer to the defined gamma table */ - const u32 (*gamma_table)[GAMMA_ENTRIES]; - u32 gamma_max; - - u32 max_width; - u32 max_height; - - struct { - void (*config_dpc)(struct isc_device *isc); - void (*config_csc)(struct isc_device *isc); - void (*config_cbc)(struct isc_device *isc); - void (*config_cc)(struct isc_device *isc); - void (*config_gam)(struct isc_device *isc); - void (*config_rlp)(struct isc_device *isc); - - void (*config_ctrls)(struct isc_device *isc, - const struct v4l2_ctrl_ops *ops); - - void (*adapt_pipeline)(struct isc_device *isc); - }; - - struct isc_reg_offsets offsets; - const struct isc_format *controller_formats; - struct isc_format *formats_list; - u32 controller_formats_size; - u32 formats_list_size; -}; - -extern const struct regmap_config atmel_isc_regmap_config; -extern const struct v4l2_async_notifier_operations atmel_isc_async_ops; - -irqreturn_t atmel_isc_interrupt(int irq, void *dev_id); -int atmel_isc_pipeline_init(struct isc_device *isc); -int atmel_isc_clk_init(struct isc_device *isc); -void atmel_isc_subdev_cleanup(struct isc_device *isc); -void atmel_isc_clk_cleanup(struct isc_device *isc); - -#endif diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c deleted file mode 100644 index 71e6e278a4b3..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c +++ /dev/null @@ -1,644 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Microchip Image Sensor Controller (ISC) driver - * - * Copyright (C) 2016-2019 Microchip Technology, Inc. - * - * Author: Songjun Wu - * Author: Eugen Hristev - * - * - * Sensor-->PFE-->WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB-->RLP-->DMA - * - * ISC video pipeline integrates the following submodules: - * PFE: Parallel Front End to sample the camera sensor input stream - * WB: Programmable white balance in the Bayer domain - * CFA: Color filter array interpolation module - * CC: Programmable color correction - * GAM: Gamma correction - * CSC: Programmable color space conversion - * CBC: Contrast and Brightness control - * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling - * RLP: This module performs rounding, range limiting - * and packing of the incoming data - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "atmel-isc-regs.h" -#include "atmel-isc.h" - -#define ISC_SAMA5D2_MAX_SUPPORT_WIDTH 2592 -#define ISC_SAMA5D2_MAX_SUPPORT_HEIGHT 1944 - -#define ISC_SAMA5D2_PIPELINE \ - (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \ - CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE) - -/* This is a list of the formats that the ISC can *output* */ -static const struct isc_format sama5d2_controller_formats[] = { - { - .fourcc = V4L2_PIX_FMT_ARGB444, - }, { - .fourcc = V4L2_PIX_FMT_ARGB555, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, - }, { - .fourcc = V4L2_PIX_FMT_ABGR32, - }, { - .fourcc = V4L2_PIX_FMT_XBGR32, - }, { - .fourcc = V4L2_PIX_FMT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_YUYV, - }, { - .fourcc = V4L2_PIX_FMT_YUV422P, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - }, { - .fourcc = V4L2_PIX_FMT_Y10, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR8, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10, - }, -}; - -/* This is a list of formats that the ISC can receive as *input* */ -static struct isc_format sama5d2_formats_list[] = { - { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_BGBG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_BGBG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_GREY, - .mbus_code = MEDIA_BUS_FMT_Y8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_YUYV, - .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565, - .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_Y10, - .mbus_code = MEDIA_BUS_FMT_Y10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - }, - -}; - -static void isc_sama5d2_config_csc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Convert RGB to YUV */ - regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc, - 0x42 | (0x81 << 16)); - regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc, - 0x19 | (0x10 << 16)); - regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc, - 0xFDA | (0xFB6 << 16)); - regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc, - 0x70 | (0x80 << 16)); - regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc, - 0x70 | (0xFA2 << 16)); - regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc, - 0xFEE | (0x80 << 16)); -} - -static void isc_sama5d2_config_cbc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, - isc->ctrls.brightness); - regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, - isc->ctrls.contrast); -} - -static void isc_sama5d2_config_cc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Configure each register at the neutral fixed point 1.0 or 0.0 */ - regmap_write(regmap, ISC_CC_RR_RG, (1 << 8)); - regmap_write(regmap, ISC_CC_RB_OR, 0); - regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16); - regmap_write(regmap, ISC_CC_GB_OG, 0); - regmap_write(regmap, ISC_CC_BR_BG, 0); - regmap_write(regmap, ISC_CC_BB_OB, (1 << 8)); -} - -static void isc_sama5d2_config_ctrls(struct isc_device *isc, - const struct v4l2_ctrl_ops *ops) -{ - struct isc_ctrls *ctrls = &isc->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; - - ctrls->contrast = 256; - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256); -} - -static void isc_sama5d2_config_dpc(struct isc_device *isc) -{ - /* This module is not present on sama5d2 pipeline */ -} - -static void isc_sama5d2_config_gam(struct isc_device *isc) -{ - /* No specific gamma configuration */ -} - -static void isc_sama5d2_config_rlp(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 rlp_mode = isc->config.rlp_cfg_mode; - - /* - * In sama5d2, the YUV planar modes and the YUYV modes are treated - * in the same way in RLP register. - * Normally, YYCC mode should be Luma(n) - Color B(n) - Color R (n) - * and YCYC should be Luma(n + 1) - Color B (n) - Luma (n) - Color R (n) - * but in sama5d2, the YCYC mode does not exist, and YYCC must be - * selected for both planar and interleaved modes, as in fact - * both modes are supported. - * - * Thus, if the YCYC mode is selected, replace it with the - * sama5d2-compliant mode which is YYCC . - */ - if ((rlp_mode & ISC_RLP_CFG_MODE_MASK) == ISC_RLP_CFG_MODE_YCYC) { - rlp_mode &= ~ISC_RLP_CFG_MODE_MASK; - rlp_mode |= ISC_RLP_CFG_MODE_YYCC; - } - - regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp, - ISC_RLP_CFG_MODE_MASK, rlp_mode); -} - -static void isc_sama5d2_adapt_pipeline(struct isc_device *isc) -{ - isc->try_config.bits_pipeline &= ISC_SAMA5D2_PIPELINE; -} - -/* Gamma table with gamma 1/2.2 */ -static const u32 isc_sama5d2_gamma_table[][GAMMA_ENTRIES] = { - /* 0 --> gamma 1/1.8 */ - { 0x65, 0x66002F, 0x950025, 0xBB0020, 0xDB001D, 0xF8001A, - 0x1130018, 0x12B0017, 0x1420016, 0x1580014, 0x16D0013, 0x1810012, - 0x1940012, 0x1A60012, 0x1B80011, 0x1C90010, 0x1DA0010, 0x1EA000F, - 0x1FA000F, 0x209000F, 0x218000F, 0x227000E, 0x235000E, 0x243000E, - 0x251000E, 0x25F000D, 0x26C000D, 0x279000D, 0x286000D, 0x293000C, - 0x2A0000C, 0x2AC000C, 0x2B8000C, 0x2C4000C, 0x2D0000B, 0x2DC000B, - 0x2E7000B, 0x2F3000B, 0x2FE000B, 0x309000B, 0x314000B, 0x31F000A, - 0x32A000A, 0x334000B, 0x33F000A, 0x349000A, 0x354000A, 0x35E000A, - 0x368000A, 0x372000A, 0x37C000A, 0x386000A, 0x3900009, 0x399000A, - 0x3A30009, 0x3AD0009, 0x3B60009, 0x3BF000A, 0x3C90009, 0x3D20009, - 0x3DB0009, 0x3E40009, 0x3ED0009, 0x3F60009 }, - - /* 1 --> gamma 1/2 */ - { 0x7F, 0x800034, 0xB50028, 0xDE0021, 0x100001E, 0x11E001B, - 0x1390019, 0x1520017, 0x16A0015, 0x1800014, 0x1940014, 0x1A80013, - 0x1BB0012, 0x1CD0011, 0x1DF0010, 0x1EF0010, 0x200000F, 0x20F000F, - 0x21F000E, 0x22D000F, 0x23C000E, 0x24A000E, 0x258000D, 0x265000D, - 0x273000C, 0x27F000D, 0x28C000C, 0x299000C, 0x2A5000C, 0x2B1000B, - 0x2BC000C, 0x2C8000B, 0x2D3000C, 0x2DF000B, 0x2EA000A, 0x2F5000A, - 0x2FF000B, 0x30A000A, 0x314000B, 0x31F000A, 0x329000A, 0x333000A, - 0x33D0009, 0x3470009, 0x350000A, 0x35A0009, 0x363000A, 0x36D0009, - 0x3760009, 0x37F0009, 0x3880009, 0x3910009, 0x39A0009, 0x3A30009, - 0x3AC0008, 0x3B40009, 0x3BD0008, 0x3C60008, 0x3CE0008, 0x3D60009, - 0x3DF0008, 0x3E70008, 0x3EF0008, 0x3F70008 }, - - /* 2 --> gamma 1/2.2 */ - { 0x99, 0x9B0038, 0xD4002A, 0xFF0023, 0x122001F, 0x141001B, - 0x15D0019, 0x1760017, 0x18E0015, 0x1A30015, 0x1B80013, 0x1CC0012, - 0x1DE0011, 0x1F00010, 0x2010010, 0x2110010, 0x221000F, 0x230000F, - 0x23F000E, 0x24D000E, 0x25B000D, 0x269000C, 0x276000C, 0x283000C, - 0x28F000C, 0x29B000C, 0x2A7000C, 0x2B3000B, 0x2BF000B, 0x2CA000B, - 0x2D5000B, 0x2E0000A, 0x2EB000A, 0x2F5000A, 0x2FF000A, 0x30A000A, - 0x3140009, 0x31E0009, 0x327000A, 0x3310009, 0x33A0009, 0x3440009, - 0x34D0009, 0x3560009, 0x35F0009, 0x3680008, 0x3710008, 0x3790009, - 0x3820008, 0x38A0008, 0x3930008, 0x39B0008, 0x3A30008, 0x3AB0008, - 0x3B30008, 0x3BB0008, 0x3C30008, 0x3CB0007, 0x3D20008, 0x3DA0007, - 0x3E20007, 0x3E90007, 0x3F00008, 0x3F80007 }, -}; - -static int isc_parse_dt(struct device *dev, struct isc_device *isc) -{ - struct device_node *np = dev->of_node; - struct device_node *epn; - struct isc_subdev_entity *subdev_entity; - unsigned int flags; - int ret = -EINVAL; - - INIT_LIST_HEAD(&isc->subdev_entities); - - for_each_endpoint_of_node(np, epn) { - struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; - - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), - &v4l2_epn); - if (ret) { - ret = -EINVAL; - dev_err(dev, "Could not parse the endpoint\n"); - break; - } - - subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), - GFP_KERNEL); - if (!subdev_entity) { - ret = -ENOMEM; - break; - } - subdev_entity->epn = epn; - - flags = v4l2_epn.bus.parallel.flags; - - if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW; - - if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW; - - if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW; - - if (v4l2_epn.bus_type == V4L2_MBUS_BT656) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC | - ISC_PFE_CFG0_CCIR656; - - list_add_tail(&subdev_entity->list, &isc->subdev_entities); - } - of_node_put(epn); - - return ret; -} - -static int atmel_isc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct isc_device *isc; - void __iomem *io_base; - struct isc_subdev_entity *subdev_entity; - int irq; - int ret; - u32 ver; - - isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL); - if (!isc) - return -ENOMEM; - - platform_set_drvdata(pdev, isc); - isc->dev = dev; - - io_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(io_base)) - return PTR_ERR(io_base); - - isc->regmap = devm_regmap_init_mmio(dev, io_base, &atmel_isc_regmap_config); - if (IS_ERR(isc->regmap)) { - ret = PTR_ERR(isc->regmap); - dev_err(dev, "failed to init register map: %d\n", ret); - return ret; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - ret = devm_request_irq(dev, irq, atmel_isc_interrupt, 0, - "atmel-sama5d2-isc", isc); - if (ret < 0) { - dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", - irq, ret); - return ret; - } - - isc->gamma_table = isc_sama5d2_gamma_table; - isc->gamma_max = 2; - - isc->max_width = ISC_SAMA5D2_MAX_SUPPORT_WIDTH; - isc->max_height = ISC_SAMA5D2_MAX_SUPPORT_HEIGHT; - - isc->config_dpc = isc_sama5d2_config_dpc; - isc->config_csc = isc_sama5d2_config_csc; - isc->config_cbc = isc_sama5d2_config_cbc; - isc->config_cc = isc_sama5d2_config_cc; - isc->config_gam = isc_sama5d2_config_gam; - isc->config_rlp = isc_sama5d2_config_rlp; - isc->config_ctrls = isc_sama5d2_config_ctrls; - - isc->adapt_pipeline = isc_sama5d2_adapt_pipeline; - - isc->offsets.csc = ISC_SAMA5D2_CSC_OFFSET; - isc->offsets.cbc = ISC_SAMA5D2_CBC_OFFSET; - isc->offsets.sub422 = ISC_SAMA5D2_SUB422_OFFSET; - isc->offsets.sub420 = ISC_SAMA5D2_SUB420_OFFSET; - isc->offsets.rlp = ISC_SAMA5D2_RLP_OFFSET; - isc->offsets.his = ISC_SAMA5D2_HIS_OFFSET; - isc->offsets.dma = ISC_SAMA5D2_DMA_OFFSET; - isc->offsets.version = ISC_SAMA5D2_VERSION_OFFSET; - isc->offsets.his_entry = ISC_SAMA5D2_HIS_ENTRY_OFFSET; - - isc->controller_formats = sama5d2_controller_formats; - isc->controller_formats_size = ARRAY_SIZE(sama5d2_controller_formats); - isc->formats_list = sama5d2_formats_list; - isc->formats_list_size = ARRAY_SIZE(sama5d2_formats_list); - - /* sama5d2-isc - 8 bits per beat */ - isc->dcfg = ISC_DCFG_YMBSIZE_BEATS8 | ISC_DCFG_CMBSIZE_BEATS8; - - /* sama5d2-isc : ISPCK is required and mandatory */ - isc->ispck_required = true; - - ret = atmel_isc_pipeline_init(isc); - if (ret) - return ret; - - isc->hclock = devm_clk_get(dev, "hclock"); - if (IS_ERR(isc->hclock)) { - ret = PTR_ERR(isc->hclock); - dev_err(dev, "failed to get hclock: %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(isc->hclock); - if (ret) { - dev_err(dev, "failed to enable hclock: %d\n", ret); - return ret; - } - - ret = atmel_isc_clk_init(isc); - if (ret) { - dev_err(dev, "failed to init isc clock: %d\n", ret); - goto unprepare_hclk; - } - ret = v4l2_device_register(dev, &isc->v4l2_dev); - if (ret) { - dev_err(dev, "unable to register v4l2 device.\n"); - goto unprepare_clk; - } - - ret = isc_parse_dt(dev, isc); - if (ret) { - dev_err(dev, "fail to parse device tree\n"); - goto unregister_v4l2_device; - } - - if (list_empty(&isc->subdev_entities)) { - dev_err(dev, "no subdev found\n"); - ret = -ENODEV; - goto unregister_v4l2_device; - } - - list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { - struct v4l2_async_connection *asd; - struct fwnode_handle *fwnode = - of_fwnode_handle(subdev_entity->epn); - - v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev); - - asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier, - fwnode, - struct v4l2_async_connection); - - of_node_put(subdev_entity->epn); - subdev_entity->epn = NULL; - - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); - goto cleanup_subdev; - } - - subdev_entity->notifier.ops = &atmel_isc_async_ops; - - ret = v4l2_async_nf_register(&subdev_entity->notifier); - if (ret) { - dev_err(dev, "fail to register async notifier\n"); - goto cleanup_subdev; - } - - if (video_is_registered(&isc->video_dev)) - break; - } - - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_request_idle(dev); - - isc->ispck = isc->isc_clks[ISC_ISPCK].clk; - - ret = clk_prepare_enable(isc->ispck); - if (ret) { - dev_err(dev, "failed to enable ispck: %d\n", ret); - goto disable_pm; - } - - /* ispck should be greater or equal to hclock */ - ret = clk_set_rate(isc->ispck, clk_get_rate(isc->hclock)); - if (ret) { - dev_err(dev, "failed to set ispck rate: %d\n", ret); - goto unprepare_clk; - } - - regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver); - dev_info(dev, "Microchip ISC version %x\n", ver); - - return 0; - -unprepare_clk: - clk_disable_unprepare(isc->ispck); - -disable_pm: - pm_runtime_disable(dev); - -cleanup_subdev: - atmel_isc_subdev_cleanup(isc); - -unregister_v4l2_device: - v4l2_device_unregister(&isc->v4l2_dev); - -unprepare_hclk: - clk_disable_unprepare(isc->hclock); - - atmel_isc_clk_cleanup(isc); - - return ret; -} - -static void atmel_isc_remove(struct platform_device *pdev) -{ - struct isc_device *isc = platform_get_drvdata(pdev); - - pm_runtime_disable(&pdev->dev); - - atmel_isc_subdev_cleanup(isc); - - v4l2_device_unregister(&isc->v4l2_dev); - - clk_disable_unprepare(isc->ispck); - clk_disable_unprepare(isc->hclock); - - atmel_isc_clk_cleanup(isc); -} - -static int __maybe_unused isc_runtime_suspend(struct device *dev) -{ - struct isc_device *isc = dev_get_drvdata(dev); - - clk_disable_unprepare(isc->ispck); - clk_disable_unprepare(isc->hclock); - - return 0; -} - -static int __maybe_unused isc_runtime_resume(struct device *dev) -{ - struct isc_device *isc = dev_get_drvdata(dev); - int ret; - - ret = clk_prepare_enable(isc->hclock); - if (ret) - return ret; - - ret = clk_prepare_enable(isc->ispck); - if (ret) - clk_disable_unprepare(isc->hclock); - - return ret; -} - -static const struct dev_pm_ops atmel_isc_dev_pm_ops = { - SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL) -}; - -#if IS_ENABLED(CONFIG_OF) -static const struct of_device_id atmel_isc_of_match[] = { - { .compatible = "atmel,sama5d2-isc" }, - { } -}; -MODULE_DEVICE_TABLE(of, atmel_isc_of_match); -#endif - -static struct platform_driver atmel_isc_driver = { - .probe = atmel_isc_probe, - .remove = atmel_isc_remove, - .driver = { - .name = "atmel-sama5d2-isc", - .pm = &atmel_isc_dev_pm_ops, - .of_match_table = of_match_ptr(atmel_isc_of_match), - }, -}; - -module_platform_driver(atmel_isc_driver); - -MODULE_AUTHOR("Songjun Wu"); -MODULE_DESCRIPTION("The V4L2 driver for Atmel-ISC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c deleted file mode 100644 index 1f74c2dd044c..000000000000 --- a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c +++ /dev/null @@ -1,607 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Microchip eXtended Image Sensor Controller (XISC) driver - * - * Copyright (C) 2019-2021 Microchip Technology, Inc. and its subsidiaries - * - * Author: Eugen Hristev - * - * Sensor-->PFE-->DPC-->WB-->CFA-->CC-->GAM-->VHXS-->CSC-->CBHS-->SUB-->RLP-->DMA-->HIS - * - * ISC video pipeline integrates the following submodules: - * PFE: Parallel Front End to sample the camera sensor input stream - * DPC: Defective Pixel Correction with black offset correction, green disparity - * correction and defective pixel correction (3 modules total) - * WB: Programmable white balance in the Bayer domain - * CFA: Color filter array interpolation module - * CC: Programmable color correction - * GAM: Gamma correction - *VHXS: Vertical and Horizontal Scaler - * CSC: Programmable color space conversion - *CBHS: Contrast Brightness Hue and Saturation control - * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling - * RLP: This module performs rounding, range limiting - * and packing of the incoming data - * DMA: This module performs DMA master accesses to write frames to external RAM - * HIS: Histogram module performs statistic counters on the frames - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "atmel-isc-regs.h" -#include "atmel-isc.h" - -#define ISC_SAMA7G5_MAX_SUPPORT_WIDTH 3264 -#define ISC_SAMA7G5_MAX_SUPPORT_HEIGHT 2464 - -#define ISC_SAMA7G5_PIPELINE \ - (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \ - CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE) - -/* This is a list of the formats that the ISC can *output* */ -static const struct isc_format sama7g5_controller_formats[] = { - { - .fourcc = V4L2_PIX_FMT_ARGB444, - }, { - .fourcc = V4L2_PIX_FMT_ARGB555, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, - }, { - .fourcc = V4L2_PIX_FMT_ABGR32, - }, { - .fourcc = V4L2_PIX_FMT_XBGR32, - }, { - .fourcc = V4L2_PIX_FMT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - }, { - .fourcc = V4L2_PIX_FMT_YUYV, - }, { - .fourcc = V4L2_PIX_FMT_YUV422P, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - }, { - .fourcc = V4L2_PIX_FMT_Y10, - }, { - .fourcc = V4L2_PIX_FMT_Y16, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR8, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10, - }, -}; - -/* This is a list of formats that the ISC can receive as *input* */ -static struct isc_format sama7g5_formats_list[] = { - { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_BGBG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_BGBG, - }, - { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_GBGB, - }, - { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_GRGR, - }, - { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE, - .cfa_baycfg = ISC_BAY_CFG_RGRG, - }, - { - .fourcc = V4L2_PIX_FMT_GREY, - .mbus_code = MEDIA_BUS_FMT_Y8_1X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_YUYV, - .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_UYVY, - .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565, - .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT, - }, - { - .fourcc = V4L2_PIX_FMT_Y10, - .mbus_code = MEDIA_BUS_FMT_Y10_1X10, - .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN, - }, -}; - -static void isc_sama7g5_config_csc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Convert RGB to YUV */ - regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc, - 0x42 | (0x81 << 16)); - regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc, - 0x19 | (0x10 << 16)); - regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc, - 0xFDA | (0xFB6 << 16)); - regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc, - 0x70 | (0x80 << 16)); - regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc, - 0x70 | (0xFA2 << 16)); - regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc, - 0xFEE | (0x80 << 16)); -} - -static void isc_sama7g5_config_cbc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Configure what is set via v4l2 ctrls */ - regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness); - regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast); - /* Configure Hue and Saturation as neutral midpoint */ - regmap_write(regmap, ISC_CBCHS_HUE, 0); - regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4)); -} - -static void isc_sama7g5_config_cc(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - /* Configure each register at the neutral fixed point 1.0 or 0.0 */ - regmap_write(regmap, ISC_CC_RR_RG, (1 << 8)); - regmap_write(regmap, ISC_CC_RB_OR, 0); - regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16); - regmap_write(regmap, ISC_CC_GB_OG, 0); - regmap_write(regmap, ISC_CC_BR_BG, 0); - regmap_write(regmap, ISC_CC_BB_OB, (1 << 8)); -} - -static void isc_sama7g5_config_ctrls(struct isc_device *isc, - const struct v4l2_ctrl_ops *ops) -{ - struct isc_ctrls *ctrls = &isc->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; - - ctrls->contrast = 16; - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16); -} - -static void isc_sama7g5_config_dpc(struct isc_device *isc) -{ - u32 bay_cfg = isc->config.sd_format->cfa_baycfg; - struct regmap *regmap = isc->regmap; - - regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BLOFF_MASK, - (64 << ISC_DPC_CFG_BLOFF_SHIFT)); - regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BAYCFG_MASK, - (bay_cfg << ISC_DPC_CFG_BAYCFG_SHIFT)); -} - -static void isc_sama7g5_config_gam(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - - regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART, - ISC_GAM_CTRL_BIPART); -} - -static void isc_sama7g5_config_rlp(struct isc_device *isc) -{ - struct regmap *regmap = isc->regmap; - u32 rlp_mode = isc->config.rlp_cfg_mode; - - regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp, - ISC_RLP_CFG_MODE_MASK | ISC_RLP_CFG_LSH | - ISC_RLP_CFG_YMODE_MASK, rlp_mode); -} - -static void isc_sama7g5_adapt_pipeline(struct isc_device *isc) -{ - isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE; -} - -/* Gamma table with gamma 1/2.2 */ -static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = { - /* index 0 --> gamma bipartite */ - { - 0x980, 0x4c0320, 0x650260, 0x7801e0, 0x8701a0, 0x940180, - 0xa00160, 0xab0120, 0xb40120, 0xbd0120, 0xc60100, 0xce0100, - 0xd600e0, 0xdd00e0, 0xe400e0, 0xeb00c0, 0xf100c0, 0xf700c0, - 0xfd00c0, 0x10300a0, 0x10800c0, 0x10e00a0, 0x11300a0, 0x11800a0, - 0x11d00a0, 0x12200a0, 0x12700a0, 0x12c0080, 0x13000a0, 0x1350080, - 0x13900a0, 0x13e0080, 0x1420076, 0x17d0062, 0x1ae0054, 0x1d8004a, - 0x1fd0044, 0x21f003e, 0x23e003a, 0x25b0036, 0x2760032, 0x28f0030, - 0x2a7002e, 0x2be002c, 0x2d4002c, 0x2ea0028, 0x2fe0028, 0x3120026, - 0x3250024, 0x3370024, 0x3490022, 0x35a0022, 0x36b0020, 0x37b0020, - 0x38b0020, 0x39b001e, 0x3aa001e, 0x3b9001c, 0x3c7001c, 0x3d5001c, - 0x3e3001c, 0x3f1001c, 0x3ff001a, 0x40c001a }, -}; - -static int xisc_parse_dt(struct device *dev, struct isc_device *isc) -{ - struct device_node *np = dev->of_node; - struct device_node *epn; - struct isc_subdev_entity *subdev_entity; - unsigned int flags; - int ret = -EINVAL; - bool mipi_mode; - - INIT_LIST_HEAD(&isc->subdev_entities); - - mipi_mode = of_property_read_bool(np, "microchip,mipi-mode"); - - for_each_endpoint_of_node(np, epn) { - struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; - - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), - &v4l2_epn); - if (ret) { - ret = -EINVAL; - dev_err(dev, "Could not parse the endpoint\n"); - break; - } - - subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), - GFP_KERNEL); - if (!subdev_entity) { - ret = -ENOMEM; - break; - } - subdev_entity->epn = epn; - - flags = v4l2_epn.bus.parallel.flags; - - if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW; - - if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW; - - if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW; - - if (v4l2_epn.bus_type == V4L2_MBUS_BT656) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC | - ISC_PFE_CFG0_CCIR656; - - if (mipi_mode) - subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI; - - list_add_tail(&subdev_entity->list, &isc->subdev_entities); - } - of_node_put(epn); - - return ret; -} - -static int microchip_xisc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct isc_device *isc; - void __iomem *io_base; - struct isc_subdev_entity *subdev_entity; - int irq; - int ret; - u32 ver; - - isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL); - if (!isc) - return -ENOMEM; - - platform_set_drvdata(pdev, isc); - isc->dev = dev; - - io_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(io_base)) - return PTR_ERR(io_base); - - isc->regmap = devm_regmap_init_mmio(dev, io_base, &atmel_isc_regmap_config); - if (IS_ERR(isc->regmap)) { - ret = PTR_ERR(isc->regmap); - dev_err(dev, "failed to init register map: %d\n", ret); - return ret; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - ret = devm_request_irq(dev, irq, atmel_isc_interrupt, 0, - "microchip-sama7g5-xisc", isc); - if (ret < 0) { - dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", - irq, ret); - return ret; - } - - isc->gamma_table = isc_sama7g5_gamma_table; - isc->gamma_max = 0; - - isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH; - isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT; - - isc->config_dpc = isc_sama7g5_config_dpc; - isc->config_csc = isc_sama7g5_config_csc; - isc->config_cbc = isc_sama7g5_config_cbc; - isc->config_cc = isc_sama7g5_config_cc; - isc->config_gam = isc_sama7g5_config_gam; - isc->config_rlp = isc_sama7g5_config_rlp; - isc->config_ctrls = isc_sama7g5_config_ctrls; - - isc->adapt_pipeline = isc_sama7g5_adapt_pipeline; - - isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET; - isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET; - isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET; - isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET; - isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET; - isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET; - isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET; - isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET; - isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET; - - isc->controller_formats = sama7g5_controller_formats; - isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats); - isc->formats_list = sama7g5_formats_list; - isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list); - - /* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */ - isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32; - - /* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */ - isc->ispck_required = false; - - ret = atmel_isc_pipeline_init(isc); - if (ret) - return ret; - - isc->hclock = devm_clk_get(dev, "hclock"); - if (IS_ERR(isc->hclock)) { - ret = PTR_ERR(isc->hclock); - dev_err(dev, "failed to get hclock: %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(isc->hclock); - if (ret) { - dev_err(dev, "failed to enable hclock: %d\n", ret); - return ret; - } - - ret = atmel_isc_clk_init(isc); - if (ret) { - dev_err(dev, "failed to init isc clock: %d\n", ret); - goto unprepare_hclk; - } - - ret = v4l2_device_register(dev, &isc->v4l2_dev); - if (ret) { - dev_err(dev, "unable to register v4l2 device.\n"); - goto unprepare_hclk; - } - - ret = xisc_parse_dt(dev, isc); - if (ret) { - dev_err(dev, "fail to parse device tree\n"); - goto unregister_v4l2_device; - } - - if (list_empty(&isc->subdev_entities)) { - dev_err(dev, "no subdev found\n"); - ret = -ENODEV; - goto unregister_v4l2_device; - } - - list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { - struct v4l2_async_connection *asd; - struct fwnode_handle *fwnode = - of_fwnode_handle(subdev_entity->epn); - - v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev); - - asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier, - fwnode, - struct v4l2_async_connection); - - of_node_put(subdev_entity->epn); - subdev_entity->epn = NULL; - - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); - goto cleanup_subdev; - } - - subdev_entity->notifier.ops = &atmel_isc_async_ops; - - ret = v4l2_async_nf_register(&subdev_entity->notifier); - if (ret) { - dev_err(dev, "fail to register async notifier\n"); - goto cleanup_subdev; - } - - if (video_is_registered(&isc->video_dev)) - break; - } - - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_request_idle(dev); - - regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver); - dev_info(dev, "Microchip XISC version %x\n", ver); - - return 0; - -cleanup_subdev: - atmel_isc_subdev_cleanup(isc); - -unregister_v4l2_device: - v4l2_device_unregister(&isc->v4l2_dev); - -unprepare_hclk: - clk_disable_unprepare(isc->hclock); - - atmel_isc_clk_cleanup(isc); - - return ret; -} - -static void microchip_xisc_remove(struct platform_device *pdev) -{ - struct isc_device *isc = platform_get_drvdata(pdev); - - pm_runtime_disable(&pdev->dev); - - atmel_isc_subdev_cleanup(isc); - - v4l2_device_unregister(&isc->v4l2_dev); - - clk_disable_unprepare(isc->hclock); - - atmel_isc_clk_cleanup(isc); -} - -static int __maybe_unused xisc_runtime_suspend(struct device *dev) -{ - struct isc_device *isc = dev_get_drvdata(dev); - - clk_disable_unprepare(isc->hclock); - - return 0; -} - -static int __maybe_unused xisc_runtime_resume(struct device *dev) -{ - struct isc_device *isc = dev_get_drvdata(dev); - int ret; - - ret = clk_prepare_enable(isc->hclock); - if (ret) - return ret; - - return ret; -} - -static const struct dev_pm_ops microchip_xisc_dev_pm_ops = { - SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL) -}; - -#if IS_ENABLED(CONFIG_OF) -static const struct of_device_id microchip_xisc_of_match[] = { - { .compatible = "microchip,sama7g5-isc" }, - { } -}; -MODULE_DEVICE_TABLE(of, microchip_xisc_of_match); -#endif - -static struct platform_driver microchip_xisc_driver = { - .probe = microchip_xisc_probe, - .remove = microchip_xisc_remove, - .driver = { - .name = "microchip-sama7g5-xisc", - .pm = µchip_xisc_dev_pm_ops, - .of_match_table = of_match_ptr(microchip_xisc_of_match), - }, -}; - -module_platform_driver(microchip_xisc_driver); - -MODULE_AUTHOR("Eugen Hristev "); -MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC"); -MODULE_LICENSE("GPL v2"); From 5d579ece43f4ed2d88bd5e14d6de682dbc532954 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 17 May 2026 19:17:43 +0200 Subject: [PATCH 209/394] media: ivtv: use clamp in ivtv_try_fmt_vid_{out,cap} Replace multiple min(), max() calls with clamp(). Signed-off-by: Thorsten Blum Signed-off-by: Hans Verkuil --- drivers/media/pci/ivtv/ivtv-ioctl.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index 8d5ea3aec06f..fc95f0bf48d5 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -467,15 +467,13 @@ static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format int h = fmt->fmt.pix.height; int min_h = 2; - w = min(w, 720); - w = max(w, 2); + w = clamp(w, 2, 720); if (id->type == IVTV_ENC_STREAM_TYPE_YUV) { /* YUV height must be a multiple of 32 */ h &= ~0x1f; min_h = 32; } - h = min(h, itv->is_50hz ? 576 : 480); - h = max(h, min_h); + h = clamp(h, min_h, itv->is_50hz ? 576 : 480); ivtv_g_fmt_vid_cap(file, fh, fmt); fmt->fmt.pix.width = w; fmt->fmt.pix.height = h; @@ -516,8 +514,7 @@ static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format int field = fmt->fmt.pix.field; int ret = ivtv_g_fmt_vid_out(file, fh, fmt); - w = min(w, 720); - w = max(w, 2); + w = clamp(w, 2, 720); /* Why can the height be 576 even when the output is NTSC? Internally the buffers of the PVR350 are always set to 720x576. The @@ -533,8 +530,7 @@ static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format resolution is locked to the broadcast standard and not scaled. Thanks to Ian Armstrong for this explanation. */ - h = min(h, 576); - h = max(h, 2); + h = clamp(h, 2, 576); if (id->type == IVTV_DEC_STREAM_TYPE_YUV) fmt->fmt.pix.field = field; fmt->fmt.pix.width = w; From 2958d579ffb13571c9788d741e6115dc917b05a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Fri, 15 May 2026 18:15:28 +0200 Subject: [PATCH 210/394] media: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. While touching all these arrays, unify usage of whitespace and commas. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Hans Verkuil --- drivers/media/cec/i2c/tda9950.c | 2 +- drivers/media/dvb-frontends/a8293.c | 4 +- drivers/media/dvb-frontends/af9013.c | 4 +- drivers/media/dvb-frontends/af9033.c | 4 +- drivers/media/dvb-frontends/au8522_decoder.c | 4 +- drivers/media/dvb-frontends/cxd2099.c | 4 +- drivers/media/dvb-frontends/cxd2820r_core.c | 4 +- drivers/media/dvb-frontends/dvb-pll.c | 44 +++++++++---------- drivers/media/dvb-frontends/helene.c | 4 +- drivers/media/dvb-frontends/lgdt3306a.c | 4 +- drivers/media/dvb-frontends/lgdt330x.c | 4 +- drivers/media/dvb-frontends/m88ds3103.c | 10 ++--- drivers/media/dvb-frontends/mn88443x.c | 8 ++-- drivers/media/dvb-frontends/mn88472.c | 4 +- drivers/media/dvb-frontends/mn88473.c | 4 +- drivers/media/dvb-frontends/mxl692.c | 4 +- drivers/media/dvb-frontends/rtl2830.c | 4 +- drivers/media/dvb-frontends/rtl2832.c | 4 +- drivers/media/dvb-frontends/si2165.c | 4 +- drivers/media/dvb-frontends/si2168.c | 4 +- drivers/media/dvb-frontends/sp2.c | 4 +- drivers/media/dvb-frontends/stv090x.c | 4 +- drivers/media/dvb-frontends/stv6110x.c | 4 +- drivers/media/dvb-frontends/tc90522.c | 6 +-- drivers/media/dvb-frontends/tda10071.c | 4 +- drivers/media/dvb-frontends/ts2020.c | 6 +-- drivers/media/i2c/ad5820.c | 4 +- drivers/media/i2c/adp1653.c | 2 +- drivers/media/i2c/adv7170.c | 4 +- drivers/media/i2c/adv7175.c | 4 +- drivers/media/i2c/adv7180.c | 24 +++++----- drivers/media/i2c/adv7183.c | 4 +- drivers/media/i2c/adv7343.c | 4 +- drivers/media/i2c/adv7393.c | 4 +- drivers/media/i2c/adv7511-v4l2.c | 2 +- drivers/media/i2c/adv7604.c | 8 ++-- drivers/media/i2c/adv7842.c | 2 +- drivers/media/i2c/ak881x.c | 4 +- drivers/media/i2c/bt819.c | 6 +-- drivers/media/i2c/bt856.c | 2 +- drivers/media/i2c/bt866.c | 2 +- drivers/media/i2c/cs3308.c | 2 +- drivers/media/i2c/cs5345.c | 2 +- drivers/media/i2c/cs53l32a.c | 2 +- drivers/media/i2c/cx25840/cx25840-core.c | 2 +- drivers/media/i2c/ds90ub913.c | 4 +- drivers/media/i2c/ds90ub953.c | 6 +-- drivers/media/i2c/ds90ub960.c | 8 ++-- drivers/media/i2c/dw9714.c | 2 +- drivers/media/i2c/et8ek8/et8ek8_driver.c | 2 +- drivers/media/i2c/imx274.c | 2 +- drivers/media/i2c/ir-kbd-i2c.c | 6 +-- drivers/media/i2c/isl7998x.c | 2 +- drivers/media/i2c/ks0127.c | 6 +-- drivers/media/i2c/lm3560.c | 4 +- drivers/media/i2c/lm3646.c | 4 +- drivers/media/i2c/m52790.c | 2 +- drivers/media/i2c/max2175.c | 4 +- drivers/media/i2c/ml86v7667.c | 4 +- drivers/media/i2c/msp3400-driver.c | 2 +- drivers/media/i2c/mt9m001.c | 2 +- drivers/media/i2c/mt9m111.c | 2 +- drivers/media/i2c/mt9t112.c | 2 +- drivers/media/i2c/mt9v011.c | 2 +- drivers/media/i2c/ov13858.c | 4 +- drivers/media/i2c/ov2640.c | 2 +- drivers/media/i2c/ov2659.c | 2 +- drivers/media/i2c/ov5640.c | 4 +- drivers/media/i2c/ov5645.c | 4 +- drivers/media/i2c/ov5647.c | 2 +- drivers/media/i2c/ov7640.c | 2 +- drivers/media/i2c/ov7670.c | 4 +- drivers/media/i2c/ov772x.c | 2 +- drivers/media/i2c/ov7740.c | 2 +- drivers/media/i2c/ov9640.c | 2 +- drivers/media/i2c/ov9650.c | 4 +- drivers/media/i2c/rj54n1cb0c.c | 2 +- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 2 +- drivers/media/i2c/s5k5baf.c | 2 +- drivers/media/i2c/saa6588.c | 2 +- drivers/media/i2c/saa6752hs.c | 2 +- drivers/media/i2c/saa7110.c | 2 +- drivers/media/i2c/saa7115.c | 14 +++--- drivers/media/i2c/saa7127.c | 10 ++--- drivers/media/i2c/saa717x.c | 2 +- drivers/media/i2c/saa7185.c | 2 +- drivers/media/i2c/sony-btf-mpx.c | 2 +- drivers/media/i2c/tc358743.c | 4 +- drivers/media/i2c/tda1997x.c | 6 +-- drivers/media/i2c/tda7432.c | 2 +- drivers/media/i2c/tda9840.c | 2 +- drivers/media/i2c/tea6415c.c | 2 +- drivers/media/i2c/tea6420.c | 2 +- drivers/media/i2c/ths7303.c | 6 +-- drivers/media/i2c/ths8200.c | 4 +- drivers/media/i2c/tlv320aic23b.c | 2 +- drivers/media/i2c/tvaudio.c | 2 +- drivers/media/i2c/tvp514x.c | 8 ++-- drivers/media/i2c/tvp5150.c | 2 +- drivers/media/i2c/tvp7002.c | 2 +- drivers/media/i2c/tw2804.c | 2 +- drivers/media/i2c/tw9900.c | 2 +- drivers/media/i2c/tw9903.c | 2 +- drivers/media/i2c/tw9906.c | 2 +- drivers/media/i2c/tw9910.c | 2 +- drivers/media/i2c/uda1342.c | 2 +- drivers/media/i2c/upd64031a.c | 2 +- drivers/media/i2c/upd64083.c | 2 +- drivers/media/i2c/video-i2c.c | 6 +-- drivers/media/i2c/vp27smpx.c | 2 +- drivers/media/i2c/vpx3220.c | 6 +-- drivers/media/i2c/wm8739.c | 2 +- drivers/media/i2c/wm8775.c | 2 +- drivers/media/radio/radio-tea5764.c | 2 +- drivers/media/radio/saa7706h.c | 4 +- drivers/media/radio/si470x/radio-si470x-i2c.c | 2 +- drivers/media/radio/si4713/si4713.c | 2 +- drivers/media/radio/tef6862.c | 4 +- .../media/test-drivers/vidtv/vidtv_demod.c | 4 +- .../media/test-drivers/vidtv/vidtv_tuner.c | 4 +- drivers/media/tuners/e4000.c | 4 +- drivers/media/tuners/fc2580.c | 4 +- drivers/media/tuners/m88rs6000t.c | 4 +- drivers/media/tuners/mt2060.c | 4 +- drivers/media/tuners/mxl301rf.c | 4 +- drivers/media/tuners/qm1d1b0004.c | 4 +- drivers/media/tuners/qm1d1c0042.c | 4 +- drivers/media/tuners/si2157.c | 10 ++--- drivers/media/tuners/tda18212.c | 4 +- drivers/media/tuners/tda18250.c | 4 +- drivers/media/tuners/tua9001.c | 4 +- drivers/media/usb/go7007/s2250-board.c | 2 +- drivers/media/v4l2-core/tuner-core.c | 2 +- 133 files changed, 268 insertions(+), 268 deletions(-) diff --git a/drivers/media/cec/i2c/tda9950.c b/drivers/media/cec/i2c/tda9950.c index cbff851e0c85..2bece4e63687 100644 --- a/drivers/media/cec/i2c/tda9950.c +++ b/drivers/media/cec/i2c/tda9950.c @@ -486,7 +486,7 @@ static void tda9950_remove(struct i2c_client *client) } static struct i2c_device_id tda9950_ids[] = { - { "tda9950" }, + { .name = "tda9950" }, { } }; MODULE_DEVICE_TABLE(i2c, tda9950_ids); diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c index 7c0963054a8f..52e3dc928327 100644 --- a/drivers/media/dvb-frontends/a8293.c +++ b/drivers/media/dvb-frontends/a8293.c @@ -256,8 +256,8 @@ static void a8293_remove(struct i2c_client *client) } static const struct i2c_device_id a8293_id_table[] = { - { "a8293" }, - {} + { .name = "a8293" }, + { } }; MODULE_DEVICE_TABLE(i2c, a8293_id_table); diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c index 75f3063c193e..d335bfd18e44 100644 --- a/drivers/media/dvb-frontends/af9013.c +++ b/drivers/media/dvb-frontends/af9013.c @@ -1553,8 +1553,8 @@ static void af9013_remove(struct i2c_client *client) } static const struct i2c_device_id af9013_id_table[] = { - { "af9013" }, - {} + { .name = "af9013" }, + { } }; MODULE_DEVICE_TABLE(i2c, af9013_id_table); diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 9a3a4d6513dd..01761861b01f 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -1173,8 +1173,8 @@ static void af9033_remove(struct i2c_client *client) } static const struct i2c_device_id af9033_id_table[] = { - { "af9033" }, - {} + { .name = "af9033" }, + { } }; MODULE_DEVICE_TABLE(i2c, af9033_id_table); diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 58b959b272c6..9ae47ea3976b 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -768,8 +768,8 @@ static void au8522_remove(struct i2c_client *client) } static const struct i2c_device_id au8522_id[] = { - { "au8522" }, - {} + { .name = "au8522" }, + { } }; MODULE_DEVICE_TABLE(i2c, au8522_id); diff --git a/drivers/media/dvb-frontends/cxd2099.c b/drivers/media/dvb-frontends/cxd2099.c index f95950a61307..e6110af1edcd 100644 --- a/drivers/media/dvb-frontends/cxd2099.c +++ b/drivers/media/dvb-frontends/cxd2099.c @@ -672,8 +672,8 @@ static void cxd2099_remove(struct i2c_client *client) } static const struct i2c_device_id cxd2099_id[] = { - { "cxd2099" }, - {} + { .name = "cxd2099" }, + { } }; MODULE_DEVICE_TABLE(i2c, cxd2099_id); diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c index 3deefeff4bd1..fbbffe9e7251 100644 --- a/drivers/media/dvb-frontends/cxd2820r_core.c +++ b/drivers/media/dvb-frontends/cxd2820r_core.c @@ -723,8 +723,8 @@ static void cxd2820r_remove(struct i2c_client *client) } static const struct i2c_device_id cxd2820r_id_table[] = { - { "cxd2820r" }, - {} + { .name = "cxd2820r" }, + { } }; MODULE_DEVICE_TABLE(i2c, cxd2820r_id_table); diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c index f8fd23f60e3f..d36c163b772f 100644 --- a/drivers/media/dvb-frontends/dvb-pll.c +++ b/drivers/media/dvb-frontends/dvb-pll.c @@ -911,28 +911,28 @@ static void dvb_pll_remove(struct i2c_client *client) static const struct i2c_device_id dvb_pll_id[] = { - {"dtt7579", DVB_PLL_THOMSON_DTT7579}, - {"dtt759x", DVB_PLL_THOMSON_DTT759X}, - {"z201", DVB_PLL_LG_Z201}, - {"unknown_1", DVB_PLL_UNKNOWN_1}, - {"tua6010xs", DVB_PLL_TUA6010XS}, - {"env57h1xd5", DVB_PLL_ENV57H1XD5}, - {"tua6034", DVB_PLL_TUA6034}, - {"tda665x", DVB_PLL_TDA665X}, - {"tded4", DVB_PLL_TDED4}, - {"tdhu2", DVB_PLL_TDHU2}, - {"tbmv", DVB_PLL_SAMSUNG_TBMV}, - {"sd1878_tda8261", DVB_PLL_PHILIPS_SD1878_TDA8261}, - {"opera1", DVB_PLL_OPERA1}, - {"dtos403ih102a", DVB_PLL_SAMSUNG_DTOS403IH102A}, - {"tdtc9251dh0", DVB_PLL_SAMSUNG_TDTC9251DH0}, - {"tbdu18132", DVB_PLL_SAMSUNG_TBDU18132}, - {"tbmu24112", DVB_PLL_SAMSUNG_TBMU24112}, - {"tdee4", DVB_PLL_TDEE4}, - {"dtt7520x", DVB_PLL_THOMSON_DTT7520X}, - {"tua6034_friio", DVB_PLL_TUA6034_FRIIO}, - {"tda665x_earthpt1", DVB_PLL_TDA665X_EARTH_PT1}, - {} + { .name = "dtt7579", .driver_data = DVB_PLL_THOMSON_DTT7579 }, + { .name = "dtt759x", .driver_data = DVB_PLL_THOMSON_DTT759X }, + { .name = "z201", .driver_data = DVB_PLL_LG_Z201 }, + { .name = "unknown_1", .driver_data = DVB_PLL_UNKNOWN_1 }, + { .name = "tua6010xs", .driver_data = DVB_PLL_TUA6010XS }, + { .name = "env57h1xd5", .driver_data = DVB_PLL_ENV57H1XD5 }, + { .name = "tua6034", .driver_data = DVB_PLL_TUA6034 }, + { .name = "tda665x", .driver_data = DVB_PLL_TDA665X }, + { .name = "tded4", .driver_data = DVB_PLL_TDED4 }, + { .name = "tdhu2", .driver_data = DVB_PLL_TDHU2 }, + { .name = "tbmv", .driver_data = DVB_PLL_SAMSUNG_TBMV }, + { .name = "sd1878_tda8261", .driver_data = DVB_PLL_PHILIPS_SD1878_TDA8261 }, + { .name = "opera1", .driver_data = DVB_PLL_OPERA1 }, + { .name = "dtos403ih102a", .driver_data = DVB_PLL_SAMSUNG_DTOS403IH102A }, + { .name = "tdtc9251dh0", .driver_data = DVB_PLL_SAMSUNG_TDTC9251DH0 }, + { .name = "tbdu18132", .driver_data = DVB_PLL_SAMSUNG_TBDU18132 }, + { .name = "tbmu24112", .driver_data = DVB_PLL_SAMSUNG_TBMU24112 }, + { .name = "tdee4", .driver_data = DVB_PLL_TDEE4 }, + { .name = "dtt7520x", .driver_data = DVB_PLL_THOMSON_DTT7520X }, + { .name = "tua6034_friio", .driver_data = DVB_PLL_TUA6034_FRIIO }, + { .name = "tda665x_earthpt1", .driver_data = DVB_PLL_TDA665X_EARTH_PT1 }, + { } }; diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index 1402d124544e..993280fefc2c 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -1101,8 +1101,8 @@ static int helene_probe(struct i2c_client *client) } static const struct i2c_device_id helene_id[] = { - { "helene", }, - {} + { .name = "helene" }, + { } }; MODULE_DEVICE_TABLE(i2c, helene_id); diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index b6a66e122ed5..f4a3136baea3 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -2244,8 +2244,8 @@ static void lgdt3306a_remove(struct i2c_client *client) } static const struct i2c_device_id lgdt3306a_id_table[] = { - { "lgdt3306a" }, - {} + { .name = "lgdt3306a" }, + { } }; MODULE_DEVICE_TABLE(i2c, lgdt3306a_id_table); diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c index 19a4a05b3499..d90f642e9237 100644 --- a/drivers/media/dvb-frontends/lgdt330x.c +++ b/drivers/media/dvb-frontends/lgdt330x.c @@ -983,8 +983,8 @@ static void lgdt330x_remove(struct i2c_client *client) } static const struct i2c_device_id lgdt330x_id_table[] = { - { "lgdt330x" }, - {} + { .name = "lgdt330x" }, + { } }; MODULE_DEVICE_TABLE(i2c, lgdt330x_id_table); diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index 44bee1f3c5e9..d79b88848a8c 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -2221,11 +2221,11 @@ static void m88ds3103_remove(struct i2c_client *client) } static const struct i2c_device_id m88ds3103_id_table[] = { - {"m88ds3103", M88DS3103_CHIPTYPE_3103}, - {"m88rs6000", M88DS3103_CHIPTYPE_RS6000}, - {"m88ds3103b", M88DS3103_CHIPTYPE_3103B}, - {"m88ds3103c", M88DS3103_CHIPTYPE_3103C}, - {} + { .name = "m88ds3103", .driver_data = M88DS3103_CHIPTYPE_3103 }, + { .name = "m88rs6000", .driver_data = M88DS3103_CHIPTYPE_RS6000 }, + { .name = "m88ds3103b", .driver_data = M88DS3103_CHIPTYPE_3103B }, + { .name = "m88ds3103c", .driver_data = M88DS3103_CHIPTYPE_3103C }, + { } }; MODULE_DEVICE_TABLE(i2c, m88ds3103_id_table); diff --git a/drivers/media/dvb-frontends/mn88443x.c b/drivers/media/dvb-frontends/mn88443x.c index 818c4e67364c..cc6bd17daf73 100644 --- a/drivers/media/dvb-frontends/mn88443x.c +++ b/drivers/media/dvb-frontends/mn88443x.c @@ -787,10 +787,10 @@ static const struct of_device_id mn88443x_of_match[] = { MODULE_DEVICE_TABLE(of, mn88443x_of_match); static const struct i2c_device_id mn88443x_i2c_id[] = { - { "mn884433", (kernel_ulong_t)&mn88443x_spec_pri }, - { "mn884434-0", (kernel_ulong_t)&mn88443x_spec_pri }, - { "mn884434-1", (kernel_ulong_t)&mn88443x_spec_sec }, - {} + { .name = "mn884433", .driver_data = (kernel_ulong_t)&mn88443x_spec_pri }, + { .name = "mn884434-0", .driver_data = (kernel_ulong_t)&mn88443x_spec_pri }, + { .name = "mn884434-1", .driver_data = (kernel_ulong_t)&mn88443x_spec_sec }, + { } }; MODULE_DEVICE_TABLE(i2c, mn88443x_i2c_id); diff --git a/drivers/media/dvb-frontends/mn88472.c b/drivers/media/dvb-frontends/mn88472.c index 275c404ce286..42b78b9aba20 100644 --- a/drivers/media/dvb-frontends/mn88472.c +++ b/drivers/media/dvb-frontends/mn88472.c @@ -708,8 +708,8 @@ static void mn88472_remove(struct i2c_client *client) } static const struct i2c_device_id mn88472_id_table[] = { - { "mn88472" }, - {} + { .name = "mn88472" }, + { } }; MODULE_DEVICE_TABLE(i2c, mn88472_id_table); diff --git a/drivers/media/dvb-frontends/mn88473.c b/drivers/media/dvb-frontends/mn88473.c index 40a0cb1d9c67..b7b11a000969 100644 --- a/drivers/media/dvb-frontends/mn88473.c +++ b/drivers/media/dvb-frontends/mn88473.c @@ -743,8 +743,8 @@ static void mn88473_remove(struct i2c_client *client) } static const struct i2c_device_id mn88473_id_table[] = { - { "mn88473" }, - {} + { .name = "mn88473" }, + { } }; MODULE_DEVICE_TABLE(i2c, mn88473_id_table); diff --git a/drivers/media/dvb-frontends/mxl692.c b/drivers/media/dvb-frontends/mxl692.c index 2d8eaa20723f..bca4a4c3dd66 100644 --- a/drivers/media/dvb-frontends/mxl692.c +++ b/drivers/media/dvb-frontends/mxl692.c @@ -1346,8 +1346,8 @@ static void mxl692_remove(struct i2c_client *client) } static const struct i2c_device_id mxl692_id_table[] = { - { "mxl692" }, - {} + { .name = "mxl692" }, + { } }; MODULE_DEVICE_TABLE(i2c, mxl692_id_table); diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index f0ee7c38ee79..4c44e2d695f8 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -876,8 +876,8 @@ static void rtl2830_remove(struct i2c_client *client) } static const struct i2c_device_id rtl2830_id_table[] = { - { "rtl2830" }, - {} + { .name = "rtl2830" }, + { } }; MODULE_DEVICE_TABLE(i2c, rtl2830_id_table); diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 9898f729304a..b31cdb44b383 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -1125,8 +1125,8 @@ static void rtl2832_remove(struct i2c_client *client) } static const struct i2c_device_id rtl2832_id_table[] = { - { "rtl2832" }, - {} + { .name = "rtl2832" }, + { } }; MODULE_DEVICE_TABLE(i2c, rtl2832_id_table); diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c index 4170696af9f0..f1241b63aa5c 100644 --- a/drivers/media/dvb-frontends/si2165.c +++ b/drivers/media/dvb-frontends/si2165.c @@ -1281,8 +1281,8 @@ static void si2165_remove(struct i2c_client *client) } static const struct i2c_device_id si2165_id_table[] = { - { "si2165" }, - {} + { .name = "si2165" }, + { } }; MODULE_DEVICE_TABLE(i2c, si2165_id_table); diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 9c5bac8cda47..8bc3b6eb1dd3 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -790,8 +790,8 @@ static void si2168_remove(struct i2c_client *client) } static const struct i2c_device_id si2168_id_table[] = { - { "si2168" }, - {} + { .name = "si2168" }, + { } }; MODULE_DEVICE_TABLE(i2c, si2168_id_table); diff --git a/drivers/media/dvb-frontends/sp2.c b/drivers/media/dvb-frontends/sp2.c index 071c7371c699..63dec4b4fd2f 100644 --- a/drivers/media/dvb-frontends/sp2.c +++ b/drivers/media/dvb-frontends/sp2.c @@ -407,8 +407,8 @@ static void sp2_remove(struct i2c_client *client) } static const struct i2c_device_id sp2_id[] = { - { "sp2" }, - {} + { .name = "sp2" }, + { } }; MODULE_DEVICE_TABLE(i2c, sp2_id); diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c index 657df713865e..932bbed5497a 100644 --- a/drivers/media/dvb-frontends/stv090x.c +++ b/drivers/media/dvb-frontends/stv090x.c @@ -5079,8 +5079,8 @@ struct dvb_frontend *stv090x_attach(struct stv090x_config *config, EXPORT_SYMBOL_GPL(stv090x_attach); static const struct i2c_device_id stv090x_id_table[] = { - { "stv090x" }, - {} + { .name = "stv090x" }, + { } }; MODULE_DEVICE_TABLE(i2c, stv090x_id_table); diff --git a/drivers/media/dvb-frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c index 7506f39ead55..5075333d7ffc 100644 --- a/drivers/media/dvb-frontends/stv6110x.c +++ b/drivers/media/dvb-frontends/stv6110x.c @@ -470,8 +470,8 @@ const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, EXPORT_SYMBOL_GPL(stv6110x_attach); static const struct i2c_device_id stv6110x_id_table[] = { - { "stv6110x" }, - {} + { .name = "stv6110x" }, + { } }; MODULE_DEVICE_TABLE(i2c, stv6110x_id_table); diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c index f1343ba67b97..9a5ffb5f70fc 100644 --- a/drivers/media/dvb-frontends/tc90522.c +++ b/drivers/media/dvb-frontends/tc90522.c @@ -830,9 +830,9 @@ static void tc90522_remove(struct i2c_client *client) static const struct i2c_device_id tc90522_id[] = { - { TC90522_I2C_DEV_SAT, 0 }, - { TC90522_I2C_DEV_TER, 1 }, - {} + { .name = TC90522_I2C_DEV_SAT, .driver_data = 0 }, + { .name = TC90522_I2C_DEV_TER, .driver_data = 1 }, + { } }; MODULE_DEVICE_TABLE(i2c, tc90522_id); diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 6e6c2e5c427e..7a635c86b86c 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -1230,8 +1230,8 @@ static void tda10071_remove(struct i2c_client *client) } static const struct i2c_device_id tda10071_id_table[] = { - { "tda10071_cx24118" }, - {} + { .name = "tda10071_cx24118" }, + { } }; MODULE_DEVICE_TABLE(i2c, tda10071_id_table); diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index 8b6006635e49..8775083f4dd6 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -716,9 +716,9 @@ static void ts2020_remove(struct i2c_client *client) } static const struct i2c_device_id ts2020_id_table[] = { - { "ts2020" }, - { "ts2022" }, - {} + { .name = "ts2020" }, + { .name = "ts2022" }, + { } }; MODULE_DEVICE_TABLE(i2c, ts2020_id_table); diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index f60271082fb5..fcce2857b69e 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -347,8 +347,8 @@ static void ad5820_remove(struct i2c_client *client) } static const struct i2c_device_id ad5820_id_table[] = { - { "ad5820" }, - { "ad5821" }, + { .name = "ad5820" }, + { .name = "ad5821" }, { } }; MODULE_DEVICE_TABLE(i2c, ad5820_id_table); diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c index 391bc75bfcd0..177a6e8d7fb8 100644 --- a/drivers/media/i2c/adp1653.c +++ b/drivers/media/i2c/adp1653.c @@ -522,7 +522,7 @@ static void adp1653_remove(struct i2c_client *client) } static const struct i2c_device_id adp1653_id_table[] = { - { ADP1653_NAME }, + { .name = ADP1653_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, adp1653_id_table); diff --git a/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c index ef8682b980b4..812998729207 100644 --- a/drivers/media/i2c/adv7170.c +++ b/drivers/media/i2c/adv7170.c @@ -377,8 +377,8 @@ static void adv7170_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7170_id[] = { - { "adv7170" }, - { "adv7171" }, + { .name = "adv7170" }, + { .name = "adv7171" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7170_id); diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c index 384da1ec5bf9..f1caab8e2abd 100644 --- a/drivers/media/i2c/adv7175.c +++ b/drivers/media/i2c/adv7175.c @@ -432,8 +432,8 @@ static void adv7175_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7175_id[] = { - { "adv7175" }, - { "adv7176" }, + { .name = "adv7175" }, + { .name = "adv7176" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7175_id); diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 669b0b3165b1..e5d11a6e6766 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -1626,18 +1626,18 @@ static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume); #endif static const struct i2c_device_id adv7180_id[] = { - { "adv7180", (kernel_ulong_t)&adv7180_info }, - { "adv7180cp", (kernel_ulong_t)&adv7180_info }, - { "adv7180st", (kernel_ulong_t)&adv7180_info }, - { "adv7182", (kernel_ulong_t)&adv7182_info }, - { "adv7280", (kernel_ulong_t)&adv7280_info }, - { "adv7280-m", (kernel_ulong_t)&adv7280_m_info }, - { "adv7281", (kernel_ulong_t)&adv7281_info }, - { "adv7281-m", (kernel_ulong_t)&adv7281_m_info }, - { "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info }, - { "adv7282", (kernel_ulong_t)&adv7282_info }, - { "adv7282-m", (kernel_ulong_t)&adv7282_m_info }, - {} + { .name = "adv7180", .driver_data = (kernel_ulong_t)&adv7180_info }, + { .name = "adv7180cp", .driver_data = (kernel_ulong_t)&adv7180_info }, + { .name = "adv7180st", .driver_data = (kernel_ulong_t)&adv7180_info }, + { .name = "adv7182", .driver_data = (kernel_ulong_t)&adv7182_info }, + { .name = "adv7280", .driver_data = (kernel_ulong_t)&adv7280_info }, + { .name = "adv7280-m", .driver_data = (kernel_ulong_t)&adv7280_m_info }, + { .name = "adv7281", .driver_data = (kernel_ulong_t)&adv7281_info }, + { .name = "adv7281-m", .driver_data = (kernel_ulong_t)&adv7281_m_info }, + { .name = "adv7281-ma", .driver_data = (kernel_ulong_t)&adv7281_ma_info }, + { .name = "adv7282", .driver_data = (kernel_ulong_t)&adv7282_info }, + { .name = "adv7282-m", .driver_data = (kernel_ulong_t)&adv7282_m_info }, + { } }; MODULE_DEVICE_TABLE(i2c, adv7180_id); diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index 25a31a6dd456..a04a1a205fe0 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -619,8 +619,8 @@ static void adv7183_remove(struct i2c_client *client) } static const struct i2c_device_id adv7183_id[] = { - { "adv7183" }, - {} + { .name = "adv7183" }, + { } }; MODULE_DEVICE_TABLE(i2c, adv7183_id); diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c index b96443404a26..9b91d7073d3c 100644 --- a/drivers/media/i2c/adv7343.c +++ b/drivers/media/i2c/adv7343.c @@ -502,8 +502,8 @@ static void adv7343_remove(struct i2c_client *client) } static const struct i2c_device_id adv7343_id[] = { - { "adv7343" }, - {} + { .name = "adv7343" }, + { } }; MODULE_DEVICE_TABLE(i2c, adv7343_id); diff --git a/drivers/media/i2c/adv7393.c b/drivers/media/i2c/adv7393.c index c7994bd0bbd4..6f948ba02f86 100644 --- a/drivers/media/i2c/adv7393.c +++ b/drivers/media/i2c/adv7393.c @@ -446,8 +446,8 @@ static void adv7393_remove(struct i2c_client *client) } static const struct i2c_device_id adv7393_id[] = { - { "adv7393" }, - {} + { .name = "adv7393" }, + { } }; MODULE_DEVICE_TABLE(i2c, adv7393_id); diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index 853c7806de92..860cff50c522 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -2008,7 +2008,7 @@ static void adv7511_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7511_id[] = { - { "adv7511-v4l2" }, + { .name = "adv7511-v4l2" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7511_id); diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index ae75982fb514..ac9c69ce438f 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -3236,10 +3236,10 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { }; static const struct i2c_device_id adv76xx_i2c_id[] = { - { "adv7604", (kernel_ulong_t)&adv76xx_chip_info[ADV7604] }, - { "adv7610", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, - { "adv7611", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, - { "adv7612", (kernel_ulong_t)&adv76xx_chip_info[ADV7612] }, + { .name = "adv7604", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7604] }, + { .name = "adv7610", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, + { .name = "adv7611", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7611] }, + { .name = "adv7612", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7612] }, { } }; MODULE_DEVICE_TABLE(i2c, adv76xx_i2c_id); diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index ea6966c0605e..3cfae89ce944 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -3675,7 +3675,7 @@ static void adv7842_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7842_id[] = { - { "adv7842" }, + { .name = "adv7842" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7842_id); diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c index ee575d01a676..cea46f01997d 100644 --- a/drivers/media/i2c/ak881x.c +++ b/drivers/media/i2c/ak881x.c @@ -304,8 +304,8 @@ static void ak881x_remove(struct i2c_client *client) } static const struct i2c_device_id ak881x_id[] = { - { "ak8813" }, - { "ak8814" }, + { .name = "ak8813" }, + { .name = "ak8814" }, { } }; MODULE_DEVICE_TABLE(i2c, ak881x_id); diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c index f97245f91f88..da44100062fa 100644 --- a/drivers/media/i2c/bt819.c +++ b/drivers/media/i2c/bt819.c @@ -457,9 +457,9 @@ static void bt819_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id bt819_id[] = { - { "bt819a" }, - { "bt817a" }, - { "bt815a" }, + { .name = "bt819a" }, + { .name = "bt817a" }, + { .name = "bt815a" }, { } }; MODULE_DEVICE_TABLE(i2c, bt819_id); diff --git a/drivers/media/i2c/bt856.c b/drivers/media/i2c/bt856.c index 6852aa47cafb..656719158bb4 100644 --- a/drivers/media/i2c/bt856.c +++ b/drivers/media/i2c/bt856.c @@ -230,7 +230,7 @@ static void bt856_remove(struct i2c_client *client) } static const struct i2c_device_id bt856_id[] = { - { "bt856" }, + { .name = "bt856" }, { } }; MODULE_DEVICE_TABLE(i2c, bt856_id); diff --git a/drivers/media/i2c/bt866.c b/drivers/media/i2c/bt866.c index a2cc34d35ed2..f5104c7d9f90 100644 --- a/drivers/media/i2c/bt866.c +++ b/drivers/media/i2c/bt866.c @@ -197,7 +197,7 @@ static void bt866_remove(struct i2c_client *client) } static const struct i2c_device_id bt866_id[] = { - { "bt866" }, + { .name = "bt866" }, { } }; MODULE_DEVICE_TABLE(i2c, bt866_id); diff --git a/drivers/media/i2c/cs3308.c b/drivers/media/i2c/cs3308.c index 3f2f993e16a6..ad37aeecf05e 100644 --- a/drivers/media/i2c/cs3308.c +++ b/drivers/media/i2c/cs3308.c @@ -109,7 +109,7 @@ static void cs3308_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id cs3308_id[] = { - { "cs3308" }, + { .name = "cs3308" }, { } }; MODULE_DEVICE_TABLE(i2c, cs3308_id); diff --git a/drivers/media/i2c/cs5345.c b/drivers/media/i2c/cs5345.c index 3a9797a50e82..49b8020fbea2 100644 --- a/drivers/media/i2c/cs5345.c +++ b/drivers/media/i2c/cs5345.c @@ -189,7 +189,7 @@ static void cs5345_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id cs5345_id[] = { - { "cs5345" }, + { .name = "cs5345" }, { } }; MODULE_DEVICE_TABLE(i2c, cs5345_id); diff --git a/drivers/media/i2c/cs53l32a.c b/drivers/media/i2c/cs53l32a.c index c4cad3293905..a894dcf6b5a9 100644 --- a/drivers/media/i2c/cs53l32a.c +++ b/drivers/media/i2c/cs53l32a.c @@ -200,7 +200,7 @@ static void cs53l32a_remove(struct i2c_client *client) } static const struct i2c_device_id cs53l32a_id[] = { - { "cs53l32a" }, + { .name = "cs53l32a" }, { } }; MODULE_DEVICE_TABLE(i2c, cs53l32a_id); diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index 69d5cc648c0f..8110d40931d9 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -3989,7 +3989,7 @@ static void cx25840_remove(struct i2c_client *client) } static const struct i2c_device_id cx25840_id[] = { - { "cx25840" }, + { .name = "cx25840" }, { } }; MODULE_DEVICE_TABLE(i2c, cx25840_id); diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index 49aa5f4a172c..6abb5e324ae1 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -872,8 +872,8 @@ static void ub913_remove(struct i2c_client *client) } static const struct i2c_device_id ub913_id[] = { - { "ds90ub913a-q1" }, - {} + { .name = "ds90ub913a-q1" }, + { } }; MODULE_DEVICE_TABLE(i2c, ub913_id); diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index a8ab67f4137f..d4228e1134ff 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -1347,9 +1347,9 @@ static const struct ub953_hw_data ds90ub971_hw = { }; static const struct i2c_device_id ub953_id[] = { - { "ds90ub953-q1", (kernel_ulong_t)&ds90ub953_hw }, - { "ds90ub971-q1", (kernel_ulong_t)&ds90ub971_hw }, - {} + { .name = "ds90ub953-q1", .driver_data = (kernel_ulong_t)&ds90ub953_hw }, + { .name = "ds90ub971-q1", .driver_data = (kernel_ulong_t)&ds90ub971_hw }, + { } }; MODULE_DEVICE_TABLE(i2c, ub953_id); diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index d50e977cf6ce..15a9797b47ac 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -5266,10 +5266,10 @@ static const struct ub960_hw_data ds90ub9702_hw = { }; static const struct i2c_device_id ub960_id[] = { - { "ds90ub954-q1", (kernel_ulong_t)&ds90ub954_hw }, - { "ds90ub960-q1", (kernel_ulong_t)&ds90ub960_hw }, - { "ds90ub9702-q1", (kernel_ulong_t)&ds90ub9702_hw }, - {} + { .name = "ds90ub954-q1", .driver_data = (kernel_ulong_t)&ds90ub954_hw }, + { .name = "ds90ub960-q1", .driver_data = (kernel_ulong_t)&ds90ub960_hw }, + { .name = "ds90ub9702-q1", .driver_data = (kernel_ulong_t)&ds90ub9702_hw }, + { } }; MODULE_DEVICE_TABLE(i2c, ub960_id); diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c index 3288de539452..1d686b1d2fd7 100644 --- a/drivers/media/i2c/dw9714.c +++ b/drivers/media/i2c/dw9714.c @@ -307,7 +307,7 @@ static int __maybe_unused dw9714_vcm_resume(struct device *dev) } static const struct i2c_device_id dw9714_id_table[] = { - { DW9714_NAME }, + { .name = DW9714_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, dw9714_id_table); diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index 50121c3e5b48..738e2801016a 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -1485,7 +1485,7 @@ static const struct of_device_id et8ek8_of_table[] = { MODULE_DEVICE_TABLE(of, et8ek8_of_table); static const struct i2c_device_id et8ek8_id_table[] = { - { ET8EK8_NAME }, + { .name = ET8EK8_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, et8ek8_id_table); diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 241821572e03..882dc2e9dd8f 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -1913,7 +1913,7 @@ static const struct of_device_id imx274_of_id_table[] = { MODULE_DEVICE_TABLE(of, imx274_of_id_table); static const struct i2c_device_id imx274_id[] = { - { "IMX274" }, + { .name = "IMX274" }, { } }; MODULE_DEVICE_TABLE(i2c, imx274_id); diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index 604745317004..f2bf2b354000 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -978,10 +978,10 @@ static void ir_remove(struct i2c_client *client) static const struct i2c_device_id ir_kbd_id[] = { /* Generic entry for any IR receiver */ - { "ir_video", 0 }, + { .name = "ir_video", .driver_data = 0 }, /* IR device specific entries should be added here */ - { "ir_z8f0811_haup", FLAG_TX }, - { "ir_z8f0811_hdpvr", FLAG_TX | FLAG_HDPVR }, + { .name = "ir_z8f0811_haup", .driver_data = FLAG_TX }, + { .name = "ir_z8f0811_hdpvr", .driver_data = FLAG_TX | FLAG_HDPVR }, { } }; MODULE_DEVICE_TABLE(i2c, ir_kbd_id); diff --git a/drivers/media/i2c/isl7998x.c b/drivers/media/i2c/isl7998x.c index 5ffd53e005ee..a77538d2343c 100644 --- a/drivers/media/i2c/isl7998x.c +++ b/drivers/media/i2c/isl7998x.c @@ -1561,7 +1561,7 @@ static const struct of_device_id isl7998x_of_match[] = { MODULE_DEVICE_TABLE(of, isl7998x_of_match); static const struct i2c_device_id isl7998x_id[] = { - { "isl79987" }, + { .name = "isl79987" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, isl7998x_id); diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c index f3fba9179684..8c66be38adc3 100644 --- a/drivers/media/i2c/ks0127.c +++ b/drivers/media/i2c/ks0127.c @@ -677,9 +677,9 @@ static void ks0127_remove(struct i2c_client *client) } static const struct i2c_device_id ks0127_id[] = { - { "ks0127" }, - { "ks0127b" }, - { "ks0122s" }, + { .name = "ks0127" }, + { .name = "ks0127b" }, + { .name = "ks0122s" }, { } }; MODULE_DEVICE_TABLE(i2c, ks0127_id); diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index 46d316a26e50..c3c90d830ee2 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -704,8 +704,8 @@ static const struct of_device_id lm3560_of_match[] = { MODULE_DEVICE_TABLE(of, lm3560_of_match); static const struct i2c_device_id lm3560_id_table[] = { - { "lm3559", (kernel_ulong_t)&lm3559_config }, - { "lm3560", (kernel_ulong_t)&lm3560_config }, + { .name = "lm3559", .driver_data = (kernel_ulong_t)&lm3559_config }, + { .name = "lm3560", .driver_data = (kernel_ulong_t)&lm3560_config }, { } }; MODULE_DEVICE_TABLE(i2c, lm3560_id_table); diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c index 2d16e42ec224..9030cbe32afc 100644 --- a/drivers/media/i2c/lm3646.c +++ b/drivers/media/i2c/lm3646.c @@ -386,8 +386,8 @@ static void lm3646_remove(struct i2c_client *client) } static const struct i2c_device_id lm3646_id_table[] = { - { LM3646_NAME }, - {} + { .name = LM3646_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, lm3646_id_table); diff --git a/drivers/media/i2c/m52790.c b/drivers/media/i2c/m52790.c index 9e1ecfd01e2a..f3adf8c2b27f 100644 --- a/drivers/media/i2c/m52790.c +++ b/drivers/media/i2c/m52790.c @@ -163,7 +163,7 @@ static void m52790_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id m52790_id[] = { - { "m52790" }, + { .name = "m52790" }, { } }; MODULE_DEVICE_TABLE(i2c, m52790_id); diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c index bf02ca23a284..1cc388b52902 100644 --- a/drivers/media/i2c/max2175.c +++ b/drivers/media/i2c/max2175.c @@ -1413,8 +1413,8 @@ static void max2175_remove(struct i2c_client *client) } static const struct i2c_device_id max2175_id[] = { - { DRIVER_NAME }, - {} + { .name = DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, max2175_id); diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c index 57ba3693649a..48b7d589df31 100644 --- a/drivers/media/i2c/ml86v7667.c +++ b/drivers/media/i2c/ml86v7667.c @@ -424,8 +424,8 @@ static void ml86v7667_remove(struct i2c_client *client) } static const struct i2c_device_id ml86v7667_id[] = { - { DRV_NAME }, - {} + { .name = DRV_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, ml86v7667_id); diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index 4c0b0ad68c08..413cfbc2dd94 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -874,7 +874,7 @@ static const struct dev_pm_ops msp3400_pm_ops = { }; static const struct i2c_device_id msp_id[] = { - { "msp3400" }, + { .name = "msp3400" }, { } }; MODULE_DEVICE_TABLE(i2c, msp_id); diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 7a6114d18dfc..0ade967b357b 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -855,7 +855,7 @@ static void mt9m001_remove(struct i2c_client *client) } static const struct i2c_device_id mt9m001_id[] = { - { "mt9m001" }, + { .name = "mt9m001" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9m001_id); diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 3532c7c38bec..4e748080b798 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -1384,7 +1384,7 @@ static const struct of_device_id mt9m111_of_match[] = { MODULE_DEVICE_TABLE(of, mt9m111_of_match); static const struct i2c_device_id mt9m111_id[] = { - { "mt9m111" }, + { .name = "mt9m111" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9m111_id); diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c index 2d2c840fc002..bd2268154ca7 100644 --- a/drivers/media/i2c/mt9t112.c +++ b/drivers/media/i2c/mt9t112.c @@ -1108,7 +1108,7 @@ static void mt9t112_remove(struct i2c_client *client) } static const struct i2c_device_id mt9t112_id[] = { - { "mt9t112" }, + { .name = "mt9t112" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9t112_id); diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 055b7915260a..985517f1cff7 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -582,7 +582,7 @@ static void mt9v011_remove(struct i2c_client *c) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id mt9v011_id[] = { - { "mt9v011" }, + { .name = "mt9v011" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9v011_id); diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 162b49046990..09d58e8b1c7f 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1747,8 +1747,8 @@ static void ov13858_remove(struct i2c_client *client) } static const struct i2c_device_id ov13858_id_table[] = { - { "ov13858" }, - {} + { .name = "ov13858" }, + { } }; MODULE_DEVICE_TABLE(i2c, ov13858_id_table); diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index d27fc2df64e6..50feb608b92b 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -1271,7 +1271,7 @@ static void ov2640_remove(struct i2c_client *client) } static const struct i2c_device_id ov2640_id[] = { - { "ov2640" }, + { .name = "ov2640" }, { } }; MODULE_DEVICE_TABLE(i2c, ov2640_id); diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 061401b020fc..7d8c7c3465a4 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1553,7 +1553,7 @@ static const struct dev_pm_ops ov2659_pm_ops = { }; static const struct i2c_device_id ov2659_id[] = { - { "ov2659" }, + { .name = "ov2659" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov2659_id); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 85ecc23b3587..92d2d6cd4ba4 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -3999,8 +3999,8 @@ static const struct dev_pm_ops ov5640_pm_ops = { }; static const struct i2c_device_id ov5640_id[] = { - { "ov5640" }, - {} + { .name = "ov5640" }, + { } }; MODULE_DEVICE_TABLE(i2c, ov5640_id); diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index b10d408034a1..c772ef6e51d2 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -1219,8 +1219,8 @@ static void ov5645_remove(struct i2c_client *client) } static const struct i2c_device_id ov5645_id[] = { - { "ov5645" }, - {} + { .name = "ov5645" }, + { } }; MODULE_DEVICE_TABLE(i2c, ov5645_id); diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index db9bd2892140..3facf92b3841 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -1278,7 +1278,7 @@ static const struct dev_pm_ops ov5647_pm_ops = { }; static const struct i2c_device_id ov5647_id[] = { - { "ov5647" }, + { .name = "ov5647" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov5647_id); diff --git a/drivers/media/i2c/ov7640.c b/drivers/media/i2c/ov7640.c index 9f68d89936eb..0fd90bc67e29 100644 --- a/drivers/media/i2c/ov7640.c +++ b/drivers/media/i2c/ov7640.c @@ -77,7 +77,7 @@ static void ov7640_remove(struct i2c_client *client) } static const struct i2c_device_id ov7640_id[] = { - { "ov7640" }, + { .name = "ov7640" }, { } }; MODULE_DEVICE_TABLE(i2c, ov7640_id); diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 0cb96b6c9990..b6d238ba0d53 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1997,8 +1997,8 @@ static const struct ov7670_devtype ov7675_devdata = { }; static const struct i2c_device_id ov7670_id[] = { - { "ov7670", (kernel_ulong_t)&ov7670_devdata }, - { "ov7675", (kernel_ulong_t)&ov7675_devdata }, + { .name = "ov7670", .driver_data = (kernel_ulong_t)&ov7670_devdata }, + { .name = "ov7675", .driver_data = (kernel_ulong_t)&ov7675_devdata }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov7670_id); diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 062e1023a411..be3ba284ee0b 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -1546,7 +1546,7 @@ static void ov772x_remove(struct i2c_client *client) } static const struct i2c_device_id ov772x_id[] = { - { "ov772x" }, + { .name = "ov772x" }, { } }; MODULE_DEVICE_TABLE(i2c, ov772x_id); diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 632fb80469be..c2e02f191816 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1149,7 +1149,7 @@ static int __maybe_unused ov7740_runtime_resume(struct device *dev) } static const struct i2c_device_id ov7740_id[] = { - { "ov7740" }, + { .name = "ov7740" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov7740_id); diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 2190c52b1433..122f411044ce 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -752,7 +752,7 @@ static void ov9640_remove(struct i2c_client *client) } static const struct i2c_device_id ov9640_id[] = { - { "ov9640" }, + { .name = "ov9640" }, { } }; MODULE_DEVICE_TABLE(i2c, ov9640_id); diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index c94e8fe29f22..5c85db8a4a38 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -1567,8 +1567,8 @@ static void ov965x_remove(struct i2c_client *client) } static const struct i2c_device_id ov965x_id[] = { - { "OV9650" }, - { "OV9652" }, + { .name = "OV9650" }, + { .name = "OV9652" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov965x_id); diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c index e95342d706c3..23352d71a108 100644 --- a/drivers/media/i2c/rj54n1cb0c.c +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -1413,7 +1413,7 @@ static void rj54n1_remove(struct i2c_client *client) } static const struct i2c_device_id rj54n1_id[] = { - { "rj54n1cb0c" }, + { .name = "rj54n1cb0c" }, { } }; MODULE_DEVICE_TABLE(i2c, rj54n1_id); diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index ab31ee2b596b..551387cea521 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1728,7 +1728,7 @@ static void s5c73m3_remove(struct i2c_client *client) } static const struct i2c_device_id s5c73m3_id[] = { - { DRIVER_NAME }, + { .name = DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, s5c73m3_id); diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index d1d00eca8708..378d273055ee 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -2007,7 +2007,7 @@ static void s5k5baf_remove(struct i2c_client *c) } static const struct i2c_device_id s5k5baf_id[] = { - { S5K5BAF_DRIVER_NAME }, + { .name = S5K5BAF_DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, s5k5baf_id); diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index 90ae4121a68a..56bfa3d2604b 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -495,7 +495,7 @@ static void saa6588_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa6588_id[] = { - { "saa6588" }, + { .name = "saa6588" }, { } }; MODULE_DEVICE_TABLE(i2c, saa6588_id); diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index 1c0031ba43b4..c6bf0b0902e8 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -770,7 +770,7 @@ static void saa6752hs_remove(struct i2c_client *client) } static const struct i2c_device_id saa6752hs_id[] = { - { "saa6752hs" }, + { .name = "saa6752hs" }, { } }; MODULE_DEVICE_TABLE(i2c, saa6752hs_id); diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c index 942aeeb40c52..652058b8f766 100644 --- a/drivers/media/i2c/saa7110.c +++ b/drivers/media/i2c/saa7110.c @@ -439,7 +439,7 @@ static void saa7110_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa7110_id[] = { - { "saa7110" }, + { .name = "saa7110" }, { } }; MODULE_DEVICE_TABLE(i2c, saa7110_id); diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 48d6730d9271..7cce90750c93 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -1928,13 +1928,13 @@ static void saa711x_remove(struct i2c_client *client) } static const struct i2c_device_id saa711x_id[] = { - { "saa7115_auto", 1 }, /* autodetect */ - { "saa7111", 0 }, - { "saa7113", 0 }, - { "saa7114", 0 }, - { "saa7115", 0 }, - { "saa7118", 0 }, - { "gm7113c", 0 }, + { .name = "saa7115_auto", .driver_data = 1 }, /* autodetect */ + { .name = "saa7111", .driver_data = 0 }, + { .name = "saa7113", .driver_data = 0 }, + { .name = "saa7114", .driver_data = 0 }, + { .name = "saa7115", .driver_data = 0 }, + { .name = "saa7118", .driver_data = 0 }, + { .name = "gm7113c", .driver_data = 0 }, { } }; MODULE_DEVICE_TABLE(i2c, saa711x_id); diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c index a42a7ffe3768..fdf17f6fae67 100644 --- a/drivers/media/i2c/saa7127.c +++ b/drivers/media/i2c/saa7127.c @@ -797,11 +797,11 @@ static void saa7127_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa7127_id[] = { - { "saa7127_auto", 0 }, /* auto-detection */ - { "saa7126", SAA7127 }, - { "saa7127", SAA7127 }, - { "saa7128", SAA7129 }, - { "saa7129", SAA7129 }, + { .name = "saa7127_auto", .driver_data = 0 }, /* auto-detection */ + { .name = "saa7126", .driver_data = SAA7127 }, + { .name = "saa7127", .driver_data = SAA7127 }, + { .name = "saa7128", .driver_data = SAA7129 }, + { .name = "saa7129", .driver_data = SAA7129 }, { } }; MODULE_DEVICE_TABLE(i2c, saa7127_id); diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c index 713331be947c..0536ceb54650 100644 --- a/drivers/media/i2c/saa717x.c +++ b/drivers/media/i2c/saa717x.c @@ -1334,7 +1334,7 @@ static void saa717x_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa717x_id[] = { - { "saa717x" }, + { .name = "saa717x" }, { } }; MODULE_DEVICE_TABLE(i2c, saa717x_id); diff --git a/drivers/media/i2c/saa7185.c b/drivers/media/i2c/saa7185.c index c04e452a332b..8e5c0eab907d 100644 --- a/drivers/media/i2c/saa7185.c +++ b/drivers/media/i2c/saa7185.c @@ -334,7 +334,7 @@ static void saa7185_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa7185_id[] = { - { "saa7185" }, + { .name = "saa7185" }, { } }; MODULE_DEVICE_TABLE(i2c, saa7185_id); diff --git a/drivers/media/i2c/sony-btf-mpx.c b/drivers/media/i2c/sony-btf-mpx.c index 16072a9f8247..4c87de0fe1f0 100644 --- a/drivers/media/i2c/sony-btf-mpx.c +++ b/drivers/media/i2c/sony-btf-mpx.c @@ -366,7 +366,7 @@ static void sony_btf_mpx_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id sony_btf_mpx_id[] = { - { "sony-btf-mpx" }, + { .name = "sony-btf-mpx" }, { } }; MODULE_DEVICE_TABLE(i2c, sony_btf_mpx_id); diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index a0ca19359c43..fbd38bbfee03 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -2358,8 +2358,8 @@ static void tc358743_remove(struct i2c_client *client) } static const struct i2c_device_id tc358743_id[] = { - { "tc358743" }, - {} + { .name = "tc358743" }, + { } }; MODULE_DEVICE_TABLE(i2c, tc358743_id); diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 5c6dda5338f5..afa1d6f34c9c 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -2274,9 +2274,9 @@ static int tda1997x_set_power(struct tda1997x_state *state, bool on) } static const struct i2c_device_id tda1997x_i2c_id[] = { - {"tda19971", (kernel_ulong_t)&tda1997x_chip_info[TDA19971]}, - {"tda19973", (kernel_ulong_t)&tda1997x_chip_info[TDA19973]}, - { }, + { .name = "tda19971", .driver_data = (kernel_ulong_t)&tda1997x_chip_info[TDA19971] }, + { .name = "tda19973", .driver_data = (kernel_ulong_t)&tda1997x_chip_info[TDA19973] }, + { } }; MODULE_DEVICE_TABLE(i2c, tda1997x_i2c_id); diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c index 76ef0fdddf76..a0b65a595ba8 100644 --- a/drivers/media/i2c/tda7432.c +++ b/drivers/media/i2c/tda7432.c @@ -400,7 +400,7 @@ static void tda7432_remove(struct i2c_client *client) } static const struct i2c_device_id tda7432_id[] = { - { "tda7432" }, + { .name = "tda7432" }, { } }; MODULE_DEVICE_TABLE(i2c, tda7432_id); diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c index e3b266db571f..b34d992777fb 100644 --- a/drivers/media/i2c/tda9840.c +++ b/drivers/media/i2c/tda9840.c @@ -182,7 +182,7 @@ static void tda9840_remove(struct i2c_client *client) } static const struct i2c_device_id tda9840_id[] = { - { "tda9840" }, + { .name = "tda9840" }, { } }; MODULE_DEVICE_TABLE(i2c, tda9840_id); diff --git a/drivers/media/i2c/tea6415c.c b/drivers/media/i2c/tea6415c.c index 0cd2e6c52e20..ea7730a7ee2c 100644 --- a/drivers/media/i2c/tea6415c.c +++ b/drivers/media/i2c/tea6415c.c @@ -141,7 +141,7 @@ static void tea6415c_remove(struct i2c_client *client) } static const struct i2c_device_id tea6415c_id[] = { - { "tea6415c" }, + { .name = "tea6415c" }, { } }; MODULE_DEVICE_TABLE(i2c, tea6415c_id); diff --git a/drivers/media/i2c/tea6420.c b/drivers/media/i2c/tea6420.c index 400883fc0c0f..bebb9a8095db 100644 --- a/drivers/media/i2c/tea6420.c +++ b/drivers/media/i2c/tea6420.c @@ -123,7 +123,7 @@ static void tea6420_remove(struct i2c_client *client) } static const struct i2c_device_id tea6420_id[] = { - { "tea6420" }, + { .name = "tea6420" }, { } }; MODULE_DEVICE_TABLE(i2c, tea6420_id); diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c index ff268ebeb4d9..fa5b9c884c1d 100644 --- a/drivers/media/i2c/ths7303.c +++ b/drivers/media/i2c/ths7303.c @@ -369,9 +369,9 @@ static void ths7303_remove(struct i2c_client *client) } static const struct i2c_device_id ths7303_id[] = { - { "ths7303" }, - { "ths7353" }, - {} + { .name = "ths7303" }, + { .name = "ths7353" }, + { } }; MODULE_DEVICE_TABLE(i2c, ths7303_id); diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 686f10641c7a..808ef16ec3b3 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -487,8 +487,8 @@ static void ths8200_remove(struct i2c_client *client) } static const struct i2c_device_id ths8200_id[] = { - { "ths8200" }, - {} + { .name = "ths8200" }, + { } }; MODULE_DEVICE_TABLE(i2c, ths8200_id); diff --git a/drivers/media/i2c/tlv320aic23b.c b/drivers/media/i2c/tlv320aic23b.c index 6f6bc5236565..7deeef317714 100644 --- a/drivers/media/i2c/tlv320aic23b.c +++ b/drivers/media/i2c/tlv320aic23b.c @@ -188,7 +188,7 @@ static void tlv320aic23b_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id tlv320aic23b_id[] = { - { "tlv320aic23b" }, + { .name = "tlv320aic23b" }, { } }; MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id); diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index 6267e9ad39c0..f136310b899d 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -2086,7 +2086,7 @@ static void tvaudio_remove(struct i2c_client *client) detect which device is present. So rather than listing all supported devices here, we pretend to support a single, fake device type. */ static const struct i2c_device_id tvaudio_id[] = { - { "tvaudio" }, + { .name = "tvaudio" }, { } }; MODULE_DEVICE_TABLE(i2c, tvaudio_id); diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index 7af8f37646d6..376eecb0b673 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -1129,10 +1129,10 @@ static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { * driver_data - Driver data */ static const struct i2c_device_id tvp514x_id[] = { - {"tvp5146", (kernel_ulong_t)tvp5146_init_reg_seq }, - {"tvp5146m2", (kernel_ulong_t)tvp514xm_init_reg_seq }, - {"tvp5147", (kernel_ulong_t)tvp5147_init_reg_seq }, - {"tvp5147m1", (kernel_ulong_t)tvp514xm_init_reg_seq }, + { .name = "tvp5146", .driver_data = (kernel_ulong_t)tvp5146_init_reg_seq }, + { .name = "tvp5146m2", .driver_data = (kernel_ulong_t)tvp514xm_init_reg_seq }, + { .name = "tvp5147", .driver_data = (kernel_ulong_t)tvp5147_init_reg_seq }, + { .name = "tvp5147m1", .driver_data = (kernel_ulong_t)tvp514xm_init_reg_seq }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, tvp514x_id); diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index e3675c744d9e..9c204f38935d 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -2265,7 +2265,7 @@ static const struct dev_pm_ops tvp5150_pm_ops = { }; static const struct i2c_device_id tvp5150_id[] = { - { "tvp5150" }, + { .name = "tvp5150" }, { } }; MODULE_DEVICE_TABLE(i2c, tvp5150_id); diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index c09a5bd71fd0..3979ccde5a95 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -1070,7 +1070,7 @@ static void tvp7002_remove(struct i2c_client *c) /* I2C Device ID table */ static const struct i2c_device_id tvp7002_id[] = { - { "tvp7002" }, + { .name = "tvp7002" }, { } }; MODULE_DEVICE_TABLE(i2c, tvp7002_id); diff --git a/drivers/media/i2c/tw2804.c b/drivers/media/i2c/tw2804.c index 3d154f4fb5f9..713e078ff3da 100644 --- a/drivers/media/i2c/tw2804.c +++ b/drivers/media/i2c/tw2804.c @@ -414,7 +414,7 @@ static void tw2804_remove(struct i2c_client *client) } static const struct i2c_device_id tw2804_id[] = { - { "tw2804" }, + { .name = "tw2804" }, { } }; MODULE_DEVICE_TABLE(i2c, tw2804_id); diff --git a/drivers/media/i2c/tw9900.c b/drivers/media/i2c/tw9900.c index 53efdeaed1db..617fcf7f0b45 100644 --- a/drivers/media/i2c/tw9900.c +++ b/drivers/media/i2c/tw9900.c @@ -753,7 +753,7 @@ static const struct dev_pm_ops tw9900_pm_ops = { }; static const struct i2c_device_id tw9900_id[] = { - { "tw9900" }, + { .name = "tw9900" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9900_id); diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c index c3eafd5d5dc8..db063b885b09 100644 --- a/drivers/media/i2c/tw9903.c +++ b/drivers/media/i2c/tw9903.c @@ -246,7 +246,7 @@ static void tw9903_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id tw9903_id[] = { - { "tw9903" }, + { .name = "tw9903" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9903_id); diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c index 0ab43fe42d7f..079e2f49f38f 100644 --- a/drivers/media/i2c/tw9906.c +++ b/drivers/media/i2c/tw9906.c @@ -214,7 +214,7 @@ static void tw9906_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id tw9906_id[] = { - { "tw9906" }, + { .name = "tw9906" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9906_id); diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index f3e400304e04..872207e688bf 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -996,7 +996,7 @@ static void tw9910_remove(struct i2c_client *client) } static const struct i2c_device_id tw9910_id[] = { - { "tw9910" }, + { .name = "tw9910" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9910_id); diff --git a/drivers/media/i2c/uda1342.c b/drivers/media/i2c/uda1342.c index 2e4540ee2df2..437726788ba0 100644 --- a/drivers/media/i2c/uda1342.c +++ b/drivers/media/i2c/uda1342.c @@ -79,7 +79,7 @@ static void uda1342_remove(struct i2c_client *client) } static const struct i2c_device_id uda1342_id[] = { - { "uda1342" }, + { .name = "uda1342" }, { } }; MODULE_DEVICE_TABLE(i2c, uda1342_id); diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c index a178af46e695..670118c16872 100644 --- a/drivers/media/i2c/upd64031a.c +++ b/drivers/media/i2c/upd64031a.c @@ -219,7 +219,7 @@ static void upd64031a_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id upd64031a_id[] = { - { "upd64031a" }, + { .name = "upd64031a" }, { } }; MODULE_DEVICE_TABLE(i2c, upd64031a_id); diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c index 5421dc5e32c9..e610dffa9e10 100644 --- a/drivers/media/i2c/upd64083.c +++ b/drivers/media/i2c/upd64083.c @@ -190,7 +190,7 @@ static void upd64083_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id upd64083_id[] = { - { "upd64083" }, + { .name = "upd64083" }, { } }; MODULE_DEVICE_TABLE(i2c, upd64083_id); diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 2b23914717d1..56b99eea54a1 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -922,9 +922,9 @@ static const struct dev_pm_ops video_i2c_pm_ops = { }; static const struct i2c_device_id video_i2c_id_table[] = { - { "amg88xx", (kernel_ulong_t)&video_i2c_chip[AMG88XX] }, - { "mlx90640", (kernel_ulong_t)&video_i2c_chip[MLX90640] }, - {} + { .name = "amg88xx", .driver_data = (kernel_ulong_t)&video_i2c_chip[AMG88XX] }, + { .name = "mlx90640", .driver_data = (kernel_ulong_t)&video_i2c_chip[MLX90640] }, + { } }; MODULE_DEVICE_TABLE(i2c, video_i2c_id_table); diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c index df21950be24f..21fcfdd4c163 100644 --- a/drivers/media/i2c/vp27smpx.c +++ b/drivers/media/i2c/vp27smpx.c @@ -172,7 +172,7 @@ static void vp27smpx_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id vp27smpx_id[] = { - { "vp27smpx" }, + { .name = "vp27smpx" }, { } }; MODULE_DEVICE_TABLE(i2c, vp27smpx_id); diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c index 5f1a22284168..29bcbb5a5fbb 100644 --- a/drivers/media/i2c/vpx3220.c +++ b/drivers/media/i2c/vpx3220.c @@ -535,9 +535,9 @@ static void vpx3220_remove(struct i2c_client *client) } static const struct i2c_device_id vpx3220_id[] = { - { "vpx3220a" }, - { "vpx3216b" }, - { "vpx3214c" }, + { .name = "vpx3220a" }, + { .name = "vpx3216b" }, + { .name = "vpx3214c" }, { } }; MODULE_DEVICE_TABLE(i2c, vpx3220_id); diff --git a/drivers/media/i2c/wm8739.c b/drivers/media/i2c/wm8739.c index 72eb10339d06..db62bafb447c 100644 --- a/drivers/media/i2c/wm8739.c +++ b/drivers/media/i2c/wm8739.c @@ -243,7 +243,7 @@ static void wm8739_remove(struct i2c_client *client) } static const struct i2c_device_id wm8739_id[] = { - { "wm8739" }, + { .name = "wm8739" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8739_id); diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c index 56778d3bc28a..5db197bb6fda 100644 --- a/drivers/media/i2c/wm8775.c +++ b/drivers/media/i2c/wm8775.c @@ -289,7 +289,7 @@ static void wm8775_remove(struct i2c_client *client) } static const struct i2c_device_id wm8775_id[] = { - { "wm8775" }, + { .name = "wm8775" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8775_id); diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index 156cca2866aa..d9547f625e30 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -502,7 +502,7 @@ static void tea5764_i2c_remove(struct i2c_client *client) /* I2C subsystem interface */ static const struct i2c_device_id tea5764_id[] = { - { "radio-tea5764" }, + { .name = "radio-tea5764" }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(i2c, tea5764_id); diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c index 9572a866defb..bd8bd295a9ec 100644 --- a/drivers/media/radio/saa7706h.c +++ b/drivers/media/radio/saa7706h.c @@ -395,8 +395,8 @@ static void saa7706h_remove(struct i2c_client *client) } static const struct i2c_device_id saa7706h_id[] = { - { DRIVER_NAME }, - {} + { .name = DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, saa7706h_id); diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index 3932a449a1b1..a1e570af9c59 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -28,7 +28,7 @@ /* I2C Device ID List */ static const struct i2c_device_id si470x_i2c_id[] = { /* Generic Entry */ - { "si470x" }, + { .name = "si470x" }, /* Terminating entry */ { } }; diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index e71272c6de37..0c0354566b0a 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -1639,7 +1639,7 @@ static void si4713_remove(struct i2c_client *client) /* si4713_i2c_driver - i2c driver interface */ static const struct i2c_device_id si4713_id[] = { - { "si4713" }, + { .name = "si4713" }, { } }; MODULE_DEVICE_TABLE(i2c, si4713_id); diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c index 3a6d7926e856..2596d7f4f3f1 100644 --- a/drivers/media/radio/tef6862.c +++ b/drivers/media/radio/tef6862.c @@ -173,8 +173,8 @@ static void tef6862_remove(struct i2c_client *client) } static const struct i2c_device_id tef6862_id[] = { - { DRIVER_NAME }, - {} + { .name = DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, tef6862_id); diff --git a/drivers/media/test-drivers/vidtv/vidtv_demod.c b/drivers/media/test-drivers/vidtv/vidtv_demod.c index c382e9e94c32..6e5fe402976b 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_demod.c +++ b/drivers/media/test-drivers/vidtv/vidtv_demod.c @@ -407,8 +407,8 @@ static const struct dvb_frontend_ops vidtv_demod_ops = { }; static const struct i2c_device_id vidtv_demod_i2c_id_table[] = { - { "dvb_vidtv_demod" }, - {} + { .name = "dvb_vidtv_demod" }, + { } }; MODULE_DEVICE_TABLE(i2c, vidtv_demod_i2c_id_table); diff --git a/drivers/media/test-drivers/vidtv/vidtv_tuner.c b/drivers/media/test-drivers/vidtv/vidtv_tuner.c index ee55df4029bc..bd50b86e927c 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_tuner.c +++ b/drivers/media/test-drivers/vidtv/vidtv_tuner.c @@ -385,8 +385,8 @@ static const struct dvb_tuner_ops vidtv_tuner_ops = { }; static const struct i2c_device_id vidtv_tuner_i2c_id_table[] = { - { "dvb_vidtv_tuner" }, - {} + { .name = "dvb_vidtv_tuner" }, + { } }; MODULE_DEVICE_TABLE(i2c, vidtv_tuner_i2c_id_table); diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index b83f37a77224..94abb3715401 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -719,8 +719,8 @@ static void e4000_remove(struct i2c_client *client) } static const struct i2c_device_id e4000_id_table[] = { - { "e4000" }, - {} + { .name = "e4000" }, + { } }; MODULE_DEVICE_TABLE(i2c, e4000_id_table); diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c index 75087d9b224f..b76fa1320f5f 100644 --- a/drivers/media/tuners/fc2580.c +++ b/drivers/media/tuners/fc2580.c @@ -600,8 +600,8 @@ static void fc2580_remove(struct i2c_client *client) } static const struct i2c_device_id fc2580_id_table[] = { - { "fc2580" }, - {} + { .name = "fc2580" }, + { } }; MODULE_DEVICE_TABLE(i2c, fc2580_id_table); diff --git a/drivers/media/tuners/m88rs6000t.c b/drivers/media/tuners/m88rs6000t.c index 0a724cdf0f6d..1addb3d229cf 100644 --- a/drivers/media/tuners/m88rs6000t.c +++ b/drivers/media/tuners/m88rs6000t.c @@ -709,8 +709,8 @@ static void m88rs6000t_remove(struct i2c_client *client) } static const struct i2c_device_id m88rs6000t_id[] = { - { "m88rs6000t" }, - {} + { .name = "m88rs6000t" }, + { } }; MODULE_DEVICE_TABLE(i2c, m88rs6000t_id); diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c index ef3196e6bd30..c57233c7441a 100644 --- a/drivers/media/tuners/mt2060.c +++ b/drivers/media/tuners/mt2060.c @@ -514,8 +514,8 @@ static void mt2060_remove(struct i2c_client *client) } static const struct i2c_device_id mt2060_id_table[] = { - { "mt2060" }, - {} + { .name = "mt2060" }, + { } }; MODULE_DEVICE_TABLE(i2c, mt2060_id_table); diff --git a/drivers/media/tuners/mxl301rf.c b/drivers/media/tuners/mxl301rf.c index cfc78891ce03..1d84456cb19b 100644 --- a/drivers/media/tuners/mxl301rf.c +++ b/drivers/media/tuners/mxl301rf.c @@ -317,8 +317,8 @@ static void mxl301rf_remove(struct i2c_client *client) static const struct i2c_device_id mxl301rf_id[] = { - { "mxl301rf" }, - {} + { .name = "mxl301rf" }, + { } }; MODULE_DEVICE_TABLE(i2c, mxl301rf_id); diff --git a/drivers/media/tuners/qm1d1b0004.c b/drivers/media/tuners/qm1d1b0004.c index 07ad84f42c9f..59d98681e674 100644 --- a/drivers/media/tuners/qm1d1b0004.c +++ b/drivers/media/tuners/qm1d1b0004.c @@ -243,8 +243,8 @@ static void qm1d1b0004_remove(struct i2c_client *client) static const struct i2c_device_id qm1d1b0004_id[] = { - { "qm1d1b0004" }, - {} + { .name = "qm1d1b0004" }, + { } }; MODULE_DEVICE_TABLE(i2c, qm1d1b0004_id); diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c index db60562ad698..2d19cfdb67b9 100644 --- a/drivers/media/tuners/qm1d1c0042.c +++ b/drivers/media/tuners/qm1d1c0042.c @@ -434,8 +434,8 @@ static void qm1d1c0042_remove(struct i2c_client *client) static const struct i2c_device_id qm1d1c0042_id[] = { - { "qm1d1c0042" }, - {} + { .name = "qm1d1c0042" }, + { } }; MODULE_DEVICE_TABLE(i2c, qm1d1c0042_id); diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 4d67e347c22f..d517a91e6fbc 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -1097,11 +1097,11 @@ static void si2157_remove(struct i2c_client *client) * all SiLabs TER tuners, as the driver should auto-detect it. */ static const struct i2c_device_id si2157_id_table[] = { - {"si2157", SI2157}, - {"si2146", SI2146}, - {"si2141", SI2141}, - {"si2177", SI2177}, - {} + { .name = "si2157", .driver_data = SI2157 }, + { .name = "si2146", .driver_data = SI2146 }, + { .name = "si2141", .driver_data = SI2141 }, + { .name = "si2177", .driver_data = SI2177 }, + { } }; MODULE_DEVICE_TABLE(i2c, si2157_id_table); diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c index 5f583c010408..18d1850691fb 100644 --- a/drivers/media/tuners/tda18212.c +++ b/drivers/media/tuners/tda18212.c @@ -254,8 +254,8 @@ static void tda18212_remove(struct i2c_client *client) } static const struct i2c_device_id tda18212_id[] = { - { "tda18212" }, - {} + { .name = "tda18212" }, + { } }; MODULE_DEVICE_TABLE(i2c, tda18212_id); diff --git a/drivers/media/tuners/tda18250.c b/drivers/media/tuners/tda18250.c index caaf5e0d0c9b..7bb945ba0989 100644 --- a/drivers/media/tuners/tda18250.c +++ b/drivers/media/tuners/tda18250.c @@ -868,8 +868,8 @@ static void tda18250_remove(struct i2c_client *client) } static const struct i2c_device_id tda18250_id_table[] = { - { "tda18250" }, - {} + { .name = "tda18250" }, + { } }; MODULE_DEVICE_TABLE(i2c, tda18250_id_table); diff --git a/drivers/media/tuners/tua9001.c b/drivers/media/tuners/tua9001.c index c0aed1b441e2..fcdbf1a0c1d8 100644 --- a/drivers/media/tuners/tua9001.c +++ b/drivers/media/tuners/tua9001.c @@ -245,8 +245,8 @@ static void tua9001_remove(struct i2c_client *client) } static const struct i2c_device_id tua9001_id_table[] = { - { "tua9001" }, - {} + { .name = "tua9001" }, + { } }; MODULE_DEVICE_TABLE(i2c, tua9001_id_table); diff --git a/drivers/media/usb/go7007/s2250-board.c b/drivers/media/usb/go7007/s2250-board.c index 567f851d5896..0901d79e827d 100644 --- a/drivers/media/usb/go7007/s2250-board.c +++ b/drivers/media/usb/go7007/s2250-board.c @@ -611,7 +611,7 @@ static void s2250_remove(struct i2c_client *client) } static const struct i2c_device_id s2250_id[] = { - { "s2250" }, + { .name = "s2250" }, { } }; MODULE_DEVICE_TABLE(i2c, s2250_id); diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index 004ec4d7beea..1e130e6f903f 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -1401,7 +1401,7 @@ static const struct dev_pm_ops tuner_pm_ops = { }; static const struct i2c_device_id tuner_id[] = { - { "tuner", }, /* autodetect */ + { .name = "tuner" }, /* autodetect */ { } }; MODULE_DEVICE_TABLE(i2c, tuner_id); From 0812f34bf5dd3e1df495bf6f2c5fe4d140c10d37 Mon Sep 17 00:00:00 2001 From: Tomasz Unger Date: Sun, 22 Feb 2026 14:16:37 +0100 Subject: [PATCH 211/394] staging: media: atomisp: Fix spelling mistakes in comments Fix various spelling mistakes found by codespell: - aviod => avoid - corrent => correct - stablization => stabilization - addtional => additional - facor => factor - steams => streams Signed-off-by: Tomasz Unger Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_cmd.c | 8 ++++---- drivers/staging/media/atomisp/pci/atomisp_cmd.h | 2 +- drivers/staging/media/atomisp/pci/atomisp_compat_css20.c | 8 ++++---- drivers/staging/media/atomisp/pci/atomisp_compat_css20.h | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index fec369575d88..5e5f3fe1ba7d 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -811,7 +811,7 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, /* New global dvs 6axis config should be blocked * here if there's a buffer with per-frame parameters * pending in CSS frame buffer queue. - * This is to aviod zooming vibration since global + * This is to avoid zooming vibration since global * parameters take effect immediately while * per-frame parameters are taken after previous * buffers in CSS got processed. @@ -974,7 +974,7 @@ irqreturn_t atomisp_isr_thread(int irq, void *isp_ptr) * to a FIFO, then process the event in the FIFO. * This will not have issue in single stream mode, but it do have some * issue in multiple stream case. The issue is that - * ia_css_pipe_dequeue_buffer() will not return the corrent buffer in + * ia_css_pipe_dequeue_buffer() will not return the correct buffer in * a specific pipe. * * This is due to ia_css_pipe_dequeue_buffer() does not take the @@ -1575,7 +1575,7 @@ int atomisp_set_dis_vector(struct atomisp_sub_device *asd, } /* - * Function to set/get image stablization statistics + * Function to set/get image stabilization statistics */ int atomisp_get_dis_stat(struct atomisp_sub_device *asd, struct atomisp_dis_statistics *stats) @@ -3232,7 +3232,7 @@ int atomisp_bad_pixel_param(struct atomisp_sub_device *asd, int flag, } /* - * Function to enable/disable video image stablization + * Function to enable/disable video image stabilization */ int atomisp_video_stable(struct atomisp_sub_device *asd, int flag, __s32 *value) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.h b/drivers/staging/media/atomisp/pci/atomisp_cmd.h index 82199dc9284e..d3d1f2574e77 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.h +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.h @@ -153,7 +153,7 @@ int atomisp_bad_pixel(struct atomisp_sub_device *asd, int flag, int atomisp_bad_pixel_param(struct atomisp_sub_device *asd, int flag, struct atomisp_dp_config *config); -/* Function to enable/disable video image stablization */ +/* Function to enable/disable video image stabilization */ int atomisp_video_stable(struct atomisp_sub_device *asd, int flag, __s32 *value); diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c index be5f37f4a6fd..c30501a36f86 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -1957,7 +1957,7 @@ static void __configure_capture_pp_input(struct atomisp_sub_device *asd, /* * For CSS2.1, preview pipe could support bayer downscaling, yuv decimation and - * yuv downscaling, which needs addtional configurations. + * yuv downscaling, which needs additional configurations. */ static void __configure_preview_pp_input(struct atomisp_sub_device *asd, unsigned int width, unsigned int height, @@ -2044,7 +2044,7 @@ static void __configure_preview_pp_input(struct atomisp_sub_device *asd, } } /* - * calculate YUV Decimation, YUV downscaling facor: + * calculate YUV Decimation, YUV downscaling factor: * YUV Downscaling factor must not exceed 2. * YUV Decimation factor could be 2, 4. */ @@ -2085,7 +2085,7 @@ static void __configure_preview_pp_input(struct atomisp_sub_device *asd, /* * For CSS2.1, offline video pipe could support bayer decimation, and - * yuv downscaling, which needs addtional configurations. + * yuv downscaling, which needs additional configurations. */ static void __configure_video_pp_input(struct atomisp_sub_device *asd, unsigned int width, unsigned int height, @@ -3002,7 +3002,7 @@ int atomisp_css_get_zoom_factor(struct atomisp_sub_device *asd, } /* - * Function to set/get image stablization statistics + * Function to set/get image stabilization statistics */ int atomisp_css_get_dis_stat(struct atomisp_sub_device *asd, struct atomisp_dis_statistics *stats) diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h index 75781807544a..5188df4f469c 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h @@ -40,7 +40,7 @@ enum atomisp_css_stream_state { }; /* - * Sensor of external ISP can send multiple steams with different mipi data + * Sensor of external ISP can send multiple streams with different mipi data * type in the same virtual channel. This information needs to come from the * sensor or external ISP */ From c565afd284e5b5f04e24b20018958ed7b9bc55a3 Mon Sep 17 00:00:00 2001 From: Tomasz Unger Date: Thu, 26 Feb 2026 12:16:53 +0100 Subject: [PATCH 212/394] staging: media: atomisp: replace sprintf() with strscpy() Auditing calls to sprintf(). This code is fine because we are copying 9 characters into a 52 character buffer. But it would be cleaner to use strscpy() instead. Additionally, the 2-argument version of strscpy() checks at compile time that dst is an array, not just a pointer. This is the only sprintf() call in the whole driver. Signed-off-by: Tomasz Unger Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_subdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c index 3d56ca83ecb7..cef44ec9ebde 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c @@ -808,7 +808,7 @@ static int isp_subdev_init_entities(struct atomisp_sub_device *asd) int ret; v4l2_subdev_init(sd, &isp_subdev_v4l2_ops); - sprintf(sd->name, "Atom ISP"); + strscpy(sd->name, "Atom ISP"); v4l2_set_subdevdata(sd, asd); sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; From f3c450f5f13221798de7acbe667d55fbadc63b94 Mon Sep 17 00:00:00 2001 From: Matt Wardle Date: Tue, 10 Feb 2026 09:23:58 +0000 Subject: [PATCH 213/394] staging: media: atomisp: Remove braces for single statement blocks Fix checkpatch.pl warnings: WARNING: braces {} are not necessary for single statement blocks WARNING: braces {} are not necessary for any arm of this statement Signed-off-by: Matt Wardle Signed-off-by: Sakari Ailus --- .../base/circbuf/interface/ia_css_circbuf.h | 3 +- .../circbuf/interface/ia_css_circbuf_desc.h | 3 +- .../atomisp/pci/base/refcount/src/refcount.c | 3 +- .../pci/camera/pipe/src/pipe_stagedesc.c | 9 ++---- .../atomisp/pci/camera/pipe/src/pipe_util.c | 3 +- .../host/isys_stream2mmio_private.h | 3 +- .../host/event_fifo_private.h | 3 +- .../host/input_formatter.c | 3 +- .../hive_isp_css_common/host/input_system.c | 14 ++++----- .../pci/hive_isp_css_common/host/irq.c | 30 ++++++++----------- .../pci/hive_isp_css_common/host/mmu.c | 3 +- .../pci/hive_isp_css_common/host/vmem.c | 4 +-- .../isp/kernels/anr/anr_2/ia_css_anr2.host.c | 3 +- .../pci/isp/kernels/bnlm/ia_css_bnlm.host.c | 6 ++-- .../isp/kernels/ctc/ctc2/ia_css_ctc2.host.c | 7 ++--- .../isp/kernels/eed1_8/ia_css_eed1_8.host.c | 13 ++++---- .../isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c | 3 +- .../kernels/sdis/sdis_1.0/ia_css_sdis.host.c | 6 ++-- .../atomisp/pci/runtime/binary/src/binary.c | 3 +- .../pci/runtime/debug/src/ia_css_debug.c | 6 ++-- .../pci/runtime/isys/src/virtual_isys.c | 3 +- .../pci/runtime/pipeline/src/pipeline.c | 3 +- .../staging/media/atomisp/pci/sh_css_params.c | 12 +++----- 23 files changed, 53 insertions(+), 93 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h index 86300991d30e..148f9f6febfb 100644 --- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h +++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h @@ -146,9 +146,8 @@ static inline uint8_t ia_css_circbuf_get_pos_at_offset( OP___assert(cb->desc->size > 0); /* step 1: adjudst the offset */ - while (offset < 0) { + while (offset < 0) offset += cb->desc->size; - } /* step 2: shift and round by the upper limit */ dest = OP_std_modadd(base, offset, cb->desc->size); diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h index 5645a7bf493c..3d89626a0733 100644 --- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h +++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h @@ -84,9 +84,8 @@ static inline uint8_t ia_css_circbuf_desc_get_pos_at_offset( OP___assert(cb_desc->size > 0); /* step 1: adjust the offset */ - while (offset < 0) { + while (offset < 0) offset += cb_desc->size; - } /* step 2: shift and round by the upper limit */ dest = OP_std_modadd(base, offset, cb_desc->size); diff --git a/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c b/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c index 58e4e3173b40..4a8675d0129a 100644 --- a/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c +++ b/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c @@ -238,9 +238,8 @@ void ia_css_refcount_clear(s32 id, clear_func clear_func_ptr) hmm_free(entry->data); } - if (entry->count != 0) { + if (entry->count != 0) IA_CSS_WARNING("Ref count for entry %x is not zero!", entry->id); - } assert(entry->count == 0); diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c index a9f736398f50..fa2f8ed5b053 100644 --- a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c +++ b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c @@ -32,9 +32,8 @@ void ia_css_pipe_get_generic_stage_desc( stage_desc->max_input_width = 0; stage_desc->mode = binary->info->sp.pipeline.mode; stage_desc->in_frame = in_frame; - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) stage_desc->out_frame[i] = out_frame[i]; - } stage_desc->vf_frame = vf_frame; ERR: IA_CSS_LEAVE_PRIVATE(""); @@ -59,9 +58,8 @@ void ia_css_pipe_get_firmwares_stage_desc( stage_desc->max_input_width = 0; stage_desc->mode = mode; stage_desc->in_frame = in_frame; - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) stage_desc->out_frame[i] = out_frame[i]; - } stage_desc->vf_frame = vf_frame; } @@ -82,8 +80,7 @@ void ia_css_pipe_get_sp_func_stage_desc( stage_desc->mode = (unsigned int)-1; stage_desc->in_frame = NULL; stage_desc->out_frame[0] = out_frame; - for (i = 1; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 1; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) stage_desc->out_frame[i] = NULL; - } stage_desc->vf_frame = NULL; } diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c index c7c42b472cc7..6cb3ecbd7297 100644 --- a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c +++ b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c @@ -26,9 +26,8 @@ void ia_css_pipe_util_create_output_frames( unsigned int i; assert(frames); - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) frames[i] = NULL; - } } void ia_css_pipe_util_set_output_frames( diff --git a/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h b/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h index 3210dd6bf9ca..8e295cd78129 100644 --- a/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h +++ b/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h @@ -41,9 +41,8 @@ STORAGE_CLASS_STREAM2MMIO_C void stream2mmio_get_state( * Get the values of the register-set per * stream2mmio-controller sids. */ - for (i = STREAM2MMIO_SID0_ID; i < N_STREAM2MMIO_SID_PROCS[ID]; i++) { + for (i = STREAM2MMIO_SID0_ID; i < N_STREAM2MMIO_SID_PROCS[ID]; i++) stream2mmio_get_sid_state(ID, i, &state->sid_state[i]); - } } /** diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h index 439c69444942..e77d7cf61356 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h @@ -26,9 +26,8 @@ STORAGE_CLASS_EVENT_C void event_wait_for(const event_ID_t ID) STORAGE_CLASS_EVENT_C void cnd_event_wait_for(const event_ID_t ID, const bool cnd) { - if (cnd) { + if (cnd) event_wait_for(ID); - } } STORAGE_CLASS_EVENT_C hrt_data event_receive_token(const event_ID_t ID) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c index 40b3f1e48c56..c353461dcdf2 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c @@ -62,9 +62,8 @@ void input_formatter_rst( * WICH USES THE STREAM2MEMRY BLOCK. * MUST BE FIXED PROPERLY */ - if (!HIVE_IF_BIN_COPY[ID]) { + if (!HIVE_IF_BIN_COPY[ID]) input_formatter_reg_store(ID, addr, rst); - } return; } diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c index 9f1199c4761c..68b0ad27615d 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c @@ -138,11 +138,10 @@ void receiver_port_enable( hrt_data reg = receiver_port_reg_load(ID, port_ID, _HRT_CSS_RECEIVER_DEVICE_READY_REG_IDX); - if (cnd) { + if (cnd) reg |= 0x01; - } else { + else reg &= ~0x01; - } receiver_port_reg_store(ID, port_ID, _HRT_CSS_RECEIVER_DEVICE_READY_REG_IDX, reg); @@ -194,9 +193,8 @@ static void receiver_rst( assert(ID < N_RX_ID); // Disable all ports. - for (port_id = MIPI_PORT0_ID; port_id < N_MIPI_PORT_ID; port_id++) { + for (port_id = MIPI_PORT0_ID; port_id < N_MIPI_PORT_ID; port_id++) receiver_port_enable(ID, port_id, false); - } // AM: Additional actions for stopping receiver? } @@ -830,15 +828,13 @@ input_system_err_t input_system_configuration_commit(void) // The last configuration step is to configure the input buffer. input_system_err_t error = input_buffer_configuration(); - if (error != INPUT_SYSTEM_ERR_NO_ERROR) { + if (error != INPUT_SYSTEM_ERR_NO_ERROR) return error; - } // Translate the whole configuration into registers. error = configuration_to_registers(); - if (error != INPUT_SYSTEM_ERR_NO_ERROR) { + if (error != INPUT_SYSTEM_ERR_NO_ERROR) return error; - } // Translate the whole configuration into ctrl commands etc. diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c index b66560bca625..61dea386361a 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c @@ -55,9 +55,8 @@ void irq_clear_all( assert(ID < N_IRQ_ID); assert(IRQ_N_CHANNEL[ID] <= HRT_DATA_WIDTH); - if (IRQ_N_CHANNEL[ID] < HRT_DATA_WIDTH) { + if (IRQ_N_CHANNEL[ID] < HRT_DATA_WIDTH) mask = ~((~(hrt_data)0) >> IRQ_N_CHANNEL[ID]); - } irq_reg_store(ID, _HRT_IRQ_CONTROLLER_CLEAR_REG_IDX, mask); @@ -115,9 +114,9 @@ void irq_enable_pulse( { unsigned int edge_out = 0x0; - if (pulse) { + if (pulse) edge_out = 0xffffffff; - } + /* output is given as edge, not pulse */ irq_reg_store(ID, _HRT_IRQ_CONTROLLER_EDGE_NOT_PULSE_REG_IDX, edge_out); @@ -259,9 +258,9 @@ void virq_clear_all(void) { irq_ID_t irq_id; - for (irq_id = (irq_ID_t)0; irq_id < N_IRQ_ID; irq_id++) { + for (irq_id = (irq_ID_t)0; irq_id < N_IRQ_ID; irq_id++) irq_clear_all(irq_id); - } + return; } @@ -301,9 +300,9 @@ void virq_clear_info(struct virq_info *irq_info) assert(irq_info); - for (ID = (irq_ID_t)0 ; ID < N_IRQ_ID; ID++) { + for (ID = (irq_ID_t)0 ; ID < N_IRQ_ID; ID++) irq_info->irq_status_reg[ID] = 0; - } + return; } @@ -324,20 +323,17 @@ enum hrt_isp_css_irq_status virq_get_channel_id( break; } - if (idx == IRQ_N_CHANNEL[IRQ0_ID]) { + if (idx == IRQ_N_CHANNEL[IRQ0_ID]) return hrt_isp_css_irq_status_error; - } /* Check whether there are more bits set on device 0 */ - if (irq_status != (1U << idx)) { + if (irq_status != (1U << idx)) status = hrt_isp_css_irq_status_more_irqs; - } /* Check whether we have an IRQ on one of the nested devices */ for (ID = N_IRQ_ID - 1 ; ID > (irq_ID_t)0; ID--) { - if (IRQ_NESTING_ID[ID] == (enum virq_id)idx) { + if (IRQ_NESTING_ID[ID] == (enum virq_id)idx) break; - } } /* If we have a nested IRQ, load that state, discard the device 0 state */ @@ -350,9 +346,8 @@ enum hrt_isp_css_irq_status virq_get_channel_id( break; } - if (idx == IRQ_N_CHANNEL[ID]) { + if (idx == IRQ_N_CHANNEL[ID]) return hrt_isp_css_irq_status_error; - } /* Alternatively check whether there are more bits set on this device */ if (irq_status != (1U << idx)) { @@ -408,9 +403,8 @@ static inline irq_ID_t virq_get_irq_id( assert(channel_ID); for (ID = (irq_ID_t)0 ; ID < N_IRQ_ID; ID++) { - if (irq_ID < IRQ_N_ID_OFFSET[ID + 1]) { + if (irq_ID < IRQ_N_ID_OFFSET[ID + 1]) break; - } } *channel_ID = (unsigned int)irq_ID - IRQ_N_ID_OFFSET[ID]; diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c index 064e88a5e064..70d118fe1e70 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c @@ -32,7 +32,6 @@ void mmu_invalidate_cache_all(void) { mmu_ID_t mmu_id; - for (mmu_id = (mmu_ID_t)0; mmu_id < N_MMU_ID; mmu_id++) { + for (mmu_id = (mmu_ID_t)0; mmu_id < N_MMU_ID; mmu_id++) mmu_invalidate_cache(mmu_id); - } } diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c index 722b684fbc37..1c5c50406633 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c @@ -168,9 +168,9 @@ static void store_vector( //load_vector (&v[1][0], &to[ISP_NWAY]); /* Fetch the next vector, since it will be overwritten. */ hive_uedge *data = (hive_uedge *)v; - for (i = 0; i < ISP_NWAY; i++) { + for (i = 0; i < ISP_NWAY; i++) hive_sim_wide_pack(data, (hive_wide)&from[i], ISP_VEC_ELEMBITS, i); - } + assert(ISP_BAMEM_BASE[ID] != (hrt_address) - 1); #if !defined(HRT_MEMORY_ACCESS) ia_css_device_store(ISP_BAMEM_BASE[ID] + (unsigned long)to, &v, size); diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c index 09599884bdae..08af8a848fbf 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c @@ -22,9 +22,8 @@ ia_css_anr2_vmem_encode( for (i = 0; i < ANR_PARAM_SIZE; i++) { unsigned int j; - for (j = 0; j < ISP_VEC_NELEMS; j++) { + for (j = 0; j < ISP_VEC_NELEMS; j++) to->data[i][j] = from->data[i * ISP_VEC_NELEMS + j]; - } } } diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c index cd867937ee13..fcd7d7e2afe8 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c @@ -125,14 +125,12 @@ ia_css_bnlm_vmem_encode( bnlm_lut_encode(&to->div_lut, div_lut_nearests, div_lut_slopes, BNLM_DIV_LUT_SIZE); memset(to->div_lut_intercepts, 0, sizeof(to->div_lut_intercepts)); - for (i = 0; i < BNLM_DIV_LUT_SIZE; i++) { + for (i = 0; i < BNLM_DIV_LUT_SIZE; i++) to->div_lut_intercepts[0][i] = div_lut_intercepts[i]; - } memset(to->power_of_2, 0, sizeof(to->power_of_2)); - for (i = 0; i < (ISP_VEC_ELEMBITS - 1); i++) { + for (i = 0; i < (ISP_VEC_ELEMBITS - 1); i++) to->power_of_2[0][i] = 1 << i; - } } /* - Encodes BNLM public parameters into DMEM parameters */ diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c index 38751b8e9e6a..177487b6b237 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c @@ -52,13 +52,12 @@ static int ctc2_slope(int y1, int y0, int x1, int x0) /*the slope must lie within the range (-max_slope-1) >= (dydx) >= (max_slope) */ - if (slope <= -max_slope - 1) { + if (slope <= -max_slope - 1) dydx = -max_slope - 1; - } else if (slope >= max_slope) { + else if (slope >= max_slope) dydx = max_slope; - } else { + else dydx = slope; - } return dydx; } diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c index 8e4451fcc8e3..76d76ca4401a 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c @@ -140,17 +140,14 @@ ia_css_eed1_8_vmem_encode( assert(tcinv_x[0] == 0); assert(fcinv_x[0] == 0); - for (j = 1; j < NUMBER_OF_CHGRINV_POINTS; j++) { + for (j = 1; j < NUMBER_OF_CHGRINV_POINTS; j++) assert(chgrinv_x[j] > chgrinv_x[j - 1]); - } - for (j = 1; j < NUMBER_OF_TCINV_POINTS; j++) { + for (j = 1; j < NUMBER_OF_TCINV_POINTS; j++) assert(tcinv_x[j] > tcinv_x[j - 1]); - } - for (j = 1; j < NUMBER_OF_FCINV_POINTS; j++) { + for (j = 1; j < NUMBER_OF_FCINV_POINTS; j++) assert(fcinv_x[j] > fcinv_x[j - 1]); - } /* The implementation of the calculating 1/x is based on the availability * of the OP_vec_shuffle16 operation. @@ -260,9 +257,9 @@ ia_css_eed1_8_encode( to->margin_neg_diff = (from->neg_margin1 - from->neg_margin0); /* Encode DEWEnhance exp (e_dew_enh_asr) */ - for (i = 0; i < (IA_CSS_NUMBER_OF_DEW_ENHANCE_SEGMENTS - 1); i++) { + for (i = 0; i < (IA_CSS_NUMBER_OF_DEW_ENHANCE_SEGMENTS - 1); i++) min_exp = max(min_exp, from->dew_enhance_seg_exp[i]); - } + to->e_dew_enh_asr = 13 - clamp(min_exp, 0, 13); to->dedgew_max = from->dedgew_max; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c index 13678138c48c..823a1b0c0991 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c @@ -240,9 +240,8 @@ ia_css_s3a_hmem_decode( /* Calculate sum of histogram of R, which should not be less than count_for_3a */ sum_r = 0; - for (i = 0; i < HMEM_UNIT_SIZE; i++) { + for (i = 0; i < HMEM_UNIT_SIZE; i++) sum_r += out_ptr[i].r; - } if (sum_r < count_for_3a) { /* histogram is invalid */ return; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c index bd2def6c341a..0ca7ef5bb064 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c @@ -50,9 +50,8 @@ void ia_css_sdis_horicoef_vmem_encode( assert(size % (IA_CSS_DVS_NUM_COEF_TYPES * ISP_VEC_NELEMS * sizeof( short)) == 0); - for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++) { + for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++) fill_row(&private[type * stride], &public[type * width], width, padding); - } } void ia_css_sdis_vertcoef_vmem_encode( @@ -77,9 +76,8 @@ void ia_css_sdis_vertcoef_vmem_encode( assert(size % (IA_CSS_DVS_NUM_COEF_TYPES * ISP_VEC_NELEMS * sizeof( short)) == 0); - for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++) { + for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++) fill_row(&private[type * stride], &public[type * height], height, padding); - } } void ia_css_sdis_horiproj_encode( diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c index af93ca96747c..60ae7bf5512b 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c @@ -670,9 +670,8 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, err = ia_css_isp_param_allocate_isp_parameters( &binary->mem_params, &binary->css_params, &info->mem_initializers); - if (err) { + if (err) return err; - } } for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c index b411ca2f415e..58d99abe70aa 100644 --- a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c +++ b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c @@ -1123,9 +1123,8 @@ ia_css_debug_pipe_graph_dump_prologue(void) void ia_css_debug_pipe_graph_dump_epilogue(void) { - if (strlen(ring_buffer) > 0) { + if (strlen(ring_buffer) > 0) dtrace_dot(ring_buffer); - } if (pg_inst.stream_format != N_ATOMISP_INPUT_FORMAT) { /* An input stream format has been set so assume we have @@ -1778,9 +1777,8 @@ static void debug_dump_one_trace(enum TRACE_CORE_ID proc_id) * When tid value is 111b, the data will be interpreted differently: * tid val is ignored, major field contains 2 bits (msb) for format type */ - if (tid_val == FIELD_TID_SEL_FORMAT_PAT) { + if (tid_val == FIELD_TID_SEL_FORMAT_PAT) dump_format = FIELD_FORMAT_UNPACK(trace_read_buf[j]); - } } switch (dump_format) { case TRACE_DUMP_FORMAT_POINT: diff --git a/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c b/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c index e6c11d5f77b4..8e8433f5b07a 100644 --- a/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c +++ b/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c @@ -295,9 +295,8 @@ static bool create_input_system_channel( if (!rc) return false; - if (!acquire_sid(me->stream2mmio_id, &me->stream2mmio_sid_id)) { + if (!acquire_sid(me->stream2mmio_id, &me->stream2mmio_sid_id)) return false; - } if (!acquire_ib_buffer( metadata ? cfg->metadata.bits_per_pixel : diff --git a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c index 0470871f8fff..8d11466fda1b 100644 --- a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c +++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c @@ -575,9 +575,8 @@ static int pipeline_stage_create( binary = stage_desc->binary; firmware = stage_desc->firmware; vf_frame = stage_desc->vf_frame; - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) out_frame[i] = stage_desc->out_frame[i]; - } stage = kvzalloc_obj(*stage); if (!stage) { diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c index fcebace11daf..8cbb5d598005 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/sh_css_params.c @@ -1928,9 +1928,8 @@ sh_css_set_per_frame_isp_config_on_pipe( params = stream->per_frame_isp_params_configs; /* update new ISP params object with the new config */ - if (!sh_css_init_isp_params_from_global(stream, params, false, pipe)) { + if (!sh_css_init_isp_params_from_global(stream, params, false, pipe)) err1 = -EINVAL; - } err2 = sh_css_init_isp_params_from_config(stream->pipes[0], params, config, pipe); @@ -2004,9 +2003,8 @@ sh_css_init_isp_params_from_config(struct ia_css_pipe *pipe, * user. */ /* we do not exit from this point immediately to allow internal * firmware feature testing. */ - if (is_dp_10bpp) { + if (is_dp_10bpp) err = -EINVAL; - } } else { err = -EINVAL; goto exit; @@ -3034,9 +3032,8 @@ process_kernel_parameters(unsigned int pipe_id, ia_css_ob_configure(¶ms->stream_configs.ob, isp_pipe_version, raw_bit_depth); } - if (params->config_changed[IA_CSS_S3A_ID]) { + if (params->config_changed[IA_CSS_S3A_ID]) ia_css_s3a_configure(raw_bit_depth); - } /* Copy stage uds parameters to config, since they can differ per stage. */ params->crop_config.crop_pos = params->uds[stage->stage_num].crop_pos; @@ -3899,9 +3896,8 @@ sh_css_invalidate_params(struct ia_css_stream *stream) params->isp_params_changed = true; for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) { for (j = 0; j < SH_CSS_MAX_STAGES; j++) { - for (mem = 0; mem < N_IA_CSS_MEMORIES; mem++) { + for (mem = 0; mem < N_IA_CSS_MEMORIES; mem++) params->isp_mem_params_changed[i][j][mem] = true; - } } } From 2785424faf21c6c43aa7b78ed9e800e580caa324 Mon Sep 17 00:00:00 2001 From: Matt Wardle Date: Tue, 10 Feb 2026 09:24:02 +0000 Subject: [PATCH 214/394] staging: media: atomisp: Fix function indentation and braces Fix parameter indentation for functions and move opening braces onto new line. Fix checkpatch.pl errors: ERROR: open brace '{' following function definitions go on the next line Signed-off-by: Matt Wardle Signed-off-by: Sakari Ailus --- .../isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c | 11 ++--- .../isp/kernels/raw/raw_1.0/ia_css_raw.host.c | 4 +- .../kernels/sdis/sdis_2/ia_css_sdis2.host.c | 7 ++- .../isp/kernels/vf/vf_1.0/ia_css_vf.host.c | 22 ++++----- .../atomisp/pci/runtime/binary/src/binary.c | 48 +++++++++---------- .../pci/runtime/isp_param/src/isp_param.c | 18 ++++--- 6 files changed, 52 insertions(+), 58 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c index e9d6dd0bbfe2..4c85b5a62224 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c @@ -258,12 +258,11 @@ convert_allocate_dvs_6axis_config( return me; } -int -store_dvs_6axis_config( - const struct ia_css_dvs_6axis_config *dvs_6axis_config, - const struct ia_css_binary *binary, - const struct ia_css_frame_info *dvs_in_frame_info, - ia_css_ptr ddr_addr_y) { +int store_dvs_6axis_config(const struct ia_css_dvs_6axis_config *dvs_6axis_config, + const struct ia_css_binary *binary, + const struct ia_css_frame_info *dvs_in_frame_info, + ia_css_ptr ddr_addr_y) +{ struct ia_css_host_data *me; assert(dvs_6axis_config); diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c index a00f8d049a33..80fd64a8eb98 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c @@ -21,8 +21,8 @@ static const struct ia_css_raw_configuration default_config = { }; /* MW: These areMIPI / ISYS properties, not camera function properties */ -static enum sh_stream_format -css2isp_stream_format(enum atomisp_input_format from) { +static enum sh_stream_format css2isp_stream_format(enum atomisp_input_format from) +{ switch (from) { case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY: diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c index 4a38d678f334..da58b61b091f 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c @@ -164,10 +164,9 @@ void ia_css_sdis2_clear_coefficients( dvs2_coefs->ver_coefs.even_imag = NULL; } -int -ia_css_get_dvs2_statistics( - struct ia_css_dvs2_statistics *host_stats, - const struct ia_css_isp_dvs_statistics *isp_stats) { +int ia_css_get_dvs2_statistics(struct ia_css_dvs2_statistics *host_stats, + const struct ia_css_isp_dvs_statistics *isp_stats) +{ struct ia_css_isp_dvs_statistics_map *map; int ret = 0; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c index 3c675063c4a7..f8870f7e790d 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c @@ -45,11 +45,10 @@ int ia_css_vf_config(struct sh_css_isp_vf_isp_config *to, * to the requested viewfinder resolution on the upper side. The output cannot * be smaller than the requested viewfinder resolution. */ -int -sh_css_vf_downscale_log2( - const struct ia_css_frame_info *out_info, - const struct ia_css_frame_info *vf_info, - unsigned int *downscale_log2) { +int sh_css_vf_downscale_log2(const struct ia_css_frame_info *out_info, + const struct ia_css_frame_info *vf_info, + unsigned int *downscale_log2) +{ unsigned int ds_log2 = 0; unsigned int out_width; @@ -80,13 +79,12 @@ sh_css_vf_downscale_log2( return 0; } -static int -configure_kernel( - const struct ia_css_binary_info *info, - const struct ia_css_frame_info *out_info, - const struct ia_css_frame_info *vf_info, - unsigned int *downscale_log2, - struct ia_css_vf_configuration *config) { +static int configure_kernel(const struct ia_css_binary_info *info, + const struct ia_css_frame_info *out_info, + const struct ia_css_frame_info *vf_info, + unsigned int *downscale_log2, + struct ia_css_vf_configuration *config) +{ int err; unsigned int vf_log_ds = 0; diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c index 60ae7bf5512b..c7962549e999 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c @@ -347,10 +347,10 @@ ia_css_binary_dvs_stat_grid_info( return; } -int -ia_css_binary_3a_grid_info(const struct ia_css_binary *binary, - struct ia_css_grid_info *info, - struct ia_css_pipe *pipe) { +int ia_css_binary_3a_grid_info(const struct ia_css_binary *binary, + struct ia_css_grid_info *info, + struct ia_css_pipe *pipe) +{ struct ia_css_3a_grid_info *s3a_info; int err = 0; @@ -439,9 +439,9 @@ supports_bds_factor(u32 supported_factors, return ((supported_factors & PACK_BDS_FACTOR(bds_factor)) != 0); } -static int -binary_init_info(struct ia_css_binary_xinfo *info, unsigned int i, - bool *binary_found) { +static int binary_init_info(struct ia_css_binary_xinfo *info, unsigned int i, + bool *binary_found) +{ const unsigned char *blob = sh_css_blob_info[i].blob; unsigned int size = sh_css_blob_info[i].header.blob.size; @@ -464,8 +464,8 @@ binary_init_info(struct ia_css_binary_xinfo *info, unsigned int i, /* When binaries are put at the beginning, they will only * be selected if no other primary matches. */ -int -ia_css_binary_init_infos(void) { +int ia_css_binary_init_infos(void) +{ unsigned int i; unsigned int num_of_isp_binaries = sh_css_num_binaries - NUM_OF_SPS - NUM_OF_BLS; @@ -497,8 +497,8 @@ ia_css_binary_init_infos(void) { return 0; } -int -ia_css_binary_uninit(void) { +int ia_css_binary_uninit(void) +{ unsigned int i; struct ia_css_binary_xinfo *b; @@ -625,19 +625,19 @@ binary_in_frame_padded_width(int in_frame_width, return rval; } -int -ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, - bool online, - bool two_ppc, - enum atomisp_input_format stream_format, - const struct ia_css_frame_info *in_info, /* can be NULL */ - const struct ia_css_frame_info *bds_out_info, /* can be NULL */ - const struct ia_css_frame_info *out_info[], /* can be NULL */ - const struct ia_css_frame_info *vf_info, /* can be NULL */ - struct ia_css_binary *binary, - struct ia_css_resolution *dvs_env, - int stream_config_left_padding, - bool accelerator) { +int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, + bool online, + bool two_ppc, + enum atomisp_input_format stream_format, + const struct ia_css_frame_info *in_info, /* can be NULL */ + const struct ia_css_frame_info *bds_out_info, /* can be NULL */ + const struct ia_css_frame_info *out_info[], /* can be NULL */ + const struct ia_css_frame_info *vf_info, /* can be NULL */ + struct ia_css_binary *binary, + struct ia_css_resolution *dvs_env, + int stream_config_left_padding, + bool accelerator) +{ const struct ia_css_binary_info *info = &xinfo->sp; unsigned int dvs_env_width = 0, dvs_env_height = 0, diff --git a/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c b/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c index 251dd75a7613..354e5405fd60 100644 --- a/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c +++ b/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c @@ -93,11 +93,10 @@ ia_css_init_memory_interface( } } -int -ia_css_isp_param_allocate_isp_parameters( - struct ia_css_isp_param_host_segments *mem_params, - struct ia_css_isp_param_css_segments *css_params, - const struct ia_css_isp_param_isp_segments *mem_initializers) { +int ia_css_isp_param_allocate_isp_parameters(struct ia_css_isp_param_host_segments *mem_params, + struct ia_css_isp_param_css_segments *css_params, + const struct ia_css_isp_param_isp_segments *mem_initializers) +{ int err = 0; unsigned int mem, pclass; @@ -171,11 +170,10 @@ ia_css_isp_param_load_fw_params( } } -int -ia_css_isp_param_copy_isp_mem_if_to_ddr( - struct ia_css_isp_param_css_segments *ddr, - const struct ia_css_isp_param_host_segments *host, - enum ia_css_param_class pclass) { +int ia_css_isp_param_copy_isp_mem_if_to_ddr(struct ia_css_isp_param_css_segments *ddr, + const struct ia_css_isp_param_host_segments *host, + enum ia_css_param_class pclass) +{ unsigned int mem; for (mem = 0; mem < N_IA_CSS_ISP_MEMORIES; mem++) From a380fa65d526b3e0d527cce0379b426bbe3f1c2e Mon Sep 17 00:00:00 2001 From: Matt Wardle Date: Tue, 10 Feb 2026 09:24:15 +0000 Subject: [PATCH 215/394] staging: media: atomisp: Fix braces on incorrect lines Fix checkpatch.pl errors: ERROR: that open brace { should be on the previous line Signed-off-by: Matt Wardle Signed-off-by: Sakari Ailus --- .../isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c | 3 +- .../isp/kernels/raw/raw_1.0/ia_css_raw.host.c | 3 +- .../kernels/sdis/sdis_2/ia_css_sdis2.host.c | 6 +- .../isp/kernels/vf/vf_1.0/ia_css_vf.host.c | 3 +- .../atomisp/pci/runtime/binary/src/binary.c | 60 +++++++------------ .../pci/runtime/isp_param/src/isp_param.c | 6 +- drivers/staging/media/atomisp/pci/sh_css.c | 6 +- 7 files changed, 29 insertions(+), 58 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c index 4c85b5a62224..cf6c29155758 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c @@ -273,8 +273,7 @@ int store_dvs_6axis_config(const struct ia_css_dvs_6axis_config *dvs_6axis_confi binary, dvs_in_frame_info); - if (!me) - { + if (!me) { IA_CSS_LEAVE_ERR_PRIVATE(-ENOMEM); return -ENOMEM; } diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c index 80fd64a8eb98..fb0e2a88cadb 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c @@ -23,8 +23,7 @@ static const struct ia_css_raw_configuration default_config = { /* MW: These areMIPI / ISYS properties, not camera function properties */ static enum sh_stream_format css2isp_stream_format(enum atomisp_input_format from) { - switch (from) - { + switch (from) { case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY: return sh_stream_format_yuv420_legacy; case ATOMISP_INPUT_FORMAT_YUV420_8: diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c index da58b61b091f..67860a5e4441 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c @@ -176,13 +176,11 @@ int ia_css_get_dvs2_statistics(struct ia_css_dvs2_statistics *host_stats, assert(isp_stats); map = ia_css_isp_dvs_statistics_map_allocate(isp_stats, NULL); - if (map) - { + if (map) { hmm_load(isp_stats->data_ptr, map->data_ptr, isp_stats->size); ia_css_translate_dvs2_statistics(host_stats, map); ia_css_isp_dvs_statistics_map_free(map); - } else - { + } else { IA_CSS_ERROR("out of memory"); ret = -ENOMEM; } diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c index f8870f7e790d..152faab2b169 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c @@ -64,8 +64,7 @@ int sh_css_vf_downscale_log2(const struct ia_css_frame_info *out_info, * test for the height since the vmem buffers only put restrictions on * the width of a line, not on the number of lines in a frame. */ - while (out_width >= vf_info->res.width) - { + while (out_width >= vf_info->res.width) { ds_log2++; out_width /= 2; } diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c index c7962549e999..39b37b557aff 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c @@ -477,8 +477,7 @@ int ia_css_binary_init_infos(void) if (!all_binaries) return -ENOMEM; - for (i = 0; i < num_of_isp_binaries; i++) - { + for (i = 0; i < num_of_isp_binaries; i++) { int ret; struct ia_css_binary_xinfo *binary = &all_binaries[i]; bool binary_found; @@ -502,8 +501,7 @@ int ia_css_binary_uninit(void) unsigned int i; struct ia_css_binary_xinfo *b; - for (i = 0; i < IA_CSS_BINARY_NUM_MODES; i++) - { + for (i = 0; i < IA_CSS_BINARY_NUM_MODES; i++) { for (b = binary_infos[i]; b; b = b->next) { if (b->xmem_addr) hmm_free(b->xmem_addr); @@ -664,8 +662,7 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, assert(binary); binary->info = xinfo; - if (!accelerator) - { + if (!accelerator) { /* binary->css_params has been filled by accelerator itself. */ err = ia_css_isp_param_allocate_isp_parameters( &binary->mem_params, &binary->css_params, @@ -673,15 +670,13 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, if (err) return err; } - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) - { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { if (out_info[i] && (out_info[i]->res.width != 0)) { bin_out_info = out_info[i]; break; } } - if (in_info && bin_out_info) - { + if (in_info && bin_out_info) { need_scaling = (in_info->res.width != bin_out_info->res.width) || (in_info->res.height != bin_out_info->res.height); } @@ -712,8 +707,7 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->internal_frame_info.res.height = isp_internal_height; binary->internal_frame_info.raw_bit_depth = bits_per_pixel; - if (in_info) - { + if (in_info) { binary->effective_in_frame_res.width = in_info->res.width; binary->effective_in_frame_res.height = in_info->res.height; @@ -741,15 +735,13 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->in_frame_info.crop_info = in_info->crop_info; } - if (online) - { + if (online) { bits_per_pixel = ia_css_util_input_format_bpp( stream_format, two_ppc); } binary->in_frame_info.raw_bit_depth = bits_per_pixel; - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) - { + for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { if (out_info[i]) { binary->out_frame_info[i].res.width = out_info[i]->res.width; binary->out_frame_info[i].res.height = out_info[i]->res.height; @@ -768,8 +760,7 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, } } - if (vf_info && (vf_info->res.width != 0)) - { + if (vf_info && (vf_info->res.width != 0)) { err = ia_css_vf_configure(binary, bin_out_info, (struct ia_css_frame_info *)vf_info, &vf_log_ds); if (err) { @@ -787,8 +778,7 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->input_format = stream_format; /* viewfinder output info */ - if ((vf_info) && (vf_info->res.width != 0)) - { + if ((vf_info) && (vf_info->res.width != 0)) { unsigned int vf_out_vecs, vf_out_width, vf_out_height; binary->vf_frame_info.format = vf_info->format; @@ -820,23 +810,20 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->vf_frame_info.padded_width = vf_out_width; binary->vf_frame_info.res.height = vf_out_height; } - } else - { + } else { binary->vf_frame_info.res.width = 0; binary->vf_frame_info.padded_width = 0; binary->vf_frame_info.res.height = 0; } - if (info->enable.ca_gdc) - { + if (info->enable.ca_gdc) { binary->morph_tbl_width = _ISP_MORPH_TABLE_WIDTH(isp_internal_width); binary->morph_tbl_aligned_width = _ISP_MORPH_TABLE_ALIGNED_WIDTH(isp_internal_width); binary->morph_tbl_height = _ISP_MORPH_TABLE_HEIGHT(isp_internal_height); - } else - { + } else { binary->morph_tbl_width = 0; binary->morph_tbl_aligned_width = 0; binary->morph_tbl_height = 0; @@ -846,8 +833,7 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, sc_3a_dis_padded_width = binary->in_frame_info.padded_width; sc_3a_dis_height = binary->in_frame_info.res.height; if (bds_out_info && in_info && - bds_out_info->res.width != in_info->res.width) - { + bds_out_info->res.width != in_info->res.width) { /* TODO: Next, "internal_frame_info" should be derived from * bds_out. So this part will change once it is in place! */ sc_3a_dis_width = bds_out_info->res.width + info->pipeline.left_cropping; @@ -857,18 +843,15 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, s3a_isp_width = _ISP_S3A_ELEMS_ISP_WIDTH(sc_3a_dis_padded_width, info->pipeline.left_cropping); - if (info->s3a.fixed_s3a_deci_log) - { + if (info->s3a.fixed_s3a_deci_log) { s3a_log_deci = info->s3a.fixed_s3a_deci_log; - } else - { + } else { s3a_log_deci = binary_grid_deci_factor_log2(s3a_isp_width, sc_3a_dis_height); } binary->deci_factor_log2 = s3a_log_deci; - if (info->enable.s3a) - { + if (info->enable.s3a) { binary->s3atbl_width = _ISP_S3ATBL_WIDTH(sc_3a_dis_width, s3a_log_deci); @@ -881,21 +864,18 @@ int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, binary->s3atbl_isp_height = _ISP_S3ATBL_ISP_HEIGHT(sc_3a_dis_height, s3a_log_deci); - } else - { + } else { binary->s3atbl_width = 0; binary->s3atbl_height = 0; binary->s3atbl_isp_width = 0; binary->s3atbl_isp_height = 0; } - if (info->enable.sc) - { + if (info->enable.sc) { binary->sctbl_width_per_color = _ISP_SCTBL_WIDTH_PER_COLOR(sc_3a_dis_padded_width, s3a_log_deci); binary->sctbl_aligned_width_per_color = SH_CSS_MAX_SCTBL_ALIGNED_WIDTH_PER_COLOR; binary->sctbl_height = _ISP_SCTBL_HEIGHT(sc_3a_dis_height, s3a_log_deci); - } else - { + } else { binary->sctbl_width_per_color = 0; binary->sctbl_aligned_width_per_color = 0; binary->sctbl_height = 0; diff --git a/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c b/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c index 354e5405fd60..1d20eb650757 100644 --- a/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c +++ b/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c @@ -101,8 +101,7 @@ int ia_css_isp_param_allocate_isp_parameters(struct ia_css_isp_param_host_segmen unsigned int mem, pclass; pclass = IA_CSS_PARAM_CLASS_PARAM; - for (mem = 0; mem < IA_CSS_NUM_MEMORIES; mem++) - { + for (mem = 0; mem < IA_CSS_NUM_MEMORIES; mem++) { for (pclass = 0; pclass < IA_CSS_NUM_PARAM_CLASSES; pclass++) { u32 size = 0; @@ -176,8 +175,7 @@ int ia_css_isp_param_copy_isp_mem_if_to_ddr(struct ia_css_isp_param_css_segments { unsigned int mem; - for (mem = 0; mem < N_IA_CSS_ISP_MEMORIES; mem++) - { + for (mem = 0; mem < N_IA_CSS_ISP_MEMORIES; mem++) { size_t size = host->params[pclass][mem].size; ia_css_ptr ddr_mem_ptr = ddr->params[pclass][mem].address; char *host_mem_ptr = host->params[pclass][mem].address; diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 6cda5925fa45..f40b6912c14b 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -2256,8 +2256,7 @@ alloc_continuous_frames(struct ia_css_pipe *pipe, bool init_time) ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "alloc_continuous_frames() IA_CSS_FRAME_FORMAT_RAW_PACKED\n"); ref_info.format = IA_CSS_FRAME_FORMAT_RAW_PACKED; - } else - { + } else { ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "alloc_continuous_frames() IA_CSS_FRAME_FORMAT_RAW\n"); ref_info.format = IA_CSS_FRAME_FORMAT_RAW; @@ -7856,8 +7855,7 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, /* check if mipi size specified */ if (stream_config->mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) - if (!IS_ISP2401 || !stream_config->online) - { + if (!IS_ISP2401 || !stream_config->online) { unsigned int port = (unsigned int)stream_config->source.port.port; if (port >= N_MIPI_PORT_ID) { From 5cb289b5397d198f0ae1ee70dc31fee1c46ff5f9 Mon Sep 17 00:00:00 2001 From: Hamdan Khan Date: Mon, 9 Feb 2026 21:20:26 +0500 Subject: [PATCH 216/394] staging: media: atomisp: Fix typos and formatting in headers Update block and inline comments to follow kernel commenting conventions, fix typos and wording, remove redundant comments and reformat long comments for clarity and line length consistency. Although some comments used the /** ... */ style, they are not kernel-doc comments and are converted to normal comment style. No functional changes are intended. Signed-off-by: Hamdan Khan Signed-off-by: Sakari Ailus --- .../media/atomisp/include/linux/atomisp.h | 182 +++++++++--------- .../include/linux/atomisp_gmin_platform.h | 7 +- .../atomisp/include/linux/atomisp_platform.h | 42 ++-- 3 files changed, 120 insertions(+), 111 deletions(-) diff --git a/drivers/staging/media/atomisp/include/linux/atomisp.h b/drivers/staging/media/atomisp/include/linux/atomisp.h index 3c8fa3f5808d..7d7f57c2d67c 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp.h @@ -22,7 +22,7 @@ #define ATOMISP_HW_STEPPING_A0 0x00 #define ATOMISP_HW_STEPPING_B0 0x10 -/*ISP binary running mode*/ +/* ISP binary running mode */ #define CI_MODE_PREVIEW 0x8000 #define CI_MODE_VIDEO 0x4000 #define CI_MODE_STILL_CAPTURE 0x2000 @@ -59,9 +59,9 @@ /* Configuration used by Bayer noise reduction and YCC noise reduction */ struct atomisp_nr_config { - /* [gain] Strength of noise reduction for Bayer NR (Used by Bayer NR) */ + /* [gain] Strength of noise reduction for Bayer NR */ unsigned int bnr_gain; - /* [gain] Strength of noise reduction for YCC NR (Used by YCC NR) */ + /* [gain] Strength of noise reduction for YCC NR */ unsigned int ynr_gain; /* [intensity] Sensitivity of Edge (Used by Bayer NR) */ unsigned int direction; @@ -78,7 +78,8 @@ struct atomisp_tnr_config { unsigned int threshold_uv;/* [intensity] Motion sensitivity for U/V */ }; -/* Histogram. This contains num_elements values of type unsigned int. +/* + * Contains num_elements values of type unsigned int. * The data pointer is a DDR pointer (virtual address). */ struct atomisp_histogram { @@ -94,7 +95,7 @@ enum atomisp_ob_mode { /* Optical black level configuration */ struct atomisp_ob_config { - /* Obtical black level mode (Fixed / Raster) */ + /* Optical black level mode (Fixed / Raster) */ enum atomisp_ob_mode mode; /* [intensity] optical black level for GR (relevant for fixed mode) */ unsigned int level_gr; @@ -146,8 +147,7 @@ struct atomisp_3a_config { unsigned int ae_y_coef_r; /* [gain] Weight of R for Y */ unsigned int ae_y_coef_g; /* [gain] Weight of G for Y */ unsigned int ae_y_coef_b; /* [gain] Weight of B for Y */ - unsigned int awb_lg_high_raw; /* [intensity] - AWB level gate high for raw */ + unsigned int awb_lg_high_raw; /* [intensity] AWB level gate high for raw */ unsigned int awb_lg_low; /* [intensity] AWB level gate low */ unsigned int awb_lg_high; /* [intensity] AWB level gate high */ int af_fir1_coef[7]; /* [factor] AF FIR coefficients of fir1 */ @@ -188,14 +188,15 @@ struct atomisp_dis_vector { int y; }; -/* DVS 2.0 Coefficient types. This structure contains 4 pointers to - * arrays that contain the coefficients for each type. +/* + * DVS 2.0 Coefficient types. This structure contains 4 pointers to + * arrays that contain the coefficients for each type. */ struct atomisp_dvs2_coef_types { - short __user *odd_real; /** real part of the odd coefficients*/ - short __user *odd_imag; /** imaginary part of the odd coefficients*/ - short __user *even_real;/** real part of the even coefficients*/ - short __user *even_imag;/** imaginary part of the even coefficients*/ + short __user *odd_real; /* Real part of the odd coefficients */ + short __user *odd_imag; /* Imaginary part of the odd coefficients */ + short __user *even_real;/* Real part of the even coefficients */ + short __user *even_imag;/* Imaginary part of the even coefficients */ }; /* @@ -203,10 +204,10 @@ struct atomisp_dvs2_coef_types { * arrays that contain the statistics for each type. */ struct atomisp_dvs2_stat_types { - int __user *odd_real; /** real part of the odd statistics*/ - int __user *odd_imag; /** imaginary part of the odd statistics*/ - int __user *even_real;/** real part of the even statistics*/ - int __user *even_imag;/** imaginary part of the even statistics*/ + int __user *odd_real; /* Real part of the odd statistics */ + int __user *odd_imag; /* Imaginary part of the odd statistics */ + int __user *even_real;/* Real part of the even statistics */ + int __user *even_imag;/* Imaginary part of the even statistics */ }; struct atomisp_dis_coefficients { @@ -234,12 +235,12 @@ struct atomisp_3a_rgby_output { }; /* - * Because we have 2 pipes at max to output metadata, therefore driver will use - * ATOMISP_MAIN_METADATA to specify the metadata from the pipe which keeps - * streaming always and use ATOMISP_SEC_METADATA to specify the metadata from - * the pipe which is streaming by request like capture pipe of ZSL or SDV mode - * as secondary metadata. And for the use case which has only one pipe - * streaming like online capture, ATOMISP_MAIN_METADATA will be used. + * As the driver can output metadata on two pipes at max, + * ATOMISP_MAIN_METADATA is used for the pipe that streams continuously. + * ATOMISP_SEC_METADATA is used for the pipe that streams on demand, e.g., + * the capture pipe in ZSL or SDV modes. + * In use cases with a single streaming pipe (like online capture), + * only ATOMISP_MAIN_METADATA is used. */ enum atomisp_metadata_type { ATOMISP_MAIN_METADATA = 0, @@ -257,7 +258,7 @@ struct atomisp_3a_statistics { struct atomisp_3a_output __user *data; struct atomisp_3a_rgby_output __user *rgby_data; u32 exp_id; /* exposure ID */ - u32 isp_config_id; /* isp config ID */ + u32 isp_config_id; }; /* White Balance (Gain Adjust) */ @@ -272,8 +273,8 @@ struct atomisp_wb_config { /* Color Space Conversion settings */ struct atomisp_cc_config { unsigned int fraction_bits; - int matrix[3 * 3]; /* RGB2YUV Color matrix, signed - <13-fraction_bits>. */ + /* RGB2YUV Color matrix, signed <13-fraction_bits>. */ + int matrix[3 * 3]; }; /* De pixel noise configuration */ @@ -291,13 +292,15 @@ struct atomisp_ce_config { /* Defect pixel correction configuration */ struct atomisp_dp_config { - /* [intensity] The threshold of defect Pixel Correction, representing + /* + * [intensity] The threshold of defect Pixel Correction, representing * the permissible difference of intensity between one pixel and its * surrounding pixels. Smaller values result in more frequent pixel * corrections. u0_16 */ unsigned int threshold; - /* [gain] The sensitivity of mis-correction. ISP will miss a lot of + /* + * [gain] The sensitivity of mis-correction. ISP will miss a lot of * defects if the value is set too large. u8_8 */ unsigned int gain; @@ -312,7 +315,7 @@ struct atomisp_xnr_config { __u16 threshold; }; -/* metadata config */ +/* Metadata config */ struct atomisp_metadata_config { u32 metadata_height; u32 metadata_stride; @@ -322,31 +325,30 @@ struct atomisp_metadata_config { * Generic resolution structure. */ struct atomisp_resolution { - u32 width; /** Width */ - u32 height; /** Height */ + u32 width; + u32 height; }; /* - * This specifies the coordinates (x,y) + * Specifies the zoom point coordinates (x,y) */ struct atomisp_zoom_point { - s32 x; /** x coordinate */ - s32 y; /** y coordinate */ + s32 x; + s32 y; }; /* - * This specifies the region + * Specifies the zoom region */ struct atomisp_zoom_region { - struct atomisp_zoom_point - origin; /* Starting point coordinates for the region */ + struct atomisp_zoom_point origin; /* Starting point coordinates for the region */ struct atomisp_resolution resolution; /* Region resolution */ }; struct atomisp_dz_config { - u32 dx; /** Horizontal zoom factor */ - u32 dy; /** Vertical zoom factor */ - struct atomisp_zoom_region zoom_region; /** region for zoom */ + u32 dx; /* Horizontal zoom factor */ + u32 dy; /* Vertical zoom factor */ + struct atomisp_zoom_region zoom_region; }; struct atomisp_parm { @@ -367,8 +369,8 @@ struct atomisp_parm { }; struct dvs2_bq_resolution { - int width_bq; /* width [BQ] */ - int height_bq; /* height [BQ] */ + int width_bq; + int height_bq; }; struct atomisp_dvs2_bq_resolutions { @@ -378,9 +380,9 @@ struct atomisp_dvs2_bq_resolutions { struct dvs2_bq_resolution output_bq; /* GDC effective envelope size [BQ] */ struct dvs2_bq_resolution envelope_bq; - /* isp pipe filter size [BQ] */ + /* ISP pipe filter size [BQ] */ struct dvs2_bq_resolution ispfilter_bq; - /* GDC shit size [BQ] */ + /* GDC shift size [BQ] */ struct dvs2_bq_resolution gdc_shift_bq; }; @@ -411,8 +413,8 @@ struct atomisp_parameters { struct atomisp_cnr_config *cnr_config; /* Chroma Noise Reduction */ struct atomisp_macc_config *macc_config; /* MACC */ struct atomisp_ctc_config *ctc_config; /* Chroma Tone Control */ - struct atomisp_aa_config *aa_config; /* Anti-Aliasing */ - struct atomisp_aa_config *baa_config; /* Anti-Aliasing */ + struct atomisp_aa_config *aa_config; /* Anti-Aliasing */ + struct atomisp_aa_config *baa_config; /* Anti-Aliasing */ struct atomisp_ce_config *ce_config; struct atomisp_dvs_6axis_config *dvs_6axis_config; struct atomisp_ob_config *ob_config; /* Objective Black config */ @@ -425,10 +427,8 @@ struct atomisp_parameters { struct atomisp_3a_config *a3a_config; /* 3A Statistics config */ struct atomisp_xnr_config *xnr_config; /* eXtra Noise Reduction */ struct atomisp_dz_config *dz_config; /* Digital Zoom */ - struct atomisp_cc_config *yuv2rgb_cc_config; /* Color - Correction config */ - struct atomisp_cc_config *rgb2yuv_cc_config; /* Color - Correction config */ + struct atomisp_cc_config *yuv2rgb_cc_config; /* Color Correction config */ + struct atomisp_cc_config *rgb2yuv_cc_config; /* Color Correction config */ struct atomisp_macc_table *macc_table; struct atomisp_gamma_table *gamma_table; struct atomisp_ctc_table *ctc_table; @@ -466,8 +466,8 @@ struct atomisp_parameters { void *res_mgr_2500_config; /* - * Output frame pointer the config is to be applied to (optional), - * set to NULL to make this config is applied as global. + * Output frame pointer to which the config will be applied to (optional), + * Set to NULL to make this config be applied as global. */ void *output_frame; /* @@ -490,9 +490,9 @@ struct atomisp_gamma_table { unsigned short data[ATOMISP_GAMMA_TABLE_SIZE]; }; -/* Morphing table for advanced ISP. - * Each line of width elements takes up COORD_TABLE_EXT_WIDTH elements - * in memory. +/* + * Morphing table for advanced ISP. + * Each line of width elements takes up COORD_TABLE_EXT_WIDTH elements in memory. */ #define ATOMISP_MORPH_TABLE_NUM_PLANES 6 struct atomisp_morph_table { @@ -519,7 +519,7 @@ struct atomisp_shading_table { __u16 *data[ATOMISP_NUM_SC_COLORS]; }; -/* parameter for MACC */ +/* Parameter for MACC */ #define ATOMISP_NUM_MACC_AXES 16 struct atomisp_macc_table { short data[4 * ATOMISP_NUM_MACC_AXES]; @@ -530,7 +530,7 @@ struct atomisp_macc_config { struct atomisp_macc_table table; }; -/* Parameter for ctc parameter control */ +/* CTC parameter control */ #define ATOMISP_CTC_TABLE_SIZE 1024 struct atomisp_ctc_table { unsigned short data[ATOMISP_CTC_TABLE_SIZE]; @@ -538,9 +538,10 @@ struct atomisp_ctc_table { /* Parameter for overlay image loading */ struct atomisp_overlay { - /* the frame containing the overlay data The overlay frame width should - * be the multiples of 2*ISP_VEC_NELEMS. The overlay frame height - * should be the multiples of 2. + /* + * The frame containing the overlay data. The overlay frame width should + * be a multiple of 2 * ISP_VEC_NELEMS. The overlay frame height + * should be a multiple of 2. */ struct v4l2_framebuffer *frame; /* Y value of overlay background */ @@ -549,23 +550,27 @@ struct atomisp_overlay { char bg_u; /* V value of overlay background */ char bg_v; - /* the blending percent of input data for Y subpixels */ + /* The blending percentage of input data for Y subpixels */ unsigned char blend_input_perc_y; - /* the blending percent of input data for U subpixels */ + /* The blending percentage of input data for U subpixels */ unsigned char blend_input_perc_u; - /* the blending percent of input data for V subpixels */ + /* The blending percentage of input data for V subpixels */ unsigned char blend_input_perc_v; - /* the blending percent of overlay data for Y subpixels */ + /* The blending percentage of overlay data for Y subpixels */ unsigned char blend_overlay_perc_y; - /* the blending percent of overlay data for U subpixels */ + /* The blending percentage of overlay data for U subpixels */ unsigned char blend_overlay_perc_u; - /* the blending percent of overlay data for V subpixels */ + /* The blending percentage of overlay data for V subpixels */ unsigned char blend_overlay_perc_v; - /* the overlay start x pixel position on output frame It should be the - multiples of 2*ISP_VEC_NELEMS. */ + /* + * The overlay "start x" pixel position on the output frame. It should be a + * multiple of 2 * ISP_VEC_NELEMS. + */ unsigned int overlay_start_x; - /* the overlay start y pixel position on output frame It should be the - multiples of 2. */ + /* + * The overlay "start y" pixel position on the output frame. It should be a + * multiple of 2. + */ unsigned int overlay_start_y; }; @@ -659,7 +664,7 @@ enum atomisp_burst_capture_options { #define EXT_ISP_SHOT_MODE_ANIMATED_PHOTO 10 #define EXT_ISP_SHOT_MODE_SPORTS 11 -/*Private IOCTLs for ISP */ +/* Private IOCTLs for ISP */ #define ATOMISP_IOC_G_XNR \ _IOR('v', BASE_VIDIOC_PRIVATE + 0, int) #define ATOMISP_IOC_S_XNR \ @@ -684,7 +689,8 @@ enum atomisp_burst_capture_options { _IOR('v', BASE_VIDIOC_PRIVATE + 5, struct atomisp_ee_config) #define ATOMISP_IOC_S_EE \ _IOW('v', BASE_VIDIOC_PRIVATE + 5, struct atomisp_ee_config) -/* Digital Image Stabilization: +/* + * Digital Image Stabilization: * 1. get dis statistics: reads DIS statistics from ISP (every frame) * 2. set dis coefficients: set DIS filter coefficients (one time) * 3. set dis motion vector: set motion vector (result of DIS, every frame) @@ -716,54 +722,54 @@ enum atomisp_burst_capture_options { #define ATOMISP_IOC_S_ISP_GDC_TAB \ _IOW('v', BASE_VIDIOC_PRIVATE + 10, struct atomisp_morph_table) -/* macc parameter control*/ +/* MACC parameter control */ #define ATOMISP_IOC_G_ISP_MACC \ _IOR('v', BASE_VIDIOC_PRIVATE + 12, struct atomisp_macc_config) #define ATOMISP_IOC_S_ISP_MACC \ _IOW('v', BASE_VIDIOC_PRIVATE + 12, struct atomisp_macc_config) -/* Defect pixel detection & Correction */ +/* Defect pixel detection & correction */ #define ATOMISP_IOC_G_ISP_BAD_PIXEL_DETECTION \ _IOR('v', BASE_VIDIOC_PRIVATE + 13, struct atomisp_dp_config) #define ATOMISP_IOC_S_ISP_BAD_PIXEL_DETECTION \ _IOW('v', BASE_VIDIOC_PRIVATE + 13, struct atomisp_dp_config) -/* False Color Correction */ +/* False color correction */ #define ATOMISP_IOC_G_ISP_FALSE_COLOR_CORRECTION \ _IOR('v', BASE_VIDIOC_PRIVATE + 14, struct atomisp_de_config) #define ATOMISP_IOC_S_ISP_FALSE_COLOR_CORRECTION \ _IOW('v', BASE_VIDIOC_PRIVATE + 14, struct atomisp_de_config) -/* ctc parameter control */ +/* CTC parameter control */ #define ATOMISP_IOC_G_ISP_CTC \ _IOR('v', BASE_VIDIOC_PRIVATE + 15, struct atomisp_ctc_table) #define ATOMISP_IOC_S_ISP_CTC \ _IOW('v', BASE_VIDIOC_PRIVATE + 15, struct atomisp_ctc_table) -/* white balance Correction */ +/* White balance correction */ #define ATOMISP_IOC_G_ISP_WHITE_BALANCE \ _IOR('v', BASE_VIDIOC_PRIVATE + 16, struct atomisp_wb_config) #define ATOMISP_IOC_S_ISP_WHITE_BALANCE \ _IOW('v', BASE_VIDIOC_PRIVATE + 16, struct atomisp_wb_config) -/* fpn table loading */ +/* FPN table loading */ #define ATOMISP_IOC_S_ISP_FPN_TABLE \ _IOW('v', BASE_VIDIOC_PRIVATE + 17, struct v4l2_framebuffer) -/* overlay image loading */ +/* Overlay image loading */ #define ATOMISP_IOC_G_ISP_OVERLAY \ _IOWR('v', BASE_VIDIOC_PRIVATE + 18, struct atomisp_overlay) #define ATOMISP_IOC_S_ISP_OVERLAY \ _IOW('v', BASE_VIDIOC_PRIVATE + 18, struct atomisp_overlay) -/* bcd driver bridge */ +/* BCD driver bridge */ #define ATOMISP_IOC_CAMERA_BRIDGE \ _IOWR('v', BASE_VIDIOC_PRIVATE + 19, struct atomisp_bc_video_package) #define ATOMISP_IOC_S_EXPOSURE \ _IOW('v', BASE_VIDIOC_PRIVATE + 21, struct atomisp_exposure) -/* white balance Correction */ +/* White balance correction */ #define ATOMISP_IOC_G_3A_CONFIG \ _IOR('v', BASE_VIDIOC_PRIVATE + 23, struct atomisp_3a_config) #define ATOMISP_IOC_S_3A_CONFIG \ @@ -773,7 +779,7 @@ enum atomisp_burst_capture_options { #define ATOMISP_IOC_S_ISP_SHD_TAB \ _IOWR('v', BASE_VIDIOC_PRIVATE + 27, struct atomisp_shading_table) -/* Gamma Correction */ +/* Gamma correction */ #define ATOMISP_IOC_G_ISP_GAMMA_CORRECTION \ _IOR('v', BASE_VIDIOC_PRIVATE + 28, struct atomisp_gc_config) @@ -804,7 +810,7 @@ enum atomisp_burst_capture_options { #define ATOMISP_IOC_S_ARRAY_RESOLUTION \ _IOW('v', BASE_VIDIOC_PRIVATE + 45, struct atomisp_resolution) -/* for depth mode sensor frame sync compensation */ +/* For depth mode sensor frame sync compensation */ #define ATOMISP_IOC_G_DEPTH_SYNC_COMP \ _IOR('v', BASE_VIDIOC_PRIVATE + 46, unsigned int) @@ -822,7 +828,7 @@ enum atomisp_burst_capture_options { * _IOW('v', BASE_VIDIOC_PRIVATE + 56, struct atomisp_sensor_regs) */ -/* ISP Private control IDs */ +/* ISP Private control IDs */ #define V4L2_CID_ATOMISP_BAD_PIXEL_DETECTION \ (V4L2_CID_PRIVATE_BASE + 0) #define V4L2_CID_ATOMISP_POSTPROCESS_GDC_CAC \ @@ -836,8 +842,10 @@ enum atomisp_burst_capture_options { #define V4L2_CID_ATOMISP_LOW_LIGHT \ (V4L2_CID_PRIVATE_BASE + 5) -/* Camera class: - * Exposure, Flash and privacy (indicator) light controls, to be upstreamed */ +/* + * Camera class: + * Exposure, Flash and privacy (indicator) light controls, to be upstreamed + */ #define V4L2_CID_CAMERA_LASTP1 (V4L2_CID_CAMERA_CLASS_BASE + 1024) #define V4L2_CID_RUN_MODE (V4L2_CID_CAMERA_LASTP1 + 20) @@ -879,7 +887,7 @@ enum atomisp_burst_capture_options { #define V4L2_EVENT_ATOMISP_ACC_COMPLETE (V4L2_EVENT_PRIVATE_START + 4) #define V4L2_EVENT_ATOMISP_PAUSE_BUFFER (V4L2_EVENT_PRIVATE_START + 5) #define V4L2_EVENT_ATOMISP_CSS_RESET (V4L2_EVENT_PRIVATE_START + 6) -/* Nonstandard color effects for V4L2_CID_COLORFX */ +/* Non-standard color effects for V4L2_CID_COLORFX */ enum { V4L2_COLORFX_SKIN_WHITEN_LOW = 1001, V4L2_COLORFX_SKIN_WHITEN_HIGH = 1002, diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h index 426c5ee4ec18..74092af1c659 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h @@ -15,8 +15,7 @@ int atomisp_gmin_remove_subdev(struct v4l2_subdev *sd); int gmin_get_var_int(struct device *dev, bool is_gmin, const char *var, int def); struct camera_sensor_platform_data * -gmin_camera_platform_data( - struct v4l2_subdev *subdev, - enum atomisp_input_format csi_format, - enum atomisp_bayer_order csi_bayer); + gmin_camera_platform_data(struct v4l2_subdev *subdev, + enum atomisp_input_format csi_format, + enum atomisp_bayer_order csi_bayer); #endif diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h index 6146555fe9cf..0d6f2a4039f1 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h @@ -57,7 +57,8 @@ enum atomisp_input_format { ATOMISP_INPUT_FORMAT_RAW_16, /* RAW data, 16 bits per pixel */ ATOMISP_INPUT_FORMAT_BINARY_8, /* Binary byte stream. */ - /* CSI2-MIPI specific format: Generic short packet data. It is used to + /* + * CSI2-MIPI specific format: Generic short packet data. It is used to * keep the timing information for the opening/closing of shutters, * triggering of flashes and etc. */ @@ -70,18 +71,18 @@ enum atomisp_input_format { ATOMISP_INPUT_FORMAT_GENERIC_SHORT7, /* Generic Short Packet Code 7 */ ATOMISP_INPUT_FORMAT_GENERIC_SHORT8, /* Generic Short Packet Code 8 */ - /* CSI2-MIPI specific format: YUV data. - */ - ATOMISP_INPUT_FORMAT_YUV420_8_SHIFT, /* YUV420 8-bit (Chroma Shifted - Pixel Sampling) */ - ATOMISP_INPUT_FORMAT_YUV420_10_SHIFT, /* YUV420 8-bit (Chroma Shifted - Pixel Sampling) */ + /* YUV data */ + /* YUV420 8-bit (Chroma Shifted Pixel Sampling) */ + ATOMISP_INPUT_FORMAT_YUV420_8_SHIFT, + /* YUV420 10-bit (Chroma Shifted Pixel Sampling) */ + ATOMISP_INPUT_FORMAT_YUV420_10_SHIFT, - /* CSI2-MIPI specific format: Generic long packet data - */ - ATOMISP_INPUT_FORMAT_EMBEDDED, /* Embedded 8-bit non Image Data */ + /* CSI2-MIPI specific format: Generic long packet data */ + /* Embedded 8-bit non Image Data */ + ATOMISP_INPUT_FORMAT_EMBEDDED, - /* CSI2-MIPI specific format: User defined byte-based data. For example, + /* + * User defined byte-based data. For example, * the data transmitter (e.g. the SoC sensor) can keep the JPEG data as * the User Defined Data Type 4 and the MPEG data as the * User Defined Data Type 7. @@ -105,9 +106,9 @@ struct intel_v4l2_subdev_table { }; /* - * Sensor of external ISP can send multiple streams with different mipi data + * Sensor of external ISP can send multiple streams with different MIPI data * type in the same virtual channel. This information needs to come from the - * sensor or external ISP + * sensor or external ISP. */ struct atomisp_isys_config_info { u8 input_format; @@ -118,16 +119,17 @@ struct atomisp_isys_config_info { struct atomisp_input_stream_info { enum atomisp_input_stream_id stream; u8 enable; - /* Sensor driver fills ch_id with the id - of the virtual channel. */ + /* Sensor driver fills ch_id with the id of the virtual channel. */ u8 ch_id; - /* Tells how many streams in this virtual channel. If 0 ignore rest - * and the input format will be from mipi_info */ + /* + * Tells the number of streams in this virtual channel. If 0, ignore the + * rest and the input format will be from mipi_info. + */ u8 isys_configs; /* - * if more isys_configs is more than 0, sensor needs to configure the - * input format differently. width and height can be 0. If width and - * height is not zero, then the corresponding data needs to be set + * If isys_configs is greater than 0, sensor needs to configure the + * input format differently. Width and height can be 0. If width and + * height are not zero, then the corresponding data needs to be set. */ struct atomisp_isys_config_info isys_info[MAX_STREAMS_PER_CHANNEL]; }; From 4d1e8d9ca1adde4109291cac6aaec135c4812c4e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 2 Mar 2026 15:30:40 +0100 Subject: [PATCH 217/394] staging: media: atomisp: Kill OP_std_modadd() macro The OP_std_modadd() adds no value, kill it and update the users to perform the necessary operations themselves. No intended functional changes. Signed-off-by: Andy Shevchenko Reviewed-by: Ethan Tidmore Signed-off-by: Sakari Ailus --- .../atomisp/pci/base/circbuf/interface/ia_css_circbuf.h | 6 +----- .../pci/base/circbuf/interface/ia_css_circbuf_desc.h | 8 ++------ .../media/atomisp/pci/hive_isp_css_include/math_support.h | 6 ------ .../staging/media/atomisp/pci/runtime/queue/src/queue.c | 4 ++-- 4 files changed, 5 insertions(+), 19 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h index 148f9f6febfb..4732b45b25ee 100644 --- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h +++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h @@ -139,8 +139,6 @@ static inline uint8_t ia_css_circbuf_get_pos_at_offset( u32 base, int offset) { - u8 dest; - OP___assert(cb); OP___assert(cb->desc); OP___assert(cb->desc->size > 0); @@ -150,9 +148,7 @@ static inline uint8_t ia_css_circbuf_get_pos_at_offset( offset += cb->desc->size; /* step 2: shift and round by the upper limit */ - dest = OP_std_modadd(base, offset, cb->desc->size); - - return dest; + return (base + offset) % cb->desc->size; } /** diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h index 3d89626a0733..64f754f1d49b 100644 --- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h +++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h @@ -47,7 +47,7 @@ static inline bool ia_css_circbuf_desc_is_full( ia_css_circbuf_desc_t *cb_desc) { OP___assert(cb_desc); - return (OP_std_modadd(cb_desc->end, 1, cb_desc->size) == cb_desc->start); + return ((cb_desc->end + 1) % cb_desc->size) == cb_desc->start; } /** @@ -78,8 +78,6 @@ static inline uint8_t ia_css_circbuf_desc_get_pos_at_offset( u32 base, int offset) { - u8 dest; - OP___assert(cb_desc); OP___assert(cb_desc->size > 0); @@ -88,9 +86,7 @@ static inline uint8_t ia_css_circbuf_desc_get_pos_at_offset( offset += cb_desc->size; /* step 2: shift and round by the upper limit */ - dest = OP_std_modadd(base, offset, cb_desc->size); - - return dest; + return (base + offset) % cb_desc->size; } /** diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h index 2cb5c986790a..72a070d94736 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h @@ -14,10 +14,4 @@ #define CEIL_MUL(a, b) (CEIL_DIV(a, b) * (b)) #define CEIL_SHIFT(a, b) (((a) + (1 << (b)) - 1) >> (b)) -/* - * For SP and ISP, SDK provides the definition of OP_std_modadd. - * We need it only for host - */ -#define OP_std_modadd(base, offset, size) ((base + offset) % (size)) - #endif /* __MATH_SUPPORT_H */ diff --git a/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c b/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c index afe77d4373f8..d27c6567daeb 100644 --- a/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c +++ b/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c @@ -167,7 +167,7 @@ int ia_css_queue_dequeue(ia_css_queue_t *qhandle, uint32_t *item) *item = cb_elem.val; - cb_desc.start = OP_std_modadd(cb_desc.start, 1, cb_desc.size); + cb_desc.start = (cb_desc.start + 1) % cb_desc.size; /* c. Store the queue object */ /* Set only fields requiring update with @@ -315,7 +315,7 @@ int ia_css_queue_peek(ia_css_queue_t *qhandle, u32 offset, uint32_t *element) if (offset > num_elems) return -EINVAL; - offset = OP_std_modadd(cb_desc.start, offset, cb_desc.size); + offset = (cb_desc.start + offset) % cb_desc.size; error = ia_css_queue_item_load(qhandle, (uint8_t)offset, &cb_elem); if (error != 0) return error; From 835da7cf2da4bfbca140acc54c8d9bcb80695e6c Mon Sep 17 00:00:00 2001 From: Tomasz Unger Date: Thu, 5 Mar 2026 14:45:27 +0100 Subject: [PATCH 218/394] media: staging: atomisp: Remove unnecessary return statement in void function Remove redundant 'return;' at the end of void function ia_css_dvs_statistics_get(). Void functions do not need an explicit return statement at the end. No other occurrences in this file. Found with checkpatch.pl --strict. Signed-off-by: Tomasz Unger Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css_param_dvs.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c index 9ccdb66de2df..3d2cb2d25fdb 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c +++ b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c @@ -269,5 +269,4 @@ ia_css_dvs_statistics_get(enum dvs_statistics_type type, ia_css_get_dvs2_statistics(host_stats->p_dvs2_statistics_host, isp_stats->p_dvs_statistics_isp); } - return; } From f69992d08a06e9f105716a141731507f280f8b13 Mon Sep 17 00:00:00 2001 From: Ethan Lam Date: Mon, 2 Mar 2026 20:35:47 +0800 Subject: [PATCH 219/394] staging: media: atomisp: fix block comment style in atomisp_cmd.c Fix block comment style warnings reported by checkpatch.pl in atomisp_cmd.c. Signed-off-by: Ethan Lam Signed-off-by: Sakari Ailus --- .../staging/media/atomisp/pci/atomisp_cmd.c | 60 ++++++++++++------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index 5e5f3fe1ba7d..7c45fa10f986 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -1380,8 +1380,10 @@ static void atomisp_update_grid_info(struct atomisp_sub_device *asd, if (atomisp_css_get_grid_info(asd, pipe_id)) return; - /* We must free all buffers because they no longer match - the grid size. */ + /* + * We must free all buffers because they no longer match + * the grid size. + */ atomisp_css_free_stat_buffers(asd); err = atomisp_alloc_css_stat_bufs(asd, ATOMISP_INPUT_STREAM_GENERAL); @@ -1393,8 +1395,10 @@ static void atomisp_update_grid_info(struct atomisp_sub_device *asd, if (atomisp_alloc_3a_output_buf(asd)) { /* Failure for 3A buffers does not influence DIS buffers */ if (asd->params.s3a_output_bytes != 0) { - /* For SOC sensor happens s3a_output_bytes == 0, - * using if condition to exclude false error log */ + /* + * For SOC sensor happens s3a_output_bytes == 0, + * using if condition to exclude false error log + */ dev_err(isp->dev, "Failed to allocate memory for 3A statistics\n"); } goto err; @@ -1686,9 +1690,11 @@ int atomisp_3a_stat(struct atomisp_sub_device *asd, int flag, return -EINVAL; if (atomisp_compare_grid(asd, &config->grid_info) != 0) { - /* If the grid info in the argument differs from the current - grid info, we tell the caller to reset the grid size and - try again. */ + /* + * If the grid info in the argument differs from the current + * grid info, we tell the caller to reset the grid size and + * try again. + */ return -EAGAIN; } @@ -2462,9 +2468,11 @@ int atomisp_css_cp_dvs2_coefs(struct atomisp_sub_device *asd, if (sizeof(*cur) != sizeof(coefs->grid) || memcmp(&coefs->grid, cur, sizeof(coefs->grid))) { dev_err(asd->isp->dev, "dvs grid mismatch!\n"); - /* If the grid info in the argument differs from the current - grid info, we tell the caller to reset the grid size and - try again. */ + /* + * If the grid info in the argument differs from the current + * grid info, we tell the caller to reset the grid size and + * try again. + */ return -EAGAIN; } @@ -3025,9 +3033,11 @@ int atomisp_param(struct atomisp_sub_device *asd, int flag, atomisp_curr_user_grid_info(asd, &config->info); - /* We always return the resolution and stride even if there is + /* + * We always return the resolution and stride even if there is * no valid metadata. This allows the caller to get the - * information needed to allocate user-space buffers. */ + * information needed to allocate user-space buffers. + */ config->metadata_config.metadata_height = asd-> stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream_info. metadata_info.resolution.height; @@ -3277,8 +3287,10 @@ atomisp_bytesperline_to_padded_width(unsigned int bytesperline, return bytesperline / 2; case IA_CSS_FRAME_FORMAT_RGBA888: return bytesperline / 4; - /* The following cases could be removed, but we leave them - in to document the formats that are included. */ + /* + * The following cases could be removed, but we leave them + * in to document the formats that are included. + */ case IA_CSS_FRAME_FORMAT_NV11: case IA_CSS_FRAME_FORMAT_NV12: case IA_CSS_FRAME_FORMAT_NV16: @@ -3314,9 +3326,11 @@ atomisp_v4l2_framebuffer_to_css_frame(const struct v4l2_framebuffer *arg, padded_width = atomisp_bytesperline_to_padded_width( arg->fmt.bytesperline, sh_format); - /* Note: the padded width on an ia_css_frame is in elements, not in - bytes. The RAW frame we use here should always be a 16bit RAW - frame. This is why we bytesperline/2 is equal to the padded with */ + /* + * Note: the padded width on an ia_css_frame is in elements, not in + * bytes. The RAW frame we use here should always be a 16bit RAW + * frame. This is why we bytesperline/2 is equal to the padded with + */ if (ia_css_frame_allocate(&res, arg->fmt.width, arg->fmt.height, sh_format, padded_width, 0)) { ret = -ENOMEM; @@ -3925,8 +3939,10 @@ static inline int atomisp_set_sensor_mipi_to_isp( asd->stream_env[stream_id].isys_info[1].height); } - /* Compatibility for sensors which provide no media bus code - * in s_mbus_framefmt() nor support pad formats. */ + /* + * Compatibility for sensors which provide no media bus code + * in s_mbus_framefmt() nor support pad formats. + */ if (mipi_info && mipi_info->input_format != -1) { bayer_order = mipi_info->raw_bayer_order; @@ -4384,8 +4400,10 @@ int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f) ATOMISP_SUBDEV_PAD_SINK, V4L2_SEL_TGT_CROP); - /* Try to enable YUV downscaling if ISP input is 10 % (either - * width or height) bigger than the desired result. */ + /* + * Try to enable YUV downscaling if ISP input is 10 % (either + * width or height) bigger than the desired result. + */ if (!IS_MOFD || isp_sink_crop.width * 9 / 10 < f->fmt.pix.width || isp_sink_crop.height * 9 / 10 < f->fmt.pix.height || From 4e8156bd9517fa18c3613ea39c222c7035f0221e Mon Sep 17 00:00:00 2001 From: Zilin Guan Date: Tue, 3 Feb 2026 16:31:34 +0000 Subject: [PATCH 220/394] media: atomisp: Fix memory leak in atomisp_fixed_pattern_table() atomisp_v4l2_framebuffer_to_css_frame() allocates memory for temporary variable raw_black_frame, which must be released via ia_css_frame_free() before the function returns. However, if sh_css_set_black_frame() fails, the function returns immediately without performing this cleanup, leading to a memory leak. Fix this by assigning the return value of sh_css_set_black_frame() to ret. This ensures that the error code is propagated while allowing the execution to fall through to the ia_css_frame_free() cleanup call. The bug was originally detected on v6.13-rc1 using an experimental static analysis tool we are developing, and we have verified that the issue persists in the latest mainline kernel. The tool is based on the LLVM framework and is specifically designed to detect memory management issues. It is currently under active development and not yet publicly available. We performed build testing on x86_64 with allyesconfig. Since triggering this error path in atomisp requires specific Intel Atom ISP hardware and firmware, we were unable to perform runtime testing and instead verified the fix according to the code logic. Fixes: 85b606e02ad7 ("media: atomisp: get rid of a bunch of other wrappers") Signed-off-by: Zilin Guan Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_cmd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index 7c45fa10f986..e401c23a7575 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -3378,10 +3378,8 @@ int atomisp_fixed_pattern_table(struct atomisp_sub_device *asd, if (ret) return ret; - if (sh_css_set_black_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, - raw_black_frame) != 0) - return -ENOMEM; - + ret = sh_css_set_black_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream, + raw_black_frame); ia_css_frame_free(raw_black_frame); return ret; } From 0a41dd4aabc852692e9bc480e4a789683acd4777 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 10 Mar 2026 13:18:26 +0100 Subject: [PATCH 221/394] media: atomisp: Fix alloc_pages_bulk() failed errors Systems with the atomisp ISP do not have a lot of memory for modern standards, so these are often under memory pressure and alloc_pages_bulk() does not try very hard to free pages before returning an amount of pages which is less then requested. This leads to streaming from the camera often failing with a "alloc_pages_bulk() failed" error. vmalloc() also uses alloc_pages_bulk(), but falls back to allocating one page at a time when that fails. Do the same in alloc_private_pages() to avoid these errors. While at it also drop the weird custom GFP flags and just use GFP_KERNEL and drop the dev_err() as alloc_pages() already complaints loudly if it fails itself. Closes: https://github.com/jfwells/linux-asus-t100ta/issues/4 Signed-off-by: Hans de Goede Signed-off-by: Sakari Ailus --- .../staging/media/atomisp/pci/hmm/hmm_bo.c | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c index 856561e951a5..b91cbd0262d0 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c @@ -620,14 +620,23 @@ static void free_private_bo_pages(struct hmm_buffer_object *bo) /*Allocate pages which will be used only by ISP*/ static int alloc_private_pages(struct hmm_buffer_object *bo) { - const gfp_t gfp = __GFP_NOWARN | __GFP_RECLAIM | __GFP_FS; + unsigned int nr_allocated = 0; + struct page *page; int ret; - ret = alloc_pages_bulk(gfp, bo->pgnr, bo->pages); - if (ret != bo->pgnr) { - free_pages_bulk_array(ret, bo->pages); - dev_err(atomisp_dev, "alloc_pages_bulk() failed\n"); - return -ENOMEM; + nr_allocated = alloc_pages_bulk(GFP_KERNEL, bo->pgnr, bo->pages); + /* + * alloc_pages_bulk() does not try very hard to get pages under memory + * pressure. If necessary fall back to alloc_page(). + */ + while (nr_allocated < bo->pgnr) { + page = alloc_pages(GFP_KERNEL, 0); + if (!page) { + free_pages_bulk_array(nr_allocated, bo->pages); + return -ENOMEM; + } + bo->pages[nr_allocated] = page; + nr_allocated++; } ret = set_pages_array_uc(bo->pages, bo->pgnr); From 5ed76a163b0ca078bad206b35eddd97360e2f0ca Mon Sep 17 00:00:00 2001 From: Mahad Ibrahim Date: Mon, 5 Jan 2026 00:05:05 +0500 Subject: [PATCH 222/394] media: atomisp: Remove redundant return statement The function mmu_reg_store() returns void. The final return statement is redundant as it is followed by the closing brace. Remove the redundant return statement to simplify code and adhere to kernel coding style. Signed-off-by: Mahad Ibrahim Signed-off-by: Sakari Ailus --- .../media/atomisp/pci/hive_isp_css_include/host/mmu_public.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h index 1a435a348318..2fc137ef46da 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h @@ -63,7 +63,6 @@ static inline void mmu_reg_store( assert(ID < N_MMU_ID); assert(MMU_BASE[ID] != (hrt_address) - 1); ia_css_device_store_uint32(MMU_BASE[ID] + reg * sizeof(hrt_data), value); - return; } /*! Read from a control register of MMU[ID] From fff06b0538b1c5a690e8092757089a8078f94672 Mon Sep 17 00:00:00 2001 From: Mahad Ibrahim Date: Mon, 5 Jan 2026 00:05:06 +0500 Subject: [PATCH 223/394] media: atomisp: Fix function signature alignment Fix checkpatch.pl warnings regarding lines ending with "(" and improper spacing for indentation. This change fixes the function signatures for both function prototypes and static inline function definations in mmu_public.h. The kernel coding style prefers arguments to start immediately after the open parenthesis. Signed-off-by: Mahad Ibrahim Signed-off-by: Sakari Ailus --- .../hive_isp_css_include/host/mmu_public.h | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h index 2fc137ef46da..90a2e4e8f510 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h @@ -18,9 +18,7 @@ \return none, MMU[ID].page_table_base_index = base_index */ -void mmu_set_page_table_base_index( - const mmu_ID_t ID, - const hrt_data base_index); +void mmu_set_page_table_base_index(const mmu_ID_t ID, const hrt_data base_index); /*! Get the page table base index of MMU[ID] @@ -29,8 +27,7 @@ void mmu_set_page_table_base_index( \return MMU[ID].page_table_base_index */ -hrt_data mmu_get_page_table_base_index( - const mmu_ID_t ID); +hrt_data mmu_get_page_table_base_index(const mmu_ID_t ID); /*! Invalidate the page table cache of MMU[ID] @@ -38,8 +35,7 @@ hrt_data mmu_get_page_table_base_index( \return none */ -void mmu_invalidate_cache( - const mmu_ID_t ID); +void mmu_invalidate_cache(const mmu_ID_t ID); /*! Invalidate the page table cache of all MMUs @@ -55,10 +51,7 @@ void mmu_invalidate_cache_all(void); \return none, MMU[ID].ctrl[reg] = value */ -static inline void mmu_reg_store( - const mmu_ID_t ID, - const unsigned int reg, - const hrt_data value) +static inline void mmu_reg_store(const mmu_ID_t ID, const unsigned int reg, const hrt_data value) { assert(ID < N_MMU_ID); assert(MMU_BASE[ID] != (hrt_address) - 1); @@ -73,9 +66,7 @@ static inline void mmu_reg_store( \return MMU[ID].ctrl[reg] */ -static inline hrt_data mmu_reg_load( - const mmu_ID_t ID, - const unsigned int reg) +static inline hrt_data mmu_reg_load(const mmu_ID_t ID, const unsigned int reg) { assert(ID < N_MMU_ID); assert(MMU_BASE[ID] != (hrt_address) - 1); From 2842e567b4b90645c2fda07f464f62624e89bc7d Mon Sep 17 00:00:00 2001 From: Mahad Ibrahim Date: Mon, 5 Jan 2026 00:05:07 +0500 Subject: [PATCH 224/394] media: atomisp: Fix block comment coding style Fix checkpatch.pl warnings regarding block comments. Add missing asterisks to block comments to adhere to the kernel coding style. Signed-off-by: Mahad Ibrahim Signed-off-by: Sakari Ailus --- .../hive_isp_css_include/host/mmu_public.h | 74 ++++++++++--------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h index 90a2e4e8f510..c3495ec4835c 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h @@ -11,45 +11,50 @@ #include "device_access.h" #include "assert_support.h" -/*! Set the page table base index of MMU[ID] - - \param ID[in] MMU identifier - \param base_index[in] page table base index - - \return none, MMU[ID].page_table_base_index = base_index +/* + *! Set the page table base index of MMU[ID] + * + *\param ID[in] MMU identifier + *\param base_index[in] page table base index + * + *\return none, MMU[ID].page_table_base_index = base_index */ void mmu_set_page_table_base_index(const mmu_ID_t ID, const hrt_data base_index); -/*! Get the page table base index of MMU[ID] - - \param ID[in] MMU identifier - \param base_index[in] page table base index - - \return MMU[ID].page_table_base_index +/* + *! Get the page table base index of MMU[ID] + * + *\param ID[in] MMU identifier + *\param base_index[in] page table base index + * + *\return MMU[ID].page_table_base_index */ hrt_data mmu_get_page_table_base_index(const mmu_ID_t ID); -/*! Invalidate the page table cache of MMU[ID] - - \param ID[in] MMU identifier - - \return none +/* + *! Invalidate the page table cache of MMU[ID] + * + *\param ID[in] MMU identifier + * + *\return none */ void mmu_invalidate_cache(const mmu_ID_t ID); -/*! Invalidate the page table cache of all MMUs - - \return none +/* + *! Invalidate the page table cache of all MMUs + * + *\return none */ void mmu_invalidate_cache_all(void); -/*! Write to a control register of MMU[ID] - - \param ID[in] MMU identifier - \param reg[in] register index - \param value[in] The data to be written - - \return none, MMU[ID].ctrl[reg] = value +/* + *! Write to a control register of MMU[ID] + * + *\param ID[in] MMU identifier + *\param reg[in] register index + *\param value[in] The data to be written + * + *\return none, MMU[ID].ctrl[reg] = value */ static inline void mmu_reg_store(const mmu_ID_t ID, const unsigned int reg, const hrt_data value) { @@ -58,13 +63,14 @@ static inline void mmu_reg_store(const mmu_ID_t ID, const unsigned int reg, cons ia_css_device_store_uint32(MMU_BASE[ID] + reg * sizeof(hrt_data), value); } -/*! Read from a control register of MMU[ID] - - \param ID[in] MMU identifier - \param reg[in] register index - \param value[in] The data to be written - - \return MMU[ID].ctrl[reg] +/* + *! Read from a control register of MMU[ID] + * + *\param ID[in] MMU identifier + *\param reg[in] register index + *\param value[in] The data to be written + * + *\return MMU[ID].ctrl[reg] */ static inline hrt_data mmu_reg_load(const mmu_ID_t ID, const unsigned int reg) { From ffb694d75291b8d5433321bd16153bb973cf85bc Mon Sep 17 00:00:00 2001 From: Mahad Ibrahim Date: Mon, 5 Jan 2026 00:05:08 +0500 Subject: [PATCH 225/394] media: atomisp: Fix erroneous parameter descriptions The function mmu_get_page_table_base_index() accepts only one argument mmu_ID_t, the block comment for it shows an erroneous additional argument base_index[in]. Similarly, mmu_reg_load() only accepts two arguments, however the block comment explaining it shows an erroneous argument 'value[in]'. Remove incorrect documentation lines. Signed-off-by: Mahad Ibrahim Signed-off-by: Sakari Ailus --- .../media/atomisp/pci/hive_isp_css_include/host/mmu_public.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h index c3495ec4835c..611755e88e9f 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h @@ -25,7 +25,6 @@ void mmu_set_page_table_base_index(const mmu_ID_t ID, const hrt_data base_index) *! Get the page table base index of MMU[ID] * *\param ID[in] MMU identifier - *\param base_index[in] page table base index * *\return MMU[ID].page_table_base_index */ @@ -68,7 +67,6 @@ static inline void mmu_reg_store(const mmu_ID_t ID, const unsigned int reg, cons * *\param ID[in] MMU identifier *\param reg[in] register index - *\param value[in] The data to be written * *\return MMU[ID].ctrl[reg] */ From 33fea3423fbc91696dfeb6addf314cfb85af6ae0 Mon Sep 17 00:00:00 2001 From: Mahad Ibrahim Date: Mon, 5 Jan 2026 00:05:09 +0500 Subject: [PATCH 226/394] media: atomisp: Convert comments to kernel-doc Existing comments in mmu_public.h used Doxygen syntax and had inconsistent formatting. Convert the function documentation to the standard kernel-doc format to adhere to the Linux kernel coding style. Signed-off-by: Mahad Ibrahim Signed-off-by: Sakari Ailus --- .../hive_isp_css_include/host/mmu_public.h | 59 +++++++++---------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h index 611755e88e9f..b321f4101193 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h @@ -11,49 +11,45 @@ #include "device_access.h" #include "assert_support.h" -/* - *! Set the page table base index of MMU[ID] +/** + * mmu_set_page_table_base_index() - Set the page table base index of MMU[ID] + * @ID: MMU identifier + * @base_index: page table base index * - *\param ID[in] MMU identifier - *\param base_index[in] page table base index - * - *\return none, MMU[ID].page_table_base_index = base_index + * Return: none, MMU[ID].page_table_base_index = base_index */ void mmu_set_page_table_base_index(const mmu_ID_t ID, const hrt_data base_index); -/* - *! Get the page table base index of MMU[ID] +/** + * mmu_get_page_table_base_index() - Get the page table base index of MMU[ID] + * @ID: MMU identifier * - *\param ID[in] MMU identifier - * - *\return MMU[ID].page_table_base_index + * Return: MMU[ID].page_table_base_index */ hrt_data mmu_get_page_table_base_index(const mmu_ID_t ID); -/* - *! Invalidate the page table cache of MMU[ID] +/** + * mmu_invalidate_cache() - nvalidate the page table cache of MMU[ID] + * @ID: MMU identifier * - *\param ID[in] MMU identifier - * - *\return none + * Return: none */ void mmu_invalidate_cache(const mmu_ID_t ID); -/* - *! Invalidate the page table cache of all MMUs +/** + * mmu_invalidate_cache_all() - Invalidate the page table cache of all MMUs * - *\return none + * Return: none */ void mmu_invalidate_cache_all(void); -/* - *! Write to a control register of MMU[ID] +/** + * mmu_reg_store() - Write to a control register of MMU[ID] + * @ID: MMU identifier + * @reg: register index + * @value: The data to be written * - *\param ID[in] MMU identifier - *\param reg[in] register index - *\param value[in] The data to be written - * - *\return none, MMU[ID].ctrl[reg] = value + * Return: none, MMU[ID].ctrl[reg] = value */ static inline void mmu_reg_store(const mmu_ID_t ID, const unsigned int reg, const hrt_data value) { @@ -62,13 +58,12 @@ static inline void mmu_reg_store(const mmu_ID_t ID, const unsigned int reg, cons ia_css_device_store_uint32(MMU_BASE[ID] + reg * sizeof(hrt_data), value); } -/* - *! Read from a control register of MMU[ID] +/** + * mmu_reg_load() - Read from a control register of MMU[ID] + * @ID: MMU identifier + * @reg: register index * - *\param ID[in] MMU identifier - *\param reg[in] register index - * - *\return MMU[ID].ctrl[reg] + * Return: MMU[ID].ctrl[reg] */ static inline hrt_data mmu_reg_load(const mmu_ID_t ID, const unsigned int reg) { From 2e6b2d43309758b4dff4074c9d6a2cb084066463 Mon Sep 17 00:00:00 2001 From: Mahad Ibrahim Date: Thu, 1 Jan 2026 21:41:17 +0500 Subject: [PATCH 227/394] media: atomisp: Fix block comment coding style in sh_css_param_shading.c Fix checkpatch.pl warnings in sh_css_param_shading.c regarding block comment formatting. The warning was: - Block comments use a trailing */ on a separate line This change also moves text from the first line of block comments to subsequent lines to adhere to the standard Linux kernel coding style. Signed-off-by: Mahad Ibrahim Signed-off-by: Sakari Ailus --- .../media/atomisp/pci/sh_css_param_shading.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c index 9105334c71b1..083ac5713b6d 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c +++ b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c @@ -20,7 +20,8 @@ #include "platform_support.h" -/* Bilinear interpolation on shading tables: +/* + * Bilinear interpolation on shading tables: * For each target point T, we calculate the 4 surrounding source points: * ul (upper left), ur (upper right), ll (lower left) and lr (lower right). * We then calculate the distances from the T to the source points: x0, x1, @@ -116,8 +117,10 @@ crop_and_interpolate(unsigned int cropped_width, */ ty = out_start_row + i * out_cell_size; - /* calculate closest source points in shading table and - make sure they fall within the table */ + /* + * calculate closest source points in shading table and + * make sure they fall within the table + */ src_y0 = ty / (int)in_cell_size; if (in_cell_size < out_cell_size) src_y1 = (ty + out_cell_size) / in_cell_size; @@ -173,7 +176,8 @@ crop_and_interpolate(unsigned int cropped_width, dx0 = tx - sx0; dx1 = sx1 - tx; divx = sx1 - sx0; - /* if we're at the edge, we just use the closest + /* + * if we're at the edge, we just use the closest * point still in the grid. We make up for the divider * in this case by setting the distance to * out_cell_size, since it's actually 0. @@ -291,8 +295,10 @@ prepare_shading_table(const struct ia_css_shading_table *in_table, input_width = min(input_width, in_table->sensor_width); input_height = min(input_height, in_table->sensor_height); - /* This prepare_shading_table() function is called only in legacy API (not in new API). - Then, the legacy shading table width and height should be used. */ + /* + * This prepare_shading_table() function is called only in legacy API (not in new API). + * Then, the legacy shading table width and height should be used. + */ table_width = binary->sctbl_width_per_color; table_height = binary->sctbl_height; From 2ebf05cc41603c23959752fa6fabb7f24444bae5 Mon Sep 17 00:00:00 2001 From: Karthikey Kadati Date: Thu, 15 Jan 2026 08:42:07 +0530 Subject: [PATCH 228/394] media: atomisp: replace ia_css_region with v4l2_rect The struct ia_css_region definition is redundant as struct v4l2_rect provides the same functionality (left, top, width, height) and is the standard V4L2 type. Replace usage of ia_css_region with v4l2_rect in ia_css_dz_config and remove the definition of ia_css_region from ia_css_types.h. Also remove historical comments referencing the addition of zoom_region and include to support the v4l2_rect type. Signed-off-by: Karthikey Kadati Signed-off-by: Sakari Ailus --- .../staging/media/atomisp/pci/atomisp_cmd.c | 118 +++++++++--------- .../staging/media/atomisp/pci/ia_css_types.h | 14 +-- .../staging/media/atomisp/pci/sh_css_params.c | 18 ++- 3 files changed, 67 insertions(+), 83 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index e401c23a7575..7ccd4205ed1e 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -1768,15 +1768,13 @@ int atomisp_calculate_real_zoom_region(struct atomisp_sub_device *asd, return -EINVAL; } - if (dz_config->zoom_region.resolution.width - == asd->sensor_array_res.width - || dz_config->zoom_region.resolution.height - == asd->sensor_array_res.height) { + if (dz_config->zoom_region.width == asd->sensor_array_res.width || + dz_config->zoom_region.height == asd->sensor_array_res.height) { /*no need crop region*/ - dz_config->zoom_region.origin.x = 0; - dz_config->zoom_region.origin.y = 0; - dz_config->zoom_region.resolution.width = eff_res.width; - dz_config->zoom_region.resolution.height = eff_res.height; + dz_config->zoom_region.left = 0; + dz_config->zoom_region.top = 0; + dz_config->zoom_region.width = eff_res.width; + dz_config->zoom_region.height = eff_res.height; return 0; } @@ -1787,18 +1785,14 @@ int atomisp_calculate_real_zoom_region(struct atomisp_sub_device *asd, */ if (!IS_ISP2401) { - dz_config->zoom_region.origin.x = dz_config->zoom_region.origin.x - * eff_res.width - / asd->sensor_array_res.width; - dz_config->zoom_region.origin.y = dz_config->zoom_region.origin.y - * eff_res.height - / asd->sensor_array_res.height; - dz_config->zoom_region.resolution.width = dz_config->zoom_region.resolution.width - * eff_res.width - / asd->sensor_array_res.width; - dz_config->zoom_region.resolution.height = dz_config->zoom_region.resolution.height - * eff_res.height - / asd->sensor_array_res.height; + dz_config->zoom_region.left = dz_config->zoom_region.left * + eff_res.width / asd->sensor_array_res.width; + dz_config->zoom_region.top = dz_config->zoom_region.top * + eff_res.height / asd->sensor_array_res.height; + dz_config->zoom_region.width = dz_config->zoom_region.width * + eff_res.width / asd->sensor_array_res.width; + dz_config->zoom_region.height = dz_config->zoom_region.height * + eff_res.height / asd->sensor_array_res.height; /* * Set same ratio of crop region resolution and current pipe output * resolution @@ -1825,62 +1819,62 @@ int atomisp_calculate_real_zoom_region(struct atomisp_sub_device *asd, - asd->sensor_array_res.width * out_res.height / out_res.width; h_offset = h_offset / 2; - if (dz_config->zoom_region.origin.y < h_offset) - dz_config->zoom_region.origin.y = 0; + if (dz_config->zoom_region.top < h_offset) + dz_config->zoom_region.top = 0; else - dz_config->zoom_region.origin.y = dz_config->zoom_region.origin.y - h_offset; + dz_config->zoom_region.top = + dz_config->zoom_region.top - h_offset; w_offset = 0; } else { w_offset = asd->sensor_array_res.width - asd->sensor_array_res.height * out_res.width / out_res.height; w_offset = w_offset / 2; - if (dz_config->zoom_region.origin.x < w_offset) - dz_config->zoom_region.origin.x = 0; + if (dz_config->zoom_region.left < w_offset) + dz_config->zoom_region.left = 0; else - dz_config->zoom_region.origin.x = dz_config->zoom_region.origin.x - w_offset; + dz_config->zoom_region.left = + dz_config->zoom_region.left - w_offset; h_offset = 0; } - dz_config->zoom_region.origin.x = dz_config->zoom_region.origin.x - * eff_res.width - / (asd->sensor_array_res.width - 2 * w_offset); - dz_config->zoom_region.origin.y = dz_config->zoom_region.origin.y - * eff_res.height - / (asd->sensor_array_res.height - 2 * h_offset); - dz_config->zoom_region.resolution.width = dz_config->zoom_region.resolution.width - * eff_res.width - / (asd->sensor_array_res.width - 2 * w_offset); - dz_config->zoom_region.resolution.height = dz_config->zoom_region.resolution.height - * eff_res.height - / (asd->sensor_array_res.height - 2 * h_offset); + dz_config->zoom_region.left = dz_config->zoom_region.left * + eff_res.width / + (asd->sensor_array_res.width - 2 * w_offset); + dz_config->zoom_region.top = dz_config->zoom_region.top * + eff_res.height / + (asd->sensor_array_res.height - 2 * h_offset); + dz_config->zoom_region.width = dz_config->zoom_region.width * + eff_res.width / + (asd->sensor_array_res.width - 2 * w_offset); + dz_config->zoom_region.height = dz_config->zoom_region.height * + eff_res.height / + (asd->sensor_array_res.height - 2 * h_offset); } - if (out_res.width * dz_config->zoom_region.resolution.height - > dz_config->zoom_region.resolution.width * out_res.height) { - dz_config->zoom_region.resolution.height = - dz_config->zoom_region.resolution.width - * out_res.height / out_res.width; + if (out_res.width * dz_config->zoom_region.height > + dz_config->zoom_region.width * out_res.height) { + dz_config->zoom_region.height = dz_config->zoom_region.width * + out_res.height / out_res.width; } else { - dz_config->zoom_region.resolution.width = - dz_config->zoom_region.resolution.height - * out_res.width / out_res.height; + dz_config->zoom_region.width = dz_config->zoom_region.height * + out_res.width / out_res.height; } dev_dbg(asd->isp->dev, "%s crop region:(%d,%d),(%d,%d) eff_res(%d, %d) array_size(%d,%d) out_res(%d, %d)\n", - __func__, dz_config->zoom_region.origin.x, - dz_config->zoom_region.origin.y, - dz_config->zoom_region.resolution.width, - dz_config->zoom_region.resolution.height, + __func__, dz_config->zoom_region.left, + dz_config->zoom_region.top, + dz_config->zoom_region.width, + dz_config->zoom_region.height, eff_res.width, eff_res.height, asd->sensor_array_res.width, asd->sensor_array_res.height, out_res.width, out_res.height); - if ((dz_config->zoom_region.origin.x + - dz_config->zoom_region.resolution.width + if ((dz_config->zoom_region.left + + dz_config->zoom_region.width > eff_res.width) || - (dz_config->zoom_region.origin.y + - dz_config->zoom_region.resolution.height + (dz_config->zoom_region.top + + dz_config->zoom_region.height > eff_res.height)) return -EINVAL; @@ -1905,10 +1899,10 @@ static bool atomisp_check_zoom_region( config.width = asd->sensor_array_res.width; config.height = asd->sensor_array_res.height; - w = dz_config->zoom_region.origin.x + - dz_config->zoom_region.resolution.width; - h = dz_config->zoom_region.origin.y + - dz_config->zoom_region.resolution.height; + w = dz_config->zoom_region.left + + dz_config->zoom_region.width; + h = dz_config->zoom_region.top + + dz_config->zoom_region.height; if ((w <= config.width) && (h <= config.height) && w > 0 && h > 0) flag = true; @@ -1916,10 +1910,10 @@ static bool atomisp_check_zoom_region( /* setting error zoom region */ dev_err(asd->isp->dev, "%s zoom region ERROR:dz_config:(%d,%d),(%d,%d)array_res(%d, %d)\n", - __func__, dz_config->zoom_region.origin.x, - dz_config->zoom_region.origin.y, - dz_config->zoom_region.resolution.width, - dz_config->zoom_region.resolution.height, + __func__, dz_config->zoom_region.left, + dz_config->zoom_region.top, + dz_config->zoom_region.width, + dz_config->zoom_region.height, config.width, config.height); return flag; diff --git a/drivers/staging/media/atomisp/pci/ia_css_types.h b/drivers/staging/media/atomisp/pci/ia_css_types.h index 676d7e20b282..2b7db9cda23a 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_types.h +++ b/drivers/staging/media/atomisp/pci/ia_css_types.h @@ -15,6 +15,8 @@ * directly but still need to forward parameters for it. */ +#include + #include #include "ia_css_frac.h" @@ -427,14 +429,6 @@ struct ia_css_point { s32 y; /** y coordinate */ }; -/** - * This specifies the region - */ -struct ia_css_region { - struct ia_css_point origin; /** Starting point coordinates for the region */ - struct ia_css_resolution resolution; /** Region resolution */ -}; - /** * Digital zoom: * This feature is currently available only for video, but will become @@ -442,7 +436,7 @@ struct ia_css_region { * Set the digital zoom factor, this is a logarithmic scale. The actual zoom * factor will be 64/x. * Setting dx or dy to 0 disables digital zoom for that direction. - * New API change for Digital zoom:(added struct ia_css_region zoom_region) + * * zoom_region specifies the origin of the zoom region and width and * height of that region. * origin : This is the coordinate (x,y) within the effective input resolution @@ -455,7 +449,7 @@ struct ia_css_region { struct ia_css_dz_config { u32 dx; /** Horizontal zoom factor */ u32 dy; /** Vertical zoom factor */ - struct ia_css_region zoom_region; /** region for zoom */ + struct v4l2_rect zoom_region; /** region for zoom */ }; /* The still capture mode, this can be RAW (simply copy sensor input to DDR), diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c index 8cbb5d598005..b8d492115196 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/sh_css_params.c @@ -657,11 +657,7 @@ static const int zoom_table[4][HRT_GDC_N] = { static const struct ia_css_dz_config default_dz_config = { HRT_GDC_N, HRT_GDC_N, - { - \ - {0, 0}, \ - {0, 0}, \ - } + { 0, 0, 0, 0 } }; static const struct ia_css_vector default_motion_config = { @@ -1210,8 +1206,8 @@ ia_css_process_zoom_and_motion( } assert(stage->stage_num < SH_CSS_MAX_STAGES); - if (params->dz_config.zoom_region.resolution.width == 0 && - params->dz_config.zoom_region.resolution.height == 0) { + if (params->dz_config.zoom_region.width == 0 && + params->dz_config.zoom_region.height == 0) { sh_css_update_uds_and_crop_info( &info->sp, &binary->in_frame_info, @@ -4092,10 +4088,10 @@ sh_css_update_uds_and_crop_info_based_on_zoom_region( assert(motion_vector); assert(uds); assert(sp_out_crop_pos); - x0 = zoom->zoom_region.origin.x; - y0 = zoom->zoom_region.origin.y; - x1 = zoom->zoom_region.resolution.width + x0; - y1 = zoom->zoom_region.resolution.height + y0; + x0 = zoom->zoom_region.left; + y0 = zoom->zoom_region.top; + x1 = zoom->zoom_region.width + x0; + y1 = zoom->zoom_region.height + y0; if ((x0 > x1) || (y0 > y1) || (x1 > pipe_in_res.width) || (y1 > pipe_in_res.height)) return -EINVAL; From 628f763aee0047ff44974388d6f70f75a763026b Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Thu, 2 Apr 2026 20:23:19 -0400 Subject: [PATCH 229/394] media: atomisp: gc2235: fix UAF and memory leak gc2235_probe() handles its error paths incorrectly. If media_entity_pads_init() fails, gc2235_remove() is called, which tears down the subdev and frees dev, but then still falls through to atomisp_register_i2c_module(). This results in use-after-free. If atomisp_register_i2c_module() fails, the media entity and control handler are left initialized and dev is leaked. gc2235_remove() unconditionally calls media_entity_cleanup() and v4l2_ctrl_handler_free(), but these are not initialized at every error path in gc2235_probe(). Replace gc2235_remove() calls in the probe error paths with explicit unwind labels that free only the resources initialized at each point of failure, in reverse order of initialization. Fixes: a49d25364dfb ("staging/atomisp: Add support for the Intel IPU v2") Signed-off-by: Yuho Choi Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus --- .../media/atomisp/i2c/atomisp-gc2235.c | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c index d3414312e1de..998c9f46bd06 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c @@ -809,7 +809,7 @@ static int gc2235_probe(struct i2c_client *client) ret = gc2235_s_config(&dev->sd, client->irq, gcpdev); if (ret) - goto out_free; + goto err_unregister_subdev; dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; dev->pad.flags = MEDIA_PAD_FL_SOURCE; @@ -818,18 +818,16 @@ static int gc2235_probe(struct i2c_client *client) ret = v4l2_ctrl_handler_init(&dev->ctrl_handler, ARRAY_SIZE(gc2235_controls)); - if (ret) { - gc2235_remove(client); - return ret; - } + if (ret) + goto err_csi_cfg; for (i = 0; i < ARRAY_SIZE(gc2235_controls); i++) v4l2_ctrl_new_custom(&dev->ctrl_handler, &gc2235_controls[i], NULL); if (dev->ctrl_handler.error) { - gc2235_remove(client); - return dev->ctrl_handler.error; + ret = dev->ctrl_handler.error; + goto err_ctrl_handler; } /* Use same lock for controls as for everything else. */ @@ -838,14 +836,23 @@ static int gc2235_probe(struct i2c_client *client) ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); if (ret) - gc2235_remove(client); + goto err_ctrl_handler; - return atomisp_register_i2c_module(&dev->sd, gcpdev); + ret = atomisp_register_i2c_module(&dev->sd, gcpdev); + if (ret) + goto err_media_cleanup; -out_free: + return 0; + +err_media_cleanup: + media_entity_cleanup(&dev->sd.entity); +err_ctrl_handler: + v4l2_ctrl_handler_free(&dev->ctrl_handler); +err_csi_cfg: + dev->platform_data->csi_cfg(&dev->sd, 0); +err_unregister_subdev: v4l2_device_unregister_subdev(&dev->sd); kfree(dev); - return ret; } From ddc87e50b9611c509ffbeeef5e5f74282cbce709 Mon Sep 17 00:00:00 2001 From: Abinash Singh Date: Sun, 22 Mar 2026 12:36:14 +0530 Subject: [PATCH 230/394] staging: media: atomisp: replace uint32_t with u32 Replace usage of uint32_t with u32 to comply with kernel coding style guidelines. Reported by checkpatch.pl. CHECK: Prefer kernel type 'u32' over 'uint32_t' Signed-off-by: Abinash Singh Signed-off-by: Sakari Ailus --- .../staging/media/atomisp/pci/atomisp_subdev.c | 18 +++++++++--------- .../staging/media/atomisp/pci/atomisp_subdev.h | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c index cef44ec9ebde..9de9cd884d99 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c @@ -180,8 +180,8 @@ static int isp_subdev_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int isp_subdev_validate_rect(struct v4l2_subdev *sd, uint32_t pad, - uint32_t target) +static int isp_subdev_validate_rect(struct v4l2_subdev *sd, u32 pad, + u32 target) { switch (pad) { case ATOMISP_SUBDEV_PAD_SINK: @@ -203,8 +203,8 @@ static int isp_subdev_validate_rect(struct v4l2_subdev *sd, uint32_t pad, struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - u32 which, uint32_t pad, - uint32_t target) + u32 which, u32 pad, + u32 target) { struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); @@ -229,8 +229,8 @@ struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *atomisp_subdev_get_ffmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, uint32_t which, - uint32_t pad) + struct v4l2_subdev_state *sd_state, u32 which, + u32 pad) { struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); @@ -242,7 +242,7 @@ struct v4l2_mbus_framefmt static void isp_get_fmt_rect(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - uint32_t which, + u32 which, struct v4l2_mbus_framefmt **ffmt, struct v4l2_rect *crop[ATOMISP_SUBDEV_PADS_NUM], struct v4l2_rect *comp[ATOMISP_SUBDEV_PADS_NUM]) @@ -291,7 +291,7 @@ static const char *atomisp_pad_str(unsigned int pad) int atomisp_subdev_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - u32 which, uint32_t pad, uint32_t target, + u32 which, u32 pad, u32 target, u32 flags, struct v4l2_rect *r) { struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); @@ -467,7 +467,7 @@ static int isp_subdev_set_selection(struct v4l2_subdev *sd, void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - uint32_t which, + u32 which, u32 pad, struct v4l2_mbus_framefmt *ffmt) { struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd); diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.h b/drivers/staging/media/atomisp/pci/atomisp_subdev.h index e1d0168cb91d..b12bb65be3f2 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.h +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.h @@ -315,20 +315,20 @@ bool atomisp_subdev_format_conversion(struct atomisp_sub_device *asd); /* Get pointer to appropriate format */ struct v4l2_mbus_framefmt *atomisp_subdev_get_ffmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, uint32_t which, - uint32_t pad); + struct v4l2_subdev_state *sd_state, u32 which, + u32 pad); struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - u32 which, uint32_t pad, - uint32_t target); + u32 which, u32 pad, + u32 target); int atomisp_subdev_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - u32 which, uint32_t pad, uint32_t target, + u32 which, u32 pad, u32 target, u32 flags, struct v4l2_rect *r); /* Actually set the format */ void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, - uint32_t which, + u32 which, u32 pad, struct v4l2_mbus_framefmt *ffmt); void atomisp_subdev_cleanup_pending_events(struct atomisp_sub_device *asd); From c06b73f1e19176c0534cd4e1ee0afc044e9774d6 Mon Sep 17 00:00:00 2001 From: Anushka Badhe Date: Wed, 25 Mar 2026 18:54:34 +0530 Subject: [PATCH 231/394] staging: media: atomisp: pci: fix split GP_TIMER_BASE declaration Merge declaration of const GP_TIMER_BASE split across 2 lines to improve readability. Signed-off-by: Anushka Badhe Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/system_local.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/system_local.c b/drivers/staging/media/atomisp/pci/system_local.c index a8a93760d5b1..69bcd557a821 100644 --- a/drivers/staging/media/atomisp/pci/system_local.c +++ b/drivers/staging/media/atomisp/pci/system_local.c @@ -86,8 +86,7 @@ const hrt_address GP_DEVICE_BASE[N_GP_DEVICE_ID] = { /*GP TIMER , all timer registers are inter-twined, * so, having multiple base addresses for * different timers does not help*/ -const hrt_address GP_TIMER_BASE = - (hrt_address)0x0000000000000600ULL; +const hrt_address GP_TIMER_BASE = (hrt_address)0x0000000000000600ULL; /* GPIO */ const hrt_address GPIO_BASE[N_GPIO_ID] = { From e4e6bc933a63a2a4ed576e6449e455993f79cedd Mon Sep 17 00:00:00 2001 From: "Jose A. Perez de Azpillaga" Date: Sun, 5 Apr 2026 11:30:43 +0200 Subject: [PATCH 232/394] media: atomisp: gate ref and tnr frame config behind ISP enable flags The FIXME comment noted that delay_frames can be NULL for certain pipeline configurations, without knowing why. The reason is that when a binary does not enable ref_frame, delay frame allocation is intentionally skipped to save memory, leaving the pointers NULL by design. The ISP feature flags in binary->info->sp.enable accurately reflect which features are active for a given binary. Using enable.ref_frame and enable.tnr as the predicate for their respective configuration steps ensures the configuration path stays in sync with what was actually built into the pipeline Signed-off-by: Jose A. Perez de Azpillaga Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css_sp.c | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.c b/drivers/staging/media/atomisp/pci/sh_css_sp.c index 6da151e7a873..abdffff41ae2 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_sp.c +++ b/drivers/staging/media/atomisp/pci/sh_css_sp.c @@ -775,9 +775,13 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline, ret = ia_css_fpn_configure(binary, &binary->in_frame_info); if (ret) return ret; - ret = ia_css_crop_configure(binary, ia_css_frame_get_info(args->delay_frames[0])); - if (ret) - return ret; + + if (binary->info->sp.enable.ref_frame) { + ret = ia_css_crop_configure(binary, ia_css_frame_get_info(args->delay_frames[0])); + if (ret) + return ret; + } + ret = ia_css_qplane_configure(pipeline, binary, &binary->in_frame_info); if (ret) return ret; @@ -807,22 +811,18 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline, if (ret) return ret; - /* - * FIXME: args->delay_frames can be NULL here - * - * Somehow, the driver at the Intel Atom Yocto tree doesn't seem to - * suffer from the same issue. - * - * Anyway, the function below should now handle a NULL delay_frames - * without crashing, but the pipeline should likely be built without - * adding it at the first place (or there are a hidden bug somewhere) - */ - ret = ia_css_ref_configure(binary, args->delay_frames, pipeline->dvs_frame_delay); - if (ret) - return ret; - ret = ia_css_tnr_configure(binary, args->tnr_frames); - if (ret) - return ret; + if (binary->info->sp.enable.ref_frame) { + ret = ia_css_ref_configure(binary, args->delay_frames, pipeline->dvs_frame_delay); + if (ret) + return ret; + } + + if (binary->info->sp.enable.tnr) { + ret = ia_css_tnr_configure(binary, args->tnr_frames); + if (ret) + return ret; + } + return ia_css_bayer_io_config(binary, args); } From 7d0e5f2ee1dc48def2e937415bf4dae3789767f9 Mon Sep 17 00:00:00 2001 From: "Jose A. Perez de Azpillaga" Date: Sun, 5 Apr 2026 11:30:44 +0200 Subject: [PATCH 233/394] media: atomisp: remove redundant call to ia_css_output0_configure() The function configure_isp_from_args() contained a duplicate call to ia_css_output0_configure() using the same output frame index. Remove the redundant call to simplify the configuration path. The ia_css_output0_configure() function acts as a configuration setter. It populates a struct ia_css_output0_configuration from the frame info and caches it in the binary parameters. Calling it twice with the same out_frame[0] pointer merely overwrites the exact same state with identical values. It has no cumulative state, neither does its order matter relative to ia_css_copy_output_configure(). ia_css_configure_output0() writes into binary->mem_params.params[], a software-side DMEM parameter buffer in kernel memory. The ISP firmware receives these parameters later as a batch, not at the time of the call. Calling a pure memory write twice with the same pointer and same value simply overwrites the same location with identical data, there is no hardware interaction that could require repetition. Signed-off-by: Jose A. Perez de Azpillaga Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css_sp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.c b/drivers/staging/media/atomisp/pci/sh_css_sp.c index abdffff41ae2..2beb7168517f 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_sp.c +++ b/drivers/staging/media/atomisp/pci/sh_css_sp.c @@ -792,9 +792,6 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline, if (ret) return ret; ret = ia_css_copy_output_configure(binary, args->copy_output); - if (ret) - return ret; - ret = ia_css_output0_configure(binary, ia_css_frame_get_info(args->out_frame[0])); if (ret) return ret; ret = ia_css_iterator_configure(binary, ia_css_frame_get_info(args->in_frame)); From 7488f3e0732b9226dbcaa09c74d14443a14de7a5 Mon Sep 17 00:00:00 2001 From: Mohamad El Harake Date: Fri, 10 Apr 2026 00:41:58 +0300 Subject: [PATCH 234/394] media: atomisp: avoid ACPI package count underflow in gmin_cfg_get_dsm gmin_cfg_get_dsm() iterates over ACPI _DSM package elements as key/value pairs using obj->package.count - 1 as the loop bound. If package.count is 0, the subtraction underflows and may lead to out-of-bounds access. Use i + 1 < obj->package.count instead. Signed-off-by: Mohamad El Harake Reviewed-by: Hans de Goede Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c index ba61cc28fac1..cca91c6d71a5 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c @@ -113,7 +113,7 @@ static char *gmin_cfg_get_dsm(struct acpi_device *adev, const char *key) if (!obj) return NULL; - for (i = 0; i < obj->package.count - 1; i += 2) { + for (i = 0; i + 1 < obj->package.count; i += 2) { key_el = &obj->package.elements[i + 0]; val_el = &obj->package.elements[i + 1]; From 79b29f7f26256ad16d2af7a53571c6d2ee2ffd60 Mon Sep 17 00:00:00 2001 From: Michael Ugrin Date: Sat, 11 Apr 2026 10:34:05 -0700 Subject: [PATCH 235/394] staging: media: atomisp: use umin() for strscpy size arguments Replace open-coded ternary min expressions with umin() in strscpy() calls, as suggested by Dan Carpenter. Signed-off-by: Michael Ugrin Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus --- .../atomisp/pci/runtime/debug/src/ia_css_debug.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c index 58d99abe70aa..5113aa5973f3 100644 --- a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c +++ b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c @@ -1255,8 +1255,7 @@ ia_css_debug_pipe_graph_dump_stage( while (ei[p] != ',') p--; /* Last comma found, copy till that comma */ - strscpy(enable_info1, ei, - p > sizeof(enable_info1) ? sizeof(enable_info1) : p); + strscpy(enable_info1, ei, umin(p, sizeof(enable_info1))); ei += p + 1; l = strlen(ei); @@ -1267,8 +1266,7 @@ ia_css_debug_pipe_graph_dump_stage( * it is not guaranteed dword aligned */ - strscpy(enable_info2, ei, - l > sizeof(enable_info2) ? sizeof(enable_info2) : l); + strscpy(enable_info2, ei, umin(l, sizeof(enable_info2))); snprintf(enable_info, sizeof(enable_info), "%s\\n%s", enable_info1, enable_info2); @@ -1279,8 +1277,7 @@ ia_css_debug_pipe_graph_dump_stage( while (ei[p] != ',') p--; - strscpy(enable_info2, ei, - p > sizeof(enable_info2) ? sizeof(enable_info2) : p); + strscpy(enable_info2, ei, umin(p, sizeof(enable_info2))); ei += p + 1; l = strlen(ei); @@ -1302,7 +1299,7 @@ ia_css_debug_pipe_graph_dump_stage( while (ei[p] != ',') p--; strscpy(enable_info3, ei, - p > sizeof(enable_info3) ? sizeof(enable_info3) : p); + umin(p, sizeof(enable_info3))); ei += p + 1; strscpy(enable_info3, ei, sizeof(enable_info3)); From 9087395a383212ab1beaefcbe3b57ed131c7823d Mon Sep 17 00:00:00 2001 From: "Jose A. Perez de Azpillaga" Date: Thu, 26 Mar 2026 22:34:07 +0100 Subject: [PATCH 236/394] staging: media: atomisp: fix loop shadowing in ia_css_stream_destroy() The nested loop inside the IS_ISP2401 block incorrectly uses the same variable 'i' as the outer loop. This shadows the outer loop variable and causes premature termination or skipped array elements. Change the inner loop to use a new variable 'j' to prevent this. Fixes: 113401c67386 ("media: atomisp: sh_css: Removed #ifdef ISP2401 to make code generic") Signed-off-by: Jose A. Perez de Azpillaga Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index f40b6912c14b..456c0b2df231 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -8190,7 +8190,7 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, int ia_css_stream_destroy(struct ia_css_stream *stream) { - int i; + int i, j; int err = 0; IA_CSS_ENTER_PRIVATE("stream = %p", stream); @@ -8221,10 +8221,10 @@ ia_css_stream_destroy(struct ia_css_stream *stream) sp_pipeline_input_terminal = &sh_css_sp_group.pipe_io[sp_thread_id].input; - for (i = 0; i < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; i++) { + for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) { ia_css_isys_stream_h isys_stream = - &sp_pipeline_input_terminal->context.virtual_input_system_stream[i]; - if (stream->config.isys_config[i].valid && isys_stream->valid) + &sp_pipeline_input_terminal->context.virtual_input_system_stream[j]; + if (stream->config.isys_config[j].valid && isys_stream->valid) ia_css_isys_stream_destroy(isys_stream); } } From d133bd6ac9beb934875f924e61900990ceb38678 Mon Sep 17 00:00:00 2001 From: "Jose A. Perez de Azpillaga" Date: Thu, 26 Mar 2026 22:34:08 +0100 Subject: [PATCH 237/394] staging: media: atomisp: extract ISP2401 cleanup into helper function To reduce indentation and improve the readability of ia_css_stream_destroy(), extract the ISP2401-specific cleanup block into a new static helper function, ia_css_stream_destroy_isp2401(). Signed-off-by: Jose A. Perez de Azpillaga Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css.c | 89 +++++++++++----------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 456c0b2df231..dcfb602c53e3 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -8187,10 +8187,53 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, return err; } +static void ia_css_stream_destroy_isp2401(struct ia_css_stream *stream) +{ + int i, j; + + for (i = 0; i < stream->num_pipes; i++) { + struct ia_css_pipe *entry = stream->pipes[i]; + unsigned int sp_thread_id; + struct sh_css_sp_pipeline_terminal *terminal; + + assert(entry); + if (entry) { + if (!ia_css_pipeline_get_sp_thread_id( + ia_css_pipe_get_pipe_num(entry), &sp_thread_id)) + return; + + terminal = &sh_css_sp_group.pipe_io[sp_thread_id].input; + + for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) { + ia_css_isys_stream_h isys_stream = + &terminal->context.virtual_input_system_stream[j]; + if (stream->config.isys_config[j].valid && isys_stream->valid) + ia_css_isys_stream_destroy(isys_stream); + } + } + } + + if (stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) { + for (i = 0; i < stream->num_pipes; i++) { + struct ia_css_pipe *entry = stream->pipes[i]; + /* + * free any mipi frames that are remaining: + * some test stream create-destroy cycles do + * not generate output frames + * and the mipi buffer is not freed in the + * deque function + */ + if (entry) + free_mipi_frames(entry); + } + } + stream_unregister_with_csi_rx(stream); +} + int ia_css_stream_destroy(struct ia_css_stream *stream) { - int i, j; + int i; int err = 0; IA_CSS_ENTER_PRIVATE("stream = %p", stream); @@ -8204,48 +8247,8 @@ ia_css_stream_destroy(struct ia_css_stream *stream) if ((stream->last_pipe) && ia_css_pipeline_is_mapped(stream->last_pipe->pipe_num)) { - if (IS_ISP2401) { - for (i = 0; i < stream->num_pipes; i++) { - struct ia_css_pipe *entry = stream->pipes[i]; - unsigned int sp_thread_id; - struct sh_css_sp_pipeline_terminal *sp_pipeline_input_terminal; - - assert(entry); - if (entry) { - /* get the SP thread id */ - if (!ia_css_pipeline_get_sp_thread_id( - ia_css_pipe_get_pipe_num(entry), &sp_thread_id)) - return -EINVAL; - - /* get the target input terminal */ - sp_pipeline_input_terminal = - &sh_css_sp_group.pipe_io[sp_thread_id].input; - - for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) { - ia_css_isys_stream_h isys_stream = - &sp_pipeline_input_terminal->context.virtual_input_system_stream[j]; - if (stream->config.isys_config[j].valid && isys_stream->valid) - ia_css_isys_stream_destroy(isys_stream); - } - } - } - - if (stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) { - for (i = 0; i < stream->num_pipes; i++) { - struct ia_css_pipe *entry = stream->pipes[i]; - /* - * free any mipi frames that are remaining: - * some test stream create-destroy cycles do - * not generate output frames - * and the mipi buffer is not freed in the - * deque function - */ - if (entry) - free_mipi_frames(entry); - } - } - stream_unregister_with_csi_rx(stream); - } + if (IS_ISP2401) + ia_css_stream_destroy_isp2401(stream); for (i = 0; i < stream->num_pipes; i++) { struct ia_css_pipe *curr_pipe = stream->pipes[i]; From f14d52b67e63993e2d184da5f49d884d527dd475 Mon Sep 17 00:00:00 2001 From: "Jose A. Perez de Azpillaga" Date: Thu, 26 Mar 2026 22:34:09 +0100 Subject: [PATCH 238/394] staging: media: atomisp: improve cleanup robustness in ia_css_stream_destroy_isp2401() Remove the 'assert(entry)' call. In the atomisp driver, assert() is a wrapper around BUG(), which intentionally crashes the entire kernel. This is dangerous and inappropriate for a simple null pointer check during stream teardown. Replace it with a safe 'if (!entry) continue;' check. Change the early 'return' to a 'continue'. In a destruction path, it is better to proceed with cleaning up as many resources as possible rather than aborting early, which would result in memory leaks for the remaining pipes. Signed-off-by: Jose A. Perez de Azpillaga Reviewed-by: Dan Carpenter Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css.c | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index dcfb602c53e3..c6838772553d 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -8196,26 +8196,25 @@ static void ia_css_stream_destroy_isp2401(struct ia_css_stream *stream) unsigned int sp_thread_id; struct sh_css_sp_pipeline_terminal *terminal; - assert(entry); - if (entry) { - if (!ia_css_pipeline_get_sp_thread_id( + if (!entry) + continue; + + if (!ia_css_pipeline_get_sp_thread_id( ia_css_pipe_get_pipe_num(entry), &sp_thread_id)) - return; + continue; - terminal = &sh_css_sp_group.pipe_io[sp_thread_id].input; + terminal = &sh_css_sp_group.pipe_io[sp_thread_id].input; - for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) { - ia_css_isys_stream_h isys_stream = - &terminal->context.virtual_input_system_stream[j]; - if (stream->config.isys_config[j].valid && isys_stream->valid) - ia_css_isys_stream_destroy(isys_stream); - } + for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) { + ia_css_isys_stream_h isys_stream = + &terminal->context.virtual_input_system_stream[j]; + if (stream->config.isys_config[j].valid && isys_stream->valid) + ia_css_isys_stream_destroy(isys_stream); } } if (stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) { for (i = 0; i < stream->num_pipes; i++) { - struct ia_css_pipe *entry = stream->pipes[i]; /* * free any mipi frames that are remaining: * some test stream create-destroy cycles do @@ -8223,8 +8222,8 @@ static void ia_css_stream_destroy_isp2401(struct ia_css_stream *stream) * and the mipi buffer is not freed in the * deque function */ - if (entry) - free_mipi_frames(entry); + if (stream->pipes[i]) + free_mipi_frames(stream->pipes[i]); } } stream_unregister_with_csi_rx(stream); From f4d51e55dd47ef467fbe37d8575e20eee41b092d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 25 Mar 2026 13:59:43 +0100 Subject: [PATCH 239/394] staging: media: atomisp: reduce load_primary_binaries() stack usage The load_primary_binaries() function is overly complex and has som large variables on the stack, which can cause warnings depending on CONFIG_FRAME_WARN setting: drivers/staging/media/atomisp/pci/sh_css.c: In function 'load_primary_binaries': drivers/staging/media/atomisp/pci/sh_css.c:5260:1: error: the frame size of 1560 bytes is larger than 1536 bytes [-Werror=frame-larger-than=] Half of the stack usage is for the prim_descr[] array, but only one member of the array is used at any given time. Reduce the stack usage by turning the array into a single structure. Fixes: a49d25364dfb ("staging/atomisp: Add support for the Intel IPU v2") Cc: stable@vger.kernel.org Signed-off-by: Arnd Bergmann Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index c6838772553d..792379407828 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -5019,7 +5019,6 @@ static int load_primary_binaries( struct ia_css_capture_settings *mycs; unsigned int i; bool need_extra_yuv_scaler = false; - struct ia_css_binary_descr prim_descr[MAX_NUM_PRIMARY_STAGES]; IA_CSS_ENTER_PRIVATE(""); assert(pipe); @@ -5188,15 +5187,16 @@ static int load_primary_binaries( /* Primary */ for (i = 0; i < mycs->num_primary_stage; i++) { + struct ia_css_binary_descr prim_descr; struct ia_css_frame_info *local_vf_info = NULL; if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0] && (i == mycs->num_primary_stage - 1)) local_vf_info = &vf_info; - ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr[i], + ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr, &prim_in_info, &prim_out_info, local_vf_info, i); - err = ia_css_binary_find(&prim_descr[i], &mycs->primary_binary[i]); + err = ia_css_binary_find(&prim_descr, &mycs->primary_binary[i]); if (err) { IA_CSS_LEAVE_ERR_PRIVATE(err); return err; From 016a8735b8bab9a5a6c9c4b0f10e479e6c315315 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 20 Mar 2026 22:54:19 +0100 Subject: [PATCH 240/394] media: atomisp: Drop unused include This driver includes the legacy header but does not use any symbols from it. Drop the inclusion. Signed-off-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c index 4026e98c5845..236122722703 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include "../../include/linux/atomisp_platform.h" #include "../../include/linux/atomisp_gmin_platform.h" From de1844c0da4ad2987bca07d299402a6c73ba902d Mon Sep 17 00:00:00 2001 From: Zile Xiong Date: Thu, 19 Mar 2026 18:35:45 +0800 Subject: [PATCH 241/394] staging: media: atomisp: hmm: remove unnecessary casts Drop unnecessary casts when accessing vma->vm_private_data. No functional change. Signed-off-by: Zile Xiong Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/hmm/hmm_bo.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c index b91cbd0262d0..0a8f401a1ca1 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c @@ -984,8 +984,7 @@ void hmm_bo_unref(struct hmm_buffer_object *bo) static void hmm_bo_vm_open(struct vm_area_struct *vma) { - struct hmm_buffer_object *bo = - (struct hmm_buffer_object *)vma->vm_private_data; + struct hmm_buffer_object *bo = vma->vm_private_data; check_bo_null_return_void(bo); @@ -1002,8 +1001,7 @@ static void hmm_bo_vm_open(struct vm_area_struct *vma) static void hmm_bo_vm_close(struct vm_area_struct *vma) { - struct hmm_buffer_object *bo = - (struct hmm_buffer_object *)vma->vm_private_data; + struct hmm_buffer_object *bo = vma->vm_private_data; check_bo_null_return_void(bo); From 4d542f256cf2ff23a4a2c5411cbdf9af0191b8ca Mon Sep 17 00:00:00 2001 From: Lin YuChen Date: Sat, 14 Mar 2026 01:55:26 +0800 Subject: [PATCH 242/394] staging: media: atomisp: use kmalloc_array() for sh_css_blob_info Replace the open-coded multiplication in kmalloc() with kmalloc_array() to provide overflow protection and improve code readability. Signed-off-by: Lin YuChen Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css_firmware.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_firmware.c b/drivers/staging/media/atomisp/pci/sh_css_firmware.c index 57ecf5549c23..af12df2f9b09 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_firmware.c +++ b/drivers/staging/media/atomisp/pci/sh_css_firmware.c @@ -253,9 +253,9 @@ sh_css_load_firmware(struct device *dev, const char *fw_data, sh_css_num_binaries = file_header->binary_nr; /* Only allocate memory for ISP blob info */ if (sh_css_num_binaries > NUM_OF_SPS) { - sh_css_blob_info = kmalloc( - (sh_css_num_binaries - NUM_OF_SPS) * - sizeof(*sh_css_blob_info), GFP_KERNEL); + sh_css_blob_info = + kmalloc_array(sh_css_num_binaries - NUM_OF_SPS, + sizeof(*sh_css_blob_info), GFP_KERNEL); if (!sh_css_blob_info) return -ENOMEM; } else { From cede4f26b4df8b13dcfe3667b098eceb2ef26eb0 Mon Sep 17 00:00:00 2001 From: Oskar Ray-Frayssinet Date: Sun, 1 Mar 2026 23:30:38 +0100 Subject: [PATCH 243/394] staging: media: atomisp: remove unnecessary else after return in atomisp_cmd.c Remove unnecessary else clause after return statement as the else branch is not needed when the if branch always returns. Signed-off-by: Oskar Ray-Frayssinet Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_cmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index 7ccd4205ed1e..6174177eb869 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -2032,8 +2032,8 @@ static unsigned int long copy_from_compatible(void *to, const void *from, { if (from_user) return copy_from_user(to, (void __user *)from, n); - else - memcpy(to, from, n); + + memcpy(to, from, n); return 0; } From 7222dc8751e0c1a179acd6f8a2c664a7f16081b1 Mon Sep 17 00:00:00 2001 From: Oskar Ray-Frayssinet Date: Sun, 1 Mar 2026 22:44:25 +0100 Subject: [PATCH 244/394] staging: media: atomisp: use __func__ in debug message in atomisp_cmd.c Replace hardcoded function name string with __func__ macro in dev_dbg call as recommended by kernel coding style. Signed-off-by: Oskar Ray-Frayssinet Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index 6174177eb869..6cd500d9fd26 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -4011,7 +4011,7 @@ static int css_input_resolution_changed(struct atomisp_sub_device *asd, struct atomisp_metadata_buf *md_buf = NULL, *_md_buf; unsigned int i; - dev_dbg(asd->isp->dev, "css_input_resolution_changed to %ux%u\n", + dev_dbg(asd->isp->dev, "%s: to %ux%u\n", __func__, ffmt->width, ffmt->height); if (IS_ISP2401) From d178c7ca8fefc28115d35b94c3b1f4d653e34182 Mon Sep 17 00:00:00 2001 From: Feng Ning Date: Sun, 12 Apr 2026 00:05:08 +0000 Subject: [PATCH 245/394] staging: media: atomisp: use array3_size() for overflow-safe allocation Replace open-coded width * height * sizeof() multiplications with array3_size() to prevent integer overflow in buffer allocations. The atomisp driver computes DVS, morphing table, shading table and statistics buffer sizes using unchecked arithmetic. When dimensions are attacker-controlled or simply large, the product can silently wrap, causing kvmalloc() to allocate an undersized buffer. array3_size() saturates to SIZE_MAX on overflow, so kvmalloc() returns NULL instead of succeeding with too few bytes. Signed-off-by: Feng Ning Signed-off-by: Sakari Ailus --- .../media/atomisp/pci/sh_css_param_dvs.c | 11 +++--- .../media/atomisp/pci/sh_css_param_shading.c | 4 ++- .../staging/media/atomisp/pci/sh_css_params.c | 35 +++++++++++-------- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c index 3d2cb2d25fdb..ad2a9b84e232 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c +++ b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c @@ -4,6 +4,7 @@ * Copyright (c) 2015, Intel Corporation. */ +#include #include "sh_css_param_dvs.h" #include #include @@ -48,7 +49,7 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res, } /* Generate Y buffers */ - dvs_config->xcoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t), + dvs_config->xcoords_y = kvmalloc(array3_size(width_y, height_y, sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->xcoords_y) { IA_CSS_ERROR("out of memory"); @@ -56,7 +57,7 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res, goto exit; } - dvs_config->ycoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t), + dvs_config->ycoords_y = kvmalloc(array3_size(width_y, height_y, sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->ycoords_y) { IA_CSS_ERROR("out of memory"); @@ -67,7 +68,8 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res, /* Generate UV buffers */ IA_CSS_LOG("UV W %d H %d", width_uv, height_uv); - dvs_config->xcoords_uv = kvmalloc(width_uv * height_uv * sizeof(uint32_t), + dvs_config->xcoords_uv = kvmalloc(array3_size(width_uv, height_uv, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->xcoords_uv) { IA_CSS_ERROR("out of memory"); @@ -75,7 +77,8 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res, goto exit; } - dvs_config->ycoords_uv = kvmalloc(width_uv * height_uv * sizeof(uint32_t), + dvs_config->ycoords_uv = kvmalloc(array3_size(width_uv, height_uv, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->ycoords_uv) { IA_CSS_ERROR("out of memory"); diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c index 083ac5713b6d..0ac85cdea010 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c +++ b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c @@ -4,6 +4,7 @@ * Copyright (c) 2015, Intel Corporation. */ +#include #include #include @@ -345,7 +346,8 @@ ia_css_shading_table_alloc( me->fraction_bits = 0; for (i = 0; i < IA_CSS_SC_NUM_COLORS; i++) { me->data[i] = - kvmalloc(width * height * sizeof(*me->data[0]), + kvmalloc(array3_size(width, height, + sizeof(*me->data[0])), GFP_KERNEL); if (!me->data[i]) { unsigned int j; diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c index b8d492115196..1c8af03db1c7 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/sh_css_params.c @@ -4,6 +4,7 @@ * Copyright (c) 2015, Intel Corporation. */ +#include #include #include "gdc_device.h" /* gdc_lut_store(), ... */ @@ -1377,11 +1378,11 @@ struct ia_css_morph_table *ia_css_morph_table_allocate( } for (i = 0; i < IA_CSS_MORPH_TABLE_NUM_PLANES; i++) { - me->coordinates_x[i] = kvmalloc(height * width * - sizeof(*me->coordinates_x[i]), + me->coordinates_x[i] = kvmalloc(array3_size(height, width, + sizeof(*me->coordinates_x[i])), GFP_KERNEL); - me->coordinates_y[i] = kvmalloc(height * width * - sizeof(*me->coordinates_y[i]), + me->coordinates_y[i] = kvmalloc(array3_size(height, width, + sizeof(*me->coordinates_y[i])), GFP_KERNEL); if ((!me->coordinates_x[i]) || @@ -4198,13 +4199,17 @@ ia_css_dvs_statistics_allocate(const struct ia_css_dvs_grid_info *grid) goto err; me->grid = *grid; - me->hor_proj = kvmalloc(grid->height * IA_CSS_DVS_NUM_COEF_TYPES * - sizeof(*me->hor_proj), GFP_KERNEL); + me->hor_proj = kvmalloc(array3_size(grid->height, + IA_CSS_DVS_NUM_COEF_TYPES, + sizeof(*me->hor_proj)), + GFP_KERNEL); if (!me->hor_proj) goto err; - me->ver_proj = kvmalloc(grid->width * IA_CSS_DVS_NUM_COEF_TYPES * - sizeof(*me->ver_proj), GFP_KERNEL); + me->ver_proj = kvmalloc(array3_size(grid->width, + IA_CSS_DVS_NUM_COEF_TYPES, + sizeof(*me->ver_proj)), + GFP_KERNEL); if (!me->ver_proj) goto err; @@ -4470,24 +4475,26 @@ ia_css_dvs2_6axis_config_allocate(const struct ia_css_stream *stream) params->pipe_dvs_6axis_config[IA_CSS_PIPE_ID_VIDEO]->height_uv; IA_CSS_LOG("table Y: W %d H %d", width_y, height_y); IA_CSS_LOG("table UV: W %d H %d", width_uv, height_uv); - dvs_config->xcoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t), + dvs_config->xcoords_y = kvmalloc(array3_size(width_y, height_y, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->xcoords_y) goto err; - dvs_config->ycoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t), + dvs_config->ycoords_y = kvmalloc(array3_size(width_y, height_y, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->ycoords_y) goto err; - dvs_config->xcoords_uv = kvmalloc(width_uv * height_uv * - sizeof(uint32_t), + dvs_config->xcoords_uv = kvmalloc(array3_size(width_uv, height_uv, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->xcoords_uv) goto err; - dvs_config->ycoords_uv = kvmalloc(width_uv * height_uv * - sizeof(uint32_t), + dvs_config->ycoords_uv = kvmalloc(array3_size(width_uv, height_uv, + sizeof(uint32_t)), GFP_KERNEL); if (!dvs_config->ycoords_uv) goto err; From 07e475301406cff58281d1ffc9b0ea52bf682a5a Mon Sep 17 00:00:00 2001 From: Chelsy Ratnawat Date: Mon, 25 Aug 2025 03:15:15 -0700 Subject: [PATCH 246/394] media: atomisp: Use string choices helpers Use string_choices.h helpers instead of hard-coded strings. Signed-off-by: Chelsy Ratnawat Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/atomisp_compat_css20.c | 3 ++- drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c | 5 +++-- drivers/staging/media/atomisp/pci/atomisp_v4l2.c | 5 +++-- .../staging/media/atomisp/pci/runtime/binary/src/binary.c | 3 ++- drivers/staging/media/atomisp/pci/sh_css.c | 3 ++- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c index c30501a36f86..d2bacd1ffe7b 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -27,6 +27,7 @@ #include #include +#include /* Assume max number of ACC stages */ #define MAX_ACC_STAGES 20 @@ -841,7 +842,7 @@ int atomisp_css_irq_enable(struct atomisp_device *isp, { dev_dbg(isp->dev, "%s: css irq info 0x%08x: %s (%d).\n", __func__, info, - enable ? "enable" : "disable", enable); + str_enable_disable(enable), enable); if (ia_css_irq_enable(info, enable)) { dev_warn(isp->dev, "%s:Invalid irq info: 0x%08x when %s.\n", __func__, info, diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c index 236122722703..a1d3a16921ea 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "../../include/linux/atomisp_platform.h" #include "../../include/linux/atomisp_gmin_platform.h" @@ -916,7 +917,7 @@ static int gmin_acpi_pm_ctrl(struct v4l2_subdev *subdev, int on) return 0; dev_dbg(subdev->dev, "Setting power state to %s\n", - on ? "on" : "off"); + str_on_off(on)); if (on) ret = acpi_device_set_power(adev, @@ -929,7 +930,7 @@ static int gmin_acpi_pm_ctrl(struct v4l2_subdev *subdev, int on) gs->clock_on = on; else dev_err(subdev->dev, "Couldn't set power state to %s\n", - on ? "on" : "off"); + str_on_off(on)); return ret; } diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index 900a67552d6a..812230397409 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -527,7 +528,7 @@ static int atomisp_mrfld_power(struct atomisp_device *isp, bool enable) u32 val = enable ? MRFLD_ISPSSPM0_IUNIT_POWER_ON : MRFLD_ISPSSPM0_IUNIT_POWER_OFF; - dev_dbg(isp->dev, "IUNIT power-%s.\n", enable ? "on" : "off"); + dev_dbg(isp->dev, "IUNIT power-%s.\n", str_on_off(enable)); /* WA for P-Unit, if DVFS enabled, ISP timeout observed */ if (IS_CHT && enable && !isp->pm_only) { @@ -569,7 +570,7 @@ static int atomisp_mrfld_power(struct atomisp_device *isp, bool enable) usleep_range(100, 150); } while (1); - dev_err(isp->dev, "IUNIT power-%s timeout.\n", enable ? "on" : "off"); + dev_err(isp->dev, "IUNIT power-%s timeout.\n", str_on_off(enable)); return -EBUSY; } diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c index 39b37b557aff..e9016d7775dc 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c @@ -5,6 +5,7 @@ */ #include +#include #include #include /* HR_GDC_N */ @@ -1220,7 +1221,7 @@ int ia_css_binary_find(struct ia_css_binary_descr *descr, struct ia_css_binary * dev_dbg(atomisp_dev, "Using binary %s (id %d), type %d, mode %d, continuous %s\n", xcandidate->blob->name, xcandidate->sp.id, xcandidate->type, xcandidate->sp.pipeline.mode, - xcandidate->sp.enable.continuous ? "true" : "false"); + str_true_false(xcandidate->sp.enable.continuous)); if (err) dev_err(atomisp_dev, "Failed to find a firmware binary matching the pipeline parameters\n"); diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 792379407828..c136c1745eb9 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -7,6 +7,7 @@ /*! \file */ #include #include +#include #include #include "hmm.h" @@ -1478,7 +1479,7 @@ map_sp_threads(struct ia_css_stream *stream, bool map) enum ia_css_pipe_id pipe_id; IA_CSS_ENTER_PRIVATE("stream = %p, map = %s", - stream, map ? "true" : "false"); + stream, str_true_false(map)); if (!stream) { IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL); From 3322fc4dcaf1fe6b6fe68b976f6d93c2b87ea169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Barna=C5=9B?= Date: Thu, 21 Aug 2025 13:35:20 +0000 Subject: [PATCH 247/394] staging: media: atomisp: Whitespaces style cleanup in gdc.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clean up coding style whitespace issues in drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c. Fixes include: - removal of unnecessary line breaks - correcting spacing around operators - correcting spaces between types and names Signed-off-by: Adrian BarnaÅ› Signed-off-by: Sakari Ailus --- .../pci/hive_isp_css_common/host/gdc.c | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c index 8bb78b4d7c67..a06d69353939 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c @@ -26,9 +26,7 @@ static inline void gdc_reg_store( /* * Exported function implementations */ -void gdc_lut_store( - const gdc_ID_t ID, - const int data[4][HRT_GDC_N]) +void gdc_lut_store(const gdc_ID_t ID, const int data[4][HRT_GDC_N]) { unsigned int i, lut_offset = HRT_GDC_LUT_IDX; @@ -36,15 +34,13 @@ void gdc_lut_store( assert(HRT_GDC_LUT_COEFF_OFFSET <= (4 * sizeof(hrt_data))); for (i = 0; i < HRT_GDC_N; i++) { - hrt_data entry_0 = data[0][i] & HRT_GDC_BCI_COEF_MASK; - hrt_data entry_1 = data[1][i] & HRT_GDC_BCI_COEF_MASK; - hrt_data entry_2 = data[2][i] & HRT_GDC_BCI_COEF_MASK; - hrt_data entry_3 = data[3][i] & HRT_GDC_BCI_COEF_MASK; + hrt_data entry_0 = data[0][i] & HRT_GDC_BCI_COEF_MASK; + hrt_data entry_1 = data[1][i] & HRT_GDC_BCI_COEF_MASK; + hrt_data entry_2 = data[2][i] & HRT_GDC_BCI_COEF_MASK; + hrt_data entry_3 = data[3][i] & HRT_GDC_BCI_COEF_MASK; - hrt_data word_0 = entry_0 | - (entry_1 << HRT_GDC_LUT_COEFF_OFFSET); - hrt_data word_1 = entry_2 | - (entry_3 << HRT_GDC_LUT_COEFF_OFFSET); + hrt_data word_0 = entry_0 | (entry_1 << HRT_GDC_LUT_COEFF_OFFSET); + hrt_data word_1 = entry_2 | (entry_3 << HRT_GDC_LUT_COEFF_OFFSET); gdc_reg_store(ID, lut_offset++, word_0); gdc_reg_store(ID, lut_offset++, word_1); @@ -85,8 +81,7 @@ void gdc_lut_convert_to_isp_format(const int in_lut[4][HRT_GDC_N], } } -int gdc_get_unity( - const gdc_ID_t ID) +int gdc_get_unity(const gdc_ID_t ID) { assert(ID < N_GDC_ID); (void)ID; From 2bc7dd2b1fcc2a1f65db8967af60327ca8ec4f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Barna=C5=9B?= Date: Thu, 21 Aug 2025 13:35:21 +0000 Subject: [PATCH 248/394] staging: media: atomisp: Remove return from end of void function in gdc.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix checkpatch.pl warning on useless return on the end of the void function. Signed-off-by: Adrian BarnaÅ› Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c index a06d69353939..b31e3809c0e4 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c @@ -45,7 +45,6 @@ void gdc_lut_store(const gdc_ID_t ID, const int data[4][HRT_GDC_N]) gdc_reg_store(ID, lut_offset++, word_0); gdc_reg_store(ID, lut_offset++, word_1); } - return; } /* From d13b75670eb29df50760f8139535ecc6e4b81589 Mon Sep 17 00:00:00 2001 From: LiangCheng Wang Date: Sat, 26 Jul 2025 15:53:11 +0800 Subject: [PATCH 249/394] staging: media: atomisp: improve kernel-doc for ia_css_aa_config Move kernel-doc comment for strength field in ia_css_aa_config from inline to structure-level comment for better readability and tooling compatibility. Signed-off-by: LiangCheng Wang Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- .../atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h index 2f568a7062da..f825f537a536 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h @@ -28,11 +28,13 @@ * ISP block: BAA2 * ISP1: BAA2 is used. * ISP2: BAA2 is used. + * + * @strength: Strength of the filter, in u0.13 fixed-point format. + * Valid range: [0, 8191]. A value of 0 means the filter is + * ineffective (default). */ struct ia_css_aa_config { - u16 strength; /** Strength of the filter. - u0.13, [0,8191], - default/ineffective 0 */ + u16 strength; }; #endif /* __IA_CSS_AA2_TYPES_H */ From 15b4210a063877ddaa7f410069f86573bc0f5152 Mon Sep 17 00:00:00 2001 From: LiangCheng Wang Date: Sat, 26 Jul 2025 15:53:12 +0800 Subject: [PATCH 250/394] staging: media: atomisp: fix indentation in anr files Fix inconsistent tab/space usage and bring function definitions into a single-line format, matching kernel coding style. Signed-off-by: LiangCheng Wang Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- .../isp/kernels/anr/anr_1.0/ia_css_anr.host.c | 20 +++++++------------ .../isp/kernels/anr/anr_1.0/ia_css_anr.host.h | 20 +++++++------------ .../isp/kernels/anr/anr_2/ia_css_anr2.host.c | 14 +++++-------- .../isp/kernels/anr/anr_2/ia_css_anr2.host.h | 13 +++++------- 4 files changed, 24 insertions(+), 43 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c index 899d566234b9..49c22a68ec55 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c @@ -21,20 +21,16 @@ const struct ia_css_anr_config default_anr_config = { {10, 20, 30} }; -void -ia_css_anr_encode( - struct sh_css_isp_anr_params *to, - const struct ia_css_anr_config *from, - unsigned int size) +void ia_css_anr_encode(struct sh_css_isp_anr_params *to, + const struct ia_css_anr_config *from, + unsigned int size) { (void)size; to->threshold = from->threshold; } -void -ia_css_anr_dump( - const struct sh_css_isp_anr_params *anr, - unsigned int level) +void ia_css_anr_dump(const struct sh_css_isp_anr_params *anr, + unsigned int level) { if (!anr) return; ia_css_debug_dtrace(level, "Advance Noise Reduction:\n"); @@ -42,10 +38,8 @@ ia_css_anr_dump( "anr_threshold", anr->threshold); } -void -ia_css_anr_debug_dtrace( - const struct ia_css_anr_config *config, - unsigned int level) +void ia_css_anr_debug_dtrace(const struct ia_css_anr_config *config, + unsigned int level) { ia_css_debug_dtrace(level, "config.threshold=%d\n", diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h index 4f77900871c8..2f17d62b9208 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h @@ -12,20 +12,14 @@ extern const struct ia_css_anr_config default_anr_config; -void -ia_css_anr_encode( - struct sh_css_isp_anr_params *to, - const struct ia_css_anr_config *from, - unsigned int size); +void ia_css_anr_encode(struct sh_css_isp_anr_params *to, + const struct ia_css_anr_config *from, + unsigned int size); -void -ia_css_anr_dump( - const struct sh_css_isp_anr_params *anr, - unsigned int level); +void ia_css_anr_dump(const struct sh_css_isp_anr_params *anr, + unsigned int level); -void -ia_css_anr_debug_dtrace( - const struct ia_css_anr_config *config, unsigned int level) -; +void ia_css_anr_debug_dtrace(const struct ia_css_anr_config *config, + unsigned int level); #endif /* __IA_CSS_ANR_HOST_H */ diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c index 08af8a848fbf..6ba34dc48ad4 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c @@ -10,11 +10,9 @@ #include "ia_css_anr2.host.h" -void -ia_css_anr2_vmem_encode( - struct ia_css_isp_anr2_params *to, - const struct ia_css_anr_thres *from, - size_t size) +void ia_css_anr2_vmem_encode(struct ia_css_isp_anr2_params *to, + const struct ia_css_anr_thres *from, + size_t size) { unsigned int i; @@ -27,10 +25,8 @@ ia_css_anr2_vmem_encode( } } -void -ia_css_anr2_debug_dtrace( - const struct ia_css_anr_thres *config, - unsigned int level) +void ia_css_anr2_debug_dtrace(const struct ia_css_anr_thres *config, + unsigned int level) { (void)config; (void)level; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h index 2b1105f21c1e..36fb6c259699 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h @@ -13,15 +13,12 @@ #include "ia_css_anr2_param.h" #include "ia_css_anr2_table.host.h" -void -ia_css_anr2_vmem_encode( - struct ia_css_isp_anr2_params *to, - const struct ia_css_anr_thres *from, - size_t size); +void ia_css_anr2_vmem_encode(struct ia_css_isp_anr2_params *to, + const struct ia_css_anr_thres *from, + size_t size); -void -ia_css_anr2_debug_dtrace( - const struct ia_css_anr_thres *config, unsigned int level) +void ia_css_anr2_debug_dtrace(const struct ia_css_anr_thres *config, + unsigned int level) ; #endif /* __IA_CSS_ANR2_HOST_H */ From b643225f6189f65c436820e1585102612f8480d5 Mon Sep 17 00:00:00 2001 From: LiangCheng Wang Date: Sat, 26 Jul 2025 15:53:13 +0800 Subject: [PATCH 251/394] staging: media: atomisp: use designated initializer in anr config Improve readability by using designated initializer for default_anr_config. Signed-off-by: LiangCheng Wang Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- .../atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c index 49c22a68ec55..f4dd3ca03d75 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c @@ -11,14 +11,14 @@ #include "ia_css_anr.host.h" const struct ia_css_anr_config default_anr_config = { - 10, - { + .threshold = 10, + .thresholds = { 0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4, 0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4, 0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4, 0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4 }, - {10, 20, 30} + .factors = {10, 20, 30}, }; void ia_css_anr_encode(struct sh_css_isp_anr_params *to, From 9862d3f9de6b5e70d2ff87377beece44ec4e4e45 Mon Sep 17 00:00:00 2001 From: LiangCheng Wang Date: Sat, 26 Jul 2025 15:53:14 +0800 Subject: [PATCH 252/394] staging: media: atomisp: fix indentation in bh host files Fix inconsistent tab/space usage and bring function definitions into a single-line format, matching kernel coding style. Signed-off-by: LiangCheng Wang Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- .../pci/isp/kernels/bh/bh_2/ia_css_bh.host.c | 14 +++++--------- .../pci/isp/kernels/bh/bh_2/ia_css_bh.host.h | 14 +++++--------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c index 69c87e53f3c2..b87eb1a21b21 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c @@ -12,10 +12,8 @@ #include "ia_css_bh.host.h" -void -ia_css_bh_hmem_decode( - struct ia_css_3a_rgby_output *out_ptr, - const struct ia_css_bh_table *hmem_buf) +void ia_css_bh_hmem_decode(struct ia_css_3a_rgby_output *out_ptr, + const struct ia_css_bh_table *hmem_buf) { int i; @@ -37,11 +35,9 @@ ia_css_bh_hmem_decode( } } -void -ia_css_bh_encode( - struct sh_css_isp_bh_params *to, - const struct ia_css_3a_config *from, - unsigned int size) +void ia_css_bh_encode(struct sh_css_isp_bh_params *to, + const struct ia_css_3a_config *from, + unsigned int size) { (void)size; /* coefficients to calculate Y */ diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h index 36b360cfe62e..964d658ceec3 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h @@ -10,15 +10,11 @@ #include "ia_css_bh_param.h" #include "s3a/s3a_1.0/ia_css_s3a_types.h" -void -ia_css_bh_hmem_decode( - struct ia_css_3a_rgby_output *out_ptr, - const struct ia_css_bh_table *hmem_buf); +void ia_css_bh_hmem_decode(struct ia_css_3a_rgby_output *out_ptr, + const struct ia_css_bh_table *hmem_buf); -void -ia_css_bh_encode( - struct sh_css_isp_bh_params *to, - const struct ia_css_3a_config *from, - unsigned int size); +void ia_css_bh_encode(struct sh_css_isp_bh_params *to, + const struct ia_css_3a_config *from, + unsigned int size); #endif /* __IA_CSS_BH_HOST_H */ From 63c9b7def874b28471c5c75151cb43e8f10a9fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20R=C3=B6hling?= Date: Tue, 15 Jul 2025 13:23:26 +0200 Subject: [PATCH 253/394] media: atomisp: style fix for trailing statements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix checkpatch errors "ERROR: trailing statements should be on next line" in drivers/staging/media/atomisp/pci/sh_css_params.c. Signed-off-by: Timo Röhling Reviewed-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css_params.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c index 1c8af03db1c7..8420a22fd8f0 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/sh_css_params.c @@ -872,7 +872,8 @@ ia_css_process_kernel(struct ia_css_stream *stream, /* update the other buffers to the pipe specific copies */ for (stage = pipeline->stages; stage; stage = stage->next) { - if (!stage || !stage->binary) continue; + if (!stage || !stage->binary) + continue; process(pipeline->pipe_id, stage, params); } } @@ -3039,7 +3040,8 @@ process_kernel_parameters(unsigned int pipe_id, /* Call parameter process functions for all kernels */ /* Skip SC, since that is called on a temp sc table */ for (param_id = 0; param_id < IA_CSS_NUM_PARAMETER_IDS; param_id++) { - if (param_id == IA_CSS_SC_ID) continue; + if (param_id == IA_CSS_SC_ID) + continue; if (params->config_changed[param_id]) ia_css_kernel_process_param[param_id](pipe_id, stage, params); } @@ -3594,7 +3596,8 @@ sh_css_params_write_to_ddr_internal( IA_CSS_PARAM_CLASS_PARAM, mem); size_t size = isp_data->size; - if (!size) continue; + if (!size) + continue; buff_realloced = reallocate_buffer(&ddr_map->isp_mem_param[stage_num][mem], &ddr_map_size->isp_mem_param[stage_num][mem], size, From 12ea58282087b9df896fd0a43a92fa83e56b33c9 Mon Sep 17 00:00:00 2001 From: Huihui Huang Date: Thu, 16 Apr 2026 21:24:50 +0800 Subject: [PATCH 254/394] staging: media: atomisp: fix map and vmap leaks in stat buffer allocation There are memory leaks in drivers/staging/media/atomisp/pci/atomisp_compat_css20.c. In atomisp_css_allocate_stat_buffers(), s3a_map is allocated by ia_css_isp_3a_statistics_map_allocate() and its backing memory is mapped via hmm_vmap(). When dis_buf allocation fails, the error path frees s3a_data but does not unmap or free s3a_map. Similarly, when md_buf allocation fails, neither s3a_map nor dvs_map (and their hmm vmaps) are freed. Add the missing hmm_vunmap() and map free calls on both error paths, matching the cleanup order used in atomisp_css_free_3a_buffer() and atomisp_css_free_dis_buffer(). Signed-off-by: Huihui Huang Signed-off-by: Sakari Ailus --- .../media/atomisp/pci/atomisp_compat_css20.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c index d2bacd1ffe7b..0ee52637ea30 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -1117,8 +1117,11 @@ int atomisp_css_allocate_stat_buffers(struct atomisp_sub_device *asd, dvs_grid_info); if (!dis_buf->dis_data) { dev_err(isp->dev, "dvs buf allocation failed.\n"); - if (s3a_buf) + if (s3a_buf) { + hmm_vunmap(s3a_buf->s3a_data->data_ptr); + ia_css_isp_3a_statistics_map_free(s3a_buf->s3a_map); ia_css_isp_3a_statistics_free(s3a_buf->s3a_data); + } return -EINVAL; } @@ -1132,10 +1135,16 @@ int atomisp_css_allocate_stat_buffers(struct atomisp_sub_device *asd, md_buf->metadata = ia_css_metadata_allocate( &asd->stream_env[stream_id].stream_info.metadata_info); if (!md_buf->metadata) { - if (s3a_buf) + if (s3a_buf) { + hmm_vunmap(s3a_buf->s3a_data->data_ptr); + ia_css_isp_3a_statistics_map_free(s3a_buf->s3a_map); ia_css_isp_3a_statistics_free(s3a_buf->s3a_data); - if (dis_buf) + } + if (dis_buf) { + hmm_vunmap(dis_buf->dis_data->data_ptr); + ia_css_isp_dvs_statistics_map_free(dis_buf->dvs_map); ia_css_isp_dvs2_statistics_free(dis_buf->dis_data); + } dev_err(isp->dev, "metadata buf allocation failed.\n"); return -EINVAL; } From f6f729763c6a629893e679972dac9e2ab21f018f Mon Sep 17 00:00:00 2001 From: Oskar Ray-Frayssinet Date: Tue, 10 Mar 2026 16:03:14 +0100 Subject: [PATCH 255/394] staging: media: atomisp: replace msleep() with fsleep() in atomisp-gc2235.c Replace msleep(5) with fsleep(5000) to avoid sleeping longer than necessary. msleep() with values less than 20ms may sleep for up to 20ms due to timer granularity. fsleep() selects the appropriate sleep function automatically. Signed-off-by: Oskar Ray-Frayssinet Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/i2c/atomisp-gc2235.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c index 998c9f46bd06..f9cc5f45fe00 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c @@ -433,7 +433,7 @@ static int power_up(struct v4l2_subdev *sd) goto fail_power; } - msleep(5); + fsleep(5000); return 0; fail_clk: From 85354d984208ed64eb35cecbd8b4d84f390dd352 Mon Sep 17 00:00:00 2001 From: Taekyung Oh Date: Mon, 9 Feb 2026 16:57:34 +0000 Subject: [PATCH 256/394] staging: media: atomisp: Fix block comment style in ov2722.h Fix coding style warnings reported by checkpatch.pl. Move the comments above the corresponding code lines to align with guideline. Signed-off-by: Taekyung Oh Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/i2c/ov2722.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/ov2722.h b/drivers/staging/media/atomisp/i2c/ov2722.h index 00317d105305..b67c26dcae79 100644 --- a/drivers/staging/media/atomisp/i2c/ov2722.h +++ b/drivers/staging/media/atomisp/i2c/ov2722.h @@ -566,8 +566,8 @@ static const struct ov2722_reg ov2722_VGA_30fps[] = { #endif static const struct ov2722_reg ov2722_1632_1092_30fps[] = { - {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for - a whole frame complete.(vblank) */ + /* For stand wait for a whole frame complete.(vblank) */ + {OV2722_8BIT, 0x3021, 0x03}, {OV2722_8BIT, 0x3718, 0x10}, {OV2722_8BIT, 0x3702, 0x24}, {OV2722_8BIT, 0x373a, 0x60}, @@ -668,8 +668,8 @@ static const struct ov2722_reg ov2722_1632_1092_30fps[] = { }; static const struct ov2722_reg ov2722_1452_1092_30fps[] = { - {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for - a whole frame complete.(vblank) */ + /* For stand wait for a whole frame complete.(vblank) */ + {OV2722_8BIT, 0x3021, 0x03}, {OV2722_8BIT, 0x3718, 0x10}, {OV2722_8BIT, 0x3702, 0x24}, {OV2722_8BIT, 0x373a, 0x60}, @@ -878,8 +878,8 @@ static const struct ov2722_reg ov2722_1M3_30fps[] = { #endif static const struct ov2722_reg ov2722_1080p_30fps[] = { - {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for a whole - frame complete.(vblank) */ + /* For stand wait for a whole frame complete.(vblank) */ + {OV2722_8BIT, 0x3021, 0x03}, {OV2722_8BIT, 0x3718, 0x10}, {OV2722_8BIT, 0x3702, 0x24}, {OV2722_8BIT, 0x373a, 0x60}, From f4705de3a9d7594a7b5bab507d90ea57a9470b66 Mon Sep 17 00:00:00 2001 From: Taekyung Oh Date: Mon, 9 Feb 2026 16:57:43 +0000 Subject: [PATCH 257/394] staging: media: atomisp: remove dead code in ov2722.h Remove unused code blocks enclosed in #if 0 to clean up the code. Signed-off-by: Taekyung Oh Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/i2c/ov2722.h | 637 --------------------- 1 file changed, 637 deletions(-) diff --git a/drivers/staging/media/atomisp/i2c/ov2722.h b/drivers/staging/media/atomisp/i2c/ov2722.h index b67c26dcae79..7d5ea8801fe7 100644 --- a/drivers/staging/media/atomisp/i2c/ov2722.h +++ b/drivers/staging/media/atomisp/i2c/ov2722.h @@ -232,339 +232,6 @@ struct ov2722_write_ctrl { struct ov2722_write_buffer buffer; }; -/* - * Register settings for various resolution - */ -#if 0 -static const struct ov2722_reg ov2722_QVGA_30fps[] = { - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x0c}, - {OV2722_8BIT, 0x373a, 0x1c}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x0c}, - {OV2722_8BIT, 0x3705, 0x06}, - {OV2722_8BIT, 0x3730, 0x0e}, - {OV2722_8BIT, 0x3704, 0x1c}, - {OV2722_8BIT, 0x3f06, 0x00}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0x46}, - {OV2722_8BIT, 0x371e, 0x00}, - {OV2722_8BIT, 0x371f, 0x63}, - {OV2722_8BIT, 0x3708, 0x61}, - {OV2722_8BIT, 0x3709, 0x12}, - {OV2722_8BIT, 0x3800, 0x01}, - {OV2722_8BIT, 0x3801, 0x42}, /* H crop start: 322 */ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0x20}, /* V crop start: 32 */ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0x95}, /* H crop end: 1685 */ - {OV2722_8BIT, 0x3806, 0x04}, - {OV2722_8BIT, 0x3807, 0x27}, /* V crop end: 1063 */ - {OV2722_8BIT, 0x3808, 0x01}, - {OV2722_8BIT, 0x3809, 0x50}, /* H output size: 336 */ - {OV2722_8BIT, 0x380a, 0x01}, - {OV2722_8BIT, 0x380b, 0x00}, /* V output size: 256 */ - - /* H blank timing */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x04}, /* H window offset: 5 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x01}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0xc0}, - {OV2722_8BIT, 0x3821, 0x06}, /* flip isp*/ - {OV2722_8BIT, 0x3814, 0x71}, - {OV2722_8BIT, 0x3815, 0x71}, - {OV2722_8BIT, 0x3612, 0x49}, - {OV2722_8BIT, 0x3618, 0x00}, - {OV2722_8BIT, 0x3a08, 0x01}, - {OV2722_8BIT, 0x3a09, 0xc3}, - {OV2722_8BIT, 0x3a0a, 0x01}, - {OV2722_8BIT, 0x3a0b, 0x77}, - {OV2722_8BIT, 0x3a0d, 0x00}, - {OV2722_8BIT, 0x3a0e, 0x00}, - {OV2722_8BIT, 0x4520, 0x09}, - {OV2722_8BIT, 0x4837, 0x1b}, - {OV2722_8BIT, 0x3000, 0xff}, - {OV2722_8BIT, 0x3001, 0xff}, - {OV2722_8BIT, 0x3002, 0xf0}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0x53}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x63}, - {OV2722_8BIT, 0x3634, 0x24}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */ - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xff}, - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */ - {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */ - {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */ - {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */ - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */ - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - - /* Added for power optimization */ - {OV2722_8BIT, 0x3000, 0x00}, - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3011, 0x22}, - {OV2722_8BIT, 0x3a00, 0x58}, - {OV2722_8BIT, 0x3503, 0x17}, - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x46}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x10}, - {OV2722_TOK_TERM, 0, 0}, - -}; - -static const struct ov2722_reg ov2722_480P_30fps[] = { - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x18}, - {OV2722_8BIT, 0x373a, 0x3c}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x1d}, - {OV2722_8BIT, 0x3705, 0x12}, - {OV2722_8BIT, 0x3730, 0x1f}, - {OV2722_8BIT, 0x3704, 0x3f}, - {OV2722_8BIT, 0x3f06, 0x1d}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0x83}, - {OV2722_8BIT, 0x371e, 0x00}, - {OV2722_8BIT, 0x371f, 0xbd}, - {OV2722_8BIT, 0x3708, 0x63}, - {OV2722_8BIT, 0x3709, 0x52}, - {OV2722_8BIT, 0x3800, 0x00}, - {OV2722_8BIT, 0x3801, 0xf2}, /* H crop start: 322 - 80 = 242*/ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0x20}, /* V crop start: 32*/ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0xBB}, /* H crop end: 1643 + 80 = 1723*/ - {OV2722_8BIT, 0x3806, 0x04}, - {OV2722_8BIT, 0x3807, 0x03}, /* V crop end: 1027*/ - {OV2722_8BIT, 0x3808, 0x02}, - {OV2722_8BIT, 0x3809, 0xE0}, /* H output size: 656 +80 = 736*/ - {OV2722_8BIT, 0x380a, 0x01}, - {OV2722_8BIT, 0x380b, 0xF0}, /* V output size: 496 */ - - /* H blank timing */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x04}, /* H window offset: 5 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x01}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0x80}, - {OV2722_8BIT, 0x3821, 0x06}, /* flip isp*/ - {OV2722_8BIT, 0x3814, 0x31}, - {OV2722_8BIT, 0x3815, 0x31}, - {OV2722_8BIT, 0x3612, 0x4b}, - {OV2722_8BIT, 0x3618, 0x04}, - {OV2722_8BIT, 0x3a08, 0x02}, - {OV2722_8BIT, 0x3a09, 0x67}, - {OV2722_8BIT, 0x3a0a, 0x02}, - {OV2722_8BIT, 0x3a0b, 0x00}, - {OV2722_8BIT, 0x3a0d, 0x00}, - {OV2722_8BIT, 0x3a0e, 0x00}, - {OV2722_8BIT, 0x4520, 0x0a}, - {OV2722_8BIT, 0x4837, 0x1b}, - {OV2722_8BIT, 0x3000, 0xff}, - {OV2722_8BIT, 0x3001, 0xff}, - {OV2722_8BIT, 0x3002, 0xf0}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0x53}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x63}, - {OV2722_8BIT, 0x3634, 0x24}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */ - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xff}, - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */ - {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */ - {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */ - {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */ - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */ - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - - /* Added for power optimization */ - {OV2722_8BIT, 0x3000, 0x00}, - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3011, 0x22}, - {OV2722_8BIT, 0x3a00, 0x58}, - {OV2722_8BIT, 0x3503, 0x17}, - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x46}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x10}, - {OV2722_TOK_TERM, 0, 0}, -}; - -static const struct ov2722_reg ov2722_VGA_30fps[] = { - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x18}, - {OV2722_8BIT, 0x373a, 0x3c}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x1d}, - {OV2722_8BIT, 0x3705, 0x12}, - {OV2722_8BIT, 0x3730, 0x1f}, - {OV2722_8BIT, 0x3704, 0x3f}, - {OV2722_8BIT, 0x3f06, 0x1d}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0x83}, - {OV2722_8BIT, 0x371e, 0x00}, - {OV2722_8BIT, 0x371f, 0xbd}, - {OV2722_8BIT, 0x3708, 0x63}, - {OV2722_8BIT, 0x3709, 0x52}, - {OV2722_8BIT, 0x3800, 0x01}, - {OV2722_8BIT, 0x3801, 0x42}, /* H crop start: 322 */ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0x20}, /* V crop start: 32*/ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0x6B}, /* H crop end: 1643*/ - {OV2722_8BIT, 0x3806, 0x04}, - {OV2722_8BIT, 0x3807, 0x03}, /* V crop end: 1027*/ - {OV2722_8BIT, 0x3808, 0x02}, - {OV2722_8BIT, 0x3809, 0x90}, /* H output size: 656 */ - {OV2722_8BIT, 0x380a, 0x01}, - {OV2722_8BIT, 0x380b, 0xF0}, /* V output size: 496 */ - - /* H blank timing */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x04}, /* H window offset: 5 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x01}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0x80}, - {OV2722_8BIT, 0x3821, 0x06}, /* flip isp*/ - {OV2722_8BIT, 0x3814, 0x31}, - {OV2722_8BIT, 0x3815, 0x31}, - {OV2722_8BIT, 0x3612, 0x4b}, - {OV2722_8BIT, 0x3618, 0x04}, - {OV2722_8BIT, 0x3a08, 0x02}, - {OV2722_8BIT, 0x3a09, 0x67}, - {OV2722_8BIT, 0x3a0a, 0x02}, - {OV2722_8BIT, 0x3a0b, 0x00}, - {OV2722_8BIT, 0x3a0d, 0x00}, - {OV2722_8BIT, 0x3a0e, 0x00}, - {OV2722_8BIT, 0x4520, 0x0a}, - {OV2722_8BIT, 0x4837, 0x29}, - {OV2722_8BIT, 0x3000, 0xff}, - {OV2722_8BIT, 0x3001, 0xff}, - {OV2722_8BIT, 0x3002, 0xf0}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0x53}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x63}, - {OV2722_8BIT, 0x3634, 0x24}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */ - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xff}, - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */ - {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */ - {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */ - {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */ - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */ - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - - /* Added for power optimization */ - {OV2722_8BIT, 0x3000, 0x00}, - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3011, 0x22}, - {OV2722_8BIT, 0x3a00, 0x58}, - {OV2722_8BIT, 0x3503, 0x17}, - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x46}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x10}, - {OV2722_TOK_TERM, 0, 0}, -}; -#endif - static const struct ov2722_reg ov2722_1632_1092_30fps[] = { /* For stand wait for a whole frame complete.(vblank) */ {OV2722_8BIT, 0x3021, 0x03}, @@ -768,115 +435,6 @@ static const struct ov2722_reg ov2722_1452_1092_30fps[] = { {OV2722_TOK_TERM, 0, 0} }; -#if 0 -static const struct ov2722_reg ov2722_1M3_30fps[] = { - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x24}, - {OV2722_8BIT, 0x373a, 0x60}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x2e}, - {OV2722_8BIT, 0x3705, 0x10}, - {OV2722_8BIT, 0x3730, 0x30}, - {OV2722_8BIT, 0x3704, 0x62}, - {OV2722_8BIT, 0x3f06, 0x3a}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0xc4}, - {OV2722_8BIT, 0x371e, 0x01}, - {OV2722_8BIT, 0x371f, 0x0d}, - {OV2722_8BIT, 0x3708, 0x61}, - {OV2722_8BIT, 0x3709, 0x12}, - {OV2722_8BIT, 0x3800, 0x01}, - {OV2722_8BIT, 0x3801, 0x4a}, /* H crop start: 330 */ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0x03}, /* V crop start: 3 */ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0xe1}, /* H crop end: 1761 */ - {OV2722_8BIT, 0x3806, 0x04}, - {OV2722_8BIT, 0x3807, 0x47}, /* V crop end: 1095 */ - {OV2722_8BIT, 0x3808, 0x05}, - {OV2722_8BIT, 0x3809, 0x88}, /* H output size: 1416 */ - {OV2722_8BIT, 0x380a, 0x04}, - {OV2722_8BIT, 0x380b, 0x0a}, /* V output size: 1034 */ - - /* H blank timing */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x05}, /* H window offset: 5 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x02}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0x80}, - {OV2722_8BIT, 0x3821, 0x06}, /* flip isp */ - {OV2722_8BIT, 0x3814, 0x11}, - {OV2722_8BIT, 0x3815, 0x11}, - {OV2722_8BIT, 0x3612, 0x0b}, - {OV2722_8BIT, 0x3618, 0x04}, - {OV2722_8BIT, 0x3a08, 0x01}, - {OV2722_8BIT, 0x3a09, 0x50}, - {OV2722_8BIT, 0x3a0a, 0x01}, - {OV2722_8BIT, 0x3a0b, 0x18}, - {OV2722_8BIT, 0x3a0d, 0x03}, - {OV2722_8BIT, 0x3a0e, 0x03}, - {OV2722_8BIT, 0x4520, 0x00}, - {OV2722_8BIT, 0x4837, 0x1b}, - {OV2722_8BIT, 0x3000, 0xff}, - {OV2722_8BIT, 0x3001, 0xff}, - {OV2722_8BIT, 0x3002, 0xf0}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0xd2}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x23}, - {OV2722_8BIT, 0x3634, 0x54}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */ - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xcf}, - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */ - {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */ - {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */ - {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */ - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */ - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - - /* Added for power optimization */ - {OV2722_8BIT, 0x3000, 0x00}, - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3503, 0x17}, - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x46}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x10}, - {OV2722_TOK_TERM, 0, 0}, -}; -#endif - static const struct ov2722_reg ov2722_1080p_30fps[] = { /* For stand wait for a whole frame complete.(vblank) */ {OV2722_8BIT, 0x3021, 0x03}, @@ -982,108 +540,6 @@ static const struct ov2722_reg ov2722_1080p_30fps[] = { {OV2722_TOK_TERM, 0, 0} }; -#if 0 /* Currently unused */ -static const struct ov2722_reg ov2722_720p_30fps[] = { - {OV2722_8BIT, 0x3021, 0x03}, - {OV2722_8BIT, 0x3718, 0x10}, - {OV2722_8BIT, 0x3702, 0x24}, - {OV2722_8BIT, 0x373a, 0x60}, - {OV2722_8BIT, 0x3715, 0x01}, - {OV2722_8BIT, 0x3703, 0x2e}, - {OV2722_8BIT, 0x3705, 0x10}, - {OV2722_8BIT, 0x3730, 0x30}, - {OV2722_8BIT, 0x3704, 0x62}, - {OV2722_8BIT, 0x3f06, 0x3a}, - {OV2722_8BIT, 0x371c, 0x00}, - {OV2722_8BIT, 0x371d, 0xc4}, - {OV2722_8BIT, 0x371e, 0x01}, - {OV2722_8BIT, 0x371f, 0x0d}, - {OV2722_8BIT, 0x3708, 0x61}, - {OV2722_8BIT, 0x3709, 0x12}, - {OV2722_8BIT, 0x3800, 0x01}, - {OV2722_8BIT, 0x3801, 0x40}, /* H crop start: 320 */ - {OV2722_8BIT, 0x3802, 0x00}, - {OV2722_8BIT, 0x3803, 0xb1}, /* V crop start: 177 */ - {OV2722_8BIT, 0x3804, 0x06}, - {OV2722_8BIT, 0x3805, 0x55}, /* H crop end: 1621 */ - {OV2722_8BIT, 0x3806, 0x03}, - {OV2722_8BIT, 0x3807, 0x95}, /* V crop end: 918 */ - {OV2722_8BIT, 0x3808, 0x05}, - {OV2722_8BIT, 0x3809, 0x10}, /* H output size: 0x0788==1928 */ - {OV2722_8BIT, 0x380a, 0x02}, - {OV2722_8BIT, 0x380b, 0xe0}, /* output size: 0x02DE==734 */ - {OV2722_8BIT, 0x380c, 0x08}, - {OV2722_8BIT, 0x380d, 0x00}, /* H timing: 2048 */ - {OV2722_8BIT, 0x380e, 0x04}, - {OV2722_8BIT, 0x380f, 0xa3}, /* V timing: 1187 */ - {OV2722_8BIT, 0x3810, 0x00}, - {OV2722_8BIT, 0x3811, 0x03}, /* H window offset: 3 */ - {OV2722_8BIT, 0x3812, 0x00}, - {OV2722_8BIT, 0x3813, 0x02}, /* V window offset: 2 */ - {OV2722_8BIT, 0x3820, 0x80}, - {OV2722_8BIT, 0x3821, 0x06}, /* mirror */ - {OV2722_8BIT, 0x3814, 0x11}, - {OV2722_8BIT, 0x3815, 0x11}, - {OV2722_8BIT, 0x3612, 0x0b}, - {OV2722_8BIT, 0x3618, 0x04}, - {OV2722_8BIT, 0x3a08, 0x01}, - {OV2722_8BIT, 0x3a09, 0x50}, - {OV2722_8BIT, 0x3a0a, 0x01}, - {OV2722_8BIT, 0x3a0b, 0x18}, - {OV2722_8BIT, 0x3a0d, 0x03}, - {OV2722_8BIT, 0x3a0e, 0x03}, - {OV2722_8BIT, 0x4520, 0x00}, - {OV2722_8BIT, 0x4837, 0x1b}, - {OV2722_8BIT, 0x3600, 0x08}, - {OV2722_8BIT, 0x3621, 0xc0}, - {OV2722_8BIT, 0x3632, 0xd2}, /* added for power opt */ - {OV2722_8BIT, 0x3633, 0x23}, - {OV2722_8BIT, 0x3634, 0x54}, - {OV2722_8BIT, 0x3f01, 0x0c}, - {OV2722_8BIT, 0x5001, 0xc1}, - {OV2722_8BIT, 0x3614, 0xf0}, - {OV2722_8BIT, 0x3630, 0x2d}, - {OV2722_8BIT, 0x370b, 0x62}, - {OV2722_8BIT, 0x3706, 0x61}, - {OV2722_8BIT, 0x4000, 0x02}, - {OV2722_8BIT, 0x4002, 0xc5}, - {OV2722_8BIT, 0x4005, 0x08}, - {OV2722_8BIT, 0x404f, 0x84}, - {OV2722_8BIT, 0x4051, 0x00}, - {OV2722_8BIT, 0x5000, 0xcf}, /* manual 3a */ - {OV2722_8BIT, 0x301d, 0xf0}, /* enable group hold */ - {OV2722_8BIT, 0x3a18, 0x00}, - {OV2722_8BIT, 0x3a19, 0x80}, - {OV2722_8BIT, 0x4521, 0x00}, - {OV2722_8BIT, 0x5183, 0xb0}, - {OV2722_8BIT, 0x5184, 0xb0}, - {OV2722_8BIT, 0x5185, 0xb0}, - {OV2722_8BIT, 0x370c, 0x0c}, - {OV2722_8BIT, 0x3035, 0x00}, - {OV2722_8BIT, 0x3036, 0x26}, /* {0x3036, 0x2c}, //422.4 MHz */ - {OV2722_8BIT, 0x3037, 0xa1}, - {OV2722_8BIT, 0x303e, 0x19}, - {OV2722_8BIT, 0x3038, 0x06}, - {OV2722_8BIT, 0x3018, 0x04}, - {OV2722_8BIT, 0x3000, 0x00}, /* added for power optimization */ - {OV2722_8BIT, 0x3001, 0x00}, - {OV2722_8BIT, 0x3002, 0x00}, - {OV2722_8BIT, 0x3a0f, 0x40}, - {OV2722_8BIT, 0x3a10, 0x38}, - {OV2722_8BIT, 0x3a1b, 0x48}, - {OV2722_8BIT, 0x3a1e, 0x30}, - {OV2722_8BIT, 0x3a11, 0x90}, - {OV2722_8BIT, 0x3a1f, 0x10}, - {OV2722_8BIT, 0x3503, 0x17}, /* manual 3a */ - {OV2722_8BIT, 0x3500, 0x00}, - {OV2722_8BIT, 0x3501, 0x3F}, - {OV2722_8BIT, 0x3502, 0x00}, - {OV2722_8BIT, 0x3508, 0x00}, - {OV2722_8BIT, 0x3509, 0x00}, - {OV2722_TOK_TERM, 0, 0}, -}; -#endif - static struct ov2722_resolution ov2722_res_preview[] = { { .desc = "ov2722_1632_1092_30fps", @@ -1128,99 +584,6 @@ static struct ov2722_resolution ov2722_res_preview[] = { #define N_RES_PREVIEW (ARRAY_SIZE(ov2722_res_preview)) -/* - * Disable non-preview configurations until the configuration selection is - * improved. - */ -#if 0 -struct ov2722_resolution ov2722_res_still[] = { - { - .desc = "ov2722_480P_30fps", - .width = 1632, - .height = 1092, - .fps = 30, - .pix_clk_freq = 85, - .used = 0, - .pixels_per_line = 2260, - .lines_per_frame = 1244, - .skip_frames = 3, - .regs = ov2722_1632_1092_30fps, - .mipi_freq = 422400, - }, - { - .desc = "ov2722_1452_1092_30fps", - .width = 1452, - .height = 1092, - .fps = 30, - .pix_clk_freq = 85, - .used = 0, - .pixels_per_line = 2260, - .lines_per_frame = 1244, - .skip_frames = 3, - .regs = ov2722_1452_1092_30fps, - .mipi_freq = 422400, - }, - { - .desc = "ov2722_1080P_30fps", - .width = 1932, - .height = 1092, - .pix_clk_freq = 69, - .fps = 30, - .used = 0, - .pixels_per_line = 2068, - .lines_per_frame = 1114, - .skip_frames = 3, - .regs = ov2722_1080p_30fps, - .mipi_freq = 345600, - }, -}; - -#define N_RES_STILL (ARRAY_SIZE(ov2722_res_still)) - -struct ov2722_resolution ov2722_res_video[] = { - { - .desc = "ov2722_QVGA_30fps", - .width = 336, - .height = 256, - .fps = 30, - .pix_clk_freq = 73, - .used = 0, - .pixels_per_line = 2048, - .lines_per_frame = 1184, - .skip_frames = 3, - .regs = ov2722_QVGA_30fps, - .mipi_freq = 364800, - }, - { - .desc = "ov2722_480P_30fps", - .width = 736, - .height = 496, - .fps = 30, - .pix_clk_freq = 73, - .used = 0, - .pixels_per_line = 2048, - .lines_per_frame = 1184, - .skip_frames = 3, - .regs = ov2722_480P_30fps, - }, - { - .desc = "ov2722_1080P_30fps", - .width = 1932, - .height = 1092, - .pix_clk_freq = 69, - .fps = 30, - .used = 0, - .pixels_per_line = 2068, - .lines_per_frame = 1114, - .skip_frames = 3, - .regs = ov2722_1080p_30fps, - .mipi_freq = 345600, - }, -}; - -#define N_RES_VIDEO (ARRAY_SIZE(ov2722_res_video)) -#endif - static struct ov2722_resolution *ov2722_res = ov2722_res_preview; static unsigned long N_RES = N_RES_PREVIEW; #endif From 11f94f5b1d3001314fb0f7d1739c74482fbba960 Mon Sep 17 00:00:00 2001 From: Pedro Pontes Date: Thu, 16 Apr 2026 10:42:15 -0300 Subject: [PATCH 258/394] media: atomisp: use kmalloc_objs for array allocations Convert manual kmalloc() multiplications to the modern kmalloc_objs() interface to improve type safety and prevent potential integer overflows. Signed-off-by: Pedro Pontes Signed-off-by: Sakari Ailus --- drivers/staging/media/atomisp/pci/sh_css.c | 57 ++++++++++++---------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index c136c1745eb9..00082276f1db 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -5819,36 +5819,37 @@ static int ia_css_pipe_create_cas_scaler_desc_single_output( i *= max_scale_factor_per_stage; } - descr->in_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->in_info = kmalloc_objs(*descr->in_info, + descr->num_stage, + GFP_KERNEL); if (!descr->in_info) { err = -ENOMEM; goto ERR; } - descr->internal_out_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->internal_out_info = kmalloc_objs(*descr->internal_out_info, + descr->num_stage, + GFP_KERNEL); if (!descr->internal_out_info) { err = -ENOMEM; goto ERR; } - descr->out_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->out_info = kmalloc_objs(*descr->out_info, + descr->num_stage, + GFP_KERNEL); if (!descr->out_info) { err = -ENOMEM; goto ERR; } - descr->vf_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->vf_info = kmalloc_objs(*descr->vf_info, + descr->num_stage, + GFP_KERNEL); if (!descr->vf_info) { err = -ENOMEM; goto ERR; } - descr->is_output_stage = kmalloc(descr->num_stage * sizeof(bool), - GFP_KERNEL); + descr->is_output_stage = kmalloc_objs(*descr->is_output_stage, + descr->num_stage, + GFP_KERNEL); if (!descr->is_output_stage) { err = -ENOMEM; goto ERR; @@ -5968,35 +5969,37 @@ ia_css_pipe_create_cas_scaler_desc(struct ia_css_pipe *pipe, descr->num_stage = num_stages; - descr->in_info = kmalloc_objs(struct ia_css_frame_info, - descr->num_stage); + descr->in_info = kmalloc_objs(*descr->in_info, + descr->num_stage, + GFP_KERNEL); if (!descr->in_info) { err = -ENOMEM; goto ERR; } - descr->internal_out_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->internal_out_info = kmalloc_objs(*descr->internal_out_info, + descr->num_stage, + GFP_KERNEL); if (!descr->internal_out_info) { err = -ENOMEM; goto ERR; } - descr->out_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->out_info = kmalloc_objs(*descr->out_info, + descr->num_stage, + GFP_KERNEL); if (!descr->out_info) { err = -ENOMEM; goto ERR; } - descr->vf_info = kmalloc(descr->num_stage * - sizeof(struct ia_css_frame_info), - GFP_KERNEL); + descr->vf_info = kmalloc_objs(*descr->vf_info, + descr->num_stage, + GFP_KERNEL); if (!descr->vf_info) { err = -ENOMEM; goto ERR; } - descr->is_output_stage = kmalloc(descr->num_stage * sizeof(bool), - GFP_KERNEL); + descr->is_output_stage = kmalloc_objs(*descr->is_output_stage, + descr->num_stage, + GFP_KERNEL); if (!descr->is_output_stage) { err = -ENOMEM; goto ERR; From 4f6f28ff24709710c08557c127b3e4c3fb1b4159 Mon Sep 17 00:00:00 2001 From: Martin Hecht Date: Fri, 8 May 2026 11:59:03 +0200 Subject: [PATCH 259/394] media: i2c: alvium: fix critical pointer access in alvium_ctrl_init The current implementation of alvium_ctrl_init creates several controls in function alvium_ctrl_init and uses the returned pointer without check. That can cause write access over NULL-pointer for several controls. The reworked code checks the pointers before adding flags. Fixes: 0a7af872915e ("media: i2c: Add support for alvium camera") Cc: stable@vger.kernel.org Signed-off-by: Martin Hecht Signed-off-by: Sakari Ailus --- drivers/media/i2c/alvium-csi2.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c index 955b7072a560..dd991c2ee700 100644 --- a/drivers/media/i2c/alvium-csi2.c +++ b/drivers/media/i2c/alvium-csi2.c @@ -2100,20 +2100,21 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) V4L2_CID_PIXEL_RATE, 0, ALVIUM_DEFAULT_PIXEL_RATE_MHZ, 1, ALVIUM_DEFAULT_PIXEL_RATE_MHZ); - ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* Link freq is fixed */ ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, 0, 0, &alvium->link_freq); - ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (ctrls->link_freq) + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* Auto/manual white balance */ if (alvium->avail_ft.auto_whiteb) { ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); + if (ctrls->auto_wb) + v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); } ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, @@ -2122,6 +2123,7 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) alvium->max_bbalance, alvium->inc_bbalance, alvium->dft_bbalance); + ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, alvium->min_rbalance, @@ -2136,7 +2138,9 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); - v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true); + if (ctrls->auto_exp) + v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, + V4L2_EXPOSURE_MANUAL, true); } ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, @@ -2145,14 +2149,16 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) alvium->max_exp, alvium->inc_exp, alvium->dft_exp); - ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + if (ctrls->exposure) + ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; /* Auto/manual gain */ if (alvium->avail_ft.auto_gain) { ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); + if (ctrls->auto_gain) + v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); } if (alvium->avail_ft.gain) { @@ -2162,7 +2168,8 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) alvium->max_gain, alvium->inc_gain, alvium->dft_gain); - ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + if (ctrls->gain) + ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; } if (alvium->avail_ft.sat) From 8ba166ff7c921da610376156d7c0a5fc985fa983 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 19 May 2026 10:07:38 +0800 Subject: [PATCH 260/394] media: synopsys: Fix IPI using hardcoded datatype The imx93_csi2rx_dphy_ipi_enable() function configures the IPI datatype using csi2->formats->csi_dt, which is initialized during probe but never updated in set_fmt(). This causes the IPI to always use the probe-time default datatype, ignoring the actual media bus format negotiated at runtime. When userspace requests a different format, the IPI hardware is configured with the wrong datatype, resulting in incorrect image output. Fix by updating csi2->formats in the set_fmt callback to reflect the currently negotiated format, ensuring the IPI configuration matches the runtime datatype. Fixes: ec40b431f0ab ("media: synopsys: csi2rx: add i.MX93 support") Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Signed-off-by: Sakari Ailus --- drivers/media/platform/synopsys/dw-mipi-csi2rx.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c index 02eb4a6cafad..0b80e84983f9 100644 --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -311,7 +311,7 @@ dw_mipi_csi2rx_find_format(struct dw_mipi_csi2rx_device *csi2, u32 mbus_code) WARN_ON(csi2->formats_num == 0); for (unsigned int i = 0; i < csi2->formats_num; i++) { - const struct dw_mipi_csi2rx_format *format = &csi2->formats[i]; + const struct dw_mipi_csi2rx_format *format = &formats[i]; if (format->code == mbus_code) return format; @@ -433,7 +433,7 @@ dw_mipi_csi2rx_enum_mbus_code(struct v4l2_subdev *sd, if (code->index >= csi2->formats_num) return -EINVAL; - code->code = csi2->formats[code->index].code; + code->code = formats[code->index].code; return 0; default: return -EINVAL; @@ -470,6 +470,17 @@ static int dw_mipi_csi2rx_set_fmt(struct v4l2_subdev *sd, *src = *sink; + /* Store the CSIS format descriptor for active formats. */ + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + csi2->formats = fmt ? : + dw_mipi_csi2rx_find_format(csi2, default_format.code); + + if (!csi2->formats) { + dev_err(csi2->dev, "Failed to find valid format\n"); + return -EINVAL; + } + } + return 0; } From 4a7004c03d2227643c2ef2e546ad173b2b1480b7 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 19 May 2026 10:07:39 +0800 Subject: [PATCH 261/394] media: synopsys: Add support for RAW16 Bayer formats Add higher bit-depth raw image data support for the sensors, which supports 16-bit output. Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Signed-off-by: Sakari Ailus --- .../media/platform/synopsys/dw-mipi-csi2rx.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c index 0b80e84983f9..f45466ede2bb 100644 --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -252,6 +252,26 @@ static const struct dw_mipi_csi2rx_format formats[] = { .depth = 12, .csi_dt = MIPI_CSI2_DT_RAW12, }, + { + .code = MEDIA_BUS_FMT_SBGGR16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + }, + { + .code = MEDIA_BUS_FMT_SGBRG16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + }, + { + .code = MEDIA_BUS_FMT_SGRBG16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + }, + { + .code = MEDIA_BUS_FMT_SRGGB16_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_RAW16, + }, }; static inline struct dw_mipi_csi2rx_device *to_csi2(struct v4l2_subdev *sd) From 9823c6bd80082906e2b1d0ca03f470ff395b5557 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 19 May 2026 10:07:40 +0800 Subject: [PATCH 262/394] media: synopsys: Add support for multiple streams The current driver only supports single stream operation. Add support for multiple concurrent streams by tracking enabled streams with a bitmask and only initializing the hardware once for the first stream. This enables use cases such as surround view systems where multiple camera streams need to be processed simultaneously through the same CSI-2 receiver interface. Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Signed-off-by: Sakari Ailus --- .../media/platform/synopsys/dw-mipi-csi2rx.c | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c index f45466ede2bb..92178a3dec5d 100644 --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -113,6 +113,7 @@ struct dw_mipi_csi2rx_device { enum v4l2_mbus_type bus_type; u32 lanes_num; + u64 enabled_streams; const struct dw_mipi_csi2rx_drvdata *drvdata; }; @@ -539,26 +540,33 @@ static int dw_mipi_csi2rx_enable_streams(struct v4l2_subdev *sd, DW_MIPI_CSI2RX_PAD_SRC, &streams_mask); - ret = pm_runtime_resume_and_get(dev); - if (ret) - goto err; + if (!csi2->enabled_streams) { + ret = pm_runtime_resume_and_get(dev); + if (ret) + goto err; - ret = dw_mipi_csi2rx_start(csi2); - if (ret) { - dev_err(dev, "failed to enable CSI hardware\n"); - goto err_pm_runtime_put; + ret = dw_mipi_csi2rx_start(csi2); + if (ret) { + dev_err(dev, "failed to enable CSI hardware\n"); + goto err_pm_runtime_put; + } } ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask); if (ret) goto err_csi_stop; + csi2->enabled_streams |= streams_mask; + return 0; err_csi_stop: - dw_mipi_csi2rx_stop(csi2); + /* Stop CSI hardware if no streams are enabled */ + if (!csi2->enabled_streams) + dw_mipi_csi2rx_stop(csi2); err_pm_runtime_put: - pm_runtime_put(dev); + if (!csi2->enabled_streams) + pm_runtime_put(dev); err: return ret; } @@ -583,10 +591,15 @@ static int dw_mipi_csi2rx_disable_streams(struct v4l2_subdev *sd, &streams_mask); ret = v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask); + if (ret) + dev_err(dev, "failed to disable streams on remote subdev: %d\n", ret); - dw_mipi_csi2rx_stop(csi2); + csi2->enabled_streams &= ~streams_mask; - pm_runtime_put(dev); + if (!csi2->enabled_streams) { + dw_mipi_csi2rx_stop(csi2); + pm_runtime_put(dev); + } return ret; } From 82cdee9be284af7f3aa2b2ba1fa61c410642cac4 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 19 May 2026 10:07:41 +0800 Subject: [PATCH 263/394] media: synopsys: Add PHY stopstate wait for i.MX93 Implement waiting for D-PHY lanes to enter stop state on i.MX93. This ensures proper PHY initialization by verifying that the clock lane and all active data lanes have entered the stop state before proceeding with further operations. Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Signed-off-by: Sakari Ailus --- .../media/platform/synopsys/dw-mipi-csi2rx.c | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c index 92178a3dec5d..8a34aec550ad 100644 --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,8 @@ #define DW_REG_EXIST BIT(31) #define DW_REG(x) (DW_REG_EXIST | (x)) +#define DPHY_STOPSTATE_CLK_LANE BIT(16) + #define DPHY_TEST_CTRL0_TEST_CLR BIT(0) #define IPI_VCID_VC(x) FIELD_PREP(GENMASK(1, 0), (x)) @@ -65,6 +68,7 @@ enum dw_mipi_csi2rx_regs_index { DW_MIPI_CSI2RX_PHY_TST_CTRL0, DW_MIPI_CSI2RX_PHY_TST_CTRL1, DW_MIPI_CSI2RX_PHY_SHUTDOWNZ, + DW_MIPI_CSI2RX_PHY_STOPSTATE, DW_MIPI_CSI2RX_IPI_DATATYPE, DW_MIPI_CSI2RX_IPI_MEM_FLUSH, DW_MIPI_CSI2RX_IPI_MODE, @@ -87,6 +91,7 @@ struct dw_mipi_csi2rx_drvdata { void (*dphy_assert_reset)(struct dw_mipi_csi2rx_device *csi2); void (*dphy_deassert_reset)(struct dw_mipi_csi2rx_device *csi2); void (*ipi_enable)(struct dw_mipi_csi2rx_device *csi2); + int (*wait_for_phy_stopstate)(struct dw_mipi_csi2rx_device *csi2); }; struct dw_mipi_csi2rx_format { @@ -139,6 +144,7 @@ static const u32 imx93_regs[DW_MIPI_CSI2RX_MAX] = { [DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40), [DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44), [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48), + [DW_MIPI_CSI2RX_PHY_STOPSTATE] = DW_REG(0x4c), [DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50), [DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54), [DW_MIPI_CSI2RX_IPI_MODE] = DW_REG(0x80), @@ -556,10 +562,19 @@ static int dw_mipi_csi2rx_enable_streams(struct v4l2_subdev *sd, if (ret) goto err_csi_stop; + if (!csi2->enabled_streams && + csi2->drvdata->wait_for_phy_stopstate) { + ret = csi2->drvdata->wait_for_phy_stopstate(csi2); + if (ret) + goto err_disable_streams; + } + csi2->enabled_streams |= streams_mask; return 0; +err_disable_streams: + v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask); err_csi_stop: /* Stop CSI hardware if no streams are enabled */ if (!csi2->enabled_streams) @@ -871,11 +886,32 @@ static void imx93_csi2rx_dphy_ipi_enable(struct dw_mipi_csi2rx_device *csi2) dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MODE, val); } +static int imx93_csi2rx_wait_for_phy_stopstate(struct dw_mipi_csi2rx_device *csi2) +{ + struct device *dev = csi2->dev; + u32 stopstate_mask; + u32 val; + int ret; + + stopstate_mask = DPHY_STOPSTATE_CLK_LANE | GENMASK(csi2->lanes_num - 1, 0); + + ret = read_poll_timeout(dw_mipi_csi2rx_read, val, + (val & stopstate_mask) == stopstate_mask, + 10, 1000, true, + csi2, DW_MIPI_CSI2RX_PHY_STOPSTATE); + if (ret) + dev_err(dev, "lanes are not in stop state: %#x, expected %#x\n", + val, stopstate_mask); + + return ret; +} + static const struct dw_mipi_csi2rx_drvdata imx93_drvdata = { .regs = imx93_regs, .dphy_assert_reset = imx93_csi2rx_dphy_assert_reset, .dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset, .ipi_enable = imx93_csi2rx_dphy_ipi_enable, + .wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate, }; static const struct of_device_id dw_mipi_csi2rx_of_match[] = { From 113423645c9363acdb03859c65b5b030ae9ddd43 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 19 May 2026 10:07:42 +0800 Subject: [PATCH 264/394] media: dt-bindings: add NXP i.MX95 compatible string The i.MX95 CSI-2 controller is nearly identical to i.MX93, with the main difference being the data output interface: i.MX93 use IPI (Image Pixel Interface), which requires: - Pixel clock input - Software configuration through registers i.MX95 uses IDI (Image Data Interface), which: - Does not require pixel clock - Is software transparent (no register configuration needed) Due to these differences in register layout and initialization needs, the two variants cannot share the same compatible string. The driver needs to distinguish between them to handle the interface correctly. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Signed-off-by: Sakari Ailus --- .../media/rockchip,rk3568-mipi-csi2.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml index fbcf28e9e1da..8bfad0fca3b7 100644 --- a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml +++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml @@ -19,6 +19,7 @@ properties: oneOf: - enum: - fsl,imx93-mipi-csi2 + - fsl,imx95-mipi-csi2 - rockchip,rk3568-mipi-csi2 - items: - enum: @@ -140,6 +141,21 @@ allOf: clock-names: minItems: 2 + - if: + properties: + compatible: + contains: + const: fsl,imx95-mipi-csi2 + then: + properties: + interrupts: + maxItems: 1 + interrupt-names: false + clocks: + maxItems: 1 + clock-names: + maxItems: 1 + examples: - | #include From af92c6d86b2bc3849935dc6ecfe78eba016a47a4 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 19 May 2026 10:07:43 +0800 Subject: [PATCH 265/394] media: synopsys: Add support for i.MX95 Add support for the i.MX95 MIPI CSI-2 receiver. The i.MX95 variant is nearly identical to i.MX93, with the main difference being the use of IDI (Image Data Interface) instead of IPI (Image Pixel Interface). However, the IDI interface is transparent to software, requiring only a different register map definition while sharing the same PHY control functions with i.MX93. Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Signed-off-by: Sakari Ailus --- .../media/platform/synopsys/dw-mipi-csi2rx.c | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c index 8a34aec550ad..41e48365167e 100644 --- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -154,6 +154,17 @@ static const u32 imx93_regs[DW_MIPI_CSI2RX_MAX] = { [DW_MIPI_CSI2RX_IPI_SOFTRSTN] = DW_REG(0xa0), }; +static const u32 imx95_regs[DW_MIPI_CSI2RX_MAX] = { + [DW_MIPI_CSI2RX_N_LANES] = DW_REG(0x4), + [DW_MIPI_CSI2RX_RESETN] = DW_REG(0x8), + [DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40), + [DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44), + [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48), + [DW_MIPI_CSI2RX_PHY_STOPSTATE] = DW_REG(0x4c), + [DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50), + [DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54), +}; + static const struct v4l2_mbus_framefmt default_format = { .width = 3840, .height = 2160, @@ -914,11 +925,22 @@ static const struct dw_mipi_csi2rx_drvdata imx93_drvdata = { .wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate, }; +static const struct dw_mipi_csi2rx_drvdata imx95_drvdata = { + .regs = imx95_regs, + .dphy_assert_reset = imx93_csi2rx_dphy_assert_reset, + .dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset, + .wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate, +}; + static const struct of_device_id dw_mipi_csi2rx_of_match[] = { { .compatible = "fsl,imx93-mipi-csi2", .data = &imx93_drvdata, }, + { + .compatible = "fsl,imx95-mipi-csi2", + .data = &imx95_drvdata, + }, { .compatible = "rockchip,rk3568-mipi-csi2", .data = &rk3568_drvdata, From 614e627e0d0bf3de3819c098adde06cad633be2b Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Tue, 19 May 2026 23:00:30 +0300 Subject: [PATCH 266/394] media: i2c: imx274: trivial cleanup Fix some typos, removed superfluous comments/code, some minor code alignment. Signed-off-by: Eugen Hristev Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx274.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 882dc2e9dd8f..76da647f9cf9 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -182,7 +182,7 @@ struct imx274_mode { }; /* - * imx274 test pattern related structure + * imx274 test pattern related enum */ enum { TEST_PATTERN_DISABLED = 0, @@ -533,7 +533,7 @@ static const struct imx274_mode imx274_modes[] = { /* * struct imx274_ctrls - imx274 ctrl structure * @handler: V4L2 ctrl handler structure - * @exposure: Pointer to expsure ctrl structure + * @exposure: Pointer to exposure ctrl structure * @gain: Pointer to gain ctrl structure * @vflip: Pointer to vflip ctrl structure * @test_pattern: Pointer to test pattern ctrl structure @@ -547,7 +547,7 @@ struct imx274_ctrls { }; /* - * struct stim274 - imx274 device structure + * struct stimx274 - imx274 device structure * @sd: V4L2 subdevice structure * @pad: Media pad structure * @client: Pointer to I2C client @@ -587,9 +587,6 @@ struct stimx274 { ? rounddown((dim), (step)) \ : rounddown((dim) + (step) / 2, (step)))) -/* - * Function declaration - */ static int imx274_set_gain(struct stimx274 *priv, struct v4l2_ctrl *ctrl); static int imx274_set_exposure(struct stimx274 *priv, int val); static int imx274_set_vflip(struct stimx274 *priv, int val); @@ -640,9 +637,9 @@ static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[]) for (next = table;; next++) { if ((next->addr != range_start + range_count) || - (next->addr == IMX274_TABLE_END) || - (next->addr == IMX274_TABLE_WAIT_MS) || - (range_count == max_range_vals)) { + next->addr == IMX274_TABLE_END || + next->addr == IMX274_TABLE_WAIT_MS || + range_count == max_range_vals) { if (range_count == 1) err = regmap_write(regmap, range_start, range_vals[0]); @@ -650,8 +647,6 @@ static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[]) err = regmap_bulk_write(regmap, range_start, &range_vals[0], range_count); - else - err = 0; if (err) return err; @@ -1960,7 +1955,6 @@ static int imx274_probe(struct i2c_client *client) struct device *dev = &client->dev; int ret; - /* initialize imx274 */ imx274 = devm_kzalloc(dev, sizeof(*imx274), GFP_KERNEL); if (!imx274) return -ENOMEM; @@ -2125,7 +2119,7 @@ static const struct dev_pm_ops imx274_pm_ops = { static struct i2c_driver imx274_i2c_driver = { .driver = { - .name = DRIVER_NAME, + .name = DRIVER_NAME, .pm = &imx274_pm_ops, .of_match_table = imx274_of_id_table, }, From 148d96822b100a1ff81247f37957722074d07c81 Mon Sep 17 00:00:00 2001 From: Rishikesh Donadkar Date: Wed, 20 May 2026 17:30:06 +0530 Subject: [PATCH 267/394] media: ti: j721e-csi2rx: Remove word size alignment on frame width j721e-csi2rx driver has a limitation of frame width being a multiple word size. However, there is no such limitation imposed by the hardware [1]. Remove this limitation from the driver. Link: https://www.ti.com/lit/pdf/spruj16 Reviewed-by: Yemike Abhilash Chandra Reviewed-by: Tomi Valkeinen Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index b75aa363d1bf..710d05a05353 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -43,7 +43,6 @@ #define SHIM_PSI_CFG0_DST_TAG GENMASK(31, 16) #define TI_CSI2RX_MAX_PIX_PER_CLK 4 -#define PSIL_WORD_SIZE_BYTES 16 /* * There are no hard limits on the width or height. The DMA engine can handle * all sizes. The max width and height are arbitrary numbers for this driver. @@ -250,19 +249,12 @@ static void ti_csi2rx_fill_fmt(const struct ti_csi2rx_fmt *csi_fmt, struct v4l2_format *v4l2_fmt) { struct v4l2_pix_format *pix = &v4l2_fmt->fmt.pix; - unsigned int pixels_in_word; - - pixels_in_word = PSIL_WORD_SIZE_BYTES * 8 / csi_fmt->bpp; /* Clamp width and height to sensible maximums (16K x 16K) */ pix->width = clamp_t(unsigned int, pix->width, - pixels_in_word, - MAX_WIDTH_BYTES * 8 / csi_fmt->bpp); + 1, MAX_WIDTH_BYTES * 8 / csi_fmt->bpp); pix->height = clamp_t(unsigned int, pix->height, 1, MAX_HEIGHT_LINES); - /* Width should be a multiple of transfer word-size */ - pix->width = rounddown(pix->width, pixels_in_word); - v4l2_fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; pix->pixelformat = csi_fmt->fourcc; pix->bytesperline = pix->width * (csi_fmt->bpp / 8); @@ -360,23 +352,15 @@ static int ti_csi2rx_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { const struct ti_csi2rx_fmt *fmt; - unsigned int pixels_in_word; fmt = find_format_by_fourcc(fsize->pixel_format); if (!fmt || fsize->index != 0) return -EINVAL; - /* - * Number of pixels in one PSI-L word. The transfer happens in multiples - * of PSI-L word sizes. - */ - pixels_in_word = PSIL_WORD_SIZE_BYTES * 8 / fmt->bpp; - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise.min_width = pixels_in_word; - fsize->stepwise.max_width = rounddown(MAX_WIDTH_BYTES * 8 / fmt->bpp, - pixels_in_word); - fsize->stepwise.step_width = pixels_in_word; + fsize->stepwise.min_width = 1; + fsize->stepwise.max_width = MAX_WIDTH_BYTES * 8 / fmt->bpp; + fsize->stepwise.step_width = 1; fsize->stepwise.min_height = 1; fsize->stepwise.max_height = MAX_HEIGHT_LINES; fsize->stepwise.step_height = 1; From bfd10a287ca277508b822f49ad469dbee316b597 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:07 +0530 Subject: [PATCH 268/394] dt-bindings: media: ti,j721e-csi2rx-shim: Support 32 dma chans The CSI2RX SHIM IP can support 32x DMA channels. These can be used to split incoming "streams" of data on the CSI-RX port, distinguished by MIPI Virtual Channel (or Data Type), into different locations in memory. Actual number of DMA channels allocated to CSI-RX is dependent on the usecase, and can be modified using the K3 Resource Partitioning tool [1]. So set the minimum channels as 1 and maximum as 32. Link: https://software-dl.ti.com/processor-sdk-linux/esd/AM62X/10_00_07_04/exports/docs/linux/How_to_Guides/Host/K3_Resource_Partitioning_Tool.html [1] Link: https://www.ti.com/lit/pdf/spruiv7 Signed-off-by: Jai Luthra Reviewed-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart Reviewed-by: Rob Herring (Arm) Reviewed-by: Yemike Abhilash Chandra Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- .../bindings/media/ti,j721e-csi2rx-shim.yaml | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml b/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml index b9f033f2f3ce..bf62998b0445 100644 --- a/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml +++ b/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml @@ -20,11 +20,44 @@ properties: const: ti,j721e-csi2rx-shim dmas: - maxItems: 1 + minItems: 1 + maxItems: 32 dma-names: + minItems: 1 items: - const: rx0 + - const: rx1 + - const: rx2 + - const: rx3 + - const: rx4 + - const: rx5 + - const: rx6 + - const: rx7 + - const: rx8 + - const: rx9 + - const: rx10 + - const: rx11 + - const: rx12 + - const: rx13 + - const: rx14 + - const: rx15 + - const: rx16 + - const: rx17 + - const: rx18 + - const: rx19 + - const: rx20 + - const: rx21 + - const: rx22 + - const: rx23 + - const: rx24 + - const: rx25 + - const: rx26 + - const: rx27 + - const: rx28 + - const: rx29 + - const: rx30 + - const: rx31 reg: maxItems: 1 @@ -62,8 +95,8 @@ examples: ti_csi2rx0: ticsi2rx@4500000 { compatible = "ti,j721e-csi2rx-shim"; - dmas = <&main_udmap 0x4940>; - dma-names = "rx0"; + dmas = <&main_udmap 0x4940>, <&main_udmap 0x4941>; + dma-names = "rx0", "rx1"; reg = <0x4500000 0x1000>; power-domains = <&k3_pds 26 TI_SCI_PD_EXCLUSIVE>; #address-cells = <1>; From c071e5fd44944745c078df12f27f86916b214ab7 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:08 +0530 Subject: [PATCH 269/394] media: ti: j721e-csi2rx: separate out device and context The TI CSI2RX wrapper has two parts: the main device and the DMA contexts. The driver was originally written with single camera capture in mind, so only one DMA context was needed. For the sake of simplicity, the context specific stuff was not modeled different to the main device. To enable multiplexed stream capture, the contexts need to be separated out from the main device. Create a struct ti_csi2rx_ctx that holds the DMA context specific things. Separate out functions handling the device and context related functionality. Reviewed-by: Yemike Abhilash Chandra Reviewed-by: Tomi Valkeinen Co-developed-by: Pratyush Yadav Signed-off-by: Pratyush Yadav Signed-off-by: Jai Luthra Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 425 ++++++++++-------- 1 file changed, 235 insertions(+), 190 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 710d05a05353..23717a3b6c4c 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -43,6 +43,8 @@ #define SHIM_PSI_CFG0_DST_TAG GENMASK(31, 16) #define TI_CSI2RX_MAX_PIX_PER_CLK 4 +#define TI_CSI2RX_NUM_CTX 1 + /* * There are no hard limits on the width or height. The DMA engine can handle * all sizes. The max width and height are arbitrary numbers for this driver. @@ -69,7 +71,7 @@ struct ti_csi2rx_buffer { /* Common v4l2 buffer. Must be first. */ struct vb2_v4l2_buffer vb; struct list_head list; - struct ti_csi2rx_dev *csi; + struct ti_csi2rx_ctx *ctx; }; enum ti_csi2rx_dma_state { @@ -89,30 +91,38 @@ struct ti_csi2rx_dma { * Queue of buffers submitted to DMA engine. */ struct list_head submitted; - /* Buffer to drain stale data from PSI-L endpoint */ - struct { - void *vaddr; - dma_addr_t paddr; - size_t len; - } drain; +}; + +struct ti_csi2rx_dev; + +struct ti_csi2rx_ctx { + struct ti_csi2rx_dev *csi; + struct video_device vdev; + struct vb2_queue vidq; + struct mutex mutex; /* To serialize ioctls. */ + struct v4l2_format v_fmt; + struct ti_csi2rx_dma dma; + u32 sequence; + u32 idx; }; struct ti_csi2rx_dev { struct device *dev; void __iomem *shim; struct v4l2_device v4l2_dev; - struct video_device vdev; struct media_device mdev; struct media_pipeline pipe; struct media_pad pad; struct v4l2_async_notifier notifier; struct v4l2_subdev *source; - struct vb2_queue vidq; - struct mutex mutex; /* To serialize ioctls. */ - struct v4l2_format v_fmt; - struct ti_csi2rx_dma dma; - u32 sequence; + struct ti_csi2rx_ctx ctx[TI_CSI2RX_NUM_CTX]; u8 pix_per_clk; + /* Buffer to drain stale data from PSI-L endpoint */ + struct { + void *vaddr; + dma_addr_t paddr; + size_t len; + } drain; }; static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = { @@ -218,7 +228,7 @@ static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = { }; /* Forward declaration needed by ti_csi2rx_dma_callback. */ -static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi, +static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx, struct ti_csi2rx_buffer *buf); static const struct ti_csi2rx_fmt *find_format_by_fourcc(u32 pixelformat) @@ -301,7 +311,7 @@ static int ti_csi2rx_enum_fmt_vid_cap(struct file *file, void *priv, static int ti_csi2rx_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct ti_csi2rx_dev *csi = video_drvdata(file); + struct ti_csi2rx_ctx *csi = video_drvdata(file); *f = csi->v_fmt; @@ -332,7 +342,7 @@ static int ti_csi2rx_try_fmt_vid_cap(struct file *file, void *priv, static int ti_csi2rx_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct ti_csi2rx_dev *csi = video_drvdata(file); + struct ti_csi2rx_ctx *csi = video_drvdata(file); struct vb2_queue *q = &csi->vidq; int ret; @@ -410,26 +420,35 @@ static int csi_async_notifier_bound(struct v4l2_async_notifier *notifier, static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier) { struct ti_csi2rx_dev *csi = dev_get_drvdata(notifier->v4l2_dev->dev); - struct video_device *vdev = &csi->vdev; - int ret; + int ret, i; - ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - if (ret) - return ret; + for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) { + struct ti_csi2rx_ctx *ctx = &csi->ctx[i]; + struct video_device *vdev = &ctx->vdev; - ret = media_create_pad_link(&csi->source->entity, CSI2RX_BRIDGE_SOURCE_PAD, - &vdev->entity, csi->pad.index, - MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); - - if (ret) { - video_unregister_device(vdev); - return ret; + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) + goto unregister_dev; } + ret = media_create_pad_link(&csi->source->entity, + CSI2RX_BRIDGE_SOURCE_PAD, + &csi->ctx[0].vdev.entity, csi->pad.index, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + goto unregister_dev; + ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev); if (ret) - video_unregister_device(vdev); + goto unregister_dev; + return 0; + +unregister_dev: + i--; + for (; i >= 0; i--) + video_unregister_device(&csi->ctx[i].vdev); return ret; } @@ -474,13 +493,14 @@ static int ti_csi2rx_notifier_register(struct ti_csi2rx_dev *csi) } /* Request maximum possible pixels per clock from the bridge */ -static void ti_csi2rx_request_max_ppc(struct ti_csi2rx_dev *csi) +static void ti_csi2rx_request_max_ppc(struct ti_csi2rx_ctx *ctx) { + struct ti_csi2rx_dev *csi = ctx->csi; u8 ppc = TI_CSI2RX_MAX_PIX_PER_CLK; struct media_pad *pad; int ret; - pad = media_entity_remote_source_pad_unique(&csi->vdev.entity); + pad = media_entity_remote_source_pad_unique(&ctx->vdev.entity); if (IS_ERR(pad)) return; @@ -493,19 +513,20 @@ static void ti_csi2rx_request_max_ppc(struct ti_csi2rx_dev *csi) } } -static void ti_csi2rx_setup_shim(struct ti_csi2rx_dev *csi) +static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) { + struct ti_csi2rx_dev *csi = ctx->csi; const struct ti_csi2rx_fmt *fmt; unsigned int reg; - fmt = find_format_by_fourcc(csi->v_fmt.fmt.pix.pixelformat); + fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat); /* De-assert the pixel interface reset. */ reg = SHIM_CNTL_PIX_RST; writel(reg, csi->shim + SHIM_CNTL); /* Negotiate pixel count from the source */ - ti_csi2rx_request_max_ppc(csi); + ti_csi2rx_request_max_ppc(ctx); reg = SHIM_DMACNTX_EN; reg |= FIELD_PREP(SHIM_DMACNTX_FMT, fmt->csi_dt); @@ -572,8 +593,9 @@ static void ti_csi2rx_drain_callback(void *param) * To prevent that stale data corrupting the subsequent transactions, it is * required to issue DMA requests to drain it out. */ -static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi) +static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx) { + struct ti_csi2rx_dev *csi = ctx->csi; struct dma_async_tx_descriptor *desc; struct completion drain_complete; dma_cookie_t cookie; @@ -581,8 +603,8 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi) init_completion(&drain_complete); - desc = dmaengine_prep_slave_single(csi->dma.chan, csi->dma.drain.paddr, - csi->dma.drain.len, DMA_DEV_TO_MEM, + desc = dmaengine_prep_slave_single(ctx->dma.chan, csi->drain.paddr, + csi->drain.len, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { ret = -EIO; @@ -597,11 +619,11 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi) if (ret) goto out; - dma_async_issue_pending(csi->dma.chan); + dma_async_issue_pending(ctx->dma.chan); if (!wait_for_completion_timeout(&drain_complete, msecs_to_jiffies(DRAIN_TIMEOUT_MS))) { - dmaengine_terminate_sync(csi->dma.chan); + dmaengine_terminate_sync(ctx->dma.chan); dev_dbg(csi->dev, "DMA transfer timed out for drain buffer\n"); ret = -ETIMEDOUT; goto out; @@ -613,8 +635,9 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi) static void ti_csi2rx_dma_callback(void *param) { struct ti_csi2rx_buffer *buf = param; - struct ti_csi2rx_dev *csi = buf->csi; - struct ti_csi2rx_dma *dma = &csi->dma; + struct ti_csi2rx_ctx *ctx = buf->ctx; + struct ti_csi2rx_dev *csi = ctx->csi; + struct ti_csi2rx_dma *dma = &ctx->dma; unsigned long flags; /* @@ -622,7 +645,7 @@ static void ti_csi2rx_dma_callback(void *param) * hardware monitor registers. */ buf->vb.vb2_buf.timestamp = ktime_get_ns(); - buf->vb.sequence = csi->sequence++; + buf->vb.sequence = ctx->sequence++; spin_lock_irqsave(&dma->lock, flags); @@ -634,7 +657,7 @@ static void ti_csi2rx_dma_callback(void *param) while (!list_empty(&dma->queue)) { buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); - if (ti_csi2rx_start_dma(csi, buf)) { + if (ti_csi2rx_start_dma(ctx, buf)) { dev_err(csi->dev, "Failed to queue the next buffer for DMA\n"); list_del(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); @@ -649,17 +672,17 @@ static void ti_csi2rx_dma_callback(void *param) spin_unlock_irqrestore(&dma->lock, flags); } -static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi, +static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx, struct ti_csi2rx_buffer *buf) { unsigned long addr; struct dma_async_tx_descriptor *desc; - size_t len = csi->v_fmt.fmt.pix.sizeimage; + size_t len = ctx->v_fmt.fmt.pix.sizeimage; dma_cookie_t cookie; int ret = 0; addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - desc = dmaengine_prep_slave_single(csi->dma.chan, addr, len, + desc = dmaengine_prep_slave_single(ctx->dma.chan, addr, len, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) @@ -673,20 +696,20 @@ static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi, if (ret) return ret; - dma_async_issue_pending(csi->dma.chan); + dma_async_issue_pending(ctx->dma.chan); return 0; } -static void ti_csi2rx_stop_dma(struct ti_csi2rx_dev *csi) +static void ti_csi2rx_stop_dma(struct ti_csi2rx_ctx *ctx) { - struct ti_csi2rx_dma *dma = &csi->dma; + struct ti_csi2rx_dma *dma = &ctx->dma; enum ti_csi2rx_dma_state state; unsigned long flags; int ret; spin_lock_irqsave(&dma->lock, flags); - state = csi->dma.state; + state = ctx->dma.state; dma->state = TI_CSI2RX_DMA_STOPPED; spin_unlock_irqrestore(&dma->lock, flags); @@ -697,30 +720,30 @@ static void ti_csi2rx_stop_dma(struct ti_csi2rx_dev *csi) * is stopped, as the module-level pixel reset cannot be * enforced before terminating DMA. */ - ret = ti_csi2rx_drain_dma(csi); + ret = ti_csi2rx_drain_dma(ctx); if (ret && ret != -ETIMEDOUT) - dev_warn(csi->dev, + dev_warn(ctx->csi->dev, "Failed to drain DMA. Next frame might be bogus\n"); } - ret = dmaengine_terminate_sync(csi->dma.chan); + ret = dmaengine_terminate_sync(ctx->dma.chan); if (ret) - dev_err(csi->dev, "Failed to stop DMA: %d\n", ret); + dev_err(ctx->csi->dev, "Failed to stop DMA: %d\n", ret); } -static void ti_csi2rx_cleanup_buffers(struct ti_csi2rx_dev *csi, +static void ti_csi2rx_cleanup_buffers(struct ti_csi2rx_ctx *ctx, enum vb2_buffer_state state) { - struct ti_csi2rx_dma *dma = &csi->dma; + struct ti_csi2rx_dma *dma = &ctx->dma; struct ti_csi2rx_buffer *buf, *tmp; unsigned long flags; spin_lock_irqsave(&dma->lock, flags); - list_for_each_entry_safe(buf, tmp, &csi->dma.queue, list) { + list_for_each_entry_safe(buf, tmp, &ctx->dma.queue, list) { list_del(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, state); } - list_for_each_entry_safe(buf, tmp, &csi->dma.submitted, list) { + list_for_each_entry_safe(buf, tmp, &ctx->dma.submitted, list) { list_del(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, state); } @@ -731,8 +754,8 @@ static int ti_csi2rx_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(q); - unsigned int size = csi->v_fmt.fmt.pix.sizeimage; + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(q); + unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; if (*nplanes) { if (sizes[0] < size) @@ -748,11 +771,11 @@ static int ti_csi2rx_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, static int ti_csi2rx_buffer_prepare(struct vb2_buffer *vb) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vb->vb2_queue); - unsigned long size = csi->v_fmt.fmt.pix.sizeimage; + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = ctx->v_fmt.fmt.pix.sizeimage; if (vb2_plane_size(vb, 0) < size) { - dev_err(csi->dev, "Data will not fit into plane\n"); + dev_err(ctx->csi->dev, "Data will not fit into plane\n"); return -EINVAL; } @@ -762,15 +785,15 @@ static int ti_csi2rx_buffer_prepare(struct vb2_buffer *vb) static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vb->vb2_queue); + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct ti_csi2rx_buffer *buf; - struct ti_csi2rx_dma *dma = &csi->dma; + struct ti_csi2rx_dma *dma = &ctx->dma; bool restart_dma = false; unsigned long flags = 0; int ret; buf = container_of(vb, struct ti_csi2rx_buffer, vb.vb2_buf); - buf->csi = csi; + buf->ctx = ctx; spin_lock_irqsave(&dma->lock, flags); /* @@ -799,18 +822,18 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) * the application and will only confuse it. Issue a DMA * transaction to drain that up. */ - ret = ti_csi2rx_drain_dma(csi); + ret = ti_csi2rx_drain_dma(ctx); if (ret && ret != -ETIMEDOUT) - dev_warn(csi->dev, + dev_warn(ctx->csi->dev, "Failed to drain DMA. Next frame might be bogus\n"); spin_lock_irqsave(&dma->lock, flags); - ret = ti_csi2rx_start_dma(csi, buf); + ret = ti_csi2rx_start_dma(ctx, buf); if (ret) { vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); dma->state = TI_CSI2RX_DMA_IDLE; spin_unlock_irqrestore(&dma->lock, flags); - dev_err(csi->dev, "Failed to start DMA: %d\n", ret); + dev_err(ctx->csi->dev, "Failed to start DMA: %d\n", ret); } else { list_add_tail(&buf->list, &dma->submitted); spin_unlock_irqrestore(&dma->lock, flags); @@ -820,8 +843,9 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vq); - struct ti_csi2rx_dma *dma = &csi->dma; + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq); + struct ti_csi2rx_dev *csi = ctx->csi; + struct ti_csi2rx_dma *dma = &ctx->dma; struct ti_csi2rx_buffer *buf; unsigned long flags; int ret = 0; @@ -833,18 +857,18 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) return ret; - ret = video_device_pipeline_start(&csi->vdev, &csi->pipe); + ret = video_device_pipeline_start(&ctx->vdev, &csi->pipe); if (ret) goto err; - ti_csi2rx_setup_shim(csi); + ti_csi2rx_setup_shim(ctx); - csi->sequence = 0; + ctx->sequence = 0; spin_lock_irqsave(&dma->lock, flags); buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); - ret = ti_csi2rx_start_dma(csi, buf); + ret = ti_csi2rx_start_dma(ctx, buf); if (ret) { dev_err(csi->dev, "Failed to start DMA: %d\n", ret); spin_unlock_irqrestore(&dma->lock, flags); @@ -862,22 +886,23 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) return 0; err_dma: - ti_csi2rx_stop_dma(csi); + ti_csi2rx_stop_dma(ctx); err_pipeline: - video_device_pipeline_stop(&csi->vdev); + video_device_pipeline_stop(&ctx->vdev); writel(0, csi->shim + SHIM_CNTL); writel(0, csi->shim + SHIM_DMACNTX); err: - ti_csi2rx_cleanup_buffers(csi, VB2_BUF_STATE_QUEUED); + ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_QUEUED); return ret; } static void ti_csi2rx_stop_streaming(struct vb2_queue *vq) { - struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vq); + struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq); + struct ti_csi2rx_dev *csi = ctx->csi; int ret; - video_device_pipeline_stop(&csi->vdev); + video_device_pipeline_stop(&ctx->vdev); writel(0, csi->shim + SHIM_CNTL); writel(0, csi->shim + SHIM_DMACNTX); @@ -886,8 +911,8 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq) if (ret) dev_err(csi->dev, "Failed to stop subdev stream\n"); - ti_csi2rx_stop_dma(csi); - ti_csi2rx_cleanup_buffers(csi, VB2_BUF_STATE_ERROR); + ti_csi2rx_stop_dma(ctx); + ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_ERROR); } static const struct vb2_ops csi_vb2_qops = { @@ -898,20 +923,43 @@ static const struct vb2_ops csi_vb2_qops = { .stop_streaming = ti_csi2rx_stop_streaming, }; -static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi) +static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi) { - struct vb2_queue *q = &csi->vidq; + media_device_unregister(&csi->mdev); + v4l2_device_unregister(&csi->v4l2_dev); + media_device_cleanup(&csi->mdev); +} + +static void ti_csi2rx_cleanup_notifier(struct ti_csi2rx_dev *csi) +{ + v4l2_async_nf_unregister(&csi->notifier); + v4l2_async_nf_cleanup(&csi->notifier); +} + +static void ti_csi2rx_cleanup_ctx(struct ti_csi2rx_ctx *ctx) +{ + dma_release_channel(ctx->dma.chan); + vb2_queue_release(&ctx->vidq); + + video_unregister_device(&ctx->vdev); + + mutex_destroy(&ctx->mutex); +} + +static int ti_csi2rx_init_vb2q(struct ti_csi2rx_ctx *ctx) +{ + struct vb2_queue *q = &ctx->vidq; int ret; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_DMABUF; - q->drv_priv = csi; + q->drv_priv = ctx; q->buf_struct_size = sizeof(struct ti_csi2rx_buffer); q->ops = &csi_vb2_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->dev = dmaengine_get_dma_device(csi->dma.chan); - q->lock = &csi->mutex; + q->dev = dmaengine_get_dma_device(ctx->dma.chan); + q->lock = &ctx->mutex; q->min_queued_buffers = 1; q->allow_cache_hints = 1; @@ -919,7 +967,7 @@ static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi) if (ret) return ret; - csi->vdev.queue = q; + ctx->vdev.queue = q; return 0; } @@ -928,8 +976,9 @@ static int ti_csi2rx_link_validate(struct media_link *link) { struct media_entity *entity = link->sink->entity; struct video_device *vdev = media_entity_to_video_device(entity); - struct ti_csi2rx_dev *csi = container_of(vdev, struct ti_csi2rx_dev, vdev); - struct v4l2_pix_format *csi_fmt = &csi->v_fmt.fmt.pix; + struct ti_csi2rx_ctx *ctx = container_of(vdev, struct ti_csi2rx_ctx, vdev); + struct ti_csi2rx_dev *csi = ctx->csi; + struct v4l2_pix_format *csi_fmt = &ctx->v_fmt.fmt.pix; struct v4l2_subdev_format source_fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, .pad = link->source->index, @@ -982,84 +1031,37 @@ static const struct media_entity_operations ti_csi2rx_video_entity_ops = { .link_validate = ti_csi2rx_link_validate, }; -static int ti_csi2rx_init_dma(struct ti_csi2rx_dev *csi) +static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx) { struct dma_slave_config cfg = { .src_addr_width = DMA_SLAVE_BUSWIDTH_16_BYTES, }; int ret; - INIT_LIST_HEAD(&csi->dma.queue); - INIT_LIST_HEAD(&csi->dma.submitted); - spin_lock_init(&csi->dma.lock); + ctx->dma.chan = dma_request_chan(ctx->csi->dev, "rx0"); + if (IS_ERR(ctx->dma.chan)) + return PTR_ERR(ctx->dma.chan); - csi->dma.state = TI_CSI2RX_DMA_STOPPED; - - csi->dma.chan = dma_request_chan(csi->dev, "rx0"); - if (IS_ERR(csi->dma.chan)) - return PTR_ERR(csi->dma.chan); - - ret = dmaengine_slave_config(csi->dma.chan, &cfg); + ret = dmaengine_slave_config(ctx->dma.chan, &cfg); if (ret) { - dma_release_channel(csi->dma.chan); + dma_release_channel(ctx->dma.chan); return ret; } - csi->dma.drain.len = DRAIN_BUFFER_SIZE; - csi->dma.drain.vaddr = dma_alloc_coherent(csi->dev, csi->dma.drain.len, - &csi->dma.drain.paddr, - GFP_KERNEL); - if (!csi->dma.drain.vaddr) - return -ENOMEM; - return 0; } static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) { struct media_device *mdev = &csi->mdev; - struct video_device *vdev = &csi->vdev; - const struct ti_csi2rx_fmt *fmt; - struct v4l2_pix_format *pix_fmt = &csi->v_fmt.fmt.pix; int ret; - fmt = find_format_by_fourcc(V4L2_PIX_FMT_UYVY); - if (!fmt) - return -EINVAL; - - pix_fmt->width = 640; - pix_fmt->height = 480; - pix_fmt->field = V4L2_FIELD_NONE; - pix_fmt->colorspace = V4L2_COLORSPACE_SRGB; - pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; - pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; - pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB; - - ti_csi2rx_fill_fmt(fmt, &csi->v_fmt); - mdev->dev = csi->dev; mdev->hw_revision = 1; strscpy(mdev->model, "TI-CSI2RX", sizeof(mdev->model)); media_device_init(mdev); - strscpy(vdev->name, TI_CSI2RX_MODULE_NAME, sizeof(vdev->name)); - vdev->v4l2_dev = &csi->v4l2_dev; - vdev->vfl_dir = VFL_DIR_RX; - vdev->fops = &csi_fops; - vdev->ioctl_ops = &csi_ioctl_ops; - vdev->release = video_device_release_empty; - vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_IO_MC; - vdev->lock = &csi->mutex; - video_set_drvdata(vdev, csi); - - csi->pad.flags = MEDIA_PAD_FL_SINK; - vdev->entity.ops = &ti_csi2rx_video_entity_ops; - ret = media_entity_pads_init(&csi->vdev.entity, 1, &csi->pad); - if (ret) - return ret; - csi->v4l2_dev.mdev = mdev; ret = v4l2_device_register(csi->dev, &csi->v4l2_dev); @@ -1076,36 +1078,72 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) return 0; } -static void ti_csi2rx_cleanup_dma(struct ti_csi2rx_dev *csi) +static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx) { - dma_free_coherent(csi->dev, csi->dma.drain.len, - csi->dma.drain.vaddr, csi->dma.drain.paddr); - csi->dma.drain.vaddr = NULL; - dma_release_channel(csi->dma.chan); -} + struct ti_csi2rx_dev *csi = ctx->csi; + struct video_device *vdev = &ctx->vdev; + const struct ti_csi2rx_fmt *fmt; + struct v4l2_pix_format *pix_fmt = &ctx->v_fmt.fmt.pix; + int ret; -static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi) -{ - media_device_unregister(&csi->mdev); - v4l2_device_unregister(&csi->v4l2_dev); - media_device_cleanup(&csi->mdev); -} + mutex_init(&ctx->mutex); -static void ti_csi2rx_cleanup_subdev(struct ti_csi2rx_dev *csi) -{ - v4l2_async_nf_unregister(&csi->notifier); - v4l2_async_nf_cleanup(&csi->notifier); -} + fmt = find_format_by_fourcc(V4L2_PIX_FMT_UYVY); + if (!fmt) + return -EINVAL; -static void ti_csi2rx_cleanup_vb2q(struct ti_csi2rx_dev *csi) -{ - vb2_queue_release(&csi->vidq); + pix_fmt->width = 640; + pix_fmt->height = 480; + pix_fmt->field = V4L2_FIELD_NONE; + pix_fmt->colorspace = V4L2_COLORSPACE_SRGB; + pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601, + pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE, + pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB, + + ti_csi2rx_fill_fmt(fmt, &ctx->v_fmt); + + csi->pad.flags = MEDIA_PAD_FL_SINK; + vdev->entity.ops = &ti_csi2rx_video_entity_ops; + ret = media_entity_pads_init(&ctx->vdev.entity, 1, &csi->pad); + if (ret) + return ret; + + snprintf(vdev->name, sizeof(vdev->name), "%s context %u", + dev_name(csi->dev), ctx->idx); + vdev->v4l2_dev = &csi->v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->fops = &csi_fops; + vdev->ioctl_ops = &csi_ioctl_ops; + vdev->release = video_device_release_empty; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_IO_MC; + vdev->lock = &ctx->mutex; + video_set_drvdata(vdev, ctx); + + INIT_LIST_HEAD(&ctx->dma.queue); + INIT_LIST_HEAD(&ctx->dma.submitted); + spin_lock_init(&ctx->dma.lock); + ctx->dma.state = TI_CSI2RX_DMA_STOPPED; + + ret = ti_csi2rx_init_dma(ctx); + if (ret) + return ret; + + ret = ti_csi2rx_init_vb2q(ctx); + if (ret) + goto cleanup_dma; + + return 0; + +cleanup_dma: + dma_release_channel(ctx->dma.chan); + return ret; } static int ti_csi2rx_probe(struct platform_device *pdev) { struct ti_csi2rx_dev *csi; - int ret; + int ret, i; csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL); if (!csi) @@ -1114,62 +1152,69 @@ static int ti_csi2rx_probe(struct platform_device *pdev) csi->dev = &pdev->dev; platform_set_drvdata(pdev, csi); - mutex_init(&csi->mutex); csi->shim = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(csi->shim)) { ret = PTR_ERR(csi->shim); - goto err_mutex; + return ret; } - ret = ti_csi2rx_init_dma(csi); - if (ret) - goto err_mutex; + csi->drain.len = DRAIN_BUFFER_SIZE; + csi->drain.vaddr = dma_alloc_coherent(csi->dev, csi->drain.len, + &csi->drain.paddr, + GFP_KERNEL); + if (!csi->drain.vaddr) + return -ENOMEM; ret = ti_csi2rx_v4l2_init(csi); - if (ret) - goto err_dma; - - ret = ti_csi2rx_init_vb2q(csi); if (ret) goto err_v4l2; + for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) { + csi->ctx[i].idx = i; + csi->ctx[i].csi = csi; + ret = ti_csi2rx_init_ctx(&csi->ctx[i]); + if (ret) + goto err_ctx; + } + ret = ti_csi2rx_notifier_register(csi); if (ret) - goto err_vb2q; + goto err_ctx; ret = devm_of_platform_populate(csi->dev); if (ret) { dev_err(csi->dev, "Failed to create children: %d\n", ret); - goto err_subdev; + goto err_notifier; } return 0; -err_subdev: - ti_csi2rx_cleanup_subdev(csi); -err_vb2q: - ti_csi2rx_cleanup_vb2q(csi); -err_v4l2: +err_notifier: + ti_csi2rx_cleanup_notifier(csi); +err_ctx: + i--; + for (; i >= 0; i--) + ti_csi2rx_cleanup_ctx(&csi->ctx[i]); ti_csi2rx_cleanup_v4l2(csi); -err_dma: - ti_csi2rx_cleanup_dma(csi); -err_mutex: - mutex_destroy(&csi->mutex); +err_v4l2: + dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr, + csi->drain.paddr); return ret; } static void ti_csi2rx_remove(struct platform_device *pdev) { struct ti_csi2rx_dev *csi = platform_get_drvdata(pdev); + unsigned int i; - video_unregister_device(&csi->vdev); + for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) + ti_csi2rx_cleanup_ctx(&csi->ctx[i]); - ti_csi2rx_cleanup_vb2q(csi); - ti_csi2rx_cleanup_subdev(csi); + ti_csi2rx_cleanup_notifier(csi); ti_csi2rx_cleanup_v4l2(csi); - ti_csi2rx_cleanup_dma(csi); - mutex_destroy(&csi->mutex); + dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr, + csi->drain.paddr); } static const struct of_device_id ti_csi2rx_of_match[] = { From e94ae7d2827d9c29a61a2ac61acc73a5e6d75cca Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Wed, 20 May 2026 17:30:09 +0530 Subject: [PATCH 270/394] media: ti: j721e-csi2rx: prepare SHIM code for multiple contexts Currently the SHIM code to configure the context only touches the first context. Add support for writing to the context's registers based on the context index. Signed-off-by: Pratyush Yadav Signed-off-by: Jai Luthra Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Yemike Abhilash Chandra Reviewed-by: Tomi Valkeinen Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- .../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 23717a3b6c4c..4adfae425f19 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -27,7 +27,7 @@ #define SHIM_CNTL 0x10 #define SHIM_CNTL_PIX_RST BIT(0) -#define SHIM_DMACNTX 0x20 +#define SHIM_DMACNTX(i) (0x20 + ((i) * 0x20)) #define SHIM_DMACNTX_EN BIT(31) #define SHIM_DMACNTX_YUV422 GENMASK(27, 26) #define SHIM_DMACNTX_DUAL_PCK_CFG BIT(24) @@ -38,7 +38,7 @@ #define SHIM_DMACNTX_SIZE_16 1 #define SHIM_DMACNTX_SIZE_32 2 -#define SHIM_PSI_CFG0 0x24 +#define SHIM_PSI_CFG0(i) (0x24 + ((i) * 0x20)) #define SHIM_PSI_CFG0_SRC_TAG GENMASK(15, 0) #define SHIM_PSI_CFG0_DST_TAG GENMASK(31, 16) @@ -568,11 +568,11 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) break; } - writel(reg, csi->shim + SHIM_DMACNTX); + writel(reg, csi->shim + SHIM_DMACNTX(ctx->idx)); reg = FIELD_PREP(SHIM_PSI_CFG0_SRC_TAG, 0) | FIELD_PREP(SHIM_PSI_CFG0_DST_TAG, 0); - writel(reg, csi->shim + SHIM_PSI_CFG0); + writel(reg, csi->shim + SHIM_PSI_CFG0(ctx->idx)); } static void ti_csi2rx_drain_callback(void *param) @@ -890,7 +890,7 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) err_pipeline: video_device_pipeline_stop(&ctx->vdev); writel(0, csi->shim + SHIM_CNTL); - writel(0, csi->shim + SHIM_DMACNTX); + writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); err: ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_QUEUED); return ret; @@ -905,7 +905,7 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq) video_device_pipeline_stop(&ctx->vdev); writel(0, csi->shim + SHIM_CNTL); - writel(0, csi->shim + SHIM_DMACNTX); + writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); ret = v4l2_subdev_call(csi->source, video, s_stream, 0); if (ret) From 16fe3fc16e4b30e2f61a8668afde11d8245aae5a Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Wed, 20 May 2026 17:30:10 +0530 Subject: [PATCH 271/394] media: ti: j721e-csi2rx: allocate DMA channel based on context index With multiple contexts, there needs to be a different DMA channel for each context. Earlier, the DMA channel name was hard coded to "rx0" for the sake of simplicity. Generate the DMA channel name based on its index and get the channel corresponding to the context. Signed-off-by: Pratyush Yadav Signed-off-by: Jai Luthra Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Yemike Abhilash Chandra Reviewed-by: Tomi Valkeinen Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 4adfae425f19..c781b312cea8 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -1036,9 +1036,11 @@ static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx) struct dma_slave_config cfg = { .src_addr_width = DMA_SLAVE_BUSWIDTH_16_BYTES, }; + char name[5]; int ret; - ctx->dma.chan = dma_request_chan(ctx->csi->dev, "rx0"); + snprintf(name, sizeof(name), "rx%u", ctx->idx); + ctx->dma.chan = dma_request_chan(ctx->csi->dev, name); if (IS_ERR(ctx->dma.chan)) return PTR_ERR(ctx->dma.chan); From 982135c0eac6d56c29cb42fdb0c3879cdd369d67 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:11 +0530 Subject: [PATCH 272/394] media: ti: j721e-csi2rx: add a subdev for the core device With single stream capture, it was simpler to use the video device as the media entity representing the main TI CSI2RX device. Now with multi stream capture coming into the picture, the model has shifted to each video device having a link to the main device's subdev. The routing would then be set on this subdev. Add this subdev, link each context to this subdev's entity and link the subdev's entity to the source. Also add an array of media pads. It will have one sink pad and source pads equal to the number of contexts. Support the new enable_stream()/disable_stream() APIs in the subdev instead of s_stream() hook. Reviewed-by: Yemike Abhilash Chandra Co-developed-by: Pratyush Yadav Signed-off-by: Pratyush Yadav Signed-off-by: Jai Luthra Signed-off-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 288 +++++++++++++++--- 1 file changed, 244 insertions(+), 44 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index c781b312cea8..e01d95eab19c 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -54,6 +54,11 @@ #define MAX_WIDTH_BYTES SZ_16K #define MAX_HEIGHT_LINES SZ_16K +#define TI_CSI2RX_PAD_SINK 0 +#define TI_CSI2RX_PAD_FIRST_SOURCE 1 +#define TI_CSI2RX_NUM_SOURCE_PADS 1 +#define TI_CSI2RX_NUM_PADS (1 + TI_CSI2RX_NUM_SOURCE_PADS) + #define DRAIN_TIMEOUT_MS 50 #define DRAIN_BUFFER_SIZE SZ_32K @@ -102,6 +107,7 @@ struct ti_csi2rx_ctx { struct mutex mutex; /* To serialize ioctls. */ struct v4l2_format v_fmt; struct ti_csi2rx_dma dma; + struct media_pad pad; u32 sequence; u32 idx; }; @@ -109,12 +115,14 @@ struct ti_csi2rx_ctx { struct ti_csi2rx_dev { struct device *dev; void __iomem *shim; + unsigned int enable_count; struct v4l2_device v4l2_dev; struct media_device mdev; struct media_pipeline pipe; - struct media_pad pad; + struct media_pad pads[TI_CSI2RX_NUM_PADS]; struct v4l2_async_notifier notifier; struct v4l2_subdev *source; + struct v4l2_subdev subdev; struct ti_csi2rx_ctx ctx[TI_CSI2RX_NUM_CTX]; u8 pix_per_clk; /* Buffer to drain stale data from PSI-L endpoint */ @@ -125,6 +133,22 @@ struct ti_csi2rx_dev { } drain; }; +static inline struct ti_csi2rx_dev *to_csi2rx_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ti_csi2rx_dev, subdev); +} + +static const struct v4l2_mbus_framefmt ti_csi2rx_default_fmt = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, +}; + static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = { { .fourcc = V4L2_PIX_FMT_YUYV, @@ -422,6 +446,18 @@ static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier) struct ti_csi2rx_dev *csi = dev_get_drvdata(notifier->v4l2_dev->dev); int ret, i; + /* Create link from source to subdev */ + ret = media_create_pad_link(&csi->source->entity, + CSI2RX_BRIDGE_SOURCE_PAD, + &csi->subdev.entity, + TI_CSI2RX_PAD_SINK, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + + if (ret) + return ret; + + /* Create and link video nodes for all DMA contexts */ for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) { struct ti_csi2rx_ctx *ctx = &csi->ctx[i]; struct video_device *vdev = &ctx->vdev; @@ -429,15 +465,17 @@ static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier) ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) goto unregister_dev; - } - ret = media_create_pad_link(&csi->source->entity, - CSI2RX_BRIDGE_SOURCE_PAD, - &csi->ctx[0].vdev.entity, csi->pad.index, - MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (ret) - goto unregister_dev; + ret = media_create_pad_link(&csi->subdev.entity, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, + &vdev->entity, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) { + video_unregister_device(vdev); + goto unregister_dev; + } + } ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev); if (ret) @@ -447,8 +485,10 @@ static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier) unregister_dev: i--; - for (; i >= 0; i--) + for (; i >= 0; i--) { + media_entity_remove_links(&csi->ctx[i].vdev.entity); video_unregister_device(&csi->ctx[i].vdev); + } return ret; } @@ -493,14 +533,13 @@ static int ti_csi2rx_notifier_register(struct ti_csi2rx_dev *csi) } /* Request maximum possible pixels per clock from the bridge */ -static void ti_csi2rx_request_max_ppc(struct ti_csi2rx_ctx *ctx) +static void ti_csi2rx_request_max_ppc(struct ti_csi2rx_dev *csi) { - struct ti_csi2rx_dev *csi = ctx->csi; u8 ppc = TI_CSI2RX_MAX_PIX_PER_CLK; struct media_pad *pad; int ret; - pad = media_entity_remote_source_pad_unique(&ctx->vdev.entity); + pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); if (IS_ERR(pad)) return; @@ -526,7 +565,7 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) writel(reg, csi->shim + SHIM_CNTL); /* Negotiate pixel count from the source */ - ti_csi2rx_request_max_ppc(ctx); + ti_csi2rx_request_max_ppc(csi); reg = SHIM_DMACNTX_EN; reg |= FIELD_PREP(SHIM_DMACNTX_FMT, fmt->csi_dt); @@ -879,7 +918,9 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) dma->state = TI_CSI2RX_DMA_ACTIVE; spin_unlock_irqrestore(&dma->lock, flags); - ret = v4l2_subdev_call(csi->source, video, s_stream, 1); + ret = v4l2_subdev_enable_streams(&csi->subdev, + TI_CSI2RX_PAD_FIRST_SOURCE, + BIT_U64(0)); if (ret) goto err_dma; @@ -907,7 +948,9 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq) writel(0, csi->shim + SHIM_CNTL); writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); - ret = v4l2_subdev_call(csi->source, video, s_stream, 0); + ret = v4l2_subdev_disable_streams(&csi->subdev, + TI_CSI2RX_PAD_FIRST_SOURCE, + BIT_U64(0)); if (ret) dev_err(csi->dev, "Failed to stop subdev stream\n"); @@ -923,8 +966,121 @@ static const struct vb2_ops csi_vb2_qops = { .stop_streaming = ti_csi2rx_stop_streaming, }; +static int ti_csi2rx_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code_enum) +{ + if (code_enum->index >= ARRAY_SIZE(ti_csi2rx_formats)) + return -EINVAL; + + code_enum->code = ti_csi2rx_formats[code_enum->index].code; + + return 0; +} + +static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt; + + /* No transcoding, don't allow setting source fmt */ + if (format->pad > TI_CSI2RX_PAD_SINK) + return v4l2_subdev_get_fmt(sd, state, format); + + if (!find_format_by_code(format->format.code)) + format->format.code = ti_csi2rx_formats[0].code; + + format->format.field = V4L2_FIELD_NONE; + + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); + *fmt = format->format; + + fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_FIRST_SOURCE, + format->stream); + *fmt = format->format; + + return 0; +} + +static int ti_csi2rx_sd_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_SINK); + *fmt = ti_csi2rx_default_fmt; + + fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_FIRST_SOURCE); + *fmt = ti_csi2rx_default_fmt; + + return 0; +} + +static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + struct media_pad *remote_pad; + int ret = 0; + + remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); + if (!remote_pad) + return -ENODEV; + + ret = v4l2_subdev_enable_streams(csi->source, remote_pad->index, + BIT_U64(0)); + if (ret) + return ret; + + csi->enable_count++; + + return 0; +} + +static int ti_csi2rx_sd_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + struct media_pad *remote_pad; + int ret = 0; + + remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); + if (!remote_pad) + return -ENODEV; + + if (csi->enable_count == 0) + return -EINVAL; + + ret = v4l2_subdev_disable_streams(csi->source, remote_pad->index, + BIT_U64(0)); + if (!ret) + --csi->enable_count; + + return 0; +} + +static const struct v4l2_subdev_pad_ops ti_csi2rx_subdev_pad_ops = { + .enum_mbus_code = ti_csi2rx_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ti_csi2rx_sd_set_fmt, + .enable_streams = ti_csi2rx_sd_enable_streams, + .disable_streams = ti_csi2rx_sd_disable_streams, +}; + +static const struct v4l2_subdev_ops ti_csi2rx_subdev_ops = { + .pad = &ti_csi2rx_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops ti_csi2rx_internal_ops = { + .init_state = ti_csi2rx_sd_init_state, +}; + static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi) { + v4l2_subdev_cleanup(&csi->subdev); media_device_unregister(&csi->mdev); v4l2_device_unregister(&csi->v4l2_dev); media_device_cleanup(&csi->mdev); @@ -979,48 +1135,52 @@ static int ti_csi2rx_link_validate(struct media_link *link) struct ti_csi2rx_ctx *ctx = container_of(vdev, struct ti_csi2rx_ctx, vdev); struct ti_csi2rx_dev *csi = ctx->csi; struct v4l2_pix_format *csi_fmt = &ctx->v_fmt.fmt.pix; - struct v4l2_subdev_format source_fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = link->source->index, - }; + struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; const struct ti_csi2rx_fmt *ti_fmt; - int ret; - ret = v4l2_subdev_call_state_active(csi->source, pad, - get_fmt, &source_fmt); - if (ret) - return ret; + state = v4l2_subdev_lock_and_get_active_state(&csi->subdev); + format = v4l2_subdev_state_get_format(state, link->source->index, 0); + v4l2_subdev_unlock_state(state); - if (source_fmt.format.width != csi_fmt->width) { + if (!format) { + dev_err(csi->dev, + "No format present on \"%s\":%u:0\n", + link->source->entity->name, link->source->index); + return 0; + } + + if (format->width != csi_fmt->width) { dev_dbg(csi->dev, "Width does not match (source %u, sink %u)\n", - source_fmt.format.width, csi_fmt->width); + format->width, csi_fmt->width); return -EPIPE; } - if (source_fmt.format.height != csi_fmt->height) { + if (format->height != csi_fmt->height) { dev_dbg(csi->dev, "Height does not match (source %u, sink %u)\n", - source_fmt.format.height, csi_fmt->height); + format->height, csi_fmt->height); return -EPIPE; } - if (source_fmt.format.field != csi_fmt->field && + if (format->field != csi_fmt->field && csi_fmt->field != V4L2_FIELD_NONE) { dev_dbg(csi->dev, "Field does not match (source %u, sink %u)\n", - source_fmt.format.field, csi_fmt->field); + format->field, csi_fmt->field); return -EPIPE; } - ti_fmt = find_format_by_code(source_fmt.format.code); + ti_fmt = find_format_by_code(format->code); if (!ti_fmt) { dev_dbg(csi->dev, "Media bus format 0x%x not supported\n", - source_fmt.format.code); + format->code); return -EPIPE; } if (ti_fmt->fourcc != csi_fmt->pixelformat) { dev_dbg(csi->dev, - "Cannot transform source fmt 0x%x to sink fmt 0x%x\n", - ti_fmt->fourcc, csi_fmt->pixelformat); + "Cannot transform \"%s\":%u format %p4cc to %p4cc\n", + link->source->entity->name, link->source->index, + &ti_fmt->fourcc, &csi_fmt->pixelformat); return -EPIPE; } @@ -1031,6 +1191,11 @@ static const struct media_entity_operations ti_csi2rx_video_entity_ops = { .link_validate = ti_csi2rx_link_validate, }; +static const struct media_entity_operations ti_csi2rx_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, +}; + static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx) { struct dma_slave_config cfg = { @@ -1056,6 +1221,7 @@ static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx) static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) { struct media_device *mdev = &csi->mdev; + struct v4l2_subdev *sd = &csi->subdev; int ret; mdev->dev = csi->dev; @@ -1068,16 +1234,51 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) ret = v4l2_device_register(csi->dev, &csi->v4l2_dev); if (ret) - return ret; + goto cleanup_media; ret = media_device_register(mdev); - if (ret) { - v4l2_device_unregister(&csi->v4l2_dev); - media_device_cleanup(mdev); - return ret; - } + if (ret) + goto unregister_v4l2; + + v4l2_subdev_init(sd, &ti_csi2rx_subdev_ops); + sd->internal_ops = &ti_csi2rx_internal_ops; + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + strscpy(sd->name, dev_name(csi->dev), sizeof(sd->name)); + sd->dev = csi->dev; + sd->entity.ops = &ti_csi2rx_subdev_entity_ops; + + csi->pads[TI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + + for (unsigned int i = TI_CSI2RX_PAD_FIRST_SOURCE; + i < TI_CSI2RX_NUM_PADS; i++) + csi->pads[i].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(csi->pads), + csi->pads); + if (ret) + goto unregister_media; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto unregister_media; + + ret = v4l2_device_register_subdev(&csi->v4l2_dev, sd); + if (ret) + goto cleanup_subdev; return 0; + +cleanup_subdev: + v4l2_subdev_cleanup(sd); +unregister_media: + media_device_unregister(mdev); +unregister_v4l2: + v4l2_device_unregister(&csi->v4l2_dev); +cleanup_media: + media_device_cleanup(mdev); + + return ret; } static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx) @@ -1104,9 +1305,9 @@ static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx) ti_csi2rx_fill_fmt(fmt, &ctx->v_fmt); - csi->pad.flags = MEDIA_PAD_FL_SINK; + ctx->pad.flags = MEDIA_PAD_FL_SINK; vdev->entity.ops = &ti_csi2rx_video_entity_ops; - ret = media_entity_pads_init(&ctx->vdev.entity, 1, &csi->pad); + ret = media_entity_pads_init(&ctx->vdev.entity, 1, &ctx->pad); if (ret) return ret; @@ -1214,7 +1415,6 @@ static void ti_csi2rx_remove(struct platform_device *pdev) ti_csi2rx_cleanup_notifier(csi); ti_csi2rx_cleanup_v4l2(csi); - dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr, csi->drain.paddr); } From 8b08ed98f8601156ab7b36e3d054355123229fa3 Mon Sep 17 00:00:00 2001 From: Rishikesh Donadkar Date: Wed, 20 May 2026 17:30:12 +0530 Subject: [PATCH 273/394] media: cadence: csi2rx: Move to .enable/disable_streams API The enable_streams() API in v4l2 supports passing a bitmask to enable each pad/stream combination individually on any media subdev. Use this API instead of s_stream() API. Implement the enable_stream and disable_stream hooks in place of the stream-unaware s_stream hook. Remove the lock that was used to serialize stream starts/stops which is not required anymore since the v4l2-core serializes the enable/disable_streams() calls for the subdev. Reviewed-by: Tomi Valkeinen Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- drivers/media/platform/cadence/cdns-csi2rx.c | 114 +++++++++---------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index cde690c6fdee..2b25314ba6ab 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -125,12 +125,6 @@ struct csi2rx_priv { unsigned int count; int error_irq; - /* - * Used to prevent race conditions between multiple, - * concurrent calls to start and stop. - */ - struct mutex lock; - void __iomem *base; struct clk *sys_clk; struct clk *p_clk; @@ -268,20 +262,21 @@ static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx) &csi2rx->source_subdev->entity.pads[csi2rx->source_pad]; union phy_configure_opts opts = { }; struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; - struct v4l2_subdev_format sd_fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = CSI2RX_PAD_SINK, - }; + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *framefmt; const struct csi2rx_fmt *fmt; s64 link_freq; int ret; - ret = v4l2_subdev_call_state_active(&csi2rx->subdev, pad, get_fmt, - &sd_fmt); - if (ret < 0) - return ret; + state = v4l2_subdev_get_locked_active_state(&csi2rx->subdev); - fmt = csi2rx_get_fmt_by_code(sd_fmt.format.code); + framefmt = v4l2_subdev_state_get_format(state, CSI2RX_PAD_SINK, 0); + if (!framefmt) { + dev_err(csi2rx->dev, "Did not find active sink format\n"); + return -EINVAL; + } + + fmt = csi2rx_get_fmt_by_code(framefmt->code); link_freq = v4l2_get_link_freq(src_pad, fmt->bpp, 2 * csi2rx->num_lanes); @@ -401,16 +396,10 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) reset_control_deassert(csi2rx->sys_rst); - ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true); - if (ret) - goto err_disable_sysclk; - clk_disable_unprepare(csi2rx->p_clk); return 0; -err_disable_sysclk: - clk_disable_unprepare(csi2rx->sys_clk); err_disable_pixclk: for (; i > 0; i--) { reset_control_assert(csi2rx->pixel_rst[i - 1]); @@ -459,9 +448,6 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) reset_control_assert(csi2rx->p_rst); clk_disable_unprepare(csi2rx->p_clk); - if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false)) - dev_warn(csi2rx->dev, "Couldn't disable our subdev\n"); - if (csi2rx->dphy) { writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); @@ -485,38 +471,56 @@ static int csi2rx_log_status(struct v4l2_subdev *sd) return 0; } -static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) +static int csi2rx_enable_streams(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); - int ret = 0; + int ret; - mutex_lock(&csi2rx->lock); - - if (enable) { - /* - * If we're not the first users, there's no need to - * enable the whole controller. - */ - if (!csi2rx->count) { - ret = csi2rx_start(csi2rx); - if (ret) - goto out; - } - - csi2rx->count++; - } else { - csi2rx->count--; - - /* - * Let the last user turn off the lights. - */ - if (!csi2rx->count) - csi2rx_stop(csi2rx); + /* + * If we're not the first users, there's no need to + * enable the whole controller. + */ + if (!csi2rx->count) { + ret = csi2rx_start(csi2rx); + if (ret) + return ret; } -out: - mutex_unlock(&csi2rx->lock); - return ret; + /* Start streaming on the source */ + ret = v4l2_subdev_enable_streams(csi2rx->source_subdev, csi2rx->source_pad, + BIT_U64(0)); + if (ret) { + dev_err(csi2rx->dev, + "Failed to start streams %d on subdev\n", 0); + if (!csi2rx->count) + csi2rx_stop(csi2rx); + return ret; + } + + csi2rx->count++; + return 0; +} + +static int csi2rx_disable_streams(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + + if (v4l2_subdev_disable_streams(csi2rx->source_subdev, + csi2rx->source_pad, BIT_U64(0))) { + dev_err(csi2rx->dev, "Couldn't disable our subdev\n"); + } + + csi2rx->count--; + + /* Let the last user turn off the lights. */ + if (!csi2rx->count) + csi2rx_stop(csi2rx); + + return 0; } static int csi2rx_enum_mbus_code(struct v4l2_subdev *subdev, @@ -611,10 +615,8 @@ static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = { .enum_mbus_code = csi2rx_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = csi2rx_set_fmt, -}; - -static const struct v4l2_subdev_video_ops csi2rx_video_ops = { - .s_stream = csi2rx_s_stream, + .enable_streams = csi2rx_enable_streams, + .disable_streams = csi2rx_disable_streams, }; static const struct v4l2_subdev_core_ops csi2rx_core_ops = { @@ -623,7 +625,6 @@ static const struct v4l2_subdev_core_ops csi2rx_core_ops = { static const struct v4l2_subdev_ops csi2rx_subdev_ops = { .core = &csi2rx_core_ops, - .video = &csi2rx_video_ops, .pad = &csi2rx_pad_ops, }; @@ -829,7 +830,6 @@ static int csi2rx_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, csi2rx); csi2rx->dev = &pdev->dev; - mutex_init(&csi2rx->lock); ret = csi2rx_get_resources(csi2rx, pdev); if (ret) From 57283236b7a283ac4a7c54b69d7ab008f1d441e8 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Wed, 20 May 2026 17:30:13 +0530 Subject: [PATCH 274/394] media: ti: j721e-csi2rx: get number of contexts from device tree Different platforms that use this driver might have different number of DMA channels allocated for CSI. So only as many DMA contexts can be used as the number of DMA channels available. Get the number of channels provided via device tree and only configure that many contexts, and hence only that many pads. Reviewed-by: Yemike Abhilash Chandra Reviewed-by: Tomi Valkeinen Signed-off-by: Pratyush Yadav Co-developed-by: Jai Luthra Signed-off-by: Jai Luthra Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index e01d95eab19c..26a8eaa98b3d 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -43,7 +43,7 @@ #define SHIM_PSI_CFG0_DST_TAG GENMASK(31, 16) #define TI_CSI2RX_MAX_PIX_PER_CLK 4 -#define TI_CSI2RX_NUM_CTX 1 +#define TI_CSI2RX_MAX_CTX 32 /* * There are no hard limits on the width or height. The DMA engine can handle @@ -56,8 +56,8 @@ #define TI_CSI2RX_PAD_SINK 0 #define TI_CSI2RX_PAD_FIRST_SOURCE 1 -#define TI_CSI2RX_NUM_SOURCE_PADS 1 -#define TI_CSI2RX_NUM_PADS (1 + TI_CSI2RX_NUM_SOURCE_PADS) +#define TI_CSI2RX_MAX_SOURCE_PADS TI_CSI2RX_MAX_CTX +#define TI_CSI2RX_MAX_PADS (1 + TI_CSI2RX_MAX_SOURCE_PADS) #define DRAIN_TIMEOUT_MS 50 #define DRAIN_BUFFER_SIZE SZ_32K @@ -116,14 +116,15 @@ struct ti_csi2rx_dev { struct device *dev; void __iomem *shim; unsigned int enable_count; + unsigned int num_ctx; struct v4l2_device v4l2_dev; struct media_device mdev; struct media_pipeline pipe; - struct media_pad pads[TI_CSI2RX_NUM_PADS]; + struct media_pad pads[TI_CSI2RX_MAX_PADS]; struct v4l2_async_notifier notifier; struct v4l2_subdev *source; struct v4l2_subdev subdev; - struct ti_csi2rx_ctx ctx[TI_CSI2RX_NUM_CTX]; + struct ti_csi2rx_ctx ctx[TI_CSI2RX_MAX_CTX]; u8 pix_per_clk; /* Buffer to drain stale data from PSI-L endpoint */ struct { @@ -458,7 +459,7 @@ static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier) return ret; /* Create and link video nodes for all DMA contexts */ - for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) { + for (i = 0; i < csi->num_ctx; i++) { struct ti_csi2rx_ctx *ctx = &csi->ctx[i]; struct video_device *vdev = &ctx->vdev; @@ -1251,10 +1252,11 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) csi->pads[TI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; for (unsigned int i = TI_CSI2RX_PAD_FIRST_SOURCE; - i < TI_CSI2RX_NUM_PADS; i++) + i < TI_CSI2RX_PAD_FIRST_SOURCE + csi->num_ctx; i++) csi->pads[i].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(csi->pads), + ret = media_entity_pads_init(&sd->entity, + TI_CSI2RX_PAD_FIRST_SOURCE + csi->num_ctx, csi->pads); if (ret) goto unregister_media; @@ -1345,8 +1347,9 @@ static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx) static int ti_csi2rx_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct ti_csi2rx_dev *csi; - int ret, i; + int ret = 0, i, count; csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL); if (!csi) @@ -1368,11 +1371,28 @@ static int ti_csi2rx_probe(struct platform_device *pdev) if (!csi->drain.vaddr) return -ENOMEM; + /* Only use as many contexts as the number of DMA channels allocated. */ + count = of_property_count_strings(np, "dma-names"); + if (count < 0) { + dev_err(csi->dev, "Failed to get DMA channel count: %d\n", count); + ret = count; + goto err_dma_chan; + } + + csi->num_ctx = count; + if (csi->num_ctx > TI_CSI2RX_MAX_CTX) { + dev_err(csi->dev, + "%u DMA channels passed. Maximum is %u.\n", + csi->num_ctx, TI_CSI2RX_MAX_CTX); + ret = -EINVAL; + goto err_dma_chan; + } + ret = ti_csi2rx_v4l2_init(csi); if (ret) - goto err_v4l2; + goto err_dma_chan; - for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) { + for (i = 0; i < csi->num_ctx; i++) { csi->ctx[i].idx = i; csi->ctx[i].csi = csi; ret = ti_csi2rx_init_ctx(&csi->ctx[i]); @@ -1399,7 +1419,7 @@ static int ti_csi2rx_probe(struct platform_device *pdev) for (; i >= 0; i--) ti_csi2rx_cleanup_ctx(&csi->ctx[i]); ti_csi2rx_cleanup_v4l2(csi); -err_v4l2: +err_dma_chan: dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr, csi->drain.paddr); return ret; @@ -1410,7 +1430,7 @@ static void ti_csi2rx_remove(struct platform_device *pdev) struct ti_csi2rx_dev *csi = platform_get_drvdata(pdev); unsigned int i; - for (i = 0; i < TI_CSI2RX_NUM_CTX; i++) + for (i = 0; i < csi->num_ctx; i++) ti_csi2rx_cleanup_ctx(&csi->ctx[i]); ti_csi2rx_cleanup_notifier(csi); From 94dfbd4f29b823025db59da18bcb3e4eb8148972 Mon Sep 17 00:00:00 2001 From: Rishikesh Donadkar Date: Wed, 20 May 2026 17:30:14 +0530 Subject: [PATCH 275/394] media: cadence: csi2rx: Add .get_frame_desc op The cdns-csi2rx subdev passes streams through without any modification Use v4l2_subdev_get_frame_desc_passthrough() helper and add the .get_frame_desc op Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- drivers/media/platform/cadence/cdns-csi2rx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 2b25314ba6ab..bba0e4c0a34d 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -617,6 +617,7 @@ static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = { .set_fmt = csi2rx_set_fmt, .enable_streams = csi2rx_enable_streams, .disable_streams = csi2rx_disable_streams, + .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough, }; static const struct v4l2_subdev_core_ops csi2rx_core_ops = { From d71fc9d98cf73aebe7227b8d941be3dcfb17ab2d Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:15 +0530 Subject: [PATCH 276/394] media: ti: j721e-csi2rx: add support for processing virtual channels Use get_frame_desc() to get the frame desc from the connected source, and use the provided virtual channel and DT instead of defaults. As we don't support multiple streams yet, we will just always use stream 0. If the source doesn't support get_frame_desc(), fall back to the previous method of always capturing virtual channel 0. Reviewed-by: Yemike Abhilash Chandra Co-developed-by: Pratyush Yadav Signed-off-by: Pratyush Yadav Signed-off-by: Jai Luthra Signed-off-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 26a8eaa98b3d..d0a681ba78eb 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -32,6 +32,7 @@ #define SHIM_DMACNTX_YUV422 GENMASK(27, 26) #define SHIM_DMACNTX_DUAL_PCK_CFG BIT(24) #define SHIM_DMACNTX_SIZE GENMASK(21, 20) +#define SHIM_DMACNTX_VC GENMASK(9, 6) #define SHIM_DMACNTX_FMT GENMASK(5, 0) #define SHIM_DMACNTX_YUV422_MODE_11 3 #define SHIM_DMACNTX_SIZE_8 0 @@ -110,6 +111,9 @@ struct ti_csi2rx_ctx { struct media_pad pad; u32 sequence; u32 idx; + u32 vc; + u32 dt; + u32 stream; }; struct ti_csi2rx_dev { @@ -569,7 +573,7 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) ti_csi2rx_request_max_ppc(csi); reg = SHIM_DMACNTX_EN; - reg |= FIELD_PREP(SHIM_DMACNTX_FMT, fmt->csi_dt); + reg |= FIELD_PREP(SHIM_DMACNTX_FMT, ctx->dt); /* * The hardware assumes incoming YUV422 8-bit data on MIPI CSI2 bus @@ -608,6 +612,8 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) break; } + reg |= FIELD_PREP(SHIM_DMACNTX_VC, ctx->vc); + writel(reg, csi->shim + SHIM_DMACNTX(ctx->idx)); reg = FIELD_PREP(SHIM_PSI_CFG0_SRC_TAG, 0) | @@ -881,12 +887,46 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) } } +static int ti_csi2rx_get_vc_and_dt(struct ti_csi2rx_ctx *ctx) +{ + struct ti_csi2rx_dev *csi = ctx->csi; + struct v4l2_mbus_frame_desc fd; + struct media_pad *pad; + int ret, i; + + pad = media_entity_remote_pad_unique(&csi->subdev.entity, MEDIA_PAD_FL_SOURCE); + if (IS_ERR(pad)) + return PTR_ERR(pad); + + ret = v4l2_subdev_call(csi->source, pad, get_frame_desc, pad->index, &fd); + if (ret) + return ret; + + if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) + return -EINVAL; + + for (i = 0; i < fd.num_entries; i++) { + if (ctx->stream == fd.entry[i].stream) { + ctx->vc = fd.entry[i].bus.csi2.vc; + ctx->dt = fd.entry[i].bus.csi2.dt; + break; + } + + /* Return error if no matching stream found */ + if (i == fd.num_entries) + return -EINVAL; + } + + return 0; +} + static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) { struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq); struct ti_csi2rx_dev *csi = ctx->csi; struct ti_csi2rx_dma *dma = &ctx->dma; struct ti_csi2rx_buffer *buf; + const struct ti_csi2rx_fmt *fmt; unsigned long flags; int ret = 0; @@ -901,6 +941,15 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) goto err; + ret = ti_csi2rx_get_vc_and_dt(ctx); + if (ret == -ENOIOCTLCMD) { + ctx->vc = 0; + fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat); + ctx->dt = fmt->csi_dt; + } else if (ret < 0) { + goto err; + } + ti_csi2rx_setup_shim(ctx); ctx->sequence = 0; From c974827d4b08d6eaf16b6010bf81960ba9ec1ac7 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:16 +0530 Subject: [PATCH 277/394] media: cadence: csi2rx: add multistream support Cadence CSI-2 bridge IP supports capturing multiple virtual "streams" of data over the same physical interface using MIPI Virtual Channels. While the hardware IP supports usecases where streams coming in the sink pad can be broadcasted to multiple source pads, the driver will need significant re-architecture to make that possible. The two users of this IP in mainline linux are TI Shim and StarFive JH7110 CAMSS, and both have only integrated the first source pad i.e stream0 of this IP. So for now keep it simple and only allow 1-to-1 mapping of streams from sink to source, without any broadcasting. Signed-off-by: Jai Luthra Reviewed-by: Changhuang Liang Reviewed-by: Yemike Abhilash Chandra Reviewed-by: Tomi Valkeinen Co-developed-by: Rishikesh Donadkar Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- drivers/media/platform/cadence/cdns-csi2rx.c | 241 +++++++++++++++---- 1 file changed, 190 insertions(+), 51 deletions(-) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index bba0e4c0a34d..8931e824c597 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -135,6 +135,7 @@ struct csi2rx_priv { struct phy *dphy; u8 num_pixels[CSI2RX_STREAMS_MAX]; + u32 vc_select[CSI2RX_STREAMS_MAX]; u8 lanes[CSI2RX_LANES_MAX]; u8 num_lanes; u8 max_lanes; @@ -229,6 +230,21 @@ static const struct csi2rx_fmt *csi2rx_get_fmt_by_code(u32 code) return NULL; } +static int csi2rx_get_frame_desc_from_source(struct csi2rx_priv *csi2rx, + struct v4l2_mbus_frame_desc *fd) +{ + struct media_pad *remote_pad; + + remote_pad = media_entity_remote_source_pad_unique(&csi2rx->subdev.entity); + if (IS_ERR(remote_pad)) { + dev_err(csi2rx->dev, "No remote pad found for sink\n"); + return PTR_ERR(remote_pad); + } + + return v4l2_subdev_call(csi2rx->source_subdev, pad, get_frame_desc, + remote_pad->index, fd); +} + static inline struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev) { @@ -258,30 +274,46 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx) static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx) { - struct media_pad *src_pad = - &csi2rx->source_subdev->entity.pads[csi2rx->source_pad]; union phy_configure_opts opts = { }; struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; - struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *framefmt; + struct v4l2_subdev_state *state; const struct csi2rx_fmt *fmt; + struct v4l2_subdev_route *route; + int source_pad = csi2rx->source_pad; + struct media_pad *pad = &csi2rx->source_subdev->entity.pads[source_pad]; s64 link_freq; int ret; + u32 bpp; state = v4l2_subdev_get_locked_active_state(&csi2rx->subdev); - framefmt = v4l2_subdev_state_get_format(state, CSI2RX_PAD_SINK, 0); - if (!framefmt) { - dev_err(csi2rx->dev, "Did not find active sink format\n"); - return -EINVAL; + /* + * For multi-stream transmitters there is no single pixel rate. + * + * In multistream usecase pass bpp as 0 so that v4l2_get_link_freq() + * returns an error if it falls back to V4L2_CID_PIXEL_RATE. + */ + if (state->routing.num_routes > 1) { + bpp = 0; + } else { + route = &state->routing.routes[0]; + framefmt = v4l2_subdev_state_get_format(state, CSI2RX_PAD_SINK, + route->sink_stream); + if (!framefmt) { + dev_err(csi2rx->dev, "Did not find active sink format\n"); + return -EINVAL; + } + + fmt = csi2rx_get_fmt_by_code(framefmt->code); + bpp = fmt->bpp; } - fmt = csi2rx_get_fmt_by_code(framefmt->code); - - link_freq = v4l2_get_link_freq(src_pad, - fmt->bpp, 2 * csi2rx->num_lanes); - if (link_freq < 0) + link_freq = v4l2_get_link_freq(pad, bpp, 2 * csi2rx->num_lanes); + if (link_freq < 0) { + dev_err(csi2rx->dev, "Unable to calculate link frequency\n"); return link_freq; + } ret = phy_mipi_dphy_get_default_config_for_hsclk(link_freq, csi2rx->num_lanes, cfg); @@ -379,11 +411,7 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) csi2rx->num_pixels[i]), csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); - /* - * Enable one virtual channel. When multiple virtual channels - * are supported this will have to be changed. - */ - writel(CSI2RX_STREAM_DATA_CFG_VC_SELECT(0), + writel(csi2rx->vc_select[i], csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)); writel(CSI2RX_STREAM_CTRL_START, @@ -471,18 +499,57 @@ static int csi2rx_log_status(struct v4l2_subdev *sd) return 0; } +static void csi2rx_update_vc_select(struct csi2rx_priv *csi2rx, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_frame_desc fd = {0}; + struct v4l2_subdev_route *route; + unsigned int i; + int ret; + + ret = csi2rx_get_frame_desc_from_source(csi2rx, &fd); + if (ret || fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + dev_dbg(csi2rx->dev, + "Failed to get source frame desc, allowing only VC=0\n"); + for (i = 0; i < CSI2RX_STREAMS_MAX; i++) + csi2rx->vc_select[i] = CSI2RX_STREAM_DATA_CFG_VC_SELECT(0); + return; + } + + /* If source provides per-stream VC info, use it to filter by VC */ + memset(csi2rx->vc_select, 0, sizeof(csi2rx->vc_select)); + + for_each_active_route(&state->routing, route) { + u32 cdns_stream = route->source_pad - CSI2RX_PAD_SOURCE_STREAM0; + + for (i = 0; i < fd.num_entries; i++) { + if (fd.entry[i].stream != route->sink_stream) + continue; + + csi2rx->vc_select[cdns_stream] |= + CSI2RX_STREAM_DATA_CFG_VC_SELECT(fd.entry[i].bus.csi2.vc); + } + } +} + static int csi2rx_enable_streams(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) { struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + u64 sink_streams; int ret; + sink_streams = v4l2_subdev_state_xlate_streams(state, pad, + CSI2RX_PAD_SINK, + &streams_mask); + /* * If we're not the first users, there's no need to * enable the whole controller. */ if (!csi2rx->count) { + csi2rx_update_vc_select(csi2rx, state); ret = csi2rx_start(csi2rx); if (ret) return ret; @@ -490,10 +557,11 @@ static int csi2rx_enable_streams(struct v4l2_subdev *subdev, /* Start streaming on the source */ ret = v4l2_subdev_enable_streams(csi2rx->source_subdev, csi2rx->source_pad, - BIT_U64(0)); + sink_streams); if (ret) { dev_err(csi2rx->dev, - "Failed to start streams %d on subdev\n", 0); + "Failed to start streams %#llx on subdev\n", + sink_streams); if (!csi2rx->count) csi2rx_stop(csi2rx); return ret; @@ -508,9 +576,14 @@ static int csi2rx_disable_streams(struct v4l2_subdev *subdev, u64 streams_mask) { struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + u64 sink_streams; + + sink_streams = v4l2_subdev_state_xlate_streams(state, pad, + CSI2RX_PAD_SINK, + &streams_mask); if (v4l2_subdev_disable_streams(csi2rx->source_subdev, - csi2rx->source_pad, BIT_U64(0))) { + csi2rx->source_pad, sink_streams)) { dev_err(csi2rx->dev, "Couldn't disable our subdev\n"); } @@ -535,12 +608,53 @@ static int csi2rx_enum_mbus_code(struct v4l2_subdev *subdev, return 0; } +static int _csi2rx_set_routing(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + int ret; + + ret = v4l2_subdev_routing_validate(subdev, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + return v4l2_subdev_set_routing_with_fmt(subdev, state, routing, &format); +} + +static int csi2rx_set_routing(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + int ret; + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && csi2rx->count) + return -EBUSY; + + ret = _csi2rx_set_routing(subdev, state, routing); + if (ret) + return ret; + + return 0; +} + static int csi2rx_set_fmt(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt; - unsigned int i; /* No transcoding, source and sink formats must match. */ if (format->pad != CSI2RX_PAD_SINK) @@ -552,14 +666,16 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev, format->format.field = V4L2_FIELD_NONE; /* Set sink format */ - fmt = v4l2_subdev_state_get_format(state, format->pad); + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); *fmt = format->format; - /* Propagate to source formats */ - for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) { - fmt = v4l2_subdev_state_get_format(state, i); - *fmt = format->format; - } + /* Propagate to source format */ + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; return 0; } @@ -567,21 +683,22 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev, static int csi2rx_init_state(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state) { - struct v4l2_subdev_format format = { - .pad = CSI2RX_PAD_SINK, - .format = { - .width = 640, - .height = 480, - .code = MEDIA_BUS_FMT_UYVY8_1X16, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_SRGB, - .ycbcr_enc = V4L2_YCBCR_ENC_601, - .quantization = V4L2_QUANTIZATION_LIM_RANGE, - .xfer_func = V4L2_XFER_FUNC_SRGB, + struct v4l2_subdev_route routes[] = { + { + .sink_pad = CSI2RX_PAD_SINK, + .sink_stream = 0, + .source_pad = CSI2RX_PAD_SOURCE_STREAM0, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, }, }; - return csi2rx_set_fmt(subdev, state, &format); + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _csi2rx_set_routing(subdev, state, &routing); } int cdns_csi2rx_negotiate_ppc(struct v4l2_subdev *subdev, unsigned int pad, @@ -589,35 +706,55 @@ int cdns_csi2rx_negotiate_ppc(struct v4l2_subdev *subdev, unsigned int pad, { struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); const struct csi2rx_fmt *csi_fmt; + struct v4l2_subdev_route *route; struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *fmt; + int ret = 0; if (!ppc || pad < CSI2RX_PAD_SOURCE_STREAM0 || pad >= CSI2RX_PAD_MAX) return -EINVAL; state = v4l2_subdev_lock_and_get_active_state(subdev); - fmt = v4l2_subdev_state_get_format(state, pad); - csi_fmt = csi2rx_get_fmt_by_code(fmt->code); + /* Check all streams on requested pad */ + for_each_active_route(&state->routing, route) { + if (route->source_pad != pad) + continue; - /* Reduce requested PPC if it is too high */ - *ppc = min(*ppc, csi_fmt->max_pixels); + fmt = v4l2_subdev_state_get_format(state, route->source_pad, + route->source_stream); + if (!fmt) { + ret = -EPIPE; + *ppc = 1; + break; + } + csi_fmt = csi2rx_get_fmt_by_code(fmt->code); + if (!csi_fmt) { + ret = -EINVAL; + *ppc = 1; + break; + } + + /* Reduce requested PPC if it is too high for this stream */ + *ppc = min(*ppc, csi_fmt->max_pixels); + } v4l2_subdev_unlock_state(state); csi2rx->num_pixels[pad - CSI2RX_PAD_SOURCE_STREAM0] = CSI2RX_STREAM_CFG_NUM_PIXELS(*ppc); - return 0; + return ret; } EXPORT_SYMBOL_FOR_MODULES(cdns_csi2rx_negotiate_ppc, "j721e-csi2rx"); static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = { - .enum_mbus_code = csi2rx_enum_mbus_code, - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = csi2rx_set_fmt, - .enable_streams = csi2rx_enable_streams, - .disable_streams = csi2rx_disable_streams, - .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough, + .enum_mbus_code = csi2rx_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = csi2rx_set_fmt, + .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough, + .set_routing = csi2rx_set_routing, + .enable_streams = csi2rx_enable_streams, + .disable_streams = csi2rx_disable_streams, }; static const struct v4l2_subdev_core_ops csi2rx_core_ops = { @@ -636,6 +773,7 @@ static const struct v4l2_subdev_internal_ops csi2rx_internal_ops = { static const struct media_entity_operations csi2rx_media_ops = { .link_validate = v4l2_subdev_link_validate, .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, }; static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, @@ -853,7 +991,8 @@ static int csi2rx_probe(struct platform_device *pdev) csi2rx->pads[CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) csi2rx->pads[i].flags = MEDIA_PAD_FL_SOURCE; - csi2rx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + csi2rx->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_STREAMS; csi2rx->subdev.entity.ops = &csi2rx_media_ops; ret = media_entity_pads_init(&csi2rx->subdev.entity, CSI2RX_PAD_MAX, From 3ed9c0a1fdbac36942af32fc1b1490a8ba241e55 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:17 +0530 Subject: [PATCH 278/394] media: ti: j721e-csi2rx: add multistream support Each CSI2 stream can be multiplexed into 32 independent streams, each identified by its virtual channel number and data type. The incoming data from these streams can be filtered on the basis of either the virtual channel or the data type. To capture this multiplexed stream, the application needs to tell the driver how it wants to route the data. It needs to specify which context should process which stream. This is done via the new routing APIs. Add ioctls to accept routing information from the application and save that in the driver. This can be used when starting streaming on a context to determine which route and consequently which virtual channel it should process. De-assert the pixel interface reset on first start_streaming() and assert it on the last stop_streaming(). Reviewed-by: Yemike Abhilash Chandra Co-developed-by: Pratyush Yadav Signed-off-by: Pratyush Yadav Signed-off-by: Jai Luthra Co-developed-by: Rishikesh Donadkar Signed-off-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 274 ++++++++++++------ 1 file changed, 193 insertions(+), 81 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index d0a681ba78eb..1ec63715baf2 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -143,17 +143,6 @@ static inline struct ti_csi2rx_dev *to_csi2rx_dev(struct v4l2_subdev *sd) return container_of(sd, struct ti_csi2rx_dev, subdev); } -static const struct v4l2_mbus_framefmt ti_csi2rx_default_fmt = { - .width = 640, - .height = 480, - .code = MEDIA_BUS_FMT_UYVY8_1X16, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_SRGB, - .ycbcr_enc = V4L2_YCBCR_ENC_601, - .quantization = V4L2_QUANTIZATION_LIM_RANGE, - .xfer_func = V4L2_XFER_FUNC_SRGB, -}; - static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = { { .fourcc = V4L2_PIX_FMT_YUYV, @@ -565,10 +554,6 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat); - /* De-assert the pixel interface reset. */ - reg = SHIM_CNTL_PIX_RST; - writel(reg, csi->shim + SHIM_CNTL); - /* Negotiate pixel count from the source */ ti_csi2rx_request_max_ppc(csi); @@ -887,34 +872,82 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) } } +static int ti_csi2rx_get_stream(struct ti_csi2rx_ctx *ctx) +{ + struct ti_csi2rx_dev *csi = ctx->csi; + struct media_pad *pad; + struct v4l2_subdev_state *state; + struct v4l2_subdev_route *r; + + /* Get the source pad connected to this ctx */ + pad = media_entity_remote_source_pad_unique(ctx->pad.entity); + if (!pad) { + dev_err(csi->dev, "No pad connected to ctx %d\n", ctx->idx); + return -ENODEV; + } + + state = v4l2_subdev_get_locked_active_state(&csi->subdev); + + for_each_active_route(&state->routing, r) { + if (r->source_pad == pad->index) { + ctx->stream = r->sink_stream; + return 0; + } + } + + /* No route found for this ctx */ + return -ENODEV; +} + static int ti_csi2rx_get_vc_and_dt(struct ti_csi2rx_ctx *ctx) { struct ti_csi2rx_dev *csi = ctx->csi; + struct ti_csi2rx_ctx *curr_ctx; struct v4l2_mbus_frame_desc fd; - struct media_pad *pad; - int ret, i; + struct media_pad *source_pad; + const struct ti_csi2rx_fmt *fmt; + int ret; + unsigned int i, j; - pad = media_entity_remote_pad_unique(&csi->subdev.entity, MEDIA_PAD_FL_SOURCE); - if (IS_ERR(pad)) - return PTR_ERR(pad); + /* Get the frame desc from source */ + source_pad = media_entity_remote_pad_unique(&csi->subdev.entity, MEDIA_PAD_FL_SOURCE); + if (IS_ERR(source_pad)) + return PTR_ERR(source_pad); - ret = v4l2_subdev_call(csi->source, pad, get_frame_desc, pad->index, &fd); - if (ret) + ret = v4l2_subdev_call(csi->source, pad, get_frame_desc, source_pad->index, &fd); + if (ret) { + if (ret == -ENOIOCTLCMD) { + ctx->vc = 0; + fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat); + ctx->dt = fmt->csi_dt; + } return ret; + } if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) return -EINVAL; - for (i = 0; i < fd.num_entries; i++) { - if (ctx->stream == fd.entry[i].stream) { - ctx->vc = fd.entry[i].bus.csi2.vc; - ctx->dt = fd.entry[i].bus.csi2.dt; - break; - } + for (i = 0; i < csi->num_ctx; i++) { + curr_ctx = &csi->ctx[i]; - /* Return error if no matching stream found */ - if (i == fd.num_entries) - return -EINVAL; + /* Capture VC 0 by default */ + curr_ctx->vc = 0; + + ret = ti_csi2rx_get_stream(curr_ctx); + if (ret) + continue; + + for (j = 0; j < fd.num_entries; j++) { + if (curr_ctx->stream == fd.entry[j].stream) { + curr_ctx->vc = fd.entry[j].bus.csi2.vc; + curr_ctx->dt = fd.entry[j].bus.csi2.dt; + break; + } + + /* Return error if no matching stream found */ + if (j == fd.num_entries) + return -EINVAL; + } } return 0; @@ -925,8 +958,6 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq); struct ti_csi2rx_dev *csi = ctx->csi; struct ti_csi2rx_dma *dma = &ctx->dma; - struct ti_csi2rx_buffer *buf; - const struct ti_csi2rx_fmt *fmt; unsigned long flags; int ret = 0; @@ -941,35 +972,9 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) goto err; - ret = ti_csi2rx_get_vc_and_dt(ctx); - if (ret == -ENOIOCTLCMD) { - ctx->vc = 0; - fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat); - ctx->dt = fmt->csi_dt; - } else if (ret < 0) { - goto err; - } - - ti_csi2rx_setup_shim(ctx); - - ctx->sequence = 0; - - spin_lock_irqsave(&dma->lock, flags); - buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); - - ret = ti_csi2rx_start_dma(ctx, buf); - if (ret) { - dev_err(csi->dev, "Failed to start DMA: %d\n", ret); - spin_unlock_irqrestore(&dma->lock, flags); - goto err_pipeline; - } - - list_move_tail(&buf->list, &dma->submitted); - dma->state = TI_CSI2RX_DMA_ACTIVE; - spin_unlock_irqrestore(&dma->lock, flags); - + /* Start stream 0, we don't allow multiple streams on the source pad */ ret = v4l2_subdev_enable_streams(&csi->subdev, - TI_CSI2RX_PAD_FIRST_SOURCE, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, BIT_U64(0)); if (ret) goto err_dma; @@ -978,7 +983,6 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) err_dma: ti_csi2rx_stop_dma(ctx); -err_pipeline: video_device_pipeline_stop(&ctx->vdev); writel(0, csi->shim + SHIM_CNTL); writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); @@ -995,11 +999,8 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq) video_device_pipeline_stop(&ctx->vdev); - writel(0, csi->shim + SHIM_CNTL); - writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); - ret = v4l2_subdev_disable_streams(&csi->subdev, - TI_CSI2RX_PAD_FIRST_SOURCE, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, BIT_U64(0)); if (ret) dev_err(csi->dev, "Failed to stop subdev stream\n"); @@ -1046,25 +1047,84 @@ static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd, fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); *fmt = format->format; - fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_FIRST_SOURCE, - format->stream); + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + *fmt = format->format; return 0; } +static int _ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + int ret; + + static const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | + V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING); + + if (ret) + return ret; + + /* Only stream ID 0 allowed on source pads */ + for (unsigned int i = 0; i < routing->num_routes; ++i) { + const struct v4l2_subdev_route *route = &routing->routes[i]; + + if (route->source_stream != 0) + return -EINVAL; + } + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); + + return ret; +} + +static int ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + + if (csi->enable_count > 0) + return -EBUSY; + + return _ti_csi2rx_sd_set_routing(sd, state, routing); +} + static int ti_csi2rx_sd_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state) { - struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_route routes[] = { { + .sink_pad = 0, + .sink_stream = 0, + .source_pad = TI_CSI2RX_PAD_FIRST_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + } }; - fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_SINK); - *fmt = ti_csi2rx_default_fmt; + struct v4l2_subdev_krouting routing = { + .num_routes = 1, + .routes = routes, + }; - fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_FIRST_SOURCE); - *fmt = ti_csi2rx_default_fmt; - - return 0; + /* Initialize routing to single route to the fist source pad */ + return _ti_csi2rx_sd_set_routing(sd, state, &routing); } static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd, @@ -1072,15 +1132,56 @@ static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + struct ti_csi2rx_ctx *ctx = &csi->ctx[pad - TI_CSI2RX_PAD_FIRST_SOURCE]; + struct ti_csi2rx_dma *dma = &ctx->dma; struct media_pad *remote_pad; + struct ti_csi2rx_buffer *buf; + unsigned long flags; + u64 sink_streams; int ret = 0; + unsigned int reg; + + ret = ti_csi2rx_get_stream(ctx); + if (ret) + return ret; + + /* Get the VC and DT for all enabled ctx on first stream start */ + if (!csi->enable_count) { + ret = ti_csi2rx_get_vc_and_dt(ctx); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + /* De-assert the pixel interface reset. */ + reg = SHIM_CNTL_PIX_RST; + writel(reg, csi->shim + SHIM_CNTL); + } + + ti_csi2rx_setup_shim(ctx); + ctx->sequence = 0; + + spin_lock_irqsave(&dma->lock, flags); + buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); + + ret = ti_csi2rx_start_dma(ctx, buf); + if (ret) { + dev_err(csi->dev, "Failed to start DMA: %d\n", ret); + spin_unlock_irqrestore(&dma->lock, flags); + return ret; + } + + list_move_tail(&buf->list, &dma->submitted); + dma->state = TI_CSI2RX_DMA_ACTIVE; + spin_unlock_irqrestore(&dma->lock, flags); remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); if (!remote_pad) return -ENODEV; + sink_streams = v4l2_subdev_state_xlate_streams(state, pad, + TI_CSI2RX_PAD_SINK, + &streams_mask); ret = v4l2_subdev_enable_streams(csi->source, remote_pad->index, - BIT_U64(0)); + sink_streams); if (ret) return ret; @@ -1094,18 +1195,28 @@ static int ti_csi2rx_sd_disable_streams(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); + struct ti_csi2rx_ctx *ctx = &csi->ctx[pad - TI_CSI2RX_PAD_FIRST_SOURCE]; struct media_pad *remote_pad; + u64 sink_streams; int ret = 0; + WARN_ON(csi->enable_count == 0); + + writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); + + /* assert pixel reset to prevent stale data */ + if (csi->enable_count == 1) + writel(0, csi->shim + SHIM_CNTL); + remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); if (!remote_pad) return -ENODEV; - - if (csi->enable_count == 0) - return -EINVAL; + sink_streams = v4l2_subdev_state_xlate_streams(state, pad, + TI_CSI2RX_PAD_SINK, + &streams_mask); ret = v4l2_subdev_disable_streams(csi->source, remote_pad->index, - BIT_U64(0)); + sink_streams); if (!ret) --csi->enable_count; @@ -1114,6 +1225,7 @@ static int ti_csi2rx_sd_disable_streams(struct v4l2_subdev *sd, static const struct v4l2_subdev_pad_ops ti_csi2rx_subdev_pad_ops = { .enum_mbus_code = ti_csi2rx_enum_mbus_code, + .set_routing = ti_csi2rx_sd_set_routing, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ti_csi2rx_sd_set_fmt, .enable_streams = ti_csi2rx_sd_enable_streams, @@ -1293,7 +1405,7 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) v4l2_subdev_init(sd, &ti_csi2rx_subdev_ops); sd->internal_ops = &ti_csi2rx_internal_ops; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; - sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; strscpy(sd->name, dev_name(csi->dev), sizeof(sd->name)); sd->dev = csi->dev; sd->entity.ops = &ti_csi2rx_subdev_entity_ops; From 7b6887a034e4586e7cb0371e192065d5740865f1 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:18 +0530 Subject: [PATCH 279/394] media: ti: j721e-csi2rx: Submit all available buffers We already make sure to submit all available buffers to DMA in each DMA completion callback. Move that logic in a separate function, and use it during stream start as well, as most application queue all their buffers before stream on. Signed-off-by: Jai Luthra Reviewed-by: Tomi Valkeinen Reviewed-by: Yemike Abhilash Chandra Co-developed-by: Rishikesh Donadkar Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 1ec63715baf2..071ad969dfa6 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -663,11 +663,32 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx) return ret; } +static int ti_csi2rx_dma_submit_pending(struct ti_csi2rx_ctx *ctx) +{ + struct ti_csi2rx_dma *dma = &ctx->dma; + struct ti_csi2rx_buffer *buf; + int ret = 0; + + /* If there are more buffers to process then start their transfer. */ + while (!list_empty(&dma->queue)) { + buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); + ret = ti_csi2rx_start_dma(ctx, buf); + if (ret) { + dev_err(ctx->csi->dev, + "Failed to queue the next buffer for DMA\n"); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + list_del(&buf->list); + } else { + list_move_tail(&buf->list, &dma->submitted); + } + } + return ret; +} + static void ti_csi2rx_dma_callback(void *param) { struct ti_csi2rx_buffer *buf = param; struct ti_csi2rx_ctx *ctx = buf->ctx; - struct ti_csi2rx_dev *csi = ctx->csi; struct ti_csi2rx_dma *dma = &ctx->dma; unsigned long flags; @@ -684,18 +705,7 @@ static void ti_csi2rx_dma_callback(void *param) vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); list_del(&buf->list); - /* If there are more buffers to process then start their transfer. */ - while (!list_empty(&dma->queue)) { - buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); - - if (ti_csi2rx_start_dma(ctx, buf)) { - dev_err(csi->dev, "Failed to queue the next buffer for DMA\n"); - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } else { - list_move_tail(&buf->list, &dma->submitted); - } - } + ti_csi2rx_dma_submit_pending(ctx); if (list_empty(&dma->submitted)) dma->state = TI_CSI2RX_DMA_IDLE; @@ -1135,7 +1145,6 @@ static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd, struct ti_csi2rx_ctx *ctx = &csi->ctx[pad - TI_CSI2RX_PAD_FIRST_SOURCE]; struct ti_csi2rx_dma *dma = &ctx->dma; struct media_pad *remote_pad; - struct ti_csi2rx_buffer *buf; unsigned long flags; u64 sink_streams; int ret = 0; @@ -1160,16 +1169,13 @@ static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd, ctx->sequence = 0; spin_lock_irqsave(&dma->lock, flags); - buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list); - ret = ti_csi2rx_start_dma(ctx, buf); + ret = ti_csi2rx_dma_submit_pending(ctx); if (ret) { - dev_err(csi->dev, "Failed to start DMA: %d\n", ret); spin_unlock_irqrestore(&dma->lock, flags); return ret; } - list_move_tail(&buf->list, &dma->submitted); dma->state = TI_CSI2RX_DMA_ACTIVE; spin_unlock_irqrestore(&dma->lock, flags); From 13ef2b0fa7e848c6d4658aa2847f6725a71c8712 Mon Sep 17 00:00:00 2001 From: Rishikesh Donadkar Date: Wed, 20 May 2026 17:30:19 +0530 Subject: [PATCH 280/394] media: ti: j721e-csi2rx: Change the drain architecture for multistream On buffer starvation the DMA is marked IDLE, and the stale data in the internal FIFOs gets drained only on the next VIDIOC_QBUF call from the userspace. This approach works fine for a single stream case. But in multistream scenarios, buffer starvation for one stream can block the shared HW FIFO of the CSI2RX IP. This can stall the pipeline for all other streams, even if buffers are available for them. This patch introduces a new architecture, that continuously drains data from the shared HW FIFO into a small (32KiB) buffer if no buffers are made available to the driver from the userspace. This ensures independence between different streams, where a slower downstream element for one camera does not block streaming for other cameras. Additionally, after we drain for a stream, the next frame will be a partial frame, as a portion of its data will have already been drained before a valid buffer is queued by user space to the driver. Return the partial frame to user space with VB2_BUF_STATE_ERROR. Use wait for completion barrier to make sure the shared hardware FIFO is cleared of the data at the end of stream after the source has stopped sending data. Reviewed-by: Jai Luthra Reviewed-by: Yemike Abhilash Chandra Signed-off-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 121 +++++++++--------- 1 file changed, 58 insertions(+), 63 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 071ad969dfa6..3142849f9bb9 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -82,8 +82,8 @@ struct ti_csi2rx_buffer { enum ti_csi2rx_dma_state { TI_CSI2RX_DMA_STOPPED, /* Streaming not started yet. */ - TI_CSI2RX_DMA_IDLE, /* Streaming but no pending DMA operation. */ TI_CSI2RX_DMA_ACTIVE, /* Streaming and pending DMA operation. */ + TI_CSI2RX_DMA_DRAINING, /* Dumping all the data in drain buffer */ }; struct ti_csi2rx_dma { @@ -109,6 +109,7 @@ struct ti_csi2rx_ctx { struct v4l2_format v_fmt; struct ti_csi2rx_dma dma; struct media_pad pad; + struct completion drain_complete; u32 sequence; u32 idx; u32 vc; @@ -249,6 +250,10 @@ static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = { static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx, struct ti_csi2rx_buffer *buf); +/* Forward declarations needed by ti_csi2rx_drain_callback. */ +static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx); +static int ti_csi2rx_dma_submit_pending(struct ti_csi2rx_ctx *ctx); + static const struct ti_csi2rx_fmt *find_format_by_fourcc(u32 pixelformat) { unsigned int i; @@ -608,9 +613,32 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) static void ti_csi2rx_drain_callback(void *param) { - struct completion *drain_complete = param; + struct ti_csi2rx_ctx *ctx = param; + struct ti_csi2rx_dma *dma = &ctx->dma; + unsigned long flags; - complete(drain_complete); + spin_lock_irqsave(&dma->lock, flags); + + if (dma->state == TI_CSI2RX_DMA_STOPPED) { + complete(&ctx->drain_complete); + spin_unlock_irqrestore(&dma->lock, flags); + return; + } + + /* + * If dma->queue is empty, it indicates that no buffer has been + * provided by user space. In this case, initiate a transactions + * to drain the DMA. Since one drain of size DRAIN_BUFFER_SIZE + * will be done here, the subsequent frame will be a + * partial frame, with a size of frame_size - DRAIN_BUFFER_SIZE + */ + if (list_empty(&dma->queue)) { + if (ti_csi2rx_drain_dma(ctx)) + dev_warn(ctx->csi->dev, "DMA drain failed\n"); + } else { + ti_csi2rx_dma_submit_pending(ctx); + } + spin_unlock_irqrestore(&dma->lock, flags); } /* @@ -628,12 +656,9 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx) { struct ti_csi2rx_dev *csi = ctx->csi; struct dma_async_tx_descriptor *desc; - struct completion drain_complete; dma_cookie_t cookie; int ret; - init_completion(&drain_complete); - desc = dmaengine_prep_slave_single(ctx->dma.chan, csi->drain.paddr, csi->drain.len, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -643,7 +668,7 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx) } desc->callback = ti_csi2rx_drain_callback; - desc->callback_param = &drain_complete; + desc->callback_param = ctx; cookie = dmaengine_submit(desc); ret = dma_submit_error(cookie); @@ -652,13 +677,6 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx) dma_async_issue_pending(ctx->dma.chan); - if (!wait_for_completion_timeout(&drain_complete, - msecs_to_jiffies(DRAIN_TIMEOUT_MS))) { - dmaengine_terminate_sync(ctx->dma.chan); - dev_dbg(csi->dev, "DMA transfer timed out for drain buffer\n"); - ret = -ETIMEDOUT; - goto out; - } out: return ret; } @@ -702,14 +720,24 @@ static void ti_csi2rx_dma_callback(void *param) spin_lock_irqsave(&dma->lock, flags); WARN_ON(!list_is_first(&buf->list, &dma->submitted)); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + + if (dma->state == TI_CSI2RX_DMA_DRAINING) { + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + dma->state = TI_CSI2RX_DMA_ACTIVE; + } else { + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + } + list_del(&buf->list); ti_csi2rx_dma_submit_pending(ctx); - if (list_empty(&dma->submitted)) - dma->state = TI_CSI2RX_DMA_IDLE; - + if (list_empty(&dma->submitted)) { + dma->state = TI_CSI2RX_DMA_DRAINING; + if (ti_csi2rx_drain_dma(ctx)) + dev_warn(ctx->csi->dev, + "DMA drain failed on one of the transactions\n"); + } spin_unlock_irqrestore(&dma->lock, flags); } @@ -754,6 +782,8 @@ static void ti_csi2rx_stop_dma(struct ti_csi2rx_ctx *ctx) dma->state = TI_CSI2RX_DMA_STOPPED; spin_unlock_irqrestore(&dma->lock, flags); + init_completion(&ctx->drain_complete); + if (state != TI_CSI2RX_DMA_STOPPED) { /* * Normal DMA termination does not clean up pending data on @@ -762,11 +792,19 @@ static void ti_csi2rx_stop_dma(struct ti_csi2rx_ctx *ctx) * enforced before terminating DMA. */ ret = ti_csi2rx_drain_dma(ctx); - if (ret && ret != -ETIMEDOUT) + if (ret) dev_warn(ctx->csi->dev, "Failed to drain DMA. Next frame might be bogus\n"); } + /* We wait for the drain to complete so that the stream stops + * cleanly, making sure the shared hardware FIFO is cleared of + * data from the current stream. No more data will be coming from + * the source after this. + */ + wait_for_completion_timeout(&ctx->drain_complete, + msecs_to_jiffies(DRAIN_TIMEOUT_MS)); + ret = dmaengine_terminate_sync(ctx->dma.chan); if (ret) dev_err(ctx->csi->dev, "Failed to stop DMA: %d\n", ret); @@ -829,57 +867,14 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb) struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct ti_csi2rx_buffer *buf; struct ti_csi2rx_dma *dma = &ctx->dma; - bool restart_dma = false; unsigned long flags = 0; - int ret; buf = container_of(vb, struct ti_csi2rx_buffer, vb.vb2_buf); buf->ctx = ctx; spin_lock_irqsave(&dma->lock, flags); - /* - * Usually the DMA callback takes care of queueing the pending buffers. - * But if DMA has stalled due to lack of buffers, restart it now. - */ - if (dma->state == TI_CSI2RX_DMA_IDLE) { - /* - * Do not restart DMA with the lock held because - * ti_csi2rx_drain_dma() might block for completion. - * There won't be a race on queueing DMA anyway since the - * callback is not being fired. - */ - restart_dma = true; - dma->state = TI_CSI2RX_DMA_ACTIVE; - } else { - list_add_tail(&buf->list, &dma->queue); - } + list_add_tail(&buf->list, &dma->queue); spin_unlock_irqrestore(&dma->lock, flags); - - if (restart_dma) { - /* - * Once frames start dropping, some data gets stuck in the DMA - * pipeline somewhere. So the first DMA transfer after frame - * drops gives a partial frame. This is obviously not useful to - * the application and will only confuse it. Issue a DMA - * transaction to drain that up. - */ - ret = ti_csi2rx_drain_dma(ctx); - if (ret && ret != -ETIMEDOUT) - dev_warn(ctx->csi->dev, - "Failed to drain DMA. Next frame might be bogus\n"); - - spin_lock_irqsave(&dma->lock, flags); - ret = ti_csi2rx_start_dma(ctx, buf); - if (ret) { - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - dma->state = TI_CSI2RX_DMA_IDLE; - spin_unlock_irqrestore(&dma->lock, flags); - dev_err(ctx->csi->dev, "Failed to start DMA: %d\n", ret); - } else { - list_add_tail(&buf->list, &dma->submitted); - spin_unlock_irqrestore(&dma->lock, flags); - } - } } static int ti_csi2rx_get_stream(struct ti_csi2rx_ctx *ctx) From c85d3d5362a8352c9e94f390ecd1b245e97458d0 Mon Sep 17 00:00:00 2001 From: Changhuang Liang Date: Wed, 20 May 2026 17:30:20 +0530 Subject: [PATCH 281/394] media: cadence: csi2rx: Support runtime PM Use runtime power management hooks to save power when CSI-RX is not in use. Also, shift to goto based error handling in csi2rx_enable_streams() function Signed-off-by: Changhuang Liang Tested-by: Rishikesh Donadkar Reviewed-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- drivers/media/platform/cadence/Kconfig | 1 + drivers/media/platform/cadence/cdns-csi2rx.c | 135 ++++++++++++------- 2 files changed, 87 insertions(+), 49 deletions(-) diff --git a/drivers/media/platform/cadence/Kconfig b/drivers/media/platform/cadence/Kconfig index 1aa608c00dbc..ea85ef82760e 100644 --- a/drivers/media/platform/cadence/Kconfig +++ b/drivers/media/platform/cadence/Kconfig @@ -5,6 +5,7 @@ comment "Cadence media platform drivers" config VIDEO_CADENCE_CSI2RX tristate "Cadence MIPI-CSI2 RX Controller" depends on VIDEO_DEV + depends on PM select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 8931e824c597..1ff2d8f78d5b 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -340,11 +340,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) u32 reg; int ret; - ret = clk_prepare_enable(csi2rx->p_clk); - if (ret) - return ret; - - reset_control_deassert(csi2rx->p_rst); csi2rx_reset(csi2rx); if (csi2rx->error_irq >= 0) @@ -385,7 +380,7 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) { dev_err(csi2rx->dev, "Failed to configure external DPHY: %d\n", ret); - goto err_disable_pclk; + return ret; } } @@ -400,12 +395,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) * hence the reference counting. */ for (i = 0; i < csi2rx->max_streams; i++) { - ret = clk_prepare_enable(csi2rx->pixel_clk[i]); - if (ret) - goto err_disable_pixclk; - - reset_control_deassert(csi2rx->pixel_rst[i]); - writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF | FIELD_PREP(CSI2RX_STREAM_CFG_NUM_PIXELS_MASK, csi2rx->num_pixels[i]), @@ -418,30 +407,8 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); } - ret = clk_prepare_enable(csi2rx->sys_clk); - if (ret) - goto err_disable_pixclk; - - reset_control_deassert(csi2rx->sys_rst); - - clk_disable_unprepare(csi2rx->p_clk); return 0; - -err_disable_pixclk: - for (; i > 0; i--) { - reset_control_assert(csi2rx->pixel_rst[i - 1]); - clk_disable_unprepare(csi2rx->pixel_clk[i - 1]); - } - - if (csi2rx->dphy) { - writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); - phy_power_off(csi2rx->dphy); - } -err_disable_pclk: - clk_disable_unprepare(csi2rx->p_clk); - - return ret; } static void csi2rx_stop(struct csi2rx_priv *csi2rx) @@ -450,10 +417,6 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) u32 val; int ret; - clk_prepare_enable(csi2rx->p_clk); - reset_control_assert(csi2rx->sys_rst); - clk_disable_unprepare(csi2rx->sys_clk); - writel(0, csi2rx->base + CSI2RX_ERROR_IRQS_MASK_REG); for (i = 0; i < csi2rx->max_streams; i++) { @@ -468,14 +431,8 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) if (ret) dev_warn(csi2rx->dev, "Failed to stop streaming on pad%u\n", i); - - reset_control_assert(csi2rx->pixel_rst[i]); - clk_disable_unprepare(csi2rx->pixel_clk[i]); } - reset_control_assert(csi2rx->p_rst); - clk_disable_unprepare(csi2rx->p_clk); - if (csi2rx->dphy) { writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); @@ -549,10 +506,15 @@ static int csi2rx_enable_streams(struct v4l2_subdev *subdev, * enable the whole controller. */ if (!csi2rx->count) { + ret = pm_runtime_resume_and_get(csi2rx->dev); + if (ret < 0) + goto err; + csi2rx_update_vc_select(csi2rx, state); + ret = csi2rx_start(csi2rx); if (ret) - return ret; + goto err_put_pm; } /* Start streaming on the source */ @@ -562,13 +524,20 @@ static int csi2rx_enable_streams(struct v4l2_subdev *subdev, dev_err(csi2rx->dev, "Failed to start streams %#llx on subdev\n", sink_streams); - if (!csi2rx->count) - csi2rx_stop(csi2rx); - return ret; + goto err_stop_csi; } csi2rx->count++; return 0; + +err_stop_csi: + if (!csi2rx->count) + csi2rx_stop(csi2rx); +err_put_pm: + if (!csi2rx->count) + pm_runtime_put(csi2rx->dev); +err: + return ret; } static int csi2rx_disable_streams(struct v4l2_subdev *subdev, @@ -590,8 +559,10 @@ static int csi2rx_disable_streams(struct v4l2_subdev *subdev, csi2rx->count--; /* Let the last user turn off the lights. */ - if (!csi2rx->count) + if (!csi2rx->count) { csi2rx_stop(csi2rx); + pm_runtime_put(csi2rx->dev); + } return 0; } @@ -1019,6 +990,7 @@ static int csi2rx_probe(struct platform_device *pdev) if (ret) goto err_cleanup; + pm_runtime_enable(csi2rx->dev); ret = v4l2_async_register_subdev(&csi2rx->subdev); if (ret < 0) goto err_free_state; @@ -1033,6 +1005,7 @@ static int csi2rx_probe(struct platform_device *pdev) err_free_state: v4l2_subdev_cleanup(&csi2rx->subdev); + pm_runtime_disable(csi2rx->dev); err_cleanup: v4l2_async_nf_unregister(&csi2rx->notifier); v4l2_async_nf_cleanup(&csi2rx->notifier); @@ -1051,9 +1024,72 @@ static void csi2rx_remove(struct platform_device *pdev) v4l2_async_unregister_subdev(&csi2rx->subdev); v4l2_subdev_cleanup(&csi2rx->subdev); media_entity_cleanup(&csi2rx->subdev.entity); + pm_runtime_disable(csi2rx->dev); kfree(csi2rx); } +static int csi2rx_runtime_suspend(struct device *dev) +{ + struct csi2rx_priv *csi2rx = dev_get_drvdata(dev); + + reset_control_assert(csi2rx->sys_rst); + clk_disable_unprepare(csi2rx->sys_clk); + + for (unsigned int i = 0; i < csi2rx->max_streams; i++) { + reset_control_assert(csi2rx->pixel_rst[i]); + clk_disable_unprepare(csi2rx->pixel_clk[i]); + } + + reset_control_assert(csi2rx->p_rst); + clk_disable_unprepare(csi2rx->p_clk); + + return 0; +} + +static int csi2rx_runtime_resume(struct device *dev) +{ + struct csi2rx_priv *csi2rx = dev_get_drvdata(dev); + unsigned int i; + int ret; + + ret = clk_prepare_enable(csi2rx->p_clk); + if (ret) + return ret; + + reset_control_deassert(csi2rx->p_rst); + + for (i = 0; i < csi2rx->max_streams; i++) { + ret = clk_prepare_enable(csi2rx->pixel_clk[i]); + if (ret) + goto err_disable_pixclk; + + reset_control_deassert(csi2rx->pixel_rst[i]); + } + + ret = clk_prepare_enable(csi2rx->sys_clk); + if (ret) + goto err_disable_pixclk; + + reset_control_deassert(csi2rx->sys_rst); + + return 0; + +err_disable_pixclk: + while (i--) { + reset_control_assert(csi2rx->pixel_rst[i]); + clk_disable_unprepare(csi2rx->pixel_clk[i]); + } + + reset_control_assert(csi2rx->p_rst); + clk_disable_unprepare(csi2rx->p_clk); + + return ret; +} + +static const struct dev_pm_ops csi2rx_pm_ops = { + RUNTIME_PM_OPS(csi2rx_runtime_suspend, csi2rx_runtime_resume, NULL) +}; + static const struct of_device_id csi2rx_of_table[] = { { .compatible = "starfive,jh7110-csi2rx" }, { .compatible = "cdns,csi2rx" }, @@ -1068,6 +1104,7 @@ static struct platform_driver csi2rx_driver = { .driver = { .name = "cdns-csi2rx", .of_match_table = csi2rx_of_table, + .pm = &csi2rx_pm_ops, }, }; module_platform_driver(csi2rx_driver); From c0429f49f4267496a710f8db619696e87c796832 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:21 +0530 Subject: [PATCH 282/394] media: ti: j721e-csi2rx: Support runtime suspend Add support for runtime power-management to enable powering off the shared power domain between Cadence CSI2RX and TI CSI2RX wrapper when the device(s) are not in use. When powering off the IP, the PSI-L endpoint loses the paired DMA channels. Thus we have to release the DMA channels at runtime suspend and request them again at resume. Tested-by: Rishikesh Donadkar Reviewed-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen Signed-off-by: Jai Luthra Co-developed-by: Rishikesh Donadkar Signed-off-by: Rishikesh Donadkar Signed-off-by: Sakari Ailus --- drivers/media/platform/ti/Kconfig | 1 + .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 56 ++++++++++++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/ti/Kconfig b/drivers/media/platform/ti/Kconfig index da33facf4467..d0cb05481bd8 100644 --- a/drivers/media/platform/ti/Kconfig +++ b/drivers/media/platform/ti/Kconfig @@ -83,6 +83,7 @@ config VIDEO_TI_J721E_CSI2RX depends on VIDEO_CADENCE_CSI2RX depends on PHY_CADENCE_DPHY_RX || COMPILE_TEST depends on ARCH_K3 || COMPILE_TEST + depends on PM select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE help diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 3142849f9bb9..d68b8d6ffeb1 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -964,14 +965,18 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) struct ti_csi2rx_dev *csi = ctx->csi; struct ti_csi2rx_dma *dma = &ctx->dma; unsigned long flags; - int ret = 0; + int ret; + + ret = pm_runtime_resume_and_get(csi->dev); + if (ret) + return ret; spin_lock_irqsave(&dma->lock, flags); if (list_empty(&dma->queue)) ret = -EIO; spin_unlock_irqrestore(&dma->lock, flags); if (ret) - return ret; + goto err; ret = video_device_pipeline_start(&ctx->vdev, &csi->pipe); if (ret) @@ -993,6 +998,8 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); err: ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_QUEUED); + pm_runtime_put(csi->dev); + return ret; } @@ -1012,6 +1019,7 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq) ti_csi2rx_stop_dma(ctx); ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_ERROR); + pm_runtime_put(csi->dev); } static const struct vb2_ops csi_vb2_qops = { @@ -1257,7 +1265,9 @@ static void ti_csi2rx_cleanup_notifier(struct ti_csi2rx_dev *csi) static void ti_csi2rx_cleanup_ctx(struct ti_csi2rx_ctx *ctx) { - dma_release_channel(ctx->dma.chan); + if (!pm_runtime_status_suspended(ctx->csi->dev)) + dma_release_channel(ctx->dma.chan); + vb2_queue_release(&ctx->vidq); video_unregister_device(&ctx->vdev); @@ -1507,6 +1517,38 @@ static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx) return ret; } +static int ti_csi2rx_runtime_suspend(struct device *dev) +{ + struct ti_csi2rx_dev *csi = dev_get_drvdata(dev); + + if (csi->enable_count != 0) + return -EBUSY; + + for (unsigned int i = 0; i < csi->num_ctx; i++) + dma_release_channel(csi->ctx[i].dma.chan); + + return 0; +} + +static int ti_csi2rx_runtime_resume(struct device *dev) +{ + struct ti_csi2rx_dev *csi = dev_get_drvdata(dev); + int ret; + + for (unsigned int i = 0; i < csi->num_ctx; i++) { + ret = ti_csi2rx_init_dma(&csi->ctx[i]); + if (ret) + return ret; + } + + return 0; +} + +static const struct dev_pm_ops ti_csi2rx_pm_ops = { + RUNTIME_PM_OPS(ti_csi2rx_runtime_suspend, ti_csi2rx_runtime_resume, + NULL) +}; + static int ti_csi2rx_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1562,6 +1604,9 @@ static int ti_csi2rx_probe(struct platform_device *pdev) goto err_ctx; } + pm_runtime_set_active(csi->dev); + pm_runtime_enable(csi->dev); + ret = ti_csi2rx_notifier_register(csi); if (ret) goto err_ctx; @@ -1592,6 +1637,9 @@ static void ti_csi2rx_remove(struct platform_device *pdev) struct ti_csi2rx_dev *csi = platform_get_drvdata(pdev); unsigned int i; + if (!pm_runtime_status_suspended(&pdev->dev)) + pm_runtime_set_suspended(&pdev->dev); + for (i = 0; i < csi->num_ctx; i++) ti_csi2rx_cleanup_ctx(&csi->ctx[i]); @@ -1599,6 +1647,7 @@ static void ti_csi2rx_remove(struct platform_device *pdev) ti_csi2rx_cleanup_v4l2(csi); dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr, csi->drain.paddr); + pm_runtime_disable(&pdev->dev); } static const struct of_device_id ti_csi2rx_of_match[] = { @@ -1613,6 +1662,7 @@ static struct platform_driver ti_csi2rx_pdrv = { .driver = { .name = TI_CSI2RX_MODULE_NAME, .of_match_table = ti_csi2rx_of_match, + .pm = &ti_csi2rx_pm_ops, }, }; From ec8d4573c3d51b248fcc4fdd333a3b48f8e35c41 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Wed, 20 May 2026 17:30:22 +0530 Subject: [PATCH 283/394] media: ti: j721e-csi2rx: Support system suspend using pm_notifier As this device is the "orchestrator" for the rest of the media pipeline, we need to stop all on-going streams before system suspend and enable them back when the system wakes up from sleep. Using .suspend/.resume callbacks does not work, as the order of those callbacks amongst various devices in the camera pipeline like the sensor, FPD serdes, CSI bridge etc. is impossible to enforce, even with device links. For example, the Cadence CSI bridge is a child device of this device, thus we cannot create a device link with the CSI bridge as a provider and this device as consumer. This can lead to situations where all the dependencies for the bridge have not yet resumed when we request the subdev to start streaming again through the .resume callback defined in this device. Instead here we register a notifier callback with the PM framework which is triggered when the system is fully functional. At this point we can cleanly stop or start the streams, because we know all other devices and their dependencies are functional. A downside of this approach is that the userspace is also alive (not frozen yet, or just thawed), so the suspend notifier might complete before the userspace has completed all ioctls, like QBUF/DQBUF/STREAMON/STREAMOFF. Tested-by: Rishikesh Donadkar Reviewed-by: Rishikesh Donadkar Signed-off-by: Jai Luthra Signed-off-by: Rishikesh Donadkar Reviewed-by: Tomi Valkeinen Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index d68b8d6ffeb1..21388284cbaa 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -131,6 +131,7 @@ struct ti_csi2rx_dev { struct v4l2_subdev *source; struct v4l2_subdev subdev; struct ti_csi2rx_ctx ctx[TI_CSI2RX_MAX_CTX]; + struct notifier_block pm_notifier; u8 pix_per_clk; /* Buffer to drain stale data from PSI-L endpoint */ struct { @@ -1544,6 +1545,124 @@ static int ti_csi2rx_runtime_resume(struct device *dev) return 0; } +static int ti_csi2rx_suspend(struct device *dev) +{ + struct ti_csi2rx_dev *csi = dev_get_drvdata(dev); + enum ti_csi2rx_dma_state state; + struct ti_csi2rx_ctx *ctx; + struct ti_csi2rx_dma *dma; + unsigned long flags = 0; + int i, ret = 0; + + /* If device was not in use we can simply suspend */ + if (pm_runtime_status_suspended(dev)) + return 0; + + /* + * If device is running, assert the pixel reset to cleanly stop any + * on-going streams before we suspend. + */ + writel(0, csi->shim + SHIM_CNTL); + + for (i = 0; i < csi->num_ctx; i++) { + ctx = &csi->ctx[i]; + dma = &ctx->dma; + + spin_lock_irqsave(&dma->lock, flags); + state = dma->state; + spin_unlock_irqrestore(&dma->lock, flags); + + if (state != TI_CSI2RX_DMA_STOPPED) { + /* Disable source */ + ret = v4l2_subdev_disable_streams(&csi->subdev, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, + BIT(0)); + if (ret) + dev_err(csi->dev, "Failed to stop subdev stream\n"); + } + + /* Stop any on-going streams */ + writel(0, csi->shim + SHIM_DMACNTX(ctx->idx)); + + /* Drain DMA */ + ti_csi2rx_drain_dma(ctx); + + /* Terminate DMA */ + ret = dmaengine_terminate_sync(ctx->dma.chan); + if (ret) + dev_err(csi->dev, "Failed to stop DMA\n"); + } + + return ret; +} + +static int ti_csi2rx_resume(struct device *dev) +{ + struct ti_csi2rx_dev *csi = dev_get_drvdata(dev); + struct ti_csi2rx_ctx *ctx; + struct ti_csi2rx_dma *dma; + struct ti_csi2rx_buffer *buf; + unsigned long flags = 0; + unsigned int reg; + int i, ret = 0; + + /* If device was not in use, we can simply wakeup */ + if (pm_runtime_status_suspended(dev)) + return 0; + + /* If device was in use before, restore all the running streams */ + reg = SHIM_CNTL_PIX_RST; + writel(reg, csi->shim + SHIM_CNTL); + + for (i = 0; i < csi->num_ctx; i++) { + ctx = &csi->ctx[i]; + dma = &ctx->dma; + spin_lock_irqsave(&dma->lock, flags); + if (dma->state != TI_CSI2RX_DMA_STOPPED) { + /* Re-submit all previously submitted buffers to DMA */ + list_for_each_entry(buf, &ctx->dma.submitted, list) { + ti_csi2rx_start_dma(ctx, buf); + } + spin_unlock_irqrestore(&dma->lock, flags); + + /* Restore stream config */ + ti_csi2rx_setup_shim(ctx); + + ret = v4l2_subdev_enable_streams(&csi->subdev, + TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx, + BIT(0)); + if (ret) + dev_err(ctx->csi->dev, "Failed to start subdev\n"); + } else { + spin_unlock_irqrestore(&dma->lock, flags); + } + } + + return ret; +} + +static int ti_csi2rx_pm_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct ti_csi2rx_dev *csi = + container_of(nb, struct ti_csi2rx_dev, pm_notifier); + + switch (action) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + case PM_RESTORE_PREPARE: + ti_csi2rx_suspend(csi->dev); + break; + case PM_POST_SUSPEND: + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + ti_csi2rx_resume(csi->dev); + break; + } + + return NOTIFY_DONE; +} + static const struct dev_pm_ops ti_csi2rx_pm_ops = { RUNTIME_PM_OPS(ti_csi2rx_runtime_suspend, ti_csi2rx_runtime_resume, NULL) @@ -1617,6 +1736,20 @@ static int ti_csi2rx_probe(struct platform_device *pdev) goto err_notifier; } + /* + * Use PM notifier instead of .suspend/.resume callbacks because the + * ordering of callbacks among camera pipeline devices (sensor, serdes, + * CSI bridge) cannot be enforced even with device links. The notifier + * is called when the system is fully functional, ensuring all + * dependencies are available when stopping/starting streams. + */ + csi->pm_notifier.notifier_call = ti_csi2rx_pm_notifier; + ret = register_pm_notifier(&csi->pm_notifier); + if (ret) { + dev_err(csi->dev, "Failed to create PM notifier: %d\n", ret); + goto err_notifier; + } + return 0; err_notifier: @@ -1644,6 +1777,8 @@ static void ti_csi2rx_remove(struct platform_device *pdev) ti_csi2rx_cleanup_ctx(&csi->ctx[i]); ti_csi2rx_cleanup_notifier(csi); + unregister_pm_notifier(&csi->pm_notifier); + ti_csi2rx_cleanup_v4l2(csi); dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr, csi->drain.paddr); From 1d793a29efb4260f90913f5287939bf95573b073 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 May 2026 09:30:44 +0200 Subject: [PATCH 284/394] media: vivid: add vivid_update_reduced_fps() Don't call vivid_update_format_cap() when switching to/from reduced fps for HDMI inputs: that will also reset the format, which is overkill for this. Make a new vivid_update_reduced_fps() function that just updates the dev->timeperframe_vid_cap. Reviewed-by: Nicolas Dufresne Fixes: c79aa6aeadb0 ("[media] vivid-capture: add control for reduced frame rate") Cc: stable@vger.kernel.org Signed-off-by: Hans Verkuil --- .../media/test-drivers/vivid/vivid-ctrls.c | 3 +- .../media/test-drivers/vivid/vivid-vid-cap.c | 32 +++++++++++-------- .../media/test-drivers/vivid/vivid-vid-cap.h | 1 + 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index f94c15ff84f7..1077445f5772 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -609,7 +609,8 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) break; case VIVID_CID_REDUCED_FPS: dev->reduced_fps = ctrl->val; - vivid_update_format_cap(dev, true); + if (dev->input_type[dev->input] == HDMI) + vivid_update_reduced_fps(dev); break; case VIVID_CID_HAS_CROP_CAP: dev->has_crop_cap = ctrl->val; diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index b95f06a9b5ae..76e0b161c049 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -364,6 +364,24 @@ static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev) return TPG_PIXEL_ASPECT_SQUARE; } +void vivid_update_reduced_fps(struct vivid_dev *dev) +{ + struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; + unsigned int size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); + u64 pixelclock; + + if (dev->reduced_fps && can_reduce_fps(bt)) { + pixelclock = div_u64(bt->pixelclock * 1000, 1001); + bt->flags |= V4L2_DV_FL_REDUCED_FPS; + } else { + pixelclock = bt->pixelclock; + bt->flags &= ~V4L2_DV_FL_REDUCED_FPS; + } + dev->timeperframe_vid_cap = (struct v4l2_fract) { + size / 100, (u32)pixelclock / 100 + }; +} + /* * Called whenever the format has to be reset which can occur when * changing inputs, standard, timings, etc. @@ -372,8 +390,6 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) { struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; u32 dims[V4L2_CTRL_MAX_DIMS] = {}; - unsigned size; - u64 pixelclock; switch (dev->input_type[dev->input]) { case WEBCAM: @@ -402,17 +418,7 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) case HDMI: dev->src_rect.width = bt->width; dev->src_rect.height = bt->height; - size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt); - if (dev->reduced_fps && can_reduce_fps(bt)) { - pixelclock = div_u64(bt->pixelclock * 1000, 1001); - bt->flags |= V4L2_DV_FL_REDUCED_FPS; - } else { - pixelclock = bt->pixelclock; - bt->flags &= ~V4L2_DV_FL_REDUCED_FPS; - } - dev->timeperframe_vid_cap = (struct v4l2_fract) { - size / 100, (u32)pixelclock / 100 - }; + vivid_update_reduced_fps(dev); if (bt->interlaced) dev->field_cap = V4L2_FIELD_ALTERNATE; else diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.h b/drivers/media/test-drivers/vivid/vivid-vid-cap.h index 38a99f7e038e..d08a85927510 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.h +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.h @@ -9,6 +9,7 @@ #define _VIVID_VID_CAP_H_ void vivid_update_quality(struct vivid_dev *dev); +void vivid_update_reduced_fps(struct vivid_dev *dev); void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls); void vivid_update_outputs(struct vivid_dev *dev); void vivid_update_connected_outputs(struct vivid_dev *dev); From c2d1a2130c93f6d758af58590b86b2254c7a1dec Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 May 2026 09:22:41 +0200 Subject: [PATCH 285/394] media: vivid: check for vb2_is_busy() when toggling caps The vivid_update_format_cap/out() functions must only be called if the capture/output queue are not busy. But for the controls that select the CROP/COMPOSE/SCALE capability that is not checked. Only when streaming starts will they be set to 'grabbed' and it is impossible to change the control, but between REQBUFS and STREAMON you are still allowed to set these controls. Since vivid_update_format_cap/out will change the format, this can cause unexpected results. Besides adding these checks, also add a WARN_ON in vivid_update_format_cap/out() if the queue is busy. I'm 90% certain that this is the cause of this syzbot bug: https://syzkaller.appspot.com/bug?extid=dac8f5eaa46837e97b89 But since we never have reproducers, it is hard to be certain. In any case, these checks are needed regardless. Reviewed-by: Nicolas Dufresne Fixes: 73c3f48230cd ("[media] vivid: add the control handling code") Cc: stable@vger.kernel.org Reported-by: syzbot+dac8f5eaa46837e97b89@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=dac8f5eaa46837e97b89 Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vivid/vivid-ctrls.c | 12 ++++++++++++ drivers/media/test-drivers/vivid/vivid-vid-cap.c | 6 ++++++ drivers/media/test-drivers/vivid/vivid-vid-out.c | 6 ++++++ 3 files changed, 24 insertions(+) diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index 1077445f5772..a8a134b36720 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -613,14 +613,20 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) vivid_update_reduced_fps(dev); break; case VIVID_CID_HAS_CROP_CAP: + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->has_crop_cap = ctrl->val; vivid_update_format_cap(dev, true); break; case VIVID_CID_HAS_COMPOSE_CAP: + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->has_compose_cap = ctrl->val; vivid_update_format_cap(dev, true); break; case VIVID_CID_HAS_SCALER_CAP: + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->has_scaler_cap = ctrl->val; vivid_update_format_cap(dev, true); break; @@ -1117,14 +1123,20 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case VIVID_CID_HAS_CROP_OUT: + if (vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; dev->has_crop_out = ctrl->val; vivid_update_format_out(dev); break; case VIVID_CID_HAS_COMPOSE_OUT: + if (vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; dev->has_compose_out = ctrl->val; vivid_update_format_out(dev); break; case VIVID_CID_HAS_SCALER_OUT: + if (vb2_is_busy(&dev->vb_vid_out_q)) + return -EBUSY; dev->has_scaler_out = ctrl->val; vivid_update_format_out(dev); break; diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index 76e0b161c049..e20449084709 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -391,6 +391,12 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls) struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt; u32 dims[V4L2_CTRL_MAX_DIMS] = {}; + /* + * This resets the format, so must never be called while vb2_is_busy(). + */ + if (WARN_ON(vb2_is_busy(&dev->vb_vid_cap_q))) + return; + switch (dev->input_type[dev->input]) { case WEBCAM: default: diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c index 8c037b90833e..23e1d5a189ee 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c @@ -214,6 +214,12 @@ void vivid_update_format_out(struct vivid_dev *dev) unsigned size, p; u64 pixelclock; + /* + * This resets the format, so must never be called while vb2_is_busy(). + */ + if (WARN_ON(vb2_is_busy(&dev->vb_vid_out_q))) + return; + switch (dev->output_type[dev->output]) { case SVID: default: From b3a035a02213c0409ce5f14bcbb0a435a6eca085 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 6 May 2026 19:54:37 +0300 Subject: [PATCH 286/394] media: mc-entity: Fix documentation typo in function name The media_entity_pads_init() function name is misspelled. Fix it. Reviewed-by: Frank Li Reviewed-by: Jacopo Mondi Link: https://patch.msgid.link/20260506165438.1767378-1-laurent.pinchart@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- include/media/media-entity.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/media-entity.h b/include/media/media-entity.h index b91ff6f8c3bb..904f61c14dc1 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -726,7 +726,7 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, * the entity (currently, it does nothing). * * Calling media_entity_cleanup() on a media_entity whose memory has been - * zeroed but that has not been initialized with media_entity_pad_init() is + * zeroed but that has not been initialized with media_entity_pads_init() is * valid and is a no-op. */ #if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) From aef73a9dad5c761e4a70fa171fed8e85ca1c1abd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 6 May 2026 19:54:38 +0300 Subject: [PATCH 287/394] media: mc-entity: Drop ifdef for media_entity_cleanup definition The media_entity_cleanup() function is defined in media-entity.h as a static inline no-op when CONFIG_MEDIA_CONTROLLER is enabled, and as a no-op macro otherwise. This complexity is unneeded. Use a static inline function in all cases. Reviewed-by: Jacopo Mondi Link: https://patch.msgid.link/20260506165438.1767378-2-laurent.pinchart@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- include/media/media-entity.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 904f61c14dc1..d9b72cd87d52 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -729,11 +729,9 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, * zeroed but that has not been initialized with media_entity_pads_init() is * valid and is a no-op. */ -#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) -static inline void media_entity_cleanup(struct media_entity *entity) {} -#else -#define media_entity_cleanup(entity) do { } while (false) -#endif +static inline void media_entity_cleanup(struct media_entity *entity) +{ +} /** * media_get_pad_index() - retrieves a pad index from an entity From c4c01c4fd4a3916ffdfb35ad9f511c48e289f51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Fri, 1 May 2026 21:03:39 +0200 Subject: [PATCH 288/394] media: uapi: rkisp: Correct name version enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The name of the enum to hold the mapping of parameter buffer versions have a typo in the name, correct it. While this is a uAPI header the impact should be minimal as the enum is only used as a collection for the one version number supported. Fixes: e9d05e9d5db1 ("media: uapi: rkisp1-config: Add extensible params format") Cc: stable@vger.kernel.org Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260501190339.3449193-1-niklas.soderlund+renesas@ragnatech.se Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- include/uapi/linux/rkisp1-config.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/uapi/linux/rkisp1-config.h b/include/uapi/linux/rkisp1-config.h index b2d2a71f7baf..7638b2220600 100644 --- a/include/uapi/linux/rkisp1-config.h +++ b/include/uapi/linux/rkisp1-config.h @@ -1535,11 +1535,11 @@ struct rkisp1_ext_params_wdr_config { sizeof(struct rkisp1_ext_params_wdr_config)) /** - * enum rksip1_ext_param_buffer_version - RkISP1 extensible parameters version + * enum rkisp1_ext_param_buffer_version - RkISP1 extensible parameters version * * @RKISP1_EXT_PARAM_BUFFER_V1: First version of RkISP1 extensible parameters */ -enum rksip1_ext_param_buffer_version { +enum rkisp1_ext_param_buffer_version { RKISP1_EXT_PARAM_BUFFER_V1 = V4L2_ISP_PARAMS_VERSION_V1, }; @@ -1601,7 +1601,7 @@ enum rksip1_ext_param_buffer_version { * +---------------------------------------------------------------------+ * * @version: The RkISP1 extensible parameters buffer version, see - * :c:type:`rksip1_ext_param_buffer_version` + * :c:type:`rkisp1_ext_param_buffer_version` * @data_size: The RkISP1 configuration data effective size, excluding this * header * @data: The RkISP1 extensible configuration data blocks From 27caa94f4d13d16f7f63eec879ea7d4d79699415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barnab=C3=A1s=20P=C5=91cze?= Date: Mon, 11 May 2026 17:09:57 +0200 Subject: [PATCH 289/394] media: rkisp1: Add support for CAC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CAC block implements chromatic aberration correction. Expose it to userspace using the extensible parameters format. This was tested on the i.MX8MP platform, but based on available documentation it is also present in the RK3399 variant (V10). Thus presumably also in later versions, so no feature flag is introduced. Signed-off-by: Barnabás PÅ‘cze Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260511150957.581049-1-barnabas.pocze@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../platform/rockchip/rkisp1/rkisp1-params.c | 68 +++++++++++ .../platform/rockchip/rkisp1/rkisp1-regs.h | 15 ++- include/uapi/linux/rkisp1-config.h | 107 +++++++++++++++++- 3 files changed, 187 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index 6442436a5e42..042b759eba62 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -64,6 +64,7 @@ union rkisp1_ext_params_config { struct rkisp1_ext_params_compand_bls_config compand_bls; struct rkisp1_ext_params_compand_curve_config compand_curve; struct rkisp1_ext_params_wdr_config wdr; + struct rkisp1_ext_params_cac_config cac; }; enum rkisp1_params_formats { @@ -1413,6 +1414,47 @@ static void rkisp1_wdr_config(struct rkisp1_params *params, RKISP1_CIF_ISP_WDR_TONE_CURVE_YM_MASK); } +static void rkisp1_cac_config(struct rkisp1_params *params, + const struct rkisp1_cif_isp_cac_config *arg) +{ + u32 val; + + /* + * The enable bit is in the same register (RKISP1_CIF_ISP_CAC_CTRL), + * so only set the clipping mode, and do not modify the other bits. + */ + val = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_CAC_CTRL); + val &= ~(RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE | + RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE); + val |= FIELD_PREP(RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE, arg->h_clip_mode) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE, arg->v_clip_mode); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_CTRL, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_COUNT_START_H_MASK, arg->h_count_start) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_COUNT_START_V_MASK, arg->v_count_start); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_COUNT_START, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[0]) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[0]); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_A, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[1]) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[1]); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_B, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[2]) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[2]); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_C, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_NF_MASK, arg->x_nf) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_NS_MASK, arg->x_ns); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_X_NORM, val); + + val = FIELD_PREP(RKISP1_CIF_ISP_CAC_NF_MASK, arg->y_nf) | + FIELD_PREP(RKISP1_CIF_ISP_CAC_NS_MASK, arg->y_ns); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_Y_NORM, val); +} + static void rkisp1_isp_isr_other_config(struct rkisp1_params *params, const struct rkisp1_params_cfg *new_params) @@ -2089,6 +2131,25 @@ static void rkisp1_ext_params_wdr(struct rkisp1_params *params, RKISP1_CIF_ISP_WDR_CTRL_ENABLE); } +static void rkisp1_ext_params_cac(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_cac_config *cac = &block->cac; + + if (cac->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CAC_CTRL, + RKISP1_CIF_ISP_CAC_CTRL_ENABLE); + return; + } + + rkisp1_cac_config(params, &cac->config); + + if ((cac->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(cac->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CAC_CTRL, + RKISP1_CIF_ISP_CAC_CTRL_ENABLE); +} + typedef void (*rkisp1_block_handler)(struct rkisp1_params *params, const union rkisp1_ext_params_config *config); @@ -2185,6 +2246,10 @@ static const struct rkisp1_ext_params_handler { .handler = rkisp1_ext_params_wdr, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC] = { + .handler = rkisp1_ext_params_cac, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, }; #define RKISP1_PARAMS_BLOCK_INFO(block, data) \ @@ -2215,6 +2280,7 @@ rkisp1_ext_params_block_types_info[] = { RKISP1_PARAMS_BLOCK_INFO(COMPAND_EXPAND, compand_curve), RKISP1_PARAMS_BLOCK_INFO(COMPAND_COMPRESS, compand_curve), RKISP1_PARAMS_BLOCK_INFO(WDR, wdr), + RKISP1_PARAMS_BLOCK_INFO(CAC, cac), }; static_assert(ARRAY_SIZE(rkisp1_ext_params_handlers) == @@ -2474,6 +2540,8 @@ void rkisp1_params_disable(struct rkisp1_params *params) rkisp1_ie_enable(params, false); rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPF_MODE, RKISP1_CIF_ISP_DPF_MODE_EN); + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CAC_CTRL, + RKISP1_CIF_ISP_CAC_CTRL_ENABLE); } static const struct rkisp1_params_ops rkisp1_v10_params_ops = { diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h index fbeb186cde0d..2b842194de8f 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h @@ -724,6 +724,17 @@ #define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MASK GENMASK(20, 16) #define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MAX 16U +/* CAC */ +#define RKISP1_CIF_ISP_CAC_CTRL_ENABLE BIT(0) +#define RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE GENMASK(2, 1) +#define RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE BIT(3) +#define RKISP1_CIF_ISP_CAC_COUNT_START_H_MASK GENMASK(12, 0) +#define RKISP1_CIF_ISP_CAC_COUNT_START_V_MASK GENMASK(28, 16) +#define RKISP1_CIF_ISP_CAC_RED_MASK GENMASK(8, 0) +#define RKISP1_CIF_ISP_CAC_BLUE_MASK GENMASK(24, 16) +#define RKISP1_CIF_ISP_CAC_NF_MASK GENMASK(4, 0) +#define RKISP1_CIF_ISP_CAC_NS_MASK GENMASK(19, 16) + /* =================================================================== */ /* CIF Registers */ /* =================================================================== */ @@ -1196,8 +1207,8 @@ #define RKISP1_CIF_ISP_CAC_A (RKISP1_CIF_ISP_CAC_BASE + 0x00000008) #define RKISP1_CIF_ISP_CAC_B (RKISP1_CIF_ISP_CAC_BASE + 0x0000000c) #define RKISP1_CIF_ISP_CAC_C (RKISP1_CIF_ISP_CAC_BASE + 0x00000010) -#define RKISP1_CIF_ISP_X_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000014) -#define RKISP1_CIF_ISP_Y_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000018) +#define RKISP1_CIF_ISP_CAC_X_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000014) +#define RKISP1_CIF_ISP_CAC_Y_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000018) #define RKISP1_CIF_ISP_EXP_BASE 0x00002600 #define RKISP1_CIF_ISP_EXP_CTRL (RKISP1_CIF_ISP_EXP_BASE + 0x00000000) diff --git a/include/uapi/linux/rkisp1-config.h b/include/uapi/linux/rkisp1-config.h index 7638b2220600..d97384abc157 100644 --- a/include/uapi/linux/rkisp1-config.h +++ b/include/uapi/linux/rkisp1-config.h @@ -967,6 +967,92 @@ struct rkisp1_cif_isp_wdr_config { __u8 use_iref; }; +/* + * enum rkisp1_cif_isp_cac_h_clip_mode - horizontal clipping mode + * + * @RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4PX: +/- 4 pixels + * @RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4_5PX: +/- 4/5 pixels depending on bayer position + */ +enum rkisp1_cif_isp_cac_h_clip_mode { + RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4PX = 0, + RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4_5PX = 1, +}; + +/** + * enum rkisp1_cif_isp_cac_v_clip_mode - vertical clipping mode + * + * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_2PX: +/- 2 pixels + * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3PX: +/- 3 pixels + * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3_4PX: +/- 3/4 pixels depending on bayer position + */ +enum rkisp1_cif_isp_cac_v_clip_mode { + RKISP1_CIF_ISP_CAC_V_CLIP_MODE_2PX = 0, + RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3PX = 1, + RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3_4PX = 2, +}; + +/** + * struct rkisp1_cif_isp_cac_config - chromatic aberration correction configuration + * + * The correction is carried out by shifting the red and blue pixels relative + * to the green ones, depending on the distance from the optical center: + * + * @h_count_start: horizontal coordinate of the optical center (13-bit unsigned integer; [1,8191]) + * @v_count_start: vertical coordinate of the optical center (13-bit unsigned integer; [1,8191]) + * + * For each pixel, the x/y distances from the optical center are calculated and + * then transformed into the [0,255] range based on the following formula: + * + * (((d << 4) >> ns) * nf) >> 5 + * + * where `d` is the distance, `ns` and `nf` are the normalization parameters: + * + * @x_nf: horizontal normalization scale parameter (5-bit unsigned integer; [0,31]) + * @x_ns: horizontal normalization shift parameter (4-bit unsigned integer; [0,15]) + * + * @y_nf: vertical normalization scale parameter (5-bit unsigned integer; [0,31]) + * @y_ns: vertical normalization shift parameter (4-bit unsigned integer; [0,15]) + * + * These parameters should be chosen based on the image resolution, the position + * of the optical center, and the shape of pixels, so that no normalized distance + * is larger than 255. If the pixels have square shape, the two sets of parameters + * should be equal. + * + * The actual amount of correction is calculated with a third degree polynomial: + * + * c[0] * r + c[1] * r^2 + c[2] * r^3 + * + * where `c` is the set of coefficients for the given color, and `r` is distance: + * + * @red: red coefficients (5.4 two's complement; [-16,15.9375]) + * @blue: blue coefficients (5.4 two's complement; [-16,15.9375]) + * + * Finally, the amount is clipped as requested: + * + * @h_clip_mode: maximum horizontal shift (from enum rkisp1_cif_isp_cac_h_clip_mode) + * @v_clip_mode: maximum vertical shift (from enum rkisp1_cif_isp_cac_v_clip_mode) + * + * A positive result will shift away from the optical center, while a negative + * one will shift towards the optical center. In the latter case, the pixel + * values at the edges are duplicated. + */ +struct rkisp1_cif_isp_cac_config { + __u8 h_clip_mode; + __u8 v_clip_mode; + + __u16 h_count_start; + __u16 v_count_start; + + __u16 red[3]; + __u16 blue[3]; + + __u8 x_nf; + __u8 x_ns; + + __u8 y_nf; + __u8 y_ns; +}; + /*---------- PART2: Measurement Statistics ------------*/ /** @@ -1138,6 +1224,7 @@ struct rkisp1_stat_buffer { * @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND: Companding expand curve * @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS: Companding compress curve * @RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR: Wide dynamic range + * @RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC: Chromatic aberration correction */ enum rkisp1_ext_params_block_type { RKISP1_EXT_PARAMS_BLOCK_TYPE_BLS, @@ -1161,6 +1248,7 @@ enum rkisp1_ext_params_block_type { RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND, RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS, RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR, + RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC, }; /* For backward compatibility */ @@ -1507,6 +1595,22 @@ struct rkisp1_ext_params_wdr_config { struct rkisp1_cif_isp_wdr_config config; } __attribute__((aligned(8))); +/** + * struct rkisp1_ext_params_cac_config - RkISP1 extensible params CAC config + * + * RkISP1 extensible parameters CAC block. + * Identified by :c:type:`RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC`. + * + * @header: The RkISP1 extensible parameters header, see + * :c:type:`rkisp1_ext_params_block_header` + * @config: CAC configuration, see + * :c:type:`rkisp1_cif_isp_cac_config` + */ +struct rkisp1_ext_params_cac_config { + struct rkisp1_ext_params_block_header header; + struct rkisp1_cif_isp_cac_config config; +} __attribute__((aligned(8))); + /* * The rkisp1_ext_params_compand_curve_config structure is counted twice as it * is used for both the COMPAND_EXPAND and COMPAND_COMPRESS block types. @@ -1532,7 +1636,8 @@ struct rkisp1_ext_params_wdr_config { sizeof(struct rkisp1_ext_params_compand_bls_config) +\ sizeof(struct rkisp1_ext_params_compand_curve_config) +\ sizeof(struct rkisp1_ext_params_compand_curve_config) +\ - sizeof(struct rkisp1_ext_params_wdr_config)) + sizeof(struct rkisp1_ext_params_wdr_config) +\ + sizeof(struct rkisp1_ext_params_cac_config)) /** * enum rkisp1_ext_param_buffer_version - RkISP1 extensible parameters version From b670bf89824ede5d07d20bb9bfbafb754846081d Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Thu, 7 May 2026 12:13:15 +0800 Subject: [PATCH 290/394] media: nxp: imx8-isi: Fix use-after-free on remove KASAN reports a slab-use-after-free in __media_entity_remove_link() during rmmod of imx8_isi: BUG: KASAN: slab-use-after-free in __media_entity_remove_link+0x608/0x650 Read of size 2 at addr ffff0000d47cb02a by task rmmod/724 Call trace: __media_entity_remove_link+0x608/0x650 __media_entity_remove_links+0x78/0x144 __media_device_unregister_entity+0x150/0x280 media_device_unregister_entity+0x48/0x68 v4l2_device_unregister_subdev+0x158/0x300 v4l2_async_unbind_subdev_one+0x22c/0x358 v4l2_async_nf_unbind_all_subdevs+0xfc/0x1c0 v4l2_async_nf_unregister+0x5c/0x14c mxc_isi_remove+0x124/0x2a0 [imx8_isi] Allocated by task 249: __kmalloc_noprof+0x27c/0x690 mxc_isi_crossbar_init+0x22c/0x560 [imx8_isi] Freed by task 724: kfree+0x1e4/0x5b0 mxc_isi_crossbar_cleanup+0x34/0x80 [imx8_isi] mxc_isi_remove+0x11c/0x2a0 [imx8_isi] The problem is that mxc_isi_remove() calls mxc_isi_crossbar_cleanup() before mxc_isi_v4l2_cleanup(). The crossbar cleanup frees the media entity pads, but the subsequent v4l2 cleanup still tries to remove media links that reference those pads. Fix this by calling mxc_isi_v4l2_cleanup() before mxc_isi_crossbar_cleanup() to ensure all media entities are properly unregistered while the pads are still valid. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Xiaolei Wang Reviewed-by: Frank Li Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260507041318.491594-2-xiaolei.wang@windriver.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index 4bf8570e1b9e..2d639b789910 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -556,8 +556,8 @@ static void mxc_isi_remove(struct platform_device *pdev) mxc_isi_pipe_cleanup(pipe); } - mxc_isi_crossbar_cleanup(&isi->crossbar); mxc_isi_v4l2_cleanup(isi); + mxc_isi_crossbar_cleanup(&isi->crossbar); } static const struct of_device_id mxc_isi_of_match[] = { From 567418eedd25b3d86d489807682030b4b98b73d9 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Thu, 7 May 2026 12:13:16 +0800 Subject: [PATCH 291/394] media: nxp: imx8-isi: Add missing v4l2_subdev_cleanup() in crossbar and pipe Both mxc_isi_crossbar_init() and mxc_isi_pipe_init() call v4l2_subdev_init_finalize() which allocates the subdev active state, but neither mxc_isi_crossbar_cleanup() nor mxc_isi_pipe_cleanup() calls v4l2_subdev_cleanup() to free it. This causes a memory leak on every rmmod, reported by kmemleak: unreferenced object 0xffff0000d06fc800 (size 192): comm "(udev-worker)", pid 254, jiffies 4294913455 backtrace (crc 36eeae58): kmemleak_alloc+0x34/0x40 __kvmalloc_node_noprof+0x5f8/0x7d8 __v4l2_subdev_state_alloc+0x1fc/0x30c __v4l2_subdev_init_finalize+0x178/0x368 Add the missing v4l2_subdev_cleanup() calls before media_entity_cleanup() in both crossbar and pipe cleanup paths. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Xiaolei Wang Reviewed-by: Frank Li Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260507041318.491594-3-xiaolei.wang@windriver.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c | 1 + drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index 605a45124103..c580c831972e 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -491,6 +491,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi) void mxc_isi_crossbar_cleanup(struct mxc_isi_crossbar *xbar) { + v4l2_subdev_cleanup(&xbar->sd); media_entity_cleanup(&xbar->sd.entity); kfree(xbar->pads); kfree(xbar->inputs); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index a41c51dd9ce0..cb50af2270f6 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -819,6 +819,7 @@ void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe) { struct v4l2_subdev *sd = &pipe->sd; + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); mutex_destroy(&pipe->lock); } From 8262de0663318124824aaafd97ddb5d7bb53bd77 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Thu, 7 May 2026 12:13:17 +0800 Subject: [PATCH 292/394] media: nxp: imx8-isi: Fix missing v4l2_subdev_cleanup() in pipe init error path After v4l2_subdev_init_finalize() succeeds in mxc_isi_pipe_init(), if platform_get_irq() or devm_request_irq() fails, the error path jumps to a label that only calls media_entity_cleanup() and mutex_destroy(), missing the v4l2_subdev_cleanup() call needed to free the subdev active state allocated by v4l2_subdev_init_finalize(). Add an error_subdev label that calls v4l2_subdev_cleanup() before falling through to the existing error cleanup. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Xiaolei Wang Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260507041318.491594-4-xiaolei.wang@windriver.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index cb50af2270f6..a59b9456b590 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -796,18 +796,20 @@ int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id) irq = platform_get_irq(to_platform_device(isi->dev), id); if (irq < 0) { ret = irq; - goto error; + goto error_subdev; } ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler, 0, dev_name(isi->dev), pipe); if (ret < 0) { dev_err(isi->dev, "failed to request IRQ (%d)\n", ret); - goto error; + goto error_subdev; } return 0; +error_subdev: + v4l2_subdev_cleanup(sd); error: media_entity_cleanup(&sd->entity); mutex_destroy(&pipe->lock); From d970b27cc48ec42f8a72bc3a4a4ad2e5c7a36395 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Thu, 7 May 2026 12:13:18 +0800 Subject: [PATCH 293/394] media: nxp: imx8-isi: Clean up already-initialized pipes on probe failure When mxc_isi_pipe_init() fails partway through the channel loop or when mxc_isi_v4l2_init() fails, the already initialized pipes are not cleaned up. Fix this by calling mxc_isi_pipe_cleanup() for each already-initialized pipe in the err_xbar error path. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Xiaolei Wang Reviewed-by: Laurent Pinchart Reviewed-by: Frank Li Link: https://patch.msgid.link/20260507041318.491594-5-xiaolei.wang@windriver.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index 2d639b789910..e8545761b5ff 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -538,6 +538,8 @@ static int mxc_isi_probe(struct platform_device *pdev) return 0; err_xbar: + while (i--) + mxc_isi_pipe_cleanup(&isi->pipes[i]); mxc_isi_crossbar_cleanup(&isi->crossbar); return ret; From 75a54af91f33084c0cef11b3cb2f89765af78920 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Fri, 20 Mar 2026 14:42:02 +0800 Subject: [PATCH 294/394] media: nxp: imx8-isi: Prioritize pending buffers over discard buffers The number of times to use the discard buffer is determined by the out_pending list size: discard = list_empty(&video->out_pending) ? 2 : list_is_singular(&video->out_pending) ? 1 : 0; In the current buffer selection logic, when both discard and pending buffers are available, the driver fills hardware slots with discard buffers first which results in an unnecessary frame drop even though a user buffer was queued and ready. Change the buffer selection logic to use pending buffers first (up to the number available), and only use discard buffers to fill remaining slots when insufficient pending buffers are queued. This improves behavior by: - Reducing discarded frames at stream start when user buffers are ready - Decreasing latency in delivering captured frames to user-space - Ensuring user buffers are utilized as soon as they are queued - Improving overall buffer utilization efficiency Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260320-isi_min_buffers-v3-2-66e0fabccca3@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c index 1be3a728f32f..fe4adfa3a1f0 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -792,7 +792,11 @@ static void mxc_isi_video_queue_first_buffers(struct mxc_isi_video *video) struct mxc_isi_buffer *buf; struct list_head *list; - list = i < discard ? &video->out_discard : &video->out_pending; + /* + * Queue buffers: prioritize pending buffers, then discard + * buffers. + */ + list = (i < 2 - discard) ? &video->out_pending : &video->out_discard; buf = list_first_entry(list, struct mxc_isi_buffer, list); mxc_isi_channel_set_outbuf(video->pipe, buf->dma_addrs, buf_id); From 57a7ec5c9f38ce6c4d6209c4b75c8e57e1fea6cf Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Mon, 23 Mar 2026 16:33:30 +0800 Subject: [PATCH 295/394] media: nxp: imx8-isi: Fix potential out-of-bounds issues The maximum downscaling factor supported by ISI can be up to 16. Add minimum value constraint before applying the setting to hardware. Otherwise, the process will not respond even when Ctrl+C is executed. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260323-isi-v3-1-8df53b24e622@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/nxp/imx8-isi/imx8-isi-core.h | 16 ++++++++++++++++ .../media/platform/nxp/imx8-isi/imx8-isi-m2m.c | 11 ++++++++--- .../media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 13 ++++++++----- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h index 14d63ec36416..7547a6559d4c 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -11,6 +11,7 @@ #define __MXC_ISI_CORE_H__ #include +#include #include #include #include @@ -414,4 +415,19 @@ static inline void mxc_isi_debug_cleanup(struct mxc_isi_dev *isi) } #endif +/* + * ISI scaling engine works in two parts: it performs pre-decimation of + * the image followed by bilinear filtering to achieve the desired + * downscaling factor. + * + * The decimation filter provides a maximum downscaling factor of 8, and + * the subsequent bilinear filter provides a maximum downscaling factor + * of 2. Combined, the maximum scaling factor can be up to 16. + */ +static inline unsigned int +mxc_isi_clamp_downscale_16(unsigned int val, unsigned int max_val) +{ + return clamp(val, max(1U, DIV_ROUND_UP(max_val, 16)), max_val); +} + #endif /* __MXC_ISI_CORE_H__ */ diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c index a39ad7a1ab18..de398b232d74 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c @@ -509,9 +509,14 @@ __mxc_isi_m2m_try_fmt_vid(struct mxc_isi_m2m_ctx *ctx, const enum mxc_isi_video_type type) { if (type == MXC_ISI_VIDEO_M2M_CAP) { - /* Downscaling only */ - pix->width = min(pix->width, ctx->queues.out.format.width); - pix->height = min(pix->height, ctx->queues.out.format.height); + const struct v4l2_pix_format_mplane *format = + &ctx->queues.out.format; + + /* Downscaling only, by up to 16. */ + pix->width = mxc_isi_clamp_downscale_16(pix->width, + format->width); + pix->height = mxc_isi_clamp_downscale_16(pix->height, + format->height); } return mxc_isi_format_try(ctx->m2m->pipe, pix, type); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index a59b9456b590..2d0843c86534 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -641,16 +641,19 @@ static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd, /* Composing is supported on the sink only. */ return -EINVAL; - /* The sink crop is bound by the sink format downscaling only). */ + /* + * The ISI supports downscaling only, with a factor up to 16. + * Clamp the compose rectangle size accordingly. + */ format = mxc_isi_pipe_get_pad_format(pipe, state, MXC_ISI_PIPE_PAD_SINK); sel->r.left = 0; sel->r.top = 0; - sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH, - format->width); - sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT, - format->height); + sel->r.width = mxc_isi_clamp_downscale_16(sel->r.width, + format->width); + sel->r.height = mxc_isi_clamp_downscale_16(sel->r.height, + format->height); rect = mxc_isi_pipe_get_pad_compose(pipe, state, MXC_ISI_PIPE_PAD_SINK); From 5eb54da3f874b44149556542d949909898865e29 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Mon, 23 Mar 2026 16:33:31 +0800 Subject: [PATCH 296/394] media: nxp: imx8-isi: Fix scale factor calculation for hardware rounding The ISI hardware rounds the actual output size up to an integer, as described in i.MX93 Reference Manual section 57.7.8 (Channel 0 Scale Factor). The scale factor must be calculated to ensure the theoretical output value rounds up to exactly the desired size. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260323-isi-v3-2-8df53b24e622@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c index 0187d4ab97e8..16b20ea2d1db 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c @@ -112,7 +112,14 @@ static u32 mxc_isi_channel_scaling_ratio(unsigned int from, unsigned int to, else *dec = 8; - return min_t(u32, from * 0x1000 / (to * *dec), ISI_DOWNSCALE_THRESHOLD); + /* + * The ISI rounds output dimensions up to the next integer (i.MX93 RM + * section 57.7.8). Calculate the scale factor such that the theoretical + * output (input / scale_factor) rounds up to exactly the desired + * output. + */ + return min_t(u32, DIV_ROUND_UP(from * 0x1000, to * *dec), + ISI_DOWNSCALE_THRESHOLD); } static void mxc_isi_channel_set_scaling(struct mxc_isi_pipe *pipe, From f0748d9decedd0ef749d21c6b281cf26958dfd55 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:25 +0300 Subject: [PATCH 297/394] media: renesas: vsp1: Avoid forward function declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorder functions to avoid the forward declaration of the vsp1_du_pipeline_configure(). No functional change intended. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-2-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/vsp1/vsp1_drm.c | 90 +++++++++---------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 15d266439564..79b85968b061 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -57,6 +57,50 @@ static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe, * Pipeline Configuration */ +/* Configure all entities in the pipeline. */ +static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) +{ + struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); + struct vsp1_entity *entity; + struct vsp1_entity *next; + struct vsp1_dl_list *dl; + struct vsp1_dl_body *dlb; + unsigned int dl_flags = 0; + + vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], + drm_pipe->width, 0); + + if (drm_pipe->force_brx_release) + dl_flags |= VSP1_DL_FRAME_END_INTERNAL; + if (pipe->output->writeback) + dl_flags |= VSP1_DL_FRAME_END_WRITEBACK; + + dl = vsp1_dl_list_get(pipe->output->dlm); + dlb = vsp1_dl_list_get_body0(dl); + + list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) { + /* Disconnect unused entities from the pipeline. */ + if (!entity->pipe) { + vsp1_dl_body_write(dlb, entity->route->reg, + VI6_DPR_NODE_UNUSED); + + entity->sink = NULL; + list_del(&entity->list_pipe); + + continue; + } + + vsp1_entity_route_setup(entity, pipe, dlb); + vsp1_entity_configure_stream(entity, entity->state, pipe, + dl, dlb); + vsp1_entity_configure_frame(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, + &pipe->part_table[0], dl, dlb); + } + + vsp1_dl_list_commit(dl, dl_flags); +} + /* * Insert the UIF in the pipeline between the prev and next entities. If no UIF * is available connect the two entities directly. @@ -224,8 +268,6 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, /* Setup the BRx source pad. */ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1, struct vsp1_pipeline *pipe); -static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe); - static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, struct vsp1_pipeline *pipe) { @@ -541,50 +583,6 @@ static int vsp1_du_pipeline_setup_output(struct vsp1_device *vsp1, return 0; } -/* Configure all entities in the pipeline. */ -static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) -{ - struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); - struct vsp1_entity *entity; - struct vsp1_entity *next; - struct vsp1_dl_list *dl; - struct vsp1_dl_body *dlb; - unsigned int dl_flags = 0; - - vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], - drm_pipe->width, 0); - - if (drm_pipe->force_brx_release) - dl_flags |= VSP1_DL_FRAME_END_INTERNAL; - if (pipe->output->writeback) - dl_flags |= VSP1_DL_FRAME_END_WRITEBACK; - - dl = vsp1_dl_list_get(pipe->output->dlm); - dlb = vsp1_dl_list_get_body0(dl); - - list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) { - /* Disconnect unused entities from the pipeline. */ - if (!entity->pipe) { - vsp1_dl_body_write(dlb, entity->route->reg, - VI6_DPR_NODE_UNUSED); - - entity->sink = NULL; - list_del(&entity->list_pipe); - - continue; - } - - vsp1_entity_route_setup(entity, pipe, dlb); - vsp1_entity_configure_stream(entity, entity->state, pipe, - dl, dlb); - vsp1_entity_configure_frame(entity, pipe, dl, dlb); - vsp1_entity_configure_partition(entity, pipe, - &pipe->part_table[0], dl, dlb); - } - - vsp1_dl_list_commit(dl, dl_flags); -} - static int vsp1_du_pipeline_set_rwpf_format(struct vsp1_device *vsp1, struct vsp1_rwpf *rwpf, u32 pixelformat, unsigned int pitch) From 79448d5386f62a3caaba7fe04204a587a8bfa5c4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:26 +0300 Subject: [PATCH 298/394] media: renesas: vsp1: Split vsp1_du_setup_lif() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vsp1_du_setup_lif() function is used to configure and enable a pipeline, as well as disable it, depending on the cfg argument being a valid pointer or NULL. This creates a confusing API. Improve it by splitting the function in two, a vsp1_du_enable() function to configure a pipeline, and a vsp1_du_disable() function to disaple it. Keep vsp1_du_setup_lif() as an inline wrapper for existing callers in the DRM subsystem, to simplify merging. The callers will be updated separately and the old API will then be removed. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-3-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/vsp1/vsp1_drm.c | 140 ++++++++++-------- include/media/vsp1.h | 14 +- 2 files changed, 91 insertions(+), 63 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 79b85968b061..1f431874064d 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -629,14 +629,14 @@ int vsp1_du_init(struct device *dev) EXPORT_SYMBOL_GPL(vsp1_du_init); /** - * vsp1_du_setup_lif - Setup the output part of the VSP pipeline + * vsp1_du_enable - Setup and enable a DU pipeline * @dev: the VSP device * @pipe_index: the DRM pipeline index * @cfg: the LIF configuration * * Configure the output part of VSP DRM pipeline for the given frame @cfg.width * and @cfg.height. This sets up formats on the BRx source pad, the WPF sink and - * source pads, and the LIF sink pad. + * source pads, and the LIF sink pad, and then starts the pipeline. * * The @pipe_index argument selects which DRM pipeline to setup. The number of * available pipelines depend on the VSP instance. @@ -649,14 +649,13 @@ EXPORT_SYMBOL_GPL(vsp1_du_init); * * Return 0 on success or a negative error code on failure. */ -int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, - const struct vsp1_du_lif_config *cfg) +int vsp1_du_enable(struct device *dev, unsigned int pipe_index, + const struct vsp1_du_lif_config *cfg) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe; struct vsp1_pipeline *pipe; unsigned long flags; - unsigned int i; int ret; if (pipe_index >= vsp1->info->lif_count) @@ -665,60 +664,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, drm_pipe = &vsp1->drm->pipe[pipe_index]; pipe = &drm_pipe->pipe; - if (!cfg) { - struct vsp1_brx *brx; - - mutex_lock(&vsp1->drm->lock); - - brx = to_brx(&pipe->brx->subdev); - - /* - * NULL configuration means the CRTC is being disabled, stop - * the pipeline and turn the light off. - */ - ret = vsp1_pipeline_stop(pipe); - if (ret == -ETIMEDOUT) - dev_err(vsp1->dev, "DRM pipeline stop timeout\n"); - - for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { - struct vsp1_rwpf *rpf = pipe->inputs[i]; - - if (!rpf) - continue; - - /* - * Remove the RPF from the pipe and the list of BRx - * inputs. - */ - WARN_ON(!rpf->entity.pipe); - rpf->entity.pipe = NULL; - list_del(&rpf->entity.list_pipe); - pipe->inputs[i] = NULL; - - brx->inputs[rpf->brx_input].rpf = NULL; - } - - drm_pipe->du_complete = NULL; - pipe->num_inputs = 0; - - dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n", - __func__, pipe->lif->index, - BRX_NAME(pipe->brx)); - - list_del(&pipe->brx->list_pipe); - pipe->brx->pipe = NULL; - pipe->brx = NULL; - - mutex_unlock(&vsp1->drm->lock); - - vsp1_dlm_reset(pipe->output->dlm); - vsp1_device_put(vsp1); - - dev_dbg(vsp1->dev, "%s: pipeline disabled\n", __func__); - - return 0; - } - /* Reset the underrun counter */ pipe->underrun_count = 0; @@ -741,7 +686,7 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, if (ret < 0) goto unlock; - vsp1_pipeline_dump(pipe, "LIF setup"); + vsp1_pipeline_dump(pipe, "DU enable"); /* Enable the VSP1. */ ret = vsp1_device_get(vsp1); @@ -777,7 +722,80 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, return 0; } -EXPORT_SYMBOL_GPL(vsp1_du_setup_lif); +EXPORT_SYMBOL_GPL(vsp1_du_enable); + +/** + * vsp1_du_disable - Disable and stop a DU pipeline + * @dev: the VSP device + * @pipe_index: the DRM pipeline index + * + * The @pipe_index argument selects which DRM pipeline to disable. The number + * of available pipelines depend on the VSP instance. + * + * Return 0 on success or a negative error code on failure. + */ +int vsp1_du_disable(struct device *dev, unsigned int pipe_index) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + struct vsp1_drm_pipeline *drm_pipe; + struct vsp1_pipeline *pipe; + struct vsp1_brx *brx; + unsigned int i; + int ret; + + if (pipe_index >= vsp1->info->lif_count) + return -EINVAL; + + drm_pipe = &vsp1->drm->pipe[pipe_index]; + pipe = &drm_pipe->pipe; + + mutex_lock(&vsp1->drm->lock); + + brx = to_brx(&pipe->brx->subdev); + + ret = vsp1_pipeline_stop(pipe); + if (ret == -ETIMEDOUT) + dev_err(vsp1->dev, "DRM pipeline stop timeout\n"); + + for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { + struct vsp1_rwpf *rpf = pipe->inputs[i]; + + if (!rpf) + continue; + + /* + * Remove the RPF from the pipe and the list of BRx + * inputs. + */ + WARN_ON(!rpf->entity.pipe); + rpf->entity.pipe = NULL; + list_del(&rpf->entity.list_pipe); + pipe->inputs[i] = NULL; + + brx->inputs[rpf->brx_input].rpf = NULL; + } + + drm_pipe->du_complete = NULL; + pipe->num_inputs = 0; + + dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n", + __func__, pipe->lif->index, + BRX_NAME(pipe->brx)); + + list_del(&pipe->brx->list_pipe); + pipe->brx->pipe = NULL; + pipe->brx = NULL; + + mutex_unlock(&vsp1->drm->lock); + + vsp1_dlm_reset(pipe->output->dlm); + vsp1_device_put(vsp1); + + dev_dbg(vsp1->dev, "%s: pipeline disabled\n", __func__); + + return 0; +} +EXPORT_SYMBOL_GPL(vsp1_du_disable); /** * vsp1_du_atomic_begin - Prepare for an atomic update diff --git a/include/media/vsp1.h b/include/media/vsp1.h index d9b91ff02761..d2085cdb7fcb 100644 --- a/include/media/vsp1.h +++ b/include/media/vsp1.h @@ -44,8 +44,18 @@ struct vsp1_du_lif_config { void *callback_data; }; -int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, - const struct vsp1_du_lif_config *cfg); +int vsp1_du_enable(struct device *dev, unsigned int pipe_index, + const struct vsp1_du_lif_config *cfg); +int vsp1_du_disable(struct device *dev, unsigned int pipe_index); + +static inline int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, + const struct vsp1_du_lif_config *cfg) +{ + if (cfg) + return vsp1_du_enable(dev, pipe_index, cfg); + else + return vsp1_du_disable(dev, pipe_index); +} /** * struct vsp1_du_atomic_config - VSP atomic configuration parameters From 5301fdf971972d36d7266432e91dc025d590c0fe Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:27 +0300 Subject: [PATCH 299/394] drm: renesas: rcar-du: Switch to new VSP API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vsp1_du_setup_lif() function is deprecated. Use the new vsp1_du_enable() and vsp1_du_disable() functions instead. Reviewed-by: Niklas Söderlund Acked-by: Dave Airlie Link: https://patch.msgid.link/20260511235637.3468558-4-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c index a4a49dcd8233..5cfb4d50bc67 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c @@ -87,12 +87,12 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc) __rcar_du_plane_setup(crtc->group, &state); - vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); + vsp1_du_enable(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); } void rcar_du_vsp_disable(struct rcar_du_crtc *crtc) { - vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL); + vsp1_du_disable(crtc->vsp->vsp, crtc->vsp_pipe); } void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc) From 6579799e96cefa8198253f4e059f8ca887c2e537 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:28 +0300 Subject: [PATCH 300/394] drm: renesas: rz-du: Switch to new VSP API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vsp1_du_setup_lif() function is deprecated. Use the new vsp1_du_enable() and vsp1_du_disable() functions instead. Reviewed-by: Niklas Söderlund Acked-by: Biju Das Acked-by: Dave Airlie Link: https://patch.msgid.link/20260511235637.3468558-5-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c index bd486377f037..d5a1d36db2c1 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c @@ -55,12 +55,12 @@ void rzg2l_du_vsp_enable(struct rzg2l_du_crtc *crtc) .callback_data = crtc, }; - vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); + vsp1_du_enable(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); } void rzg2l_du_vsp_disable(struct rzg2l_du_crtc *crtc) { - vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL); + vsp1_du_disable(crtc->vsp->vsp, crtc->vsp_pipe); } void rzg2l_du_vsp_atomic_flush(struct rzg2l_du_crtc *crtc) From 38abda35a6b8769a57c12338aab88e7e979f8c24 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:29 +0300 Subject: [PATCH 301/394] media: renesas: vsp1: Use mutex guards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace manual mutex locking and unlocking with guards. This simplifies error paths and reduces the amount of code. Limit the changes to locations where the guard covers until the end of the function to ease review. Scoped guards will be introduced separately. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-6-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/vsp1/vsp1_brx.c | 26 ++++------- .../media/platform/renesas/vsp1/vsp1_drm.c | 7 +-- .../media/platform/renesas/vsp1/vsp1_entity.c | 19 +++----- .../media/platform/renesas/vsp1/vsp1_histo.c | 35 +++++---------- .../media/platform/renesas/vsp1/vsp1_hsit.c | 15 +++---- .../media/platform/renesas/vsp1/vsp1_rwpf.c | 44 ++++++------------- .../media/platform/renesas/vsp1/vsp1_sru.c | 13 ++---- .../media/platform/renesas/vsp1/vsp1_uds.c | 13 ++---- .../media/platform/renesas/vsp1/vsp1_uif.c | 29 ++++-------- .../media/platform/renesas/vsp1/vsp1_video.c | 20 ++++----- .../media/platform/renesas/vsp1/vsp1_wpf.c | 17 +++---- 11 files changed, 77 insertions(+), 161 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index b1a2c68e9944..ce68d911ac30 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -130,15 +130,12 @@ static int brx_set_format(struct v4l2_subdev *subdev, struct vsp1_brx *brx = to_brx(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&brx->entity.lock); + guard(mutex)(&brx->entity.lock); state = vsp1_entity_get_state(&brx->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; brx_try_format(brx, state, fmt->pad, &fmt->format); @@ -172,9 +169,7 @@ static int brx_set_format(struct v4l2_subdev *subdev, *format = fmt->format; } -done: - mutex_unlock(&brx->entity.lock); - return ret; + return 0; } static int brx_get_selection(struct v4l2_subdev *subdev, @@ -219,7 +214,6 @@ static int brx_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *compose; - int ret = 0; if (sel->pad == brx->entity.source_pad) return -EINVAL; @@ -227,13 +221,11 @@ static int brx_set_selection(struct v4l2_subdev *subdev, if (sel->target != V4L2_SEL_TGT_COMPOSE) return -EINVAL; - mutex_lock(&brx->entity.lock); + guard(mutex)(&brx->entity.lock); state = vsp1_entity_get_state(&brx->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; /* * The compose rectangle top left corner must be inside the output @@ -254,9 +246,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev, compose = v4l2_subdev_state_get_compose(state, sel->pad); *compose = sel->r; -done: - mutex_unlock(&brx->entity.lock); - return ret; + return 0; } static const struct v4l2_subdev_pad_ops brx_pad_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 1f431874064d..1439cf7bfb59 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -920,7 +920,7 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, drm_pipe->crc = cfg->crc; - mutex_lock(&vsp1->drm->lock); + guard(mutex)(&vsp1->drm->lock); if (cfg->writeback.pixelformat) { const struct vsp1_du_writeback_config *wb_cfg = &cfg->writeback; @@ -929,7 +929,7 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, wb_cfg->pixelformat, wb_cfg->pitch); if (WARN_ON(ret < 0)) - goto done; + return; pipe->output->mem.addr[0] = wb_cfg->mem[0]; pipe->output->mem.addr[1] = wb_cfg->mem[1]; @@ -942,9 +942,6 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, vsp1_pipeline_dump(pipe, "atomic update"); vsp1_du_pipeline_configure(pipe); - -done: - mutex_unlock(&vsp1->drm->lock); } EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 1dad9589768c..1a1dafe5331e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -172,9 +172,9 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - mutex_lock(&entity->lock); + guard(mutex)(&entity->lock); + fmt->format = *v4l2_subdev_state_get_format(state, fmt->pad); - mutex_unlock(&entity->lock); return 0; } @@ -308,22 +308,19 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *format; struct v4l2_rect *selection; unsigned int i; - int ret = 0; - mutex_lock(&entity->lock); + guard(mutex)(&entity->lock); state = vsp1_entity_get_state(entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == entity->source_pad) { /* The output format can't be modified. */ fmt->format = *format; - goto done; + return 0; } /* @@ -369,9 +366,7 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, selection->width = format->width; selection->height = format->height; -done: - mutex_unlock(&entity->lock); - return ret; + return 0; } static int vsp1_entity_init_state(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index 3f87a2c9df0e..72f6ef2fdc4f 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -196,18 +196,15 @@ static int histo_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - int ret = 0; if (sel->pad != HISTO_PAD_SINK) return -EINVAL; - mutex_lock(&histo->entity.lock); + guard(mutex)(&histo->entity.lock); state = vsp1_entity_get_state(&histo->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; switch (sel->target) { case V4L2_SEL_TGT_COMPOSE_BOUNDS: @@ -237,13 +234,10 @@ static int histo_get_selection(struct v4l2_subdev *subdev, break; default: - ret = -EINVAL; - break; + return -EINVAL; } -done: - mutex_unlock(&histo->entity.lock); - return ret; + return 0; } static int histo_set_crop(struct v4l2_subdev *subdev, @@ -321,29 +315,22 @@ static int histo_set_selection(struct v4l2_subdev *subdev, { struct vsp1_histogram *histo = subdev_to_histo(subdev); struct v4l2_subdev_state *state; - int ret; if (sel->pad != HISTO_PAD_SINK) return -EINVAL; - mutex_lock(&histo->entity.lock); + guard(mutex)(&histo->entity.lock); state = vsp1_entity_get_state(&histo->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; if (sel->target == V4L2_SEL_TGT_CROP) - ret = histo_set_crop(subdev, state, sel); + return histo_set_crop(subdev, state, sel); else if (sel->target == V4L2_SEL_TGT_COMPOSE) - ret = histo_set_compose(subdev, state, sel); + return histo_set_compose(subdev, state, sel); else - ret = -EINVAL; - -done: - mutex_unlock(&histo->entity.lock); - return ret; + return -EINVAL; } static int histo_set_format(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index 830e124beb7b..df069c228243 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -115,15 +115,12 @@ static int hsit_set_format(struct v4l2_subdev *subdev, struct vsp1_hsit *hsit = to_hsit(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&hsit->entity.lock); + guard(mutex)(&hsit->entity.lock); state = vsp1_entity_get_state(&hsit->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; format = v4l2_subdev_state_get_format(state, fmt->pad); @@ -133,7 +130,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, * modified. */ fmt->format = *format; - goto done; + return 0; } format->code = hsit->inverse ? MEDIA_BUS_FMT_AHSV8888_1X32 @@ -161,9 +158,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, vsp1_entity_adjust_color_space(format); -done: - mutex_unlock(&hsit->entity.lock); - return ret; + return 0; } static const struct v4l2_subdev_pad_ops hsit_pad_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index c72518b29f84..ced01870acd6 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -116,15 +116,12 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&rwpf->entity.lock); + guard(mutex)(&rwpf->entity.lock); state = vsp1_entity_get_state(&rwpf->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && @@ -174,7 +171,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, fmt->format = *format; fmt->format.flags = flags; - goto done; + return 0; } format->code = fmt->format.code; @@ -213,9 +210,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, format->height = fmt->format.width; } -done: - mutex_unlock(&rwpf->entity.lock); - return ret; + return 0; } static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, @@ -225,7 +220,6 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; /* * Cropping is only supported on the RPF and is implemented on the sink @@ -234,13 +228,11 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK) return -EINVAL; - mutex_lock(&rwpf->entity.lock); + guard(mutex)(&rwpf->entity.lock); state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; switch (sel->target) { case V4L2_SEL_TGT_CROP: @@ -256,13 +248,10 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, break; default: - ret = -EINVAL; - break; + return -EINVAL; } -done: - mutex_unlock(&rwpf->entity.lock); - return ret; + return 0; } static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, @@ -275,7 +264,6 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - int ret = 0; /* * Cropping is only supported on the RPF and is implemented on the sink @@ -287,13 +275,11 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - mutex_lock(&rwpf->entity.lock); + guard(mutex)(&rwpf->entity.lock); state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; /* Make sure the crop rectangle is entirely contained in the image. */ format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); @@ -342,9 +328,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, format->width = crop->width; format->height = crop->height; -done: - mutex_unlock(&rwpf->entity.lock); - return ret; + return 0; } static const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index 94149da0c900..3fd9fde5c724 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -216,15 +216,12 @@ static int sru_set_format(struct v4l2_subdev *subdev, struct vsp1_sru *sru = to_sru(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&sru->entity.lock); + guard(mutex)(&sru->entity.lock); state = vsp1_entity_get_state(&sru->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; sru_try_format(sru, state, fmt->pad, &fmt->format); @@ -239,9 +236,7 @@ static int sru_set_format(struct v4l2_subdev *subdev, sru_try_format(sru, state, SRU_PAD_SOURCE, format); } -done: - mutex_unlock(&sru->entity.lock); - return ret; + return 0; } static const struct v4l2_subdev_pad_ops sru_pad_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index dd4722315c56..9f7bb112929e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -199,15 +199,12 @@ static int uds_set_format(struct v4l2_subdev *subdev, struct vsp1_uds *uds = to_uds(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; - mutex_lock(&uds->entity.lock); + guard(mutex)(&uds->entity.lock); state = vsp1_entity_get_state(&uds->entity, sd_state, fmt->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; uds_try_format(uds, state, fmt->pad, &fmt->format); @@ -222,9 +219,7 @@ static int uds_set_format(struct v4l2_subdev *subdev, uds_try_format(uds, state, UDS_PAD_SOURCE, format); } -done: - mutex_unlock(&uds->entity.lock); - return ret; + return 0; } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index 3aefe5c9d421..52dbfe58a70d 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -60,18 +60,15 @@ static int uif_get_selection(struct v4l2_subdev *subdev, struct vsp1_uif *uif = to_uif(subdev); struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; - int ret = 0; if (sel->pad != UIF_PAD_SINK) return -EINVAL; - mutex_lock(&uif->entity.lock); + guard(mutex)(&uif->entity.lock); state = vsp1_entity_get_state(&uif->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: @@ -88,13 +85,10 @@ static int uif_get_selection(struct v4l2_subdev *subdev, break; default: - ret = -EINVAL; - break; + return -EINVAL; } -done: - mutex_unlock(&uif->entity.lock); - return ret; + return 0; } static int uif_set_selection(struct v4l2_subdev *subdev, @@ -105,19 +99,16 @@ static int uif_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *format; struct v4l2_rect *selection; - int ret = 0; if (sel->pad != UIF_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - mutex_lock(&uif->entity.lock); + guard(mutex)(&uif->entity.lock); state = vsp1_entity_get_state(&uif->entity, sd_state, sel->which); - if (!state) { - ret = -EINVAL; - goto done; - } + if (!state) + return -EINVAL; /* The crop rectangle must be inside the input frame. */ format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK); @@ -133,9 +124,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, selection = v4l2_subdev_state_get_crop(state, sel->pad); *selection = sel->r; -done: - mutex_unlock(&uif->entity.lock); - return ret; + return 0; } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index fe1dac11d4ae..a9ed6bc87b00 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -595,9 +595,9 @@ static void vsp1_video_pipeline_put(struct vsp1_pipeline *pipe) { struct media_device *mdev = &pipe->output->entity.vsp1->media_dev; - mutex_lock(&mdev->graph_mutex); + guard(mutex)(&mdev->graph_mutex); + kref_put(&pipe->kref, vsp1_video_pipeline_release); - mutex_unlock(&mdev->graph_mutex); } /* ----------------------------------------------------------------------------- @@ -938,9 +938,9 @@ vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format) if (format->type != video->queue.type) return -EINVAL; - mutex_lock(&video->lock); + guard(mutex)(&video->lock); + format->fmt.pix_mp = video->rwpf->format; - mutex_unlock(&video->lock); return 0; } @@ -972,19 +972,15 @@ vsp1_video_set_format(struct file *file, void *fh, struct v4l2_format *format) if (ret < 0) return ret; - mutex_lock(&video->lock); + guard(mutex)(&video->lock); - if (vb2_is_busy(&video->queue)) { - ret = -EBUSY; - goto done; - } + if (vb2_is_busy(&video->queue)) + return -EBUSY; video->rwpf->format = format->fmt.pix_mp; video->rwpf->fmtinfo = info; -done: - mutex_unlock(&video->lock); - return ret; + return 0; } static int diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index cd6c5592221b..e7ed3c8e9e90 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -47,7 +47,6 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) struct v4l2_mbus_framefmt *sink_format; struct v4l2_mbus_framefmt *source_format; bool rotate; - int ret = 0; /* * Only consider the 0°/180° from/to 90°/270° modifications, the rest @@ -58,19 +57,17 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) return 0; /* Changing rotation isn't allowed when buffers are allocated. */ - mutex_lock(&video->lock); + guard(mutex)(&video->lock); - if (vb2_is_busy(&video->queue)) { - ret = -EBUSY; - goto done; - } + if (vb2_is_busy(&video->queue)) + return -EBUSY; sink_format = v4l2_subdev_state_get_format(wpf->entity.state, RWPF_PAD_SINK); source_format = v4l2_subdev_state_get_format(wpf->entity.state, RWPF_PAD_SOURCE); - mutex_lock(&wpf->entity.lock); + guard(mutex)(&wpf->entity.lock); if (rotate) { source_format->width = sink_format->height; @@ -82,11 +79,7 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) wpf->flip.rotate = rotate; - mutex_unlock(&wpf->entity.lock); - -done: - mutex_unlock(&video->lock); - return ret; + return 0; } static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl) From 2bf10aeb232e91d25799885343f2e282bf9659a9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:30 +0300 Subject: [PATCH 302/394] media: renesas: vsp1: Use mutex scoped guards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace remaining manual mutex locking and unlocking with scoped guards. This simplifies error paths and reduces the amount of code. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-7-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/vsp1/vsp1_brx.c | 7 +- .../media/platform/renesas/vsp1/vsp1_drm.c | 117 ++++++++---------- .../media/platform/renesas/vsp1/vsp1_entity.c | 8 +- .../media/platform/renesas/vsp1/vsp1_hgo.c | 10 +- .../media/platform/renesas/vsp1/vsp1_hgt.c | 16 +-- .../media/platform/renesas/vsp1/vsp1_video.c | 60 ++++----- 6 files changed, 102 insertions(+), 116 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index ce68d911ac30..d150a92b2617 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -196,9 +196,10 @@ static int brx_get_selection(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - mutex_lock(&brx->entity.lock); - sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); - mutex_unlock(&brx->entity.lock); + scoped_guard(mutex, &brx->entity.lock) { + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); + } + return 0; default: diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 1439cf7bfb59..2b64d9b5a81c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -675,43 +675,37 @@ int vsp1_du_enable(struct device *dev, unsigned int pipe_index, __func__, pipe_index, cfg->width, cfg->height, pipe->interlaced ? "i" : ""); - mutex_lock(&vsp1->drm->lock); + scoped_guard(mutex, &vsp1->drm->lock) { + /* Setup formats through the pipeline. */ + ret = vsp1_du_pipeline_setup_inputs(vsp1, pipe); + if (ret < 0) + return ret; - /* Setup formats through the pipeline. */ - ret = vsp1_du_pipeline_setup_inputs(vsp1, pipe); - if (ret < 0) - goto unlock; + ret = vsp1_du_pipeline_setup_output(vsp1, pipe); + if (ret < 0) + return ret; - ret = vsp1_du_pipeline_setup_output(vsp1, pipe); - if (ret < 0) - goto unlock; + vsp1_pipeline_dump(pipe, "DU enable"); - vsp1_pipeline_dump(pipe, "DU enable"); + /* Enable the VSP1. */ + ret = vsp1_device_get(vsp1); + if (ret < 0) + return ret; - /* Enable the VSP1. */ - ret = vsp1_device_get(vsp1); - if (ret < 0) - goto unlock; + /* + * Register a callback to allow us to notify the DRM driver of frame + * completion events. + */ + drm_pipe->du_complete = cfg->callback; + drm_pipe->du_private = cfg->callback_data; - /* - * Register a callback to allow us to notify the DRM driver of frame - * completion events. - */ - drm_pipe->du_complete = cfg->callback; - drm_pipe->du_private = cfg->callback_data; + /* Disable the display interrupts. */ + vsp1_write(vsp1, VI6_DISP_IRQ_STA(pipe_index), 0); + vsp1_write(vsp1, VI6_DISP_IRQ_ENB(pipe_index), 0); - /* Disable the display interrupts. */ - vsp1_write(vsp1, VI6_DISP_IRQ_STA(pipe_index), 0); - vsp1_write(vsp1, VI6_DISP_IRQ_ENB(pipe_index), 0); - - /* Configure all entities in the pipeline. */ - vsp1_du_pipeline_configure(pipe); - -unlock: - mutex_unlock(&vsp1->drm->lock); - - if (ret < 0) - return ret; + /* Configure all entities in the pipeline. */ + vsp1_du_pipeline_configure(pipe); + } /* Start the pipeline. */ spin_lock_irqsave(&pipe->irqlock, flags); @@ -739,7 +733,6 @@ int vsp1_du_disable(struct device *dev, unsigned int pipe_index) struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe; struct vsp1_pipeline *pipe; - struct vsp1_brx *brx; unsigned int i; int ret; @@ -749,45 +742,43 @@ int vsp1_du_disable(struct device *dev, unsigned int pipe_index) drm_pipe = &vsp1->drm->pipe[pipe_index]; pipe = &drm_pipe->pipe; - mutex_lock(&vsp1->drm->lock); + scoped_guard(mutex, &vsp1->drm->lock) { + struct vsp1_brx *brx = to_brx(&pipe->brx->subdev); - brx = to_brx(&pipe->brx->subdev); + ret = vsp1_pipeline_stop(pipe); + if (ret == -ETIMEDOUT) + dev_err(vsp1->dev, "DRM pipeline stop timeout\n"); - ret = vsp1_pipeline_stop(pipe); - if (ret == -ETIMEDOUT) - dev_err(vsp1->dev, "DRM pipeline stop timeout\n"); + for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { + struct vsp1_rwpf *rpf = pipe->inputs[i]; - for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { - struct vsp1_rwpf *rpf = pipe->inputs[i]; + if (!rpf) + continue; - if (!rpf) - continue; + /* + * Remove the RPF from the pipe and the list of BRx + * inputs. + */ + WARN_ON(!rpf->entity.pipe); + rpf->entity.pipe = NULL; + list_del(&rpf->entity.list_pipe); + pipe->inputs[i] = NULL; - /* - * Remove the RPF from the pipe and the list of BRx - * inputs. - */ - WARN_ON(!rpf->entity.pipe); - rpf->entity.pipe = NULL; - list_del(&rpf->entity.list_pipe); - pipe->inputs[i] = NULL; + brx->inputs[rpf->brx_input].rpf = NULL; + } - brx->inputs[rpf->brx_input].rpf = NULL; + drm_pipe->du_complete = NULL; + pipe->num_inputs = 0; + + dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n", + __func__, pipe->lif->index, + BRX_NAME(pipe->brx)); + + list_del(&pipe->brx->list_pipe); + pipe->brx->pipe = NULL; + pipe->brx = NULL; } - drm_pipe->du_complete = NULL; - pipe->num_inputs = 0; - - dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n", - __func__, pipe->lif->index, - BRX_NAME(pipe->brx)); - - list_del(&pipe->brx->list_pipe); - pipe->brx->pipe = NULL; - pipe->brx = NULL; - - mutex_unlock(&vsp1->drm->lock); - vsp1_dlm_reset(pipe->output->dlm); vsp1_device_put(vsp1); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 1a1dafe5331e..50aae8ee724f 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -216,10 +216,10 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - mutex_lock(&entity->lock); - format = v4l2_subdev_state_get_format(state, 0); - code->code = format->code; - mutex_unlock(&entity->lock); + scoped_guard(mutex, &entity->lock) { + format = v4l2_subdev_state_get_format(state, 0); + code->code = format->code; + } } return 0; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c index 2c8ce7175a4e..0ef512e3a94b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c @@ -153,11 +153,11 @@ static void hgo_configure_stream(struct vsp1_entity *entity, (crop->width << VI6_HGO_SIZE_HSIZE_SHIFT) | (crop->height << VI6_HGO_SIZE_VSIZE_SHIFT)); - mutex_lock(hgo->ctrls.handler.lock); - hgo->max_rgb = hgo->ctrls.max_rgb->cur.val; - if (hgo->ctrls.num_bins) - hgo->num_bins = hgo_num_bins[hgo->ctrls.num_bins->cur.val]; - mutex_unlock(hgo->ctrls.handler.lock); + scoped_guard(mutex, hgo->ctrls.handler.lock) { + hgo->max_rgb = hgo->ctrls.max_rgb->cur.val; + if (hgo->ctrls.num_bins) + hgo->num_bins = hgo_num_bins[hgo->ctrls.num_bins->cur.val]; + } hratio = crop->width * 2 / compose->width / 3; vratio = crop->height * 2 / compose->height / 3; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c index 858f330d44fa..78b5a9201c70 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c @@ -152,15 +152,15 @@ static void hgt_configure_stream(struct vsp1_entity *entity, (crop->width << VI6_HGT_SIZE_HSIZE_SHIFT) | (crop->height << VI6_HGT_SIZE_VSIZE_SHIFT)); - mutex_lock(hgt->ctrls.lock); - for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) { - lower = hgt->hue_areas[i*2 + 0]; - upper = hgt->hue_areas[i*2 + 1]; - vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i), - (lower << VI6_HGT_HUE_AREA_LOWER_SHIFT) | - (upper << VI6_HGT_HUE_AREA_UPPER_SHIFT)); + scoped_guard(mutex, hgt->ctrls.lock) { + for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) { + lower = hgt->hue_areas[i*2 + 0]; + upper = hgt->hue_areas[i*2 + 1]; + vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i), + (lower << VI6_HGT_HUE_AREA_LOWER_SHIFT) | + (upper << VI6_HGT_HUE_AREA_UPPER_SHIFT)); + } } - mutex_unlock(hgt->ctrls.lock); hratio = crop->width * 2 / compose->width / 3; vratio = crop->height * 2 / compose->height / 3; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index a9ed6bc87b00..b110f0309b26 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -815,22 +815,21 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) unsigned long flags; int ret; - mutex_lock(&pipe->lock); - if (pipe->stream_count == pipe->num_inputs) { - ret = vsp1_video_setup_pipeline(pipe); - if (ret < 0) { - vsp1_video_release_buffers(video); - vsp1_video_cleanup_pipeline(pipe); - mutex_unlock(&pipe->lock); - return ret; + scoped_guard(mutex, &pipe->lock) { + if (pipe->stream_count == pipe->num_inputs) { + ret = vsp1_video_setup_pipeline(pipe); + if (ret < 0) { + vsp1_video_release_buffers(video); + vsp1_video_cleanup_pipeline(pipe); + return ret; + } + + start_pipeline = true; } - start_pipeline = true; + pipe->stream_count++; } - pipe->stream_count++; - mutex_unlock(&pipe->lock); - /* * vsp1_pipeline_ready() is not sufficient to establish that all streams * are prepared and the pipeline is configured, as multiple streams @@ -864,16 +863,17 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) pipe->buffers_ready &= ~(1 << video->pipe_index); spin_unlock_irqrestore(&video->irqlock, flags); - mutex_lock(&pipe->lock); - if (--pipe->stream_count == pipe->num_inputs) { - /* Stop the pipeline. */ - ret = vsp1_pipeline_stop(pipe); - if (ret == -ETIMEDOUT) - dev_err(video->vsp1->dev, "pipeline stop timeout\n"); + scoped_guard(mutex, &pipe->lock) { + if (--pipe->stream_count == pipe->num_inputs) { + /* Stop the pipeline. */ + ret = vsp1_pipeline_stop(pipe); + if (ret == -ETIMEDOUT) + dev_err(video->vsp1->dev, + "pipeline stop timeout\n"); - vsp1_video_cleanup_pipeline(pipe); + vsp1_video_cleanup_pipeline(pipe); + } } - mutex_unlock(&pipe->lock); video_device_pipeline_stop(&video->video); vsp1_video_release_buffers(video); @@ -1000,22 +1000,16 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) * touching an entity in the pipeline can be activated or deactivated * once streaming is started. */ - mutex_lock(&mdev->graph_mutex); + scoped_guard(mutex, &mdev->graph_mutex) { + pipe = vsp1_video_pipeline_get(video); + if (IS_ERR(pipe)) + return PTR_ERR(pipe); - pipe = vsp1_video_pipeline_get(video); - if (IS_ERR(pipe)) { - mutex_unlock(&mdev->graph_mutex); - return PTR_ERR(pipe); + ret = __video_device_pipeline_start(&video->video, &pipe->pipe); + if (ret < 0) + goto err_pipe; } - ret = __video_device_pipeline_start(&video->video, &pipe->pipe); - if (ret < 0) { - mutex_unlock(&mdev->graph_mutex); - goto err_pipe; - } - - mutex_unlock(&mdev->graph_mutex); - /* * Verify that the configured format matches the output of the connected * subdev. From 84928d3f32be0f6dc90822823e1a185956354386 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:31 +0300 Subject: [PATCH 303/394] media: renesas: vsp1: Use spinlock guards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace manual spinlock locking and unlocking with guards. This simplifies error paths and reduces the amount of code. Limit the changes to locations where the guard covers until the end of the function to ease review. Scoped guards will be introduced separately. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-8-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_dl.c | 49 ++++++------------- .../media/platform/renesas/vsp1/vsp1_histo.c | 20 +++----- .../media/platform/renesas/vsp1/vsp1_pipe.c | 9 +--- .../media/platform/renesas/vsp1/vsp1_video.c | 14 ++---- .../media/platform/renesas/vsp1/vsp1_wpf.c | 4 +- 5 files changed, 31 insertions(+), 65 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c index 6c5578d9d2de..4a19ff1437b0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c @@ -336,9 +336,8 @@ void vsp1_dl_body_pool_destroy(struct vsp1_dl_body_pool *pool) struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool) { struct vsp1_dl_body *dlb = NULL; - unsigned long flags; - spin_lock_irqsave(&pool->lock, flags); + guard(spinlock_irqsave)(&pool->lock); if (!list_empty(&pool->free)) { dlb = list_first_entry(&pool->free, struct vsp1_dl_body, free); @@ -346,8 +345,6 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool) refcount_set(&dlb->refcnt, 1); } - spin_unlock_irqrestore(&pool->lock, flags); - return dlb; } @@ -359,8 +356,6 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool) */ void vsp1_dl_body_put(struct vsp1_dl_body *dlb) { - unsigned long flags; - if (!dlb) return; @@ -369,9 +364,9 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb) dlb->num_entries = 0; - spin_lock_irqsave(&dlb->pool->lock, flags); + guard(spinlock_irqsave)(&dlb->pool->lock); + list_add_tail(&dlb->free, &dlb->pool->free); - spin_unlock_irqrestore(&dlb->pool->lock, flags); } /** @@ -493,9 +488,8 @@ static struct vsp1_dl_ext_cmd *vsp1_dl_ext_cmd_get(struct vsp1_dl_cmd_pool *pool) { struct vsp1_dl_ext_cmd *cmd = NULL; - unsigned long flags; - spin_lock_irqsave(&pool->lock, flags); + guard(spinlock_irqsave)(&pool->lock); if (!list_empty(&pool->free)) { cmd = list_first_entry(&pool->free, struct vsp1_dl_ext_cmd, @@ -503,24 +497,20 @@ struct vsp1_dl_ext_cmd *vsp1_dl_ext_cmd_get(struct vsp1_dl_cmd_pool *pool) list_del(&cmd->free); } - spin_unlock_irqrestore(&pool->lock, flags); - return cmd; } static void vsp1_dl_ext_cmd_put(struct vsp1_dl_ext_cmd *cmd) { - unsigned long flags; - if (!cmd) return; /* Reset flags, these mark data usage. */ cmd->flags = 0; - spin_lock_irqsave(&cmd->pool->lock, flags); + guard(spinlock_irqsave)(&cmd->pool->lock); + list_add_tail(&cmd->free, &cmd->pool->free); - spin_unlock_irqrestore(&cmd->pool->lock, flags); } static void vsp1_dl_ext_cmd_pool_destroy(struct vsp1_dl_cmd_pool *pool) @@ -611,11 +601,10 @@ static void vsp1_dl_list_free(struct vsp1_dl_list *dl) struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) { struct vsp1_dl_list *dl = NULL; - unsigned long flags; lockdep_assert_not_held(&dlm->lock); - spin_lock_irqsave(&dlm->lock, flags); + guard(spinlock_irqsave)(&dlm->lock); if (!list_empty(&dlm->free)) { dl = list_first_entry(&dlm->free, struct vsp1_dl_list, list); @@ -629,8 +618,6 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) dl->allocated = true; } - spin_unlock_irqrestore(&dlm->lock, flags); - return dl; } @@ -690,14 +677,12 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) */ void vsp1_dl_list_put(struct vsp1_dl_list *dl) { - unsigned long flags; - if (!dl) return; - spin_lock_irqsave(&dl->dlm->lock, flags); + guard(spinlock_irqsave)(&dl->dlm->lock); + __vsp1_dl_list_put(dl); - spin_unlock_irqrestore(&dl->dlm->lock, flags); } /** @@ -937,7 +922,6 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags) { struct vsp1_dl_manager *dlm = dl->dlm; struct vsp1_dl_list *dl_next; - unsigned long flags; /* Fill the header for the head and chained display lists. */ vsp1_dl_list_fill_header(dl, list_empty(&dl->chain)); @@ -950,14 +934,12 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags) dl->flags = dl_flags & ~VSP1_DL_FRAME_END_COMPLETED; - spin_lock_irqsave(&dlm->lock, flags); + guard(spinlock_irqsave)(&dlm->lock); if (dlm->singleshot) vsp1_dl_list_commit_singleshot(dl); else vsp1_dl_list_commit_continuous(dl); - - spin_unlock_irqrestore(&dlm->lock, flags); } /* ----------------------------------------------------------------------------- @@ -991,7 +973,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) u32 status = vsp1_read(vsp1, VI6_STATUS); unsigned int flags = 0; - spin_lock(&dlm->lock); + guard(spinlock)(&dlm->lock); /* * The mem-to-mem pipelines work in single-shot mode. No new display @@ -1001,7 +983,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) __vsp1_dl_list_put(dlm->active); dlm->active = NULL; flags |= VSP1_DL_FRAME_END_COMPLETED; - goto done; + return flags; } /* @@ -1011,7 +993,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) * and retry. */ if (vsp1_dl_list_hw_update_pending(dlm)) - goto done; + return flags; /* * Progressive streams report only TOP fields. If we have a BOTTOM @@ -1019,7 +1001,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) * next frame end interrupt. */ if (status & VI6_STATUS_FLD_STD(dlm->index)) - goto done; + return flags; /* * If the active display list has the writeback flag set, the frame @@ -1058,9 +1040,6 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) dlm->pending = NULL; } -done: - spin_unlock(&dlm->lock); - return flags; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index 72f6ef2fdc4f..97dbfb93abe9 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -35,20 +35,18 @@ to_vsp1_histogram_buffer(struct vb2_v4l2_buffer *vbuf) struct vsp1_histogram_buffer * vsp1_histogram_buffer_get(struct vsp1_histogram *histo) { - struct vsp1_histogram_buffer *buf = NULL; + struct vsp1_histogram_buffer *buf; - spin_lock(&histo->irqlock); + guard(spinlock)(&histo->irqlock); if (list_empty(&histo->irqqueue)) - goto done; + return NULL; buf = list_first_entry(&histo->irqqueue, struct vsp1_histogram_buffer, queue); list_del(&buf->queue); histo->readout = true; -done: - spin_unlock(&histo->irqlock); return buf; } @@ -68,10 +66,10 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, vb2_set_plane_payload(&buf->buf.vb2_buf, 0, size); vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); - spin_lock(&histo->irqlock); + guard(spinlock)(&histo->irqlock); + histo->readout = false; wake_up(&histo->wait_queue); - spin_unlock(&histo->irqlock); } /* ----------------------------------------------------------------------------- @@ -123,9 +121,9 @@ static void histo_buffer_queue(struct vb2_buffer *vb) struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue); struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf); - spin_lock_irq(&histo->irqlock); + guard(spinlock_irq)(&histo->irqlock); + list_add_tail(&buf->queue, &histo->irqqueue); - spin_unlock_irq(&histo->irqlock); } static int histo_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -138,7 +136,7 @@ static void histo_stop_streaming(struct vb2_queue *vq) struct vsp1_histogram *histo = vb2_get_drv_priv(vq); struct vsp1_histogram_buffer *buffer; - spin_lock_irq(&histo->irqlock); + guard(spinlock_irq)(&histo->irqlock); /* Remove all buffers from the IRQ queue. */ list_for_each_entry(buffer, &histo->irqqueue, queue) @@ -147,8 +145,6 @@ static void histo_stop_streaming(struct vb2_queue *vq) /* Wait for the buffer being read out (if any) to complete. */ wait_event_lock_irq(histo->wait_queue, !histo->readout, histo->irqlock); - - spin_unlock_irq(&histo->irqlock); } static const struct vb2_ops histo_video_queue_qops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 5d769cc42fe1..9e2b9b15331a 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -487,14 +487,9 @@ void vsp1_pipeline_run(struct vsp1_pipeline *pipe) bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe) { - unsigned long flags; - bool stopped; + guard(spinlock_irqsave)(&pipe->irqlock); - spin_lock_irqsave(&pipe->irqlock, flags); - stopped = pipe->state == VSP1_PIPELINE_STOPPED; - spin_unlock_irqrestore(&pipe->irqlock, flags); - - return stopped; + return pipe->state == VSP1_PIPELINE_STOPPED; } int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index b110f0309b26..b5b82e12fcb1 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -672,7 +672,7 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) if (!empty) return; - spin_lock_irqsave(&pipe->irqlock, flags); + guard(spinlock_irqsave)(&pipe->irqlock); video->rwpf->mem = buf->mem; pipe->buffers_ready |= 1 << video->pipe_index; @@ -680,8 +680,6 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) if (vb2_start_streaming_called(&video->queue) && vsp1_pipeline_ready(pipe)) vsp1_video_pipeline_run(pipe); - - spin_unlock_irqrestore(&pipe->irqlock, flags); } static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) @@ -783,14 +781,13 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) static void vsp1_video_release_buffers(struct vsp1_video *video) { struct vsp1_vb2_buffer *buffer; - unsigned long flags; /* Remove all buffers from the IRQ queue. */ - spin_lock_irqsave(&video->irqlock, flags); + guard(spinlock_irqsave)(&video->irqlock); + list_for_each_entry(buffer, &video->irqqueue, queue) vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR); INIT_LIST_HEAD(&video->irqqueue); - spin_unlock_irqrestore(&video->irqlock, flags); } static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe) @@ -812,7 +809,6 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) struct vsp1_video *video = vb2_get_drv_priv(vq); struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; bool start_pipeline = false; - unsigned long flags; int ret; scoped_guard(mutex, &pipe->lock) { @@ -840,10 +836,10 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) if (!start_pipeline) return 0; - spin_lock_irqsave(&pipe->irqlock, flags); + guard(spinlock_irqsave)(&pipe->irqlock); + if (vsp1_pipeline_ready(pipe)) vsp1_video_pipeline_run(pipe); - spin_unlock_irqrestore(&pipe->irqlock, flags); return 0; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index e7ed3c8e9e90..327c7457126f 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -111,9 +111,9 @@ static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl) if (rotation == 180 || rotation == 270) flip ^= BIT(WPF_CTRL_VFLIP) | BIT(WPF_CTRL_HFLIP); - spin_lock_irq(&wpf->flip.lock); + guard(spinlock_irq)(&wpf->flip.lock); + wpf->flip.pending = flip; - spin_unlock_irq(&wpf->flip.lock); return 0; } From de52d7a161290e64947c795195a231aa66a73c16 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:32 +0300 Subject: [PATCH 304/394] media: renesas: vsp1: Use spinlock scoped guards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace remaining manual spinlock locking and unlocking with scoped guards. This simplifies error paths and reduces the amount of code. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-9-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/vsp1/vsp1_clu.c | 15 +++-- drivers/media/platform/renesas/vsp1/vsp1_dl.c | 14 ++--- .../media/platform/renesas/vsp1/vsp1_drm.c | 7 +-- .../media/platform/renesas/vsp1/vsp1_lut.c | 15 +++-- .../media/platform/renesas/vsp1/vsp1_pipe.c | 15 +++-- .../media/platform/renesas/vsp1/vsp1_video.c | 59 ++++++++----------- .../media/platform/renesas/vsp1/vsp1_wpf.c | 9 ++- 7 files changed, 59 insertions(+), 75 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index 04c466c4da81..a6e4bcab5101 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -53,9 +53,9 @@ static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl) for (i = 0; i < CLU_SIZE; ++i) vsp1_dl_body_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]); - spin_lock_irq(&clu->lock); - swap(clu->clu, dlb); - spin_unlock_irq(&clu->lock); + scoped_guard(spinlock_irq, &clu->lock) { + swap(clu->clu, dlb); + } vsp1_dl_body_put(dlb); return 0; @@ -162,7 +162,6 @@ static void clu_configure_frame(struct vsp1_entity *entity, { struct vsp1_clu *clu = to_clu(&entity->subdev); struct vsp1_dl_body *clu_dlb; - unsigned long flags; u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN; /* 2D mode can only be used with the YCbCr pixel encoding. */ @@ -173,10 +172,10 @@ static void clu_configure_frame(struct vsp1_entity *entity, vsp1_clu_write(clu, dlb, VI6_CLU_CTRL, ctrl); - spin_lock_irqsave(&clu->lock, flags); - clu_dlb = clu->clu; - clu->clu = NULL; - spin_unlock_irqrestore(&clu->lock, flags); + scoped_guard(spinlock_irqsave, &clu->lock) { + clu_dlb = clu->clu; + clu->clu = NULL; + } if (clu_dlb) { vsp1_dl_list_add_body(dl, clu_dlb); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c index 4a19ff1437b0..3dc74fed91dc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c @@ -1064,17 +1064,15 @@ void vsp1_dlm_setup(struct vsp1_device *vsp1) void vsp1_dlm_reset(struct vsp1_dl_manager *dlm) { - unsigned long flags; size_t list_count; - spin_lock_irqsave(&dlm->lock, flags); + scoped_guard(spinlock_irqsave, &dlm->lock) { + __vsp1_dl_list_put(dlm->active); + __vsp1_dl_list_put(dlm->queued); + __vsp1_dl_list_put(dlm->pending); - __vsp1_dl_list_put(dlm->active); - __vsp1_dl_list_put(dlm->queued); - __vsp1_dl_list_put(dlm->pending); - - list_count = list_count_nodes(&dlm->free); - spin_unlock_irqrestore(&dlm->lock, flags); + list_count = list_count_nodes(&dlm->free); + } WARN_ON_ONCE(list_count != dlm->list_count); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 2b64d9b5a81c..f6fbd3475329 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -655,7 +655,6 @@ int vsp1_du_enable(struct device *dev, unsigned int pipe_index, struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe; struct vsp1_pipeline *pipe; - unsigned long flags; int ret; if (pipe_index >= vsp1->info->lif_count) @@ -708,9 +707,9 @@ int vsp1_du_enable(struct device *dev, unsigned int pipe_index, } /* Start the pipeline. */ - spin_lock_irqsave(&pipe->irqlock, flags); - vsp1_pipeline_run(pipe); - spin_unlock_irqrestore(&pipe->irqlock, flags); + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + vsp1_pipeline_run(pipe); + } dev_dbg(vsp1->dev, "%s: pipeline enabled\n", __func__); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index 94bdedcc5c92..a22c31e17cb7 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c @@ -50,9 +50,9 @@ static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl) vsp1_dl_body_write(dlb, VI6_LUT_TABLE + 4 * i, ctrl->p_new.p_u32[i]); - spin_lock_irq(&lut->lock); - swap(lut->lut, dlb); - spin_unlock_irq(&lut->lock); + scoped_guard(spinlock_irq, &lut->lock) { + swap(lut->lut, dlb); + } vsp1_dl_body_put(dlb); return 0; @@ -132,12 +132,11 @@ static void lut_configure_frame(struct vsp1_entity *entity, { struct vsp1_lut *lut = to_lut(&entity->subdev); struct vsp1_dl_body *lut_dlb; - unsigned long flags; - spin_lock_irqsave(&lut->lock, flags); - lut_dlb = lut->lut; - lut->lut = NULL; - spin_unlock_irqrestore(&lut->lock, flags); + scoped_guard(spinlock_irqsave, &lut->lock) { + lut_dlb = lut->lut; + lut->lut = NULL; + } if (lut_dlb) { vsp1_dl_list_add_body(dl, lut_dlb); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 9e2b9b15331a..12079fbdc3d3 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -496,7 +496,6 @@ int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; struct vsp1_entity *entity; - unsigned long flags; int ret; if (pipe->lif) { @@ -506,16 +505,16 @@ int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) */ ret = vsp1_reset_wpf(vsp1, pipe->output->entity.index); if (ret == 0) { - spin_lock_irqsave(&pipe->irqlock, flags); - pipe->state = VSP1_PIPELINE_STOPPED; - spin_unlock_irqrestore(&pipe->irqlock, flags); + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + pipe->state = VSP1_PIPELINE_STOPPED; + } } } else { /* Otherwise just request a stop and wait. */ - spin_lock_irqsave(&pipe->irqlock, flags); - if (pipe->state == VSP1_PIPELINE_RUNNING) - pipe->state = VSP1_PIPELINE_STOPPING; - spin_unlock_irqrestore(&pipe->irqlock, flags); + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; + } ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), msecs_to_jiffies(500)); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index b5b82e12fcb1..5a1d284213ad 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -209,26 +209,21 @@ vsp1_video_complete_buffer(struct vsp1_video *video) struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; struct vsp1_vb2_buffer *next = NULL; struct vsp1_vb2_buffer *done; - unsigned long flags; unsigned int i; - spin_lock_irqsave(&video->irqlock, flags); + scoped_guard(spinlock_irqsave, &video->irqlock) { + if (list_empty(&video->irqqueue)) + return NULL; - if (list_empty(&video->irqqueue)) { - spin_unlock_irqrestore(&video->irqlock, flags); - return NULL; - } - - done = list_first_entry(&video->irqqueue, - struct vsp1_vb2_buffer, queue); - - list_del(&done->queue); - - if (!list_empty(&video->irqqueue)) - next = list_first_entry(&video->irqqueue, + done = list_first_entry(&video->irqqueue, struct vsp1_vb2_buffer, queue); - spin_unlock_irqrestore(&video->irqlock, flags); + list_del(&done->queue); + + if (!list_empty(&video->irqqueue)) + next = list_first_entry(&video->irqqueue, + struct vsp1_vb2_buffer, queue); + } done->buf.sequence = pipe->sequence; done->buf.vb2_buf.timestamp = ktime_get_ns(); @@ -661,13 +656,12 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf); - unsigned long flags; bool empty; - spin_lock_irqsave(&video->irqlock, flags); - empty = list_empty(&video->irqqueue); - list_add_tail(&buf->queue, &video->irqqueue); - spin_unlock_irqrestore(&video->irqlock, flags); + scoped_guard(spinlock_irqsave, &video->irqlock) { + empty = list_empty(&video->irqqueue); + list_add_tail(&buf->queue, &video->irqqueue); + } if (!empty) return; @@ -848,16 +842,15 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) { struct vsp1_video *video = vb2_get_drv_priv(vq); struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; - unsigned long flags; int ret; /* * Clear the buffers ready flag to make sure the device won't be started * by a QBUF on the video node on the other side of the pipeline. */ - spin_lock_irqsave(&video->irqlock, flags); - pipe->buffers_ready &= ~(1 << video->pipe_index); - spin_unlock_irqrestore(&video->irqlock, flags); + scoped_guard(spinlock_irqsave, &video->irqlock) { + pipe->buffers_ready &= ~(1 << video->pipe_index); + } scoped_guard(mutex, &pipe->lock) { if (--pipe->stream_count == pipe->num_inputs) { @@ -1123,7 +1116,6 @@ static const struct media_entity_operations vsp1_video_media_ops = { void vsp1_video_suspend(struct vsp1_device *vsp1) { - unsigned long flags; unsigned int i; int ret; @@ -1143,10 +1135,10 @@ void vsp1_video_suspend(struct vsp1_device *vsp1) if (pipe == NULL) continue; - spin_lock_irqsave(&pipe->irqlock, flags); - if (pipe->state == VSP1_PIPELINE_RUNNING) - pipe->state = VSP1_PIPELINE_STOPPING; - spin_unlock_irqrestore(&pipe->irqlock, flags); + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; + } } for (i = 0; i < vsp1->info->wpf_count; ++i) { @@ -1170,7 +1162,6 @@ void vsp1_video_suspend(struct vsp1_device *vsp1) void vsp1_video_resume(struct vsp1_device *vsp1) { - unsigned long flags; unsigned int i; /* Resume all running pipelines. */ @@ -1191,10 +1182,10 @@ void vsp1_video_resume(struct vsp1_device *vsp1) */ pipe->configured = false; - spin_lock_irqsave(&pipe->irqlock, flags); - if (vsp1_pipeline_ready(pipe)) - vsp1_video_pipeline_run(pipe); - spin_unlock_irqrestore(&pipe->irqlock, flags); + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + if (vsp1_pipeline_ready(pipe)) + vsp1_video_pipeline_run(pipe); + } } } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index 327c7457126f..0ec707d2913f 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -366,13 +366,12 @@ static void wpf_configure_frame(struct vsp1_entity *entity, const unsigned int mask = BIT(WPF_CTRL_VFLIP) | BIT(WPF_CTRL_HFLIP); struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); - unsigned long flags; u32 outfmt; - spin_lock_irqsave(&wpf->flip.lock, flags); - wpf->flip.active = (wpf->flip.active & ~mask) - | (wpf->flip.pending & mask); - spin_unlock_irqrestore(&wpf->flip.lock, flags); + scoped_guard(spinlock_irqsave, &wpf->flip.lock) { + wpf->flip.active = (wpf->flip.active & ~mask) + | (wpf->flip.pending & mask); + } outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt; From 20b317d4862cce1b92195d5915b3f3a139b28b8c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:33 +0300 Subject: [PATCH 305/394] media: renesas: vsp1: Simplify iteration over format arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a vsp1_for_each_format() macro to iterate over format arrays, to improve readability. No functional change intended. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-10-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/vsp1/vsp1_pipe.c | 36 ++++++------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 12079fbdc3d3..32bb02ce0366 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -229,6 +229,10 @@ static const struct vsp1_format_info vsp1_video_hsit_formats[] = { 1, { 32, 0, 0 }, false, false, 1, 1, false }, }; +#define vsp1_for_each_format(info, formats) \ + for (const struct vsp1_format_info *info = &formats[0]; \ + info < formats + ARRAY_SIZE(formats); ++info) + /** * vsp1_get_format_info - Retrieve format information for a 4CC * @vsp1: the VSP1 device @@ -240,30 +244,20 @@ static const struct vsp1_format_info vsp1_video_hsit_formats[] = { const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, u32 fourcc) { - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { - const struct vsp1_format_info *info = &vsp1_video_formats[i]; - + vsp1_for_each_format(info, vsp1_video_formats) { if (info->fourcc == fourcc) return info; } if (vsp1->info->gen == 2) { - for (i = 0; i < ARRAY_SIZE(vsp1_video_gen2_formats); ++i) { - const struct vsp1_format_info *info = - &vsp1_video_gen2_formats[i]; - + vsp1_for_each_format(info, vsp1_video_gen2_formats) { if (info->fourcc == fourcc) return info; } } if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) { - for (i = 0; i < ARRAY_SIZE(vsp1_video_hsit_formats); ++i) { - const struct vsp1_format_info *info = - &vsp1_video_hsit_formats[i]; - + vsp1_for_each_format(info, vsp1_video_hsit_formats) { if (info->fourcc == fourcc) return info; } @@ -287,8 +281,6 @@ const struct vsp1_format_info * vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, u32 code) { - unsigned int i; - if (!code) { if (index < ARRAY_SIZE(vsp1_video_formats)) return &vsp1_video_formats[index]; @@ -308,9 +300,7 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, return NULL; } - for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { - const struct vsp1_format_info *info = &vsp1_video_formats[i]; - + vsp1_for_each_format(info, vsp1_video_formats) { if (info->mbus == code) { if (!index) return info; @@ -319,10 +309,7 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, } if (vsp1->info->gen == 2) { - for (i = 0; i < ARRAY_SIZE(vsp1_video_gen2_formats); ++i) { - const struct vsp1_format_info *info = - &vsp1_video_gen2_formats[i]; - + vsp1_for_each_format(info, vsp1_video_gen2_formats) { if (info->mbus == code) { if (!index) return info; @@ -332,10 +319,7 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, } if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) { - for (i = 0; i < ARRAY_SIZE(vsp1_video_hsit_formats); ++i) { - const struct vsp1_format_info *info = - &vsp1_video_hsit_formats[i]; - + vsp1_for_each_format(info, vsp1_video_hsit_formats) { if (info->mbus == code) { if (!index) return info; From bb16e2895a4161aa90a884bc2e9ef3f229f40e46 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:35 +0300 Subject: [PATCH 306/394] media: renesas: vsp1: Drop deprecated vsp1_du_setup_lif() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vsp1_du_setup_lif() is deprecated and its last users are gone. Drop it. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-12-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- include/media/vsp1.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/include/media/vsp1.h b/include/media/vsp1.h index d2085cdb7fcb..98089e0a4385 100644 --- a/include/media/vsp1.h +++ b/include/media/vsp1.h @@ -48,15 +48,6 @@ int vsp1_du_enable(struct device *dev, unsigned int pipe_index, const struct vsp1_du_lif_config *cfg); int vsp1_du_disable(struct device *dev, unsigned int pipe_index); -static inline int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, - const struct vsp1_du_lif_config *cfg) -{ - if (cfg) - return vsp1_du_enable(dev, pipe_index, cfg); - else - return vsp1_du_disable(dev, pipe_index); -} - /** * struct vsp1_du_atomic_config - VSP atomic configuration parameters * @pixelformat: plane pixel format (V4L2 4CC) From 4abd2f68950a296b6d547376d7d0f8b351e72f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:06 +0200 Subject: [PATCH 307/394] media: dt-bindings: media: rockchip-rga: add rockchip,rk3588-rga3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new compatible for the RGA3 (Raster Graphic Acceleration 3) peripheral found on the RK3588 SoC. Also specify an iommu property, as the RGA3 contains the generic rockchip iommu. While other versions also have an iommu, it's usually specific to them. The RK3588 contains one RGA2-Enhance core (also contained on the RK3399) and two RGA3 cores. Both feature a similar functionality of scaling, cropping and rotating of up to two input images into one output image. Key differences of the RGA3 are: - supports 10bit YUV output formats - supports 8x8 tiles and FBCD as inputs and outputs - supports BT2020 color space conversion - max output resolution of (8192-64)x(8192-64) - MMU can map up to 32G DDR RAM - fully planar formats (3 planes) are not supported - max scale up/down factor of 8 (RGA2 allows up to 16) Acked-by: Rob Herring (Arm) Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../devicetree/bindings/media/rockchip-rga.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/rockchip-rga.yaml b/Documentation/devicetree/bindings/media/rockchip-rga.yaml index ac17cda65191..7bd92f733666 100644 --- a/Documentation/devicetree/bindings/media/rockchip-rga.yaml +++ b/Documentation/devicetree/bindings/media/rockchip-rga.yaml @@ -9,7 +9,11 @@ title: Rockchip 2D raster graphic acceleration controller (RGA) description: RGA is a standalone 2D raster graphic acceleration unit. It accelerates 2D graphics operations, such as point/line drawing, image scaling, rotation, - BitBLT, alpha blending and image blur/sharpness. + BitBLT, alpha blending and image blur/sharpness. There exist many versions + of this unit that differ in the supported inputs/output formats, + the attached IOMMU and the supported operations on the input. As some SoCs + include multiple RGA units with different versions, a more specific + compatible name to differentiate the concrete unit is used for them. maintainers: - Jacob Chen @@ -20,6 +24,7 @@ properties: oneOf: - const: rockchip,rk3288-rga - const: rockchip,rk3399-rga + - const: rockchip,rk3588-rga3 - items: - enum: - rockchip,rk3228-rga @@ -45,6 +50,9 @@ properties: power-domains: maxItems: 1 + iommus: + maxItems: 1 + resets: maxItems: 3 From 3bb5a7d25a2bdc6ad22af404fb6f2cca01e96aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:07 +0200 Subject: [PATCH 308/394] media: v4l2-common: sort RGB formats in v4l2_format_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort the RGB formats in v4l2_format_info to match the format definitions in include/uapi/linux/videodev2.h . Also introduce the same sections to partition the list of formats and align the format info in each section. The alignment of the 1 or 2 bytes RGB formats contains an additional space in preparation of adding the missing formats to the list, as for V4L2_PIX_FMT_ARGB555X an additional space is necessary. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 54 +++++++++++++++------------ 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index bceafc4e92c8..f6cb7e14f223 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -245,33 +245,39 @@ EXPORT_SYMBOL_GPL(v4l2_s_parm_cap); const struct v4l2_format_info *v4l2_format_info(u32 format) { static const struct v4l2_format_info formats[] = { - /* RGB formats */ - { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + /* RGB formats (1 or 2 bytes per pixel) */ + { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + + /* RGB formats (3 or 4 bytes per pixel) */ + { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGBX1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + /* RGB formats (6 or 8 bytes per pixel) */ + { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + + /* HSV formats */ + { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + /* YUV packed formats */ { .format = V4L2_PIX_FMT_YUYV, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_YVYU, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, From e623f2b7397079005e940d101b80796b3c39d229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:08 +0200 Subject: [PATCH 309/394] media: v4l2-common: add missing 1 and 2 byte RGB formats to v4l2_format_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add all missing one and two byte RGB formats to v4l2_format_info. This allows drivers to more consistently use v4l2_format_info, as it now covers all currently defined RGB formats. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index f6cb7e14f223..b55ab72958ea 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -246,8 +246,29 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { static const struct v4l2_format_info formats[] = { /* RGB formats (1 or 2 bytes per pixel) */ + { .format = V4L2_PIX_FMT_RGB332, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XRGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XBGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XRGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XBGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_XRGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, /* RGB formats (3 or 4 bytes per pixel) */ From 84fbe791a6203e35a602f7fef94d967466cd8d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:09 +0200 Subject: [PATCH 310/394] media: v4l2-common: add has_alpha to v4l2_format_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a has_alpha value to the v4l2_format_info struct to indicate if the format contains an alpha component. This information can currently not be queried in a generic way, but might be useful for potential drivers to properly setup alpha blending to copy or set the alpha value. The implementation is based on the drm_format_info implementation. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 32 +++++++++++++-------------- include/media/v4l2-common.h | 2 ++ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index b55ab72958ea..3cc8b04e1ea6 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -248,26 +248,26 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) /* RGB formats (1 or 2 bytes per pixel) */ { .format = V4L2_PIX_FMT_RGB332, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_XRGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_RGBX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_XBGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGRA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_BGRX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_XRGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_RGBX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_XBGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGRA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_BGRX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_XRGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, @@ -276,24 +276,24 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGBX1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, + { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, /* RGB formats (6 or 8 bytes per pixel) */ { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, - { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true }, /* HSV formats */ { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index f8b1faced79c..401d8506c24b 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -520,6 +520,7 @@ enum v4l2_pixel_encoding { * @vdiv: Vertical chroma subsampling factor * @block_w: Per-plane macroblock pixel width (optional) * @block_h: Per-plane macroblock pixel height (optional) + * @has_alpha: Does the format embeds an alpha component? */ struct v4l2_format_info { u32 format; @@ -532,6 +533,7 @@ struct v4l2_format_info { u8 vdiv; u8 block_w[4]; u8 block_h[4]; + bool has_alpha; }; static inline bool v4l2_is_format_rgb(const struct v4l2_format_info *f) From 2c225846271fc6b4f7e2a34271ebe828ed461772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:10 +0200 Subject: [PATCH 311/394] media: v4l2-common: add v4l2_fill_pixfmt_mp_aligned helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a v4l2_fill_pixfmt_mp_aligned helper which allows the user to specify a custom stride alignment in bytes. This is necessary for hardware like the Rockchip RGA3, which requires the stride value to be aligned to a 16 bytes boundary. The code makes some assumptions about the v4l2 format to simplify the calculation. They currently hold for all known v4l2 formats. v4l2_format_plane_stride uses an unsigned int as argument type to avoid the later multiplication from overflowing the u8 value. All other places use u8, as no practical use cases for a larger alignment are known at the moment. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 56 +++++++++++++++++++++------ include/media/v4l2-common.h | 4 ++ 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 3cc8b04e1ea6..65db7340ad38 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -432,14 +432,33 @@ static inline unsigned int v4l2_format_block_height(const struct v4l2_format_inf } static inline unsigned int v4l2_format_plane_stride(const struct v4l2_format_info *info, int plane, - unsigned int width) + unsigned int width, unsigned int byte_alignment) { unsigned int hdiv = plane ? info->hdiv : 1; unsigned int aligned_width = ALIGN(width, v4l2_format_block_width(info, plane)); - return DIV_ROUND_UP(aligned_width, hdiv) * - info->bpp[plane] / info->bpp_div[plane]; + /* + * Formats with a single memory plane derive the stride of the + * other planes from the y stride. To avoid hardware or software + * deriving a different stride for the composite plane, + * multiply the alignment accordingly. + * + * It assumes the following format properties: + * - bpp_div[0] == bpp_div[1] + * - The multiplication factor doesn't differ between the non y planes + * - The multiplication factor is a power of 2 + */ + if (info->mem_planes == 1 && info->comp_planes > 1) { + if (plane == 0) + byte_alignment *= DIV_ROUND_UP(info->hdiv * info->bpp[0], info->bpp[1]); + else + byte_alignment *= DIV_ROUND_UP(info->bpp[1], info->hdiv * info->bpp[0]); + } + + return ALIGN(DIV_ROUND_UP(aligned_width, hdiv) * info->bpp[plane] / + info->bpp_div[plane], + byte_alignment); } static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_info *info, int plane, @@ -453,9 +472,10 @@ static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_inf } static inline unsigned int v4l2_format_plane_size(const struct v4l2_format_info *info, int plane, - unsigned int width, unsigned int height) + unsigned int width, unsigned int height, + u8 stride_alignment) { - return v4l2_format_plane_stride(info, plane, width) * + return v4l2_format_plane_stride(info, plane, width, stride_alignment) * v4l2_format_plane_height(info, plane, height); } @@ -476,8 +496,9 @@ void v4l2_apply_frmsize_constraints(u32 *width, u32 *height, } EXPORT_SYMBOL_GPL(v4l2_apply_frmsize_constraints); -int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, - u32 pixelformat, u32 width, u32 height) +int v4l2_fill_pixfmt_mp_aligned(struct v4l2_pix_format_mplane *pixfmt, + u32 pixelformat, u32 width, u32 height, + u8 stride_alignment) { const struct v4l2_format_info *info; struct v4l2_plane_pix_format *plane; @@ -494,23 +515,34 @@ int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, if (info->mem_planes == 1) { plane = &pixfmt->plane_fmt[0]; - plane->bytesperline = v4l2_format_plane_stride(info, 0, width); + plane->bytesperline = v4l2_format_plane_stride(info, 0, width, + stride_alignment); plane->sizeimage = 0; for (i = 0; i < info->comp_planes; i++) plane->sizeimage += - v4l2_format_plane_size(info, i, width, height); + v4l2_format_plane_size(info, i, width, height, + stride_alignment); } else { for (i = 0; i < info->comp_planes; i++) { plane = &pixfmt->plane_fmt[i]; plane->bytesperline = - v4l2_format_plane_stride(info, i, width); + v4l2_format_plane_stride(info, i, width, + stride_alignment); plane->sizeimage = plane->bytesperline * v4l2_format_plane_height(info, i, height); } } return 0; } +EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt_mp_aligned); + +int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, + u32 pixelformat, u32 width, u32 height) +{ + return v4l2_fill_pixfmt_mp_aligned(pixfmt, pixelformat, + width, height, 1); +} EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt_mp); int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, @@ -530,12 +562,12 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, pixfmt->width = width; pixfmt->height = height; pixfmt->pixelformat = pixelformat; - pixfmt->bytesperline = v4l2_format_plane_stride(info, 0, width); + pixfmt->bytesperline = v4l2_format_plane_stride(info, 0, width, 1); pixfmt->sizeimage = 0; for (i = 0; i < info->comp_planes; i++) pixfmt->sizeimage += - v4l2_format_plane_size(info, i, width, height); + v4l2_format_plane_size(info, i, width, height, 1); return 0; } EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt); diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 401d8506c24b..edd416178c33 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -558,6 +558,10 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, u32 width, u32 height); int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, u32 pixelformat, u32 width, u32 height); +/* @stride_alignment is a power of 2 value in bytes */ +int v4l2_fill_pixfmt_mp_aligned(struct v4l2_pix_format_mplane *pixfmt, + u32 pixelformat, u32 width, u32 height, + u8 stride_alignment); /** * v4l2_get_link_freq - Get link rate from transmitter From 65017e26c065d1240b0512c15867ef1128034fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:11 +0200 Subject: [PATCH 312/394] media: rockchip: rga: fix too small buffer size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the command buffer size being only a quarter of the actual size. The RGA_CMDBUF_SIZE macro was potentially intended to specify the length of the cmdbuf u32 array pointer. But as it's used to specify the size of the allocation, which is counted in bytes. Therefore adjust the macro size to bytes as it better matches the variable name and adjust it's users accordingly. As the command buffer is relatively small, it probably didn't caused an issue due to being smaller than a single page. Fixes: f7e7b48e6d79 ("[media] rockchip/rga: v4l2 m2m support") Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 2 +- drivers/media/platform/rockchip/rga/rga-hw.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 43ed742a1649..d1618bb24750 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -414,7 +414,7 @@ static void rga_cmd_set(struct rga_ctx *ctx, { struct rockchip_rga *rga = ctx->rga; - memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE * 4); + memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE); rga_cmd_set_src_addr(ctx, src->dma_desc_pa); /* diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index cc6bd7f5b030..2b8537a5fd0d 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -6,7 +6,7 @@ #ifndef __RGA_HW_H__ #define __RGA_HW_H__ -#define RGA_CMDBUF_SIZE 0x20 +#define RGA_CMDBUF_SIZE 0x80 /* Hardware limits */ #define MAX_WIDTH 8192 From a2a4f69fd2e333bacd17d1788eb5211cb6f373cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:12 +0200 Subject: [PATCH 313/394] media: rockchip: rga: use clk_bulk api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the clk_bulk API to avoid code duplication for each of the three clocks. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga.c | 65 +++-------------------- drivers/media/platform/rockchip/rga/rga.h | 6 +-- 2 files changed, 11 insertions(+), 60 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index fea63b94c5f3..4e710a050cb7 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -698,48 +698,10 @@ static const struct video_device rga_videodev = { .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, }; -static int rga_enable_clocks(struct rockchip_rga *rga) -{ - int ret; - - ret = clk_prepare_enable(rga->sclk); - if (ret) { - dev_err(rga->dev, "Cannot enable rga sclk: %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(rga->aclk); - if (ret) { - dev_err(rga->dev, "Cannot enable rga aclk: %d\n", ret); - goto err_disable_sclk; - } - - ret = clk_prepare_enable(rga->hclk); - if (ret) { - dev_err(rga->dev, "Cannot enable rga hclk: %d\n", ret); - goto err_disable_aclk; - } - - return 0; - -err_disable_aclk: - clk_disable_unprepare(rga->aclk); -err_disable_sclk: - clk_disable_unprepare(rga->sclk); - - return ret; -} - -static void rga_disable_clocks(struct rockchip_rga *rga) -{ - clk_disable_unprepare(rga->sclk); - clk_disable_unprepare(rga->hclk); - clk_disable_unprepare(rga->aclk); -} - static int rga_parse_dt(struct rockchip_rga *rga) { struct reset_control *core_rst, *axi_rst, *ahb_rst; + int ret; core_rst = devm_reset_control_get(rga->dev, "core"); if (IS_ERR(core_rst)) { @@ -771,23 +733,12 @@ static int rga_parse_dt(struct rockchip_rga *rga) udelay(1); reset_control_deassert(ahb_rst); - rga->sclk = devm_clk_get(rga->dev, "sclk"); - if (IS_ERR(rga->sclk)) { - dev_err(rga->dev, "failed to get sclk clock\n"); - return PTR_ERR(rga->sclk); - } - - rga->aclk = devm_clk_get(rga->dev, "aclk"); - if (IS_ERR(rga->aclk)) { - dev_err(rga->dev, "failed to get aclk clock\n"); - return PTR_ERR(rga->aclk); - } - - rga->hclk = devm_clk_get(rga->dev, "hclk"); - if (IS_ERR(rga->hclk)) { - dev_err(rga->dev, "failed to get hclk clock\n"); - return PTR_ERR(rga->hclk); + ret = devm_clk_bulk_get_all(rga->dev, &rga->clks); + if (ret < 0) { + dev_err(rga->dev, "failed to get clocks\n"); + return ret; } + rga->num_clks = ret; return 0; } @@ -935,7 +886,7 @@ static int __maybe_unused rga_runtime_suspend(struct device *dev) { struct rockchip_rga *rga = dev_get_drvdata(dev); - rga_disable_clocks(rga); + clk_bulk_disable_unprepare(rga->num_clks, rga->clks); return 0; } @@ -944,7 +895,7 @@ static int __maybe_unused rga_runtime_resume(struct device *dev) { struct rockchip_rga *rga = dev_get_drvdata(dev); - return rga_enable_clocks(rga); + return clk_bulk_prepare_enable(rga->num_clks, rga->clks); } static const struct dev_pm_ops rga_pm = { diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 72a28b120fab..2db10acecb40 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -6,6 +6,7 @@ #ifndef __RGA_H__ #define __RGA_H__ +#include #include #include #include @@ -81,9 +82,8 @@ struct rockchip_rga { struct device *dev; struct regmap *grf; void __iomem *regs; - struct clk *sclk; - struct clk *aclk; - struct clk *hclk; + struct clk_bulk_data *clks; + int num_clks; struct rockchip_rga_version version; /* vfd lock */ From 74d010fbdc4478293ceb96eed7f6609fe6c7529c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:13 +0200 Subject: [PATCH 314/394] media: rockchip: rga: use stride for offset calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the stride instead of the width for the offset calculation. This ensures that the bytesperline value doesn't need to match the width value of the image. Furthermore this patch removes the dependency on the uv_factor property and instead reuses the v4l2_format_info to determine the correct division factor. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-buf.c | 14 +++++++++----- drivers/media/platform/rockchip/rga/rga.c | 16 ---------------- drivers/media/platform/rockchip/rga/rga.h | 1 - 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index bb575873f2b2..65fc0d5b4aa1 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -14,7 +14,6 @@ #include #include -#include "rga-hw.h" #include "rga.h" static ssize_t fill_descriptors(struct rga_dma_desc *desc, size_t max_desc, @@ -95,14 +94,19 @@ static int rga_buf_init(struct vb2_buffer *vb) return 0; } -static int get_plane_offset(struct rga_frame *f, int plane) +static int get_plane_offset(struct rga_frame *f, + const struct v4l2_format_info *info, + int plane) { + u32 stride = f->pix.plane_fmt[0].bytesperline; + if (plane == 0) return 0; if (plane == 1) - return f->width * f->height; + return stride * f->height; if (plane == 2) - return f->width * f->height + (f->width * f->height / f->fmt->uv_factor); + return stride * f->height + + (stride * f->height / info->hdiv / info->vdiv); return -EINVAL; } @@ -148,7 +152,7 @@ static int rga_buf_prepare(struct vb2_buffer *vb) /* Fill the remaining planes */ info = v4l2_format_info(f->fmt->fourcc); for (i = info->mem_planes; i < info->comp_planes; i++) - offsets[i] = get_plane_offset(f, i); + offsets[i] = get_plane_offset(f, info, i); rbuf->offset.y_off = offsets[0]; rbuf->offset.u_off = offsets[1]; diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 4e710a050cb7..c07207edffdb 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -190,7 +190,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_ALPHA_SWAP, .hw_format = RGA_COLOR_FMT_ABGR8888, .depth = 32, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -199,7 +198,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR8888, .depth = 32, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -208,7 +206,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_XBGR8888, .depth = 32, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -217,7 +214,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_RGB888, .depth = 24, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -226,7 +222,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_RGB888, .depth = 24, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -235,7 +230,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR4444, .depth = 16, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -244,7 +238,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR1555, .depth = 16, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -253,7 +246,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_BGR565, .depth = 16, - .uv_factor = 1, .y_div = 1, .x_div = 1, }, @@ -262,7 +254,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, .depth = 12, - .uv_factor = 4, .y_div = 2, .x_div = 1, }, @@ -271,7 +262,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV422SP, .depth = 16, - .uv_factor = 2, .y_div = 1, .x_div = 1, }, @@ -280,7 +270,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, .depth = 12, - .uv_factor = 4, .y_div = 2, .x_div = 1, }, @@ -289,7 +278,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, .depth = 12, - .uv_factor = 4, .y_div = 2, .x_div = 1, }, @@ -298,7 +286,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV422SP, .depth = 16, - .uv_factor = 2, .y_div = 1, .x_div = 1, }, @@ -307,7 +294,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420P, .depth = 12, - .uv_factor = 4, .y_div = 2, .x_div = 2, }, @@ -316,7 +302,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV422P, .depth = 16, - .uv_factor = 2, .y_div = 1, .x_div = 2, }, @@ -325,7 +310,6 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV420P, .depth = 12, - .uv_factor = 4, .y_div = 2, .x_div = 2, }, diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 2db10acecb40..477cf5b62bbb 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -17,7 +17,6 @@ struct rga_fmt { u32 fourcc; int depth; - u8 uv_factor; u8 y_div; u8 x_div; u8 color_swap; From dbf91b1ab6d95662ceb66cb55abceda5b4dc2f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:14 +0200 Subject: [PATCH 315/394] media: rockchip: rga: remove redundant rga_frame variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the redundant rga_frame variables width, height and color space. The value of these variables is already contained in the pix member of rga_frame. The code also keeps these values in sync. Therefore drop them in favor of the existing pix member. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-buf.c | 6 ++-- drivers/media/platform/rockchip/rga/rga-hw.c | 6 ++-- drivers/media/platform/rockchip/rga/rga.c | 32 +++++++------------ drivers/media/platform/rockchip/rga/rga.h | 5 --- 4 files changed, 18 insertions(+), 31 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index 65fc0d5b4aa1..ffc6162b2e68 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -103,10 +103,10 @@ static int get_plane_offset(struct rga_frame *f, if (plane == 0) return 0; if (plane == 1) - return stride * f->height; + return stride * f->pix.height; if (plane == 2) - return stride * f->height + - (stride * f->height / info->hdiv / info->vdiv); + return stride * f->pix.height + + (stride * f->pix.height / info->hdiv / info->vdiv); return -EINVAL; } diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index d1618bb24750..ec6c17504ca1 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -53,7 +53,7 @@ rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset, x_div = frm->fmt->x_div; y_div = frm->fmt->y_div; uv_stride = frm->stride / x_div; - pixel_width = frm->stride / frm->width; + pixel_width = frm->stride / frm->pix.width; lt->y_off = offset->y_off + y * frm->stride + x * pixel_width; lt->u_off = offset->u_off + (y / y_div) * uv_stride + x / x_div; @@ -191,7 +191,7 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) if (RGA_COLOR_FMT_IS_YUV(ctx->in.fmt->hw_format) && RGA_COLOR_FMT_IS_RGB(ctx->out.fmt->hw_format)) { - switch (ctx->in.colorspace) { + switch (ctx->in.pix.colorspace) { case V4L2_COLORSPACE_REC709: src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; break; @@ -203,7 +203,7 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) if (RGA_COLOR_FMT_IS_RGB(ctx->in.fmt->hw_format) && RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) { - switch (ctx->out.colorspace) { + switch (ctx->out.pix.colorspace) { case V4L2_COLORSPACE_REC709: dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; break; diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index c07207edffdb..ca8d8a53dc25 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -329,9 +329,6 @@ static struct rga_fmt *rga_fmt_find(u32 pixelformat) } static struct rga_frame def_frame = { - .width = DEFAULT_WIDTH, - .height = DEFAULT_HEIGHT, - .colorspace = V4L2_COLORSPACE_DEFAULT, .crop.left = 0, .crop.top = 0, .crop.width = DEFAULT_WIDTH, @@ -363,9 +360,9 @@ static int rga_open(struct file *file) ctx->out = def_frame; v4l2_fill_pixfmt_mp(&ctx->in.pix, - ctx->in.fmt->fourcc, ctx->out.width, ctx->out.height); + ctx->in.fmt->fourcc, DEFAULT_WIDTH, DEFAULT_HEIGHT); v4l2_fill_pixfmt_mp(&ctx->out.pix, - ctx->out.fmt->fourcc, ctx->out.width, ctx->out.height); + ctx->out.fmt->fourcc, DEFAULT_WIDTH, DEFAULT_HEIGHT); if (mutex_lock_interruptible(&rga->mutex)) { kfree(ctx); @@ -453,10 +450,8 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) if (IS_ERR(frm)) return PTR_ERR(frm); - v4l2_fill_pixfmt_mp(pix_fmt, frm->fmt->fourcc, frm->width, frm->height); - + *pix_fmt = frm->pix; pix_fmt->field = V4L2_FIELD_NONE; - pix_fmt->colorspace = frm->colorspace; return 0; } @@ -505,27 +500,24 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frm = rga_get_frame(ctx, f->type); if (IS_ERR(frm)) return PTR_ERR(frm); - frm->width = pix_fmt->width; - frm->height = pix_fmt->height; frm->size = 0; for (i = 0; i < pix_fmt->num_planes; i++) frm->size += pix_fmt->plane_fmt[i].sizeimage; frm->fmt = rga_fmt_find(pix_fmt->pixelformat); frm->stride = pix_fmt->plane_fmt[0].bytesperline; - frm->colorspace = pix_fmt->colorspace; /* Reset crop settings */ frm->crop.left = 0; frm->crop.top = 0; - frm->crop.width = frm->width; - frm->crop.height = frm->height; + frm->crop.width = pix_fmt->width; + frm->crop.height = pix_fmt->height; frm->pix = *pix_fmt; v4l2_dbg(debug, 1, &rga->v4l2_dev, "[%s] fmt - %p4cc %dx%d (stride %d, sizeimage %d)\n", V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE", - &frm->fmt->fourcc, frm->width, frm->height, + &frm->fmt->fourcc, pix_fmt->width, pix_fmt->height, frm->stride, frm->size); for (i = 0; i < pix_fmt->num_planes; i++) { @@ -579,8 +571,8 @@ static int vidioc_g_selection(struct file *file, void *priv, } else { s->r.left = 0; s->r.top = 0; - s->r.width = f->width; - s->r.height = f->height; + s->r.width = f->pix.width; + s->r.height = f->pix.height; } return 0; @@ -629,8 +621,8 @@ static int vidioc_s_selection(struct file *file, void *priv, return -EINVAL; } - if (s->r.left + s->r.width > f->width || - s->r.top + s->r.height > f->height || + if (s->r.left + s->r.width > f->pix.width || + s->r.top + s->r.height > f->pix.height || s->r.width < MIN_WIDTH || s->r.height < MIN_HEIGHT) { v4l2_dbg(debug, 1, &rga->v4l2_dev, "unsupported crop value.\n"); return -EINVAL; @@ -821,8 +813,8 @@ static int rga_probe(struct platform_device *pdev) goto rel_m2m; } - def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; - def_frame.size = def_frame.stride * def_frame.height; + def_frame.stride = (DEFAULT_WIDTH * def_frame.fmt->depth) >> 3; + def_frame.size = def_frame.stride * DEFAULT_HEIGHT; ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 477cf5b62bbb..c4a3905a48f0 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -24,11 +24,6 @@ struct rga_fmt { }; struct rga_frame { - /* Original dimensions */ - u32 width; - u32 height; - u32 colorspace; - /* Crop */ struct v4l2_rect crop; From ce1b5e4239aae88e71aca0e9db1116d46be2fc0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:15 +0200 Subject: [PATCH 316/394] media: rockchip: rga: announce and sync colorimetry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Announce the capability to adjust the quantization and ycbcr_enc on the capture side and check if the SET_CSC flag is set when the colorimetry is changed. Furthermore copy the colorimetry from the output to the capture side to fix the currently failing v4l2-compliance tests, which expect exactly this behavior. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga.c | 37 +++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index ca8d8a53dc25..8c34f73d6976 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -437,6 +437,15 @@ static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f fmt = &formats[f->index]; f->pixelformat = fmt->fourcc; + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return 0; + + /* allow changing the quantization and xfer func for YUV formats */ + if (v4l2_is_format_yuv(v4l2_format_info(f->pixelformat))) + f->flags |= V4L2_FMT_FLAG_CSC_QUANTIZATION | + V4L2_FMT_FLAG_CSC_YCBCR_ENC; + return 0; } @@ -459,8 +468,25 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp; + struct rga_ctx *ctx = file_to_rga_ctx(file); struct rga_fmt *fmt; + if (V4L2_TYPE_IS_CAPTURE(f->type)) { + const struct rga_frame *frm; + + frm = rga_get_frame(ctx, f->type); + if (IS_ERR(frm)) + return PTR_ERR(frm); + + if (!(pix_fmt->flags & V4L2_PIX_FMT_FLAG_SET_CSC)) { + pix_fmt->quantization = frm->pix.quantization; + pix_fmt->ycbcr_enc = frm->pix.ycbcr_enc; + } + /* disallow values not announced in vidioc_enum_fmt */ + pix_fmt->colorspace = frm->pix.colorspace; + pix_fmt->xfer_func = frm->pix.xfer_func; + } + fmt = rga_fmt_find(pix_fmt->pixelformat); if (!fmt) fmt = &formats[0]; @@ -506,6 +532,17 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frm->fmt = rga_fmt_find(pix_fmt->pixelformat); frm->stride = pix_fmt->plane_fmt[0].bytesperline; + /* + * Copy colorimetry from output to capture as required by the + * v4l2-compliance tests + */ + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + ctx->out.pix.colorspace = pix_fmt->colorspace; + ctx->out.pix.ycbcr_enc = pix_fmt->ycbcr_enc; + ctx->out.pix.quantization = pix_fmt->quantization; + ctx->out.pix.xfer_func = pix_fmt->xfer_func; + } + /* Reset crop settings */ frm->crop.left = 0; frm->crop.top = 0; From 0fab01bb159a3c0cf79f44a7126987548e001a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:16 +0200 Subject: [PATCH 317/394] media: rockchip: rga: move hw specific parts to a dedicated struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for the RGA3 unit, move RGA2 specific parts from rga.c to rga-hw.c and create a struct to reference the RGA2 specific functions and formats. This also allows to remove the rga-hw.h reference from the include list of the rga driver. Also document the command finish interrupt with a dedicated define. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 166 ++++++++++++++- drivers/media/platform/rockchip/rga/rga-hw.h | 5 +- drivers/media/platform/rockchip/rga/rga.c | 211 ++++--------------- drivers/media/platform/rockchip/rga/rga.h | 23 +- 4 files changed, 227 insertions(+), 178 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index ec6c17504ca1..40498796507e 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -437,8 +437,8 @@ static void rga_cmd_set(struct rga_ctx *ctx, PAGE_SIZE, DMA_BIDIRECTIONAL); } -void rga_hw_start(struct rockchip_rga *rga, - struct rga_vb_buffer *src, struct rga_vb_buffer *dst) +static void rga_hw_start(struct rockchip_rga *rga, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst) { struct rga_ctx *ctx = rga->curr; @@ -452,3 +452,165 @@ void rga_hw_start(struct rockchip_rga *rga, rga_write(rga, RGA_CMD_CTRL, 0x1); } + +static bool rga_handle_irq(struct rockchip_rga *rga) +{ + int intr; + + intr = rga_read(rga, RGA_INT) & 0xf; + + rga_mod(rga, RGA_INT, intr << 4, 0xf << 4); + + return intr & RGA_INT_COMMAND_FINISHED; +} + +static void rga_get_version(struct rockchip_rga *rga) +{ + rga->version.major = (rga_read(rga, RGA_VERSION_INFO) >> 24) & 0xFF; + rga->version.minor = (rga_read(rga, RGA_VERSION_INFO) >> 20) & 0x0F; +} + +static struct rga_fmt formats[] = { + { + .fourcc = V4L2_PIX_FMT_ARGB32, + .color_swap = RGA_COLOR_ALPHA_SWAP, + .hw_format = RGA_COLOR_FMT_ABGR8888, + .depth = 32, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_ABGR32, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_ABGR8888, + .depth = 32, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_XBGR32, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_XBGR8888, + .depth = 32, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_RGB24, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_RGB888, + .depth = 24, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_RGB888, + .depth = 24, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_ARGB444, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_ABGR4444, + .depth = 16, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_ARGB555, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_ABGR1555, + .depth = 16, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .color_swap = RGA_COLOR_RB_SWAP, + .hw_format = RGA_COLOR_FMT_BGR565, + .depth = 16, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .color_swap = RGA_COLOR_UV_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420SP, + .depth = 12, + .y_div = 2, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV61, + .color_swap = RGA_COLOR_UV_SWAP, + .hw_format = RGA_COLOR_FMT_YUV422SP, + .depth = 16, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420SP, + .depth = 12, + .y_div = 2, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420SP, + .depth = 12, + .y_div = 2, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV422SP, + .depth = 16, + .y_div = 1, + .x_div = 1, + }, + { + .fourcc = V4L2_PIX_FMT_YUV420, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420P, + .depth = 12, + .y_div = 2, + .x_div = 2, + }, + { + .fourcc = V4L2_PIX_FMT_YUV422P, + .color_swap = RGA_COLOR_NONE_SWAP, + .hw_format = RGA_COLOR_FMT_YUV422P, + .depth = 16, + .y_div = 1, + .x_div = 2, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420, + .color_swap = RGA_COLOR_UV_SWAP, + .hw_format = RGA_COLOR_FMT_YUV420P, + .depth = 12, + .y_div = 2, + .x_div = 2, + }, +}; + +const struct rga_hw rga2_hw = { + .formats = formats, + .num_formats = ARRAY_SIZE(formats), + .cmdbuf_size = RGA_CMDBUF_SIZE, + .min_width = MIN_WIDTH, + .max_width = MAX_WIDTH, + .min_height = MIN_HEIGHT, + .max_height = MAX_HEIGHT, + + .start = rga_hw_start, + .handle_irq = rga_handle_irq, + .get_version = rga_get_version, +}; diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index 2b8537a5fd0d..c2e34be75193 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -15,9 +15,6 @@ #define MIN_WIDTH 34 #define MIN_HEIGHT 34 -#define DEFAULT_WIDTH 100 -#define DEFAULT_HEIGHT 100 - #define RGA_TIMEOUT 500 /* Registers address */ @@ -178,6 +175,8 @@ #define RGA_ALPHA_COLOR_NORMAL 0 #define RGA_ALPHA_COLOR_MULTIPLY_CAL 1 +#define RGA_INT_COMMAND_FINISHED 4 + /* Registers union */ union rga_mode_ctrl { unsigned int val; diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 8c34f73d6976..f599c992829d 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -25,7 +25,6 @@ #include #include -#include "rga-hw.h" #include "rga.h" static int debug; @@ -47,7 +46,7 @@ static void device_run(void *prv) dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - rga_hw_start(rga, vb_to_rga(src), vb_to_rga(dst)); + rga->hw->start(rga, vb_to_rga(src), vb_to_rga(dst)); spin_unlock_irqrestore(&rga->ctrl_lock, flags); } @@ -55,13 +54,8 @@ static void device_run(void *prv) static irqreturn_t rga_isr(int irq, void *prv) { struct rockchip_rga *rga = prv; - int intr; - intr = rga_read(rga, RGA_INT) & 0xf; - - rga_mod(rga, RGA_INT, intr << 4, 0xf << 4); - - if (intr & 0x04) { + if (rga->hw->handle_irq(rga)) { struct vb2_v4l2_buffer *src, *dst; struct rga_ctx *ctx = rga->curr; @@ -184,158 +178,17 @@ static int rga_setup_ctrls(struct rga_ctx *ctx) return 0; } -static struct rga_fmt formats[] = { - { - .fourcc = V4L2_PIX_FMT_ARGB32, - .color_swap = RGA_COLOR_ALPHA_SWAP, - .hw_format = RGA_COLOR_FMT_ABGR8888, - .depth = 32, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_ABGR32, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_ABGR8888, - .depth = 32, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_XBGR32, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_XBGR8888, - .depth = 32, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_RGB24, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_RGB888, - .depth = 24, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_BGR24, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_RGB888, - .depth = 24, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_ARGB444, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_ABGR4444, - .depth = 16, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_ARGB555, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_ABGR1555, - .depth = 16, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_RGB565, - .color_swap = RGA_COLOR_RB_SWAP, - .hw_format = RGA_COLOR_FMT_BGR565, - .depth = 16, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV21, - .color_swap = RGA_COLOR_UV_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, - .y_div = 2, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV61, - .color_swap = RGA_COLOR_UV_SWAP, - .hw_format = RGA_COLOR_FMT_YUV422SP, - .depth = 16, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV12, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, - .y_div = 2, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV12M, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, - .y_div = 2, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_NV16, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV422SP, - .depth = 16, - .y_div = 1, - .x_div = 1, - }, - { - .fourcc = V4L2_PIX_FMT_YUV420, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420P, - .depth = 12, - .y_div = 2, - .x_div = 2, - }, - { - .fourcc = V4L2_PIX_FMT_YUV422P, - .color_swap = RGA_COLOR_NONE_SWAP, - .hw_format = RGA_COLOR_FMT_YUV422P, - .depth = 16, - .y_div = 1, - .x_div = 2, - }, - { - .fourcc = V4L2_PIX_FMT_YVU420, - .color_swap = RGA_COLOR_UV_SWAP, - .hw_format = RGA_COLOR_FMT_YUV420P, - .depth = 12, - .y_div = 2, - .x_div = 2, - }, -}; - -#define NUM_FORMATS ARRAY_SIZE(formats) - -static struct rga_fmt *rga_fmt_find(u32 pixelformat) +static struct rga_fmt *rga_fmt_find(struct rockchip_rga *rga, u32 pixelformat) { unsigned int i; - for (i = 0; i < NUM_FORMATS; i++) { - if (formats[i].fourcc == pixelformat) - return &formats[i]; + for (i = 0; i < rga->hw->num_formats; i++) { + if (rga->hw->formats[i].fourcc == pixelformat) + return &rga->hw->formats[i]; } return NULL; } -static struct rga_frame def_frame = { - .crop.left = 0, - .crop.top = 0, - .crop.width = DEFAULT_WIDTH, - .crop.height = DEFAULT_HEIGHT, - .fmt = &formats[0], -}; - struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type) { if (V4L2_TYPE_IS_OUTPUT(type)) @@ -350,6 +203,18 @@ static int rga_open(struct file *file) struct rockchip_rga *rga = video_drvdata(file); struct rga_ctx *ctx = NULL; int ret = 0; + u32 def_width = clamp(DEFAULT_WIDTH, rga->hw->min_width, rga->hw->max_width); + u32 def_height = clamp(DEFAULT_HEIGHT, rga->hw->min_height, rga->hw->max_height); + struct rga_frame def_frame = { + .crop.left = 0, + .crop.top = 0, + .crop.width = def_width, + .crop.height = def_height, + .fmt = &rga->hw->formats[0], + }; + + def_frame.stride = (def_width * def_frame.fmt->depth) >> 3; + def_frame.size = def_frame.stride * def_height; ctx = kzalloc_obj(*ctx); if (!ctx) @@ -360,9 +225,9 @@ static int rga_open(struct file *file) ctx->out = def_frame; v4l2_fill_pixfmt_mp(&ctx->in.pix, - ctx->in.fmt->fourcc, DEFAULT_WIDTH, DEFAULT_HEIGHT); + ctx->in.fmt->fourcc, def_width, def_height); v4l2_fill_pixfmt_mp(&ctx->out.pix, - ctx->out.fmt->fourcc, DEFAULT_WIDTH, DEFAULT_HEIGHT); + ctx->out.fmt->fourcc, def_width, def_height); if (mutex_lock_interruptible(&rga->mutex)) { kfree(ctx); @@ -429,12 +294,13 @@ vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { + struct rockchip_rga *rga = video_drvdata(file); struct rga_fmt *fmt; - if (f->index >= NUM_FORMATS) + if (f->index >= rga->hw->num_formats) return -EINVAL; - fmt = &formats[f->index]; + fmt = &rga->hw->formats[f->index]; f->pixelformat = fmt->fourcc; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && @@ -469,6 +335,7 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp; struct rga_ctx *ctx = file_to_rga_ctx(file); + const struct rga_hw *hw = ctx->rga->hw; struct rga_fmt *fmt; if (V4L2_TYPE_IS_CAPTURE(f->type)) { @@ -487,14 +354,14 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) pix_fmt->xfer_func = frm->pix.xfer_func; } - fmt = rga_fmt_find(pix_fmt->pixelformat); + fmt = rga_fmt_find(ctx->rga, pix_fmt->pixelformat); if (!fmt) - fmt = &formats[0]; + fmt = &hw->formats[0]; pix_fmt->width = clamp(pix_fmt->width, - (u32)MIN_WIDTH, (u32)MAX_WIDTH); + hw->min_width, hw->max_width); pix_fmt->height = clamp(pix_fmt->height, - (u32)MIN_HEIGHT, (u32)MAX_HEIGHT); + hw->min_height, hw->max_height); v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height); pix_fmt->field = V4L2_FIELD_NONE; @@ -529,7 +396,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frm->size = 0; for (i = 0; i < pix_fmt->num_planes; i++) frm->size += pix_fmt->plane_fmt[i].sizeimage; - frm->fmt = rga_fmt_find(pix_fmt->pixelformat); + frm->fmt = rga_fmt_find(rga, pix_fmt->pixelformat); frm->stride = pix_fmt->plane_fmt[0].bytesperline; /* @@ -660,7 +527,7 @@ static int vidioc_s_selection(struct file *file, void *priv, if (s->r.left + s->r.width > f->pix.width || s->r.top + s->r.height > f->pix.height || - s->r.width < MIN_WIDTH || s->r.height < MIN_HEIGHT) { + s->r.width < rga->hw->min_width || s->r.height < rga->hw->min_height) { v4l2_dbg(debug, 1, &rga->v4l2_dev, "unsupported crop value.\n"); return -EINVAL; } @@ -770,6 +637,10 @@ static int rga_probe(struct platform_device *pdev) if (!rga) return -ENOMEM; + rga->hw = of_device_get_match_data(&pdev->dev); + if (!rga->hw) + return dev_err_probe(&pdev->dev, -ENODEV, "failed to get match data\n"); + rga->dev = &pdev->dev; spin_lock_init(&rga->ctrl_lock); mutex_init(&rga->mutex); @@ -833,8 +704,7 @@ static int rga_probe(struct platform_device *pdev) if (ret < 0) goto rel_m2m; - rga->version.major = (rga_read(rga, RGA_VERSION_INFO) >> 24) & 0xFF; - rga->version.minor = (rga_read(rga, RGA_VERSION_INFO) >> 20) & 0x0F; + rga->hw->get_version(rga); v4l2_info(&rga->v4l2_dev, "HW Version: 0x%02x.%02x\n", rga->version.major, rga->version.minor); @@ -842,7 +712,7 @@ static int rga_probe(struct platform_device *pdev) pm_runtime_put(rga->dev); /* Create CMD buffer */ - rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, RGA_CMDBUF_SIZE, + rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, rga->hw->cmdbuf_size, &rga->cmdbuf_phy, GFP_KERNEL, DMA_ATTR_WRITE_COMBINE); if (!rga->cmdbuf_virt) { @@ -850,9 +720,6 @@ static int rga_probe(struct platform_device *pdev) goto rel_m2m; } - def_frame.stride = (DEFAULT_WIDTH * def_frame.fmt->depth) >> 3; - def_frame.size = def_frame.stride * DEFAULT_HEIGHT; - ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&rga->v4l2_dev, "Failed to register video device\n"); @@ -865,7 +732,7 @@ static int rga_probe(struct platform_device *pdev) return 0; free_dma: - dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt, + dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt, rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); rel_m2m: v4l2_m2m_release(rga->m2m_dev); @@ -883,7 +750,7 @@ static void rga_remove(struct platform_device *pdev) { struct rockchip_rga *rga = platform_get_drvdata(pdev); - dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt, + dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt, rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); v4l2_info(&rga->v4l2_dev, "Removing\n"); @@ -919,9 +786,11 @@ static const struct dev_pm_ops rga_pm = { static const struct of_device_id rockchip_rga_match[] = { { .compatible = "rockchip,rk3288-rga", + .data = &rga2_hw, }, { .compatible = "rockchip,rk3399-rga", + .data = &rga2_hw, }, {}, }; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index c4a3905a48f0..640e51028534 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -14,6 +14,9 @@ #define RGA_NAME "rockchip-rga" +#define DEFAULT_WIDTH 100 +#define DEFAULT_HEIGHT 100 + struct rga_fmt { u32 fourcc; int depth; @@ -68,6 +71,8 @@ static inline struct rga_ctx *file_to_rga_ctx(struct file *filp) return container_of(file_to_v4l2_fh(filp), struct rga_ctx, fh); } +struct rga_hw; + struct rockchip_rga { struct v4l2_device v4l2_dev; struct v4l2_m2m_dev *m2m_dev; @@ -88,6 +93,8 @@ struct rockchip_rga { struct rga_ctx *curr; dma_addr_t cmdbuf_phy; void *cmdbuf_virt; + + const struct rga_hw *hw; }; struct rga_addr_offset { @@ -138,7 +145,19 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask) rga_write(rga, reg, temp); }; -void rga_hw_start(struct rockchip_rga *rga, - struct rga_vb_buffer *src, struct rga_vb_buffer *dst); +struct rga_hw { + struct rga_fmt *formats; + u32 num_formats; + size_t cmdbuf_size; + u32 min_width, min_height; + u32 max_width, max_height; + + void (*start)(struct rockchip_rga *rga, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst); + bool (*handle_irq)(struct rockchip_rga *rga); + void (*get_version)(struct rockchip_rga *rga); +}; + +extern const struct rga_hw rga2_hw; #endif From 92f50870ae987b8e2e5334e4ee38f82f6f405d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:17 +0200 Subject: [PATCH 318/394] media: rockchip: rga: avoid odd frame sizes for YUV formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid odd frame sizes for YUV formats, as they may cause undefined behavior. This is done in preparation for the RGA3, which hangs when the output format is set to 129x129 pixel YUV420 SP (NV12). This requirement is documented explicitly for the RGA3 in section 5.6.3 of the RK3588 TRM Part 2. For the RGA2 the RK3588 TRM Part 2 (section 6.1.2) and RK3568 TRM Part 2 (section 14.2) only mentions the x/y offsets and stride aligning requirements. But the vendor driver for the RGA2 also contains checks for the width and height to be aligned to 2 bytes. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index f599c992829d..77b8c7ab7427 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -337,6 +337,19 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) struct rga_ctx *ctx = file_to_rga_ctx(file); const struct rga_hw *hw = ctx->rga->hw; struct rga_fmt *fmt; + struct v4l2_frmsize_stepwise frmsize = { + .min_width = hw->min_width, + .max_width = hw->max_width, + .min_height = hw->min_height, + .max_height = hw->max_height, + .step_width = 1, + .step_height = 1, + }; + + if (v4l2_is_format_yuv(v4l2_format_info(pix_fmt->pixelformat))) { + frmsize.step_width = 2; + frmsize.step_height = 2; + } if (V4L2_TYPE_IS_CAPTURE(f->type)) { const struct rga_frame *frm; @@ -358,11 +371,7 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) if (!fmt) fmt = &hw->formats[0]; - pix_fmt->width = clamp(pix_fmt->width, - hw->min_width, hw->max_width); - pix_fmt->height = clamp(pix_fmt->height, - hw->min_height, hw->max_height); - + v4l2_apply_frmsize_constraints(&pix_fmt->width, &pix_fmt->height, &frmsize); v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height); pix_fmt->field = V4L2_FIELD_NONE; From 3de2ea285a15e27a2e81c442124062806235b26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:18 +0200 Subject: [PATCH 319/394] media: rockchip: rga: calculate x_div/y_div using v4l2_format_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Calculate the x_div and y_div variables with the information from v4l2_format_info instead of storing these in the rga_fmt struct. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 45 +++++--------------- drivers/media/platform/rockchip/rga/rga.h | 2 - 2 files changed, 11 insertions(+), 36 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 40498796507e..17f7a67c0b4b 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -42,6 +42,7 @@ rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset, { struct rga_corners_addr_offset offsets; struct rga_addr_offset *lt, *lb, *rt, *rb; + const struct v4l2_format_info *format_info; unsigned int x_div = 0, y_div = 0, uv_stride = 0, pixel_width = 0; @@ -50,8 +51,16 @@ rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset, rt = &offsets.right_top; rb = &offsets.right_bottom; - x_div = frm->fmt->x_div; - y_div = frm->fmt->y_div; + format_info = v4l2_format_info(frm->pix.pixelformat); + /* x_div is only used for the u/v planes. + * When the format doesn't have these, use 1 to avoid a division by zero. + */ + if (format_info->bpp[1]) + x_div = format_info->hdiv * format_info->bpp_div[1] / + format_info->bpp[1]; + else + x_div = 1; + y_div = format_info->vdiv; uv_stride = frm->stride / x_div; pixel_width = frm->stride / frm->pix.width; @@ -476,128 +485,96 @@ static struct rga_fmt formats[] = { .color_swap = RGA_COLOR_ALPHA_SWAP, .hw_format = RGA_COLOR_FMT_ABGR8888, .depth = 32, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_ABGR32, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR8888, .depth = 32, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_XBGR32, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_XBGR8888, .depth = 32, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_RGB24, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_RGB888, .depth = 24, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_BGR24, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_RGB888, .depth = 24, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_ARGB444, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR4444, .depth = 16, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_ARGB555, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR1555, .depth = 16, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_RGB565, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_BGR565, .depth = 16, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_NV21, .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, .depth = 12, - .y_div = 2, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_NV61, .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV422SP, .depth = 16, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_NV12, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, .depth = 12, - .y_div = 2, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_NV12M, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, .depth = 12, - .y_div = 2, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_NV16, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV422SP, .depth = 16, - .y_div = 1, - .x_div = 1, }, { .fourcc = V4L2_PIX_FMT_YUV420, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420P, .depth = 12, - .y_div = 2, - .x_div = 2, }, { .fourcc = V4L2_PIX_FMT_YUV422P, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV422P, .depth = 16, - .y_div = 1, - .x_div = 2, }, { .fourcc = V4L2_PIX_FMT_YVU420, .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV420P, .depth = 12, - .y_div = 2, - .x_div = 2, }, }; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 640e51028534..27b3c9b4f220 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -20,8 +20,6 @@ struct rga_fmt { u32 fourcc; int depth; - u8 y_div; - u8 x_div; u8 color_swap; u8 hw_format; }; From 6f4e57d57c474be1202a2e0c5c7a9b543503216a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:19 +0200 Subject: [PATCH 320/394] media: rockchip: rga: move cmdbuf to rga_ctx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the command buffer to the rga_ctx struct in preparation to reuse an already prepared command buffer. This allows to split the command buffer setup in a further commit to setup a template for the command buffer at streamon and only update the buffer addresses in device_run and trigger the command stream. No sync point is added, as one command buffer should only be used for one conversion at a time. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 28 +++++------- drivers/media/platform/rockchip/rga/rga.c | 48 +++++++++++--------- drivers/media/platform/rockchip/rga/rga.h | 5 +- 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 17f7a67c0b4b..9881c14f908d 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -122,8 +122,7 @@ static struct rga_addr_offset *rga_lookup_draw_pos(struct static void rga_cmd_set_src_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_SRC_BASE - RGA_MODE_BASE_REG; @@ -135,8 +134,7 @@ static void rga_cmd_set_src_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_SRC1_BASE - RGA_MODE_BASE_REG; @@ -148,8 +146,7 @@ static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) { - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int reg; reg = RGA_MMU_DST_BASE - RGA_MODE_BASE_REG; @@ -162,7 +159,7 @@ static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, dma_addr_t dma_addr) static void rga_cmd_set_trans_info(struct rga_ctx *ctx) { struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int scale_dst_w, scale_dst_h; unsigned int src_h, src_w, dst_h, dst_w; union rga_src_info src_info; @@ -322,8 +319,7 @@ static void rga_cmd_set_src_info(struct rga_ctx *ctx, struct rga_addr_offset *offset) { struct rga_corners_addr_offset src_offsets; - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int src_h, src_w, src_x, src_y; src_h = ctx->in.crop.height; @@ -350,8 +346,7 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx, { struct rga_addr_offset *dst_offset; struct rga_corners_addr_offset offsets; - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; unsigned int dst_h, dst_w, dst_x, dst_y; unsigned int mir_mode = 0; unsigned int rot_mode = 0; @@ -397,8 +392,7 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx, static void rga_cmd_set_mode(struct rga_ctx *ctx) { - struct rockchip_rga *rga = ctx->rga; - u32 *dest = rga->cmdbuf_virt; + u32 *dest = ctx->cmdbuf_virt; union rga_mode_ctrl mode; union rga_alpha_ctrl0 alpha_ctrl0; union rga_alpha_ctrl1 alpha_ctrl1; @@ -423,7 +417,7 @@ static void rga_cmd_set(struct rga_ctx *ctx, { struct rockchip_rga *rga = ctx->rga; - memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE); + memset(ctx->cmdbuf_virt, 0, RGA_CMDBUF_SIZE); rga_cmd_set_src_addr(ctx, src->dma_desc_pa); /* @@ -439,11 +433,11 @@ static void rga_cmd_set(struct rga_ctx *ctx, rga_cmd_set_dst_info(ctx, &dst->offset); rga_cmd_set_trans_info(ctx); - rga_write(rga, RGA_CMD_BASE, rga->cmdbuf_phy); + rga_write(rga, RGA_CMD_BASE, ctx->cmdbuf_phy); /* sync CMD buf for RGA */ - dma_sync_single_for_device(rga->dev, rga->cmdbuf_phy, - PAGE_SIZE, DMA_BIDIRECTIONAL); + dma_sync_single_for_device(rga->dev, ctx->cmdbuf_phy, + PAGE_SIZE, DMA_BIDIRECTIONAL); } static void rga_hw_start(struct rockchip_rga *rga, diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 77b8c7ab7427..bf6bbcbfc869 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -219,6 +219,16 @@ static int rga_open(struct file *file) ctx = kzalloc_obj(*ctx); if (!ctx) return -ENOMEM; + + /* Create CMD buffer */ + ctx->cmdbuf_virt = dma_alloc_attrs(rga->dev, rga->hw->cmdbuf_size, + &ctx->cmdbuf_phy, GFP_KERNEL, + DMA_ATTR_WRITE_COMBINE); + if (!ctx->cmdbuf_virt) { + ret = -ENOMEM; + goto rel_ctx; + } + ctx->rga = rga; /* Set default formats */ ctx->in = def_frame; @@ -230,15 +240,13 @@ static int rga_open(struct file *file) ctx->out.fmt->fourcc, def_width, def_height); if (mutex_lock_interruptible(&rga->mutex)) { - kfree(ctx); - return -ERESTARTSYS; + ret = -ERESTARTSYS; + goto rel_cmdbuf; } ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rga->m2m_dev, ctx, &queue_init); if (IS_ERR(ctx->fh.m2m_ctx)) { ret = PTR_ERR(ctx->fh.m2m_ctx); - mutex_unlock(&rga->mutex); - kfree(ctx); - return ret; + goto unlock_mutex; } v4l2_fh_init(&ctx->fh, video_devdata(file)); v4l2_fh_add(&ctx->fh, file); @@ -252,6 +260,15 @@ static int rga_open(struct file *file) mutex_unlock(&rga->mutex); return 0; + +unlock_mutex: + mutex_unlock(&rga->mutex); +rel_cmdbuf: + dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, ctx->cmdbuf_virt, + ctx->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); +rel_ctx: + kfree(ctx); + return ret; } static int rga_release(struct file *file) @@ -266,6 +283,10 @@ static int rga_release(struct file *file) v4l2_ctrl_handler_free(&ctx->ctrl_handler); v4l2_fh_del(&ctx->fh, file); v4l2_fh_exit(&ctx->fh); + + dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, ctx->cmdbuf_virt, + ctx->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); + kfree(ctx); mutex_unlock(&rga->mutex); @@ -720,19 +741,10 @@ static int rga_probe(struct platform_device *pdev) pm_runtime_put(rga->dev); - /* Create CMD buffer */ - rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, rga->hw->cmdbuf_size, - &rga->cmdbuf_phy, GFP_KERNEL, - DMA_ATTR_WRITE_COMBINE); - if (!rga->cmdbuf_virt) { - ret = -ENOMEM; - goto rel_m2m; - } - ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&rga->v4l2_dev, "Failed to register video device\n"); - goto free_dma; + goto rel_m2m; } v4l2_info(&rga->v4l2_dev, "Registered %s as /dev/%s\n", @@ -740,9 +752,6 @@ static int rga_probe(struct platform_device *pdev) return 0; -free_dma: - dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt, - rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); rel_m2m: v4l2_m2m_release(rga->m2m_dev); rel_vdev: @@ -759,9 +768,6 @@ static void rga_remove(struct platform_device *pdev) { struct rockchip_rga *rga = platform_get_drvdata(pdev); - dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt, - rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); - v4l2_info(&rga->v4l2_dev, "Removing\n"); v4l2_m2m_release(rga->m2m_dev); diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 27b3c9b4f220..04aeb7b42952 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -53,6 +53,9 @@ struct rga_ctx { struct rga_frame out; struct v4l2_ctrl_handler ctrl_handler; + void *cmdbuf_virt; + dma_addr_t cmdbuf_phy; + int osequence; int csequence; @@ -89,8 +92,6 @@ struct rockchip_rga { spinlock_t ctrl_lock; struct rga_ctx *curr; - dma_addr_t cmdbuf_phy; - void *cmdbuf_virt; const struct rga_hw *hw; }; From 00910c4f731773276d75acdcee3bc15f19a37705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:20 +0200 Subject: [PATCH 321/394] media: rockchip: rga: align stride to 4 bytes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an alignment setting to rga_hw to set the desired stride alignment. As the RGA2 register for the stride counts in word units, the code already divides the bytesperline value by 4 when writing it into the register. Therefore fix the alignment to a multiple of 4 to avoid potential off by one errors due from the division. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 1 + drivers/media/platform/rockchip/rga/rga.c | 11 ++++++----- drivers/media/platform/rockchip/rga/rga.h | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 9881c14f908d..dac3cb6aa17d 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -580,6 +580,7 @@ const struct rga_hw rga2_hw = { .max_width = MAX_WIDTH, .min_height = MIN_HEIGHT, .max_height = MAX_HEIGHT, + .stride_alignment = 4, .start = rga_hw_start, .handle_irq = rga_handle_irq, diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index bf6bbcbfc869..d080cb672740 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -234,10 +234,10 @@ static int rga_open(struct file *file) ctx->in = def_frame; ctx->out = def_frame; - v4l2_fill_pixfmt_mp(&ctx->in.pix, - ctx->in.fmt->fourcc, def_width, def_height); - v4l2_fill_pixfmt_mp(&ctx->out.pix, - ctx->out.fmt->fourcc, def_width, def_height); + v4l2_fill_pixfmt_mp_aligned(&ctx->in.pix, ctx->in.fmt->fourcc, + def_width, def_height, rga->hw->stride_alignment); + v4l2_fill_pixfmt_mp_aligned(&ctx->out.pix, ctx->out.fmt->fourcc, + def_width, def_height, rga->hw->stride_alignment); if (mutex_lock_interruptible(&rga->mutex)) { ret = -ERESTARTSYS; @@ -393,7 +393,8 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) fmt = &hw->formats[0]; v4l2_apply_frmsize_constraints(&pix_fmt->width, &pix_fmt->height, &frmsize); - v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height); + v4l2_fill_pixfmt_mp_aligned(pix_fmt, fmt->fourcc, + pix_fmt->width, pix_fmt->height, hw->stride_alignment); pix_fmt->field = V4L2_FIELD_NONE; return 0; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 04aeb7b42952..38518146910a 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -150,6 +150,7 @@ struct rga_hw { size_t cmdbuf_size; u32 min_width, min_height; u32 max_width, max_height; + u8 stride_alignment; void (*start)(struct rockchip_rga *rga, struct rga_vb_buffer *src, struct rga_vb_buffer *dst); From 10e2141179fe480763d10b604d7e4f9c62e8ba76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:21 +0200 Subject: [PATCH 322/394] media: rockchip: rga: reuse cmdbuf contents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reuse the command buffer contents instead of completely writing it for every frame. Therefore we only need to replace the source and destination addresses for each frame. This reduces the amount of CPU and memory operations done in each frame. A new cmdbuf_dirty flag notes if the cmdbuf has to be rewritten on the next frame. The initial idea of initializing the cmdbuf on streamon broke the ability to update controls while streaming (e.g. mirroring). Signed-off-by: Sven Püschel Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 13 +++++++++---- drivers/media/platform/rockchip/rga/rga.c | 11 +++++++++-- drivers/media/platform/rockchip/rga/rga.h | 2 ++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index dac3cb6aa17d..567d39e58d33 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -417,8 +417,6 @@ static void rga_cmd_set(struct rga_ctx *ctx, { struct rockchip_rga *rga = ctx->rga; - memset(ctx->cmdbuf_virt, 0, RGA_CMDBUF_SIZE); - rga_cmd_set_src_addr(ctx, src->dma_desc_pa); /* * Due to hardware bug, @@ -427,11 +425,9 @@ static void rga_cmd_set(struct rga_ctx *ctx, rga_cmd_set_src1_addr(ctx, dst->dma_desc_pa); rga_cmd_set_dst_addr(ctx, dst->dma_desc_pa); - rga_cmd_set_mode(ctx); rga_cmd_set_src_info(ctx, &src->offset); rga_cmd_set_dst_info(ctx, &dst->offset); - rga_cmd_set_trans_info(ctx); rga_write(rga, RGA_CMD_BASE, ctx->cmdbuf_phy); @@ -440,6 +436,14 @@ static void rga_cmd_set(struct rga_ctx *ctx, PAGE_SIZE, DMA_BIDIRECTIONAL); } +static void rga_hw_setup_cmdbuf(struct rga_ctx *ctx) +{ + memset(ctx->cmdbuf_virt, 0, RGA_CMDBUF_SIZE); + + rga_cmd_set_mode(ctx); + rga_cmd_set_trans_info(ctx); +} + static void rga_hw_start(struct rockchip_rga *rga, struct rga_vb_buffer *src, struct rga_vb_buffer *dst) { @@ -582,6 +586,7 @@ const struct rga_hw rga2_hw = { .max_height = MAX_HEIGHT, .stride_alignment = 4, + .setup_cmdbuf = rga_hw_setup_cmdbuf, .start = rga_hw_start, .handle_irq = rga_handle_irq, .get_version = rga_get_version, diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index d080cb672740..394b14b9469d 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -38,6 +38,11 @@ static void device_run(void *prv) unsigned long flags; spin_lock_irqsave(&rga->ctrl_lock, flags); + if (ctx->cmdbuf_dirty) { + ctx->cmdbuf_dirty = false; + rga->hw->setup_cmdbuf(ctx); + } + spin_unlock_irqrestore(&rga->ctrl_lock, flags); rga->curr = ctx; @@ -47,8 +52,6 @@ static void device_run(void *prv) dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); rga->hw->start(rga, vb_to_rga(src), vb_to_rga(dst)); - - spin_unlock_irqrestore(&rga->ctrl_lock, flags); } static irqreturn_t rga_isr(int irq, void *prv) @@ -141,6 +144,7 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl) ctx->fill_color = ctrl->val; break; } + ctx->cmdbuf_dirty = true; spin_unlock_irqrestore(&ctx->rga->ctrl_lock, flags); return 0; } @@ -228,6 +232,7 @@ static int rga_open(struct file *file) ret = -ENOMEM; goto rel_ctx; } + ctx->cmdbuf_dirty = true; ctx->rga = rga; /* Set default formats */ @@ -448,6 +453,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frm->crop.height = pix_fmt->height; frm->pix = *pix_fmt; + ctx->cmdbuf_dirty = true; v4l2_dbg(debug, 1, &rga->v4l2_dev, "[%s] fmt - %p4cc %dx%d (stride %d, sizeimage %d)\n", @@ -564,6 +570,7 @@ static int vidioc_s_selection(struct file *file, void *priv, } f->crop = s->r; + ctx->cmdbuf_dirty = true; return ret; } diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 38518146910a..5360f092fecf 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -55,6 +55,7 @@ struct rga_ctx { void *cmdbuf_virt; dma_addr_t cmdbuf_phy; + bool cmdbuf_dirty; int osequence; int csequence; @@ -152,6 +153,7 @@ struct rga_hw { u32 max_width, max_height; u8 stride_alignment; + void (*setup_cmdbuf)(struct rga_ctx *ctx); void (*start)(struct rockchip_rga *rga, struct rga_vb_buffer *src, struct rga_vb_buffer *dst); bool (*handle_irq)(struct rockchip_rga *rga); From 9481ec73f6e6bff573a5716692ceb9669b68f134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:22 +0200 Subject: [PATCH 323/394] media: rockchip: rga: check scaling factor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check the scaling factor to avoid potential problems. This is relevant for the upcoming RGA3 support, as it can hang when the scaling factor is exceeded. The check is done at streamon when the other side is already streaming to avoid incorrectly failing if the application configures the other side after calling streamon. As try_fmt shouldn't be state aware, it cannot be used to limit the format based on the scaling factor. Therefore the check is done just before the actual streaming would be started. As the driver allows changing the rotation and selection while streaming, add additional checks to ensure these changes don't exceed the scaling factor. Signed-off-by: Sven Püschel Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-buf.c | 28 +++++++++ drivers/media/platform/rockchip/rga/rga-hw.c | 1 + drivers/media/platform/rockchip/rga/rga-hw.h | 1 + drivers/media/platform/rockchip/rga/rga.c | 63 ++++++++++++++++++- drivers/media/platform/rockchip/rga/rga.h | 4 ++ 5 files changed, 94 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index ffc6162b2e68..dcaba66f5c1f 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -197,6 +197,33 @@ static void rga_buf_return_buffers(struct vb2_queue *q, } } +static int rga_buf_prepare_streaming(struct vb2_queue *q) +{ + struct rga_ctx *ctx = vb2_get_drv_priv(q); + const struct rga_hw *hw = ctx->rga->hw; + int ret; + + /* It's safe to check the streaming state of the other queue, + * as the streamon ioctl's can't race due to the lock set in + * the queue_init function. + */ + if ((V4L2_TYPE_IS_OUTPUT(q->type) && + vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx))) || + (V4L2_TYPE_IS_CAPTURE(q->type) && + vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx)))) { + /* + * As the other side is already streaming, + * check that the max scaling factor isn't exceeded. + */ + ret = rga_check_scaling(hw, &ctx->in.crop, &ctx->out.crop, + ctx->rotate); + if (ret < 0) + return ret; + } + + return 0; +} + static int rga_buf_start_streaming(struct vb2_queue *q, unsigned int count) { struct rga_ctx *ctx = vb2_get_drv_priv(q); @@ -232,6 +259,7 @@ const struct vb2_ops rga_qops = { .buf_prepare = rga_buf_prepare, .buf_queue = rga_buf_queue, .buf_cleanup = rga_buf_cleanup, + .prepare_streaming = rga_buf_prepare_streaming, .start_streaming = rga_buf_start_streaming, .stop_streaming = rga_buf_stop_streaming, }; diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 567d39e58d33..f2900812ba76 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -584,6 +584,7 @@ const struct rga_hw rga2_hw = { .max_width = MAX_WIDTH, .min_height = MIN_HEIGHT, .max_height = MAX_HEIGHT, + .max_scaling_factor = MAX_SCALING_FACTOR, .stride_alignment = 4, .setup_cmdbuf = rga_hw_setup_cmdbuf, diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index c2e34be75193..805ec23e5e3f 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -14,6 +14,7 @@ #define MIN_WIDTH 34 #define MIN_HEIGHT 34 +#define MAX_SCALING_FACTOR 16 #define RGA_TIMEOUT 500 diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 394b14b9469d..22954bbae55f 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -127,7 +127,9 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl) { struct rga_ctx *ctx = container_of(ctrl->handler, struct rga_ctx, ctrl_handler); + const struct rga_hw *hw = ctx->rga->hw; unsigned long flags; + int ret = 0; spin_lock_irqsave(&ctx->rga->ctrl_lock, flags); switch (ctrl->id) { @@ -138,6 +140,13 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl) ctx->vflip = ctrl->val; break; case V4L2_CID_ROTATE: + if (vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx)) && + vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx))) { + ret = rga_check_scaling(hw, &ctx->in.crop, + &ctx->out.crop, ctrl->val); + if (ret < 0) + goto s_ctrl_done; + } ctx->rotate = ctrl->val; break; case V4L2_CID_BG_COLOR: @@ -145,8 +154,10 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl) break; } ctx->cmdbuf_dirty = true; + +s_ctrl_done: spin_unlock_irqrestore(&ctx->rga->ctrl_lock, flags); - return 0; + return ret; } static const struct v4l2_ctrl_ops rga_ctrl_ops = { @@ -182,6 +193,38 @@ static int rga_setup_ctrls(struct rga_ctx *ctx) return 0; } +static bool check_scaling_factor(const struct rga_hw *hw, u32 src_size, + u32 dst_size) +{ + if (src_size < dst_size) + return src_size * hw->max_scaling_factor >= dst_size; + else + return dst_size * hw->max_scaling_factor >= src_size; +} + +int rga_check_scaling(const struct rga_hw *hw, const struct v4l2_rect *crop_in, + const struct v4l2_rect *crop_out, u32 rotate) +{ + u32 scaled_width; + u32 scaled_height; + + if (rotate == 90 || rotate == 270) { + scaled_width = crop_out->height; + scaled_height = crop_out->width; + } else { + scaled_width = crop_out->width; + scaled_height = crop_out->height; + } + + if (!check_scaling_factor(hw, crop_in->width, scaled_width)) + return -EINVAL; + + if (!check_scaling_factor(hw, crop_in->height, scaled_height)) + return -EINVAL; + + return 0; +} + static struct rga_fmt *rga_fmt_find(struct rockchip_rga *rga, u32 pixelformat) { unsigned int i; @@ -525,7 +568,6 @@ static int vidioc_s_selection(struct file *file, void *priv, struct rga_ctx *ctx = file_to_rga_ctx(file); struct rockchip_rga *rga = ctx->rga; struct rga_frame *f; - int ret = 0; f = rga_get_frame(ctx, s->type); if (IS_ERR(f)) @@ -569,10 +611,25 @@ static int vidioc_s_selection(struct file *file, void *priv, return -EINVAL; } + if (vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx)) && + vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx))) { + int ret = 0; + + if (V4L2_TYPE_IS_OUTPUT(s->type)) + ret = rga_check_scaling(rga->hw, &s->r, &ctx->out.crop, + ctx->rotate); + else + ret = rga_check_scaling(rga->hw, &ctx->in.crop, &s->r, + ctx->rotate); + + if (ret < 0) + return ret; + } + f->crop = s->r; ctx->cmdbuf_dirty = true; - return ret; + return 0; } static const struct v4l2_ioctl_ops rga_ioctl_ops = { diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 5360f092fecf..df525c6aea8b 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -123,6 +123,9 @@ static inline struct rga_vb_buffer *vb_to_rga(struct vb2_v4l2_buffer *vb) struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type); +int rga_check_scaling(const struct rga_hw *hw, const struct v4l2_rect *crop_in, + const struct v4l2_rect *crop_out, u32 rotate); + /* RGA Buffers Manage */ extern const struct vb2_ops rga_qops; @@ -151,6 +154,7 @@ struct rga_hw { size_t cmdbuf_size; u32 min_width, min_height; u32 max_width, max_height; + u8 max_scaling_factor; u8 stride_alignment; void (*setup_cmdbuf)(struct rga_ctx *ctx); From d8eb890079d19e4b5ac8073038c4fea006992e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:23 +0200 Subject: [PATCH 324/394] media: rockchip: rga: use card type to specify rga type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation of the RGA3 support add a filed to the rga_hw struct to specify the desired card type value. This allows the user to differentiate the RGA2 and RGA3 video device nodes. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 1 + drivers/media/platform/rockchip/rga/rga.c | 4 +++- drivers/media/platform/rockchip/rga/rga.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index f2900812ba76..43fd023b7571 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -577,6 +577,7 @@ static struct rga_fmt formats[] = { }; const struct rga_hw rga2_hw = { + .card_type = "rga2", .formats = formats, .num_formats = ARRAY_SIZE(formats), .cmdbuf_size = RGA_CMDBUF_SIZE, diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 22954bbae55f..91775b43ff61 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -354,8 +354,10 @@ static const struct v4l2_file_operations rga_fops = { static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { + struct rockchip_rga *rga = video_drvdata(file); + strscpy(cap->driver, RGA_NAME, sizeof(cap->driver)); - strscpy(cap->card, "rockchip-rga", sizeof(cap->card)); + strscpy(cap->card, rga->hw->card_type, sizeof(cap->card)); strscpy(cap->bus_info, "platform:rga", sizeof(cap->bus_info)); return 0; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index df525c6aea8b..cee2e75ea89f 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -149,6 +149,7 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask) }; struct rga_hw { + const char *card_type; struct rga_fmt *formats; u32 num_formats; size_t cmdbuf_size; From e21531a6b031a46f37f61d4d7c7508868fb3d434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:24 +0200 Subject: [PATCH 325/394] media: rockchip: rga: change offset to dma_addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the offset to dma_addresses, as the current naming is misleading. The offset naming comes from the fact that it references the offset in the mapped iommu address space. But from the hardware point of view this is an address, as also pointed out by the register naming (e.g. RGA_DST_Y_RGB_BASE_ADDR). Therefore also change the type to dma_addr_t, as with an external iommu driver this would also be the correct type. This change is a preparation for the RGA3 support, which uses an external iommu and therefore just gets an dma_addr_t for each buffer. The field renaming allows to reuse the existing fields of rga_vb_buffer to store these values. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-buf.c | 12 +- drivers/media/platform/rockchip/rga/rga-hw.c | 105 +++++++++--------- drivers/media/platform/rockchip/rga/rga.h | 12 +- 3 files changed, 64 insertions(+), 65 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index dcaba66f5c1f..ab9554c1c4cd 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -121,7 +121,7 @@ static int rga_buf_prepare(struct vb2_buffer *vb) size_t curr_desc = 0; int i; const struct v4l2_format_info *info; - unsigned int offsets[VIDEO_MAX_PLANES]; + dma_addr_t dma_addrs[VIDEO_MAX_PLANES]; if (IS_ERR(f)) return PTR_ERR(f); @@ -145,18 +145,18 @@ static int rga_buf_prepare(struct vb2_buffer *vb) "Failed to map video buffer to RGA\n"); return n_desc; } - offsets[i] = curr_desc << PAGE_SHIFT; + dma_addrs[i] = curr_desc << PAGE_SHIFT; curr_desc += n_desc; } /* Fill the remaining planes */ info = v4l2_format_info(f->fmt->fourcc); for (i = info->mem_planes; i < info->comp_planes; i++) - offsets[i] = get_plane_offset(f, info, i); + dma_addrs[i] = dma_addrs[0] + get_plane_offset(f, info, i); - rbuf->offset.y_off = offsets[0]; - rbuf->offset.u_off = offsets[1]; - rbuf->offset.v_off = offsets[2]; + rbuf->dma_addrs.y_addr = dma_addrs[0]; + rbuf->dma_addrs.u_addr = dma_addrs[1]; + rbuf->dma_addrs.v_addr = dma_addrs[2]; return 0; } diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 43fd023b7571..99cf57d5ba89 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -16,11 +16,11 @@ enum e_rga_start_pos { RB = 3, }; -struct rga_corners_addr_offset { - struct rga_addr_offset left_top; - struct rga_addr_offset right_top; - struct rga_addr_offset left_bottom; - struct rga_addr_offset right_bottom; +struct rga_corners_addrs { + struct rga_addrs left_top; + struct rga_addrs right_top; + struct rga_addrs left_bottom; + struct rga_addrs right_bottom; }; static unsigned int rga_get_scaling(unsigned int src, unsigned int dst) @@ -36,20 +36,20 @@ static unsigned int rga_get_scaling(unsigned int src, unsigned int dst) return (src > dst) ? ((dst << 16) / src) : ((src << 16) / dst); } -static struct rga_corners_addr_offset -rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset, - unsigned int x, unsigned int y, unsigned int w, unsigned int h) +static struct rga_corners_addrs +rga_get_corner_addrs(struct rga_frame *frm, struct rga_addrs *addrs, + unsigned int x, unsigned int y, unsigned int w, unsigned int h) { - struct rga_corners_addr_offset offsets; - struct rga_addr_offset *lt, *lb, *rt, *rb; + struct rga_corners_addrs corner_addrs; + struct rga_addrs *lt, *lb, *rt, *rb; const struct v4l2_format_info *format_info; unsigned int x_div = 0, y_div = 0, uv_stride = 0, pixel_width = 0; - lt = &offsets.left_top; - lb = &offsets.left_bottom; - rt = &offsets.right_top; - rb = &offsets.right_bottom; + lt = &corner_addrs.left_top; + lb = &corner_addrs.left_bottom; + rt = &corner_addrs.right_top; + rb = &corner_addrs.right_bottom; format_info = v4l2_format_info(frm->pix.pixelformat); /* x_div is only used for the u/v planes. @@ -64,29 +64,28 @@ rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset, uv_stride = frm->stride / x_div; pixel_width = frm->stride / frm->pix.width; - lt->y_off = offset->y_off + y * frm->stride + x * pixel_width; - lt->u_off = offset->u_off + (y / y_div) * uv_stride + x / x_div; - lt->v_off = offset->v_off + (y / y_div) * uv_stride + x / x_div; + lt->y_addr = addrs->y_addr + y * frm->stride + x * pixel_width; + lt->u_addr = addrs->u_addr + (y / y_div) * uv_stride + x / x_div; + lt->v_addr = addrs->v_addr + (y / y_div) * uv_stride + x / x_div; - lb->y_off = lt->y_off + (h - 1) * frm->stride; - lb->u_off = lt->u_off + (h / y_div - 1) * uv_stride; - lb->v_off = lt->v_off + (h / y_div - 1) * uv_stride; + lb->y_addr = lt->y_addr + (h - 1) * frm->stride; + lb->u_addr = lt->u_addr + (h / y_div - 1) * uv_stride; + lb->v_addr = lt->v_addr + (h / y_div - 1) * uv_stride; - rt->y_off = lt->y_off + (w - 1) * pixel_width; - rt->u_off = lt->u_off + w / x_div - 1; - rt->v_off = lt->v_off + w / x_div - 1; + rt->y_addr = lt->y_addr + (w - 1) * pixel_width; + rt->u_addr = lt->u_addr + w / x_div - 1; + rt->v_addr = lt->v_addr + w / x_div - 1; - rb->y_off = lb->y_off + (w - 1) * pixel_width; - rb->u_off = lb->u_off + w / x_div - 1; - rb->v_off = lb->v_off + w / x_div - 1; + rb->y_addr = lb->y_addr + (w - 1) * pixel_width; + rb->u_addr = lb->u_addr + w / x_div - 1; + rb->v_addr = lb->v_addr + w / x_div - 1; - return offsets; + return corner_addrs; } -static struct rga_addr_offset *rga_lookup_draw_pos(struct - rga_corners_addr_offset - * offsets, u32 rotate_mode, - u32 mirr_mode) +static struct rga_addrs *rga_lookup_draw_pos(struct rga_corners_addrs *corner_addrs, + u32 rotate_mode, + u32 mirr_mode) { static enum e_rga_start_pos rot_mir_point_matrix[4][4] = { { @@ -103,18 +102,18 @@ static struct rga_addr_offset *rga_lookup_draw_pos(struct }, }; - if (!offsets) + if (!corner_addrs) return NULL; switch (rot_mir_point_matrix[rotate_mode][mirr_mode]) { case LT: - return &offsets->left_top; + return &corner_addrs->left_top; case LB: - return &offsets->left_bottom; + return &corner_addrs->left_bottom; case RT: - return &offsets->right_top; + return &corner_addrs->right_top; case RB: - return &offsets->right_bottom; + return &corner_addrs->right_bottom; } return NULL; @@ -316,9 +315,9 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) } static void rga_cmd_set_src_info(struct rga_ctx *ctx, - struct rga_addr_offset *offset) + struct rga_addrs *addrs) { - struct rga_corners_addr_offset src_offsets; + struct rga_corners_addrs src_corner_addrs; u32 *dest = ctx->cmdbuf_virt; unsigned int src_h, src_w, src_x, src_y; @@ -330,22 +329,22 @@ static void rga_cmd_set_src_info(struct rga_ctx *ctx, /* * Calculate the source framebuffer base address with offset pixel. */ - src_offsets = rga_get_addr_offset(&ctx->in, offset, - src_x, src_y, src_w, src_h); + src_corner_addrs = rga_get_corner_addrs(&ctx->in, addrs, + src_x, src_y, src_w, src_h); dest[(RGA_SRC_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - src_offsets.left_top.y_off; + src_corner_addrs.left_top.y_addr; dest[(RGA_SRC_CB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - src_offsets.left_top.u_off; + src_corner_addrs.left_top.u_addr; dest[(RGA_SRC_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - src_offsets.left_top.v_off; + src_corner_addrs.left_top.v_addr; } static void rga_cmd_set_dst_info(struct rga_ctx *ctx, - struct rga_addr_offset *offset) + struct rga_addrs *addrs) { - struct rga_addr_offset *dst_offset; - struct rga_corners_addr_offset offsets; + struct rga_addrs *dst_addrs; + struct rga_corners_addrs corner_addrs; u32 *dest = ctx->cmdbuf_virt; unsigned int dst_h, dst_w, dst_x, dst_y; unsigned int mir_mode = 0; @@ -379,15 +378,15 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx, /* * Configure the dest framebuffer base address with pixel offset. */ - offsets = rga_get_addr_offset(&ctx->out, offset, dst_x, dst_y, dst_w, dst_h); - dst_offset = rga_lookup_draw_pos(&offsets, rot_mode, mir_mode); + corner_addrs = rga_get_corner_addrs(&ctx->out, addrs, dst_x, dst_y, dst_w, dst_h); + dst_addrs = rga_lookup_draw_pos(&corner_addrs, rot_mode, mir_mode); dest[(RGA_DST_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - dst_offset->y_off; + dst_addrs->y_addr; dest[(RGA_DST_CB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - dst_offset->u_off; + dst_addrs->u_addr; dest[(RGA_DST_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] = - dst_offset->v_off; + dst_addrs->v_addr; } static void rga_cmd_set_mode(struct rga_ctx *ctx) @@ -426,8 +425,8 @@ static void rga_cmd_set(struct rga_ctx *ctx, rga_cmd_set_dst_addr(ctx, dst->dma_desc_pa); - rga_cmd_set_src_info(ctx, &src->offset); - rga_cmd_set_dst_info(ctx, &dst->offset); + rga_cmd_set_src_info(ctx, &src->dma_addrs); + rga_cmd_set_dst_info(ctx, &dst->dma_addrs); rga_write(rga, RGA_CMD_BASE, ctx->cmdbuf_phy); diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index cee2e75ea89f..bf21a57555a5 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -97,10 +97,10 @@ struct rockchip_rga { const struct rga_hw *hw; }; -struct rga_addr_offset { - unsigned int y_off; - unsigned int u_off; - unsigned int v_off; +struct rga_addrs { + dma_addr_t y_addr; + dma_addr_t u_addr; + dma_addr_t v_addr; }; struct rga_vb_buffer { @@ -112,8 +112,8 @@ struct rga_vb_buffer { dma_addr_t dma_desc_pa; size_t n_desc; - /* Plane offsets of this buffer into the mapping */ - struct rga_addr_offset offset; + /* Plane DMA addresses after the MMU mapping of the buffer */ + struct rga_addrs dma_addrs; }; static inline struct rga_vb_buffer *vb_to_rga(struct vb2_v4l2_buffer *vb) From 5873cb8ce139bdeb60747d672667de19736b759b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:25 +0200 Subject: [PATCH 326/394] media: rockchip: rga: support external iommus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for the RGA3 add support for external iommus. This is a transition step to just disable the RGA2 specific mmu table setup code. Currently a simple rga_hw struct field is used to set the internal iommu. But to handle the case of more sophisticated detection mechanisms (e.g. check for an iommu property in the device tree), it is abstracted by an inline function. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/Kconfig | 1 + drivers/media/platform/rockchip/rga/rga-buf.c | 31 +++++++++++++------ drivers/media/platform/rockchip/rga/rga-hw.c | 1 + drivers/media/platform/rockchip/rga/rga.c | 11 +++++-- drivers/media/platform/rockchip/rga/rga.h | 6 ++++ 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/Kconfig b/drivers/media/platform/rockchip/rga/Kconfig index 727a0f6ea466..846e555829f3 100644 --- a/drivers/media/platform/rockchip/rga/Kconfig +++ b/drivers/media/platform/rockchip/rga/Kconfig @@ -3,6 +3,7 @@ config VIDEO_ROCKCHIP_RGA depends on V4L_MEM2MEM_DRIVERS depends on VIDEO_DEV depends on ARCH_ROCKCHIP || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_DMA_SG select V4L2_MEM2MEM_DEV help diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index ab9554c1c4cd..cd6904d5fe5a 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "rga.h" @@ -82,6 +83,9 @@ static int rga_buf_init(struct vb2_buffer *vb) if (IS_ERR(f)) return PTR_ERR(f); + if (!rga_has_internal_iommu(rga)) + return 0; + n_desc = DIV_ROUND_UP(f->size, PAGE_SIZE); rbuf->n_desc = n_desc; @@ -136,17 +140,21 @@ static int rga_buf_prepare(struct vb2_buffer *vb) for (i = 0; i < vb->num_planes; i++) { vb2_set_plane_payload(vb, i, f->pix.plane_fmt[i].sizeimage); - /* Create local MMU table for RGA */ - n_desc = fill_descriptors(&rbuf->dma_desc[curr_desc], - rbuf->n_desc - curr_desc, - vb2_dma_sg_plane_desc(vb, i)); - if (n_desc < 0) { - v4l2_err(&ctx->rga->v4l2_dev, - "Failed to map video buffer to RGA\n"); - return n_desc; + if (rga_has_internal_iommu(ctx->rga)) { + /* Create local MMU table for RGA */ + n_desc = fill_descriptors(&rbuf->dma_desc[curr_desc], + rbuf->n_desc - curr_desc, + vb2_dma_sg_plane_desc(vb, i)); + if (n_desc < 0) { + v4l2_err(&ctx->rga->v4l2_dev, + "Failed to map video buffer to RGA\n"); + return n_desc; + } + dma_addrs[i] = curr_desc << PAGE_SHIFT; + curr_desc += n_desc; + } else { + dma_addrs[i] = vb2_dma_contig_plane_dma_addr(vb, i); } - dma_addrs[i] = curr_desc << PAGE_SHIFT; - curr_desc += n_desc; } /* Fill the remaining planes */ @@ -176,6 +184,9 @@ static void rga_buf_cleanup(struct vb2_buffer *vb) struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct rockchip_rga *rga = ctx->rga; + if (!rga_has_internal_iommu(rga)) + return; + dma_free_coherent(rga->dev, rbuf->n_desc * sizeof(*rbuf->dma_desc), rbuf->dma_desc, rbuf->dma_desc_pa); } diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 99cf57d5ba89..73584706a47e 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -577,6 +577,7 @@ static struct rga_fmt formats[] = { const struct rga_hw rga2_hw = { .card_type = "rga2", + .has_internal_iommu = true, .formats = formats, .num_formats = ARRAY_SIZE(formats), .cmdbuf_size = RGA_CMDBUF_SIZE, diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 91775b43ff61..e3c99c3f7c5b 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "rga.h" @@ -98,7 +99,10 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) src_vq->io_modes = VB2_MMAP | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->ops = &rga_qops; - src_vq->mem_ops = &vb2_dma_sg_memops; + if (rga_has_internal_iommu(ctx->rga)) + src_vq->mem_ops = &vb2_dma_sg_memops; + else + src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->gfp_flags = __GFP_DMA32; src_vq->buf_struct_size = sizeof(struct rga_vb_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; @@ -113,7 +117,10 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->ops = &rga_qops; - dst_vq->mem_ops = &vb2_dma_sg_memops; + if (rga_has_internal_iommu(ctx->rga)) + dst_vq->mem_ops = &vb2_dma_sg_memops; + else + dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->gfp_flags = __GFP_DMA32; dst_vq->buf_struct_size = sizeof(struct rga_vb_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index bf21a57555a5..b180df5c4837 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -150,6 +150,7 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask) struct rga_hw { const char *card_type; + bool has_internal_iommu; struct rga_fmt *formats; u32 num_formats; size_t cmdbuf_size; @@ -165,6 +166,11 @@ struct rga_hw { void (*get_version)(struct rockchip_rga *rga); }; +static inline bool rga_has_internal_iommu(const struct rockchip_rga *rga) +{ + return rga->hw->has_internal_iommu; +} + extern const struct rga_hw rga2_hw; #endif From 6b7c18553720a2a9594f88940c47d4a7ff9687b1 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Thu, 21 May 2026 00:44:26 +0200 Subject: [PATCH 327/394] media: rockchip: rga: share the interrupt when an external iommu is used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RGA3 and the corresponding iommu share the interrupt. So in that case, request a shared interrupt so that the iommu driver can request it as well. Signed-off-by: Michael Olbrich Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index e3c99c3f7c5b..cda3cecb1ce8 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -767,7 +767,8 @@ static int rga_probe(struct platform_device *pdev) goto err_put_clk; } - ret = devm_request_irq(rga->dev, irq, rga_isr, 0, + ret = devm_request_irq(rga->dev, irq, rga_isr, + rga_has_internal_iommu(rga) ? 0 : IRQF_SHARED, dev_name(rga->dev), rga); if (ret < 0) { dev_err(rga->dev, "failed to request irq\n"); From d11d812ddea312659c28d4402c059f88f2b4e348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:27 +0200 Subject: [PATCH 328/394] media: rockchip: rga: remove size from rga_frame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The size member is only used for the mmu page table mapping. Therefore avoid storing the value and instead only calculate it in place. This also avoids the calculation entirely when an external iommu is used. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-buf.c | 6 +++++- drivers/media/platform/rockchip/rga/rga.c | 8 ++------ drivers/media/platform/rockchip/rga/rga.h | 1 - 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index cd6904d5fe5a..092c2bdf6b67 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -79,6 +79,8 @@ static int rga_buf_init(struct vb2_buffer *vb) struct rockchip_rga *rga = ctx->rga; struct rga_frame *f = rga_get_frame(ctx, vb->vb2_queue->type); size_t n_desc = 0; + u32 size = 0; + u8 i; if (IS_ERR(f)) return PTR_ERR(f); @@ -86,7 +88,9 @@ static int rga_buf_init(struct vb2_buffer *vb) if (!rga_has_internal_iommu(rga)) return 0; - n_desc = DIV_ROUND_UP(f->size, PAGE_SIZE); + for (i = 0; i < f->pix.num_planes; i++) + size += f->pix.plane_fmt[i].sizeimage; + n_desc = DIV_ROUND_UP(size, PAGE_SIZE); rbuf->n_desc = n_desc; rbuf->dma_desc = dma_alloc_coherent(rga->dev, diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index cda3cecb1ce8..e44e81814b7c 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -268,7 +268,6 @@ static int rga_open(struct file *file) }; def_frame.stride = (def_width * def_frame.fmt->depth) >> 3; - def_frame.size = def_frame.stride * def_height; ctx = kzalloc_obj(*ctx); if (!ctx) @@ -481,9 +480,6 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frm = rga_get_frame(ctx, f->type); if (IS_ERR(frm)) return PTR_ERR(frm); - frm->size = 0; - for (i = 0; i < pix_fmt->num_planes; i++) - frm->size += pix_fmt->plane_fmt[i].sizeimage; frm->fmt = rga_fmt_find(rga, pix_fmt->pixelformat); frm->stride = pix_fmt->plane_fmt[0].bytesperline; @@ -508,10 +504,10 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) ctx->cmdbuf_dirty = true; v4l2_dbg(debug, 1, &rga->v4l2_dev, - "[%s] fmt - %p4cc %dx%d (stride %d, sizeimage %d)\n", + "[%s] fmt - %p4cc %dx%d (stride %d)\n", V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE", &frm->fmt->fourcc, pix_fmt->width, pix_fmt->height, - frm->stride, frm->size); + frm->stride); for (i = 0; i < pix_fmt->num_planes; i++) { v4l2_dbg(debug, 1, &rga->v4l2_dev, diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index b180df5c4837..d47c6821ce2c 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -34,7 +34,6 @@ struct rga_frame { /* Variables that can calculated once and reused */ u32 stride; - u32 size; }; struct rga_dma_desc { From 918c4589a0e2bb7338b556c28ad5cb36e8e27795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:28 +0200 Subject: [PATCH 329/394] media: rockchip: rga: remove stride from rga_frame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the stride variable from rga_frame. Despite the comment it didn't involve any calculation and is just a copy of the plane_fmt[0].bytesperline value. Therefore avoid this struct member and use the bytesperline value directly in the places where it is required. Also drop the dependency on the depth format member, which was only used to calculate the stride of the default format. This is already done by the v4l2_fill_pixfmt_mp_aligned helper and used as stride in try_fmt. Therefore using it's value also for the default format stride is just more consistent. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 36 +++++++------------- drivers/media/platform/rockchip/rga/rga.c | 5 +-- drivers/media/platform/rockchip/rga/rga.h | 4 --- 3 files changed, 13 insertions(+), 32 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 73584706a47e..fac3975e2d0c 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -44,7 +44,7 @@ rga_get_corner_addrs(struct rga_frame *frm, struct rga_addrs *addrs, struct rga_addrs *lt, *lb, *rt, *rb; const struct v4l2_format_info *format_info; unsigned int x_div = 0, - y_div = 0, uv_stride = 0, pixel_width = 0; + y_div = 0, y_stride = 0, uv_stride = 0, pixel_width = 0; lt = &corner_addrs.left_top; lb = &corner_addrs.left_bottom; @@ -61,14 +61,15 @@ rga_get_corner_addrs(struct rga_frame *frm, struct rga_addrs *addrs, else x_div = 1; y_div = format_info->vdiv; - uv_stride = frm->stride / x_div; - pixel_width = frm->stride / frm->pix.width; + y_stride = frm->pix.plane_fmt[0].bytesperline; + uv_stride = y_stride / x_div; + pixel_width = y_stride / frm->pix.width; - lt->y_addr = addrs->y_addr + y * frm->stride + x * pixel_width; + lt->y_addr = addrs->y_addr + y * y_stride + x * pixel_width; lt->u_addr = addrs->u_addr + (y / y_div) * uv_stride + x / x_div; lt->v_addr = addrs->v_addr + (y / y_div) * uv_stride + x / x_div; - lb->y_addr = lt->y_addr + (h - 1) * frm->stride; + lb->y_addr = lt->y_addr + (h - 1) * y_stride; lb->u_addr = lt->u_addr + (h / y_div - 1) * uv_stride; lb->v_addr = lt->v_addr + (h / y_div - 1) * uv_stride; @@ -169,6 +170,7 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) union rga_src_act_info src_act_info; union rga_dst_vir_info dst_vir_info; union rga_dst_act_info dst_act_info; + u32 in_stride, out_stride; src_h = ctx->in.crop.height; src_w = ctx->in.crop.width; @@ -291,13 +293,15 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) * Calculate the framebuffer virtual strides and active size, * note that the step of vir_stride / vir_width is 4 byte words */ - src_vir_info.data.vir_stride = ctx->in.stride >> 2; - src_vir_info.data.vir_width = ctx->in.stride >> 2; + in_stride = ctx->in.pix.plane_fmt[0].bytesperline; + src_vir_info.data.vir_stride = in_stride >> 2; + src_vir_info.data.vir_width = in_stride >> 2; src_act_info.data.act_height = src_h - 1; src_act_info.data.act_width = src_w - 1; - dst_vir_info.data.vir_stride = ctx->out.stride >> 2; + out_stride = ctx->out.pix.plane_fmt[0].bytesperline; + dst_vir_info.data.vir_stride = out_stride >> 2; dst_act_info.data.act_height = dst_h - 1; dst_act_info.data.act_width = dst_w - 1; @@ -481,97 +485,81 @@ static struct rga_fmt formats[] = { .fourcc = V4L2_PIX_FMT_ARGB32, .color_swap = RGA_COLOR_ALPHA_SWAP, .hw_format = RGA_COLOR_FMT_ABGR8888, - .depth = 32, }, { .fourcc = V4L2_PIX_FMT_ABGR32, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR8888, - .depth = 32, }, { .fourcc = V4L2_PIX_FMT_XBGR32, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_XBGR8888, - .depth = 32, }, { .fourcc = V4L2_PIX_FMT_RGB24, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_RGB888, - .depth = 24, }, { .fourcc = V4L2_PIX_FMT_BGR24, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_RGB888, - .depth = 24, }, { .fourcc = V4L2_PIX_FMT_ARGB444, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR4444, - .depth = 16, }, { .fourcc = V4L2_PIX_FMT_ARGB555, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_ABGR1555, - .depth = 16, }, { .fourcc = V4L2_PIX_FMT_RGB565, .color_swap = RGA_COLOR_RB_SWAP, .hw_format = RGA_COLOR_FMT_BGR565, - .depth = 16, }, { .fourcc = V4L2_PIX_FMT_NV21, .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, }, { .fourcc = V4L2_PIX_FMT_NV61, .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV422SP, - .depth = 16, }, { .fourcc = V4L2_PIX_FMT_NV12, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, }, { .fourcc = V4L2_PIX_FMT_NV12M, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420SP, - .depth = 12, }, { .fourcc = V4L2_PIX_FMT_NV16, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV422SP, - .depth = 16, }, { .fourcc = V4L2_PIX_FMT_YUV420, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV420P, - .depth = 12, }, { .fourcc = V4L2_PIX_FMT_YUV422P, .color_swap = RGA_COLOR_NONE_SWAP, .hw_format = RGA_COLOR_FMT_YUV422P, - .depth = 16, }, { .fourcc = V4L2_PIX_FMT_YVU420, .color_swap = RGA_COLOR_UV_SWAP, .hw_format = RGA_COLOR_FMT_YUV420P, - .depth = 12, }, }; diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index e44e81814b7c..e52d0577b236 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -267,8 +267,6 @@ static int rga_open(struct file *file) .fmt = &rga->hw->formats[0], }; - def_frame.stride = (def_width * def_frame.fmt->depth) >> 3; - ctx = kzalloc_obj(*ctx); if (!ctx) return -ENOMEM; @@ -481,7 +479,6 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) if (IS_ERR(frm)) return PTR_ERR(frm); frm->fmt = rga_fmt_find(rga, pix_fmt->pixelformat); - frm->stride = pix_fmt->plane_fmt[0].bytesperline; /* * Copy colorimetry from output to capture as required by the @@ -507,7 +504,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) "[%s] fmt - %p4cc %dx%d (stride %d)\n", V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE", &frm->fmt->fourcc, pix_fmt->width, pix_fmt->height, - frm->stride); + pix_fmt->plane_fmt[0].bytesperline); for (i = 0; i < pix_fmt->num_planes; i++) { v4l2_dbg(debug, 1, &rga->v4l2_dev, diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index d47c6821ce2c..227ab6c0532c 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -19,7 +19,6 @@ struct rga_fmt { u32 fourcc; - int depth; u8 color_swap; u8 hw_format; }; @@ -31,9 +30,6 @@ struct rga_frame { /* Image format */ struct rga_fmt *fmt; struct v4l2_pix_format_mplane pix; - - /* Variables that can calculated once and reused */ - u32 stride; }; struct rga_dma_desc { From d8427bb2efbb301632247ae15878d0e6fad41a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:29 +0200 Subject: [PATCH 330/394] media: rockchip: rga: move rga_fmt to rga-hw.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move rga_fmt to rga-hw in preparation of the RGA3 addition, as the struct contains many RGA2 specific values. They are used to write the correct register values quickly based on the chosen format. Therefore the pointer to the rga_fmt struct is kept but changed to an opaque void pointer outside of the rga-hw.h. To enumerate and set the correct formats, two helper functions need to be exposed in the rga_hw struct: enum_format just get's the vidioc_enum_fmt format and it's return value is also returned from vidioc_enum_fmt. This is a simple pass-through, as the implementation is very simple. adjust_and_map_format is a simple abstraction around the previous rga_find_format. But unlike rga_find_format, it always returns a valid format. Therefore the passed format value is also a pointer to update it in case the values are not supported by the hardware. Due to the RGA3 supporting different formats on the capture and output side, an additional parameter is_capture has been added to support this use-case. The additional ctx parameter is also added to allow the RGA3 to limit the colorimetry only if an RGB<->YCrCb transformation happens. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-buf.c | 2 +- drivers/media/platform/rockchip/rga/rga-hw.c | 49 +++++++++++++++---- drivers/media/platform/rockchip/rga/rga-hw.h | 8 +++ drivers/media/platform/rockchip/rga/rga.c | 41 ++++++---------- drivers/media/platform/rockchip/rga/rga.h | 14 ++---- 5 files changed, 67 insertions(+), 47 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index 092c2bdf6b67..c0ea6003336b 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -162,7 +162,7 @@ static int rga_buf_prepare(struct vb2_buffer *vb) } /* Fill the remaining planes */ - info = v4l2_format_info(f->fmt->fourcc); + info = v4l2_format_info(f->pix.pixelformat); for (i = info->mem_planes; i < info->comp_planes; i++) dma_addrs[i] = dma_addrs[0] + get_plane_offset(f, info, i); diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index fac3975e2d0c..616ea1bc1131 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -171,6 +171,8 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) union rga_dst_vir_info dst_vir_info; union rga_dst_act_info dst_act_info; u32 in_stride, out_stride; + struct rga_fmt *in_fmt = ctx->in.fmt; + struct rga_fmt *out_fmt = ctx->out.fmt; src_h = ctx->in.crop.height; src_w = ctx->in.crop.width; @@ -186,18 +188,18 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) dst_vir_info.val = dest[(RGA_DST_VIR_INFO - RGA_MODE_BASE_REG) >> 2]; dst_act_info.val = dest[(RGA_DST_ACT_INFO - RGA_MODE_BASE_REG) >> 2]; - src_info.data.format = ctx->in.fmt->hw_format; - src_info.data.swap = ctx->in.fmt->color_swap; - dst_info.data.format = ctx->out.fmt->hw_format; - dst_info.data.swap = ctx->out.fmt->color_swap; + src_info.data.format = in_fmt->hw_format; + src_info.data.swap = in_fmt->color_swap; + dst_info.data.format = out_fmt->hw_format; + dst_info.data.swap = out_fmt->color_swap; /* * CSC mode must only be set when the colorspace families differ between * input and output. It must remain unset (zeroed) if both are the same. */ - if (RGA_COLOR_FMT_IS_YUV(ctx->in.fmt->hw_format) && - RGA_COLOR_FMT_IS_RGB(ctx->out.fmt->hw_format)) { + if (RGA_COLOR_FMT_IS_YUV(in_fmt->hw_format) && + RGA_COLOR_FMT_IS_RGB(out_fmt->hw_format)) { switch (ctx->in.pix.colorspace) { case V4L2_COLORSPACE_REC709: src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; @@ -208,8 +210,8 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) } } - if (RGA_COLOR_FMT_IS_RGB(ctx->in.fmt->hw_format) && - RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) { + if (RGA_COLOR_FMT_IS_RGB(in_fmt->hw_format) && + RGA_COLOR_FMT_IS_YUV(out_fmt->hw_format)) { switch (ctx->out.pix.colorspace) { case V4L2_COLORSPACE_REC709: dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; @@ -563,11 +565,36 @@ static struct rga_fmt formats[] = { }, }; +static void *rga_adjust_and_map_format(struct rga_ctx *ctx, + struct v4l2_pix_format_mplane *format, + bool is_output) +{ + unsigned int i; + + if (!format) + return &formats[0]; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].fourcc == format->pixelformat) + return &formats[i]; + } + + format->pixelformat = formats[0].fourcc; + return &formats[0]; +} + +static int rga_enum_format(struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(formats)) + return -EINVAL; + + f->pixelformat = formats[f->index].fourcc; + return 0; +} + const struct rga_hw rga2_hw = { .card_type = "rga2", .has_internal_iommu = true, - .formats = formats, - .num_formats = ARRAY_SIZE(formats), .cmdbuf_size = RGA_CMDBUF_SIZE, .min_width = MIN_WIDTH, .max_width = MAX_WIDTH, @@ -580,4 +607,6 @@ const struct rga_hw rga2_hw = { .start = rga_hw_start, .handle_irq = rga_handle_irq, .get_version = rga_get_version, + .adjust_and_map_format = rga_adjust_and_map_format, + .enum_format = rga_enum_format, }; diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index 805ec23e5e3f..14ffa5ebd453 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -6,6 +6,8 @@ #ifndef __RGA_HW_H__ #define __RGA_HW_H__ +#include + #define RGA_CMDBUF_SIZE 0x80 /* Hardware limits */ @@ -431,4 +433,10 @@ union rga_pat_con { } data; }; +struct rga_fmt { + u32 fourcc; + u8 color_swap; + u8 hw_format; +}; + #endif diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index e52d0577b236..1878b4e26360 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -232,17 +232,6 @@ int rga_check_scaling(const struct rga_hw *hw, const struct v4l2_rect *crop_in, return 0; } -static struct rga_fmt *rga_fmt_find(struct rockchip_rga *rga, u32 pixelformat) -{ - unsigned int i; - - for (i = 0; i < rga->hw->num_formats; i++) { - if (rga->hw->formats[i].fourcc == pixelformat) - return &rga->hw->formats[i]; - } - return NULL; -} - struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type) { if (V4L2_TYPE_IS_OUTPUT(type)) @@ -264,7 +253,6 @@ static int rga_open(struct file *file) .crop.top = 0, .crop.width = def_width, .crop.height = def_height, - .fmt = &rga->hw->formats[0], }; ctx = kzalloc_obj(*ctx); @@ -286,9 +274,12 @@ static int rga_open(struct file *file) ctx->in = def_frame; ctx->out = def_frame; - v4l2_fill_pixfmt_mp_aligned(&ctx->in.pix, ctx->in.fmt->fourcc, + ctx->in.fmt = rga->hw->adjust_and_map_format(ctx, &ctx->in.pix, true); + v4l2_fill_pixfmt_mp_aligned(&ctx->in.pix, ctx->in.pix.pixelformat, def_width, def_height, rga->hw->stride_alignment); - v4l2_fill_pixfmt_mp_aligned(&ctx->out.pix, ctx->out.fmt->fourcc, + ctx->out.fmt = + rga->hw->adjust_and_map_format(ctx, &ctx->out.pix, false); + v4l2_fill_pixfmt_mp_aligned(&ctx->out.pix, ctx->out.pix.pixelformat, def_width, def_height, rga->hw->stride_alignment); if (mutex_lock_interruptible(&rga->mutex)) { @@ -370,13 +361,11 @@ vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct rockchip_rga *rga = video_drvdata(file); - struct rga_fmt *fmt; + int ret; - if (f->index >= rga->hw->num_formats) - return -EINVAL; - - fmt = &rga->hw->formats[f->index]; - f->pixelformat = fmt->fourcc; + ret = rga->hw->enum_format(f); + if (ret != 0) + return ret; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) @@ -411,7 +400,6 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp; struct rga_ctx *ctx = file_to_rga_ctx(file); const struct rga_hw *hw = ctx->rga->hw; - struct rga_fmt *fmt; struct v4l2_frmsize_stepwise frmsize = { .min_width = hw->min_width, .max_width = hw->max_width, @@ -442,12 +430,10 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) pix_fmt->xfer_func = frm->pix.xfer_func; } - fmt = rga_fmt_find(ctx->rga, pix_fmt->pixelformat); - if (!fmt) - fmt = &hw->formats[0]; + hw->adjust_and_map_format(ctx, pix_fmt, V4L2_TYPE_IS_OUTPUT(f->type)); v4l2_apply_frmsize_constraints(&pix_fmt->width, &pix_fmt->height, &frmsize); - v4l2_fill_pixfmt_mp_aligned(pix_fmt, fmt->fourcc, + v4l2_fill_pixfmt_mp_aligned(pix_fmt, pix_fmt->pixelformat, pix_fmt->width, pix_fmt->height, hw->stride_alignment); pix_fmt->field = V4L2_FIELD_NONE; @@ -478,7 +464,8 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) frm = rga_get_frame(ctx, f->type); if (IS_ERR(frm)) return PTR_ERR(frm); - frm->fmt = rga_fmt_find(rga, pix_fmt->pixelformat); + frm->fmt = rga->hw->adjust_and_map_format(ctx, pix_fmt, + V4L2_TYPE_IS_OUTPUT(f->type)); /* * Copy colorimetry from output to capture as required by the @@ -503,7 +490,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) v4l2_dbg(debug, 1, &rga->v4l2_dev, "[%s] fmt - %p4cc %dx%d (stride %d)\n", V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE", - &frm->fmt->fourcc, pix_fmt->width, pix_fmt->height, + &pix_fmt->pixelformat, pix_fmt->width, pix_fmt->height, pix_fmt->plane_fmt[0].bytesperline); for (i = 0; i < pix_fmt->num_planes; i++) { diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 227ab6c0532c..effe364a86b0 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -17,18 +17,12 @@ #define DEFAULT_WIDTH 100 #define DEFAULT_HEIGHT 100 -struct rga_fmt { - u32 fourcc; - u8 color_swap; - u8 hw_format; -}; - struct rga_frame { /* Crop */ struct v4l2_rect crop; /* Image format */ - struct rga_fmt *fmt; + void *fmt; struct v4l2_pix_format_mplane pix; }; @@ -146,8 +140,6 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask) struct rga_hw { const char *card_type; bool has_internal_iommu; - struct rga_fmt *formats; - u32 num_formats; size_t cmdbuf_size; u32 min_width, min_height; u32 max_width, max_height; @@ -159,6 +151,10 @@ struct rga_hw { struct rga_vb_buffer *src, struct rga_vb_buffer *dst); bool (*handle_irq)(struct rockchip_rga *rga); void (*get_version)(struct rockchip_rga *rga); + void *(*adjust_and_map_format)(struct rga_ctx *ctx, + struct v4l2_pix_format_mplane *format, + bool is_output); + int (*enum_format)(struct v4l2_fmtdesc *f); }; static inline bool rga_has_internal_iommu(const struct rockchip_rga *rga) From ded2efe97dcf6d888878bb99174f9484dd81c928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:30 +0200 Subject: [PATCH 331/394] media: rockchip: rga: add feature flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to the RGA3 addition add feature flags, which can limit the exposed feature set of the video device, like rotating or selection support. This is necessary as the RGA3 doesn't initially implement the full feature set currently exposed by the driver. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-hw.c | 3 +++ drivers/media/platform/rockchip/rga/rga.c | 20 ++++++++++++-------- drivers/media/platform/rockchip/rga/rga.h | 6 ++++++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 616ea1bc1131..be1bc8ddbd03 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -602,6 +602,9 @@ const struct rga_hw rga2_hw = { .max_height = MAX_HEIGHT, .max_scaling_factor = MAX_SCALING_FACTOR, .stride_alignment = 4, + .features = RGA_FEATURE_FLIP + | RGA_FEATURE_ROTATE + | RGA_FEATURE_BG_COLOR, .setup_cmdbuf = rga_hw_setup_cmdbuf, .start = rga_hw_start, diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 1878b4e26360..8d60e94da32d 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -177,17 +177,21 @@ static int rga_setup_ctrls(struct rga_ctx *ctx) v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4); - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); + if (rga->hw->features & RGA_FEATURE_FLIP) { + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + } - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, - V4L2_CID_ROTATE, 0, 270, 90, 0); + if (rga->hw->features & RGA_FEATURE_ROTATE) + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, + V4L2_CID_ROTATE, 0, 270, 90, 0); - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, - V4L2_CID_BG_COLOR, 0, 0xffffffff, 1, 0); + if (rga->hw->features & RGA_FEATURE_BG_COLOR) + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops, + V4L2_CID_BG_COLOR, 0, 0xffffffff, 1, 0); if (ctx->ctrl_handler.error) { int err = ctx->ctrl_handler.error; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index effe364a86b0..feaf40acd4ee 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -6,6 +6,7 @@ #ifndef __RGA_H__ #define __RGA_H__ +#include #include #include #include @@ -137,6 +138,10 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask) rga_write(rga, reg, temp); }; +#define RGA_FEATURE_FLIP BIT(0) +#define RGA_FEATURE_ROTATE BIT(1) +#define RGA_FEATURE_BG_COLOR BIT(2) + struct rga_hw { const char *card_type; bool has_internal_iommu; @@ -145,6 +150,7 @@ struct rga_hw { u32 max_width, max_height; u8 max_scaling_factor; u8 stride_alignment; + u8 features; void (*setup_cmdbuf)(struct rga_ctx *ctx); void (*start)(struct rockchip_rga *rga, From bf0ee2589053b103426e1179b87f83ea512264df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:31 +0200 Subject: [PATCH 332/394] media: rockchip: rga: disable multi-core support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable multi-core support in preparation of the RGA3 addition. The RK3588 SoC features two equal RGA3 cores. This allows scheduling of the work between both cores, which is not yet implemented. Until it is implemented avoid exposing both cores as independent video devices to prevent an ABI breakage when multi-core support is added. This patch is copied from the Hantro driver patch to disable multi core support by Sebastian Reichel. See commit ccdeb8d57f7f ("media: hantro: Disable multicore support") Link: https://lore.kernel.org/all/20240618183816.77597-4-sebastian.reichel@collabora.com/ Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga.c | 47 +++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 8d60e94da32d..0152b8ef2da2 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -711,6 +711,49 @@ static int rga_parse_dt(struct rockchip_rga *rga) return 0; } +/* + * Some SoCs, like RK3588 have multiple identical RGA3 cores, but the + * kernel is currently missing support for multi-core handling. Exposing + * separate devices for each core to userspace is bad, since that does + * not allow scheduling tasks properly (and creates ABI). With this workaround + * the driver will only probe for the first core and early exit for the other + * cores. Once the driver gains multi-core support, the same technique + * for detecting the main core can be used to cluster all cores together. + */ +static int rga_disable_multicore(struct device *dev) +{ + struct device_node *node = NULL; + const char *compatible; + bool is_main_core; + int ret; + + /* Intentionally ignores the fallback strings */ + ret = of_property_read_string(dev->of_node, "compatible", &compatible); + if (ret) + return ret; + + /* The first compatible and available node found is considered the main core */ + do { + node = of_find_compatible_node(node, NULL, compatible); + if (of_device_is_available(node)) + break; + } while (node); + + if (!node) + return -EINVAL; + + is_main_core = (dev->of_node == node); + + of_node_put(node); + + if (!is_main_core) { + dev_info(dev, "missing multi-core support, ignoring this instance\n"); + return -ENODEV; + } + + return 0; +} + static int rga_probe(struct platform_device *pdev) { struct rockchip_rga *rga; @@ -721,6 +764,10 @@ static int rga_probe(struct platform_device *pdev) if (!pdev->dev.of_node) return -ENODEV; + ret = rga_disable_multicore(&pdev->dev); + if (ret) + return ret; + rga = devm_kzalloc(&pdev->dev, sizeof(*rga), GFP_KERNEL); if (!rga) return -ENOMEM; From 24a63d4c9d3ce3afc99f698cff62d79c457fc5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20P=C3=BCschel?= Date: Thu, 21 May 2026 00:44:32 +0200 Subject: [PATCH 333/394] media: rockchip: rga: add rga3 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the RGA3 unit contained in the RK3588. Only a basic feature set consisting of scaling and color conversion is implemented. Currently unimplemented features include: - Advanced formats like 10bit YUV, FBCE mode and Tile8x8 mode - Background color (V4L2_CID_BG_COLOR) - Configurable alpha value (V4L2_CID_ALPHA_COMPONENT) - Image flipping (V4L2_CID_HFLIP and V4L2_CID_VFLIP) - Image rotation (V4L2_CID_ROTATE) - Image cropping/composing (VIDIOC_S_SELECTION) - Only very basic output cropping for 1088 -> 1080 cases is implemented The register address defines were copied from the vendor Rockchip kernel sources and slightly adjusted to not start at 0 again for the cmd registers. During testing it has been noted that the scaling of the hardware is slightly incorrect. A test conversion of 128x128 RGBA to 256x256 RGBA causes a slightly larger scaling. The scaling is suddle, as it seems that the image is scaled to a 2px larger version and then cropped to it's final size. Trying to use the RGA2 scaling factor calculation didn't work. As the calculation matches the vendor kernel driver, no further research has been utilized to check if there may be some kind of better scaling factor calculation. Furthermore comparing the RGA3 conversion with the GStreamer videoconvertscale element, the chroma-site is different. A quick testing didn't reveal a chroma-site that creates the same image with the GStreamer Element. Also when converting from YUV to RGB the RGB values differ by 1 or 2. This doesn't seem to be a colorspace conversion issue but rather a slightly different precision on the calculation. Reviewed-by: Nicolas Dufresne Signed-off-by: Sven Püschel Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/Makefile | 2 +- drivers/media/platform/rockchip/rga/rga.c | 4 + drivers/media/platform/rockchip/rga/rga.h | 1 + drivers/media/platform/rockchip/rga/rga3-hw.c | 507 ++++++++++++++++++ drivers/media/platform/rockchip/rga/rga3-hw.h | 192 +++++++ 5 files changed, 705 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/rockchip/rga/rga3-hw.c create mode 100644 drivers/media/platform/rockchip/rga/rga3-hw.h diff --git a/drivers/media/platform/rockchip/rga/Makefile b/drivers/media/platform/rockchip/rga/Makefile index 1bbecdc3d8df..7326a548f3dc 100644 --- a/drivers/media/platform/rockchip/rga/Makefile +++ b/drivers/media/platform/rockchip/rga/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -rockchip-rga-objs := rga.o rga-hw.o rga-buf.o +rockchip-rga-objs := rga.o rga-hw.o rga3-hw.o rga-buf.o obj-$(CONFIG_VIDEO_ROCKCHIP_RGA) += rockchip-rga.o diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 0152b8ef2da2..b3cb6bf8eb86 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -913,6 +913,10 @@ static const struct of_device_id rockchip_rga_match[] = { .compatible = "rockchip,rk3399-rga", .data = &rga2_hw, }, + { + .compatible = "rockchip,rk3588-rga3", + .data = &rga3_hw, + }, {}, }; diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index feaf40acd4ee..bd431534d0d3 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -169,5 +169,6 @@ static inline bool rga_has_internal_iommu(const struct rockchip_rga *rga) } extern const struct rga_hw rga2_hw; +extern const struct rga_hw rga3_hw; #endif diff --git a/drivers/media/platform/rockchip/rga/rga3-hw.c b/drivers/media/platform/rockchip/rga/rga3-hw.c new file mode 100644 index 000000000000..ca1c268303dd --- /dev/null +++ b/drivers/media/platform/rockchip/rga/rga3-hw.c @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025-2026 Pengutronix e.K. + * Author: Sven Püschel + */ + +#include +#include +#include +#include + +#include + +#include "rga3-hw.h" +#include "rga.h" + +static unsigned int rga3_get_scaling(unsigned int src, unsigned int dst) +{ + /* + * RGA3 scaling factor calculation as described in chapter 5.4.7 Resize + * of the TRM Part 2. The resulting scaling factor is a 16-bit value + * and therefore normalized with 2^16. + * + * While the TRM also mentions (dst-1)/(src-1) for the up-scaling case, + * it didn't work as the value always exceeds 16 bit. Flipping the + * factors results in a correct up-scaling. This is possible as the + * RGA3 has the RGA3_WIN_SCALE_XXX_UP bit to determine if it does + * an up or downscale. + * + * The scaling factor can potentially cause a slightly larger scaling + * (e.g. 1/2px larger scale and then cropped to the destination size). + * This can be seen when scaling 128x128px RGBA to 256x256px RGBA. + * The RGA2 scaling factor calculation (without the various +/-1 + * doesn't work for the RGA3. It's assumed that this is an hardware + * accuracy limitation, as the vendor kernel driver uses the same + * scaling factor calculation. + * + * With a scaling factor of 1.0 the calculation technically also + * overflows 16 bit. This isn't relevant, as in this case the + * RGA3_WIN_SCALE_XXX_BYPASS bit completely skips the scaling operation. + */ + if (dst > src) { + if (((src - 1) << 16) % (dst - 1) == 0) + return ((src - 1) << 16) / (dst - 1) - 1; + else + return ((src - 1) << 16) / (dst - 1); + } else { + return ((dst - 1) << 16) / (src - 1) + 1; + } +} + +/* + * Check if the given format can be captured, as the RGA3 doesn't support all + * input formats also on it's output. + */ +static bool rga3_can_capture(const struct rga3_fmt *fmt) +{ + return fmt->hw_format <= RGA3_COLOR_FMT_LAST_OUTPUT; +} + +/* + * Map the transformations to the RGA3 command buffer. + * Currently this is just the scaling settings and a fixed alpha value. + */ +static void rga3_cmd_set_trans_info(struct rga_ctx *ctx) +{ + u32 *cmd = ctx->cmdbuf_virt; + unsigned int src_h, src_w, dst_h, dst_w; + unsigned int reg; + u16 hor_scl_fac, ver_scl_fac; + const struct rga3_fmt *in = ctx->in.fmt; + + /* Support basic input cropping to support 1088px inputs */ + src_h = ctx->in.crop.height; + src_w = ctx->in.crop.width; + dst_h = ctx->out.pix.height; + dst_w = ctx->out.pix.width; + + reg = RGA3_WIN0_RD_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] |= FIELD_PREP(RGA3_WIN_SCALE_HOR_UP, dst_w > src_w) + | FIELD_PREP(RGA3_WIN_SCALE_HOR_BYPASS, dst_w == src_w) + | FIELD_PREP(RGA3_WIN_SCALE_VER_UP, dst_h > src_h) + | FIELD_PREP(RGA3_WIN_SCALE_VER_BYPASS, dst_h == src_h); + + hor_scl_fac = rga3_get_scaling(src_w, dst_w); + ver_scl_fac = rga3_get_scaling(src_h, dst_h); + reg = RGA3_WIN0_SCL_FAC - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_SCALE_HOR_FAC, hor_scl_fac) + | FIELD_PREP(RGA3_SCALE_VER_FAC, ver_scl_fac); + + if (v4l2_format_info(in->fourcc)->has_alpha) { + /* copy alpha from input */ + reg = RGA3_OVLP_TOP_ALPHA - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_ALPHA_SELECT_MODE, 1) + | FIELD_PREP(RGA3_ALPHA_BLEND_MODE, 1); + reg = RGA3_OVLP_BOT_ALPHA - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_ALPHA_SELECT_MODE, 1) + | FIELD_PREP(RGA3_ALPHA_BLEND_MODE, 1); + } else { + /* just use a 255 alpha value */ + reg = RGA3_OVLP_TOP_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_OVLP_GLOBAL_ALPHA, 0xff) + | FIELD_PREP(RGA3_OVLP_COLOR_MODE, 1); + reg = RGA3_OVLP_BOT_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_OVLP_GLOBAL_ALPHA, 0xff) + | FIELD_PREP(RGA3_OVLP_COLOR_MODE, 1); + } +} + +static void rga3_cmd_set_win0_addr(struct rga_ctx *ctx, + const struct rga_addrs *addrs) +{ + u32 *cmd = ctx->cmdbuf_virt; + unsigned int reg; + + reg = RGA3_WIN0_Y_BASE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = addrs->y_addr; + reg = RGA3_WIN0_U_BASE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = addrs->u_addr; +} + +static void rga3_cmd_set_wr_addr(struct rga_ctx *ctx, + const struct rga_addrs *addrs) +{ + u32 *cmd = ctx->cmdbuf_virt; + unsigned int reg; + + reg = RGA3_WR_Y_BASE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = addrs->y_addr; + reg = RGA3_WR_U_BASE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = addrs->u_addr; +} + +/* Map the input pixel format to win0 of the comamnd buffer. */ +static void rga3_cmd_set_win0_format(struct rga_ctx *ctx) +{ + u32 *cmd = ctx->cmdbuf_virt; + const struct rga3_fmt *in = ctx->in.fmt; + const struct rga3_fmt *out = ctx->out.fmt; + const struct v4l2_format_info *in_fmt, *out_fmt; + unsigned int act_h, act_w, src_h, src_w; + bool r2y, y2r; + u8 rd_format; + const struct v4l2_pix_format_mplane *csc_pix; + u8 csc_mode; + unsigned int reg; + + act_h = ctx->in.pix.height; + act_w = ctx->in.pix.width; + /* Support basic input cropping to support 1088px inputs */ + src_h = ctx->in.crop.height; + src_w = ctx->in.crop.width; + + in_fmt = v4l2_format_info(in->fourcc); + out_fmt = v4l2_format_info(out->fourcc); + r2y = v4l2_is_format_rgb(in_fmt) && v4l2_is_format_yuv(out_fmt); + y2r = v4l2_is_format_yuv(in_fmt) && v4l2_is_format_rgb(out_fmt); + + /* The Hardware only supports formats with 1/2 planes */ + if (in_fmt->comp_planes == 2) + rd_format = RGA3_RDWR_FORMAT_SEMI_PLANAR; + else + rd_format = RGA3_RDWR_FORMAT_INTERLEAVED; + + /* set pixel format and CSC */ + csc_pix = r2y ? &ctx->out.pix : &ctx->in.pix; + switch (csc_pix->ycbcr_enc) { + case V4L2_YCBCR_ENC_BT2020: + csc_mode = RGA3_WIN_CSC_MODE_BT2020_L; + break; + case V4L2_YCBCR_ENC_709: + csc_mode = RGA3_WIN_CSC_MODE_BT709_L; + break; + default: /* should be fixed to BT601 in adjust_and_map_format */ + if (csc_pix->quantization == V4L2_QUANTIZATION_LIM_RANGE) + csc_mode = RGA3_WIN_CSC_MODE_BT601_L; + else + csc_mode = RGA3_WIN_CSC_MODE_BT601_F; + break; + } + + reg = RGA3_WIN0_RD_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] |= FIELD_PREP(RGA3_WIN_ENABLE, 1) + | FIELD_PREP(RGA3_WIN_PIC_FORMAT, in->hw_format) + | FIELD_PREP(RGA3_WIN_YC_SWAP, in->yc_swap) + | FIELD_PREP(RGA3_WIN_RBUV_SWAP, in->rbuv_swap) + | FIELD_PREP(RGA3_WIN_RD_FORMAT, rd_format) + | FIELD_PREP(RGA3_WIN_R2Y, r2y) + | FIELD_PREP(RGA3_WIN_Y2R, y2r) + | FIELD_PREP(RGA3_WIN_CSC_MODE, csc_mode); + + /* set stride */ + reg = RGA3_WIN0_VIR_STRIDE - RGA3_FIRST_CMD_REG; + /* stride needs to be in words */ + cmd[reg >> 2] = ctx->in.pix.plane_fmt[0].bytesperline >> 2; + reg = RGA3_WIN0_UV_VIR_STRIDE - RGA3_FIRST_CMD_REG; + /* The Hardware only supports formats with 1/2 planes */ + if (ctx->in.pix.num_planes == 2) + cmd[reg >> 2] = ctx->in.pix.plane_fmt[1].bytesperline >> 2; + else + cmd[reg >> 2] = ctx->in.pix.plane_fmt[0].bytesperline >> 2; + + /* set size */ + reg = RGA3_WIN0_ACT_SIZE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, act_w) + | FIELD_PREP(RGA3_HEIGHT, act_h); + reg = RGA3_WIN0_SRC_SIZE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, src_w) + | FIELD_PREP(RGA3_HEIGHT, src_h); +} + +/* Map the output pixel format to the command buffer */ +static void rga3_cmd_set_wr_format(struct rga_ctx *ctx) +{ + u32 *cmd = ctx->cmdbuf_virt; + const struct rga3_fmt *out = ctx->out.fmt; + const struct v4l2_format_info *out_fmt; + unsigned int dst_h, dst_w; + u8 wr_format; + unsigned int reg; + + dst_h = ctx->out.pix.height; + dst_w = ctx->out.pix.width; + + out_fmt = v4l2_format_info(out->fourcc); + + /* The Hardware only supports formats with 1/2 planes */ + if (out_fmt->comp_planes == 2) + wr_format = RGA3_RDWR_FORMAT_SEMI_PLANAR; + else + wr_format = RGA3_RDWR_FORMAT_INTERLEAVED; + + /* set pixel format */ + reg = RGA3_WR_CTRL - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_WR_PIC_FORMAT, out->hw_format) + | FIELD_PREP(RGA3_WR_YC_SWAP, out->yc_swap) + | FIELD_PREP(RGA3_WR_RBUV_SWAP, out->rbuv_swap) + | FIELD_PREP(RGA3_WR_FORMAT, wr_format) + /* Use the max value to avoid limiting the write speed */ + | FIELD_PREP(RGA3_WR_SW_OUTSTANDING_MAX, 63); + + /* set stride */ + reg = RGA3_WR_VIR_STRIDE - RGA3_FIRST_CMD_REG; + /* stride needs to be in words */ + cmd[reg >> 2] = ctx->out.pix.plane_fmt[0].bytesperline >> 2; + reg = RGA3_WR_PL_VIR_STRIDE - RGA3_FIRST_CMD_REG; + /* The Hardware only supports formats with 1/2 planes */ + if (ctx->out.pix.num_planes == 2) + cmd[reg >> 2] = ctx->out.pix.plane_fmt[1].bytesperline >> 2; + else + cmd[reg >> 2] = ctx->out.pix.plane_fmt[0].bytesperline >> 2; + + /* Set size. + * As two inputs are not supported, we don't use win1. + * Therefore only set the size for win0. + */ + reg = RGA3_WIN0_DST_SIZE - RGA3_FIRST_CMD_REG; + cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, dst_w) + | FIELD_PREP(RGA3_HEIGHT, dst_h); +} + +static void rga3_hw_setup_cmdbuf(struct rga_ctx *ctx) +{ + memset(ctx->cmdbuf_virt, 0, RGA3_CMDBUF_SIZE); + + rga3_cmd_set_win0_format(ctx); + rga3_cmd_set_trans_info(ctx); + rga3_cmd_set_wr_format(ctx); +} + +static void rga3_hw_start(struct rockchip_rga *rga, + struct rga_vb_buffer *src, struct rga_vb_buffer *dst) +{ + struct rga_ctx *ctx = rga->curr; + + rga3_cmd_set_win0_addr(ctx, &src->dma_addrs); + rga3_cmd_set_wr_addr(ctx, &dst->dma_addrs); + + rga_write(rga, RGA3_CMD_ADDR, ctx->cmdbuf_phy); + + /* sync CMD buf for RGA */ + dma_sync_single_for_device(rga->dev, ctx->cmdbuf_phy, + PAGE_SIZE, DMA_BIDIRECTIONAL); + + /* set to master mode and start the conversion */ + rga_write(rga, RGA3_SYS_CTRL, + FIELD_PREP(RGA3_CMD_MODE, RGA3_CMD_MODE_MASTER)); + rga_write(rga, RGA3_INT_EN, FIELD_PREP(RGA3_INT_FRM_DONE, 1)); + rga_write(rga, RGA3_CMD_CTRL, + FIELD_PREP(RGA3_CMD_LINE_START_PULSE, 1)); +} + +static bool rga3_handle_irq(struct rockchip_rga *rga) +{ + u32 intr; + + intr = rga_read(rga, RGA3_INT_RAW); + /* clear all interrupts */ + rga_write(rga, RGA3_INT_CLR, intr); + + return FIELD_GET(RGA3_INT_FRM_DONE, intr); +} + +static void rga3_get_version(struct rockchip_rga *rga) +{ + u32 version = rga_read(rga, RGA3_VERSION_NUM); + + rga->version.major = FIELD_GET(RGA3_VERSION_NUM_MAJOR, version); + rga->version.minor = FIELD_GET(RGA3_VERSION_NUM_MINOR, version); +} + +static struct rga3_fmt rga3_formats[] = { + { + .fourcc = V4L2_PIX_FMT_RGB24, + .hw_format = RGA3_COLOR_FMT_BGR888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, + .hw_format = RGA3_COLOR_FMT_BGR888, + }, + { + .fourcc = V4L2_PIX_FMT_ABGR32, + .hw_format = RGA3_COLOR_FMT_BGRA8888, + }, + { + .fourcc = V4L2_PIX_FMT_RGBA32, + .hw_format = RGA3_COLOR_FMT_BGRA8888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_XBGR32, + .hw_format = RGA3_COLOR_FMT_BGRA8888, + }, + { + .fourcc = V4L2_PIX_FMT_RGBX32, + .hw_format = RGA3_COLOR_FMT_BGRA8888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .hw_format = RGA3_COLOR_FMT_BGR565, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .hw_format = RGA3_COLOR_FMT_YUV420, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .hw_format = RGA3_COLOR_FMT_YUV420, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .hw_format = RGA3_COLOR_FMT_YUV420, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .hw_format = RGA3_COLOR_FMT_YUV420, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV16M, + .hw_format = RGA3_COLOR_FMT_YUV422, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .hw_format = RGA3_COLOR_FMT_YUV422, + }, + { + .fourcc = V4L2_PIX_FMT_NV61M, + .hw_format = RGA3_COLOR_FMT_YUV422, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_NV61, + .hw_format = RGA3_COLOR_FMT_YUV422, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .hw_format = RGA3_COLOR_FMT_YUV422, + .yc_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .hw_format = RGA3_COLOR_FMT_YUV422, + .yc_swap = 1, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .hw_format = RGA3_COLOR_FMT_YUV422, + }, + { + .fourcc = V4L2_PIX_FMT_VYUY, + .hw_format = RGA3_COLOR_FMT_YUV422, + .rbuv_swap = 1, + }, + /* Input only formats last to keep rga3_enum_format simple */ + { + .fourcc = V4L2_PIX_FMT_ARGB32, + .hw_format = RGA3_COLOR_FMT_ABGR8888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_BGRA32, + .hw_format = RGA3_COLOR_FMT_ABGR8888, + }, + { + .fourcc = V4L2_PIX_FMT_XRGB32, + .hw_format = RGA3_COLOR_FMT_ABGR8888, + .rbuv_swap = 1, + }, + { + .fourcc = V4L2_PIX_FMT_BGRX32, + .hw_format = RGA3_COLOR_FMT_ABGR8888, + }, +}; + +static int rga3_enum_format(struct v4l2_fmtdesc *f) +{ + struct rga3_fmt *fmt; + + if (f->index >= ARRAY_SIZE(rga3_formats)) + return -EINVAL; + + fmt = &rga3_formats[f->index]; + if (V4L2_TYPE_IS_CAPTURE(f->type) && !rga3_can_capture(fmt)) + return -EINVAL; + + f->pixelformat = fmt->fourcc; + return 0; +} + +static void *rga3_adjust_and_map_format(struct rga_ctx *ctx, + struct v4l2_pix_format_mplane *format, + bool is_output) +{ + unsigned int i; + const struct v4l2_format_info *format_info; + const struct v4l2_pix_format_mplane *other_format; + const struct v4l2_format_info *other_format_info; + + if (!format) + return &rga3_formats[0]; + + format_info = v4l2_format_info(format->pixelformat); + other_format = is_output ? &ctx->out.pix : &ctx->in.pix; + other_format_info = v4l2_format_info(other_format->pixelformat); + + if ((v4l2_is_format_rgb(format_info) && + v4l2_is_format_yuv(other_format_info)) || + (v4l2_is_format_yuv(format_info) && + v4l2_is_format_rgb(other_format_info))) { + /* + * The RGA3 only supports BT601, BT709 and BT2020 RGB<->YUV conversions + * Additionally BT709 and BT2020 only support limited range YUV. + */ + switch (format->ycbcr_enc) { + case V4L2_YCBCR_ENC_601: + /* supports full and limited range */ + break; + case V4L2_YCBCR_ENC_709: + case V4L2_YCBCR_ENC_BT2020: + format->quantization = V4L2_QUANTIZATION_LIM_RANGE; + break; + default: + format->ycbcr_enc = V4L2_YCBCR_ENC_601; + format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + break; + } + } + + for (i = 0; i < ARRAY_SIZE(rga3_formats); i++) { + if (!is_output && !rga3_can_capture(&rga3_formats[i])) + continue; + + if (rga3_formats[i].fourcc == format->pixelformat) + return &rga3_formats[i]; + } + + format->pixelformat = rga3_formats[0].fourcc; + return &rga3_formats[0]; +} + +const struct rga_hw rga3_hw = { + .card_type = "rga3", + .has_internal_iommu = false, + .cmdbuf_size = RGA3_CMDBUF_SIZE, + .min_width = RGA3_MIN_WIDTH, + .min_height = RGA3_MIN_HEIGHT, + /* use output size, as it's a bit smaller than the input size */ + .max_width = RGA3_MAX_OUTPUT_WIDTH, + .max_height = RGA3_MAX_OUTPUT_HEIGHT, + .max_scaling_factor = RGA3_MAX_SCALING_FACTOR, + .stride_alignment = 16, + .features = 0, + + .setup_cmdbuf = rga3_hw_setup_cmdbuf, + .start = rga3_hw_start, + .handle_irq = rga3_handle_irq, + .get_version = rga3_get_version, + .enum_format = rga3_enum_format, + .adjust_and_map_format = rga3_adjust_and_map_format, +}; diff --git a/drivers/media/platform/rockchip/rga/rga3-hw.h b/drivers/media/platform/rockchip/rga/rga3-hw.h new file mode 100644 index 000000000000..85fd8ae257ec --- /dev/null +++ b/drivers/media/platform/rockchip/rga/rga3-hw.h @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) Pengutronix e.K. + * Author: Sven Püschel + */ +#ifndef __RGA3_HW_H__ +#define __RGA3_HW_H__ + +#include +#include + +#define RGA3_CMDBUF_SIZE 0xb8 + +#define RGA3_MIN_WIDTH 128 +#define RGA3_MIN_HEIGHT 128 +#define RGA3_MAX_INPUT_WIDTH (8192 - 16) +#define RGA3_MAX_INPUT_HEIGHT (8192 - 16) +#define RGA3_MAX_OUTPUT_WIDTH (8192 - 64) +#define RGA3_MAX_OUTPUT_HEIGHT (8192 - 64) +#define RGA3_MAX_SCALING_FACTOR 8 +#define RGA3_RESET_TIMEOUT 1000 + +/* Registers address */ +/* sys reg */ +#define RGA3_SYS_CTRL 0x000 +#define RGA3_CMD_CTRL 0x004 +#define RGA3_CMD_ADDR 0x008 +#define RGA3_MI_GROUP_CTRL 0x00c +#define RGA3_ARQOS_CTRL 0x010 +#define RGA3_VERSION_NUM 0x018 +#define RGA3_VERSION_TIM 0x01c +#define RGA3_INT_EN 0x020 +#define RGA3_INT_RAW 0x024 +#define RGA3_INT_MSK 0x028 +#define RGA3_INT_CLR 0x02c +#define RGA3_RO_SRST 0x030 +#define RGA3_STATUS0 0x034 +#define RGA3_SCAN_CNT 0x038 +#define RGA3_CMD_STATE 0x040 + +/* cmd reg */ +#define RGA3_WIN0_RD_CTRL 0x100 +#define RGA3_FIRST_CMD_REG RGA3_WIN0_RD_CTRL +#define RGA3_WIN0_Y_BASE 0x110 +#define RGA3_WIN0_U_BASE 0x114 +#define RGA3_WIN0_V_BASE 0x118 +#define RGA3_WIN0_VIR_STRIDE 0x11c +#define RGA3_WIN0_FBC_OFF 0x120 +#define RGA3_WIN0_SRC_SIZE 0x124 +#define RGA3_WIN0_ACT_OFF 0x128 +#define RGA3_WIN0_ACT_SIZE 0x12c +#define RGA3_WIN0_DST_SIZE 0x130 +#define RGA3_WIN0_SCL_FAC 0x134 +#define RGA3_WIN0_UV_VIR_STRIDE 0x138 +#define RGA3_WIN1_RD_CTRL 0x140 +#define RGA3_WIN1_Y_BASE 0x150 +#define RGA3_WIN1_U_BASE 0x154 +#define RGA3_WIN1_V_BASE 0x158 +#define RGA3_WIN1_VIR_STRIDE 0x15c +#define RGA3_WIN1_FBC_OFF 0x160 +#define RGA3_WIN1_SRC_SIZE 0x164 +#define RGA3_WIN1_ACT_OFF 0x168 +#define RGA3_WIN1_ACT_SIZE 0x16c +#define RGA3_WIN1_DST_SIZE 0x170 +#define RGA3_WIN1_SCL_FAC 0x174 +#define RGA3_WIN1_UV_VIR_STRIDE 0x178 +#define RGA3_OVLP_CTRL 0x180 +#define RGA3_OVLP_OFF 0x184 +#define RGA3_OVLP_TOP_KEY_MIN 0x188 +#define RGA3_OVLP_TOP_KEY_MAX 0x18c +#define RGA3_OVLP_TOP_CTRL 0x190 +#define RGA3_OVLP_BOT_CTRL 0x194 +#define RGA3_OVLP_TOP_ALPHA 0x198 +#define RGA3_OVLP_BOT_ALPHA 0x19c +#define RGA3_WR_CTRL 0x1a0 +#define RGA3_WR_FBCE_CTRL 0x1a4 +#define RGA3_WR_VIR_STRIDE 0x1a8 +#define RGA3_WR_PL_VIR_STRIDE 0x1ac +#define RGA3_WR_Y_BASE 0x1b0 +#define RGA3_WR_U_BASE 0x1b4 +#define RGA3_WR_V_BASE 0x1b8 + +/* Registers value */ +#define RGA3_COLOR_FMT_YUV420 0x0 +#define RGA3_COLOR_FMT_YUV422 0x1 +#define RGA3_COLOR_FMT_YUV420_10B 0x2 +#define RGA3_COLOR_FMT_YUV422_10B 0x3 +/* + * Use memory ordering names + * instead of the datasheet naming RGB formats in big endian order + */ +#define RGA3_COLOR_FMT_BGR565 0x4 +#define RGA3_COLOR_FMT_BGR888 0x5 +#define RGA3_COLOR_FMT_FIRST_HAS_ALPHA RGA3_COLOR_FMT_BGRA8888 +#define RGA3_COLOR_FMT_BGRA8888 0x6 +#define RGA3_COLOR_FMT_LAST_OUTPUT RGA3_COLOR_FMT_BGRA8888 +/* the following are only supported as inputs */ +#define RGA3_COLOR_FMT_ABGR8888 0x7 +/* + * the following seem to be unnecessary, + * as they can be achieved with RB swaps + */ +#define RGA3_COLOR_FMT_RGBA8888 0x8 +#define RGA3_COLOR_FMT_ARGB8888 0x9 + +#define RGA3_RDWR_FORMAT_SEMI_PLANAR 0x1 +#define RGA3_RDWR_FORMAT_INTERLEAVED 0x2 + +#define RGA3_CMD_MODE_MASTER 0x1 + +#define RGA3_WIN_CSC_MODE_BT601_L 0x0 +#define RGA3_WIN_CSC_MODE_BT709_L 0x1 +#define RGA3_WIN_CSC_MODE_BT601_F 0x2 +#define RGA3_WIN_CSC_MODE_BT2020_L 0x3 + +/* RGA masks */ +/* SYS_CTRL */ +#define RGA3_CCLK_SRESET BIT(4) +#define RGA3_ACLK_SRESET BIT(3) +#define RGA3_CMD_MODE BIT(1) + +/* CMD_CTRL */ +#define RGA3_CMD_LINE_START_PULSE BIT(0) + +/* VERSION_NUM */ +#define RGA3_VERSION_NUM_MAJOR GENMASK(31, 28) +#define RGA3_VERSION_NUM_MINOR GENMASK(27, 20) + +/* INT_* */ +#define RGA3_INT_FRM_DONE BIT(0) +#define RGA3_INT_DMA_READ_BUS_ERR BIT(2) +#define RGA3_INT_WIN0_FBC_DEC_ERR BIT(5) +#define RGA3_INT_WIN0_HOR_ERR BIT(6) +#define RGA3_INT_WIN0_VER_ERR BIT(7) +#define RGA3_INT_WR_VER_ERR BIT(13) +#define RGA3_INT_WR_HOR_ERR BIT(14) +#define RGA3_INT_WR_BUS_ERR BIT(15) +#define RGA3_INT_WIN0_IN_FIFO_WR_ERR BIT(16) +#define RGA3_INT_WIN0_IN_FIFO_RD_ERR BIT(17) +#define RGA3_INT_WIN0_HOR_FIFO_WR_ERR BIT(18) +#define RGA3_INT_WIN0_HOR_FIFO_RD_ERR BIT(19) +#define RGA3_INT_WIN0_VER_FIFO_WR_ERR BIT(20) +#define RGA3_INT_WIN0_VER_FIFO_RD_ERR BIT(21) + +/* RO_SRST */ +#define RGA3_RO_SRST_DONE GENMASK(5, 0) + +/* *_SIZE */ +#define RGA3_HEIGHT GENMASK(28, 16) +#define RGA3_WIDTH GENMASK(12, 0) + +/* SCL_FAC */ +#define RGA3_SCALE_VER_FAC GENMASK(31, 16) +#define RGA3_SCALE_HOR_FAC GENMASK(15, 0) + +/* WINx_CTRL */ +#define RGA3_WIN_CSC_MODE GENMASK(27, 26) +#define RGA3_WIN_R2Y BIT(25) +#define RGA3_WIN_Y2R BIT(24) +#define RGA3_WIN_SCALE_VER_UP BIT(23) +#define RGA3_WIN_SCALE_VER_BYPASS BIT(22) +#define RGA3_WIN_SCALE_HOR_UP BIT(21) +#define RGA3_WIN_SCALE_HOR_BYPASS BIT(20) +#define RGA3_WIN_YC_SWAP BIT(13) +#define RGA3_WIN_RBUV_SWAP BIT(12) +#define RGA3_WIN_RD_FORMAT GENMASK(9, 8) +#define RGA3_WIN_PIC_FORMAT GENMASK(7, 4) +#define RGA3_WIN_ENABLE BIT(0) + +/* COLOR_CTRL */ +#define RGA3_OVLP_GLOBAL_ALPHA GENMASK(23, 16) +#define RGA3_OVLP_COLOR_MODE BIT(0) + +/* ALPHA_CTRL */ +#define RGA3_ALPHA_SELECT_MODE BIT(4) +#define RGA3_ALPHA_BLEND_MODE GENMASK(3, 2) + +/* WR_CTRL */ +#define RGA3_WR_YC_SWAP BIT(20) +#define RGA3_WR_SW_OUTSTANDING_MAX GENMASK(18, 13) +#define RGA3_WR_RBUV_SWAP BIT(12) +#define RGA3_WR_FORMAT GENMASK(9, 8) +#define RGA3_WR_PIC_FORMAT GENMASK(7, 4) + +struct rga3_fmt { + u32 fourcc; + u8 hw_format; + bool rbuv_swap; + bool yc_swap; +}; + +#endif From 2c869b6969f3061cbbdab587f4c0a88bd7fc3cc9 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 6 May 2026 21:41:16 +0900 Subject: [PATCH 334/394] media: cedrus: clean up media device on probe failure cedrus_probe() initializes the media device before registering the video device, the media controller, and the media device. If any of those later steps fails, probe returns without calling media_device_cleanup(), so the media device internals initialized by media_device_init() are left behind. Add a media-device cleanup label to the probe unwind path and route video registration failures through it as well. Fixes: 50e761516f2b8c ("media: platform: Add Cedrus VPU decoder driver") Cc: stable@vger.kernel.org Reviewed-by: Paul Kocialkowski Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/sunxi/cedrus/cedrus.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index ee0e286add67..bbd186b8035b 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -508,7 +508,7 @@ static int cedrus_probe(struct platform_device *pdev) ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - goto err_m2m; + goto err_media; } v4l2_info(&dev->v4l2_dev, @@ -534,7 +534,8 @@ static int cedrus_probe(struct platform_device *pdev) v4l2_m2m_unregister_media_controller(dev->m2m_dev); err_video: video_unregister_device(&dev->vfd); -err_m2m: +err_media: + media_device_cleanup(&dev->mdev); v4l2_m2m_release(dev->m2m_dev); err_v4l2: v4l2_device_unregister(&dev->v4l2_dev); From 2a1a564adab12eef528970611a74bbe3d6f1a422 Mon Sep 17 00:00:00 2001 From: Maha Maryam Javaid Date: Fri, 8 May 2026 10:57:31 +0500 Subject: [PATCH 335/394] staging: media: meson: fix typo in codec files Fix spelling mistake: substracted -> subtracted Signed-off-by: Maha Maryam Javaid Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/meson/vdec/codec_h264.c | 2 +- drivers/staging/media/meson/vdec/codec_mpeg12.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/meson/vdec/codec_h264.c b/drivers/staging/media/meson/vdec/codec_h264.c index 89e0f8624e5b..a6074de15118 100644 --- a/drivers/staging/media/meson/vdec/codec_h264.c +++ b/drivers/staging/media/meson/vdec/codec_h264.c @@ -16,7 +16,7 @@ #define SIZE_SEI (8 * SZ_1K) /* - * Offset added by the firmware which must be substracted + * Offset added by the firmware which must be subtracted * from the workspace phyaddr */ #define WORKSPACE_BUF_OFFSET 0x1000000 diff --git a/drivers/staging/media/meson/vdec/codec_mpeg12.c b/drivers/staging/media/meson/vdec/codec_mpeg12.c index 76e9ca7191ab..ab4374e3b2ef 100644 --- a/drivers/staging/media/meson/vdec/codec_mpeg12.c +++ b/drivers/staging/media/meson/vdec/codec_mpeg12.c @@ -12,7 +12,7 @@ #include "vdec_helpers.h" #define SIZE_WORKSPACE SZ_128K -/* Offset substracted by the firmware from the workspace paddr */ +/* Offset subtracted by the firmware from the workspace paddr */ #define WORKSPACE_OFFSET (5 * SZ_1K) /* map firmware registers to known MPEG1/2 functions */ From 940f161f734b25f175a95d2684c2021f6323693a Mon Sep 17 00:00:00 2001 From: Anand Moon Date: Wed, 20 May 2026 10:10:41 +0530 Subject: [PATCH 336/394] media: meson: vdec: Fix memory leak in error path of vdec_open The vdec_open() function previously jumped directly to err_m2m_release when vdec_init_ctrls() failed, skipping release of the m2m context. This caused a resource leak. Fix it by introducing a proper err_m2m_ctx_release label that calls v4l2_m2m_ctx_release(sess->m2m_ctx) before releasing the m2m device. This was identified via kmemleak: unreferenced object 0xffff0000205d6878 (size 8): comm "v4l_id", pid 5289, jiffies 4294938580 hex dump (first 8 bytes): 40 d2 49 18 00 00 ff ff @.I..... backtrace (crc d3204599): kmemleak_alloc+0xc8/0xf0 __kvmalloc_node_noprof+0x60c/0x850 v4l2_ctrl_handler_init_class+0x1b4/0x2e8 [videodev] vdec_open+0x1f4/0x788 [meson_vdec] v4l2_open+0x144/0x460 [videodev] chrdev_open+0x1ac/0x500 do_dentry_open+0x3f0/0xfe8 vfs_open+0x68/0x320 do_open+0x2d8/0x9a8 path_openat+0x1d0/0x4f0 do_filp_open+0x190/0x380 do_sys_openat2+0xf8/0x1b0 __arm64_sys_openat+0x13c/0x1e8 invoke_syscall+0xdc/0x268 el0_svc_common.constprop.0+0x178/0x258 do_el0_svc+0x4c/0x70 Fixes: 3e7f51bd9607 ("media: meson: add v4l2 m2m video decoder driver") Cc: stable@vger.kernel.org Signed-off-by: Anand Moon Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/meson/vdec/vdec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c index 4b77ec1af5a7..a039d925c0fe 100644 --- a/drivers/staging/media/meson/vdec/vdec.c +++ b/drivers/staging/media/meson/vdec/vdec.c @@ -889,7 +889,7 @@ static int vdec_open(struct file *file) ret = vdec_init_ctrls(sess); if (ret) - goto err_m2m_release; + goto err_m2m_ctx_release; sess->pixfmt_cap = formats[0].pixfmts_cap[0]; sess->fmt_out = &formats[0]; @@ -913,6 +913,8 @@ static int vdec_open(struct file *file) return 0; +err_m2m_ctx_release: + v4l2_m2m_ctx_release(sess->m2m_ctx); err_m2m_release: v4l2_m2m_release(sess->m2m_dev); err_free_sess: From 6d27f92c54ce28cfbd2a8a479a96d6f4a781b7d2 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 16 Mar 2026 11:58:22 -0400 Subject: [PATCH 337/394] media: uvcvideo: Fix deadlock if uvc_status_stop is called from async_ctrl.work If a UVC camera has an asynchronous control, uvc_status_stop may be called from async_ctrl.work: uvc_ctrl_status_event_work() uvc_ctrl_status_event() uvc_ctrl_clear_handle() uvc_pm_put() uvc_status_put() uvc_status_stop() cancel_work_sync() This will cause a deadlock, since cancel_work_sync will wait for uvc_ctrl_status_event_work to complete before returning. Fix this by returning early from uvc_status_stop if we are currently in the work function. flush_status now remains false until uvc_status_start is called again, ensuring that uvc_ctrl_status_event_work won't resubmit the URB. Fixes: a32d9c41bdb8 ("media: uvcvideo: Make power management granular") Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/all/6733bdfb-3e88-479f-8956-ab09c04c433e@linux.dev/ Signed-off-by: Sean Anderson Link: https://patch.msgid.link/20260316155823.1855434-1-sean.anderson@linux.dev Reviewed-by: Ricardo Ribalda Tested-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_status.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 65f5356bebb3..b632cf5e3fe9 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -316,6 +316,16 @@ static int uvc_status_start(struct uvc_device *dev, gfp_t flags) if (!dev->int_urb) return 0; + /* + * If the previous uvc_status_stop() call was from the async work, + * the work may still be running. Wait for it to finish before we submit + * the urb. + */ + flush_work(&dev->async_ctrl.work); + + /* Clear the flush status if we were previously stopped. */ + smp_store_release(&dev->flush_status, false); + return usb_submit_urb(dev->int_urb, flags); } @@ -336,6 +346,15 @@ static void uvc_status_stop(struct uvc_device *dev) */ smp_store_release(&dev->flush_status, true); + /* + * If we are called from the event work function, the URB is guaranteed + * to not be in flight as it has completed and has not been resubmitted. + * There's no need to cancel the work (which would deadlock), or to kill + * the URB. + */ + if (current_work() == &w->work) + return; + /* * Cancel any pending asynchronous work. If any status event was queued, * process it synchronously. @@ -354,15 +373,6 @@ static void uvc_status_stop(struct uvc_device *dev) */ if (cancel_work_sync(&w->work)) uvc_ctrl_status_event(w->chain, w->ctrl, w->data); - - /* - * From this point, there are no events on the queue and the status URB - * is dead. No events will be queued until uvc_status_start() is called. - * The barrier is needed to make sure that flush_status is visible to - * uvc_ctrl_status_event_work() when uvc_status_start() will be called - * again. - */ - smp_store_release(&dev->flush_status, false); } int uvc_status_resume(struct uvc_device *dev) From a307316ae7db7961ac485463501a040495f4e634 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 17 Apr 2026 05:19:28 +0000 Subject: [PATCH 338/394] media: uvcvideo: Do not open code uvc_queue_get_current_buffer Do not re-implement uvc_queue_get_current_buffer() logic inside uvc_video_complete(), just call the function. Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260417-uvc-meta-partial-v2-1-31d274af7d2d@chromium.org Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 62db4db4e565..a2ff73511d0e 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1704,7 +1704,6 @@ static void uvc_video_complete(struct urb *urb) struct vb2_queue *vb2_qmeta = stream->meta.queue.vdev.queue; struct uvc_buffer *buf = NULL; struct uvc_buffer *buf_meta = NULL; - unsigned long flags; int ret; switch (urb->status) { @@ -1730,13 +1729,8 @@ static void uvc_video_complete(struct urb *urb) buf = uvc_queue_get_current_buffer(queue); - if (vb2_qmeta) { - spin_lock_irqsave(&qmeta->irqlock, flags); - if (!list_empty(&qmeta->irqqueue)) - buf_meta = list_first_entry(&qmeta->irqqueue, - struct uvc_buffer, queue); - spin_unlock_irqrestore(&qmeta->irqlock, flags); - } + if (vb2_qmeta) + buf_meta = uvc_queue_get_current_buffer(qmeta); /* Re-initialise the URB async work. */ uvc_urb->async_operations = 0; From a15b773fe4ffa450b56347cc506b2d1405600f5d Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 17 Apr 2026 05:19:29 +0000 Subject: [PATCH 339/394] media: uvcvideo: Avoid partial metadata buffers If the metadata queue that is empty receives a new buffer while we are in the middle of processing a frame, the first metadata buffer will contain partial information. Avoid this by tracking the state of the metadata buffer and making sure that it is in sync with the data buffer. Now that we are at it, make sure that we skip buffers of size 1 or 0. They are not allowed by the spec... but it is a simple check to add and better be safe than sorry. Fixes: 088ead255245 ("media: uvcvideo: Add a metadata device node") Cc: stable@vger.kernel.org Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260417-uvc-meta-partial-v2-2-31d274af7d2d@chromium.org Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index a2ff73511d0e..0e691b872701 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1149,7 +1149,9 @@ static void uvc_video_stats_stop(struct uvc_streaming *stream) * uvc_video_decode_end will never be called with a NULL buffer. */ static int uvc_video_decode_start(struct uvc_streaming *stream, - struct uvc_buffer *buf, const u8 *data, int len) + struct uvc_buffer *buf, + struct uvc_buffer *meta_buf, + const u8 *data, int len) { u8 header_len; u8 fid; @@ -1278,6 +1280,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, /* TODO: Handle PTS and SCR. */ buf->state = UVC_BUF_STATE_ACTIVE; + if (meta_buf) + meta_buf->state = UVC_BUF_STATE_ACTIVE; } stream->last_fid = fid; @@ -1435,7 +1439,7 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream, ktime_t time; const u8 *scr; - if (!meta_buf || length == 2) + if (length <= 2 || !meta_buf || meta_buf->state != UVC_BUF_STATE_ACTIVE) return; has_pts = mem[1] & UVC_STREAM_PTS; @@ -1552,7 +1556,7 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb, /* Decode the payload header. */ mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset; do { - ret = uvc_video_decode_start(stream, buf, mem, + ret = uvc_video_decode_start(stream, buf, meta_buf, mem, urb->iso_frame_desc[i].actual_length); if (ret == -EAGAIN) uvc_video_next_buffers(stream, &buf, &meta_buf); @@ -1601,7 +1605,8 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb, */ if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) { do { - ret = uvc_video_decode_start(stream, buf, mem, len); + ret = uvc_video_decode_start(stream, buf, meta_buf, mem, + len); if (ret == -EAGAIN) uvc_video_next_buffers(stream, &buf, &meta_buf); } while (ret == -EAGAIN); From edc1917599c5339aedc83135cade66517e0a2972 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:20 +0000 Subject: [PATCH 340/394] media: uvcvideo: Fix dev_sof filtering in hw timestamp To avoid filling the clock circular buffer with duplicated data we only add it if the new value sof is different than the last added sof. The issue is that we compare the unprocess sof with the processed sof. If there is a sof_offset, or UVC_QUIRK_INVALID_DEVICE_SOF is enabled, the comparison will not work as expected. This patch moves the comparison to the right place. Fixes: 141270bd95d4 ("media: uvcvideo: Refactor clock circular buffer") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Tested-by: Yunke Cao Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-1-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 0e691b872701..efd746dd5db0 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -583,16 +583,7 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, if (!has_scr) return; - /* - * To limit the amount of data, drop SCRs with an SOF identical to the - * previous one. This filtering is also needed to support UVC 1.5, where - * all the data packets of the same frame contains the same SOF. In that - * case only the first one will match the host_sof. - */ sample.dev_sof = get_unaligned_le16(&data[header_size - 2]); - if (sample.dev_sof == stream->clock.last_sof) - return; - sample.dev_stc = get_unaligned_le32(&data[header_size - 6]); /* @@ -664,6 +655,16 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, } sample.dev_sof = (sample.dev_sof + stream->clock.sof_offset) & 2047; + + /* + * To limit the amount of data, drop SCRs with an SOF identical to the + * previous one. This filtering is also needed to support UVC 1.5, where + * all the data packets of the same frame contains the same SOF. In that + * case only the first one will match the host_sof. + */ + if (sample.dev_sof == stream->clock.last_sof) + return; + uvc_video_clock_add_sample(&stream->clock, &sample); stream->clock.last_sof = sample.dev_sof; } From ede7de6e6b3db552d10ac50557d69c50d1b08486 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:21 +0000 Subject: [PATCH 341/394] media: uvcvideo: Use hw timestaming if the clock buffer is full In some situations, even with a full clock buffer, it does not contain 250msec of data. This results in the driver jumping back from software to hardware timestapsing creating a nasty artifact in the video. If the clock buffer is full, use it to calculate the timestamp instead of defaulting to software stamps, the reduced accuracy is less visible than jumping from one timestamping mechanism to the other. Fixes: 6243c83be6ee8 ("media: uvcvideo: Allow hw clock updates with buffers not full") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Tested-by: Yunke Cao Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-2-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index efd746dd5db0..bb3ee942e570 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -834,15 +834,22 @@ void uvc_video_clock_update(struct uvc_streaming *stream, y2 += 2048 << 16; /* - * Have at least 1/4 of a second of timestamps before we - * try to do any calculation. Otherwise we do not have enough - * precision. This value was determined by running Android CTS - * on different devices. + * If the buffer is not full, we want to gather at least 1/4th of + * timestamps before using HW timestamping. We do this to avoid jitter + * on the initial frames. + * + * If the buffer is full we would use it regardless of how much data + * it represents. This could be solved with an infinite big circular + * buffer, but RAM is expensive these days, specially the infinitely + * big. + * + * The value of 1/4th of a second was determined by running Android's + * CTS on different devices. * * dev_sof runs at 1KHz, and we have a fixed point precision of * 16 bits. */ - if ((y2 - y1) < ((1000 / 4) << 16)) + if (clock->size != clock->count && (y2 - y1) < ((1000 / 4) << 16)) goto done; y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2 From 1719d78f832dda8dd3f09a867ba74e05d6f11308 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:22 +0000 Subject: [PATCH 342/394] media: uvcvideo: Relax the constrains for interpolating the hw clock In the initial version we set the min value to 250msec. Looks like 100msec can also provide a good value. Now that we are at it, add a macro to make it cleaner. Fixes: 6243c83be6ee8 ("media: uvcvideo: Allow hw clock updates with buffers not full") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Tested-by: Yunke Cao Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-3-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index bb3ee942e570..d2e61be35bf6 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -494,6 +494,13 @@ static int uvc_commit_video(struct uvc_streaming *stream, * Clocks and timestamps */ +/* + * The accuracy of the hardware timestamping depends on having enough data to + * interpolate between the different clock domains. This value is sof cycles, + * this is, milliseconds. + */ +#define UVC_MIN_HW_TIMESTAMP_DIFF 100 + static inline ktime_t uvc_video_get_time(void) { if (uvc_clock_param == CLOCK_MONOTONIC) @@ -843,13 +850,13 @@ void uvc_video_clock_update(struct uvc_streaming *stream, * buffer, but RAM is expensive these days, specially the infinitely * big. * - * The value of 1/4th of a second was determined by running Android's - * CTS on different devices. + * The value of UVC_MIN_HW_TIMESTAMP_DIFF was determined by running + * Android's CTS on different devices. * - * dev_sof runs at 1KHz, and we have a fixed point precision of - * 16 bits. + * y1 and y2 are dev_sof with a fixed point precision of 16 bits. */ - if (clock->size != clock->count && (y2 - y1) < ((1000 / 4) << 16)) + if (clock->size != clock->count && + (y2 - y1) < (UVC_MIN_HW_TIMESTAMP_DIFF << 16)) goto done; y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2 From ba649fff36c1fe68489a86d00285224907edd436 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:23 +0000 Subject: [PATCH 343/394] media: uvcvideo: Do not add clock samples with small sof delta Some UVC 1.1 cameras running in fast isochronous mode tend to spam the USB host with a lot of empty packets. These packets contain clock information and are added to the clock buffer but do not add any accuracy to the calculation. In fact, it is quite the opposite, in our calculations, only the first and the last timestamp is used, and we only have 32 slots. Ignore the samples that will produce less than MIN_HW_TIMESTAMP_DIFF data. Fixes: 141270bd95d4 ("media: uvcvideo: Refactor clock circular buffer") Cc: stable@vger.kernel.org Tested-by: Yunke Cao Reviewed-by: Hans de Goede Signed-off-by: Ricardo Ribalda Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-4-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index d2e61be35bf6..09d6baf87ec5 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -544,6 +544,15 @@ static void uvc_video_clock_add_sample(struct uvc_clock *clock, spin_unlock_irqrestore(&clock->lock, flags); } +static inline u16 sof_diff(u16 a, u16 b) +{ + /* + * Because the result is modulo 2048 (via & 2047), we do not need a + * special case for a < b. + */ + return (a - b) & 2047; +} + static void uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, const u8 *data, int len) @@ -664,12 +673,13 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, sample.dev_sof = (sample.dev_sof + stream->clock.sof_offset) & 2047; /* - * To limit the amount of data, drop SCRs with an SOF identical to the + * To limit the amount of data, drop SCRs with an SOF similar to the * previous one. This filtering is also needed to support UVC 1.5, where * all the data packets of the same frame contains the same SOF. In that * case only the first one will match the host_sof. */ - if (sample.dev_sof == stream->clock.last_sof) + if (sof_diff(sample.dev_sof, stream->clock.last_sof) <= + (UVC_MIN_HW_TIMESTAMP_DIFF / stream->clock.size)) return; uvc_video_clock_add_sample(&stream->clock, &sample); From 0491c93a510c32eaf8ed404b99a447e10c30291e Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:24 +0000 Subject: [PATCH 344/394] media: uvcvideo: Do not add samples if dev_sof has not changed We only save relevant samples into the circular buffer. If the data is very similar to the previous one, exit early, this allows us to avoid some expensive operations such as usb_get_current_frame_number(). Suggested-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Tested-by: Yunke Cao Reviewed-by: Hans de Goede Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-5-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 16 +++++++++++----- drivers/media/usb/uvc/uvcvideo.h | 3 ++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 09d6baf87ec5..c104ffe3b3c1 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -524,7 +524,7 @@ static void uvc_video_clock_add_sample(struct uvc_clock *clock, spin_lock_irqsave(&clock->lock, flags); - if (clock->count > 0 && clock->last_sof > sample->dev_sof) { + if (clock->count > 0 && clock->last_sof_processed > sample->dev_sof) { /* * Remove data from the circular buffer that is older than the * last SOF overflow. We only support one SOF overflow per @@ -599,7 +599,12 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, if (!has_scr) return; - sample.dev_sof = get_unaligned_le16(&data[header_size - 2]); + sample.dev_sof = get_unaligned_le16(&data[header_size - 2]) & 2047; + /* If the sample SOF is identical to the previous one, quit early. */ + if (stream->clock.last_sof_raw == sample.dev_sof) + return; + stream->clock.last_sof_raw = sample.dev_sof; + sample.dev_stc = get_unaligned_le32(&data[header_size - 6]); /* @@ -678,19 +683,20 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, * all the data packets of the same frame contains the same SOF. In that * case only the first one will match the host_sof. */ - if (sof_diff(sample.dev_sof, stream->clock.last_sof) <= + if (sof_diff(sample.dev_sof, stream->clock.last_sof_processed) <= (UVC_MIN_HW_TIMESTAMP_DIFF / stream->clock.size)) return; uvc_video_clock_add_sample(&stream->clock, &sample); - stream->clock.last_sof = sample.dev_sof; + stream->clock.last_sof_processed = sample.dev_sof; } static void uvc_video_clock_reset(struct uvc_clock *clock) { clock->head = 0; clock->count = 0; - clock->last_sof = -1; + clock->last_sof_processed = -1; + clock->last_sof_raw = -1; clock->last_sof_overflow = -1; clock->sof_offset = -1; } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 4ba35727e954..b6bcee4a222f 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -522,7 +522,8 @@ struct uvc_streaming { unsigned int size; unsigned int last_sof_overflow; - u16 last_sof; + u16 last_sof_processed; + u16 last_sof_raw; u16 sof_offset; u8 last_scr[6]; From a3d78e74dd3ed04797ea351edb7f0a19b961c063 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 13 May 2026 11:49:25 +0000 Subject: [PATCH 345/394] media: uvcvideo: Only do uvc_video_get_time() if needed There is no need to calculate the current time if the sample is going to be filtered. Move the assignment close to uvc_video_clock_add_sample(). Suggested-by: Hans de Goede Signed-off-by: Ricardo Ribalda Tested-by: Yunke Cao Reviewed-by: Hans de Goede Link: https://patch.msgid.link/20260513-uvc-hwtimestamp-v3-6-7a64838b0b02@chromium.org Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_video.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index c104ffe3b3c1..fc3536a4399f 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -645,8 +645,6 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, if (stream->dev->quirks & UVC_QUIRK_INVALID_DEVICE_SOF) sample.dev_sof = sample.host_sof; - sample.host_time = uvc_video_get_time(); - /* * The UVC specification allows device implementations that can't obtain * the USB frame number to keep their own frame counters as long as they @@ -687,6 +685,9 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, (UVC_MIN_HW_TIMESTAMP_DIFF / stream->clock.size)) return; + /* This is expensive, only do it if the sample will be added. */ + sample.host_time = uvc_video_get_time(); + uvc_video_clock_add_sample(&stream->clock, &sample); stream->clock.last_sof_processed = sample.dev_sof; } From a947fc3ba8a0c097862613bbaaa0bbd06d10b0d2 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 4 May 2026 14:43:14 +0200 Subject: [PATCH 346/394] media: rcar-vin: Drop min_queued_buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The R-Car VIN driver already uses a scratch buffer to sustain capture operations in absence of a frame buffer provided by userspace. There is no reason to require 4 buffers queued at all times for the driver to operate. Drop min_queued_buffers from the VIN driver to allow single-frame capture operations. Signed-off-by: Jacopo Mondi Reviewed-by: Jai Luthra Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rcar-vin/rcar-dma.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index f9af9177e02f..73cda0e2d45a 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -1494,7 +1494,6 @@ int rvin_dma_register(struct rvin_dev *vin, int irq) q->ops = &rvin_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_queued_buffers = 4; q->dev = vin->dev; ret = vb2_queue_init(q); From e52b90b8277a433a680a03680f1e344d4a8894e6 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 18 Dec 2025 10:23:49 +0100 Subject: [PATCH 347/394] media: staging: imx-csi: move media_pipeline to video device The imx-media driver has a single imx_media_device. Attaching the media_pipeline to the imx_media_device prevents the execution of multiple media pipelines on the device. This should be possible as long as the media_pipelines don't use the same pads or pads that be configured while the other media pipeline is streaming. Move the media_pipeline to the imx_media_video_dev to be able to construct media pipelines per imx capture device. If different media pipelines in the media device conflict, the validation will fail. Thus, the pipeline will fail to start and signal an error to user space. Reviewed-by: Frank Li Reviewed-by: Philipp Zabel Signed-off-by: Michael Tretter Signed-off-by: Frank Li Signed-off-by: Hans Verkuil --- drivers/staging/media/imx/imx-media-capture.c | 8 ++++---- drivers/staging/media/imx/imx-media-utils.c | 3 ++- drivers/staging/media/imx/imx-media.h | 7 ++++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index e9cef7af000a..bfd71d25facc 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -722,8 +722,8 @@ static int capture_start_streaming(struct vb2_queue *vq, unsigned int count) goto return_bufs; } - ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity, - true); + ret = imx_media_pipeline_set_stream(priv->md, &priv->vdev, + &priv->src_sd->entity, true); if (ret) { dev_err(priv->dev, "pipeline start failed with %d\n", ret); goto return_bufs; @@ -749,8 +749,8 @@ static void capture_stop_streaming(struct vb2_queue *vq) unsigned long flags; int ret; - ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity, - false); + ret = imx_media_pipeline_set_stream(priv->md, &priv->vdev, + &priv->src_sd->entity, false); if (ret) dev_warn(priv->dev, "pipeline stop failed with %d\n", ret); diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 1b5af8945e6b..f520529a7cfe 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -749,6 +749,7 @@ EXPORT_SYMBOL_GPL(imx_media_pipeline_subdev); * Turn current pipeline streaming on/off starting from entity. */ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, + struct imx_media_video_dev *vdev, struct media_entity *entity, bool on) { @@ -762,7 +763,7 @@ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, mutex_lock(&imxmd->md.graph_mutex); if (on) { - ret = __media_pipeline_start(entity->pads, &imxmd->pipe); + ret = __media_pipeline_start(entity->pads, &vdev->pipe); if (ret) goto out; ret = v4l2_subdev_call(sd, video, s_stream, 1); diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index 135daca7d55b..537558a7bece 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -104,6 +104,9 @@ struct imx_media_buffer { struct imx_media_video_dev { struct video_device *vfd; + /* the pipeline object */ + struct media_pipeline pipe; + /* the user format */ struct v4l2_pix_format fmt; /* the compose rectangle */ @@ -145,9 +148,6 @@ struct imx_media_dev { struct media_device md; struct v4l2_device v4l2_dev; - /* the pipeline object */ - struct media_pipeline pipe; - struct mutex mutex; /* protect elements below */ /* master video device list */ @@ -223,6 +223,7 @@ int imx_media_alloc_dma_buf(struct device *dev, int size); int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, + struct imx_media_video_dev *vdev, struct media_entity *entity, bool on); From 262d2e7cfc2805464248a4fa1635636a834f09a9 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 18 Dec 2025 10:23:50 +0100 Subject: [PATCH 348/394] media: staging: imx-csi: explicitly start media pipeline on pad 0 entity->pads is an array that contains all the pads of an entity. Calling __media_pipeline_start() or __media_pipeline_stop() on the pads, implicitly starts the pipeline with the first pad in this array as origin. Explicitly use the first pad to start the pipeline to make this more obvious to the reader. Reviewed-by: Frank Li Reviewed-by: Philipp Zabel Signed-off-by: Michael Tretter Signed-off-by: Frank Li Signed-off-by: Hans Verkuil --- drivers/staging/media/imx/imx-media-utils.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index f520529a7cfe..bd9af147a801 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -754,6 +754,7 @@ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, bool on) { struct v4l2_subdev *sd; + struct media_pad *pad; int ret = 0; if (!is_media_entity_v4l2_subdev(entity)) @@ -762,17 +763,19 @@ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, mutex_lock(&imxmd->md.graph_mutex); + pad = &entity->pads[0]; + if (on) { - ret = __media_pipeline_start(entity->pads, &vdev->pipe); + ret = __media_pipeline_start(pad, &vdev->pipe); if (ret) goto out; ret = v4l2_subdev_call(sd, video, s_stream, 1); if (ret) - __media_pipeline_stop(entity->pads); + __media_pipeline_stop(pad); } else { v4l2_subdev_call(sd, video, s_stream, 0); - if (media_pad_pipeline(entity->pads)) - __media_pipeline_stop(entity->pads); + if (media_pad_pipeline(pad)) + __media_pipeline_stop(pad); } out: From e33062c19b65f03353932ae3ff1b6ef672cf1bac Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 18 Dec 2025 10:23:51 +0100 Subject: [PATCH 349/394] media: staging: imx-csi: use media_pad_is_streaming helper The media_pad_is_streaming() helper is explicitly intended to check whether a pad has been started with media_pipeline_start(). Use it instead of relying on the implicit assumption that a pad with a pipeline is streaming. Reviewed-by: Philipp Zabel Signed-off-by: Michael Tretter Reviewed-by: Frank Li Signed-off-by: Frank Li Signed-off-by: Hans Verkuil --- drivers/staging/media/imx/imx-media-utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index bd9af147a801..1a2e5e40c99c 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -774,7 +774,7 @@ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, __media_pipeline_stop(pad); } else { v4l2_subdev_call(sd, video, s_stream, 0); - if (media_pad_pipeline(pad)) + if (media_pad_is_streaming(pad)) __media_pipeline_stop(pad); } From a96fcde9fa9a6696991d1f172a8bc2bade82a6d9 Mon Sep 17 00:00:00 2001 From: Shyam Sunder Reddy Padira Date: Sun, 3 May 2026 20:30:26 +0530 Subject: [PATCH 350/394] media: staging: imx: remove unnecessary out-of-memory error message Remove dev_err() call after dma_alloc_coherent() failure. checkpatch.pl reports this as an unnecessary out-of-memory message because failure is already conveyed by returning -ENOMEM, and the current message does not provide additional useful debugging information. Signed-off-by: Shyam Sunder Reddy Padira Signed-off-by: Frank Li Signed-off-by: Hans Verkuil --- drivers/staging/media/imx/imx-media-utils.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 1a2e5e40c99c..f119477cac6b 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -589,10 +589,8 @@ int imx_media_alloc_dma_buf(struct device *dev, buf->len = PAGE_ALIGN(size); buf->virt = dma_alloc_coherent(dev, buf->len, &buf->phys, GFP_DMA | GFP_KERNEL); - if (!buf->virt) { - dev_err(dev, "%s: failed\n", __func__); + if (!buf->virt) return -ENOMEM; - } return 0; } From 8e2b43d2c10b1b5f42805810c6854470d8774e60 Mon Sep 17 00:00:00 2001 From: Miguel Vadillo Date: Wed, 27 May 2026 10:05:29 -0700 Subject: [PATCH 351/394] media: i2c: cvs: Add driver of Intel Computer Vision Sensing Controller(CVS) Add driver for Intel Computer Vision Sensing (CVS) devices found on Intel Luna Lake (LNL), Panther Lake (PTL), and Arrow Lake (ARL) platforms. The CVS device acts as a V4L2 sub-device bridge that manages CSI-2 link ownership between the host (Linux) and firmware for camera sensors. It provides: - Query the device status via sysfs interface - CSI-2 link ownership arbitration between host and CVS firmware - MIPI CSI-2 configuration management - Privacy LED control coordination - Power management integration with runtime PM The driver consists of two main components: core.c: Core driver with probe, command transport, and power management v4l2.c: V4L2 sub-device and media framework integration Hardware Interface: - I2C for command/control communication with device firmware - GPIO signals for ownership handshaking (request/response) - Optional reset and wake interrupt for full-capability variants - Integration with Intel IPU via ipu_bridge The driver supports two hardware capability levels: - Light capability: Basic GPIO-based ownership (2 GPIOs) - Full capability: Enhanced with reset control and wake IRQ (4 GPIOs) Device-specific quirks are handled via a quirk table to accommodate variations across different CVS implementations (Lattice, Synaptics). In addition to I2C-based operation, the driver supports platform device instantiation for systems where CVS is exposed without I2C transport, falling back to GPIO-only ownership control. The CVS driver integrates with the IPU bridge for automatic device discovery via ACPI on supported platforms. PCI device IDs for Intel IPU7 (0x645d, shared by MTL and LNL) and IPU7.5 (0xb05d, shared by ARL and PTL) are included in the driver-local icvs_pci_tbl lookup table, enabling CVS to locate these IPU variants without modifying the shared ipu6-pci-table header. A PM runtime device link is established between IPU (consumer) and CVS (supplier) so that the PM framework automatically resumes CVS before IPU begins streaming, triggering cvs_runtime_resume() to claim CSI-2 link ownership. Ownership is released via cvs_runtime_suspend() after the autosuspend delay. Signed-off-by: Miguel Vadillo Tested-by: Mehdi Djait # Dell XPS 13 9350 + IPU7 Reviewed-by: Mehdi Djait Signed-off-by: Sakari Ailus --- MAINTAINERS | 6 + drivers/media/i2c/Kconfig | 2 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/cvs/Kconfig | 21 + drivers/media/i2c/cvs/Makefile | 4 + drivers/media/i2c/cvs/core.c | 1043 ++++++++++++++++++++++++++++++++ drivers/media/i2c/cvs/icvs.h | 495 +++++++++++++++ drivers/media/i2c/cvs/v4l2.c | 618 +++++++++++++++++++ 8 files changed, 2190 insertions(+) create mode 100644 drivers/media/i2c/cvs/Kconfig create mode 100644 drivers/media/i2c/cvs/Makefile create mode 100644 drivers/media/i2c/cvs/core.c create mode 100644 drivers/media/i2c/cvs/icvs.h create mode 100644 drivers/media/i2c/cvs/v4l2.c diff --git a/MAINTAINERS b/MAINTAINERS index 3124500ff432..efbf808063e5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12848,6 +12848,12 @@ S: Orphan T: git git://git.code.sf.net/p/intel-sas/isci F: drivers/scsi/isci/ +INTEL COMPUTER VISION SENSING (CVS) DRIVER +M: Miguel Vadillo +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/cvs/ + INTEL CPU family model numbers M: Tony Luck M: x86@kernel.org diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index a3ab48607dcf..5d173e0ecf42 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1657,6 +1657,8 @@ endmenu menu "Miscellaneous helper chips" visible if !MEDIA_HIDE_ANCILLARY_SUBDRV +source "drivers/media/i2c/cvs/Kconfig" + config VIDEO_I2C tristate "I2C transport video support" depends on VIDEO_DEV && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 90b276a7417a..e45359efe0e4 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -171,3 +171,4 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_WM8739) += wm8739.o obj-$(CONFIG_VIDEO_WM8775) += wm8775.o +obj-$(CONFIG_VIDEO_INTEL_CVS) += cvs/ diff --git a/drivers/media/i2c/cvs/Kconfig b/drivers/media/i2c/cvs/Kconfig new file mode 100644 index 000000000000..4309d20dd726 --- /dev/null +++ b/drivers/media/i2c/cvs/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_INTEL_CVS + tristate "Intel CVS CSI-2 bridge support" + depends on I2C && ACPI && VIDEO_DEV + depends on IPU_BRIDGE || !IPU_BRIDGE + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This adds support for the Intel Computer Vision Sensing (CVS). + + The driver registers a V4L2 sub-device to arbitrate CSI-2 link + ownership between the host and CVS firmware, and configures the + device for camera sensor streaming. + + The driver can operate with full I2C transport or in a reduced + platform (GPIO-only) mode when I2C is unavailable. + + Say Y to build into the kernel, or M to build as a module. + The module will be named intel_cvs. If unsure, say N. diff --git a/drivers/media/i2c/cvs/Makefile b/drivers/media/i2c/cvs/Makefile new file mode 100644 index 000000000000..0aeb2c2e3100 --- /dev/null +++ b/drivers/media/i2c/cvs/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +intel_cvs-y := core.o v4l2.o + +obj-$(CONFIG_VIDEO_INTEL_CVS) += intel_cvs.o diff --git a/drivers/media/i2c/cvs/core.c b/drivers/media/i2c/cvs/core.c new file mode 100644 index 000000000000..4282f33c7295 --- /dev/null +++ b/drivers/media/i2c/cvs/core.c @@ -0,0 +1,1043 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "icvs.h" + +/* Command timeouts determined experimentally */ +#define CMD_TIMEOUT (5 * HZ) +#define FW_READY_DELAY_MS 100 + +#define PCI_DEVICE_ID_INTEL_IPU7 0x645d /* MTL / LNL */ +#define PCI_DEVICE_ID_INTEL_IPU7P5 0xb05d /* ARL / PTL */ + +/* + * IPU7 PCI device IDs not covered by ipu6_pci_tbl in ipu6-pci-table.h. + * Once the IPU6 driver gains support for IPU7, this table can be dropped. + */ +static const struct pci_device_id icvs_ipu7_tbl[] = { + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7P5) }, + { } +}; + +static const struct acpi_gpio_params gpio_wake = { 0, 0, false }; +static const struct acpi_gpio_params gpio_rst = { 1, 0, false }; +static const struct acpi_gpio_params gpio_req = { 2, 0, false }; +static const struct acpi_gpio_params gpio_resp = { 3, 0, false }; +static const struct acpi_gpio_mapping icvs_acpi_gpios[] = { + { "wake-gpio", &gpio_wake, 1 }, + { "rst-gpio", &gpio_rst, 1 }, + { "req-gpio", &gpio_req, 1 }, + { "resp-gpio", &gpio_resp, 1 }, + { } +}; + +static const struct acpi_gpio_params lgpio_req = { 0, 0, false }; +static const struct acpi_gpio_params lgpio_resp = { 1, 0, false }; +static const struct acpi_gpio_mapping icvs_acpi_lgpios[] = { + { "req-gpio", &lgpio_req, 1 }, + { "resp-gpio", &lgpio_resp, 1 }, + { } +}; + +/* Device quirk table */ +static const struct icvs_device_quirk cvs_quirk_table[] = { + { 0x2ac1, 0x20d0, ICVS_NO_MIPI_CONFIG | + ICVS_NO_CAPS | + ICVS_NO_FW_UPDATE + }, /* Lattice NX33 */ + { 0x06CB, 0x0701, ICVS_SKIP_FW_RESET | + ICVS_HOST_SENSOR_PWR_CTRL | + ICVS_HOST_PRIV_CTRL | + ICVS_FW_BUF_SIZE_256 | + ICVS_FW_HEADER_SIZE_256 + }, /* Synaptics SVP7xxx */ + { } +}; + +/** + * cvs_set_quirks - Match device VID/PID and set quirks + * @ctx: CVS device context + * @vid: Vendor ID + * @pid: Product ID + * + * Searches the quirk table for a matching VID/PID and populates ctx->quirks + * with the corresponding quirk flags. + * If no match is found, quirks is set to 0. + */ +static void cvs_set_quirks(struct icvs *ctx, u16 vid, u16 pid) +{ + ctx->quirks = 0; + + for (unsigned int i = 0; i < ARRAY_SIZE(cvs_quirk_table); i++) { + if (cvs_quirk_table[i].vid == vid && + cvs_quirk_table[i].pid == pid) { + ctx->quirks = cvs_quirk_table[i].quirks; + dev_info(cvs_dev(ctx), + "Quirks: 0x%lx (VID:0x%04x PID:0x%04x)\n", + ctx->quirks, vid, pid); + return; + } + } + + dev_info(cvs_dev(ctx), + "No quirks for device (VID:0x%04x PID:0x%04x)\n", vid, pid); +} + +/* I2C transport helpers */ + +/** + * cvs_read_i2c - Issue a read-type command and fetch device response + * @ctx: CVS device context + * @cmd_id: Command identifier (big endian) + * @resp: Destination buffer for response payload + * @size: Size of payload to read into @resp (without prefix) + * + * Sends @cmd_id and reads back the response in a single I2C transaction. + * When the device prepends a 4-byte protocol prefix, the combined + * prefix+payload is read into a temporary buffer and only the payload is + * copied to @resp, avoiding any dependency on the layout of the caller's + * buffer. + * + * Return: 0 on success or negative errno. + */ +static int cvs_read_i2c(struct icvs *ctx, __be16 cmd_id, void *resp, + size_t size) +{ + size_t prefix_size = ctx->prefix ? sizeof(u32) : 0; + size_t read_size = size + prefix_size; + struct i2c_client *i2c = ctx->i2c_client; + u8 *buf __free(kfree) = NULL; + int cnt; + + if (!resp || !size) + return -EINVAL; + + cnt = i2c_master_send(i2c, (const char *)&cmd_id, sizeof(cmd_id)); + if (cnt != sizeof(cmd_id)) + return cnt < 0 ? cnt : -EIO; + + buf = kmalloc(read_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + cnt = i2c_master_recv(i2c, buf, read_size); + if (cnt != read_size) { + dev_dbg(cvs_dev(ctx), "recv cmd 0x%04x short read (%d/%zu)\n", + be16_to_cpu(cmd_id), cnt, read_size); + return cnt < 0 ? cnt : -EIO; + } + + memcpy(resp, buf + prefix_size, size); + + return 0; +} + +/** + * cvs_write_i2c - Write a raw command buffer to the device over I2C + * @ctx: CVS device context + * @data: Buffer containing command + payload + * @size: Total bytes to write + * + * Return: 0 on success or negative errno. + */ +static int cvs_write_i2c(struct icvs *ctx, const void *data, int size) +{ + struct i2c_client *i2c = ctx->i2c_client; + int cnt; + + if (size < 0 || !data) + return -EINVAL; + + cnt = i2c_master_send(i2c, data, size); + if (cnt != size) { + dev_dbg(cvs_dev(ctx), "send short (%d/%d)\n", cnt, size); + return cnt < 0 ? cnt : -EIO; + } + + return 0; +} + +/** + * cvs_checksum - Simple additive checksum helper + * @data: 32-bit aligned data buffer + * @len: Length in bytes (multiple of 4) + * + * Return: 32-bit additive checksum of the dwords in @data. + */ +static u32 cvs_checksum(const void *data, size_t len) +{ + const u32 *words = data; + u32 csum = 0; + + if (WARN_ON_ONCE(len % sizeof(u32))) + return 0; + + for (unsigned int i = 0; i < len / sizeof(u32); i++) + csum += words[i]; + + return csum; +} + +/** + * cvs_schedule_and_wait - Schedule polling work then wait for completion + * @ctx: CVS device context + * @work_delay_ms: Delay in milliseconds before polling work executes + * @wait_jiffies: Timeout (jiffies) to wait for cmd completion + * + * Queues ctx->work to run after @work_delay_ms and then waits up to + * @wait_jiffies for ctx->cmd_completion. + * + * Return: 0 on success, -ETIMEDOUT on timeout, negative errno on error. + */ +static int cvs_schedule_and_wait(struct icvs *ctx, unsigned int work_delay_ms, + unsigned long wait_jiffies) +{ + int ret; + + schedule_delayed_work(&ctx->work, msecs_to_jiffies(work_delay_ms)); + ret = wait_for_completion_killable_timeout(&ctx->cmd_completion, + wait_jiffies); + if (ret < 0) + return ret; + if (!ret) + return -ETIMEDOUT; + + return 0; +} + +/** + * cvs_wait_wake_or_sleep - Wait for wake IRQ or sleep fallback + * @ctx: CVS device context + * @timeout_jiffies: Timeout (jiffies) for wake event on full-cap devices + * @sleep_ms: Milliseconds to sleep on light-cap devices + * + * For full capability devices (ICVS_FULLCAP) waits interruptibly on + * ctx->hostwake_event until ctx->hostwake_event_arg becomes true or + * @timeout_jiffies elapses. The flag is cleared after the wait. + * For light capability devices performs a blocking msleep(@sleep_ms). + * + * Return codes normalized for caller switch handling: + * <0 : error / interrupted + * -ETIMEDOUT : timeout (wake event not observed) + * 0 : success (wake observed OR light-cap sleep elapsed) + * + * Return: negative errno, -ETIMEDOUT on timeout, 0 on success. + */ +static int cvs_wait_wake_or_sleep(struct icvs *ctx, + unsigned long timeout_jiffies, + unsigned int sleep_ms) +{ + int ret; + + if (ctx->res == ICVS_FULLCAP) { + ret = wait_event_interruptible_timeout(ctx->hostwake_event, + ctx->hostwake_event_arg, + timeout_jiffies); + ctx->hostwake_event_arg = false; + if (ret < 0) + return ret; + if (!ret) + return -ETIMEDOUT; + return 0; + } + + msleep(sleep_ms); + + return 0; /* treat sleep path as success */ +} + +/** + * cvs_config_mipi - Send a HOST_SET_MIPI_CONFIG command + * @ctx: CVS device context + * @c: Command container with conf field populated + * @len: Length of original command structure (unused except for symmetry) + * + * Packages @c->param.conf into a cvs_mipi_data_packet including size and + * checksum, then writes it to the device. + * + * Firmware note: the CVS firmware expects the MIPI configuration to be + * sent as a single transaction, including all relevant parameters and checksum. + * + * Return: 0 on success, NO_MIPI_CONFIG or negative errno from I2C write. + */ +static int cvs_config_mipi(struct icvs *ctx, struct icvs_cmd *c, size_t len) +{ + struct icvs_mipi_data_packet pkt = { + .cmd_id = c->cmd_id, + .size = sizeof(c->param.conf), + .crc = cvs_checksum(&c->param.conf, sizeof(c->param.conf)), + .conf = c->param.conf, + }; + + if (ctx->quirks & ICVS_NO_MIPI_CONFIG) + return 0; + + return cvs_write_i2c(ctx, &pkt, sizeof(pkt)); +} + +/** + * cvs_get_device_state - Query current device state bitfield + * @ctx: CVS device context + * @state: Returned state value + * + * Issues GET_DEV_STATE and fills @state. + * + * Return: 0 on success or negative errno. + */ +static int cvs_get_device_state(struct icvs *ctx, u8 *state) +{ + struct icvs_resp n = { + .cmd_id = cpu_to_be16(ICVS_GET_DEV_STATE), + }; + int ret; + + ret = cvs_read_i2c(ctx, n.cmd_id, &n.resp.state, sizeof(n.resp.state)); + if (ret) + return ret; + + *state = n.resp.state; + + return 0; +} + +/** + * cvs_get_device_caps - Read protocol capabilities + * @ctx: CVS device context + * @caps: Capability structure to populate + * + * Return: 0 on success or negative errno. + */ +static int cvs_get_device_caps(struct icvs *ctx, + struct icvs_dev_capabilities *caps) +{ + struct icvs_resp n = { + .cmd_id = cpu_to_be16(ICVS_GET_DEV_CAPABILITY), + }; + int ret; + + if (ctx->quirks & ICVS_NO_CAPS) + return 0; + + ret = cvs_read_i2c(ctx, n.cmd_id, &n.resp.cap, sizeof(n.resp.cap)); + if (ret) + return ret; + + *caps = n.resp.cap; + + return 0; +} + +/** + * cvs_hw_init - Probe device for prefix support and apply quirks + * @ctx: CVS device context + * + * Sends GET_DEV_VID_PID and probes for a 32-bit prefix. + * If it matches ICVS_PREFIX_VAL, sets ctx->prefix for subsequent reads. + * Then reads VID/PID and applies matching quirks. + * GET_DEV_VID_PID is supported by all protocol versions. + * + * Return: 0 on success or negative errno. + */ +static int cvs_hw_init(struct icvs *ctx) +{ + struct icvs_resp n = { }; + __be16 cmd = cpu_to_be16(ICVS_GET_DEV_VID_PID); + u32 resp; + int ret; + + /* + * Clear prefix so cvs_read_i2c always reads exactly sizeof(u32) bytes + * here, regardless of any value left over from a previous call (e.g. + * on resume). + */ + ctx->prefix = false; + ret = cvs_read_i2c(ctx, cmd, &resp, sizeof(resp)); + if (ret) + return ret; + + ctx->prefix = resp == ICVS_PREFIX_VAL; + + /* Now read VID/PID to apply quirks */ + ret = cvs_read_i2c(ctx, cmd, + &n.resp.vid_pid, sizeof(n.resp.vid_pid)); + if (ret) + return ret; + + cvs_set_quirks(ctx, n.resp.vid_pid.v_id, n.resp.vid_pid.p_id); + + return 0; +} + +/** + * cvs_irq_handler - Wake IRQ handler (full capability devices) + * @irq: IRQ number + * @dev_id: Device context pointer + * + * Sets a waitqueue flag and wakes up sleeping waiters. + * + * Return: IRQ_HANDLED always. + */ +static irqreturn_t cvs_irq_handler(int irq, void *dev_id) +{ + struct icvs *ctx = dev_id; + + ctx->hostwake_event_arg = true; + wake_up_interruptible(&ctx->hostwake_event); + + return IRQ_HANDLED; +} + +/** + * cvs_reset - Toggle reset GPIO for full capability devices + * @ctx: CVS device context + * + * Drives reset low briefly then high if device resources indicate full + * capability. Light devices have no reset line. + */ +static void cvs_reset(struct icvs *ctx) +{ + if (ctx->quirks & ICVS_SKIP_FW_RESET) + return; + + if (ctx->res == ICVS_FULLCAP) { + gpiod_set_value(ctx->rst, 0); + fsleep(2000); + gpiod_set_value(ctx->rst, 1); + } +} + +/** + * cvs_recv - Delayed work handler polling for command completion + * @work: Embedded delayed_work member + * + * Re-reads device state; if device_busy remains set, re-schedules itself. + * Otherwise stores state into wq_resp and completes the command. + */ +static void cvs_recv(struct work_struct *work) +{ + struct icvs *ctx = container_of(work, struct icvs, work.work); + u8 state = 0; + int ret; + + ret = cvs_get_device_state(ctx, &state); + if (ret < 0) { + dev_dbg(cvs_dev(ctx), "state read failed: %d\n", ret); + return; + } + + if (state & ICVS_DEV_STATE_BUSY) { + dev_dbg(cvs_dev(ctx), "device busy, reschedule\n"); + schedule_delayed_work(&ctx->work, + msecs_to_jiffies(FW_READY_DELAY_MS)); + return; + } + + ctx->wq_resp.resp.state = state; + complete(&ctx->cmd_completion); +} + +/** + * cvs_send - Common command submission path + * @ctx: CVS device context + * @cmd: Command buffer (icvs_cmd) with cmd_id and param populated + * @len: Buffer length + * + * Dispatches a set of supported commands: + * - ICVS_SET_DEV_HOST_ID, + * - ICVS_HOST_SENSOR_OWNER, + * - ICVS_HOST_SET_MIPI_CONFIG + * - ICVS_FW_LOADER_* + * + * For I2C based commands it sets big-endian cmd ids, writes to the device + * and waits (via delayed work) for completion or timeout. + * GPIO based ownership toggles are handled locally. + * + * Caller must hold ctx->lock when invoking this function and check for i2c + * bus availability. + * + * Return: 0 on success, negative errno, -EINVAL for unsupported command + * or status from device in ctx->wq_resp. + */ +int cvs_send(struct icvs *ctx, struct icvs_cmd *cmd, size_t len) +{ + int ret, status = 0; + + lockdep_assert_held(&ctx->lock); + + dev_dbg(cvs_dev(ctx), "send cmd = 0x%04x", be16_to_cpu(cmd->cmd_id)); + + reinit_completion(&ctx->cmd_completion); + + switch (be16_to_cpu(cmd->cmd_id)) { + case ICVS_SET_DEV_HOST_ID: + cmd->cmd_id = cpu_to_be16(ICVS_SET_DEV_HOST_ID); + ret = cvs_write_i2c(ctx, cmd, len); + if (ret < 0) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + if (ret < 0) + break; + + status = ctx->wq_resp.resp.state & + ICVS_DEV_STATE_ERROR ? -EINVAL : 0; + break; + case ICVS_HOST_SENSOR_OWNER: + gpiod_set_value_cansleep(ctx->req, cmd->param.param); + fsleep(FW_READY_DELAY_MS * USEC_PER_MSEC); + ret = gpiod_get_value_cansleep(ctx->resp); + status = cmd->param.param == ret ? 0 : -EINVAL; + ret = 0; /* success */ + break; + case ICVS_HOST_SET_MIPI_CONFIG: + cmd->cmd_id = cpu_to_be16(ICVS_HOST_SET_MIPI_CONFIG); + ret = cvs_config_mipi(ctx, cmd, len); + if (ret < 0) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + status = (ctx->wq_resp.resp.state & + ICVS_DEV_STATE_ERROR) ? -EINVAL : 0; + break; + case ICVS_FW_LOADER_START: + cmd->cmd_id = cpu_to_be16(ICVS_FW_LOADER_START); + ret = cvs_write_i2c(ctx, cmd, len); + if (ret < 0) + break; + + ret = cvs_wait_wake_or_sleep(ctx, CMD_TIMEOUT, + FW_READY_DELAY_MS); + if (ret) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + status = (ctx->wq_resp.resp.state & + ICVS_DEV_STATE_DOWNLOAD) ? 0 : -EINVAL; + break; + case ICVS_FW_LOADER_DATA: + /* Quirk for older protocols */ + if (ctx->caps.protocol_version_major >= 2 && + ctx->caps.protocol_version_minor >= 2) { + cmd->cmd_id = cpu_to_be16(ICVS_FW_LOADER_DATA); + ret = cvs_write_i2c(ctx, cmd, len); + } else { + ret = cvs_write_i2c(ctx, &cmd->param, + len - sizeof(cmd->cmd_id)); + } + + if (ret < 0) + return ret; + + ret = cvs_wait_wake_or_sleep(ctx, FW_READY_DELAY_MS, + FW_READY_DELAY_MS); + if (ret) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + status = ctx->wq_resp.resp.state & + ICVS_DEV_STATE_ERROR ? -EINVAL : 0; + break; + case ICVS_FW_LOADER_END: + cmd->cmd_id = cpu_to_be16(ICVS_FW_LOADER_END); + ret = cvs_write_i2c(ctx, cmd, len); + if (ret < 0) + break; + + ret = cvs_wait_wake_or_sleep(ctx, CMD_TIMEOUT, + FW_READY_DELAY_MS); + if (ret) + break; + + ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS, + CMD_TIMEOUT); + status = !(ctx->wq_resp.resp.state & + ICVS_DEV_STATE_DOWNLOAD) ? 0 : -EINVAL; + break; + default: + ret = -EINVAL; + break; + } + + if (ret < 0) + return ret; + + return ctx->wq_resp.status = status; +} + +/** + * cvs_set_link_owner - Switch CSI-2 link ownership between host and device + * @ctx: CVS device context + * @owner: Desired owner (ICVS_CSI_LINK_HOST or ICVS_CSI_LINK_CVS) + * + * Called from runtime PM callbacks to claim or release the CSI-2 link. + * Also callable directly for error recovery paths. + * + * Return: 0 on success or negative errno. + */ +int cvs_set_link_owner(struct icvs *ctx, enum icvs_csi_link_owner owner) +{ + struct icvs_cmd cmd = { + .cmd_id = cpu_to_be16(ICVS_HOST_SENSOR_OWNER), + .param.param = owner, + }; + size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.param); + + guard(mutex)(&ctx->lock); + return cvs_send(ctx, &cmd, cmd_size); +} + +/** + * cvs_configure_dev_caps - Configure device capability ownership bits + * @ctx: CVS device context + * + * Tells the CVS device which of its features (privacy LED, RGB camera + * power-up, vision sensing) are controlled by the host, then sends + * SET_DEV_HOST_ID. + * + * Return: 0 on success or negative errno. + */ +static int cvs_configure_dev_caps(struct icvs *ctx) +{ + struct icvs_cmd cmd = { .cmd_id = cpu_to_be16(ICVS_SET_DEV_HOST_ID) }; + size_t sz = sizeof(cmd.cmd_id) + sizeof(cmd.param.host_id); + + if (ctx->quirks & ICVS_NO_CAPS) + return 0; + + if (ctx->quirks & ICVS_HOST_VISION_SENSING) + cmd.param.host_id |= ICVS_HOST_ID_VISION_SENSING; + if (ctx->quirks & ICVS_HOST_PRIV_CTRL) + cmd.param.host_id |= ICVS_HOST_ID_PRIVACY_LED; + if (ctx->quirks & ICVS_HOST_SENSOR_PWR_CTRL) + cmd.param.host_id |= ICVS_HOST_ID_RGBCAMERA_PWRUP; + + guard(mutex)(&ctx->lock); + return cvs_send(ctx, &cmd, sz); +} + +/** + * cvs_core_probe - Shared probe path for I2C & platform instantiation + * @dev: Parent device + * @i2c: I2C client (NULL for platform devices) + * + * Discovers IPU, parses ACPI resources, sets up GPIOs/IRQs, initializes + * sub-device (CSI) and host identifier, and exposes sysfs firmware interface. + * + * Return: 0 on success or negative errno. + */ +static int cvs_core_probe(struct device *dev, struct i2c_client *i2c) +{ + struct pci_dev *ipu = NULL; + struct icvs *ctx; + int ret; + + /* Locate IPU device */ + for (unsigned int i = 0; !ipu && ipu6_pci_tbl[i].vendor; i++) + ipu = pci_get_device(ipu6_pci_tbl[i].vendor, + ipu6_pci_tbl[i].device, NULL); + for (unsigned int i = 0; !ipu && icvs_ipu7_tbl[i].vendor; i++) + ipu = pci_get_device(icvs_ipu7_tbl[i].vendor, + icvs_ipu7_tbl[i].device, NULL); + if (!ipu) + return -ENODEV; + + ret = ipu_bridge_init(&ipu->dev, ipu_bridge_parse_ssdb); + if (ret < 0) + goto err_put_ipu; + + if (!dev_fwnode(dev)) { + ret = -ENXIO; + goto err_put_ipu; + } + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + ret = -ENOMEM; + goto err_put_ipu; + } + + ctx->i2c_client = i2c; + + ret = gpiod_count(dev, NULL); + switch (ret) { + case ICVS_GPIO_SYNC: + ctx->res = ICVS_LIGHTCAP; + break; + case ICVS_GPIO_ASYNC: + ctx->res = ICVS_FULLCAP; + break; + default: + dev_err(dev, "unexpected GPIO count %d\n", ret); + ret = -EINVAL; + goto err_put_ipu; + } + + ret = devm_acpi_dev_add_driver_gpios(dev, + ctx->res == ICVS_FULLCAP ? + icvs_acpi_gpios : + icvs_acpi_lgpios); + if (ret) { + dev_err_probe(dev, ret, "failed to add ACPI GPIOs\n"); + goto err_put_ipu; + } + + ctx->req = devm_gpiod_get(dev, "req", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->req)) { + ret = dev_err_probe(dev, PTR_ERR(ctx->req), + "failed to get req GPIO\n"); + goto err_put_ipu; + } + + ctx->resp = devm_gpiod_get(dev, "resp", GPIOD_IN); + if (IS_ERR(ctx->resp)) { + ret = dev_err_probe(dev, PTR_ERR(ctx->resp), + "failed to get resp GPIO\n"); + goto err_put_ipu; + } + + if (ctx->res == ICVS_FULLCAP) { + struct gpio_desc *wake; + + ctx->rst = devm_gpiod_get(dev, "rst", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->rst)) { + ret = dev_err_probe(dev, PTR_ERR(ctx->rst), + "failed to get rst GPIO\n"); + goto err_put_ipu; + } + + wake = devm_gpiod_get(dev, "wake", GPIOD_IN); + if (IS_ERR(wake)) { + ret = dev_err_probe(dev, PTR_ERR(wake), + "failed to get wake GPIO\n"); + goto err_put_ipu; + } + + ctx->irq = gpiod_to_irq(wake); + if (ctx->irq < 0) { + ret = dev_err_probe(dev, ctx->irq, + "failed to get wake IRQ\n"); + goto err_put_ipu; + } + + ret = devm_request_threaded_irq(dev, ctx->irq, NULL, + cvs_irq_handler, + IRQF_ONESHOT | IRQF_NO_SUSPEND, + "cvs_wake", ctx); + if (ret) { + dev_err_probe(dev, ret, "failed to request IRQ\n"); + goto err_put_ipu; + } + } + + ret = devm_mutex_init(dev, &ctx->lock); + if (ret) + goto err_put_ipu; + + init_completion(&ctx->cmd_completion); + init_waitqueue_head(&ctx->hostwake_event); + INIT_DELAYED_WORK(&ctx->work, cvs_recv); + + if (i2c) { + ret = cvs_hw_init(ctx); + if (ret) { + dev_err(dev, "HW init failed (%d)\n", ret); + /* + * Fallback to GPIO-only mode. + * Some BIOS show the device on the I2C bus, however, + * the device is not accessible via I2C. + */ + ctx->i2c_client = NULL; + goto fail_i2c; + } + + ret = cvs_get_device_caps(ctx, &ctx->caps); + if (ret) { + dev_err_probe(dev, ret, "get caps failed\n"); + goto err_put_ipu; + } + + ret = cvs_configure_dev_caps(ctx); + if (ret) { + dev_err_probe(dev, ret, + "configure dev caps failed\n"); + goto err_put_ipu; + } + } + +fail_i2c: + ret = cvs_csi_init(ctx, dev, i2c); + if (ret) { + dev_err_probe(dev, ret, "CSI init failed\n"); + goto err_put_ipu; + } + + dev_set_drvdata(dev, ctx); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + /* + * Create a PM runtime device link with IPU as consumer and CVS as + * supplier. When the IPU runtime-resumes to start streaming, the PM + * framework automatically resumes CVS first, triggering + * cvs_runtime_resume() which hands CSI-2 link ownership to the host. + */ + ctx->ipu_link = device_link_add(&ipu->dev, dev, + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE | + DL_FLAG_STATELESS); + if (!ctx->ipu_link) { + dev_err(dev, "IPU device link failed\n"); + ret = -ENODEV; + goto err_csi_remove; + } + + if (has_acpi_companion(dev)) + acpi_dev_clear_dependencies(ACPI_COMPANION(dev)); + + put_device(&ipu->dev); + + return 0; + +err_csi_remove: + if (ctx->ipu_link) + device_link_del(ctx->ipu_link); + cvs_csi_remove(ctx); + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + +err_put_ipu: + put_device(&ipu->dev); + + return ret; +} + +/** + * cvs_probe - I2C driver probe entry + * @i2c: I2C client + * + * Return: 0 on success or negative errno. + */ +static int cvs_probe(struct i2c_client *i2c) +{ + return cvs_core_probe(&i2c->dev, i2c); +} + +/** + * cvs_core_remove - Shared remove logic + * @dev: Device + */ +static void cvs_core_remove(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&ctx->work); + cvs_csi_remove(ctx); + + if (ctx->ipu_link) + device_link_del(ctx->ipu_link); + + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + + cvs_reset(ctx); +} + +/** + * cvs_remove - I2C driver remove + * @client: I2C client + */ +static void cvs_remove(struct i2c_client *client) +{ + cvs_core_remove(&client->dev); +} + +/** + * cvs_suspend - System suspend callback + * @dev: Device + * + * Return: 0. + */ +static int __maybe_unused cvs_suspend(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&ctx->work); + + return 0; +} + +/** + * cvs_resume - System resume callback + * @dev: Device + * + * Re-validates I2C link prefix and re-sends host id if transport available. + * + * Return: 0 on success or negative errno if I2C check fails. + */ +static int __maybe_unused cvs_resume(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + int ret; + + if (ctx->i2c_client) { + ret = cvs_hw_init(ctx); + if (ret) + return ret; + return cvs_configure_dev_caps(ctx); + } + + return 0; +} + +/** + * cvs_runtime_resume - Runtime PM resume: claim CSI-2 link ownership + * @dev: Device + * + * Triggered automatically when the IPU (consumer) runtime-resumes, because + * a DL_FLAG_PM_RUNTIME device link makes CVS the supplier. Transfers CSI-2 + * link ownership to the host so the IPU can start receiving sensor frames. + * + * Return: 0 on success or negative errno. + */ +static int __maybe_unused cvs_runtime_resume(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + + return cvs_set_link_owner(ctx, ICVS_CSI_LINK_HOST); +} + +/** + * cvs_runtime_suspend - Runtime PM suspend: release CSI-2 link ownership + * @dev: Device + * + * Called when the streaming reference is dropped by cvs_csi_disable_streams + * via pm_runtime_put_autosuspend. Returns CSI-2 link ownership to CVS firmware. + * + * Return: 0 on success or negative errno. + */ +static int __maybe_unused cvs_runtime_suspend(struct device *dev) +{ + struct icvs *ctx = dev_get_drvdata(dev); + + return cvs_set_link_owner(ctx, ICVS_CSI_LINK_CVS); +} + +static const struct dev_pm_ops __maybe_unused cvs_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cvs_suspend, cvs_resume) + SET_RUNTIME_PM_OPS(cvs_runtime_suspend, cvs_runtime_resume, NULL) +}; + +static const struct acpi_device_id intel_cvs_acpi_match[] = { + { "INTC10DE" }, /* LNL */ + { "INTC10E0" }, /* ARL */ + { "INTC10E1" }, /* PTL */ + { } +}; +MODULE_DEVICE_TABLE(acpi, intel_cvs_acpi_match); + +static struct i2c_driver cvs_driver = { + .driver = { + .name = "intel_cvs", + .acpi_match_table = intel_cvs_acpi_match, + .pm = pm_ptr(&cvs_pm_ops), + }, + .probe = cvs_probe, + .remove = cvs_remove, +}; + +static int cvs_platform_probe(struct platform_device *pdev) +{ + return cvs_core_probe(&pdev->dev, NULL); +} + +static void cvs_platform_remove(struct platform_device *pdev) +{ + cvs_core_remove(&pdev->dev); +} + +/* + * Platform driver structure. + * + * Some platforms may instantiate the CVS device as a platform device + * without I2C support. This driver binding allows such platforms to use the + * CVS core functionality (GPIOs, CSI sub-device) without I2C. + */ +static struct platform_driver cvs_platform_driver = { + .driver = { + .name = "cvs_platform", + .acpi_match_table = intel_cvs_acpi_match, + .pm = pm_ptr(&cvs_pm_ops), + }, + .probe = cvs_platform_probe, + .remove = cvs_platform_remove, +}; + +/** + * cvs_init - Module init registering I2C and platform drivers + * + * Return: 0 on success or negative errno. + */ +static int __init cvs_init(void) +{ + int ret; + + ret = i2c_add_driver(&cvs_driver); + if (ret) + return ret; + + ret = platform_driver_register(&cvs_platform_driver); + if (ret) { + i2c_del_driver(&cvs_driver); + return ret; + } + + return 0; +} + +/** + * cvs_exit - Module exit unregistering drivers + */ +static void __exit cvs_exit(void) +{ + platform_driver_unregister(&cvs_platform_driver); + i2c_del_driver(&cvs_driver); +} +module_init(cvs_init); +module_exit(cvs_exit); + +MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); +MODULE_AUTHOR("Miguel Vadillo "); +MODULE_DESCRIPTION("Intel Vision Sensing Controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/cvs/icvs.h b/drivers/media/i2c/cvs/icvs.h new file mode 100644 index 000000000000..cfa8ef5d975c --- /dev/null +++ b/drivers/media/i2c/cvs/icvs.h @@ -0,0 +1,495 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2026 Intel Corporation + */ + +#ifndef _ICVS_H +#define _ICVS_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * PCI device IDs for all IPU generations that co-exist with the CVS bridge. + * IPU7 (0x645d) is shared by MTL and LNL; IPU7P5 (0xb05d) is shared by + * ARL and PTL. These are not yet in the shared ipu6-pci-table so they are + * listed here alongside the IPU6 family for probe-time IPU discovery. + */ +struct gpio_desc; +struct i2c_client; + +/* + * GPIO resource counts (ACPI enumerated). + * + * 4-GPIO configuration has a wake IRQ and supports asynchronous messaging. + * 2-GPIO configuration has no IRQ, so communication is synchronous only. + */ +#define ICVS_GPIO_ASYNC 4 +#define ICVS_GPIO_SYNC 2 + +/* Firmware response prefix (optional, for protocol revision 2.x or newer) */ +#define ICVS_PREFIX_VAL 0xCAFEB0BA + +/* + * CSI bridge sub-device definitions + */ +/** + * enum icvs_csi_cmd_id - Low-level CSI bridge command identifiers + * + * These numeric IDs are part of the legacy CSI-side protocol not mapped + * directly to the higher level firmware opcodes in enum icvs_command. Only + * a minimal subset is presently issued/handled by the driver. Others are + * reserved for future expansion of the CSI bridge feature set. + * + * @ICVS_CSI_SET_OWNER: Set CSI sensor ownership between host and CVS + * @ICVS_CSI_SET_CONF: Apply CSI link configuration parameters + * @ICVS_CSI_PRIVACY_NOTIF: Notify host of a privacy state transition + */ +enum icvs_csi_cmd_id { + ICVS_CSI_SET_OWNER = 0, + ICVS_CSI_SET_CONF = 2, + ICVS_CSI_PRIVACY_NOTIF = 6, +}; + +/** + * enum icvs_csi_link_owner - CSI-2 link / sensor ownership + * + * Ownership reflects which endpoint currently controls the attached image + * sensor over the CSI-2 link. Transitions may gate streaming or reconfigure + * link parameters. The host requests ownership changes via protocol opcodes + * and may need to assert GPIO signals on platforms without full capability. + * + * @ICVS_CSI_LINK_HOST: Host (Linux) owns the sensor and may start streaming + * @ICVS_CSI_LINK_CVS: CVS firmware owns the sensor (host must not stream) + */ +enum icvs_csi_link_owner { + ICVS_CSI_LINK_HOST, + ICVS_CSI_LINK_CVS, +}; + +/** + * enum icvs_csi_privacy_status - Reported privacy state + * + * Reflects user privacy (e.g. camera LED assertion and stream gating). The + * MAX value is a sentinel used for bounds checking and is not a real state. + * + * @ICVS_CSI_PRIVACY_OFF: Privacy not asserted (LED off, streaming permitted) + * @ICVS_CSI_PRIVACY_ON: Privacy asserted (LED on and/or stream gated) + * @ICVS_CSI_PRIVACY_MAX: Sentinel; not a valid operational value + */ +enum icvs_csi_privacy_status { + ICVS_CSI_PRIVACY_OFF, + ICVS_CSI_PRIVACY_ON, + ICVS_CSI_PRIVACY_MAX +}; + +/** + * enum icvs_csi_pads - Media pads exposed by the CVS sub-device + * + * The bridge presents a single sink (from the remote sensor) and a single + * source (toward the rest of the media graph / consumers). NUM_PADS is used + * for sizing arrays and iteration; it is not a real pad index. + * + * @ICVS_CSI_PAD_SINK: Sink pad receiving frames from remote sensor + * @ICVS_CSI_PAD_SOURCE: Source pad emitting frames to downstream entities + * @ICVS_CSI_NUM_PADS: Count sentinel (array size / iteration bound) + */ +enum icvs_csi_pads { + ICVS_CSI_PAD_SINK, + ICVS_CSI_PAD_SOURCE, + ICVS_CSI_NUM_PADS +}; + +/* + * Core driver structures and functions used by both I2C and platform modes + */ + +/** + * DOC: CVS device quirk flags + * + * These bit flag macros describe per-device behavioral adjustments applied + * after VID/PID matching (see cvs_i2c_check() and cvs_apply_quirks()). They + * allow the driver to selectively alter logic paths for firmware / hardware + * variants without introducing hard-coded conditionals at each call site. + * + * @ICVS_NO_MIPI_CONFIG: Device firmware performs its own MIPI link setup; + * skip sending HOST_SET_MIPI_CONFIG. + * @ICVS_SKIP_FW_RESET: Skip issuing a post firmware-update reset sequence. + * @ICVS_NO_CAPS: Device firmware does not support GET_DEV_CAPABILITY; + * skip capability query and treat caps as unsupported. + * @ICVS_FW_BUF_SIZE_256: Firmware expects chunk transfer buffer size of 256 + * bytes (override defaults if they differ). + * @ICVS_FW_HEADER_SIZE_256: Firmware header size fixed at 256 bytes offset + * for start of payload data. + * @ICVS_HOST_SENSOR_PWR_CTRL: Host must control sensor power sequencing. + * @ICVS_HOST_PRIV_CTRL: Host owns privacy LED gating. + * @ICVS_HOST_VISION_SENSING: Host enables vision sensing capability bit + * @ICVS_NO_FW_UPDATE: Device does not support firmware update. + */ +#define ICVS_NO_MIPI_CONFIG BIT(0) +#define ICVS_SKIP_FW_RESET BIT(1) +#define ICVS_NO_CAPS BIT(2) +#define ICVS_FW_BUF_SIZE_256 BIT(3) +#define ICVS_FW_HEADER_SIZE_256 BIT(4) +#define ICVS_HOST_SENSOR_PWR_CTRL BIT(5) +#define ICVS_HOST_PRIV_CTRL BIT(6) +#define ICVS_HOST_VISION_SENSING BIT(7) +#define ICVS_NO_FW_UPDATE BIT(8) + +/** + * struct icvs_device_quirk - Device-specific quirk entry + * @vid: Vendor ID + * @pid: Product ID + * @quirks: Quirk flags for this device + */ +struct icvs_device_quirk { + u16 vid; + u16 pid; + unsigned long quirks; +}; + +/** + * struct icvs_dt_config - Data type configuration for a virtual channel + * @pixel_width: Pixel width in pixels + * @pixel_height: Pixel height in pixels + * @data_type: MIPI CSI-2 data type (RAW10, RAW12, etc.) + * @reserved: Reserved for future use + */ +struct icvs_dt_config { + u16 pixel_width; + u16 pixel_height; + u8 data_type; + u8 reserved[3]; +}; + +/** + * struct icvs_vc_config - Virtual channel configuration + * @vc: Virtual channel index (0-31) + * @dt_count: Number of data types configured + * @dt_configs: Array of data type configurations (up to 4) + * @reserved: Reserved for future use + */ +struct icvs_vc_config { + u8 vc; + u8 dt_count; + struct icvs_dt_config dt_configs[4]; + u8 reserved[6]; +}; + +/** + * struct icvs_link_cfg - Host to CVS CSI link configuration + * @fps: Frames per second + * @nr_of_lanes: Number of CSI-2 data lanes used + * @phy_mode: 0 = DPHY, 1 = CPHY + * @vc_count: Number of virtual channels enabled + * @vc_configs: Per-VC configuration + * @link_freq: Link frequency in Hz + * @reserved: Reserved for future use + */ +struct icvs_link_cfg { + u8 fps; + u8 nr_of_lanes; + u8 phy_mode; + u8 vc_count; + struct icvs_vc_config vc_configs[4]; + u32 link_freq; + u8 reserved[8]; +}; + +/** + * struct icvs_fw_version - Firmware version tuple + * @major: Major version + * @minor: Minor version + * @hotfix: Hotfix/patch level + * @build: Build number + */ +struct icvs_fw_version { + u32 major; + u32 minor; + u32 hotfix; + u32 build; +}; + +/** + * struct icvs_vid_pid - Device vendor/product IDs + * @v_id: Vendor ID + * @p_id: Product ID + */ +struct icvs_vid_pid { + u16 v_id; + u16 p_id; +}; + +/** + * struct icvs_mipi_data_packet - Encapsulated MIPI link configuration packet + * @cmd_id: Command identifier + * @size: Payload size (bytes) + * @crc: Checksum over payload + * @conf: CSI link configuration + * @reserved: Reserved for future use + */ +struct icvs_mipi_data_packet { + __be16 cmd_id; + u32 size; + u32 crc; + struct icvs_link_cfg conf; + u8 reserved[70]; +} __packed; + +/** + * struct icvs_mipi_read_packet - Read-back MIPI configuration + * @size: Payload size + * @crc: Payload checksum + * @conf: CSI link configuration + */ +struct icvs_mipi_read_packet { + u32 size; + u32 crc; + struct icvs_link_cfg conf; +}; + +/* Host identifier bitfield masks */ +#define ICVS_HOST_ID_RGBCAMERA_PWRUP BIT(31) +#define ICVS_HOST_ID_PRIVACY_LED BIT(30) +#define ICVS_HOST_ID_DEVICE_POWER GENMASK(29, 28) +#define ICVS_HOST_ID_VISION_SENSING BIT(27) + +/* Device state bitfield masks (u8) */ +#define ICVS_DEV_STATE_PRIVACY BIT(0) +#define ICVS_DEV_STATE_ON BIT(1) +#define ICVS_DEV_STATE_SENSOR_OWNER BIT(2) +#define ICVS_DEV_STATE_DOWNLOAD BIT(4) +#define ICVS_DEV_STATE_ERROR BIT(6) +#define ICVS_DEV_STATE_BUSY BIT(7) + +/* Device capability bitfield masks (u16) */ +#define ICVS_CAP_HOST_MIPI_REQUIRED BIT(15) +#define ICVS_CAP_FW_ANTIROLLBACK BIT(14) +#define ICVS_CAP_PRIVACY2VISIONDRIVER BIT(13) +#define ICVS_CAP_FWUPDATE_RESET_HOST BIT(12) +#define ICVS_CAP_NO_CAMERA_FWUPDATE BIT(11) +#define ICVS_CAP_POWER_DOMAIN_SUPPORT BIT(10) +#define ICVS_CAP_FW_FLASHED_IN_PLACE BIT(9) +#define ICVS_CAP_IO_CONTEXT_HOT BIT(8) + +/** + * struct icvs_dev_capabilities - Protocol capabilities reported by device + * @protocol_version_major: Major protocol version + * @protocol_version_minor: Minor protocol version + * @capability: Capability bitfield - use ICVS_CAP_* masks + * @max_packet_time: Max packet processing time (ms) + * @max_post_dl_time: Max post-download time (s) + */ +struct icvs_dev_capabilities { + u8 protocol_version_major; + u8 protocol_version_minor; + u16 capability; + u16 max_packet_time; + u16 max_post_dl_time; +}; + +/** + * struct icvs_cmd - Generic command container + * @cmd_id: Command identifier + * @param: Parameter union providing multiple variants + * @param.param: Raw parameter + * @param.host_id: Host identifier (ICVS_SET_DEV_HOST_ID) - use + * ICVS_HOST_ID_* masks + * @param.conf: CSI link configuration (ICVS_HOST_SET_MIPI_CONFIG) + */ +struct icvs_cmd { + __be16 cmd_id; + union { + u32 param; + u32 host_id; + struct icvs_link_cfg conf; + } param; +} __packed; + +/** + * struct icvs_resp - Firmware response container + * @status: Internal status code + * @cmd_id: Original command identifier + * @resp: Response union containing variant payload + * @resp.state: Device state response (u8, use ICVS_DEV_STATE_* masks) + * @resp.cap: Capability response + * @resp.conf: Link configuration response + * @resp.mipi_read: Raw link config read-back + * @resp.vid_pid: Vendor/product identifiers + * @resp.fw_version: Firmware version tuple + */ +struct icvs_resp { + u32 status; + __be16 cmd_id; + union { + u8 state; + struct icvs_dev_capabilities cap; + struct icvs_link_cfg conf; + struct icvs_mipi_read_packet mipi_read; + struct icvs_vid_pid vid_pid; + struct icvs_fw_version fw_version; + } resp; +}; + +/** + * enum icvs_resources - Device capability / resource category + * + * Categorizes hardware resource availability which influences protocol + * features (e.g. reset control, wake IRQ, GPIO mediated ownership). Light + * capability devices expose a reduced set of control GPIOs; full capability + * devices provide all optional signals and features. NOTSUP represents an + * unsupported or uninitialized state. + * + * @ICVS_NOTSUP: Capability not supported / not yet determined + * @ICVS_LIGHTCAP: Light capability (limited GPIO / no dedicated wake IRQ) + * @ICVS_FULLCAP: Full capability (reset GPIO, wake IRQ, extended protocol) + */ +enum icvs_resources { + ICVS_NOTSUP, + ICVS_LIGHTCAP, + ICVS_FULLCAP, +}; + +/** + * enum icvs_command - Protocol command opcodes (firmware space 0x0800+) + * @ICVS_GET_DEV_STATE: Query current device state bitfield + * @ICVS_GET_DEV_FW_VERSION: Retrieve firmware version tuple + * @ICVS_GET_DEV_VID_PID: Read vendor / product identifiers + * @ICVS_GET_DEV_ERR_CODE: Fetch last error code (if any) + * @ICVS_GET_DEV_CAPABILITY: Read protocol capability structure + * @ICVS_SET_DEV_HOST_ID: Set host identity / ownership bits + * @ICVS_GET_DEV_HOST_ID: Read back host identity + * @ICVS_FW_LOADER_START: Begin firmware download sequence + * @ICVS_FW_LOADER_DATA: Stream a chunk of firmware payload + * @ICVS_FW_LOADER_END: End firmware download / trigger flash + * @ICVS_HOST_GET_MIPI_CONFIG: Request current MIPI CSI link configuration + * @ICVS_HOST_SET_MIPI_CONFIG: Apply new MIPI CSI link configuration + * @ICVS_HOST_SENSOR_OWNER: Toggle CSI sensor ownership (GPIO assist) + */ +enum icvs_command { + ICVS_GET_DEV_STATE = 0x0800, + ICVS_GET_DEV_FW_VERSION = 0x0801, + ICVS_GET_DEV_VID_PID = 0x0802, + ICVS_GET_DEV_ERR_CODE = 0x0803, + ICVS_GET_DEV_CAPABILITY = 0x0804, + ICVS_SET_DEV_HOST_ID = 0x0805, + ICVS_GET_DEV_HOST_ID = 0x0806, + ICVS_FW_LOADER_START = 0x0820, + ICVS_FW_LOADER_DATA = 0x0821, + ICVS_FW_LOADER_END = 0x0822, + ICVS_HOST_GET_MIPI_CONFIG = 0x082F, + ICVS_HOST_SET_MIPI_CONFIG = 0x0830, + ICVS_HOST_SENSOR_OWNER = 0x0831, +}; + +/** + * enum icvs_state - Device state bitfield flags + * + * These flags correspond to bits in the device state byte returned via + * GET_DEV_STATE and decoded in union icvs_dev_state. Multiple bits may be + * asserted simultaneously. Reserved bits are omitted. + * + * @ICVS_DEVICE_OFF_STATE: Raw zero value; device is powered off or + * not yet ready + * @ICVS_DEVICE_PRIVACY_ON: Privacy mode active (LED asserted and/or + * stream gated) + * @ICVS_DEVICE_ON_STATE: Device powered and responsive to protocol commands + * @ICVS_DEVICE_SENSOR_OWNER: CVS currently owns the attached CSI sensor + * @ICVS_DEVICE_DWNLD_STATE: Firmware download / flash operation in progress + * @ICVS_DEVICE_ERROR_STATE: Device has reported an error (query + * ICVS_GET_DEV_ERR_CODE) + * @ICVS_DEVICE_BUSY_STATE: Device is busy processing a prior command + */ +enum icvs_state { + ICVS_DEVICE_OFF_STATE = 0x00, + ICVS_DEVICE_PRIVACY_ON = BIT(0), + ICVS_DEVICE_ON_STATE = BIT(1), + ICVS_DEVICE_SENSOR_OWNER = BIT(2), + ICVS_DEVICE_DWNLD_STATE = BIT(4), + ICVS_DEVICE_ERROR_STATE = BIT(6), + ICVS_DEVICE_BUSY_STATE = BIT(7), +}; + +/** + * struct icvs - Core CVS device context + * @i2c_client: I2C client (NULL in platform-only mode) + * @work: Delayed work for polling device state / completion + * @wq_resp: Last response container populated by workqueue + * @cmd_completion: Completion for command waiters + * @lock: Mutex protecting command submission & shared state + * @subdev: V4L2 sub-device representing the CSI bridge entity + * @remote: Remote media pad connected to upstream camera sensor + * @notifier: Async notifier for remote sensor discovery + * @ctrl_handler: V4L2 control handler + * @freq_ctrl: (future) frequency control pointer + * @pads: Local media pads (sink/source) + * @nr_of_lanes: Active CSI-2 lane count + * @link_freq: Current link frequency (Hz) + * @ipu_link: PM runtime device link (IPU consumer, CVS supplier) + * @res: Resource capability (light/full) + * @caps: Reported device protocol capabilities + * @prefix: Firmware response prefix present + * @quirks: Device-specific quirk flags + * @rst: Reset GPIO (full capability only) + * @req: Request GPIO (ownership signaling) + * @resp: Response GPIO (ownership signaling) + * @irq: Wake IRQ (full capability) + * @hostwake_event: Waitqueue for wake events + * @hostwake_event_arg: Wake event flag + */ +struct icvs { + struct i2c_client *i2c_client; + struct delayed_work work; + struct icvs_resp wq_resp; + struct completion cmd_completion; + struct mutex lock; /* Protects command execution and device state */ + struct v4l2_subdev subdev; + struct media_pad *remote; + struct v4l2_async_notifier notifier; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *freq_ctrl; + struct media_pad pads[ICVS_CSI_NUM_PADS]; + u32 nr_of_lanes; + u64 link_freq; + struct device_link *ipu_link; + enum icvs_resources res; + struct icvs_dev_capabilities caps; + bool prefix; + unsigned long quirks; + struct gpio_desc *rst; + struct gpio_desc *req; + struct gpio_desc *resp; + int irq; + wait_queue_head_t hostwake_event; + bool hostwake_event_arg; +}; + +/** + * cvs_dev - Helper returning the struct device for a CVS context + * @ctx: CVS context + * + * Avoids repeating transport conditional logic at each call site when + * acquiring the device pointer for logging or PM operations. + * + * Return: Device pointer (never NULL if @ctx is valid). + */ +static inline struct device *cvs_dev(struct icvs *ctx) +{ + return ctx->i2c_client ? &ctx->i2c_client->dev : ctx->subdev.dev; +} + +/* Cross-unit interfaces */ +int cvs_send(struct icvs *ctx, struct icvs_cmd *cmd, size_t len); +int cvs_set_link_owner(struct icvs *ctx, enum icvs_csi_link_owner owner); +int cvs_csi_init(struct icvs *ctx, struct device *dev, struct i2c_client *i2c); +void cvs_csi_remove(struct icvs *ctx); + +#endif /* _ICVS_H */ diff --git a/drivers/media/i2c/cvs/v4l2.c b/drivers/media/i2c/cvs/v4l2.c new file mode 100644 index 000000000000..3a1ec0059ef7 --- /dev/null +++ b/drivers/media/i2c/cvs/v4l2.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Intel Corporation + * CVS driver - CSI/V4L2 subdev support + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "icvs.h" + +/* + * Helpers + */ +static inline struct icvs *notifier_to_csi(struct v4l2_async_notifier *n) +{ + return container_of(n, struct icvs, notifier); +} + +static inline struct icvs *sd_to_csi(struct v4l2_subdev *sd) +{ + return container_of(sd, struct icvs, subdev); +} + +/* + * Default formats + */ +static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = { + .width = 1, + .height = 1, + .code = MEDIA_BUS_FMT_Y8_1X8, + .field = V4L2_FIELD_NONE, +}; + +/** + * csi_set_link_cfg - Program default CSI-2 link parameters + * @ctx: CVS device context + * + * Populates a HOST_SET_MIPI_CONFIG command using current lane count and + * link frequency, then submits it to the device. + * Rest of the link parameters are left at firmware defaults. + * + * Return: 0 on success or negative errno. + */ +static int csi_set_link_cfg(struct icvs *ctx) +{ + struct icvs_cmd cmd = { + .cmd_id = cpu_to_be16(ICVS_HOST_SET_MIPI_CONFIG), + .param.conf.nr_of_lanes = ctx->nr_of_lanes, + .param.conf.link_freq = ctx->link_freq, + }; + size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf); + + guard(mutex)(&ctx->lock); + return cvs_send(ctx, &cmd, cmd_size); +} + +/* + * Streaming + */ + +/** + * cvs_csi_enable_streams - Start streaming through the bridge + * @sd: Sub-device pointer + * @state: Active state + * @pad: Pad identifier (must be ICVS_CSI_PAD_SOURCE) + * @streams_mask: Streams to enable (bit 0 supported) + * + * Runtime-resumes the bridge (triggering cvs_runtime_resume() to claim CSI-2 + * link ownership), fetches the link frequency, programs the MIPI configuration, + * and forwards the enable request downstream. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct icvs *ctx = sd_to_csi(sd); + struct v4l2_subdev *remote_sd = + media_entity_to_v4l2_subdev(ctx->remote->entity); + struct device *dev = cvs_dev(ctx); + s64 freq; + int ret; + + /* cvs_set_link_owner(ICVS_CSI_LINK_HOST) */ + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + freq = v4l2_get_link_freq(ctx->remote, 0, 0); + if (freq < 0) { + ret = freq; + goto err_rpm_put; + } + ctx->link_freq = freq; + + if (ctx->i2c_client) { + ret = csi_set_link_cfg(ctx); + if (ret < 0) + goto err_rpm_put_sync; + } + + ret = v4l2_subdev_enable_streams(remote_sd, + ctx->remote->index, + streams_mask); + if (ret) + goto err_rpm_put_sync; + + return 0; + +err_rpm_put_sync: + /* Bypass autosuspend to immediately release ownership on error */ + pm_runtime_put_sync(dev); + return ret; +err_rpm_put: + pm_runtime_put_autosuspend(dev); + return ret; +} + +/** + * cvs_csi_disable_streams - Stop streaming through the bridge + * @sd: Sub-device pointer + * @state: Active state + * @pad: Pad identifier (must be ICVS_CSI_PAD_SOURCE) + * @streams_mask: Streams to disable (bit 0 supported) + * + * Disables the remote sensor stream then drops the PM reference acquired + * during enable. After the autosuspend delay, cvs_runtime_suspend() will + * return CSI-2 link ownership to CVS firmware. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct icvs *ctx = sd_to_csi(sd); + struct v4l2_subdev *remote_sd = + media_entity_to_v4l2_subdev(ctx->remote->entity); + struct device *dev = cvs_dev(ctx); + int ret; + + ret = v4l2_subdev_disable_streams(remote_sd, + ctx->remote->index, + streams_mask); + if (ret) + dev_err(dev, "disable streams failed: %d\n", ret); + + /* cvs_set_link_owner(ICVS_CSI_LINK_CVS) */ + pm_runtime_put_autosuspend(dev); + + return ret; +} + +/* + * Pad operations / formats + */ +/** + * cvs_csi_init_state - Initialize pad formats in subdev state + * @sd: Sub-device + * @state: State container + * + * Sets all pad formats to a minimal 1x1 default. + * + * Return: 0. + */ +static int cvs_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + for (unsigned int i = 0; i < sd->entity.num_pads; i++) + *v4l2_subdev_state_get_format(state, i) = + cvs_csi_format_mbus_default; + + return 0; +} + +/** + * cvs_csi_set_fmt - Negotiate pad format + * @sd: Sub-device + * @state: State + * @format: Desired / returned format + * + * Mirrors sink format onto source pad. Accepts many media bus codes, falling + * back to Y8 if unsupported. Normalizes field setting. + * + * Return: 0. + */ +static int cvs_csi_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *src = + v4l2_subdev_state_get_format(state, ICVS_CSI_PAD_SOURCE); + struct v4l2_mbus_framefmt *sink = + v4l2_subdev_state_get_format(state, ICVS_CSI_PAD_SINK); + + if (format->pad == ICVS_CSI_PAD_SOURCE) { /* source pad mirrors sink */ + *src = *sink; + return 0; + } + + v4l_bound_align_image(&format->format.width, 1, 65536, 0, + &format->format.height, 1, 65536, 0, 0); + + switch (format->format.code) { + /* Accept a large list; default fallback to Y8 */ + case MEDIA_BUS_FMT_RGB444_1X12: + case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: + case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: + case MEDIA_BUS_FMT_RGB565_1X16: + case MEDIA_BUS_FMT_BGR565_2X8_BE: + case MEDIA_BUS_FMT_BGR565_2X8_LE: + case MEDIA_BUS_FMT_RGB565_2X8_BE: + case MEDIA_BUS_FMT_RGB565_2X8_LE: + case MEDIA_BUS_FMT_RGB666_1X18: + case MEDIA_BUS_FMT_RBG888_1X24: + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: + case MEDIA_BUS_FMT_BGR888_1X24: + case MEDIA_BUS_FMT_GBR888_1X24: + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB888_2X12_BE: + case MEDIA_BUS_FMT_RGB888_2X12_LE: + case MEDIA_BUS_FMT_ARGB8888_1X32: + case MEDIA_BUS_FMT_RGB888_1X32_PADHI: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_RGB161616_1X48: + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_UV8_1X8: + case MEDIA_BUS_FMT_UYVY8_1_5X8: + case MEDIA_BUS_FMT_VYUY8_1_5X8: + case MEDIA_BUS_FMT_YUYV8_1_5X8: + case MEDIA_BUS_FMT_YVYU8_1_5X8: + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_VYUY8_2X8: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YVYU8_2X8: + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_UYVY10_2X10: + case MEDIA_BUS_FMT_VYUY10_2X10: + case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_YVYU10_2X10: + case MEDIA_BUS_FMT_Y12_1X12: + case MEDIA_BUS_FMT_UYVY12_2X12: + case MEDIA_BUS_FMT_VYUY12_2X12: + case MEDIA_BUS_FMT_YUYV12_2X12: + case MEDIA_BUS_FMT_YVYU12_2X12: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: + case MEDIA_BUS_FMT_YDYUYDYV8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_VYUY10_1X20: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_YVYU10_1X20: + case MEDIA_BUS_FMT_VUY8_1X24: + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_VYUY12_1X24: + case MEDIA_BUS_FMT_YUYV12_1X24: + case MEDIA_BUS_FMT_YVYU12_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + case MEDIA_BUS_FMT_AYUV8_1X32: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_YUV16_1X48: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + case MEDIA_BUS_FMT_JPEG_1X8: + case MEDIA_BUS_FMT_AHSV8888_1X32: + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SBGGR14_1X14: + case MEDIA_BUS_FMT_SGBRG14_1X14: + case MEDIA_BUS_FMT_SGRBG14_1X14: + case MEDIA_BUS_FMT_SRGGB14_1X14: + case MEDIA_BUS_FMT_SBGGR16_1X16: + case MEDIA_BUS_FMT_SGBRG16_1X16: + case MEDIA_BUS_FMT_SGRBG16_1X16: + case MEDIA_BUS_FMT_SRGGB16_1X16: + break; + default: + format->format.code = MEDIA_BUS_FMT_Y8_1X8; + break; + } + + if (format->format.field == V4L2_FIELD_ANY) + format->format.field = V4L2_FIELD_NONE; + + *sink = format->format; + *src = *sink; + + return 0; +} + +/** + * cvs_csi_get_mbus_config - Provide current CSI-2 bus configuration + * @sd: Sub-device + * @pad: Pad index + * @cfg: Returned bus config + * + * Fills lane ordering and number of lanes; retrieves link frequency from + * remote entity. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_config *cfg) +{ + struct icvs *ctx = sd_to_csi(sd); + s64 freq; + + cfg->type = V4L2_MBUS_CSI2_DPHY; + for (unsigned int i = 0; i < V4L2_MBUS_CSI2_MAX_DATA_LANES; i++) + cfg->bus.mipi_csi2.data_lanes[i] = i + 1; + cfg->bus.mipi_csi2.num_data_lanes = ctx->nr_of_lanes; + + freq = v4l2_get_link_freq(ctx->remote, 0, 0); + if (freq < 0) + return -EINVAL; + + ctx->link_freq = freq; + cfg->link_freq = freq; + + return 0; +} + +static const struct v4l2_subdev_core_ops cvs_csi_subdev_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops cvs_csi_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops cvs_csi_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = cvs_csi_set_fmt, + .get_mbus_config = cvs_csi_get_mbus_config, + .enable_streams = cvs_csi_enable_streams, + .disable_streams = cvs_csi_disable_streams, +}; + +static const struct v4l2_subdev_ops cvs_csi_subdev_ops = { + .core = &cvs_csi_subdev_core_ops, + .video = &cvs_csi_video_ops, + .pad = &cvs_csi_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops cvs_csi_internal_ops = { + .init_state = cvs_csi_init_state, +}; + +static const struct media_entity_operations cvs_csi_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * Async notifier + */ +/** + * cvs_csi_notify_bound - Remote sensor bound callback + * @notifier: Async notifier + * @sd: Remote subdev + * @asc: Async match connection + * + * Locates the source pad of the remote sensor and creates a media link to + * the CVS bridge sink pad enabling it by default. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asc) +{ + struct icvs *ctx = notifier_to_csi(notifier); + int pad; + + pad = media_entity_get_fwnode_pad(&sd->entity, asc->match.fwnode, + MEDIA_PAD_FL_SOURCE); + if (pad < 0) + return pad; + + ctx->remote = &sd->entity.pads[pad]; + + return media_create_pad_link(&sd->entity, pad, &ctx->subdev.entity, + ICVS_CSI_PAD_SINK, MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} + +/** + * cvs_csi_notify_unbind - Remote sensor unbind callback + * @notifier: Notifier + * @sd: Remote subdev + * @asc: Connection + */ +static void cvs_csi_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asc) +{ + struct icvs *ctx = notifier_to_csi(notifier); + + ctx->remote = NULL; +} + +static const struct v4l2_async_notifier_operations cvs_csi_notify_ops = { + .bound = cvs_csi_notify_bound, + .unbind = cvs_csi_notify_unbind, +}; + +/* + * Controls + */ +/** + * cvs_csi_init_controls - Initialize V4L2 controls + * @ctx: CVS context + * + * Currently sets up a read-only privacy control placeholder. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_init_controls(struct icvs *ctx) +{ + struct v4l2_ctrl *privacy_ctrl; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1); + + privacy_ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, NULL, + V4L2_CID_PRIVACY, 0, 1, 1, 0); + if (privacy_ctrl) + privacy_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (ctx->ctrl_handler.error) { + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return ctx->ctrl_handler.error; + } + + ctx->subdev.ctrl_handler = &ctx->ctrl_handler; + + return 0; +} + +/* + * Firmware (graph) parsing + */ +/** + * cvs_csi_parse_firmware - Parse firmware (ACPI graph) endpoints + * @ctx: CVS context + * + * Discovers sink and remote source endpoints, validates lane counts and + * registers an async notifier for the remote sensor. + * + * Return: 0 on success or negative errno. + */ +static int cvs_csi_parse_firmware(struct icvs *ctx) +{ + struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + struct device *dev = cvs_dev(ctx); + struct fwnode_handle *sink_ep, *source_ep; + struct v4l2_async_connection *asc; + int ret; + + sink_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); + v4l2_async_subdev_nf_init(&ctx->notifier, &ctx->subdev); + ctx->notifier.ops = &cvs_csi_notify_ops; + + ret = v4l2_fwnode_endpoint_parse(sink_ep, &ep); + if (ret) + goto err_nf_cleanup; + + ctx->nr_of_lanes = ep.bus.mipi_csi2.num_data_lanes; + source_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 1, 0, 0); + ret = v4l2_fwnode_endpoint_parse(source_ep, &ep); + fwnode_handle_put(source_ep); + if (ret) + goto err_nf_cleanup; + + if (ctx->nr_of_lanes != ep.bus.mipi_csi2.num_data_lanes) { + ret = -EINVAL; + goto err_nf_cleanup; + } + + asc = v4l2_async_nf_add_fwnode_remote(&ctx->notifier, sink_ep, + struct v4l2_async_connection); + if (IS_ERR(asc)) { + ret = PTR_ERR(asc); + goto err_nf_cleanup; + } + + ret = v4l2_async_nf_register(&ctx->notifier); + if (ret) + goto err_nf_cleanup; + + fwnode_handle_put(sink_ep); + + return 0; + +err_nf_cleanup: + v4l2_async_nf_cleanup(&ctx->notifier); + fwnode_handle_put(sink_ep); + + return ret; +} + +/* + * Public CSI init/cleanup used by core probe/remove + */ +/** + * cvs_csi_init - Initialize CSI/V4L2 sub-device side of bridge + * @ctx: CVS context + * @dev: Parent device + * @i2c: I2C client (may be NULL for platform mode) + * + * Sets up sub-device, media entity pads, async notifier, controls and + * registers with the V4L2 framework. + * + * Return: 0 on success or negative errno. + */ +int cvs_csi_init(struct icvs *ctx, struct device *dev, struct i2c_client *i2c) +{ + int ret; + + if (i2c) { + v4l2_i2c_subdev_init(&ctx->subdev, i2c, &cvs_csi_subdev_ops); + } else { + v4l2_subdev_init(&ctx->subdev, &cvs_csi_subdev_ops); + ctx->subdev.dev = dev; + } + + ctx->subdev.internal_ops = &cvs_csi_internal_ops; + v4l2_set_subdevdata(&ctx->subdev, ctx); + ctx->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + ctx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + ctx->subdev.entity.ops = &cvs_csi_entity_ops; + snprintf(ctx->subdev.name, sizeof(ctx->subdev.name), "Intel CVS"); + + ret = cvs_csi_parse_firmware(ctx); + if (ret) + return ret; + + ret = cvs_csi_init_controls(ctx); + if (ret) + goto err_nf_unreg; + + ctx->pads[ICVS_CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ctx->pads[ICVS_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&ctx->subdev.entity, ICVS_CSI_NUM_PADS, + ctx->pads); + if (ret) + goto err_ctrl_cleanup; + + ctx->subdev.state_lock = ctx->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&ctx->subdev); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_async_register_subdev(&ctx->subdev); + if (ret) + goto err_entity_cleanup; + + return 0; + +err_entity_cleanup: + media_entity_cleanup(&ctx->subdev.entity); + +err_ctrl_cleanup: + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + +err_nf_unreg: + v4l2_async_nf_unregister(&ctx->notifier); + v4l2_async_nf_cleanup(&ctx->notifier); + + return ret; +} + +/** + * cvs_csi_remove - Cleanup CSI/V4L2 sub-device + * @ctx: CVS context + */ +void cvs_csi_remove(struct icvs *ctx) +{ + v4l2_async_nf_unregister(&ctx->notifier); + v4l2_async_nf_cleanup(&ctx->notifier); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_async_unregister_subdev(&ctx->subdev); + v4l2_subdev_cleanup(&ctx->subdev); + media_entity_cleanup(&ctx->subdev.entity); +} + +MODULE_AUTHOR("Miguel Vadillo "); +MODULE_DESCRIPTION("CSI/V4L2 support for Intel Vision Sensing Controller"); +MODULE_LICENSE("GPL"); From c6b1b34b509032c7e7cef9efc63cab55c2ad309e Mon Sep 17 00:00:00 2001 From: Miguel Vadillo Date: Wed, 27 May 2026 10:05:30 -0700 Subject: [PATCH 352/394] media: pci: intel: Add CVS support for IPU bridge driver CVS is located between IPU device and sensors and is available in existing commercial platforms from multiple OEMs. The connection information between them in firmware is not enough to build a V4L2 connection graph. This patch parses the connection properties from the SSDB buffer in DSDT and builds the connection using software nodes. From the IPU bridge point of view, CVS is just like IVSC. Signed-off-by: Miguel Vadillo Tested-by: Mehdi Djait # Dell XPS 13 9350 + IPU7 Reviewed-by: Mehdi Djait Signed-off-by: Sakari Ailus --- drivers/media/pci/intel/ipu-bridge.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index fc6608e33de4..88581a4c081d 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -168,6 +168,9 @@ static const struct acpi_device_id ivsc_acpi_ids[] = { { "INTC1095" }, { "INTC100A" }, { "INTC10CF" }, + { "INTC10DE" }, /* LNL */ + { "INTC10E0" }, /* ARL */ + { "INTC10E1" }, /* PTL */ }; static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev) @@ -221,7 +224,13 @@ static struct device *ipu_bridge_get_ivsc_csi_dev(struct acpi_device *adev) return csi_dev; } - return NULL; + /* Try to locate CVS device on the I2C bus */ + csi_dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev); + if (csi_dev) + return csi_dev; + + /* Fallback to platform bus for CVS device */ + return bus_find_device_by_acpi_dev(&platform_bus_type, adev); } static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor, @@ -235,7 +244,7 @@ static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor, csi_dev = ipu_bridge_get_ivsc_csi_dev(adev); if (!csi_dev) { acpi_dev_put(adev); - dev_err(ADEV_DEV(adev), "Failed to find MEI CSI dev\n"); + dev_err(ADEV_DEV(adev), "Failed to find MEI or CVS CSI dev\n"); return -ENODEV; } From 6767653b4e633d0ceaaaa734a4692306a9970573 Mon Sep 17 00:00:00 2001 From: Rishikesh Donadkar Date: Wed, 20 May 2026 19:27:05 +0530 Subject: [PATCH 353/394] media: ti: j721e-csi2rx: Minor cleanup of loop variables Replace open-coded `i--; for (; i >= 0; i--)` patterns with the idiomatic `while (i--)` in the error unwind paths of csi_async_notifier_complete() and ti_csi2rx_probe(). Also scope loop variables directly in the for statement instead of declaring them at the top of the function in ti_csi2rx_suspend(), ti_csi2rx_resume() and ti_csi2rx_remove(). Change the type to unsigned int in the first two to match csi->num_ctx. Signed-off-by: Rishikesh Donadkar Reviewed-by: Jai Luthra Signed-off-by: Sakari Ailus --- .../platform/ti/j721e-csi2rx/j721e-csi2rx.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 21388284cbaa..ef74e2da19b6 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -485,8 +485,7 @@ static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier) return 0; unregister_dev: - i--; - for (; i >= 0; i--) { + while (i--) { media_entity_remove_links(&csi->ctx[i].vdev.entity); video_unregister_device(&csi->ctx[i].vdev); } @@ -1552,7 +1551,7 @@ static int ti_csi2rx_suspend(struct device *dev) struct ti_csi2rx_ctx *ctx; struct ti_csi2rx_dma *dma; unsigned long flags = 0; - int i, ret = 0; + int ret = 0; /* If device was not in use we can simply suspend */ if (pm_runtime_status_suspended(dev)) @@ -1564,7 +1563,7 @@ static int ti_csi2rx_suspend(struct device *dev) */ writel(0, csi->shim + SHIM_CNTL); - for (i = 0; i < csi->num_ctx; i++) { + for (unsigned int i = 0; i < csi->num_ctx; i++) { ctx = &csi->ctx[i]; dma = &ctx->dma; @@ -1604,7 +1603,7 @@ static int ti_csi2rx_resume(struct device *dev) struct ti_csi2rx_buffer *buf; unsigned long flags = 0; unsigned int reg; - int i, ret = 0; + int ret = 0; /* If device was not in use, we can simply wakeup */ if (pm_runtime_status_suspended(dev)) @@ -1614,7 +1613,7 @@ static int ti_csi2rx_resume(struct device *dev) reg = SHIM_CNTL_PIX_RST; writel(reg, csi->shim + SHIM_CNTL); - for (i = 0; i < csi->num_ctx; i++) { + for (unsigned int i = 0; i < csi->num_ctx; i++) { ctx = &csi->ctx[i]; dma = &ctx->dma; spin_lock_irqsave(&dma->lock, flags); @@ -1755,8 +1754,7 @@ static int ti_csi2rx_probe(struct platform_device *pdev) err_notifier: ti_csi2rx_cleanup_notifier(csi); err_ctx: - i--; - for (; i >= 0; i--) + while (i--) ti_csi2rx_cleanup_ctx(&csi->ctx[i]); ti_csi2rx_cleanup_v4l2(csi); err_dma_chan: @@ -1768,12 +1766,11 @@ static int ti_csi2rx_probe(struct platform_device *pdev) static void ti_csi2rx_remove(struct platform_device *pdev) { struct ti_csi2rx_dev *csi = platform_get_drvdata(pdev); - unsigned int i; if (!pm_runtime_status_suspended(&pdev->dev)) pm_runtime_set_suspended(&pdev->dev); - for (i = 0; i < csi->num_ctx; i++) + for (unsigned int i = 0; i < csi->num_ctx; i++) ti_csi2rx_cleanup_ctx(&csi->ctx[i]); ti_csi2rx_cleanup_notifier(csi); From f368ded872292b01d29d6f1e4a98746f3432c991 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Fri, 22 May 2026 09:57:34 +0800 Subject: [PATCH 354/394] media: ti: j721e-csi2rx: Fix error handling for media_entity_remote_source_pad_unique() The media_entity_remote_source_pad_unique() function returns an error pointer on failure, not NULL. Fix the check to use IS_ERR() and return PTR_ERR() to correctly handle allocation failures. Fixes: 982135c0eac6 ("media: ti: j721e-csi2rx: add a subdev for the core device") Fixes: 3ed9c0a1fdba ("media: ti: j721e-csi2rx: add multistream support") Signed-off-by: Chen Ni Signed-off-by: Sakari Ailus --- .../media/platform/ti/j721e-csi2rx/j721e-csi2rx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index ef74e2da19b6..4769931b1930 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -887,9 +887,9 @@ static int ti_csi2rx_get_stream(struct ti_csi2rx_ctx *ctx) /* Get the source pad connected to this ctx */ pad = media_entity_remote_source_pad_unique(ctx->pad.entity); - if (!pad) { + if (IS_ERR(pad)) { dev_err(csi->dev, "No pad connected to ctx %d\n", ctx->idx); - return -ENODEV; + return PTR_ERR(pad); } state = v4l2_subdev_get_locked_active_state(&csi->subdev); @@ -1183,8 +1183,8 @@ static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd, spin_unlock_irqrestore(&dma->lock, flags); remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); - if (!remote_pad) - return -ENODEV; + if (IS_ERR(remote_pad)) + return PTR_ERR(remote_pad); sink_streams = v4l2_subdev_state_xlate_streams(state, pad, TI_CSI2RX_PAD_SINK, &streams_mask); @@ -1218,8 +1218,8 @@ static int ti_csi2rx_sd_disable_streams(struct v4l2_subdev *sd, writel(0, csi->shim + SHIM_CNTL); remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity); - if (!remote_pad) - return -ENODEV; + if (IS_ERR(remote_pad)) + return PTR_ERR(remote_pad); sink_streams = v4l2_subdev_state_xlate_streams(state, pad, TI_CSI2RX_PAD_SINK, &streams_mask); From 804ed52335e93d8e94dc44ae7d4c31f54efbb5fa Mon Sep 17 00:00:00 2001 From: Michael Riesch Date: Fri, 22 May 2026 23:23:07 +0200 Subject: [PATCH 355/394] Documentation: admin-guide: media: add rk3588 vicap Add a section that describes the Rockchip RK3588 VICAP. Reviewed-by: Mehdi Djait Signed-off-by: Michael Riesch Signed-off-by: Sakari Ailus --- .../admin-guide/media/rkcif-rk3588-vicap.dot | 29 +++++++++++++++++ Documentation/admin-guide/media/rkcif.rst | 32 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 Documentation/admin-guide/media/rkcif-rk3588-vicap.dot diff --git a/Documentation/admin-guide/media/rkcif-rk3588-vicap.dot b/Documentation/admin-guide/media/rkcif-rk3588-vicap.dot new file mode 100644 index 000000000000..f6d3404920b5 --- /dev/null +++ b/Documentation/admin-guide/media/rkcif-rk3588-vicap.dot @@ -0,0 +1,29 @@ +digraph board { + rankdir=TB + n00000007 [label="{{ 0} | rkcif-mipi2\n/dev/v4l-subdev0 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000007:port1 -> n0000000a + n00000007:port1 -> n00000010 [style=dashed] + n00000007:port1 -> n00000016 [style=dashed] + n00000007:port1 -> n0000001c [style=dashed] + n0000000a [label="rkcif-mipi2-id0\n/dev/video0", shape=box, style=filled, fillcolor=yellow] + n00000010 [label="rkcif-mipi2-id1\n/dev/video1", shape=box, style=filled, fillcolor=yellow] + n00000016 [label="rkcif-mipi2-id2\n/dev/video2", shape=box, style=filled, fillcolor=yellow] + n0000001c [label="rkcif-mipi2-id3\n/dev/video3", shape=box, style=filled, fillcolor=yellow] + n00000025 [label="{{ 0} | rkcif-mipi4\n/dev/v4l-subdev1 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000025:port1 -> n00000028 + n00000025:port1 -> n0000002e [style=dashed] + n00000025:port1 -> n00000034 [style=dashed] + n00000025:port1 -> n0000003a [style=dashed] + n00000028 [label="rkcif-mipi4-id0\n/dev/video4", shape=box, style=filled, fillcolor=yellow] + n0000002e [label="rkcif-mipi4-id1\n/dev/video5", shape=box, style=filled, fillcolor=yellow] + n00000034 [label="rkcif-mipi4-id2\n/dev/video6", shape=box, style=filled, fillcolor=yellow] + n0000003a [label="rkcif-mipi4-id3\n/dev/video7", shape=box, style=filled, fillcolor=yellow] + n00000043 [label="{{ 0} | dw-mipi-csi2rx fdd30000.csi\n/dev/v4l-subdev2 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000043:port1 -> n00000007:port0 + n00000048 [label="{{ 0} | dw-mipi-csi2rx fdd50000.csi\n/dev/v4l-subdev3 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000048:port1 -> n00000025:port0 + n0000004d [label="{{} | imx415 3-001a\n/dev/v4l-subdev4 | { 0}}", shape=Mrecord, style=filled, fillcolor=green] + n0000004d:port0 -> n00000043:port0 + n00000051 [label="{{} | imx415 4-001a\n/dev/v4l-subdev5 | { 0}}", shape=Mrecord, style=filled, fillcolor=green] + n00000051:port0 -> n00000048:port0 +} diff --git a/Documentation/admin-guide/media/rkcif.rst b/Documentation/admin-guide/media/rkcif.rst index 2558c121abc4..313a0ea45d16 100644 --- a/Documentation/admin-guide/media/rkcif.rst +++ b/Documentation/admin-guide/media/rkcif.rst @@ -77,3 +77,35 @@ and the following video devices: .. kernel-figure:: rkcif-rk3568-vicap.dot :alt: Topology of the RK3568 Video Capture (VICAP) unit :align: center + +Rockchip RK3588 Video Capture (VICAP) +------------------------------------- + +The RK3588 Video Capture (VICAP) unit features a digital video port and six +MIPI CSI-2 capture interfaces that can receive video data independently. +The DVP accepts parallel video data, BT.656 and BT.1120. +Since the BT.1120 protocol may feature more than one stream, the RK3588 VICAP +DVP features four DMA engines that can capture different streams. +Similarly, the RK3588 VICAP MIPI CSI-2 receivers feature four DMA engines each +to handle different Virtual Channels (VCs). + +The rkcif driver represents this hardware variant by exposing the following +V4L2 subdevices: + +* dw-mipi-csi2rx fdd30000.csi: MIPI CSI-2 receiver connected to MIPI DPHY0 +* dw-mipi-csi2rx fdd50000.csi: MIPI CSI-2 receiver connected to MIPI DPHY1 +* rkcif-mipi2: INTERFACE/CROP block for the MIPI CSI-2 receiver connected to + MIPI DPHY0 +* rkcif-mipi4: INTERFACE/CROP block for the MIPI CSI-2 receiver connected to + MIPI DPHY1 + +and the following video devices: + +* rkcif-mipi2-id{0,1,2,3}: The DMA engines connected to the rkcif-mipi2 + INTERFACE/CROP block. +* rkcif-mipi4-id{0,1,2,3}: The DMA engines connected to the rkcif-mipi4 + INTERFACE/CROP block. + +.. kernel-figure:: rkcif-rk3588-vicap.dot + :alt: Topology of the RK3588 Video Capture (VICAP) unit + :align: center From 4a6b7bf1de948251ca5f09ca1a632f036e52085c Mon Sep 17 00:00:00 2001 From: Michael Riesch Date: Fri, 22 May 2026 23:23:08 +0200 Subject: [PATCH 356/394] media: dt-bindings: add rockchip rk3588 vicap Add documentation for the Rockchip RK3588 Video Capture (VICAP) unit. To that end, make the existing rockchip,rk3568-vicap documentation more general and introduce variant specific constraints. Acked-by: Conor Dooley Signed-off-by: Michael Riesch Signed-off-by: Sakari Ailus --- .../bindings/media/rockchip,rk3568-vicap.yaml | 173 ++++++++++++++++-- 1 file changed, 154 insertions(+), 19 deletions(-) diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml index 18cd0a5a5318..080b64503b1b 100644 --- a/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml +++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml @@ -15,9 +15,15 @@ description: the data from camera sensors, video decoders, or other companion ICs and transfers it into system main memory by AXI bus. + The Rockchip RK3588 Video Capture (VICAP) is similar to its RK3568 + counterpart, but features six MIPI CSI-2 ports and additional connections + to the image signal processor (ISP) blocks. + properties: compatible: - const: rockchip,rk3568-vicap + enum: + - rockchip,rk3568-vicap + - rockchip,rk3588-vicap reg: maxItems: 1 @@ -26,11 +32,8 @@ properties: maxItems: 1 clocks: - items: - - description: ACLK - - description: HCLK - - description: DCLK - - description: ICLK + minItems: 4 + maxItems: 5 clock-names: items: @@ -38,25 +41,19 @@ properties: - const: hclk - const: dclk - const: iclk + - const: iclk1 + minItems: 4 iommus: maxItems: 1 resets: - items: - - description: ARST - - description: HRST - - description: DRST - - description: PRST - - description: IRST + minItems: 5 + maxItems: 9 reset-names: - items: - - const: arst - - const: hrst - - const: drst - - const: prst - - const: irst + minItems: 5 + maxItems: 9 rockchip,grf: $ref: /schemas/types.yaml#/definitions/phandle @@ -67,8 +64,15 @@ properties: ports: $ref: /schemas/graph.yaml#/properties/ports + additionalProperties: false properties: + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + port@0: $ref: /schemas/graph.yaml#/$defs/port-base unevaluatedProperties: false @@ -100,13 +104,75 @@ properties: port@1: $ref: /schemas/graph.yaml#/properties/port - description: Port connected to the MIPI CSI-2 receiver output. + description: Port connected to the MIPI CSI-2 receiver 0 output. properties: endpoint: $ref: video-interfaces.yaml# unevaluatedProperties: false + port@2: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 1 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@3: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 2 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@4: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 3 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@5: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 4 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@6: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the MIPI CSI-2 receiver 5 output. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@10: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the ISP0 input. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + port@11: + $ref: /schemas/graph.yaml#/properties/port + description: Port connected to the ISP1 input. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false required: - compatible - reg @@ -114,6 +180,75 @@ required: - clocks - ports +allOf: + - if: + properties: + compatible: + contains: + const: rockchip,rk3568-vicap + then: + properties: + clocks: + maxItems: 4 + + clock-names: + maxItems: 4 + + resets: + maxItems: 5 + + reset-names: + items: + - const: arst + - const: hrst + - const: drst + - const: prst + - const: irst + + ports: + properties: + port@2: false + + port@3: false + + port@4: false + + port@5: false + + port@6: false + + port@10: false + + port@11: false + + - if: + properties: + compatible: + contains: + const: rockchip,rk3588-vicap + then: + properties: + clocks: + minItems: 5 + + clock-names: + minItems: 5 + + resets: + minItems: 9 + + reset-names: + items: + - const: arst + - const: hrst + - const: drst + - const: irst0 + - const: irst1 + - const: irst2 + - const: irst3 + - const: irst4 + - const: irst5 + additionalProperties: false examples: From 2175323fdf820ebf2d861283d1de7ef394b048c5 Mon Sep 17 00:00:00 2001 From: Michael Riesch Date: Fri, 22 May 2026 23:23:09 +0200 Subject: [PATCH 357/394] media: rockchip: rkcif: add support for rk3588 vicap mipi capture The RK3588 Video Capture (VICAP) unit features a Digital Video Port (DVP) and six MIPI CSI-2 capture interfaces. Add initial support for this variant to the rkcif driver and enable the MIPI CSI-2 capture interfaces. Reviewed-by: Mehdi Djait Signed-off-by: Michael Riesch Signed-off-by: Sakari Ailus --- .../rockchip/rkcif/rkcif-capture-mipi.c | 148 +++++++++++++++++- .../rockchip/rkcif/rkcif-capture-mipi.h | 1 + .../platform/rockchip/rkcif/rkcif-common.h | 2 +- .../media/platform/rockchip/rkcif/rkcif-dev.c | 18 +++ 4 files changed, 163 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c index 9e67160a16e4..bc9518f8db50 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c +++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c @@ -30,6 +30,14 @@ #define RK3568_MIPI_CTRL0_CROP_EN BIT(5) #define RK3568_MIPI_CTRL0_WRDDR(type) ((type) << 1) +#define RK3588_MIPI_CTRL0_DMA_EN BIT(28) +#define RK3588_MIPI_CTRL0_HIGH_ALIGN BIT(27) +#define RK3588_MIPI_CTRL0_WRDDR(type) ((type) << 5) +#define RK3588_MIPI_CTRL0_CROP_EN BIT(4) +#define RK3588_MIPI_CTRL0_PARSE(type) ((type) << 1) + +#define RK3588_MIPI_CTRL_CAP_EN BIT(0) + #define RKCIF_MIPI_CTRL0_DT_ID(id) ((id) << 10) #define RKCIF_MIPI_CTRL0_VC_ID(id) ((id) << 8) #define RKCIF_MIPI_CTRL0_CAP_EN BIT(0) @@ -375,11 +383,8 @@ static u32 rkcif_rk3568_mipi_ctrl0(struct rkcif_stream *stream, const struct rkcif_output_fmt *active_out_fmt) { - u32 ctrl0 = 0; - - ctrl0 |= RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt); - ctrl0 |= RKCIF_MIPI_CTRL0_CAP_EN; - ctrl0 |= RK3568_MIPI_CTRL0_CROP_EN; + u32 ctrl0 = RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt) | + RKCIF_MIPI_CTRL0_CAP_EN | RK3568_MIPI_CTRL0_CROP_EN; if (active_out_fmt->mipi.compact) ctrl0 |= RK3568_MIPI_CTRL0_COMPACT_EN; @@ -481,6 +486,132 @@ const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data = { }, }; +static u32 +rkcif_rk3588_mipi_ctrl0(struct rkcif_stream *stream, + const struct rkcif_output_fmt *active_out_fmt) +{ + u32 ctrl0 = 0; + + ctrl0 |= RK3588_MIPI_CTRL0_DMA_EN; + ctrl0 |= RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt); + ctrl0 |= RK3588_MIPI_CTRL0_CROP_EN; + ctrl0 |= RKCIF_MIPI_CTRL0_CAP_EN; + + switch (active_out_fmt->mipi.type) { + case RKCIF_MIPI_TYPE_RAW8: + break; + case RKCIF_MIPI_TYPE_RAW10: + ctrl0 |= RK3588_MIPI_CTRL0_PARSE(0x1); + if (!active_out_fmt->mipi.compact) + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x1); + break; + case RKCIF_MIPI_TYPE_RAW12: + ctrl0 |= RK3588_MIPI_CTRL0_PARSE(0x2); + if (!active_out_fmt->mipi.compact) + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x1); + break; + case RKCIF_MIPI_TYPE_RGB888: + break; + case RKCIF_MIPI_TYPE_YUV422SP: + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x4); + break; + case RKCIF_MIPI_TYPE_YUV420SP: + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x5); + break; + case RKCIF_MIPI_TYPE_YUV400: + ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x3); + break; + default: + break; + } + + return ctrl0; +} + +const struct rkcif_mipi_match_data rkcif_rk3588_vicap_mipi_match_data = { + .mipi_num = 6, + .mipi_ctrl0 = rkcif_rk3588_mipi_ctrl0, + .regs = { + [RKCIF_MIPI_CTRL] = 0x20, + [RKCIF_MIPI_INTEN] = 0x74, + [RKCIF_MIPI_INTSTAT] = 0x78, + }, + .regs_id = { + [RKCIF_ID0] = { + [RKCIF_MIPI_CTRL0] = 0x00, + [RKCIF_MIPI_CTRL1] = 0x04, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x24, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x2c, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x34, + [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x28, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x30, + [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_CROP_START] = 0x8c, + }, + [RKCIF_ID1] = { + [RKCIF_MIPI_CTRL0] = 0x08, + [RKCIF_MIPI_CTRL1] = 0x0c, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x38, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x40, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x48, + [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x3c, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x44, + [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_CROP_START] = 0x90, + }, + [RKCIF_ID2] = { + [RKCIF_MIPI_CTRL0] = 0x10, + [RKCIF_MIPI_CTRL1] = 0x14, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x4c, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x54, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x5c, + [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x50, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x58, + [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_CROP_START] = 0x94, + }, + [RKCIF_ID3] = { + [RKCIF_MIPI_CTRL0] = 0x18, + [RKCIF_MIPI_CTRL1] = 0x1c, + [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x60, + [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x68, + [RKCIF_MIPI_FRAME0_VLW_Y] = 0x70, + [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x64, + [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x6c, + [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED, + [RKCIF_MIPI_CROP_START] = 0x98, + }, + }, + .blocks = { + { + .offset = 0x100, + }, + { + .offset = 0x200, + }, + { + .offset = 0x300, + }, + { + .offset = 0x400, + }, + { + .offset = 0x500, + }, + { + .offset = 0x600, + }, + }, +}; + static inline unsigned int rkcif_mipi_get_reg(struct rkcif_interface *interface, unsigned int index) { @@ -631,6 +762,13 @@ static int rkcif_mipi_start_streaming(struct rkcif_stream *stream) rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL1, ctrl1); rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL0, ctrl0); + /* + * TODO: This bit has a different meaning on the RK3568, but it is + * set there by default anyway. While correct, this is not exactly + * nice and shall be reworked during the next refactoring. + */ + rkcif_mipi_write(interface, RKCIF_MIPI_CTRL, RK3588_MIPI_CTRL_CAP_EN); + ret = 0; out: diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h index 7f16eadc474c..7edaca44f653 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h +++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h @@ -13,6 +13,7 @@ #include "rkcif-common.h" extern const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data; +extern const struct rkcif_mipi_match_data rkcif_rk3588_vicap_mipi_match_data; int rkcif_mipi_register(struct rkcif_device *rkcif); diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-common.h b/drivers/media/platform/rockchip/rkcif/rkcif-common.h index dd92cfbc879f..4d9211ba9bda 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-common.h +++ b/drivers/media/platform/rockchip/rkcif/rkcif-common.h @@ -27,7 +27,7 @@ #include "rkcif-regs.h" #define RKCIF_DRIVER_NAME "rockchip-cif" -#define RKCIF_CLK_MAX 4 +#define RKCIF_CLK_MAX 5 enum rkcif_format_type { RKCIF_FMT_TYPE_INVALID, diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c index b4cf1146f131..be3a174b9aab 100644 --- a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c +++ b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c @@ -53,6 +53,20 @@ static const struct rkcif_match_data rk3568_vicap_match_data = { .mipi = &rkcif_rk3568_vicap_mipi_match_data, }; +static const char *const rk3588_vicap_clks[] = { + "aclk", + "hclk", + "dclk", + "iclk", + "iclk1", +}; + +static const struct rkcif_match_data rk3588_vicap_match_data = { + .clks = rk3588_vicap_clks, + .clks_num = ARRAY_SIZE(rk3588_vicap_clks), + .mipi = &rkcif_rk3588_vicap_mipi_match_data, +}; + static const struct of_device_id rkcif_plat_of_match[] = { { .compatible = "rockchip,px30-vip", @@ -62,6 +76,10 @@ static const struct of_device_id rkcif_plat_of_match[] = { .compatible = "rockchip,rk3568-vicap", .data = &rk3568_vicap_match_data, }, + { + .compatible = "rockchip,rk3588-vicap", + .data = &rk3588_vicap_match_data, + }, {} }; MODULE_DEVICE_TABLE(of, rkcif_plat_of_match); From 3eb9ba0da0ab73e135f93ffccf07381ba11f100e Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 7 Apr 2026 01:15:52 +0300 Subject: [PATCH 358/394] Revert "media: venus: hfi_platform: Correct supported codecs for sc7280" This reverts commit c0ab2901fc68 ("media: venus: hfi_platform: Correct supported codecs for sc7280"). The codecs might be deprecated, but they still work (somewhat) perfectly and don't cause any issues with the rest of the system. Reenable VP8 codecs by reverting the offending commit. Tested with fluster: |Test|FFmpeg-VP8-v4l2m2m|GStreamer-VP8-V4L2| |TOTAL|50/61|50/61| |TOTAL TIME|12.171s|11.824s| Fixes: c0ab2901fc68 ("media: venus: hfi_platform: Correct supported codecs for sc7280") Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/venus/hfi_parser.c | 6 ++--- .../media/platform/qcom/venus/hfi_platform.c | 24 ------------------- .../media/platform/qcom/venus/hfi_platform.h | 2 -- 3 files changed, 2 insertions(+), 30 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 92765f9c8873..c4cf6cd50a9a 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -268,7 +268,6 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) const struct hfi_plat_caps *caps = NULL; u32 enc_codecs, dec_codecs, count = 0; unsigned int entries; - int ret; plat = hfi_platform_get(core->res->hfi_version); if (!plat) @@ -277,9 +276,8 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) if (inst) return 0; - ret = hfi_platform_get_codecs(core, &enc_codecs, &dec_codecs, &count); - if (ret) - return ret; + if (plat->codecs) + plat->codecs(core, &enc_codecs, &dec_codecs, &count); if (plat->capabilities) caps = plat->capabilities(core, &entries); diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c index cde7f93045ac..f19572ab1d16 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.c +++ b/drivers/media/platform/qcom/venus/hfi_platform.c @@ -2,9 +2,7 @@ /* * Copyright (c) 2020, The Linux Foundation. All rights reserved. */ -#include #include "hfi_platform.h" -#include "core.h" const struct hfi_platform *hfi_platform_get(enum hfi_version version) { @@ -73,25 +71,3 @@ hfi_platform_get_codec_lp_freq(struct venus_core *core, return freq; } - -int -hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs, - u32 *dec_codecs, u32 *count) -{ - const struct hfi_platform *plat; - - plat = hfi_platform_get(core->res->hfi_version); - if (!plat) - return -EINVAL; - - if (plat->codecs) - plat->codecs(core, enc_codecs, dec_codecs, count); - - if (IS_IRIS2_1(core)) { - *enc_codecs &= ~HFI_VIDEO_CODEC_VP8; - *dec_codecs &= ~HFI_VIDEO_CODEC_VP8; - } - - return 0; -} - diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h index 5e4f8013a6b1..a0b6d19f3e1a 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform.h +++ b/drivers/media/platform/qcom/venus/hfi_platform.h @@ -74,6 +74,4 @@ unsigned long hfi_platform_get_codec_vsp_freq(struct venus_core *core, unsigned long hfi_platform_get_codec_lp_freq(struct venus_core *core, enum hfi_version version, u32 codec, u32 session_type); -int hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs, - u32 *dec_codecs, u32 *count); #endif From 631cc1c72475bcf8365fc1772699b54249ca01e1 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 15 May 2026 14:59:25 +0300 Subject: [PATCH 359/394] media: dt-bindings: qcom,qcm2290-venus: add Venus on SM6115 The Qualcomm SM6115 platform contains the AR50_Lite core similar to the one found on the QCM2290. Define new platform-specific compatible, while using QCM2290 as a fallback. Signed-off-by: Dmitry Baryshkov [bod: Fixed indentation of compats] Signed-off-by: Bryan O'Donoghue --- .../devicetree/bindings/media/qcom,qcm2290-venus.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml b/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml index 7e6dc410c2d2..5977e7d0a71b 100644 --- a/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml @@ -18,7 +18,11 @@ allOf: properties: compatible: - const: qcom,qcm2290-venus + oneOf: + - items: + - const: qcom,sm6115-venus + - const: qcom,qcm2290-venus + - const: qcom,qcm2290-venus power-domains: maxItems: 3 From e49686fff1f11ed3085b7b9b56a20ba0ebd9a509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Apitzsch?= Date: Tue, 26 May 2026 16:24:24 +0300 Subject: [PATCH 360/394] media: dt-bindings: venus: Add qcom,msm8939 schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a schema description for the Venus video decoder/encoder IP in MSM8939. Signed-off-by: André Apitzsch Reviewed-by: Bryan O'Donoghue Reviewed-by: Krzysztof Kozlowski Signed-off-by: Erikas Bitovtas Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- .../bindings/media/qcom,msm8939-venus.yaml | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml diff --git a/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml b/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml new file mode 100644 index 000000000000..10a50a410748 --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/qcom,msm8939-venus.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm MSM8939 Venus video encode and decode accelerators + +maintainers: + - André Apitzsch + - Erikas Bitovtas + +description: + The Venus IP is a video encode and decode accelerator present + on Qualcomm platforms + +allOf: + - $ref: qcom,venus-common.yaml# + +properties: + compatible: + const: qcom,msm8939-venus + + power-domains: + maxItems: 3 + + power-domain-names: + items: + - const: venus + - const: vcodec0 + - const: vcodec1 + + clocks: + maxItems: 5 + + clock-names: + items: + - const: core + - const: iface + - const: bus + - const: vcodec0_core + - const: vcodec1_core + + iommus: + maxItems: 1 + +required: + - compatible + - iommus + - power-domain-names + +unevaluatedProperties: false + +examples: + - | + #include + #include + + video-codec@1d00000 { + compatible = "qcom,msm8939-venus"; + reg = <0x01d00000 0xff000>; + interrupts = ; + clocks = <&gcc GCC_VENUS0_VCODEC0_CLK>, + <&gcc GCC_VENUS0_AHB_CLK>, + <&gcc GCC_VENUS0_AXI_CLK>, + <&gcc GCC_VENUS0_CORE0_VCODEC0_CLK>, + <&gcc GCC_VENUS0_CORE1_VCODEC0_CLK>; + clock-names = "core", + "iface", + "bus", + "vcodec0_core", + "vcodec1_core"; + power-domains = <&gcc VENUS_GDSC>, + <&gcc VENUS_CORE0_GDSC>, + <&gcc VENUS_CORE1_GDSC>; + power-domain-names = "venus", "vcodec0", "vcodec1"; + iommus = <&apps_iommu 5>; + memory-region = <&venus_mem>; + }; From e1c9adabb268cc5d56723b7df1da49e59070f309 Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Tue, 31 Mar 2026 10:07:07 +0530 Subject: [PATCH 361/394] media: qcom: venus: drop extra padding in NV12 raw size calculation get_framesize_raw_nv12() currently adds SZ_4K to the UV plane size and an additional SZ_8K to the total buffer size. This inflates the calculated sizeimage and leads userspace to over-allocate buffers without a clear requirement. Remove the extra SZ_4K/SZ_8K padding and compute the NV12 size as the sum of Y and UV planes, keeping the final ALIGN(size, SZ_4K) intact. Fixes: e1cb72de702ad ("media: venus: helpers: move frame size calculations on common place") Signed-off-by: Renjiang Han Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/venus/helpers.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 747c388fe25f..59eee3dd9e06 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -954,8 +954,8 @@ static u32 get_framesize_raw_nv12(u32 width, u32 height) uv_sclines = ALIGN(((height + 1) >> 1), 16); y_plane = y_stride * y_sclines; - uv_plane = uv_stride * uv_sclines + SZ_4K; - size = y_plane + uv_plane + SZ_8K; + uv_plane = uv_stride * uv_sclines; + size = y_plane + uv_plane; return ALIGN(size, SZ_4K); } From 35428ae3a6a40912f1f7b47bb6c65f1a63d0b8f8 Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Tue, 31 Mar 2026 10:07:08 +0530 Subject: [PATCH 362/394] media: qcom: venus: relax encoder frame/blur dimension steps on v4 Encoder HFI capabilities on v4 advertise a 16-pixel step for frame and blur dimensions. This is overly restrictive and can cause userspace caps negotiation to fail even for valid resolutions. Relax the advertised step size to 1 and keep alignment enforcement in buffer layout and size calculations. Fixes: 8b88cabef404e ("media: venus: hfi_plat_v4: Populate codecs and capabilities for v4") Signed-off-by: Renjiang Han Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/venus/hfi_platform_v4.c | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v4.c b/drivers/media/platform/qcom/venus/hfi_platform_v4.c index cda888b56b5d..e0b3652bb440 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform_v4.c +++ b/drivers/media/platform/qcom/venus/hfi_platform_v4.c @@ -136,8 +136,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_H264, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -173,8 +173,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_HEVC, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -195,8 +195,8 @@ static const struct hfi_plat_caps caps[] = { .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, - .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .num_caps = 24, .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, @@ -210,8 +210,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_VP8, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -229,8 +229,8 @@ static const struct hfi_plat_caps caps[] = { .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, - .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, .num_caps = 23, From c53e0550288b2e08b984b24035c471941b7820c7 Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Tue, 31 Mar 2026 10:07:09 +0530 Subject: [PATCH 363/394] media: qcom: venus: relax encoder frame/blur step size on v6 Encoder HFI capabilities on v6 enforce a 16-pixel step for frame and blur dimensions, which does not reflect actual hardware requirements and can reject valid userspace configurations. Relax the step size to 1 while leaving min/max limits unchanged. Fixes: 869d77e706290 ("media: venus: hfi_plat_v6: Populate capabilities for v6") Signed-off-by: Renjiang Han Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/venus/hfi_platform_v6.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c index d8568c08cc36..fb8d10ab3404 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform_v6.c +++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c @@ -173,8 +173,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_HEVC, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 160000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -195,8 +195,8 @@ static const struct hfi_plat_caps caps[] = { .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, - .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .num_caps = 24, .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, .pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, @@ -210,8 +210,8 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_VP8, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 16}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 1}, .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 36864, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 74000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, @@ -229,8 +229,8 @@ static const struct hfi_plat_caps caps[] = { .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, .caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, .caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, - .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16}, - .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16}, + .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1}, + .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1}, .caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, .caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90}, .num_caps = 23, From 86a366c02ddbee289d0f04c63a3ebd940404cd07 Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Tue, 26 May 2026 16:24:26 +0300 Subject: [PATCH 364/394] media: qcom: venus: add power domain enable logic for Venus cores Attach power domains for vdec and venc cores and power them up if a vdec or venc session is started. Vcodec clocks are added and enabled to the core Venus device both for vcodec0 and vcodec1. To ensure they are added only once, introduce a new property "vcodec_clks", which is an array of clocks which are enabled both during decode and encode and is retrieved from the device tree only once. Reviewed-by: Bryan O'Donoghue Signed-off-by: Erikas Bitovtas Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/venus/core.h | 3 + .../media/platform/qcom/venus/pm_helpers.c | 150 +++++++++++++++++- 2 files changed, 146 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 03804c30808e..c1603eebd757 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -79,6 +79,7 @@ struct venus_resources { const struct hfi_ubwc_config *ubwc_conf; const char * const clks[VIDC_CLKS_NUM_MAX]; unsigned int clks_num; + const char * const vcodec_clks[VIDC_VCODEC_CLKS_NUM_MAX]; const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; const char * const vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; unsigned int vcodec_clks_num; @@ -143,6 +144,7 @@ struct venus_format { * @aon_base: AON base address * @irq: Venus irq * @clks: an array of struct clk pointers + * @vcodec_clks: an array of vcodec struct clk pointers * @vcodec0_clks: an array of vcodec0 struct clk pointers * @vcodec1_clks: an array of vcodec1 struct clk pointers * @video_path: an interconnect handle to video to/from memory path @@ -197,6 +199,7 @@ struct venus_core { void __iomem *aon_base; int irq; struct clk *clks[VIDC_CLKS_NUM_MAX]; + struct clk *vcodec_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct clk *vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct clk *vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct icc_path *video_path; diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index 14a4e8311a64..be1cbd5cfe84 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -89,12 +89,23 @@ static void core_clks_disable(struct venus_core *core) static int core_clks_set_rate(struct venus_core *core, unsigned long freq) { - int ret; + const struct venus_resources *res = core->res; + int ret, i; ret = dev_pm_opp_set_rate(core->dev, freq); if (ret) return ret; + if (!res->vcodec_clks_num) + goto set_rates; + + for (i = 0; i < res->vcodec_clks_num; i++) { + ret = clk_set_rate(core->vcodec_clks[i], freq); + if (ret) + return ret; + } + +set_rates: ret = clk_set_rate(core->vcodec0_clks[0], freq); if (ret) return ret; @@ -297,10 +308,33 @@ static int load_scale_v1(struct venus_inst *inst) return ret; } +static int vcodec_domains_get_v1(struct venus_core *core) +{ + struct device *dev = core->dev; + const struct venus_resources *res = core->res; + const struct dev_pm_domain_attach_data vcodec_data = { + .pd_names = res->vcodec_pmdomains, + .num_pd_names = res->vcodec_pmdomains_num, + .pd_flags = PD_FLAG_NO_DEV_LINK, + }; + + if (!res->vcodec_pmdomains) + return 0; + + return devm_pm_domain_attach_list(dev, &vcodec_data, + &core->pmdomains); +} + static int core_get_v1(struct venus_core *core) { + const struct venus_resources *res = core->res; + struct device *dev = core->dev; int ret; + ret = vcodec_domains_get_v1(core); + if (ret < 0) + return ret; + ret = core_clks_get(core); if (ret) return ret; @@ -309,9 +343,79 @@ static int core_get_v1(struct venus_core *core) if (ret) return ret; + if (!res->vcodec_pmdomains) + return 0; + + ret = vcodec_clks_get(core, dev, core->vcodec_clks, + res->vcodec_clks); + if (ret) + return ret; + return 0; } +static int vcodec_domains_enable(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + struct device *pd_dev; + int i = 0, ret; + + if (!res->vcodec_pmdomains) + return 0; + + for (; i < res->vcodec_pmdomains_num; i++) { + pd_dev = core->pmdomains->pd_devs[i]; + ret = pm_runtime_resume_and_get(pd_dev); + if (ret) + goto err; + } + + return 0; +err: + while (i--) { + pd_dev = core->pmdomains->pd_devs[i]; + pm_runtime_put_sync(pd_dev); + } + return ret; +} + +static void vcodec_domains_disable(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + struct device *pd_dev; + int i = res->vcodec_pmdomains_num; + + if (!res->vcodec_pmdomains) + return; + + while (i--) { + pd_dev = core->pmdomains->pd_devs[i]; + pm_runtime_put_sync(pd_dev); + } +} + +static int vcodec_domains_set_hw(struct venus_core *core, bool is_hw) +{ + const struct venus_resources *res = core->res; + struct device *pd_dev; + int i = 0, ret; + + for (; i < res->vcodec_pmdomains_num; i++) { + pd_dev = core->pmdomains->pd_devs[i]; + ret = dev_pm_genpd_set_hwmode(pd_dev, is_hw); + if (ret && ret != -EOPNOTSUPP) + goto err; + } + + return 0; +err: + while (i--) { + pd_dev = core->pmdomains->pd_devs[i]; + dev_pm_genpd_set_hwmode(pd_dev, !is_hw); + } + return ret; +} + static void core_put_v1(struct venus_core *core) { } @@ -320,11 +424,43 @@ static int core_power_v1(struct venus_core *core, int on) { int ret = 0; - if (on == POWER_ON) - ret = core_clks_enable(core); - else - core_clks_disable(core); + if (on == POWER_ON) { + ret = vcodec_domains_enable(core); + if (ret) + return ret; + ret = core_clks_enable(core); + if (ret) + goto fail_pmdomains; + + if (!core->res->vcodec_pmdomains) + return 0; + + ret = vcodec_clks_enable(core, core->vcodec_clks); + if (ret) + goto fail_core_clks; + + ret = vcodec_domains_set_hw(core, true); + if (ret) + goto fail_vcodec_clks; + + } else { + if (core->res->vcodec_pmdomains) { + vcodec_domains_set_hw(core, false); + vcodec_clks_disable(core, core->vcodec_clks); + } + core_clks_disable(core); + vcodec_domains_disable(core); + } + + return 0; + +fail_vcodec_clks: + vcodec_clks_disable(core, core->vcodec_clks); +fail_core_clks: + core_clks_disable(core); +fail_pmdomains: + vcodec_domains_disable(core); return ret; } @@ -875,7 +1011,7 @@ static int venc_power_v4(struct device *dev, int on) return ret; } -static int vcodec_domains_get(struct venus_core *core) +static int vcodec_domains_get_v4(struct venus_core *core) { int ret; struct device *dev = core->dev; @@ -999,7 +1135,7 @@ static int core_get_v4(struct venus_core *core) if (ret) return ret; - ret = vcodec_domains_get(core); + ret = vcodec_domains_get_v4(core); if (ret) return ret; From cc41341d1209169d7928794ae90386c17827cc76 Mon Sep 17 00:00:00 2001 From: Erikas Bitovtas Date: Tue, 26 May 2026 16:24:27 +0300 Subject: [PATCH 365/394] media: qcom: venus: add codec blacklist mechanism Add decode and encode blacklist properties to allow disabling different codecs per Venus device, instead of doing it per HFI version. Reviewed-by: Bryan O'Donoghue Signed-off-by: Erikas Bitovtas Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/venus/core.c | 5 +++++ drivers/media/platform/qcom/venus/core.h | 2 ++ drivers/media/platform/qcom/venus/hfi_parser.c | 10 +++++----- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index a87e8afb23df..bd88e2b484a5 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -178,6 +179,8 @@ static void venus_sys_error_handler(struct work_struct *work) static u32 to_v4l2_codec_type(u32 codec) { switch (codec) { + case HFI_VIDEO_CODEC_HEVC: + return V4L2_PIX_FMT_HEVC; case HFI_VIDEO_CODEC_H264: return V4L2_PIX_FMT_H264; case HFI_VIDEO_CODEC_H263: @@ -684,6 +687,8 @@ static const struct venus_resources msm8916_res = { .vmem_addr = 0, .dma_mask = 0xddc00000 - 1, .fwname = "qcom/venus-1.8/venus.mbn", + .dec_codec_blacklist = HFI_VIDEO_CODEC_HEVC | HFI_VIDEO_CODEC_SPARK, + .enc_codec_blacklist = HFI_VIDEO_CODEC_HEVC, .dec_nodename = "video-decoder", .enc_nodename = "video-encoder", }; diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index c1603eebd757..46705a666776 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -88,6 +88,8 @@ struct venus_resources { const char **opp_pmdomain; unsigned int opp_pmdomain_num; unsigned int vcodec_num; + const u32 dec_codec_blacklist; + const u32 enc_codec_blacklist; const char * const resets[VIDC_RESETS_NUM_MAX]; unsigned int resets_num; enum hfi_version hfi_version; diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index c4cf6cd50a9a..b1657443f23f 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -206,11 +206,11 @@ static int parse_codecs(struct venus_core *core, void *data) core->dec_codecs = codecs->dec_codecs; core->enc_codecs = codecs->enc_codecs; - if (IS_V1(core)) { - core->dec_codecs &= ~HFI_VIDEO_CODEC_HEVC; - core->dec_codecs &= ~HFI_VIDEO_CODEC_SPARK; - core->enc_codecs &= ~HFI_VIDEO_CODEC_HEVC; - } + if (core->res->dec_codec_blacklist) + core->dec_codecs &= ~core->res->dec_codec_blacklist; + + if (core->res->enc_codec_blacklist) + core->enc_codecs &= ~core->res->enc_codec_blacklist; return sizeof(*codecs); } From 197789c60a11eaf18b4a2e5d2dbf8d490e10395e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Apitzsch?= Date: Tue, 26 May 2026 16:24:28 +0300 Subject: [PATCH 366/394] media: qcom: venus: Add msm8939 resource struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add msm8939 configuration data and related compatible. Reviewed-by: Dmitry Baryshkov Reviewed-by: Bryan O'Donoghue Signed-off-by: André Apitzsch Signed-off-by: Erikas Bitovtas Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/venus/core.c | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index bd88e2b484a5..243e342b0ae7 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -693,6 +693,45 @@ static const struct venus_resources msm8916_res = { .enc_nodename = "video-encoder", }; +static const struct freq_tbl msm8939_freq_table[] = { + { 489600, 266670000 }, /* 1080p @ 60 */ + { 244800, 133330000 }, /* 1080p @ 30 */ + { 220800, 133330000 }, /* 720p @ 60 */ + { 108000, 133330000 }, /* 720p @ 30 */ + { 72000, 133330000 }, /* VGA @ 60 */ + { 36000, 133330000 }, /* VGA @ 30 */ +}; + +static const struct reg_val msm8939_reg_preset[] = { + { 0xe0020, 0x0aaaaaaa }, + { 0xe0024, 0x0aaaaaaa }, + { 0x80124, 0x00000003 }, +}; + +static const struct venus_resources msm8939_res = { + .freq_tbl = msm8939_freq_table, + .freq_tbl_size = ARRAY_SIZE(msm8939_freq_table), + .reg_tbl = msm8939_reg_preset, + .reg_tbl_size = ARRAY_SIZE(msm8939_reg_preset), + .clks = { "core", "iface", "bus", }, + .clks_num = 3, + .vcodec_clks = { "vcodec0_core", "vcodec1_core" }, + .vcodec_clks_num = 2, + .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" }, + .vcodec_pmdomains_num = 3, + .max_load = 489600, /* 1080p@30 + 1080p@30 */ + .hfi_version = HFI_VERSION_1XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xddc00000 - 1, + .fwname = "qcom/venus-1.8/venus.mbn", + .dec_codec_blacklist = HFI_VIDEO_CODEC_SPARK, + .enc_codec_blacklist = HFI_VIDEO_CODEC_HEVC, + .dec_nodename = "video-decoder", + .enc_nodename = "video-encoder", +}; + static const struct freq_tbl msm8996_freq_table[] = { { 1944000, 520000000 }, /* 4k UHD @ 60 (decode only) */ { 972000, 520000000 }, /* 4k UHD @ 30 */ @@ -1133,6 +1172,7 @@ static const struct venus_resources qcm2290_res = { static const struct of_device_id venus_dt_match[] = { { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, + { .compatible = "qcom,msm8939-venus", .data = &msm8939_res, }, { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, { .compatible = "qcom,msm8998-venus", .data = &msm8998_res, }, { .compatible = "qcom,qcm2290-venus", .data = &qcm2290_res, }, From 45ac2230f929b9f0fae899c4a5c6bf763250fc13 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 29 May 2026 14:27:10 +0300 Subject: [PATCH 367/394] media: iris: Fix use IRQF_NO_AUTOEN when requesting the IRQ Requesting the IRQ and then immediately disabling it is fragile as it leaves a window when the IRQ is still enabled although the underlying device might be not completely setup for IRQ handling. Pass IRQF_NO_AUTOEN instead of calling disable_irq_nosync(). Fixes: fb583a214337 ("media: iris: introduce host firmware interface with necessary hooks") Reviewed-by: Konrad Dybcio Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov [bod: Appended Fix to patch title for -stable clarity] [bod: Added cc stable for backporting] Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_probe.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index d36f0c0e785b..a755a6f73ea6 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -259,12 +259,12 @@ static int iris_probe(struct platform_device *pdev) return PTR_ERR(core->ubwc_cfg); ret = devm_request_threaded_irq(core->dev, core->irq, iris_hfi_isr, - iris_hfi_isr_handler, IRQF_TRIGGER_HIGH, "iris", core); + iris_hfi_isr_handler, + IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN, + "iris", core); if (ret) return ret; - disable_irq_nosync(core->irq); - iris_init_ops(core); core->iris_firmware_data->init_hfi_ops(core); From 96789be88f987d733a07b37279d4ebc8efd6207a Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Fri, 29 May 2026 15:34:58 +0800 Subject: [PATCH 368/394] media: dt-bindings: qcom,sm8550-iris: Add X1P42100 compatible Document the new compatible string "qcom,x1p42100-iris". Unlike SM8550 where the BSE (Bitstream Engine) is clocked implicitly via vcodec0_core, x1p42100 exposes a dedicated BSE clock vcodec0_bse that requires explicit enable/disable and frequency configuration. The SM8550 driver has no knowledge of this clock and therefore cannot operate x1p42100 hardware correctly. Reviewed-by: Bryan O'Donoghue Reviewed-by: Krzysztof Kozlowski Signed-off-by: Wangao Wang Signed-off-by: Bryan O'Donoghue --- .../bindings/media/qcom,sm8550-iris.yaml | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml b/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml index 9c4b760508b5..0400ca1bff05 100644 --- a/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml @@ -26,6 +26,7 @@ properties: - qcom,qcs8300-iris - qcom,sm8550-iris - qcom,sm8650-iris + - qcom,x1p42100-iris reg: maxItems: 1 @@ -41,13 +42,16 @@ properties: - const: mmcx clocks: - maxItems: 3 + minItems: 3 + maxItems: 4 clock-names: + minItems: 3 items: - const: iface - const: core - const: vcodec0_core + - const: vcodec0_bse firmware-name: maxItems: 1 @@ -115,6 +119,23 @@ allOf: maxItems: 1 reset-names: maxItems: 1 + - if: + properties: + compatible: + enum: + - qcom,x1p42100-iris + then: + properties: + clocks: + minItems: 4 + clock-names: + minItems: 4 + else: + properties: + clocks: + maxItems: 3 + clock-names: + maxItems: 3 unevaluatedProperties: false From 58cbdcfb3533f30952ee44e4244ff358fbff9656 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 15 May 2026 13:56:35 +0300 Subject: [PATCH 369/394] media: dt-bindings: Document SC8280XP/SM8350 Iris The Iris block on SM8350 and SC8280XP is compatible with the Iris (Venus) on SM8250. Describing in the bindings that the block is Iris v2 and not Venus. Document SM8350 and SC8280XP IP cores, using qcom,sm8250-venus as a fallback compatible. Signed-off-by: Dmitry Baryshkov Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bryan O'Donoghue --- .../devicetree/bindings/media/qcom,sm8250-venus.yaml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml index 43a10d9f664e..aca748e42aca 100644 --- a/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml @@ -10,15 +10,21 @@ maintainers: - Stanimir Varbanov description: | - The Venus IP is a video encode and decode accelerator present - on Qualcomm platforms + The Iris v2.xx IP is a video encode and decode accelerator present on + Qualcomm platforms allOf: - $ref: qcom,venus-common.yaml# properties: compatible: - const: qcom,sm8250-venus + oneOf: + - const: qcom,sm8250-venus + - items: + - enum: + - qcom,sc8280xp-iris + - qcom,sm8350-iris + - const: qcom,sm8250-venus power-domains: minItems: 2 From 897972882725db9d0d417e168b0f756ddf348a23 Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Tue, 12 May 2026 16:55:10 +0800 Subject: [PATCH 370/394] media: qcom: iris: Add intra refresh support for gen1 encoder Add support for intra refresh configuration on gen1 encoder by enabling V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD and V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE controls. Reviewed-by: Dikshita Agarwal Tested-by: Neil Armstrong # on SM8650-HDK Signed-off-by: Wangao Wang Reviewed-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_ctrls.c | 39 ++++++++++++++++++- drivers/media/platform/qcom/iris/iris_ctrls.h | 3 +- .../media/platform/qcom/iris/iris_hfi_gen1.c | 19 +++++++++ .../qcom/iris/iris_hfi_gen1_command.c | 8 ++++ .../qcom/iris/iris_hfi_gen1_defines.h | 13 +++++++ .../media/platform/qcom/iris/iris_hfi_gen2.c | 2 +- 6 files changed, 81 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index ef7adac3764d..287cfc532941 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -970,7 +970,44 @@ int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) &hfi_val, sizeof(u32)); } -int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +int iris_set_ir_period_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct v4l2_pix_format_mplane *fmt = &inst->fmt_dst->fmt.pix_mp; + u32 codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16; + u32 ir_period = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_intra_refresh hfi_val; + + if (!ir_period) + return -EINVAL; + + if (inst->fw_caps[IR_TYPE].value == + V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM) { + hfi_val.mode = HFI_INTRA_REFRESH_RANDOM; + } else if (inst->fw_caps[IR_TYPE].value == + V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC) { + hfi_val.mode = HFI_INTRA_REFRESH_CYCLIC; + } else { + return -EINVAL; + } + + /* + * Calculate the number of macroblocks in a frame, + * then determine how many macroblocks need to be + * refreshed within one ir_period. + */ + hfi_val.mbs = (fmt->width / codec_align) * (fmt->height / codec_align); + hfi_val.mbs /= ir_period; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + &hfi_val, sizeof(hfi_val)); +} + +int iris_set_ir_period_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; struct vb2_queue *q = v4l2_m2m_get_dst_vq(inst->m2m_ctx); diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h index 9518803577bc..a0d5338bdc91 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.h +++ b/drivers/media/platform/qcom/iris/iris_ctrls.h @@ -34,7 +34,8 @@ int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); -int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_ir_period_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_ir_period_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_properties(struct iris_inst *inst, u32 plane); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c index 60f51a1ba941..9344d20042fd 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c @@ -224,6 +224,25 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .flags = CAP_FLAG_OUTPUT_PORT, .set = iris_set_qp_range, }, + { + .cap_id = IR_TYPE, + .min = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM, + .max = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC, + .step_or_mask = BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM) | + BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC), + .value = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + }, + { + .cap_id = IR_PERIOD, + .min = 0, + .max = ((4096 * 2304) >> 8), + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_ir_period_gen1, + }, }; static const u32 sm8250_vdec_input_config_param_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 83373862655f..051ba0d157c7 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -687,6 +687,14 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p packet->shdr.hdr.size += sizeof(u32) + sizeof(*plane_actual_info); break; } + case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: { + struct hfi_intra_refresh *in = pdata, *intra_refresh = prop_data; + + intra_refresh->mode = in->mode; + intra_refresh->mbs = in->mbs; + packet->shdr.hdr.size += sizeof(u32) + sizeof(*intra_refresh); + break; + } default: return -EINVAL; } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h index 42226ccee3d9..04c79ee0463d 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h @@ -139,6 +139,14 @@ #define HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL 0x2005003 #define HFI_PROPERTY_PARAM_VENC_RATE_CONTROL 0x2005004 #define HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2 0x2005009 + +#define HFI_INTRA_REFRESH_NONE 0x1 +#define HFI_INTRA_REFRESH_CYCLIC 0x2 +#define HFI_INTRA_REFRESH_ADAPTIVE 0x3 +#define HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE 0x4 +#define HFI_INTRA_REFRESH_RANDOM 0x5 + +#define HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH 0x200500d #define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020 #define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001 #define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008 @@ -447,6 +455,11 @@ struct hfi_framerate { u32 framerate; }; +struct hfi_intra_refresh { + u32 mode; + u32 mbs; +}; + struct hfi_event_data { u32 error; u32 height; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c index ce8490d64854..401519fef0e2 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -713,7 +713,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { .value = 0, .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, - .set = iris_set_ir_period, + .set = iris_set_ir_period_gen2, }, }; From 6870381285b8b8161235d54456a7c4fe0e94f32e Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Tue, 12 May 2026 16:55:11 +0800 Subject: [PATCH 371/394] media: qcom: iris: Add Long-Term Reference support for encoder Add Long-Term Reference(LTR) frame support for both gen1 and gen2 encoders by enabling the following V4L2 controls: V4L2_CID_MPEG_VIDEO_LTR_COUNT V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX Tested-by: Neil Armstrong # on SM8650-HDK Reviewed-by: Dikshita Agarwal Signed-off-by: Wangao Wang Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_ctrls.c | 128 ++++++++++++++++++ drivers/media/platform/qcom/iris/iris_ctrls.h | 5 + .../media/platform/qcom/iris/iris_hfi_gen1.c | 30 ++++ .../qcom/iris/iris_hfi_gen1_command.c | 25 ++++ .../qcom/iris/iris_hfi_gen1_defines.h | 24 ++++ .../media/platform/qcom/iris/iris_hfi_gen2.c | 30 ++++ .../qcom/iris/iris_hfi_gen2_defines.h | 3 + .../platform/qcom/iris/iris_platform_common.h | 6 + .../platform/qcom/iris/iris_vpu_buffer.c | 20 ++- 9 files changed, 267 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index 287cfc532941..87d10ce1a9a5 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -112,6 +112,12 @@ static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id) return IR_TYPE; case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD: return IR_PERIOD; + case V4L2_CID_MPEG_VIDEO_LTR_COUNT: + return LTR_COUNT; + case V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES: + return USE_LTR; + case V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX: + return MARK_LTR; default: return INST_FW_CAP_MAX; } @@ -213,6 +219,12 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id) return V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE; case IR_PERIOD: return V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD; + case LTR_COUNT: + return V4L2_CID_MPEG_VIDEO_LTR_COUNT; + case USE_LTR: + return V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES; + case MARK_LTR: + return V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX; default: return 0; } @@ -1033,6 +1045,122 @@ int iris_set_ir_period_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_ty &ir_period, sizeof(u32)); } +int iris_set_ltr_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 ltr_count = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_ltr_mode ltr_mode; + + if (!ltr_count) + return -EINVAL; + + ltr_mode.count = ltr_count; + ltr_mode.mode = HFI_LTR_MODE_MANUAL; + ltr_mode.trust_mode = 1; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + <r_mode, sizeof(ltr_mode)); +} + +int iris_set_use_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 ltr_count = inst->fw_caps[LTR_COUNT].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_ltr_use ltr_use; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) + return -EINVAL; + + if (!ltr_count) + return -EINVAL; + + ltr_use.ref_ltr = inst->fw_caps[cap_id].value; + ltr_use.use_constrnt = true; + ltr_use.frames = 0; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + <r_use, sizeof(ltr_use)); +} + +int iris_set_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 ltr_count = inst->fw_caps[LTR_COUNT].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_ltr_mark ltr_mark; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) + return -EINVAL; + + if (!ltr_count) + return -EINVAL; + + ltr_mark.mark_frame = inst->fw_caps[cap_id].value; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + <r_mark, sizeof(ltr_mark)); +} + +int iris_set_ltr_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 ltr_count = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + + if (!ltr_count) + return -EINVAL; + + if (inst->hfi_rc_type == HFI_RC_CBR_VFR || + inst->hfi_rc_type == HFI_RC_CBR_CFR || + inst->hfi_rc_type == HFI_RC_OFF) { + inst->fw_caps[LTR_COUNT].value = 0; + return -EINVAL; + } + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + <r_count, sizeof(u32)); +} + +int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 ltr_count = inst->fw_caps[LTR_COUNT].value; + u32 hfi_val = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) + return -EINVAL; + + if (!ltr_count || hfi_val == INVALID_DEFAULT_MARK_OR_USE_LTR) + return -EINVAL; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &hfi_val, sizeof(u32)); +} + int iris_set_properties(struct iris_inst *inst, u32 plane) { const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h index a0d5338bdc91..996c83fdc6f4 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.h +++ b/drivers/media/platform/qcom/iris/iris_ctrls.h @@ -36,6 +36,11 @@ int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_ir_period_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_ir_period_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_ltr_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_ltr_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_use_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_properties(struct iris_inst *inst, u32 plane); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c index 9344d20042fd..6db693a602ac 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c @@ -243,6 +243,36 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .flags = CAP_FLAG_OUTPUT_PORT, .set = iris_set_ir_period_gen1, }, + { + .cap_id = LTR_COUNT, + .min = 0, + .max = MAX_LTR_FRAME_COUNT_GEN1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_PARAM_VENC_LTRMODE, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_ltr_count_gen1, + }, + { + .cap_id = USE_LTR, + .min = 0, + .max = ((1 << MAX_LTR_FRAME_COUNT_GEN1) - 1), + .step_or_mask = 0, + .value = 0, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_USELTRFRAME, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_use_ltr, + }, + { + .cap_id = MARK_LTR, + .min = 0, + .max = (MAX_LTR_FRAME_COUNT_GEN1 - 1), + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_mark_ltr, + }, }; static const u32 sm8250_vdec_input_config_param_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 051ba0d157c7..a441c897aaab 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -695,6 +695,31 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p packet->shdr.hdr.size += sizeof(u32) + sizeof(*intra_refresh); break; } + case HFI_PROPERTY_PARAM_VENC_LTRMODE: { + struct hfi_ltr_mode *in = pdata, *ltr_mode = prop_data; + + ltr_mode->mode = in->mode; + ltr_mode->count = in->count; + ltr_mode->trust_mode = in->trust_mode; + packet->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_mode); + break; + } + case HFI_PROPERTY_CONFIG_VENC_USELTRFRAME: { + struct hfi_ltr_use *in = pdata, *ltr_use = prop_data; + + ltr_use->frames = in->frames; + ltr_use->ref_ltr = in->ref_ltr; + ltr_use->use_constrnt = in->use_constrnt; + packet->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_use); + break; + } + case HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME: { + struct hfi_ltr_mark *in = pdata, *ltr_mark = prop_data; + + ltr_mark->mark_frame = in->mark_frame; + packet->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_mark); + break; + } default: return -EINVAL; } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h index 04c79ee0463d..34249fc0d047 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h @@ -147,8 +147,16 @@ #define HFI_INTRA_REFRESH_RANDOM 0x5 #define HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH 0x200500d + +#define HFI_LTR_MODE_DISABLE 0x0 +#define HFI_LTR_MODE_MANUAL 0x1 +#define HFI_LTR_MODE_PERIODIC 0x2 + +#define HFI_PROPERTY_PARAM_VENC_LTRMODE 0x200501c #define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020 #define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001 +#define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME 0x2006009 +#define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME 0x200600a #define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008 struct hfi_pkt_hdr { @@ -460,6 +468,22 @@ struct hfi_intra_refresh { u32 mbs; }; +struct hfi_ltr_mode { + u32 mode; + u32 count; + u32 trust_mode; +}; + +struct hfi_ltr_use { + u32 ref_ltr; + u32 use_constrnt; + u32 frames; +}; + +struct hfi_ltr_mark { + u32 mark_frame; +}; + struct hfi_event_data { u32 error; u32 height; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c index 401519fef0e2..495327160ec2 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -715,6 +715,36 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { CAP_FLAG_DYNAMIC_ALLOWED, .set = iris_set_ir_period_gen2, }, + { + .cap_id = LTR_COUNT, + .min = 0, + .max = MAX_LTR_FRAME_COUNT_GEN2, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_LTR_COUNT, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_ltr_count_gen2, + }, + { + .cap_id = USE_LTR, + .min = 0, + .max = ((1 << MAX_LTR_FRAME_COUNT_GEN2) - 1), + .step_or_mask = 0, + .value = 0, + .hfi_id = HFI_PROP_LTR_USE, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_use_and_mark_ltr, + }, + { + .cap_id = MARK_LTR, + .min = INVALID_DEFAULT_MARK_OR_USE_LTR, + .max = (MAX_LTR_FRAME_COUNT_GEN2 - 1), + .step_or_mask = 1, + .value = INVALID_DEFAULT_MARK_OR_USE_LTR, + .hfi_id = HFI_PROP_LTR_MARK, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_use_and_mark_ltr, + }, }; static const u32 sm8550_vdec_input_config_params_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h index cecf771c55dd..aec19efc41a5 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h @@ -71,6 +71,9 @@ enum hfi_rate_control { #define HFI_PROP_MIN_QP_PACKED 0x0300012f #define HFI_PROP_MAX_QP_PACKED 0x03000130 #define HFI_PROP_IR_RANDOM_PERIOD 0x03000131 +#define HFI_PROP_LTR_COUNT 0x03000134 +#define HFI_PROP_LTR_MARK 0x03000135 +#define HFI_PROP_LTR_USE 0x03000136 #define HFI_PROP_TOTAL_BITRATE 0x0300013b #define HFI_PROP_MAX_GOP_FRAMES 0x03000146 #define HFI_PROP_MAX_B_FRAMES 0x03000147 diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 6a108173be35..2f4392e6a42e 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -28,6 +28,9 @@ struct iris_inst; #define MAX_QP_HEVC 63 #define DEFAULT_QP 20 #define BITRATE_DEFAULT 20000000 +#define INVALID_DEFAULT_MARK_OR_USE_LTR -1 +#define MAX_LTR_FRAME_COUNT_GEN1 4 +#define MAX_LTR_FRAME_COUNT_GEN2 2 enum stage_type { STAGE_1 = 1, @@ -151,6 +154,9 @@ enum platform_inst_fw_cap_type { VFLIP, IR_TYPE, IR_PERIOD, + LTR_COUNT, + USE_LTR, + MARK_LTR, INST_FW_CAP_MAX, }; diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c index 9270422c1601..891aed5091c7 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c @@ -934,6 +934,19 @@ static u32 iris_vpu_enc_bin_size(struct iris_inst *inst) num_vpp_pipes, inst->hfi_rc_type); } +static inline u32 hfi_buffer_get_recon_count(struct iris_inst *inst) +{ + u32 num_ref = 1; + u32 ltr_count; + + ltr_count = inst->fw_caps[LTR_COUNT].value; + + if (ltr_count) + num_ref = num_ref + ltr_count; + + return num_ref; +} + static u32 iris_vpu_dec_partial_size(struct iris_inst *inst) { struct v4l2_format *f = inst->fmt_src; @@ -968,7 +981,7 @@ static u32 iris_vpu_enc_comv_size(struct iris_inst *inst) { u32 height = iris_vpu_enc_get_bitstream_height(inst); u32 width = iris_vpu_enc_get_bitstream_width(inst); - u32 num_recon = 1; + u32 num_recon = hfi_buffer_get_recon_count(inst); u32 lcu_size = 16; if (inst->codec == V4L2_PIX_FMT_HEVC) { @@ -1677,10 +1690,9 @@ static u32 iris_vpu_enc_scratch2_size(struct iris_inst *inst) { u32 frame_height = iris_vpu_enc_get_bitstream_height(inst); u32 frame_width = iris_vpu_enc_get_bitstream_width(inst); - u32 num_ref = 1; + u32 num_ref = hfi_buffer_get_recon_count(inst); - return hfi_buffer_scratch2_enc(frame_width, frame_height, num_ref, - false); + return hfi_buffer_scratch2_enc(frame_width, frame_height, num_ref, false); } static u32 iris_vpu_enc_vpss_size(struct iris_inst *inst) From e4d067d5e991b96d86d9becc5fc9c9cfc4cacc59 Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Tue, 12 May 2026 16:55:12 +0800 Subject: [PATCH 372/394] media: qcom: iris: Add B frames support for encoder Add support for B-frame configuration on both gen1 and gen2 encoders by enabling V4L2_CID_MPEG_VIDEO_B_FRAMES control. Reviewed-by: Dikshita Agarwal Tested-by: Neil Armstrong # on SM8650-HDK Signed-off-by: Wangao Wang Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_ctrls.c | 30 +++++++++++++++++++ drivers/media/platform/qcom/iris/iris_ctrls.h | 1 + .../media/platform/qcom/iris/iris_hfi_gen1.c | 18 +++++++++++ .../qcom/iris/iris_hfi_gen1_command.c | 8 +++++ .../qcom/iris/iris_hfi_gen1_defines.h | 10 +++++++ .../media/platform/qcom/iris/iris_hfi_gen2.c | 10 +++++++ .../platform/qcom/iris/iris_platform_common.h | 2 ++ .../platform/qcom/iris/iris_vpu_buffer.c | 6 +++- 8 files changed, 84 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index 87d10ce1a9a5..a6bd2eada52b 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -118,6 +118,8 @@ static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id) return USE_LTR; case V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX: return MARK_LTR; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + return B_FRAME; default: return INST_FW_CAP_MAX; } @@ -225,6 +227,8 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id) return V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES; case MARK_LTR: return V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX; + case B_FRAME: + return V4L2_CID_MPEG_VIDEO_B_FRAMES; default: return 0; } @@ -1161,6 +1165,32 @@ int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_ &hfi_val, sizeof(u32)); } +int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 gop_size = inst->fw_caps[GOP_SIZE].value; + u32 b_frame = inst->fw_caps[B_FRAME].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_intra_period intra_period; + + if (!gop_size || b_frame >= gop_size) + return -EINVAL; + + /* + * intra_period represents the length of a GOP, which includes both P-frames + * and B-frames. The counts of P-frames and B-frames within a GOP must be + * communicated to the firmware. + */ + intra_period.pframes = (gop_size - 1) / (b_frame + 1); + intra_period.bframes = b_frame; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + &intra_period, sizeof(intra_period)); +} + int iris_set_properties(struct iris_inst *inst, u32 plane) { const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h index 996c83fdc6f4..609258c81517 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.h +++ b/drivers/media/platform/qcom/iris/iris_ctrls.h @@ -41,6 +41,7 @@ int iris_set_ltr_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_ty int iris_set_use_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_properties(struct iris_inst *inst, u32 plane); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c index 6db693a602ac..792441463b4b 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c @@ -273,6 +273,24 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, .set = iris_set_mark_ltr, }, + { + .cap_id = B_FRAME, + .min = 0, + .max = 3, + .step_or_mask = 1, + .value = 0, + .flags = CAP_FLAG_OUTPUT_PORT, + }, + { + .cap_id = INTRA_PERIOD, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_intra_period, + }, }; static const u32 sm8250_vdec_input_config_param_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index a441c897aaab..05e7b5ff2b92 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -720,6 +720,14 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p packet->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_mark); break; } + case HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD: { + struct hfi_intra_period *in = pdata, *intra_period = prop_data; + + intra_period->pframes = in->pframes; + intra_period->bframes = in->bframes; + packet->shdr.hdr.size += sizeof(u32) + sizeof(*intra_period); + break; + } default: return -EINVAL; } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h index 34249fc0d047..4343661e8606 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h @@ -155,6 +155,7 @@ #define HFI_PROPERTY_PARAM_VENC_LTRMODE 0x200501c #define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020 #define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001 +#define HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD 0x2006003 #define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME 0x2006009 #define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME 0x200600a #define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008 @@ -484,6 +485,15 @@ struct hfi_ltr_mark { u32 mark_frame; }; +struct hfi_max_num_b_frames { + u32 max_num_b_frames; +}; + +struct hfi_intra_period { + u32 pframes; + u32 bframes; +}; + struct hfi_event_data { u32 error; u32 height; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c index 495327160ec2..27878b70e516 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -745,6 +745,16 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, .set = iris_set_use_and_mark_ltr, }, + { + .cap_id = B_FRAME, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_MAX_B_FRAMES, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_u32, + }, }; static const u32 sm8550_vdec_input_config_params_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 2f4392e6a42e..422e83ae0788 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -157,6 +157,8 @@ enum platform_inst_fw_cap_type { LTR_COUNT, USE_LTR, MARK_LTR, + B_FRAME, + INTRA_PERIOD, INST_FW_CAP_MAX, }; diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c index 891aed5091c7..0ed82dc2b8af 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c @@ -936,11 +936,15 @@ static u32 iris_vpu_enc_bin_size(struct iris_inst *inst) static inline u32 hfi_buffer_get_recon_count(struct iris_inst *inst) { + u32 bframe_count, ltr_count; u32 num_ref = 1; - u32 ltr_count; + bframe_count = inst->fw_caps[B_FRAME].value; ltr_count = inst->fw_caps[LTR_COUNT].value; + if (bframe_count) + num_ref = 2; + if (ltr_count) num_ref = num_ref + ltr_count; From e0507d45b3c171296248b4382271a003c6c2e058 Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Tue, 12 May 2026 16:55:13 +0800 Subject: [PATCH 373/394] media: qcom: iris: Add hierarchical coding support for encoder Add hierarchical coding support for both gen1 and gen2 encoders by enabling the following V4L2 controls: H264: V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING, V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE, V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER HEVC(gen2 only): V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE, V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER Reviewed-by: Vikash Garodia Signed-off-by: Wangao Wang Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_ctrls.c | 288 +++++++++++++++++- drivers/media/platform/qcom/iris/iris_ctrls.h | 7 +- .../media/platform/qcom/iris/iris_hfi_gen1.c | 94 +++++- .../qcom/iris/iris_hfi_gen1_command.c | 21 +- .../qcom/iris/iris_hfi_gen1_defines.h | 2 + .../media/platform/qcom/iris/iris_hfi_gen2.c | 184 ++++++++++- .../qcom/iris/iris_hfi_gen2_defines.h | 15 + .../media/platform/qcom/iris/iris_instance.h | 4 + .../platform/qcom/iris/iris_platform_common.h | 23 ++ .../platform/qcom/iris/iris_vpu_buffer.c | 28 ++ 10 files changed, 658 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index a6bd2eada52b..10e33b8a73f6 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -120,6 +120,40 @@ static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id) return MARK_LTR; case V4L2_CID_MPEG_VIDEO_B_FRAMES: return B_FRAME; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING: + return LAYER_ENABLE; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE: + return LAYER_TYPE_H264; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE: + return LAYER_TYPE_HEVC; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER: + return LAYER_COUNT_H264; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER: + return LAYER_COUNT_HEVC; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR: + return LAYER0_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR: + return LAYER1_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR: + return LAYER2_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR: + return LAYER3_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR: + return LAYER4_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR: + return LAYER5_BITRATE_H264; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR: + return LAYER0_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR: + return LAYER1_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR: + return LAYER2_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR: + return LAYER3_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR: + return LAYER4_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR: + return LAYER5_BITRATE_HEVC; default: return INST_FW_CAP_MAX; } @@ -229,6 +263,40 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id) return V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX; case B_FRAME: return V4L2_CID_MPEG_VIDEO_B_FRAMES; + case LAYER_ENABLE: + return V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING; + case LAYER_TYPE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE; + case LAYER_TYPE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE; + case LAYER_COUNT_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER; + case LAYER_COUNT_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER; + case LAYER0_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR; + case LAYER1_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR; + case LAYER2_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR; + case LAYER3_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR; + case LAYER4_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR; + case LAYER5_BITRATE_H264: + return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR; + case LAYER0_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR; + case LAYER1_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR; + case LAYER2_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR; + case LAYER3_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR; + case LAYER4_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR; + case LAYER5_BITRATE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR; default: return 0; } @@ -575,7 +643,64 @@ int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_ &hfi_val, sizeof(u32)); } -int iris_set_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +int iris_set_bitrate_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 entropy_mode = inst->fw_caps[ENTROPY_MODE].value; + u32 bitrate = inst->fw_caps[cap_id].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + struct hfi_bitrate hfi_val; + u32 max_bitrate; + + if (!(inst->fw_caps[cap_id].flags & CAP_FLAG_CLIENT_SET) && cap_id != BITRATE) + return -EINVAL; + + if (inst->codec == V4L2_PIX_FMT_HEVC) { + max_bitrate = CABAC_MAX_BITRATE; + } else { + if (entropy_mode == V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC) + max_bitrate = CABAC_MAX_BITRATE; + else + max_bitrate = CAVLC_MAX_BITRATE; + } + + hfi_val.bitrate = min(bitrate, max_bitrate); + + switch (cap_id) { + case BITRATE: + case LAYER0_BITRATE_H264: + hfi_val.layer_id = 0; + break; + case LAYER1_BITRATE_H264: + hfi_val.layer_id = 1; + break; + case LAYER2_BITRATE_H264: + hfi_val.layer_id = 2; + break; + case LAYER3_BITRATE_H264: + hfi_val.layer_id = 3; + break; + case LAYER4_BITRATE_H264: + hfi_val.layer_id = 4; + break; + case LAYER5_BITRATE_H264: + hfi_val.layer_id = 5; + break; + default: + return -EINVAL; + } + + if (hfi_val.layer_id > 0 && !inst->fw_caps[LAYER_ENABLE].value) + return -EINVAL; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_STRUCTURE, + &hfi_val, sizeof(hfi_val)); +} + +int iris_set_bitrate_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; u32 entropy_mode = inst->fw_caps[ENTROPY_MODE].value; @@ -1191,6 +1316,167 @@ int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type &intra_period, sizeof(intra_period)); } +int iris_set_layer_type(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 layer_enable = inst->fw_caps[LAYER_ENABLE].value; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + u32 layer_type; + + if (inst->hfi_rc_type == HFI_RATE_CONTROL_CQ || + inst->hfi_rc_type == HFI_RATE_CONTROL_OFF) + return -EINVAL; + + if (inst->codec == V4L2_PIX_FMT_H264) { + if (!layer_enable || !inst->fw_caps[LAYER_COUNT_H264].value) + return -EINVAL; + + if (inst->fw_caps[LAYER_TYPE_H264].value == + V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P) { + if (inst->hfi_rc_type == HFI_RC_VBR_CFR) + layer_type = HFI_HIER_P_HYBRID_LTR; + else + layer_type = HFI_HIER_P_SLIDING_WINDOW; + } else if (inst->fw_caps[LAYER_TYPE_H264].value == + V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B) { + if (inst->hfi_rc_type == HFI_RC_VBR_CFR) + layer_type = HFI_HIER_B; + else + return -EINVAL; + } else { + return -EINVAL; + } + } else if (inst->codec == V4L2_PIX_FMT_HEVC) { + if (!inst->fw_caps[LAYER_COUNT_HEVC].value) + return -EINVAL; + + if (inst->fw_caps[LAYER_TYPE_HEVC].value == + V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P) { + layer_type = HFI_HIER_P_SLIDING_WINDOW; + } else if (inst->fw_caps[LAYER_TYPE_HEVC].value == + V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B) { + if (inst->hfi_rc_type == HFI_RC_VBR_CFR) + layer_type = HFI_HIER_B; + else + return -EINVAL; + } else { + return -EINVAL; + } + } else { + return -EINVAL; + } + + inst->hfi_layer_type = layer_type; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32_ENUM, + &layer_type, sizeof(u32)); +} + +int iris_set_layer_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 layer_enable = inst->fw_caps[LAYER_ENABLE].value; + u32 layer_count = inst->fw_caps[cap_id].value; + u32 hfi_id, ret; + + if (!layer_enable || !layer_count) + return -EINVAL; + + inst->hfi_layer_count = layer_count; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) { + hfi_id = HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER; + ret = hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &layer_count, sizeof(u32)); + if (ret) + return ret; + } + + hfi_id = inst->fw_caps[cap_id].hfi_id; + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &layer_count, sizeof(u32)); +} + +int iris_set_layer_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + u32 layer_type = inst->hfi_layer_type; + u32 layer_count, layer_count_max; + + layer_count = (inst->codec == V4L2_PIX_FMT_H264) ? + inst->fw_caps[LAYER_COUNT_H264].value : + inst->fw_caps[LAYER_COUNT_HEVC].value; + + if (!layer_count) + return -EINVAL; + + if (layer_type == HFI_HIER_B) { + layer_count_max = MAX_LAYER_HB; + } else if (layer_type == HFI_HIER_P_HYBRID_LTR) { + layer_count_max = MAX_AVC_LAYER_HP_HYBRID_LTR; + } else if (layer_type == HFI_HIER_P_SLIDING_WINDOW) { + if (inst->codec == V4L2_PIX_FMT_H264) { + layer_count_max = MAX_AVC_LAYER_HP_SLIDING_WINDOW; + } else { + if (inst->hfi_rc_type == HFI_RC_VBR_CFR) + layer_count_max = MAX_HEVC_VBR_LAYER_HP_SLIDING_WINDOW; + else + layer_count_max = MAX_HEVC_LAYER_HP_SLIDING_WINDOW; + } + } else { + return -EINVAL; + } + + if (layer_count > layer_count_max) + layer_count = layer_count_max; + + layer_count += 1; /* base layer */ + inst->hfi_layer_count = layer_count; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &layer_count, sizeof(u32)); +} + +int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx); + struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + u32 bitrate = inst->fw_caps[cap_id].value; + + /* ignore layer bitrate when total bitrate is set */ + if (inst->fw_caps[BITRATE].flags & CAP_FLAG_CLIENT_SET) + return 0; + + if (!(inst->fw_caps[cap_id].flags & CAP_FLAG_CLIENT_SET)) + return -EINVAL; + + if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) + return -EINVAL; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &bitrate, sizeof(u32)); +} + int iris_set_properties(struct iris_inst *inst, u32 plane) { const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h index 609258c81517..3c462ec9190b 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.h +++ b/drivers/media/platform/qcom/iris/iris_ctrls.h @@ -22,7 +22,8 @@ int iris_set_level(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id int iris_set_profile_level_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_header_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); -int iris_set_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_bitrate_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_bitrate_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_peak_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_bitrate_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_bitrate_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); @@ -42,6 +43,10 @@ int iris_set_use_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_ int iris_set_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_layer_type(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_layer_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_layer_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_properties(struct iris_inst *inst, u32 plane); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c index 792441463b4b..ca1545d28b53 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c @@ -136,7 +136,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, - .set = iris_set_bitrate, + .set = iris_set_bitrate_gen1, }, { .cap_id = BITRATE_MODE, @@ -291,6 +291,98 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .flags = CAP_FLAG_OUTPUT_PORT, .set = iris_set_intra_period, }, + { + .cap_id = LAYER_ENABLE, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .flags = CAP_FLAG_OUTPUT_PORT, + }, + { + .cap_id = LAYER_TYPE_H264, + .min = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .max = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P), + .value = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + }, + { + .cap_id = LAYER_COUNT_H264, + .min = 0, + .max = MAX_HIER_CODING_LAYER_GEN1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_layer_count_gen1, + }, + { + .cap_id = LAYER0_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER1_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER2_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER3_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER4_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, + { + .cap_id = LAYER5_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen1, + }, }; static const u32 sm8250_vdec_input_config_param_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 05e7b5ff2b92..4cb54e5e4f1f 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -604,11 +604,10 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p break; } case HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE: { - struct hfi_bitrate *brate = prop_data; - u32 *in = pdata; + struct hfi_bitrate *in = pdata, *brate = prop_data; - brate->bitrate = *in; - brate->layer_id = 0; + brate->bitrate = in->bitrate; + brate->layer_id = in->layer_id; packet->shdr.hdr.size += sizeof(u32) + sizeof(*brate); break; } @@ -728,6 +727,20 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p packet->shdr.hdr.size += sizeof(u32) + sizeof(*intra_period); break; } + case HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER: { + u32 *in = pdata; + + packet->data[1] = *in; + packet->shdr.hdr.size += sizeof(u32) + sizeof(u32); + break; + } + case HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER: { + u32 *in = pdata; + + packet->data[1] = *in; + packet->shdr.hdr.size += sizeof(u32) + sizeof(u32); + break; + } default: return -EINVAL; } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h index 4343661e8606..0e4dee192384 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h @@ -154,11 +154,13 @@ #define HFI_PROPERTY_PARAM_VENC_LTRMODE 0x200501c #define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020 +#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER 0x2005026 #define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001 #define HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD 0x2006003 #define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME 0x2006009 #define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME 0x200600a #define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008 +#define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER 0x200600b struct hfi_pkt_hdr { u32 size; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c index 27878b70e516..7a85c1d4e5e6 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -391,7 +391,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { .hfi_id = HFI_PROP_TOTAL_BITRATE, .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, - .set = iris_set_bitrate, + .set = iris_set_bitrate_gen2, }, { .cap_id = BITRATE_PEAK, @@ -755,6 +755,188 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { .flags = CAP_FLAG_OUTPUT_PORT, .set = iris_set_u32, }, + { + .cap_id = LAYER_ENABLE, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .flags = CAP_FLAG_OUTPUT_PORT, + }, + { + .cap_id = LAYER_TYPE_H264, + .min = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B, + .max = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B) | + BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P), + .value = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .hfi_id = HFI_PROP_LAYER_ENCODING_TYPE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_layer_type, + }, + { + .cap_id = LAYER_TYPE_HEVC, + .min = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B, + .max = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B) | + BIT(V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P), + .value = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P, + .hfi_id = HFI_PROP_LAYER_ENCODING_TYPE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_layer_type, + }, + { + .cap_id = LAYER_COUNT_H264, + .min = 0, + .max = 5, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_LAYER_COUNT, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_count_gen2, + }, + { + .cap_id = LAYER_COUNT_HEVC, + .min = 0, + .max = 5, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_LAYER_COUNT, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_count_gen2, + }, + { + .cap_id = LAYER0_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER1, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER1_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER2, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER2_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER3, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER3_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER4, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER4_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER5, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER5_BITRATE_H264, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER6, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER0_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER1, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER1_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER2, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER2_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER3, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER3_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER4, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER4_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER5, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER5_BITRATE_HEVC, + .min = 1, + .max = BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER6, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + } }; static const u32 sm8550_vdec_input_config_params_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h index aec19efc41a5..d09096a9d5f9 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h @@ -74,7 +74,22 @@ enum hfi_rate_control { #define HFI_PROP_LTR_COUNT 0x03000134 #define HFI_PROP_LTR_MARK 0x03000135 #define HFI_PROP_LTR_USE 0x03000136 + +enum hfi_layer_encoding_type { + HFI_HIER_P_SLIDING_WINDOW = 0x1, + HFI_HIER_P_HYBRID_LTR = 0x2, + HFI_HIER_B = 0x3, +}; + +#define HFI_PROP_LAYER_ENCODING_TYPE 0x03000138 +#define HFI_PROP_LAYER_COUNT 0x03000139 #define HFI_PROP_TOTAL_BITRATE 0x0300013b +#define HFI_PROP_BITRATE_LAYER1 0x0300013c +#define HFI_PROP_BITRATE_LAYER2 0x0300013d +#define HFI_PROP_BITRATE_LAYER3 0x0300013e +#define HFI_PROP_BITRATE_LAYER4 0x0300013f +#define HFI_PROP_BITRATE_LAYER5 0x03000140 +#define HFI_PROP_BITRATE_LAYER6 0x03000141 #define HFI_PROP_MAX_GOP_FRAMES 0x03000146 #define HFI_PROP_MAX_B_FRAMES 0x03000147 #define HFI_PROP_QUALITY_MODE 0x03000148 diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index 352af99699dd..0041b0cc4001 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -77,6 +77,8 @@ struct iris_fmt { * @enc_raw_height: source image height for encoder instance * @enc_scale_width: scale width for encoder instance * @enc_scale_height: scale height for encoder instance + * @hfi_layer_type: hierarchical coding layer type + * @hfi_layer_count: hierarchical coding layer count */ struct iris_inst { @@ -120,6 +122,8 @@ struct iris_inst { u32 enc_raw_height; u32 enc_scale_width; u32 enc_scale_height; + u32 hfi_layer_type; + u32 hfi_layer_count; }; #endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 422e83ae0788..7ca37ca2dcca 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -31,6 +31,12 @@ struct iris_inst; #define INVALID_DEFAULT_MARK_OR_USE_LTR -1 #define MAX_LTR_FRAME_COUNT_GEN1 4 #define MAX_LTR_FRAME_COUNT_GEN2 2 +#define MAX_LAYER_HB 3 +#define MAX_AVC_LAYER_HP_HYBRID_LTR 5 +#define MAX_AVC_LAYER_HP_SLIDING_WINDOW 3 +#define MAX_HEVC_LAYER_HP_SLIDING_WINDOW 3 +#define MAX_HEVC_VBR_LAYER_HP_SLIDING_WINDOW 5 +#define MAX_HIER_CODING_LAYER_GEN1 6 enum stage_type { STAGE_1 = 1, @@ -159,6 +165,23 @@ enum platform_inst_fw_cap_type { MARK_LTR, B_FRAME, INTRA_PERIOD, + LAYER_ENABLE, + LAYER_TYPE_H264, + LAYER_TYPE_HEVC, + LAYER_COUNT_H264, + LAYER_COUNT_HEVC, + LAYER0_BITRATE_H264, + LAYER1_BITRATE_H264, + LAYER2_BITRATE_H264, + LAYER3_BITRATE_H264, + LAYER4_BITRATE_H264, + LAYER5_BITRATE_H264, + LAYER0_BITRATE_HEVC, + LAYER1_BITRATE_HEVC, + LAYER2_BITRATE_HEVC, + LAYER3_BITRATE_HEVC, + LAYER4_BITRATE_HEVC, + LAYER5_BITRATE_HEVC, INST_FW_CAP_MAX, }; diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c index 0ed82dc2b8af..c2cd4adc0823 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c @@ -936,6 +936,8 @@ static u32 iris_vpu_enc_bin_size(struct iris_inst *inst) static inline u32 hfi_buffer_get_recon_count(struct iris_inst *inst) { + u32 layer_count = inst->hfi_layer_count; + u32 layer_type = inst->hfi_layer_type; u32 bframe_count, ltr_count; u32 num_ref = 1; @@ -945,9 +947,35 @@ static inline u32 hfi_buffer_get_recon_count(struct iris_inst *inst) if (bframe_count) num_ref = 2; + /* The shift operation here is rounding logic, similar to [(x+1)/2]. */ + if (layer_type == HFI_HIER_P_HYBRID_LTR) + num_ref = (layer_count + 1) >> 1; + + if (layer_type == HFI_HIER_P_SLIDING_WINDOW) { + if (inst->codec == V4L2_PIX_FMT_HEVC) + num_ref = (layer_count + 1) >> 1; + else if (inst->codec == V4L2_PIX_FMT_H264 && layer_count < 4) + num_ref = (layer_count - 1); + else + num_ref = layer_count; + } + if (ltr_count) num_ref = num_ref + ltr_count; + /* + * The expression (1 << layers - 2) + 1 accounts for the number of reference + * frames in the Adaptive Hierarchical B-frame encoding case. In this scheme, + * the number of frames in a sub-GOP is related to (2^(number of layers) - 1), + * hence the use of the shift operation. + */ + if (layer_type == HFI_HIER_B) { + if (inst->codec == V4L2_PIX_FMT_HEVC) + num_ref = layer_count; + else + num_ref = (1 << (layer_count - 2)) + 1; + } + return num_ref; } From df5418d5f96c88430661a17143f4b74666dcc471 Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Tue, 12 May 2026 16:55:14 +0800 Subject: [PATCH 374/394] media: qcom: iris: Optimize iris_hfi_gen1_packet_session_set_property Modify iris_hfi_gen1_packet_session_set_property to simplify size calculations and remove redundant code patterns. Previously, packet->shdr.hdr.size was incremented by sizeof(u32) in every switch case, resulting in repetitive and less maintainable logic. Reviewed-by: Dikshita Agarwal Tested-by: Neil Armstrong # on SM8650-HDK Signed-off-by: Wangao Wang Reviewed-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../qcom/iris/iris_hfi_gen1_command.c | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 4cb54e5e4f1f..7674b47ad6c4 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -485,7 +485,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p { void *prop_data = &packet->data[1]; - packet->shdr.hdr.size = sizeof(*packet); + packet->shdr.hdr.size = sizeof(*packet) + sizeof(ptype); packet->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY; packet->shdr.session_id = inst->session_id; packet->num_properties = 1; @@ -498,14 +498,14 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p fsize->buffer_type = in->buffer_type; fsize->height = in->height; fsize->width = in->width; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*fsize); + packet->shdr.hdr.size += sizeof(*fsize); break; } case HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE: { struct hfi_videocores_usage_type *in = pdata, *cu = prop_data; cu->video_core_enable_mask = in->video_core_enable_mask; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*cu); + packet->shdr.hdr.size += sizeof(*cu); break; } case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT: { @@ -514,7 +514,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p hfi->buffer_type = in->buffer_type; hfi->format = in->format; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*hfi); + packet->shdr.hdr.size += sizeof(*hfi); break; } case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO: { @@ -533,7 +533,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p info->plane_format[1].buffer_alignment = 256; } - packet->shdr.hdr.size += sizeof(u32) + sizeof(*info); + packet->shdr.hdr.size += sizeof(*info); break; } case HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL: { @@ -543,7 +543,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p count->type = in->type; count->count_actual = in->count_actual; count->count_min_host = in->count_min_host; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*count); + packet->shdr.hdr.size += sizeof(*count); break; } case HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM: { @@ -552,7 +552,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p multi->buffer_type = in->buffer_type; multi->enable = in->enable; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*multi); + packet->shdr.hdr.size += sizeof(*multi); break; } case HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL: { @@ -560,7 +560,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p sz->size = in->size; sz->type = in->type; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*sz); + packet->shdr.hdr.size += sizeof(*sz); break; } case HFI_PROPERTY_PARAM_WORK_ROUTE: { @@ -568,7 +568,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p u32 *in = pdata; wr->video_work_route = *in; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*wr); + packet->shdr.hdr.size += sizeof(*wr); break; } case HFI_PROPERTY_PARAM_WORK_MODE: { @@ -576,7 +576,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p u32 *in = pdata; wm->video_work_mode = *in; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*wm); + packet->shdr.hdr.size += sizeof(*wm); break; } case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: { @@ -592,7 +592,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p /* Level not supported, falling back to 1 */ pl->level = 1; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*pl); + packet->shdr.hdr.size += sizeof(*pl); break; } case HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER: { @@ -600,7 +600,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p u32 *in = pdata; en->enable = *in; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*en); + packet->shdr.hdr.size += sizeof(*en); break; } case HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE: { @@ -608,7 +608,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p brate->bitrate = in->bitrate; brate->layer_id = in->layer_id; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*brate); + packet->shdr.hdr.size += sizeof(*brate); break; } case HFI_PROPERTY_PARAM_VENC_RATE_CONTROL: { @@ -627,7 +627,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p } packet->data[1] = *in; - packet->shdr.hdr.size += sizeof(u32) * 2; + packet->shdr.hdr.size += sizeof(u32); break; } case HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL: { @@ -637,7 +637,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p entropy->entropy_mode = *in; if (entropy->entropy_mode == HFI_H264_ENTROPY_CABAC) entropy->cabac_model = HFI_H264_CABAC_MODEL_0; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*entropy); + packet->shdr.hdr.size += sizeof(*entropy); break; } case HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2: { @@ -662,7 +662,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p ((max_qp & 0xFF) << 16); range->min_qp.enable = 7; range->max_qp.enable = 7; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*range); + packet->shdr.hdr.size += sizeof(*range); break; } case HFI_PROPERTY_CONFIG_FRAME_RATE: { @@ -671,7 +671,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p frate->buffer_type = in->buffer_type; frate->framerate = in->framerate; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*frate); + packet->shdr.hdr.size += sizeof(*frate); break; } case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO: { @@ -683,7 +683,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p plane_actual_info->plane_format[0] = in->plane_format[0]; if (in->num_planes > 1) plane_actual_info->plane_format[1] = in->plane_format[1]; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*plane_actual_info); + packet->shdr.hdr.size += sizeof(*plane_actual_info); break; } case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: { @@ -691,7 +691,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p intra_refresh->mode = in->mode; intra_refresh->mbs = in->mbs; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*intra_refresh); + packet->shdr.hdr.size += sizeof(*intra_refresh); break; } case HFI_PROPERTY_PARAM_VENC_LTRMODE: { @@ -700,7 +700,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p ltr_mode->mode = in->mode; ltr_mode->count = in->count; ltr_mode->trust_mode = in->trust_mode; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_mode); + packet->shdr.hdr.size += sizeof(*ltr_mode); break; } case HFI_PROPERTY_CONFIG_VENC_USELTRFRAME: { @@ -709,14 +709,14 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p ltr_use->frames = in->frames; ltr_use->ref_ltr = in->ref_ltr; ltr_use->use_constrnt = in->use_constrnt; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_use); + packet->shdr.hdr.size += sizeof(*ltr_use); break; } case HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME: { struct hfi_ltr_mark *in = pdata, *ltr_mark = prop_data; ltr_mark->mark_frame = in->mark_frame; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*ltr_mark); + packet->shdr.hdr.size += sizeof(*ltr_mark); break; } case HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD: { @@ -724,21 +724,21 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p intra_period->pframes = in->pframes; intra_period->bframes = in->bframes; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*intra_period); + packet->shdr.hdr.size += sizeof(*intra_period); break; } case HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER: { u32 *in = pdata; packet->data[1] = *in; - packet->shdr.hdr.size += sizeof(u32) + sizeof(u32); + packet->shdr.hdr.size += sizeof(u32); break; } case HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER: { u32 *in = pdata; packet->data[1] = *in; - packet->shdr.hdr.size += sizeof(u32) + sizeof(u32); + packet->shdr.hdr.size += sizeof(u32); break; } default: From d5e3f2b961da5048a4e588bfa81d8c599956d12c Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Tue, 12 May 2026 16:55:15 +0800 Subject: [PATCH 375/394] media: qcom: iris: Simplify COMV size calculation Unify AVC/HEVC handling by computing codec and lcu_size upfront. Reviewed-by: Dikshita Agarwal Tested-by: Neil Armstrong # on SM8650-HDK Signed-off-by: Wangao Wang Reviewed-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_vpu_buffer.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c index c2cd4adc0823..f55db869fee4 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c @@ -1014,16 +1014,13 @@ static u32 iris_vpu_enc_comv_size(struct iris_inst *inst) u32 height = iris_vpu_enc_get_bitstream_height(inst); u32 width = iris_vpu_enc_get_bitstream_width(inst); u32 num_recon = hfi_buffer_get_recon_count(inst); - u32 lcu_size = 16; + u32 codec, lcu_size; - if (inst->codec == V4L2_PIX_FMT_HEVC) { - lcu_size = 32; - return hfi_buffer_comv_enc(width, height, lcu_size, - num_recon + 1, HFI_CODEC_ENCODE_HEVC); - } + codec = (inst->codec == V4L2_PIX_FMT_HEVC) ? + HFI_CODEC_ENCODE_HEVC : HFI_CODEC_ENCODE_AVC; + lcu_size = (inst->codec == V4L2_PIX_FMT_HEVC) ? 32 : 16; - return hfi_buffer_comv_enc(width, height, lcu_size, - num_recon + 1, HFI_CODEC_ENCODE_AVC); + return hfi_buffer_comv_enc(width, height, lcu_size, num_recon + 1, codec); } static inline From 5c66647a5c3e005f9b6a96fe4aa7ec82d2701b4f Mon Sep 17 00:00:00 2001 From: Vishnu Reddy Date: Wed, 1 Apr 2026 19:19:29 +0530 Subject: [PATCH 376/394] media: iris: add FPS calculation and VPP FW overhead in frequency formula MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver was using a fixed default FPS value when calculating the VPU frequency. This caused wrong frequency requests for high‑frame‑rate streams, for example 4K at 240 FPS. Because of this, the hardware was running at a lower frequency than needed. Add the FPS measurement based on the decoder input buffer arrival rate. The measured FPS is stored per instance and used in frequency calculation instead of the fixed default FPS. The value is clamped so that it does not exceed platform limits. Add a VPP firmware overhead when running in STAGE_2. Reviewed-by: Vikash Garodia Signed-off-by: Vishnu Reddy Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/iris/iris_instance.h | 4 ++++ drivers/media/platform/qcom/iris/iris_vdec.c | 20 +++++++++++++++++++ drivers/media/platform/qcom/iris/iris_vpu2.c | 2 +- .../platform/qcom/iris/iris_vpu_common.c | 6 +++++- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index 0041b0cc4001..1d8a22ea4809 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -70,6 +70,8 @@ struct iris_fmt { * @metadata_idx: index for metadata buffer * @codec: codec type * @last_buffer_dequeued: a flag to indicate that last buffer is sent by driver + * @last_buf_ns: start time of received input buffer for current one second FPS window + * @frame_counter: input buffer counter for current one second FPS window * @frame_rate: frame rate of current instance * @operating_rate: operating rate of current instance * @hfi_rc_type: rate control type @@ -115,6 +117,8 @@ struct iris_inst { u32 metadata_idx; u32 codec; bool last_buffer_dequeued; + u64 last_buf_ns; + u32 frame_counter; u32 frame_rate; u32 operating_rate; u32 hfi_rc_type; diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index ccda3b9fb845..1d34c7bcf8f8 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -54,6 +54,7 @@ int iris_vdec_inst_init(struct iris_inst *inst) f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT); inst->buffers[BUF_OUTPUT].size = f->fmt.pix_mp.plane_fmt[0].sizeimage; + inst->frame_rate = MAXIMUM_FPS; memcpy(&inst->fw_caps[0], &core->inst_fw_caps_dec[0], INST_FW_CAP_MAX * sizeof(struct platform_inst_fw_cap)); @@ -369,6 +370,8 @@ int iris_vdec_streamon_input(struct iris_inst *inst) if (ret) return ret; + inst->frame_counter = 0; + return iris_process_streamon_input(inst); } @@ -411,6 +414,7 @@ int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) { struct iris_buffer *buf = to_iris_buffer(vbuf); struct vb2_buffer *vb2 = &vbuf->vb2_buf; + u64 cur_buf_ns, delta_ns; struct vb2_queue *q; int ret; @@ -427,6 +431,22 @@ int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf) return 0; } + if (buf->type == BUF_INPUT) { + cur_buf_ns = ktime_get_ns(); + + if (!inst->frame_counter) + inst->last_buf_ns = cur_buf_ns; + + inst->frame_counter++; + delta_ns = cur_buf_ns - inst->last_buf_ns; + + if (delta_ns >= NSEC_PER_SEC) { + inst->frame_rate = clamp_t(u32, inst->frame_counter, DEFAULT_FPS, + MAXIMUM_FPS); + inst->frame_counter = 0; + } + } + iris_scale_power(inst); return iris_queue_buffer(inst, buf); diff --git a/drivers/media/platform/qcom/iris/iris_vpu2.c b/drivers/media/platform/qcom/iris/iris_vpu2.c index 9c103a2e4e4e..73c201f4f338 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu2.c +++ b/drivers/media/platform/qcom/iris/iris_vpu2.c @@ -18,7 +18,7 @@ static u64 iris_vpu2_calc_freq(struct iris_inst *inst, size_t data_size) struct v4l2_format *inp_f = inst->fmt_src; u32 mbs_per_second, mbpf, height, width; unsigned long vpp_freq, vsp_freq; - u32 fps = DEFAULT_FPS; + u32 fps = inst->frame_rate; width = max(inp_f->fmt.pix_mp.width, inst->crop.width); height = max(inp_f->fmt.pix_mp.height, inst->crop.height); diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index c6cfc1d9fd9e..3872c4f37987 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -416,7 +416,7 @@ u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_siz u32 height, width, mbs_per_second, mbpf; u64 fw_cycles, fw_vpp_cycles; u64 vsp_cycles, vpp_cycles; - u32 fps = DEFAULT_FPS; + u32 fps = inst->frame_rate; width = max(inp_f->fmt.pix_mp.width, inst->crop.width); height = max(inp_f->fmt.pix_mp.height, inst->crop.height); @@ -435,6 +435,10 @@ u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_siz if (inst->fw_caps[PIPE].value > 1) vpp_cycles += div_u64(vpp_cycles * 59, 1000); + /* 1.05 is VPP FW overhead */ + if (inst->fw_caps[STAGE].value == STAGE_2) + vpp_cycles += mult_frac(vpp_cycles, 5, 100); + vsp_cycles = fps * data_size * 8; vsp_cycles = div_u64(vsp_cycles, 2); /* VSP FW overhead 1.05 */ From d1c9f40f764ed2a91d2903c25d1962cf43afef89 Mon Sep 17 00:00:00 2001 From: Vishnu Reddy Date: Thu, 14 May 2026 00:28:22 +0530 Subject: [PATCH 377/394] media: iris: optimize COMV buffer allocation for VPU3x and VPU4x The existing iris_vpu_dec_comv_size() used VIDEO_MAX_FRAME (32) as num_comv count unconditionally when calculating the co-located motion vector (COMV) buffer size. This resulted in an oversized COMV buffer allocation throughout decode session, wasting memory regardless of actual number of buffers required. For VPU3x and VPU4x platforms, introduce iris_vpu3x_4x_dec_comv_size() to replace iris_vpu_dec_comv_size(). These derive num_comv dynamically, it uses inst->fw_min_count once the firmware has reported its buffer requirements, and fallback to output count during initialization before firmware has communicated its requirements. This aligns the COMV buffer size to the actual count needed rather than always allocating with fixed VIDEO_MAX_FRAME value. Additionally, during iris_vdec_inst_init(), fw_min_count was initialized to MIN_BUFFERS instead of 0. This masked the fallback logic and caused the COMV size calculation to use MIN_BUFFERS even before firmware had reported its actual requirements. Fix this by initializing fw_min_count to 0. During testing of 1080p AVC, it reduces the COMV buffer size from 32.89MB to 6.16MB per decode session, significantly reducing memory consumption. Reviewed-by: Vikash Garodia Signed-off-by: Vishnu Reddy Signed-off-by: Bryan O'Donoghue --- .../qcom/iris/iris_hfi_gen2_command.c | 15 +++---------- .../platform/qcom/iris/iris_platform_common.h | 1 - .../qcom/iris/iris_platform_qcs8300.h | 1 - .../platform/qcom/iris/iris_platform_sm8550.h | 1 - drivers/media/platform/qcom/iris/iris_vdec.c | 3 ++- .../platform/qcom/iris/iris_vpu_buffer.c | 22 +++++++++++++++++-- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index c90b22a75bc5..7ac69af63ead 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -10,7 +10,6 @@ #define UNSPECIFIED_COLOR_FORMAT 5 #define NUM_SYS_INIT_PACKETS 8 -#define NUM_COMV_AV1 18 #define SYS_INIT_PKT_SIZE (sizeof(struct iris_hfi_header) + \ NUM_SYS_INIT_PACKETS * (sizeof(struct iris_hfi_packet) + sizeof(u32))) @@ -1207,18 +1206,10 @@ static u32 iris_hfi_gen2_buf_type_from_driver(u32 domain, enum iris_buffer_type static int iris_hfi_gen2_set_num_comv(struct iris_inst *inst) { - struct platform_inst_caps *caps; - struct iris_core *core = inst->core; - u32 num_comv; + u32 num_comv = inst->buffers[BUF_OUTPUT].min_count; - caps = core->iris_platform_data->inst_caps; - - /* - * AV1 needs more comv buffers than other codecs. - * Update accordingly. - */ - num_comv = (inst->codec == V4L2_PIX_FMT_AV1) ? - NUM_COMV_AV1 : caps->num_comv; + if (inst->fw_min_count) + num_comv = inst->fw_min_count; return iris_hfi_gen2_session_set_property(inst, HFI_PROP_COMV_BUFFER_COUNT, diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 7ca37ca2dcca..c522dd0a528b 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -96,7 +96,6 @@ struct platform_inst_caps { u32 mb_cycles_vpp; u32 mb_cycles_fw; u32 mb_cycles_fw_vpp; - u32 num_comv; u32 max_frame_rate; u32 max_operating_rate; }; diff --git a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h index 61025f1e965b..3cfecae80d1e 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h +++ b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h @@ -15,7 +15,6 @@ static struct platform_inst_caps platform_inst_cap_qcs8300 = { .mb_cycles_vpp = 200, .mb_cycles_fw = 326389, .mb_cycles_fw_vpp = 44156, - .num_comv = 0, .max_frame_rate = MAXIMUM_FPS, .max_operating_rate = MAXIMUM_FPS, }; diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8550.h b/drivers/media/platform/qcom/iris/iris_platform_sm8550.h index a9d9709c2e35..3c9dae995bb2 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_sm8550.h +++ b/drivers/media/platform/qcom/iris/iris_platform_sm8550.h @@ -23,7 +23,6 @@ static struct platform_inst_caps platform_inst_cap_sm8550 = { .mb_cycles_vpp = 200, .mb_cycles_fw = 489583, .mb_cycles_fw_vpp = 66234, - .num_comv = 0, .max_frame_rate = MAXIMUM_FPS, .max_operating_rate = MAXIMUM_FPS, }; diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index 1d34c7bcf8f8..dc7b051f2d01 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -24,7 +24,7 @@ int iris_vdec_inst_init(struct iris_inst *inst) inst->fmt_src = kzalloc_obj(*inst->fmt_src); inst->fmt_dst = kzalloc_obj(*inst->fmt_dst); - inst->fw_min_count = MIN_BUFFERS; + inst->fw_min_count = 0; f = inst->fmt_src; f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; @@ -259,6 +259,7 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) /* Update capture format based on new ip w/h */ output_fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); output_fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); + inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT); inst->buffers[BUF_OUTPUT].size = iris_get_buffer_size(inst, BUF_OUTPUT); inst->crop.left = 0; diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c index f55db869fee4..fb6f1016415e 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c @@ -731,6 +731,24 @@ static u32 iris_vpu_dec_comv_size(struct iris_inst *inst) u32 height = f->fmt.pix_mp.height; u32 width = f->fmt.pix_mp.width; + if (inst->codec == V4L2_PIX_FMT_H264) + return hfi_buffer_comv_h264d(width, height, num_comv); + else if (inst->codec == V4L2_PIX_FMT_HEVC) + return hfi_buffer_comv_h265d(width, height, num_comv); + + return 0; +} + +static u32 iris_vpu3x_4x_dec_comv_size(struct iris_inst *inst) +{ + u32 num_comv = inst->buffers[BUF_OUTPUT].min_count; + struct v4l2_format *f = inst->fmt_src; + u32 height = f->fmt.pix_mp.height; + u32 width = f->fmt.pix_mp.width; + + if (inst->fw_min_count) + num_comv = inst->fw_min_count; + if (inst->codec == V4L2_PIX_FMT_H264) return hfi_buffer_comv_h264d(width, height, num_comv); else if (inst->codec == V4L2_PIX_FMT_HEVC) @@ -2066,7 +2084,7 @@ u32 iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type) static const struct iris_vpu_buf_type_handle dec_internal_buf_type_handle[] = { {BUF_BIN, iris_vpu_dec_bin_size }, - {BUF_COMV, iris_vpu_dec_comv_size }, + {BUF_COMV, iris_vpu3x_4x_dec_comv_size }, {BUF_NON_COMV, iris_vpu_dec_non_comv_size }, {BUF_LINE, iris_vpu_dec_line_size }, {BUF_PERSIST, iris_vpu_dec_persist_size }, @@ -2139,7 +2157,7 @@ u32 iris_vpu4x_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_typ static const struct iris_vpu_buf_type_handle dec_internal_buf_type_handle[] = { {BUF_BIN, iris_vpu_dec_bin_size }, - {BUF_COMV, iris_vpu_dec_comv_size }, + {BUF_COMV, iris_vpu3x_4x_dec_comv_size }, {BUF_NON_COMV, iris_vpu_dec_non_comv_size }, {BUF_LINE, iris_vpu4x_dec_line_size }, {BUF_PERSIST, iris_vpu4x_dec_persist_size }, From 4147ffa3d96dde43bd4f3607db75f261fafaade2 Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Fri, 29 May 2026 15:34:59 +0800 Subject: [PATCH 378/394] media: iris: Add hardware power on/off ops for X1P42100 On X1P42100 the Iris block has an extra BSE clock. Wire this clock into the power on/off sequence. The BSE clock is used to drive the Bin Stream Engine, which is a sub-block of the video codec hardware responsible for bitstream-level processing. It is required to be enabled separately from the core clock to ensure proper codec operation. Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Signed-off-by: Wangao Wang Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_vpu_common.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index 3872c4f37987..5a85568c5ee1 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -224,6 +224,7 @@ void iris_vpu_power_off_hw(struct iris_core *core) { dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], false); iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); + iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK); iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK); iris_disable_unprepare_clock(core, IRIS_HW_CLK); } @@ -292,12 +293,18 @@ int iris_vpu_power_on_hw(struct iris_core *core) if (ret && ret != -ENOENT) goto err_disable_hw_clock; + ret = iris_prepare_enable_clock(core, IRIS_BSE_HW_CLK); + if (ret && ret != -ENOENT) + goto err_disable_hw_ahb_clock; + ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], true); if (ret) - goto err_disable_hw_ahb_clock; + goto err_disable_bse_hw_clock; return 0; +err_disable_bse_hw_clock: + iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK); err_disable_hw_ahb_clock: iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK); err_disable_hw_clock: From f014f5bec088e7c084343642a83725c77a24af39 Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Fri, 29 May 2026 15:35:00 +0800 Subject: [PATCH 379/394] media: iris: Add platform data for X1P42100 Introduce platform data for X1P42100, derived from SM8550 but using a different clock configuration and a dedicated OPP setup. Reviewed-by: Vikash Garodia Signed-off-by: Wangao Wang Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/iris/iris_platform_common.h | 1 + .../platform/qcom/iris/iris_platform_vpu3x.c | 42 +++++++++++++++++++ .../qcom/iris/iris_platform_x1p42100.h | 22 ++++++++++ drivers/media/platform/qcom/iris/iris_probe.c | 4 ++ 4 files changed, 69 insertions(+) create mode 100644 drivers/media/platform/qcom/iris/iris_platform_x1p42100.h diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index c522dd0a528b..982767faa51c 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -58,6 +58,7 @@ extern const struct iris_platform_data sm8250_data; extern const struct iris_platform_data sm8550_data; extern const struct iris_platform_data sm8650_data; extern const struct iris_platform_data sm8750_data; +extern const struct iris_platform_data x1p42100_data; enum platform_clk_type { IRIS_AXI_CLK, /* AXI0 in case of platforms with multiple AXI clocks */ diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c index c249ff827541..541ddc40e3ae 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c @@ -16,6 +16,7 @@ #include "iris_platform_sm8550.h" #include "iris_platform_sm8650.h" #include "iris_platform_sm8750.h" +#include "iris_platform_x1p42100.h" static const struct iris_firmware_desc iris_vpu30_p4_s6_gen2_desc = { .firmware_data = &iris_hfi_gen2_data, @@ -29,6 +30,12 @@ static const struct iris_firmware_desc iris_vpu30_p4_gen2_desc = { .fwname = "qcom/vpu/vpu30_p4.mbn", }; +static const struct iris_firmware_desc iris_vpu30_p1_gen2_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu30_p1_s7.mbn", +}; + static const struct iris_firmware_desc iris_vpu33_p4_gen2_desc = { .firmware_data = &iris_hfi_gen2_data, .get_vpu_buffer_size = iris_vpu33_buf_size, @@ -217,3 +224,38 @@ const struct iris_platform_data sm8750_data = { .max_core_mbpf = NUM_MBS_8K * 2, .max_core_mbps = ((7680 * 4320) / 256) * 60, }; + +/* + * Shares most of SM8550 data except: + * - clk_tbl and opp_clk_tbl for x1p42100 + * - different firmware + * - different num_vpp_pipe + */ +const struct iris_platform_data x1p42100_data = { + .firmware_desc = &iris_vpu30_p1_gen2_desc, + .vpu_ops = &iris_vpu3_ops, + .icc_tbl = iris_icc_info_vpu3x, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), + .clk_rst_tbl = sm8550_clk_reset_table, + .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table), + .bw_tbl_dec = iris_bw_table_dec_vpu3x, + .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x), + .pmdomain_tbl = iris_pmdomain_table_vpu3x, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x), + .opp_pd_tbl = iris_opp_pd_table_vpu3x, + .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x), + .clk_tbl = x1p42100_clk_table, + .clk_tbl_size = ARRAY_SIZE(x1p42100_clk_table), + .opp_clk_tbl = x1p42100_opp_clk_table, + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .inst_iris_fmts = iris_fmts_vpu3x_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec), + .inst_caps = &platform_inst_cap_sm8550, + .tz_cp_config_data = tz_cp_config_vpu3, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3), + .num_vpp_pipe = 1, + .max_session_count = 16, + .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, +}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_x1p42100.h b/drivers/media/platform/qcom/iris/iris_platform_x1p42100.h new file mode 100644 index 000000000000..d89acfbc1233 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_x1p42100.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __IRIS_PLATFORM_X1P42100_H__ +#define __IRIS_PLATFORM_X1P42100_H__ + +static const struct platform_clk_data x1p42100_clk_table[] = { + {IRIS_AXI_CLK, "iface" }, + {IRIS_CTRL_CLK, "core" }, + {IRIS_HW_CLK, "vcodec0_core" }, + {IRIS_BSE_HW_CLK, "vcodec0_bse" }, +}; + +static const char *const x1p42100_opp_clk_table[] = { + "vcodec0_core", + "vcodec0_bse", + NULL, +}; + +#endif diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index a755a6f73ea6..a53423399854 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -385,6 +385,10 @@ static const struct of_device_id iris_dt_match[] = { .compatible = "qcom,sm8750-iris", .data = &sm8750_data, }, + { + .compatible = "qcom,x1p42100-iris", + .data = &x1p42100_data, + }, { }, }; MODULE_DEVICE_TABLE(of, iris_dt_match); From 56f93da11a7c310a83dded5bee28d6661a04da13 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 29 May 2026 17:26:11 +0300 Subject: [PATCH 380/394] media: iris: drop struct iris_fmt The struct iris_fmt unites pixfmt with the plane type, however the type from the struct is not actually used. Drop the struct completely and use u32 pixfmt in all the callsites. Reviewed-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/iris/iris_instance.h | 5 - .../platform/qcom/iris/iris_platform_common.h | 2 +- .../platform/qcom/iris/iris_platform_vpu2.c | 17 +--- .../platform/qcom/iris/iris_platform_vpu3x.c | 22 +---- drivers/media/platform/qcom/iris/iris_vdec.c | 78 +++++++-------- drivers/media/platform/qcom/iris/iris_venc.c | 96 ++++++++----------- 6 files changed, 80 insertions(+), 140 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index 1d8a22ea4809..c54d8ec8562a 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -29,11 +29,6 @@ enum iris_fmt_type_cap { IRIS_FMT_QC08C, }; -struct iris_fmt { - u32 pixfmt; - u32 type; -}; - /** * struct iris_inst - holds per video instance parameters * diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 982767faa51c..6d69a1e3dcd3 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -311,7 +311,7 @@ struct iris_platform_data { const char * const *controller_rst_tbl; unsigned int controller_rst_tbl_size; u64 dma_mask; - struct iris_fmt *inst_iris_fmts; + const u32 *inst_iris_fmts; u32 inst_iris_fmts_size; struct platform_inst_caps *inst_caps; const struct tz_cp_config *tz_cp_config_data; diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c index 41986af8313b..6e06a32822bb 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c @@ -28,19 +28,10 @@ static const struct iris_firmware_desc iris_vpu20_p4_gen1_desc = { .fwname = "qcom/vpu/vpu20_p4.mbn", }; -static struct iris_fmt iris_fmts_vpu2_dec[] = { - [IRIS_FMT_H264] = { - .pixfmt = V4L2_PIX_FMT_H264, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_HEVC] = { - .pixfmt = V4L2_PIX_FMT_HEVC, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_VP9] = { - .pixfmt = V4L2_PIX_FMT_VP9, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, +static const u32 iris_fmts_vpu2_dec[] = { + [IRIS_FMT_H264] = V4L2_PIX_FMT_H264, + [IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC, + [IRIS_FMT_VP9] = V4L2_PIX_FMT_VP9, }; static struct platform_inst_caps platform_inst_cap_vpu2 = { diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c index 541ddc40e3ae..2c63adbc5579 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c @@ -48,23 +48,11 @@ static const struct iris_firmware_desc iris_vpu35_p4_gen2_desc = { .fwname = "qcom/vpu/vpu35_p4.mbn", }; -static struct iris_fmt iris_fmts_vpu3x_dec[] = { - [IRIS_FMT_H264] = { - .pixfmt = V4L2_PIX_FMT_H264, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_HEVC] = { - .pixfmt = V4L2_PIX_FMT_HEVC, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_VP9] = { - .pixfmt = V4L2_PIX_FMT_VP9, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_AV1] = { - .pixfmt = V4L2_PIX_FMT_AV1, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, +static const u32 iris_fmts_vpu3x_dec[] = { + [IRIS_FMT_H264] = V4L2_PIX_FMT_H264, + [IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC, + [IRIS_FMT_VP9] = V4L2_PIX_FMT_VP9, + [IRIS_FMT_AV1] = V4L2_PIX_FMT_AV1, }; static const struct icc_info iris_icc_info_vpu3x[] = { diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index dc7b051f2d01..b65832042dc8 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -68,23 +68,16 @@ void iris_vdec_inst_deinit(struct iris_inst *inst) kfree(inst->fmt_src); } -static const struct iris_fmt iris_vdec_formats_cap[] = { - [IRIS_FMT_NV12] = { - .pixfmt = V4L2_PIX_FMT_NV12, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - }, - [IRIS_FMT_QC08C] = { - .pixfmt = V4L2_PIX_FMT_QC08C, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - }, +static const u32 iris_vdec_formats_cap[] = { + [IRIS_FMT_NV12] = V4L2_PIX_FMT_NV12, + [IRIS_FMT_QC08C] = V4L2_PIX_FMT_QC08C, }; -static const struct iris_fmt * -find_format(struct iris_inst *inst, u32 pixfmt, u32 type) +static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type) { - const struct iris_fmt *fmt = NULL; - unsigned int size = 0; - unsigned int i; + unsigned int size, i; + const u32 *fmt; + switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: fmt = inst->core->iris_platform_data->inst_iris_fmts; @@ -95,25 +88,21 @@ find_format(struct iris_inst *inst, u32 pixfmt, u32 type) size = ARRAY_SIZE(iris_vdec_formats_cap); break; default: - return NULL; + return false; } for (i = 0; i < size; i++) { - if (fmt[i].pixfmt == pixfmt) - break; + if (fmt[i] == pixfmt) + return true; } - if (i == size || fmt[i].type != type) - return NULL; - - return &fmt[i]; + return false; } -static const struct iris_fmt * -find_format_by_index(struct iris_inst *inst, u32 index, u32 type) +static u32 find_format_by_index(struct iris_inst *inst, u32 index, u32 type) { - const struct iris_fmt *fmt = NULL; - unsigned int size = 0; + unsigned int size; + const u32 *fmt; switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -125,18 +114,18 @@ find_format_by_index(struct iris_inst *inst, u32 index, u32 type) size = ARRAY_SIZE(iris_vdec_formats_cap); break; default: - return NULL; + return 0; } - if (index >= size || fmt[index].type != type) - return NULL; + if (index >= size) + return 0; - return &fmt[index]; + return fmt[index]; } int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) { - const struct iris_fmt *fmt; + u32 fmt; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -144,14 +133,14 @@ int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) if (!fmt) return -EINVAL; - f->pixelformat = fmt->pixfmt; + f->pixelformat = fmt; f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_DYN_RESOLUTION; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: fmt = find_format_by_index(inst, f->index, f->type); if (!fmt) return -EINVAL; - f->pixelformat = fmt->pixfmt; + f->pixelformat = fmt; break; default: return -EINVAL; @@ -164,15 +153,15 @@ int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; - const struct iris_fmt *fmt; struct v4l2_format *f_inst; struct vb2_queue *src_q; + bool supported; memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); - fmt = find_format(inst, pixmp->pixelformat, f->type); + supported = check_format(inst, pixmp->pixelformat, f->type); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (!fmt) { + if (!supported) { f_inst = inst->fmt_src; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height; @@ -180,7 +169,7 @@ int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f) } break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (!fmt) { + if (!supported) { f_inst = inst->fmt_dst; f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; @@ -229,7 +218,7 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type))) + if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type)) return -EINVAL; fmt = inst->fmt_src; @@ -268,7 +257,7 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) inst->crop.height = f->fmt.pix_mp.height; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type))) + if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type)) return -EINVAL; fmt = inst->fmt_dst; @@ -297,16 +286,13 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) int iris_vdec_validate_format(struct iris_inst *inst, u32 pixelformat) { - const struct iris_fmt *fmt = NULL; + bool supported; - fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (!fmt) { - fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (!fmt) - return -EINVAL; - } + supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (!supported) + supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - return 0; + return supported ? 0 : -EINVAL; } int iris_vdec_subscribe_event(struct iris_inst *inst, const struct v4l2_event_subscription *sub) diff --git a/drivers/media/platform/qcom/iris/iris_venc.c b/drivers/media/platform/qcom/iris/iris_venc.c index aeed756ee9ca..2398992d0596 100644 --- a/drivers/media/platform/qcom/iris/iris_venc.c +++ b/drivers/media/platform/qcom/iris/iris_venc.c @@ -85,34 +85,21 @@ void iris_venc_inst_deinit(struct iris_inst *inst) kfree(inst->fmt_src); } -static const struct iris_fmt iris_venc_formats_cap[] = { - [IRIS_FMT_H264] = { - .pixfmt = V4L2_PIX_FMT_H264, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - }, - [IRIS_FMT_HEVC] = { - .pixfmt = V4L2_PIX_FMT_HEVC, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, - }, +static const u32 iris_venc_formats_cap[] = { + [IRIS_FMT_H264] = V4L2_PIX_FMT_H264, + [IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC, }; -static const struct iris_fmt iris_venc_formats_out[] = { - [IRIS_FMT_NV12] = { - .pixfmt = V4L2_PIX_FMT_NV12, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_QC08C] = { - .pixfmt = V4L2_PIX_FMT_QC08C, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, +static const u32 iris_venc_formats_out[] = { + [IRIS_FMT_NV12] = V4L2_PIX_FMT_NV12, + [IRIS_FMT_QC08C] = V4L2_PIX_FMT_QC08C, }; -static const struct iris_fmt * -find_format(struct iris_inst *inst, u32 pixfmt, u32 type) +static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type) { - const struct iris_fmt *fmt = NULL; - unsigned int size = 0; - unsigned int i; + unsigned int size, i; + const u32 *fmt; + switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: fmt = iris_venc_formats_out; @@ -123,25 +110,21 @@ find_format(struct iris_inst *inst, u32 pixfmt, u32 type) size = ARRAY_SIZE(iris_venc_formats_cap); break; default: - return NULL; + return false; } for (i = 0; i < size; i++) { - if (fmt[i].pixfmt == pixfmt) - break; + if (fmt[i] == pixfmt) + return true; } - if (i == size || fmt[i].type != type) - return NULL; - - return &fmt[i]; + return false; } -static const struct iris_fmt * -find_format_by_index(struct iris_inst *inst, u32 index, u32 type) +static u32 find_format_by_index(struct iris_inst *inst, u32 index, u32 type) { - const struct iris_fmt *fmt = NULL; - unsigned int size = 0; + unsigned int size; + const u32 *fmt; switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -153,18 +136,18 @@ find_format_by_index(struct iris_inst *inst, u32 index, u32 type) size = ARRAY_SIZE(iris_venc_formats_cap); break; default: - return NULL; + return 0; } - if (index >= size || fmt[index].type != type) - return NULL; + if (index >= size) + return 0; - return &fmt[index]; + return fmt[index]; } int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) { - const struct iris_fmt *fmt; + u32 fmt; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -172,14 +155,14 @@ int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) if (!fmt) return -EINVAL; - f->pixelformat = fmt->pixfmt; + f->pixelformat = fmt; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: fmt = find_format_by_index(inst, f->index, f->type); if (!fmt) return -EINVAL; - f->pixelformat = fmt->pixfmt; + f->pixelformat = fmt; f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL; break; default: @@ -192,14 +175,14 @@ int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; - const struct iris_fmt *fmt; struct v4l2_format *f_inst; + bool supported; memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); - fmt = find_format(inst, pixmp->pixelformat, f->type); + supported = check_format(inst, pixmp->pixelformat, f->type); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (!fmt) { + if (!supported) { f_inst = inst->fmt_src; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height; @@ -207,7 +190,7 @@ int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f) } break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (!fmt) { + if (!supported) { f_inst = inst->fmt_dst; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height; @@ -228,17 +211,17 @@ int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f) static int iris_venc_s_fmt_output(struct iris_inst *inst, struct v4l2_format *f) { - const struct iris_fmt *venc_fmt; struct v4l2_format *fmt; u32 codec_align; + bool supported; iris_venc_try_fmt(inst, f); - venc_fmt = find_format(inst, f->fmt.pix_mp.pixelformat, f->type); - if (!venc_fmt) + supported = check_format(inst, f->fmt.pix_mp.pixelformat, f->type); + if (!supported) return -EINVAL; - codec_align = venc_fmt->pixfmt == V4L2_PIX_FMT_HEVC ? 32 : 16; + codec_align = (f->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_HEVC) ? 32 : 16; fmt = inst->fmt_dst; fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; @@ -281,7 +264,7 @@ static int iris_venc_s_fmt_input(struct iris_inst *inst, struct v4l2_format *f) iris_venc_try_fmt(inst, f); - if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type))) + if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type)) return -EINVAL; fmt = inst->fmt_src; @@ -350,16 +333,13 @@ int iris_venc_s_fmt(struct iris_inst *inst, struct v4l2_format *f) int iris_venc_validate_format(struct iris_inst *inst, u32 pixelformat) { - const struct iris_fmt *fmt = NULL; + bool supported; - fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (!fmt) { - fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (!fmt) - return -EINVAL; - } + supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (!supported) + supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - return 0; + return supported ? 0 : -EINVAL; } int iris_venc_subscribe_event(struct iris_inst *inst, From 3395d7526386dccad1d9ff101a6e1f5d9c47b9ba Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Fri, 29 May 2026 17:36:54 +0300 Subject: [PATCH 381/394] media: iris: Initialize HFI ops after firmware load in core init The HFI sys ops were previously initialized in probe() but, we don't have firmware loaded at probe time. Since HFI is tightly coupled to firmware, initialize the HFI sys ops after firmware has been successfully loaded and booted. Reviewed-by: Bryan O'Donoghue Reviewed-by: Dmitry Baryshkov Signed-off-by: Dikshita Agarwal Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_core.c | 2 ++ drivers/media/platform/qcom/iris/iris_probe.c | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_core.c b/drivers/media/platform/qcom/iris/iris_core.c index e6141012cd3d..a1823ded46e8 100644 --- a/drivers/media/platform/qcom/iris/iris_core.c +++ b/drivers/media/platform/qcom/iris/iris_core.c @@ -74,6 +74,8 @@ int iris_core_init(struct iris_core *core) if (ret) goto error_unload_fw; + core->iris_firmware_data->init_hfi_ops(core); + ret = iris_hfi_core_init(core); if (ret) goto error_unload_fw; diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index a53423399854..c2dcb50a2782 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -266,7 +266,6 @@ static int iris_probe(struct platform_device *pdev) return ret; iris_init_ops(core); - core->iris_firmware_data->init_hfi_ops(core); ret = iris_init_resources(core); if (ret) From 83fcf113ea9a03255d065c27b328ed3c1e816678 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 14 Apr 2026 20:51:58 +0200 Subject: [PATCH 382/394] media: qcom: camss: csid-340: Switch to generic CSID_CFG/CTRL registers The former RDI-specific register definitions (CSID_RDI_CFG0/CTRL) are renamed to unified CSID_CFG0/CSID_CTRL variants, as their layout is interface agnostic. This refactoring provides the foundation for extending csid-340 with missing PIX interface/path support. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/camss/camss-csid-340.c | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c index 0231985746ed..9eee23bd81c2 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-340.c +++ b/drivers/media/platform/qcom/camss/camss-csid-340.c @@ -41,19 +41,20 @@ #define CSI2_RX_CFG1_MISR_EN BIT(6) #define CSI2_RX_CFG1_CGC_MODE BIT(7) -#define CSID_RDI_CFG0(rdi) (0x300 + 0x100 * (rdi)) -#define CSID_RDI_CFG0_BYTE_CNTR_EN BIT(0) -#define CSID_RDI_CFG0_TIMESTAMP_EN BIT(1) -#define CSID_RDI_CFG0_DECODE_FORMAT_MASK GENMASK(15, 12) -#define CSID_RDI_CFG0_DECODE_FORMAT_NOP CSID_RDI_CFG0_DECODE_FORMAT_MASK -#define CSID_RDI_CFG0_DT_MASK GENMASK(21, 16) -#define CSID_RDI_CFG0_VC_MASK GENMASK(23, 22) -#define CSID_RDI_CFG0_DTID_MASK GENMASK(28, 27) -#define CSID_RDI_CFG0_ENABLE BIT(31) +#define CSID_CFG0(iface) (0x300 + 0x100 * (iface)) +#define CSID_CFG0_BYTE_CNTR_EN BIT(0) +#define CSID_CFG0_TIMESTAMP_EN BIT(1) +#define CSID_CFG0_DECODE_FORMAT_MASK GENMASK(15, 12) +#define CSID_CFG0_DECODE_FORMAT_NOP CSID_CFG0_DECODE_FORMAT_MASK +#define CSID_CFG0_DT_MASK GENMASK(21, 16) +#define CSID_CFG0_VC_MASK GENMASK(23, 22) +#define CSID_CFG0_DTID_MASK GENMASK(28, 27) +#define CSID_CFG0_ENABLE BIT(31) + +#define CSID_CTRL(iface) (0x308 + 0x100 * (iface)) +#define CSID_CTRL_HALT_AT_FRAME_BOUNDARY 0 +#define CSID_CTRL_RESUME_AT_FRAME_BOUNDARY 1 -#define CSID_RDI_CTRL(rdi) (0x308 + 0x100 * (rdi)) -#define CSID_RDI_CTRL_HALT_AT_FRAME_BOUNDARY 0 -#define CSID_RDI_CTRL_RESUME_AT_FRAME_BOUNDARY 1 static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy) { @@ -71,7 +72,7 @@ static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) { - writel_relaxed(!!enable, csid->base + CSID_RDI_CTRL(rdi)); + writel_relaxed(!!enable, csid->base + CSID_CTRL(rdi)); } static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) @@ -88,7 +89,7 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * the four least significant bits of the five bit VC * bitfield to generate an internal CID value. * - * CSID_RDI_CFG0(port) + * CSID_CFG0(port) * DT_ID : 28:27 * VC : 26:22 * DT : 21:16 @@ -97,19 +98,19 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 */ dt_id = port & 0x03; - val = CSID_RDI_CFG0_DECODE_FORMAT_NOP; /* only for RDI path */ - val |= FIELD_PREP(CSID_RDI_CFG0_DT_MASK, format->data_type); - val |= FIELD_PREP(CSID_RDI_CFG0_VC_MASK, vc); - val |= FIELD_PREP(CSID_RDI_CFG0_DTID_MASK, dt_id); + val = CSID_CFG0_DECODE_FORMAT_NOP; /* only for RDI path */ + val |= FIELD_PREP(CSID_CFG0_DT_MASK, format->data_type); + val |= FIELD_PREP(CSID_CFG0_VC_MASK, vc); + val |= FIELD_PREP(CSID_CFG0_DTID_MASK, dt_id); if (enable) - val |= CSID_RDI_CFG0_ENABLE; + val |= CSID_CFG0_ENABLE; dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x port=%u vc=%u)\n", csid->id, enable ? "enable" : "disable", format->data_type, port, vc); - writel_relaxed(val, csid->base + CSID_RDI_CFG0(port)); + writel_relaxed(val, csid->base + CSID_CFG0(port)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) From 189c9ef362be65ea587074f0b73e74ac5855cf51 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 14 Apr 2026 20:51:59 +0200 Subject: [PATCH 383/394] media: qcom: camss: csid-340: Add port-to-interface mapping The CSID-340 block uses different register offsets for the PIX and RDI interfaces, but the driver previously indexed these registers directly with the camss port number. This happened to work for RDI because the port index matches the RDI register layout, but this assumption breaks with upcoming PIX interface support Introduce an explicit port-to-interface mapping and use the mapped iface index when programming CSID_CFG0 and CSID_CTRL. This replaces the standalone __csid_ctrl_rdi() helper and simplifies the RDI stream setup path. Also correct the CSID_CFG0/CTRL base offsets and clean up the code in preparation for full PIX path support. Like RDI, PIX outputs Bayer frames but can also achieve some image processing such as scaling, cropping and generating statitics (e.g. histogram), it also offer more flexebility in term of image alignment and stride. All of that can then later be leveraged to improve software or hardware frames post-processing. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/camss/camss-csid-340.c | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c index 9eee23bd81c2..183d40d59afb 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-340.c +++ b/drivers/media/platform/qcom/camss/camss-csid-340.c @@ -41,7 +41,7 @@ #define CSI2_RX_CFG1_MISR_EN BIT(6) #define CSI2_RX_CFG1_CGC_MODE BIT(7) -#define CSID_CFG0(iface) (0x300 + 0x100 * (iface)) +#define CSID_CFG0(iface) (0x200 + 0x100 * (iface)) #define CSID_CFG0_BYTE_CNTR_EN BIT(0) #define CSID_CFG0_TIMESTAMP_EN BIT(1) #define CSID_CFG0_DECODE_FORMAT_MASK GENMASK(15, 12) @@ -51,10 +51,24 @@ #define CSID_CFG0_DTID_MASK GENMASK(28, 27) #define CSID_CFG0_ENABLE BIT(31) -#define CSID_CTRL(iface) (0x308 + 0x100 * (iface)) +#define CSID_CTRL(iface) (0x208 + 0x100 * (iface)) #define CSID_CTRL_HALT_AT_FRAME_BOUNDARY 0 #define CSID_CTRL_RESUME_AT_FRAME_BOUNDARY 1 +#define CSID_MAX_RDI_SRC_STREAMS (MSM_CSID_MAX_SRC_STREAMS - 1) + +enum csid_iface { + CSID_IFACE_PIX, + CSID_IFACE_RDI0, + CSID_IFACE_RDI1, + CSID_IFACE_RDI2, +}; + +static enum csid_iface csid_port_iface_map[CSID_MAX_RDI_SRC_STREAMS] = { + [0] = CSID_IFACE_RDI0, + [1] = CSID_IFACE_RDI1, + [2] = CSID_IFACE_RDI2, +}; static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy) { @@ -70,17 +84,13 @@ static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); } -static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) -{ - writel_relaxed(!!enable, csid->base + CSID_CTRL(rdi)); -} - static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); + enum csid_iface iface = csid_port_iface_map[port]; u8 dt_id; u32 val; @@ -110,7 +120,8 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 csid->id, enable ? "enable" : "disable", format->data_type, port, vc); - writel_relaxed(val, csid->base + CSID_CFG0(port)); + writel_relaxed(val, csid->base + CSID_CFG0(iface)); + writel_relaxed(enable, csid->base + CSID_CTRL(iface)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) @@ -119,12 +130,10 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) __csid_configure_rx(csid, &csid->phy); - /* Loop through all enabled ports and configure a stream for each */ - for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { - if (csid->phy.en_vc & BIT(i)) { - __csid_configure_rdi_stream(csid, enable, i, 0); - __csid_ctrl_rdi(csid, enable, i); - } + /* RDIs */ + for (i = 0; i < CSID_MAX_RDI_SRC_STREAMS; i++) { + if (csid->phy.en_vc & BIT(i)) + __csid_configure_rdi_stream(csid, !!enable, i, 0); } } From ad543f3842f8e8998590f704fb09d160adcc610d Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 14 Apr 2026 20:52:00 +0200 Subject: [PATCH 384/394] media: qcom: camss: csid-340: Enable PIX interface routing Add PIX path support to the CSID-340 driver. The hardware exposes a dedicated PIX interface in addition to the existing RDI paths, but the driver only supported RDI stream configuration so far. The PIX path is configured similarly to RDI but requires decode-format to be specified. The PIX pipeline can subsequently perform further processing, including scaling, cropping, and statistics. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/camss/camss-csid-340.c | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c index 183d40d59afb..eca3f07b8a8a 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-340.c +++ b/drivers/media/platform/qcom/camss/camss-csid-340.c @@ -55,8 +55,6 @@ #define CSID_CTRL_HALT_AT_FRAME_BOUNDARY 0 #define CSID_CTRL_RESUME_AT_FRAME_BOUNDARY 1 -#define CSID_MAX_RDI_SRC_STREAMS (MSM_CSID_MAX_SRC_STREAMS - 1) - enum csid_iface { CSID_IFACE_PIX, CSID_IFACE_RDI0, @@ -64,10 +62,11 @@ enum csid_iface { CSID_IFACE_RDI2, }; -static enum csid_iface csid_port_iface_map[CSID_MAX_RDI_SRC_STREAMS] = { +static enum csid_iface csid_port_iface_map[MSM_CSID_MAX_SRC_STREAMS] = { [0] = CSID_IFACE_RDI0, [1] = CSID_IFACE_RDI1, [2] = CSID_IFACE_RDI2, + [3] = CSID_IFACE_PIX, }; static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy) @@ -84,13 +83,14 @@ static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); } -static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) +static void __csid_configure_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); enum csid_iface iface = csid_port_iface_map[port]; + u8 dt_id; u32 val; @@ -108,7 +108,11 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 */ dt_id = port & 0x03; - val = CSID_CFG0_DECODE_FORMAT_NOP; /* only for RDI path */ + if (iface == CSID_IFACE_PIX) + val = FIELD_PREP(CSID_CFG0_DECODE_FORMAT_MASK, format->decode_format); + else /* RDI is raw, no decoding */ + val = CSID_CFG0_DECODE_FORMAT_NOP; + val |= FIELD_PREP(CSID_CFG0_DT_MASK, format->data_type); val |= FIELD_PREP(CSID_CFG0_VC_MASK, vc); val |= FIELD_PREP(CSID_CFG0_DTID_MASK, dt_id); @@ -116,24 +120,23 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 if (enable) val |= CSID_CFG0_ENABLE; - dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x port=%u vc=%u)\n", + dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x df=0x%x port=%u vc=%u)\n", csid->id, enable ? "enable" : "disable", format->data_type, - port, vc); + format->decode_format, port, vc); writel_relaxed(val, csid->base + CSID_CFG0(iface)); writel_relaxed(enable, csid->base + CSID_CTRL(iface)); } -static void csid_configure_stream(struct csid_device *csid, u8 enable) +static void csid_configure_streams(struct csid_device *csid, u8 enable) { int i; __csid_configure_rx(csid, &csid->phy); - /* RDIs */ - for (i = 0; i < CSID_MAX_RDI_SRC_STREAMS; i++) { + for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { if (csid->phy.en_vc & BIT(i)) - __csid_configure_rdi_stream(csid, !!enable, i, 0); + __csid_configure_stream(csid, !!enable, i, 0); } } @@ -189,7 +192,7 @@ static void csid_subdev_init(struct csid_device *csid) {} const struct csid_hw_ops csid_ops_340 = { .configure_testgen_pattern = csid_configure_testgen_pattern, - .configure_stream = csid_configure_stream, + .configure_stream = csid_configure_streams, .hw_version = csid_hw_version, .isr = csid_isr, .reset = csid_reset, From 880a3e40337e5ddf18ab8d8e07fe963de8789cbf Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 14 Apr 2026 20:52:01 +0200 Subject: [PATCH 385/394] media: qcom: camss: vfe-340: Proper client handling We need to properly map camss WM index to our internal WM client instance. Today we only support RDI interfaces with the RDI_WM macro, introduce a __wm_to_client helper to support any interface. Signed-off-by: Loic Poulain Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/camss/camss-vfe-340.c | 84 ++++++++++--------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-vfe-340.c b/drivers/media/platform/qcom/camss/camss-vfe-340.c index 30d7630b3e8b..d129b0d3a6ed 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-340.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-340.c @@ -69,24 +69,19 @@ #define TFE_BUS_FRAMEDROP_CFG_0(c) BUS_REG(0x238 + (c) * 0x100) #define TFE_BUS_FRAMEDROP_CFG_1(c) BUS_REG(0x23c + (c) * 0x100) -/* - * TODO: differentiate the port id based on requested type of RDI, BHIST etc - * - * TFE write master IDs (clients) - * - * BAYER 0 - * IDEAL_RAW 1 - * STATS_TINTLESS_BG 2 - * STATS_BHIST 3 - * STATS_AWB_BG 4 - * STATS_AEC_BG 5 - * STATS_BAF 6 - * RDI0 7 - * RDI1 8 - * RDI2 9 - */ -#define RDI_WM(n) (7 + (n)) -#define TFE_WM_NUM 10 +enum tfe_client { + TFE_CLI_BAYER, + TFE_CLI_IDEAL_RAW, + TFE_CLI_STATS_TINTLESS_BG, + TFE_CLI_STATS_BHIST, + TFE_CLI_STATS_AWB_BG, + TFE_CLI_STATS_AEC_BG, + TFE_CLI_STATS_BAF, + TFE_CLI_RDI0, + TFE_CLI_RDI1, + TFE_CLI_RDI2, + TFE_CLI_NUM +}; enum tfe_iface { TFE_IFACE_PIX, @@ -108,6 +103,13 @@ enum tfe_subgroups { TFE_SUBGROUP_NUM }; +static enum tfe_client tfe_wm_client_map[VFE_LINE_NUM_MAX] = { + [VFE_LINE_RDI0] = TFE_CLI_RDI0, + [VFE_LINE_RDI1] = TFE_CLI_RDI1, + [VFE_LINE_RDI2] = TFE_CLI_RDI2, + [VFE_LINE_PIX] = TFE_CLI_BAYER, +}; + static enum tfe_iface tfe_line_iface_map[VFE_LINE_NUM_MAX] = { [VFE_LINE_RDI0] = TFE_IFACE_RDI0, [VFE_LINE_RDI1] = TFE_IFACE_RDI1, @@ -209,10 +211,10 @@ static irqreturn_t vfe_isr(int irq, void *dev) status = readl_relaxed(vfe->base + TFE_BUS_OVERFLOW_STATUS); if (status) { writel_relaxed(status, vfe->base + TFE_BUS_STATUS_CLEAR); - for (i = 0; i < TFE_WM_NUM; i++) { + for (i = 0; i < TFE_CLI_NUM; i++) { if (status & BIT(i)) dev_err_ratelimited(vfe->camss->dev, - "VFE%u: bus overflow for wm %u\n", + "VFE%u: bus overflow for client %u\n", vfe->id, i); } } @@ -235,49 +237,49 @@ static void vfe_enable_irq(struct vfe_device *vfe) TFE_BUS_IRQ_MASK_0_IMG_VIOL, vfe->base + TFE_BUS_IRQ_MASK_0); } -static void vfe_wm_update(struct vfe_device *vfe, u8 rdi, u32 addr, +static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr, struct vfe_line *line) { - u8 wm = RDI_WM(rdi); + u8 client = tfe_wm_client_map[wm]; - writel_relaxed(addr, vfe->base + TFE_BUS_IMAGE_ADDR(wm)); + writel_relaxed(addr, vfe->base + TFE_BUS_IMAGE_ADDR(client)); } -static void vfe_wm_start(struct vfe_device *vfe, u8 rdi, struct vfe_line *line) +static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line) { struct v4l2_pix_format_mplane *pix = &line->video_out.active_fmt.fmt.pix_mp; u32 stride = pix->plane_fmt[0].bytesperline; - u8 wm = RDI_WM(rdi); + u8 client = tfe_wm_client_map[wm]; /* Configuration for plain RDI frames */ - writel_relaxed(TFE_BUS_IMAGE_CFG_0_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_0(wm)); - writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(wm)); - writel_relaxed(TFE_BUS_IMAGE_CFG_2_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_2(wm)); - writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(wm)); - writel_relaxed(TFE_BUS_PACKER_CFG_FMT_PLAIN64, vfe->base + TFE_BUS_PACKER_CFG(wm)); + writel_relaxed(TFE_BUS_IMAGE_CFG_0_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_0(client)); + writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(client)); + writel_relaxed(TFE_BUS_IMAGE_CFG_2_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_2(client)); + writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(client)); + writel_relaxed(TFE_BUS_PACKER_CFG_FMT_PLAIN64, vfe->base + TFE_BUS_PACKER_CFG(client)); /* No dropped frames, one irq per frame */ - writel_relaxed(0, vfe->base + TFE_BUS_FRAMEDROP_CFG_0(wm)); - writel_relaxed(1, vfe->base + TFE_BUS_FRAMEDROP_CFG_1(wm)); - writel_relaxed(0, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_0(wm)); - writel_relaxed(1, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_1(wm)); + writel_relaxed(0, vfe->base + TFE_BUS_FRAMEDROP_CFG_0(client)); + writel_relaxed(1, vfe->base + TFE_BUS_FRAMEDROP_CFG_1(client)); + writel_relaxed(0, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_0(client)); + writel_relaxed(1, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_1(client)); vfe_enable_irq(vfe); writel(TFE_BUS_CLIENT_CFG_EN | TFE_BUS_CLIENT_CFG_MODE_FRAME, - vfe->base + TFE_BUS_CLIENT_CFG(wm)); + vfe->base + TFE_BUS_CLIENT_CFG(client)); - dev_dbg(vfe->camss->dev, "VFE%u: Started RDI%u width %u height %u stride %u\n", - vfe->id, rdi, pix->width, pix->height, stride); + dev_dbg(vfe->camss->dev, "VFE%u: Started client %u width %u height %u stride %u\n", + vfe->id, client, pix->width, pix->height, client); } -static void vfe_wm_stop(struct vfe_device *vfe, u8 rdi) +static void vfe_wm_stop(struct vfe_device *vfe, u8 wm) { - u8 wm = RDI_WM(rdi); + u8 client = tfe_wm_client_map[wm]; - writel(0, vfe->base + TFE_BUS_CLIENT_CFG(wm)); + writel(0, vfe->base + TFE_BUS_CLIENT_CFG(client)); - dev_dbg(vfe->camss->dev, "VFE%u: Stopped RDI%u\n", vfe->id, rdi); + dev_dbg(vfe->camss->dev, "VFE%u: Stopped client %u\n", vfe->id, client); } static const struct camss_video_ops vfe_video_ops_520 = { From 6a75e3d4f6428b90f398354212e3a2e0172851d6 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Tue, 14 Apr 2026 20:52:02 +0200 Subject: [PATCH 386/394] media: qcom: camss: vfe-340: Support for PIX client Add support for the vfe-340 PIX write engine, enabling frame capture through the PIX video device (e.g. msm_vfe0_pix). The PIX path requires a separate configuration flow from RDI, including cropping setup, line- based write engine configuration, and the correct packer format based on the input pixel format. In contrast to RDI, the PIX interface embeds a lightweight processing engine we can use for cropping, configuring custom stride/alignment, and, in the future, extracting frame statistics. The functionality has been validated on Arduino-Uno-Q with: media-ctl -d /dev/media0 --reset media-ctl -d /dev/media0 -l '"msm_csiphy0":1->"msm_csid0":0[1],"msm_csid0":4->"msm_vfe0_pix":0[1]' media-ctl -d /dev/media0 -V '"imx219 1-0010":0[fmt:SRGGB8_1X8/640x480 field:none]' media-ctl -d /dev/media0 -V '"msm_csiphy0":0[fmt:SRGGB8_1X8/640x480 field:none]' media-ctl -d /dev/media0 -V '"msm_csid0":0[fmt:SRGGB8_1X8/640x480 field:none]' media-ctl -d /dev/media0 -V '"msm_vfe0_pix":0[fmt:SRGGB8_1X8/640x480 field:none]' yavta -B capture-mplane --capture=3 -n 3 -f SRGGB8 -s 640x480 /dev/video3 Signed-off-by: Loic Poulain [bod: Squash down fix for bpp unused in vfe_packer_format] Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/camss/camss-vfe-340.c | 80 ++++++++++++++++--- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-vfe-340.c b/drivers/media/platform/qcom/camss/camss-vfe-340.c index d129b0d3a6ed..2526d568686d 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-340.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-340.c @@ -54,6 +54,7 @@ #define TFE_BUS_CLIENT_CFG(c) BUS_REG(0x200 + (c) * 0x100) #define TFE_BUS_CLIENT_CFG_EN BIT(0) +#define TFE_BUS_CLIENT_CFG_AUTORECOVER BIT(4) #define TFE_BUS_CLIENT_CFG_MODE_FRAME BIT(16) #define TFE_BUS_IMAGE_ADDR(c) BUS_REG(0x204 + (c) * 0x100) #define TFE_BUS_FRAME_INCR(c) BUS_REG(0x208 + (c) * 0x100) @@ -63,12 +64,23 @@ #define TFE_BUS_IMAGE_CFG_2(c) BUS_REG(0x214 + (c) * 0x100) #define TFE_BUS_IMAGE_CFG_2_DEFAULT 0xffff #define TFE_BUS_PACKER_CFG(c) BUS_REG(0x218 + (c) * 0x100) +#define TFE_BUS_PACKER_CFG_FMT_PLAIN8 0x1 #define TFE_BUS_PACKER_CFG_FMT_PLAIN64 0xa +#define TFE_BUS_PACKER_CFG_FMT_MIPI10 0xc +#define TFE_BUS_PACKER_CFG_FMT_MIPI12 0xd #define TFE_BUS_IRQ_SUBSAMPLE_CFG_0(c) BUS_REG(0x230 + (c) * 0x100) #define TFE_BUS_IRQ_SUBSAMPLE_CFG_1(c) BUS_REG(0x234 + (c) * 0x100) #define TFE_BUS_FRAMEDROP_CFG_0(c) BUS_REG(0x238 + (c) * 0x100) #define TFE_BUS_FRAMEDROP_CFG_1(c) BUS_REG(0x23c + (c) * 0x100) +#define PP_CROP_REG(a) (0x2800 + (a)) +#define TFE_PP_CROP_CFG PP_CROP_REG(0x60) +#define TFE_PP_CROP_CFG_EN (BIT(0) | BIT(9)) +#define TFE_PP_CROP_LINE_CFG PP_CROP_REG(0x68) +#define TFE_PP_CROP_FIRST GENMASK(29, 16) +#define TFE_PP_CROP_LAST GENMASK(13, 0) +#define TFE_PP_CROP_PIX_CFG PP_CROP_REG(0x6C) + enum tfe_client { TFE_CLI_BAYER, TFE_CLI_IDEAL_RAW, @@ -245,18 +257,69 @@ static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr, writel_relaxed(addr, vfe->base + TFE_BUS_IMAGE_ADDR(client)); } +static u32 vfe_packer_format(struct vfe_device *vfe, u32 pixelformat) +{ + const struct camss_formats *fmt = vfe->res->formats_rdi; + unsigned int bpp = 0; + int i; + + for (i = 0; i < fmt->nformats; i++) { + if (fmt->formats[i].pixelformat == pixelformat) { + bpp = fmt->formats[i].mbus_bpp; + break; + } + } + + switch (bpp) { + case 10: + return TFE_BUS_PACKER_CFG_FMT_MIPI10; + case 12: + return TFE_BUS_PACKER_CFG_FMT_MIPI12; + default: + return TFE_BUS_PACKER_CFG_FMT_PLAIN8; + } +} + static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line) { struct v4l2_pix_format_mplane *pix = &line->video_out.active_fmt.fmt.pix_mp; u32 stride = pix->plane_fmt[0].bytesperline; u8 client = tfe_wm_client_map[wm]; + u32 cfg = TFE_BUS_CLIENT_CFG_EN; - /* Configuration for plain RDI frames */ - writel_relaxed(TFE_BUS_IMAGE_CFG_0_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_0(client)); - writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(client)); - writel_relaxed(TFE_BUS_IMAGE_CFG_2_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_2(client)); - writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(client)); - writel_relaxed(TFE_BUS_PACKER_CFG_FMT_PLAIN64, vfe->base + TFE_BUS_PACKER_CFG(client)); + if (client == TFE_CLI_BAYER) { /* PIX - Line based */ + struct v4l2_rect *crop = &line->crop; + + /* Cropping */ + writel_relaxed(TFE_PP_CROP_CFG_EN, vfe->base + TFE_PP_CROP_CFG); + writel_relaxed(FIELD_PREP(TFE_PP_CROP_FIRST, crop->top) | + FIELD_PREP(TFE_PP_CROP_LAST, crop->top + crop->height - 1), + vfe->base + TFE_PP_CROP_LINE_CFG); + writel_relaxed(FIELD_PREP(TFE_PP_CROP_FIRST, crop->left) | + FIELD_PREP(TFE_PP_CROP_LAST, crop->left + crop->width - 1), + vfe->base + TFE_PP_CROP_PIX_CFG); + + /* Write Engine */ + writel_relaxed(pix->width | (pix->height << 16), + vfe->base + TFE_BUS_IMAGE_CFG_0(client)); + writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(client)); + writel_relaxed(stride, vfe->base + TFE_BUS_IMAGE_CFG_2(client)); + writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(client)); + writel_relaxed(vfe_packer_format(vfe, pix->pixelformat), + vfe->base + TFE_BUS_PACKER_CFG(client)); + + cfg |= TFE_BUS_CLIENT_CFG_AUTORECOVER; + } else { /* RDI - Frame based */ + writel_relaxed(TFE_BUS_IMAGE_CFG_0_DEFAULT, + vfe->base + TFE_BUS_IMAGE_CFG_0(client)); + writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(client)); + writel_relaxed(TFE_BUS_IMAGE_CFG_2_DEFAULT, + vfe->base + TFE_BUS_IMAGE_CFG_2(client)); + writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(client)); + writel_relaxed(TFE_BUS_PACKER_CFG_FMT_PLAIN64, + vfe->base + TFE_BUS_PACKER_CFG(client)); + cfg |= TFE_BUS_CLIENT_CFG_MODE_FRAME; + } /* No dropped frames, one irq per frame */ writel_relaxed(0, vfe->base + TFE_BUS_FRAMEDROP_CFG_0(client)); @@ -266,11 +329,10 @@ static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line) vfe_enable_irq(vfe); - writel(TFE_BUS_CLIENT_CFG_EN | TFE_BUS_CLIENT_CFG_MODE_FRAME, - vfe->base + TFE_BUS_CLIENT_CFG(client)); + writel(cfg, vfe->base + TFE_BUS_CLIENT_CFG(client)); dev_dbg(vfe->camss->dev, "VFE%u: Started client %u width %u height %u stride %u\n", - vfe->id, client, pix->width, pix->height, client); + vfe->id, client, pix->width, pix->height, stride); } static void vfe_wm_stop(struct vfe_device *vfe, u8 wm) From 10400b9455e735601d94e7293db1c48a685547f0 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 2 Jun 2026 22:01:24 +0100 Subject: [PATCH 387/394] media: qcom: iris: Fix FPS calculation and VPP FW overhead Use div_u64() instead of mult_fract as u64 operator division fails on 32 bit systems which don't link against libgcc. Fixes: 5c66647a5c3e ("media: iris: add FPS calculation and VPP FW overhead in frequency formula") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606030132.qnBXVDkM-lkp@intel.com/ Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_vpu_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index 5a85568c5ee1..37dbfe433a08 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -444,7 +444,7 @@ u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_siz /* 1.05 is VPP FW overhead */ if (inst->fw_caps[STAGE].value == STAGE_2) - vpp_cycles += mult_frac(vpp_cycles, 5, 100); + vpp_cycles += div_u64(vpp_cycles * 5, 100); vsp_cycles = fps * data_size * 8; vsp_cycles = div_u64(vsp_cycles, 2); From 7d460ca3917b47429439fd5793ac017b4031a92f Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 2 Jun 2026 10:39:16 +0200 Subject: [PATCH 388/394] media: qcom: iris: add helpers for 8bit and 10bit formats To simplify code checking for pixel formats, add helpers to check for 8bit and 10bit formats. Reviewed-by: Dikshita Agarwal Reviewed-by: Dmitry Baryshkov Tested-by: Wangao Wang Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_utils.c | 12 ++++++++++++ drivers/media/platform/qcom/iris/iris_utils.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c index 29b07d88507e..085665cd74ff 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.c +++ b/drivers/media/platform/qcom/iris/iris_utils.c @@ -38,6 +38,18 @@ bool iris_split_mode_enabled(struct iris_inst *inst) inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C; } +bool iris_fmt_is_8bit(u32 pixelformat) +{ + return pixelformat == V4L2_PIX_FMT_NV12 || + pixelformat == V4L2_PIX_FMT_QC08C; +} + +bool iris_fmt_is_10bit(u32 pixelformat) +{ + return pixelformat == V4L2_PIX_FMT_P010 || + pixelformat == V4L2_PIX_FMT_QC10C; +} + void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type, enum vb2_buffer_state state) { diff --git a/drivers/media/platform/qcom/iris/iris_utils.h b/drivers/media/platform/qcom/iris/iris_utils.h index b5705d156431..228a5f963812 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.h +++ b/drivers/media/platform/qcom/iris/iris_utils.h @@ -45,6 +45,8 @@ bool iris_res_is_less_than(u32 width, u32 height, u32 ref_width, u32 ref_height); int iris_get_mbpf(struct iris_inst *inst); bool iris_split_mode_enabled(struct iris_inst *inst); +bool iris_fmt_is_8bit(u32 pixelformat); +bool iris_fmt_is_10bit(u32 pixelformat); struct iris_inst *iris_get_instance(struct iris_core *core, u32 session_id); void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type, enum vb2_buffer_state state); From 7aa4969dd9af6ad4beef6614d99b3673f42359a5 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 2 Jun 2026 10:39:17 +0200 Subject: [PATCH 389/394] media: qcom: iris: add QC10C & P010 buffer size calculations The P010 (YUV format with 16-bits per pixel with interleaved UV) and QC10C (P010 compressed mode similar to QC08C) requires specific buffer calculations to allocate the right buffer size for the DPB (decoded picture buffer) frames and frames consumed by userspace. Similar to 8bit, the 10bit DPB frames uses QC10C format. Reviewed-by: Bryan O'Donoghue Tested-by: Wangao Wang Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue --- .../media/platform/qcom/iris/iris_buffer.c | 195 +++++++++++++++++- 1 file changed, 194 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index ef7f6f931557..fbe2dd87f0d6 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -15,8 +15,11 @@ #define MAX_WIDTH 4096 #define MAX_HEIGHT 2304 #define Y_STRIDE_ALIGN 128 +#define Y_STRIDE_ALIGN_P010 256 #define UV_STRIDE_ALIGN 128 +#define UV_STRIDE_ALIGN_P010 256 #define Y_SCANLINE_ALIGN 32 +#define Y_SCANLINE_ALIGN_QC10C 16 #define UV_SCANLINE_ALIGN 16 #define UV_SCANLINE_ALIGN_QC08C 32 #define META_STRIDE_ALIGNED 64 @@ -80,6 +83,63 @@ static u32 iris_yuv_buffer_size_nv12(struct iris_inst *inst) return ALIGN(y_plane + uv_plane, PIXELS_4K); } +/* + * P010: + * YUV 4:2:0 image with a plane of 10 bit Y samples followed + * by an interleaved U/V plane containing 10 bit 2x2 subsampled + * colour difference samples. + * + * <-Y/UV_Stride (aligned to 256)-> + * <----- Width*2 -------> + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^ + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | y_scanlines (aligned to 32) + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . V | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * U V U V U V U V U V U V . . . . ^ + * U V U V U V U V U V U V . . . . | + * U V U V U V U V U V U V . . . . | + * U V U V U V U V U V U V . . . . uv_scanlines (aligned to 16) + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . . . --> Buffer size aligned to 4K + * + * y_stride : Width*2 aligned to 256 + * uv_stride : Width*2 aligned to 256 + * y_scanlines: Height aligned to 32 + * uv_scanlines: Height/2 aligned to 16 + * Total size = align((y_stride * y_scanlines + * + uv_stride * uv_scanlines , 4096) + * + * Note: All the alignments are hardware requirements. + */ +static u32 iris_yuv_buffer_size_p010(struct iris_inst *inst) +{ + u32 y_plane, uv_plane, y_stride, uv_stride, y_scanlines, uv_scanlines; + struct v4l2_format *f; + + if (inst->domain == DECODER) + f = inst->fmt_dst; + else + f = inst->fmt_src; + + y_stride = ALIGN(f->fmt.pix_mp.width * 2, Y_STRIDE_ALIGN_P010); + uv_stride = ALIGN(f->fmt.pix_mp.width * 2, UV_STRIDE_ALIGN_P010); + y_scanlines = ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN); + uv_scanlines = ALIGN((f->fmt.pix_mp.height + 1) >> 1, UV_SCANLINE_ALIGN); + y_plane = y_stride * y_scanlines; + uv_plane = uv_stride * uv_scanlines; + + return ALIGN(y_plane + uv_plane, PIXELS_4K); +} + /* * QC08C: * Compressed Macro-tile format for NV12. @@ -204,6 +264,132 @@ static u32 iris_yuv_buffer_size_qc08c(struct iris_inst *inst) return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane, PIXELS_4K); } +/* + * QC10C: + * UBWC-compressed format for P010. + * Contains 4 planes in the following order - + * (A) Y_Meta_Plane + * (B) Y_UBWC_Plane + * (C) UV_Meta_Plane + * (D) UV_UBWC_Plane + * + * Y_Meta_Plane consists of meta information to decode compressed + * tile data in Y_UBWC_Plane. + * Y_UBWC_Plane consists of Y data in compressed macro-tile format. + * UBWC decoder block will use the Y_Meta_Plane data together with + * Y_UBWC_Plane data to produce loss-less uncompressed 10 bit Y samples. + * + * UV_Meta_Plane consists of meta information to decode compressed + * tile data in UV_UBWC_Plane. + * UV_UBWC_Plane consists of UV data in compressed macro-tile format. + * UBWC decoder block will use UV_Meta_Plane data together with + * UV_UBWC_Plane data to produce loss-less uncompressed 10 bit 2x2 + * subsampled color difference samples. + * + * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable + * and randomly accessible. There is no dependency between tiles. + * + * <----- Y Meta stride -----> (aligned to 64) + * <-------- Width ----------> (aligned to 48) + * M M M M M M M M M M M M . . ^ ^ + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . Height | + * M M M M M M M M M M M M . . | Meta_Y_Scanlines (aligned to 16) + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . V | + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * . . . . . . . . . . . . . . V + * <--Compressed tile Y stride --> (aligned to 256) + * <------- Width * 4/3 ---------> (aligned to 48) + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . ^ ^ + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . Height | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | Macro_tile_Y_Scanlines (aligned to 16) + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . V | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * . . . . . . . . . . . . . . . . V + * <---- UV Meta stride ----> (aligned to 64) + * <----- Width / 2 --------> (aligned to 24) + * M M M M M M M M M M M M . . ^ ^ + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . Height/2 | + * M M M M M M M M M M M M . . V M_UV_Scanlines (aligned to 16) + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * <--Compressed tile UV stride--> (aligned to 256) + * <------- Width * 4/3 ---------> (aligned to 48) + * U* V* U* V* U* V* U* V* . . . . ^ + * U* V* U* V* U* V* U* V* . . . . | + * U* V* U* V* U* V* U* V* . . . . | + * U* V* U* V* U* V* U* V* . . . . UV_Scanlines (aligned to 16) + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * + * y_stride: width aligned to 256 + * uv_stride: width aligned to 256 + * y_scanlines: height aligned to 16 + * uv_scanlines: height aligned to 16 + * y_plane: buffer size aligned to 4096 + * uv_plane: buffer size aligned to 4096 + * y_meta_stride: width aligned to 64 + * y_meta_scanlines: height aligned to 16 + * y_meta_plane: buffer size aligned to 4096 + * uv_meta_stride: width aligned to 64 + * uv_meta_scanlines: height aligned to 16 + * uv_meta_plane: buffer size aligned to 4096 + * + * Total size = align( y_plane + uv_plane + + * y_meta_plane + uv_meta_plane, 4096) + * + * Note: All the alignments are hardware requirements. + */ +static u32 iris_yuv_buffer_size_qc10c(struct iris_inst *inst) +{ + u32 y_plane, uv_plane, y_stride, uv_stride; + u32 uv_meta_stride, uv_meta_plane; + u32 y_meta_stride, y_meta_plane; + struct v4l2_format *f; + + if (inst->domain == DECODER) + f = inst->fmt_dst; + else + f = inst->fmt_src; + + y_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width, 48), + META_STRIDE_ALIGNED); + y_meta_plane = y_meta_stride * ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.height, 4), + META_SCANLINE_ALIGNED); + y_meta_plane = ALIGN(y_meta_plane, PIXELS_4K); + + y_stride = ALIGN(f->fmt.pix_mp.width * 4 / 3, Y_STRIDE_ALIGN_P010); + y_plane = ALIGN(y_stride * ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN_QC10C), + PIXELS_4K); + + uv_meta_stride = ALIGN(DIV_ROUND_UP((f->fmt.pix_mp.width + 1) >> 1, 24), + META_STRIDE_ALIGNED); + uv_meta_plane = uv_meta_stride * + ALIGN(DIV_ROUND_UP((f->fmt.pix_mp.height + 1) >> 1, 4), + META_SCANLINE_ALIGNED); + uv_meta_plane = ALIGN(uv_meta_plane, PIXELS_4K); + + uv_stride = ALIGN(f->fmt.pix_mp.width * 4 / 3, UV_STRIDE_ALIGN_P010); + uv_plane = ALIGN(uv_stride * ALIGN((f->fmt.pix_mp.height + 1) >> 1, UV_SCANLINE_ALIGN), + PIXELS_4K); + + return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane, PIXELS_4K); +} + static u32 iris_dec_bitstream_buffer_size(struct iris_inst *inst) { struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps; @@ -268,10 +454,17 @@ int iris_get_buffer_size(struct iris_inst *inst, case BUF_OUTPUT: if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C) return iris_yuv_buffer_size_qc08c(inst); + else if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC10C) + return iris_yuv_buffer_size_qc10c(inst); + else if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_P010) + return iris_yuv_buffer_size_p010(inst); else return iris_yuv_buffer_size_nv12(inst); case BUF_DPB: - return iris_yuv_buffer_size_qc08c(inst); + if (iris_fmt_is_10bit(inst->fmt_dst->fmt.pix_mp.pixelformat)) + return iris_yuv_buffer_size_qc10c(inst); + else + return iris_yuv_buffer_size_qc08c(inst); default: return 0; } From 3ea0343c09be74ae9eda9dff9c153750a6d9b961 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 2 Jun 2026 10:39:18 +0200 Subject: [PATCH 390/394] media: qcom: iris: gen2: add support for 10bit decoding Add the necessary plumbing into the HFi Gen2 to signal the decoder the right 10bit pixel format and stride when in compressed mode. Reviewed-by: Dmitry Baryshkov Tested-by: Wangao Wang Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue --- .../qcom/iris/iris_hfi_gen2_command.c | 75 ++++++++++++++++++- .../qcom/iris/iris_hfi_gen2_defines.h | 1 + drivers/media/platform/qcom/iris/iris_utils.c | 4 +- 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index 7ac69af63ead..ca2954f8bd3a 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -480,8 +480,20 @@ static int iris_hfi_gen2_set_colorformat(struct iris_inst *inst, u32 plane) if (inst->domain == DECODER) { pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat; - hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ? - HFI_COLOR_FMT_NV12 : HFI_COLOR_FMT_NV12_UBWC; + switch (pixelformat) { + case V4L2_PIX_FMT_NV12: + hfi_colorformat = HFI_COLOR_FMT_NV12; + break; + case V4L2_PIX_FMT_QC08C: + hfi_colorformat = HFI_COLOR_FMT_NV12_UBWC; + break; + case V4L2_PIX_FMT_P010: + hfi_colorformat = HFI_COLOR_FMT_P010; + break; + case V4L2_PIX_FMT_QC10C: + hfi_colorformat = HFI_COLOR_FMT_TP10_UBWC; + break; + } } else { pixelformat = inst->fmt_src->fmt.pix_mp.pixelformat; hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ? @@ -516,7 +528,8 @@ static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst, u32 stride_uv = stride_y; scanline_uv = scanline_y / 2; - if (pixelformat != V4L2_PIX_FMT_NV12) + if (pixelformat != V4L2_PIX_FMT_NV12 && + pixelformat != V4L2_PIX_FMT_P010) return 0; payload[0] = stride_y << 16 | scanline_y; @@ -531,6 +544,61 @@ static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst, u32 sizeof(u64)); } +static int iris_hfi_gen2_set_ubwc_stride_scanline(struct iris_inst *inst, u32 plane) +{ + u32 meta_stride_y, meta_scanline_y, meta_stride_uv, meta_scanline_uv; + u32 stride_y, scanline_y, stride_uv, scanline_uv; + u32 port = iris_hfi_gen2_get_port(inst, plane); + u32 pixelformat, width, height; + u32 payload[4]; + + if (inst->domain != DECODER || + inst->fmt_src->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_AV1) + return 0; + + pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat; + width = inst->fmt_dst->fmt.pix_mp.width; + height = inst->fmt_dst->fmt.pix_mp.height; + + switch (pixelformat) { + case V4L2_PIX_FMT_QC08C: + stride_y = ALIGN(width, 128); + scanline_y = ALIGN(height, 32); + stride_uv = ALIGN(width, 128); + scanline_uv = ALIGN((height + 1) >> 1, 32); + meta_stride_y = ALIGN(DIV_ROUND_UP(width, 32), 64); + meta_scanline_y = ALIGN(DIV_ROUND_UP(height, 8), 16); + meta_stride_uv = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 16), 64); + meta_scanline_uv = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 8), 16); + break; + case V4L2_PIX_FMT_QC10C: + stride_y = ALIGN(width * 4 / 3, 256); + scanline_y = ALIGN(height, 16); + stride_uv = ALIGN(width * 4 / 3, 256); + scanline_uv = ALIGN((height + 1) >> 1, 16); + meta_stride_y = ALIGN(DIV_ROUND_UP(width, 48), 64); + meta_scanline_y = ALIGN(DIV_ROUND_UP(height, 4), 16); + meta_stride_uv = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 24), 64); + meta_scanline_uv = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 4), 16); + break; + default: + return 0; + } + + payload[0] = stride_y << 16 | scanline_y; + payload[1] = stride_uv << 16 | scanline_uv; + payload[2] = meta_stride_y << 16 | meta_scanline_y; + payload[3] = meta_stride_uv << 16 | meta_scanline_uv; + + return iris_hfi_gen2_session_set_property(inst, + HFI_PROP_UBWC_STRIDE_SCANLINE, + HFI_HOST_FLAGS_NONE, + port, + HFI_PAYLOAD_U32_ARRAY, + &payload[0], + sizeof(u32) * 4); +} + static int iris_hfi_gen2_set_tier(struct iris_inst *inst, u32 plane) { u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); @@ -619,6 +687,7 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p {HFI_PROP_OPB_ENABLE, iris_hfi_gen2_set_opb_enable }, {HFI_PROP_COLOR_FORMAT, iris_hfi_gen2_set_colorformat }, {HFI_PROP_LINEAR_STRIDE_SCANLINE, iris_hfi_gen2_set_linear_stride_scanline }, + {HFI_PROP_UBWC_STRIDE_SCANLINE, iris_hfi_gen2_set_ubwc_stride_scanline }, {HFI_PROP_TIER, iris_hfi_gen2_set_tier }, {HFI_PROP_FRAME_RATE, iris_hfi_gen2_set_frame_rate }, {HFI_PROP_AV1_FILM_GRAIN_PRESENT, iris_hfi_gen2_set_film_grain }, diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h index d09096a9d5f9..776b21cd11b2 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h @@ -136,6 +136,7 @@ enum hfi_flip { #define HFI_PROP_OPB_ENABLE 0x03000184 #define HFI_PROP_AV1_TILE_ROWS_COLUMNS 0x03000187 #define HFI_PROP_AV1_DRAP_CONFIG 0x03000189 +#define HFI_PROP_UBWC_STRIDE_SCANLINE 0x03000190 #define HFI_PROP_COMV_BUFFER_COUNT 0x03000193 #define HFI_PROP_AV1_UNIFORM_TILE_SPACING 0x03000197 #define HFI_PROP_END 0x03FFFFFF diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c index 085665cd74ff..ba5c8dc1280c 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.c +++ b/drivers/media/platform/qcom/iris/iris_utils.c @@ -35,7 +35,9 @@ int iris_get_mbpf(struct iris_inst *inst) bool iris_split_mode_enabled(struct iris_inst *inst) { return inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_NV12 || - inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C; + inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C || + inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_P010 || + inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC10C; } bool iris_fmt_is_8bit(u32 pixelformat) From 2f2f76d43314a8ae86aedae28f908a6a03e22521 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 2 Jun 2026 10:39:19 +0200 Subject: [PATCH 391/394] media: qcom: iris: vdec: update size and stride calculations for 10bit formats Update the gen2 response and vdec s_fmt code to take in account the P010 and QC010 when calculating the width, height and stride. Reviewed-by: Dmitry Baryshkov Tested-by: Wangao Wang Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue --- .../qcom/iris/iris_hfi_gen2_response.c | 21 +++++++++++++--- drivers/media/platform/qcom/iris/iris_vdec.c | 24 ++++++++++++++++--- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c index c350d231265e..aca90aab8548 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c @@ -542,9 +542,24 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst) pixmp_ip->width = width; pixmp_ip->height = height; - pixmp_op->width = ALIGN(width, 128); - pixmp_op->height = ALIGN(height, 32); - pixmp_op->plane_fmt[0].bytesperline = ALIGN(width, 128); + switch (pixmp_op->pixelformat) { + case V4L2_PIX_FMT_P010: + pixmp_op->width = ALIGN(width, 128); + pixmp_op->height = ALIGN(height, 32); + pixmp_op->plane_fmt[0].bytesperline = ALIGN(width * 2, 256); + break; + case V4L2_PIX_FMT_QC10C: + pixmp_op->width = roundup(width, 192); + pixmp_op->height = ALIGN(height, 16); + pixmp_op->plane_fmt[0].bytesperline = ALIGN(pixmp_op->width * 4 / 3, 256); + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_QC08C: + pixmp_op->width = ALIGN(width, 128); + pixmp_op->height = ALIGN(height, 32); + pixmp_op->plane_fmt[0].bytesperline = pixmp_op->width; + break; + } pixmp_op->plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT); matrix_coeff = subsc_params.color_info & 0xFF; diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index b65832042dc8..92e9201cd3a4 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -263,10 +263,28 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) fmt = inst->fmt_dst; fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat; - fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); - fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); fmt->fmt.pix_mp.num_planes = 1; - fmt->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(f->fmt.pix_mp.width, 128); + switch (f->fmt.pix_mp.pixelformat) { + case V4L2_PIX_FMT_P010: + fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); + fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); + fmt->fmt.pix_mp.plane_fmt[0].bytesperline = + ALIGN(f->fmt.pix_mp.width * 2, 256); + break; + case V4L2_PIX_FMT_QC10C: + fmt->fmt.pix_mp.width = roundup(f->fmt.pix_mp.width, 192); + fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 16); + fmt->fmt.pix_mp.plane_fmt[0].bytesperline = + ALIGN(f->fmt.pix_mp.width * 4 / 3, 256); + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_QC08C: + fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); + fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); + fmt->fmt.pix_mp.plane_fmt[0].bytesperline = + ALIGN(f->fmt.pix_mp.width, 128); + break; + } fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT); inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT); inst->buffers[BUF_OUTPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage; From 20c3ef4c7cae76d4e15f31c812aa7761def2207a Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 2 Jun 2026 10:39:20 +0200 Subject: [PATCH 392/394] media: qcom: iris: vdec: update find_format to handle 8bit and 10bit formats The 10bit pixel format can be only used when the decoder identifies the stream as decoding into 10bit pixel format buffers, so update the find_format helper to filter the formats and only allow the proper formats when setting or trying a capture format. Reviewed-by: Dmitry Baryshkov Reviewed-by: Bryan O'Donoghue Tested-by: Wangao Wang Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue --- .../platform/qcom/iris/iris_platform_common.h | 1 + drivers/media/platform/qcom/iris/iris_vdec.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 6d69a1e3dcd3..c9256f2323dc 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -17,6 +17,7 @@ struct iris_inst; #define REGISTER_BIT_DEPTH(luma, chroma) ((luma) << 16 | (chroma)) #define BIT_DEPTH_8 REGISTER_BIT_DEPTH(8, 8) +#define BIT_DEPTH_10 REGISTER_BIT_DEPTH(10, 10) #define CODED_FRAMES_PROGRESSIVE 0x0 #define DEFAULT_MAX_HOST_BUF_COUNT 64 #define DEFAULT_MAX_HOST_BURST_BUF_COUNT 256 diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index 92e9201cd3a4..d55671340600 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -93,10 +93,23 @@ static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type) for (i = 0; i < size; i++) { if (fmt[i] == pixfmt) - return true; + break; } - return false; + if (i == size) + return false; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (iris_fmt_is_8bit(pixfmt) && + inst->fw_caps[BIT_DEPTH].value == BIT_DEPTH_10) + return false; + + if (iris_fmt_is_10bit(pixfmt) && + inst->fw_caps[BIT_DEPTH].value != BIT_DEPTH_10) + return false; + } + + return true; } static u32 find_format_by_index(struct iris_inst *inst, u32 index, u32 type) From 65c06d2edded3b1e1633bf75f0f7a26b609ed5ac Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 2 Jun 2026 10:39:21 +0200 Subject: [PATCH 393/394] media: qcom: iris: vdec: allow GEN2 decoding into 10bit format Add the necessary bits into the gen2 platforms tables and handlers to allow decoding streams into 10bit pixel formats. Reviewed-by: Dmitry Baryshkov Tested-by: Wangao Wang Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2.c | 8 +++++--- .../platform/qcom/iris/iris_hfi_gen2_response.c | 16 +++++++++++++++- drivers/media/platform/qcom/iris/iris_instance.h | 2 ++ drivers/media/platform/qcom/iris/iris_vdec.c | 2 ++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c index 7a85c1d4e5e6..acc0ed8adda1 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -31,9 +31,10 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { { .cap_id = PROFILE_HEVC, .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, - .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE, + .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) | - BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE), + BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE) | + BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10), .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, .hfi_id = HFI_PROP_PROFILE, .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, @@ -262,7 +263,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { { .cap_id = BIT_DEPTH, .min = BIT_DEPTH_8, - .max = BIT_DEPTH_8, + .max = BIT_DEPTH_10, .step_or_mask = 1, .value = BIT_DEPTH_8, .hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH, @@ -996,6 +997,7 @@ static const u32 sm8550_vdec_output_config_params[] = { HFI_PROP_OPB_ENABLE, HFI_PROP_COLOR_FORMAT, HFI_PROP_LINEAR_STRIDE_SCANLINE, + HFI_PROP_UBWC_STRIDE_SCANLINE, }; static const u32 sm8550_venc_output_config_params[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c index aca90aab8548..25162ae71357 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c @@ -542,6 +542,15 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst) pixmp_ip->width = width; pixmp_ip->height = height; + if (subsc_params.bit_depth == BIT_DEPTH_8 && + pixmp_op->pixelformat != V4L2_PIX_FMT_NV12 && + pixmp_op->pixelformat != V4L2_PIX_FMT_QC08C) + pixmp_op->pixelformat = V4L2_PIX_FMT_NV12; + else if (subsc_params.bit_depth == BIT_DEPTH_10 && + pixmp_op->pixelformat != V4L2_PIX_FMT_P010 && + pixmp_op->pixelformat != V4L2_PIX_FMT_QC10C) + pixmp_op->pixelformat = V4L2_PIX_FMT_P010; + switch (pixmp_op->pixelformat) { case V4L2_PIX_FMT_P010: pixmp_op->width = ALIGN(width, 128); @@ -625,7 +634,12 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst) inst->fw_caps[POC].value = subsc_params.pic_order_cnt; inst->fw_caps[TIER].value = subsc_params.tier; - if (subsc_params.bit_depth != BIT_DEPTH_8 || + if (subsc_params.bit_depth == BIT_DEPTH_8) + inst->fw_caps[BIT_DEPTH].value = BIT_DEPTH_8; + else + inst->fw_caps[BIT_DEPTH].value = BIT_DEPTH_10; + + if ((subsc_params.bit_depth != BIT_DEPTH_8 && subsc_params.bit_depth != BIT_DEPTH_10) || !(subsc_params.coded_frames & HFI_BITMASK_FRAME_MBS_ONLY_FLAG)) { dev_err(core->dev, "unsupported content, bit depth: %x, pic_struct = %x\n", subsc_params.bit_depth, subsc_params.coded_frames); diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index c54d8ec8562a..a770331d1675 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -27,6 +27,8 @@ enum iris_fmt_type_out { enum iris_fmt_type_cap { IRIS_FMT_NV12, IRIS_FMT_QC08C, + IRIS_FMT_TP10, + IRIS_FMT_QC10C, }; /** diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index d55671340600..a8d6354bee28 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -71,6 +71,8 @@ void iris_vdec_inst_deinit(struct iris_inst *inst) static const u32 iris_vdec_formats_cap[] = { [IRIS_FMT_NV12] = V4L2_PIX_FMT_NV12, [IRIS_FMT_QC08C] = V4L2_PIX_FMT_QC08C, + [IRIS_FMT_TP10] = V4L2_PIX_FMT_P010, + [IRIS_FMT_QC10C] = V4L2_PIX_FMT_QC10C, }; static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type) From 06cb687a5132fcffe624c0070576ab852ac6b568 Mon Sep 17 00:00:00 2001 From: Mirela Rabulea Date: Fri, 22 May 2026 17:31:20 +0300 Subject: [PATCH 394/394] media: v4l2-fwnode: Fix subdev owner overwritten in v4l2_async_register_subdev_sensor() The v4l2 helper v4l2_async_register_subdev_sensor() calls v4l2_async_register_subdev(), which is a macro that expands to __v4l2_async_register_subdev(sd,THIS_MODULE). Since the macro is expanded inside v4l2-fwnode.c, THIS_MODULE resolves to the v4l2-fwnode module rather than the sensor driver module that originally set sd->owner. When v4l2-fwnode is built-in, THIS_MODULE evaluates to NULL, which then overwrites the sensor driver's owner with NULL. This causes the problem that the sensor module's reference count is never incremented during async registration, so the module can be removed while the subdevice is still in use by a notifier (e.g., a CSI-2 receiver bridge driver). Fix this by renaming v4l2_async_register_subdev_sensor() to __v4l2_async_register_subdev_sensor() with an added explicit module argument and introducing a wrapper macro: #define v4l2_async_register_subdev_sensor(sd) \ __v4l2_async_register_subdev_sensor(sd, THIS_MODULE) This ensures the sensor driver module is properly referenced even when the sensor driver does not init the owner field before calling v4l2_async_register_subdev_sensor() and prevents premature module removal. Fixes: aef69d54755d ("media: v4l: fwnode: Add a convenience function for registering sensors") Cc: stable@vger.kernel.org Suggested-by: Frank Li Link: https://lore.kernel.org/linux-media/20240315073125.275501-2-sakari.ailus@linux.intel.com/ Signed-off-by: Mirela Rabulea Reviewed-by: Laurent Pinchart Reviewed-by: Frank Li Signed-off-by: Sakari Ailus --- drivers/media/v4l2-core/v4l2-fwnode.c | 6 +++--- include/media/v4l2-async.h | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 77f3298821b5..62a3a452f788 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -1256,7 +1256,7 @@ v4l2_async_nf_parse_fwnode_sensor(struct device *dev, return 0; } -int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) +int __v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module) { struct v4l2_async_notifier *notifier; int ret; @@ -1282,7 +1282,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) if (ret < 0) goto out_cleanup; - ret = v4l2_async_register_subdev(sd); + ret = __v4l2_async_register_subdev(sd, module); if (ret < 0) goto out_unregister; @@ -1300,7 +1300,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd) return ret; } -EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor); +EXPORT_SYMBOL_GPL(__v4l2_async_register_subdev_sensor); MODULE_DESCRIPTION("V4L2 fwnode binding parsing library"); MODULE_LICENSE("GPL"); diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index f26c323e9c96..54a2d9620ed5 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -333,8 +333,10 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module); * An error is returned if the module is no longer loaded on any attempts * to register it. */ +#define v4l2_async_register_subdev_sensor(sd) \ + __v4l2_async_register_subdev_sensor(sd, THIS_MODULE) int __must_check -v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd); +__v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module); /** * v4l2_async_unregister_subdev - unregisters a sub-device to the asynchronous