From ad2698efce37e910dcf3c3914263e6cb3e86f8cd Mon Sep 17 00:00:00 2001 From: Nas Chung Date: Thu, 25 Jul 2024 15:10:32 +0900 Subject: media: uapi: v4l: Change V4L2_TYPE_IS_CAPTURE condition Explicitly compare a buffer type only with valid buffer types, to avoid matching a buffer type outside of the valid buffer type set. Signed-off-by: Nas Chung Reviewed-by: Michael Tretter Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- include/uapi/linux/videodev2.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index c8cb2796130f..ccd6ad53432e 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -153,10 +153,18 @@ enum v4l2_buf_type { V4L2_BUF_TYPE_SDR_OUTPUT = 12, V4L2_BUF_TYPE_META_CAPTURE = 13, V4L2_BUF_TYPE_META_OUTPUT = 14, + /* + * Note: V4L2_TYPE_IS_VALID and V4L2_TYPE_IS_OUTPUT must + * be updated if a new type is added. + */ /* Deprecated, do not use */ V4L2_BUF_TYPE_PRIVATE = 0x80, }; +#define V4L2_TYPE_IS_VALID(type) \ + ((type) >= V4L2_BUF_TYPE_VIDEO_CAPTURE &&\ + (type) <= V4L2_BUF_TYPE_META_OUTPUT) + #define V4L2_TYPE_IS_MULTIPLANAR(type) \ ((type) == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE \ || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) @@ -171,7 +179,8 @@ enum v4l2_buf_type { || (type) == V4L2_BUF_TYPE_SDR_OUTPUT \ || (type) == V4L2_BUF_TYPE_META_OUTPUT) -#define V4L2_TYPE_IS_CAPTURE(type) (!V4L2_TYPE_IS_OUTPUT(type)) +#define V4L2_TYPE_IS_CAPTURE(type) \ + (V4L2_TYPE_IS_VALID(type) && !V4L2_TYPE_IS_OUTPUT(type)) enum v4l2_tuner_type { V4L2_TUNER_RADIO = 1, -- cgit v1.2.3-59-g8ed1b From 8e172e38a623ce284baf2514f963b29e4d47c62e Mon Sep 17 00:00:00 2001 From: Nas Chung Date: Thu, 25 Jul 2024 15:10:33 +0900 Subject: media: qcom: venus: Fix uninitialized variable warning Avoid uninitialized variable when both V4L2_TYPE_IS_OUTPUT() and V4L2_TYPE_IS_CAPTURE() return false. Signed-off-by: Nas Chung Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/vdec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 9f82882b77bc..39d0556d7237 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -154,14 +154,14 @@ find_format_by_index(struct venus_inst *inst, unsigned int index, u32 type) return NULL; for (i = 0; i < size; i++) { - bool valid; + bool valid = false; if (fmt[i].type != type) continue; if (V4L2_TYPE_IS_OUTPUT(type)) { valid = venus_helper_check_codec(inst, fmt[i].pixfmt); - } else if (V4L2_TYPE_IS_CAPTURE(type)) { + } else { valid = venus_helper_check_format(inst, fmt[i].pixfmt); if (fmt[i].pixfmt == V4L2_PIX_FMT_QC10C && -- cgit v1.2.3-59-g8ed1b From f81f69a0e3da141bdd73a16b8676f4e542533d87 Mon Sep 17 00:00:00 2001 From: Nas Chung Date: Thu, 25 Jul 2024 15:10:34 +0900 Subject: media: uapi: v4l: Fix V4L2_TYPE_IS_OUTPUT condition V4L2_TYPE_IS_OUTPUT() returns true for V4L2_BUF_TYPE_VIDEO_OVERLAY which definitely belongs to CAPTURE. Signed-off-by: Nas Chung Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- include/uapi/linux/videodev2.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index ccd6ad53432e..af86ece741e9 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -172,7 +172,6 @@ enum v4l2_buf_type { #define V4L2_TYPE_IS_OUTPUT(type) \ ((type) == V4L2_BUF_TYPE_VIDEO_OUTPUT \ || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE \ - || (type) == V4L2_BUF_TYPE_VIDEO_OVERLAY \ || (type) == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY \ || (type) == V4L2_BUF_TYPE_VBI_OUTPUT \ || (type) == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT \ -- cgit v1.2.3-59-g8ed1b From b312ac33a593b64f23b0ddba59c429e89c479a54 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Tue, 1 Apr 2025 17:06:56 +0800 Subject: media: amphion: Reduce decoding latency for HEVC decoder The amphion decoder firmware supports a low latency flush mode for the HEVC format since v1.9.0. This feature, which is enabled when the display delay is set to 0, can help to reduce the decoding latency by appending some padding data to every frame. Signed-off-by: Ming Qian Reviewed-by: Nicolas Dufresne Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/amphion/vpu_malone.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c index 4769c053c6c2..e07de425ab16 100644 --- a/drivers/media/platform/amphion/vpu_malone.c +++ b/drivers/media/platform/amphion/vpu_malone.c @@ -3,6 +3,7 @@ * Copyright 2020-2021 NXP */ +#include #include #include #include @@ -68,6 +69,12 @@ #define MALONE_DEC_FMT_RV_MASK BIT(21) +#define MALONE_VERSION_MASK 0xFFFFF +#define MALONE_VERSION(maj, min, inc) \ + (FIELD_PREP(0xF0000, maj) | FIELD_PREP(0xFF00, min) | FIELD_PREP(0xFF, inc)) +#define CHECK_VERSION(iface, maj, min) \ + (FIELD_GET(MALONE_VERSION_MASK, (iface)->fw_version) >= MALONE_VERSION(maj, min, 0)) + enum vpu_malone_stream_input_mode { INVALID_MODE = 0, FRAME_LVL, @@ -332,6 +339,8 @@ struct vpu_dec_ctrl { u32 buf_addr[VID_API_NUM_STREAMS]; }; +static const struct malone_padding_scode *get_padding_scode(u32 type, u32 fmt); + u32 vpu_malone_get_data_size(void) { return sizeof(struct vpu_dec_ctrl); @@ -654,9 +663,15 @@ static int vpu_malone_set_params(struct vpu_shared_addr *shared, hc->jpg[instance].jpg_mjpeg_interlaced = 0; } - hc->codec_param[instance].disp_imm = params->display_delay_enable ? 1 : 0; - if (malone_format != MALONE_FMT_AVC) + if (params->display_delay_enable && + get_padding_scode(SCODE_PADDING_BUFFLUSH, params->codec_format)) + hc->codec_param[instance].disp_imm = 1; + else hc->codec_param[instance].disp_imm = 0; + + if (params->codec_format == V4L2_PIX_FMT_HEVC && !CHECK_VERSION(iface, 1, 9)) + hc->codec_param[instance].disp_imm = 0; + hc->codec_param[instance].dbglog_enable = 0; iface->dbglog_desc.level = 0; @@ -1023,6 +1038,7 @@ static const struct malone_padding_scode padding_scodes[] = { {SCODE_PADDING_EOS, V4L2_PIX_FMT_JPEG, {0x0, 0x0}}, {SCODE_PADDING_BUFFLUSH, V4L2_PIX_FMT_H264, {0x15010000, 0x0}}, {SCODE_PADDING_BUFFLUSH, V4L2_PIX_FMT_H264_MVC, {0x15010000, 0x0}}, + {SCODE_PADDING_BUFFLUSH, V4L2_PIX_FMT_HEVC, {0x3e010000, 0x20}}, }; static const struct malone_padding_scode padding_scode_dft = {0x0, 0x0}; @@ -1057,8 +1073,11 @@ static int vpu_malone_add_padding_scode(struct vpu_buffer *stream_buffer, int ret; ps = get_padding_scode(scode_type, pixelformat); - if (!ps) + if (!ps) { + if (scode_type == SCODE_PADDING_BUFFLUSH) + return 0; return -EINVAL; + } wptr = readl(&str_buf->wptr); if (wptr < stream_buffer->phys || wptr > stream_buffer->phys + stream_buffer->length) -- cgit v1.2.3-59-g8ed1b From 9ea16ba6eaf93f25f61855751f71e2e701709ddf Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Tue, 1 Apr 2025 17:06:57 +0800 Subject: media: amphion: Add a frame flush mode for decoder By default the amphion decoder will pre-parse 3 frames before starting to decode the first frame. Alternatively, a block of flush padding data can be appended to the frame, which will ensure that the decoder can start decoding immediately after parsing the flush padding data, thus potentially reducing decoding latency. This mode was previously only enabled, when the display delay was set to 0. Allow the user to manually toggle the use of that mode via a module parameter called low_latency, which enables the mode without changing the display order. Signed-off-by: Ming Qian Reviewed-by: Nicolas Dufresne Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/amphion/vpu_malone.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c index e07de425ab16..feca7d4220ed 100644 --- a/drivers/media/platform/amphion/vpu_malone.c +++ b/drivers/media/platform/amphion/vpu_malone.c @@ -26,6 +26,10 @@ #include "vpu_imx8q.h" #include "vpu_malone.h" +static bool low_latency; +module_param(low_latency, bool, 0644); +MODULE_PARM_DESC(low_latency, "Set low latency frame flush mode: 0 (disable) or 1 (enable)"); + #define CMD_SIZE 25600 #define MSG_SIZE 25600 #define CODEC_SIZE 0x1000 @@ -1581,7 +1585,15 @@ static int vpu_malone_input_frame_data(struct vpu_malone_str_buffer __iomem *str vpu_malone_update_wptr(str_buf, wptr); - if (disp_imm && !vpu_vb_is_codecconfig(vbuf)) { + /* + * Enable the low latency flush mode if display delay is set to 0 + * or the low latency frame flush mode if it is set to 1. + * The low latency flush mode requires some padding data to be appended to each frame, + * but there must not be any padding data between the sequence header and the frame. + * This module is currently only supported for the H264 and HEVC formats, + * for other formats, vpu_malone_add_scode() will return 0. + */ + if ((disp_imm || low_latency) && !vpu_vb_is_codecconfig(vbuf)) { ret = vpu_malone_add_scode(inst->core->iface, inst->id, &inst->stream_buffer, -- cgit v1.2.3-59-g8ed1b From 22f572ce4e7c7fa18f46a445d4270e6d5b5429f2 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Tue, 25 Feb 2025 10:40:22 +0100 Subject: media: v4l2-common: Add helpers to calculate bytesperline and sizeimage Add helper functions to calculate plane bytesperline and sizeimage, these new helpers consider bpp div, block width and height when calculating plane bytesperline and sizeimage. Signed-off-by: Jonas Karlman Reviewed-by: Nicolas Dufresne Tested-by: Nicolas Dufresne Tested-by: Christopher Obbard Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 78 +++++++++++++++++------------------ 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index e4b2de3833ee..b7e608f1145d 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -357,6 +357,34 @@ static inline unsigned int v4l2_format_block_height(const struct v4l2_format_inf return info->block_h[plane]; } +static inline unsigned int v4l2_format_plane_stride(const struct v4l2_format_info *info, int plane, + unsigned int width) +{ + 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]; +} + +static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_info *info, int plane, + unsigned int height) +{ + unsigned int vdiv = plane ? info->vdiv : 1; + unsigned int aligned_height = + ALIGN(height, v4l2_format_block_height(info, plane)); + + return DIV_ROUND_UP(aligned_height, vdiv); +} + +static inline unsigned int v4l2_format_plane_size(const struct v4l2_format_info *info, int plane, + unsigned int width, unsigned int height) +{ + return v4l2_format_plane_stride(info, plane, width) * + v4l2_format_plane_height(info, plane, height); +} + void v4l2_apply_frmsize_constraints(u32 *width, u32 *height, const struct v4l2_frmsize_stepwise *frmsize) { @@ -392,37 +420,19 @@ int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, if (info->mem_planes == 1) { plane = &pixfmt->plane_fmt[0]; - plane->bytesperline = ALIGN(width, v4l2_format_block_width(info, 0)) * info->bpp[0] / info->bpp_div[0]; + plane->bytesperline = v4l2_format_plane_stride(info, 0, width); plane->sizeimage = 0; - for (i = 0; i < info->comp_planes; i++) { - unsigned int hdiv = (i == 0) ? 1 : info->hdiv; - unsigned int vdiv = (i == 0) ? 1 : info->vdiv; - unsigned int aligned_width; - unsigned int aligned_height; - - aligned_width = ALIGN(width, v4l2_format_block_width(info, i)); - aligned_height = ALIGN(height, v4l2_format_block_height(info, i)); - - plane->sizeimage += info->bpp[i] * - DIV_ROUND_UP(aligned_width, hdiv) * - DIV_ROUND_UP(aligned_height, vdiv) / info->bpp_div[i]; - } + for (i = 0; i < info->comp_planes; i++) + plane->sizeimage += + v4l2_format_plane_size(info, i, width, height); } else { for (i = 0; i < info->comp_planes; i++) { - unsigned int hdiv = (i == 0) ? 1 : info->hdiv; - unsigned int vdiv = (i == 0) ? 1 : info->vdiv; - unsigned int aligned_width; - unsigned int aligned_height; - - aligned_width = ALIGN(width, v4l2_format_block_width(info, i)); - aligned_height = ALIGN(height, v4l2_format_block_height(info, i)); - plane = &pixfmt->plane_fmt[i]; plane->bytesperline = - info->bpp[i] * DIV_ROUND_UP(aligned_width, hdiv) / info->bpp_div[i]; - plane->sizeimage = - plane->bytesperline * DIV_ROUND_UP(aligned_height, vdiv); + v4l2_format_plane_stride(info, i, width); + plane->sizeimage = plane->bytesperline * + v4l2_format_plane_height(info, i, height); } } return 0; @@ -446,22 +456,12 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, pixfmt->width = width; pixfmt->height = height; pixfmt->pixelformat = pixelformat; - pixfmt->bytesperline = ALIGN(width, v4l2_format_block_width(info, 0)) * info->bpp[0] / info->bpp_div[0]; + pixfmt->bytesperline = v4l2_format_plane_stride(info, 0, width); pixfmt->sizeimage = 0; - for (i = 0; i < info->comp_planes; i++) { - unsigned int hdiv = (i == 0) ? 1 : info->hdiv; - unsigned int vdiv = (i == 0) ? 1 : info->vdiv; - unsigned int aligned_width; - unsigned int aligned_height; - - aligned_width = ALIGN(width, v4l2_format_block_width(info, i)); - aligned_height = ALIGN(height, v4l2_format_block_height(info, i)); - - pixfmt->sizeimage += info->bpp[i] * - DIV_ROUND_UP(aligned_width, hdiv) * - DIV_ROUND_UP(aligned_height, vdiv) / info->bpp_div[i]; - } + for (i = 0; i < info->comp_planes; i++) + pixfmt->sizeimage += + v4l2_format_plane_size(info, i, width, height); return 0; } EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt); -- cgit v1.2.3-59-g8ed1b From dcbe2aeda2e09eb69f5feba7e171db2836d9999d Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Tue, 25 Feb 2025 10:40:23 +0100 Subject: media: v4l2: Add NV15 and NV20 pixel formats Add NV15 and NV20 pixel formats used by the Rockchip Video Decoder for 10-bit buffers. NV15 and NV20 is 10-bit 4:2:0/4:2:2 semi-planar YUV formats similar to NV12 and NV16, using 10-bit components with no padding between each component. Instead, a group of 4 luminance/chrominance samples are stored over 5 bytes in little endian order: YYYY = UVUV = 4 * 10 bits = 40 bits = 5 bytes The '15' and '20' suffix refers to the optimum effective bits per pixel which is achieved when the total number of luminance samples is a multiple of 8 for NV15 and 4 for NV20. Signed-off-by: Jonas Karlman Reviewed-by: Nicolas Dufresne Tested-by: Nicolas Dufresne Tested-by: Christopher Obbard Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../userspace-api/media/v4l/pixfmt-yuv-planar.rst | 128 +++++++++++++++++++++ drivers/media/v4l2-core/v4l2-common.c | 2 + drivers/media/v4l2-core/v4l2-ioctl.c | 2 + include/uapi/linux/videodev2.h | 2 + 4 files changed, 134 insertions(+) diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst index b788f6933855..6e4f399f1f88 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst @@ -137,6 +137,13 @@ All components are stored with the same number of bits per component. - Cb, Cr - No - Linear + * - V4L2_PIX_FMT_NV15 + - 'NV15' + - 10 + - 4:2:0 + - Cb, Cr + - Yes + - Linear * - V4L2_PIX_FMT_NV15_4L4 - 'VT15' - 15 @@ -186,6 +193,13 @@ All components are stored with the same number of bits per component. - Cr, Cb - No - Linear + * - V4L2_PIX_FMT_NV20 + - 'NV20' + - 10 + - 4:2:2 + - Cb, Cr + - Yes + - Linear * - V4L2_PIX_FMT_NV24 - 'NV24' - 8 @@ -302,6 +316,57 @@ of the luma plane. - Cr\ :sub:`11` +.. _V4L2-PIX-FMT-NV15: + +NV15 +---- + +Semi-planar 10-bit YUV 4:2:0 format similar to NV12, using 10-bit components +with no padding between each component. A group of 4 components are stored over +5 bytes in little endian order. + +.. flat-table:: Sample 4x4 NV15 Image (1 byte per cell) + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - Y'\ :sub:`00[7:0]` + - Y'\ :sub:`01[5:0]`\ Y'\ :sub:`00[9:8]` + - Y'\ :sub:`02[3:0]`\ Y'\ :sub:`01[9:6]` + - Y'\ :sub:`03[1:0]`\ Y'\ :sub:`02[9:4]` + - Y'\ :sub:`03[9:2]` + * - start + 5: + - Y'\ :sub:`10[7:0]` + - Y'\ :sub:`11[5:0]`\ Y'\ :sub:`10[9:8]` + - Y'\ :sub:`12[3:0]`\ Y'\ :sub:`11[9:6]` + - Y'\ :sub:`13[1:0]`\ Y'\ :sub:`12[9:4]` + - Y'\ :sub:`13[9:2]` + * - start + 10: + - Y'\ :sub:`20[7:0]` + - Y'\ :sub:`21[5:0]`\ Y'\ :sub:`20[9:8]` + - Y'\ :sub:`22[3:0]`\ Y'\ :sub:`21[9:6]` + - Y'\ :sub:`23[1:0]`\ Y'\ :sub:`22[9:4]` + - Y'\ :sub:`23[9:2]` + * - start + 15: + - Y'\ :sub:`30[7:0]` + - Y'\ :sub:`31[5:0]`\ Y'\ :sub:`30[9:8]` + - Y'\ :sub:`32[3:0]`\ Y'\ :sub:`31[9:6]` + - Y'\ :sub:`33[1:0]`\ Y'\ :sub:`32[9:4]` + - Y'\ :sub:`33[9:2]` + * - start + 20: + - Cb\ :sub:`00[7:0]` + - Cr\ :sub:`00[5:0]`\ Cb\ :sub:`00[9:8]` + - Cb\ :sub:`01[3:0]`\ Cr\ :sub:`00[9:6]` + - Cr\ :sub:`01[1:0]`\ Cb\ :sub:`01[9:4]` + - Cr\ :sub:`01[9:2]` + * - start + 25: + - Cb\ :sub:`10[7:0]` + - Cr\ :sub:`10[5:0]`\ Cb\ :sub:`10[9:8]` + - Cb\ :sub:`11[3:0]`\ Cr\ :sub:`10[9:6]` + - Cr\ :sub:`11[1:0]`\ Cb\ :sub:`11[9:4]` + - Cr\ :sub:`11[9:2]` + + .. _V4L2-PIX-FMT-NV12MT: .. _V4L2-PIX-FMT-NV12MT-16X16: .. _V4L2-PIX-FMT-NV12-4L4: @@ -631,6 +696,69 @@ number of lines as the luma plane. - Cr\ :sub:`32` +.. _V4L2-PIX-FMT-NV20: + +NV20 +---- + +Semi-planar 10-bit YUV 4:2:2 format similar to NV16, using 10-bit components +with no padding between each component. A group of 4 components are stored over +5 bytes in little endian order. + +.. flat-table:: Sample 4x4 NV20 Image (1 byte per cell) + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - Y'\ :sub:`00[7:0]` + - Y'\ :sub:`01[5:0]`\ Y'\ :sub:`00[9:8]` + - Y'\ :sub:`02[3:0]`\ Y'\ :sub:`01[9:6]` + - Y'\ :sub:`03[1:0]`\ Y'\ :sub:`02[9:4]` + - Y'\ :sub:`03[9:2]` + * - start + 5: + - Y'\ :sub:`10[7:0]` + - Y'\ :sub:`11[5:0]`\ Y'\ :sub:`10[9:8]` + - Y'\ :sub:`12[3:0]`\ Y'\ :sub:`11[9:6]` + - Y'\ :sub:`13[1:0]`\ Y'\ :sub:`12[9:4]` + - Y'\ :sub:`13[9:2]` + * - start + 10: + - Y'\ :sub:`20[7:0]` + - Y'\ :sub:`21[5:0]`\ Y'\ :sub:`20[9:8]` + - Y'\ :sub:`22[3:0]`\ Y'\ :sub:`21[9:6]` + - Y'\ :sub:`23[1:0]`\ Y'\ :sub:`22[9:4]` + - Y'\ :sub:`23[9:2]` + * - start + 15: + - Y'\ :sub:`30[7:0]` + - Y'\ :sub:`31[5:0]`\ Y'\ :sub:`30[9:8]` + - Y'\ :sub:`32[3:0]`\ Y'\ :sub:`31[9:6]` + - Y'\ :sub:`33[1:0]`\ Y'\ :sub:`32[9:4]` + - Y'\ :sub:`33[9:2]` + * - start + 20: + - Cb\ :sub:`00[7:0]` + - Cr\ :sub:`00[5:0]`\ Cb\ :sub:`00[9:8]` + - Cb\ :sub:`01[3:0]`\ Cr\ :sub:`00[9:6]` + - Cr\ :sub:`01[1:0]`\ Cb\ :sub:`01[9:4]` + - Cr\ :sub:`01[9:2]` + * - start + 25: + - Cb\ :sub:`10[7:0]` + - Cr\ :sub:`10[5:0]`\ Cb\ :sub:`10[9:8]` + - Cb\ :sub:`11[3:0]`\ Cr\ :sub:`10[9:6]` + - Cr\ :sub:`11[1:0]`\ Cb\ :sub:`11[9:4]` + - Cr\ :sub:`11[9:2]` + * - start + 30: + - Cb\ :sub:`20[7:0]` + - Cr\ :sub:`20[5:0]`\ Cb\ :sub:`20[9:8]` + - Cb\ :sub:`21[3:0]`\ Cr\ :sub:`20[9:6]` + - Cr\ :sub:`21[1:0]`\ Cb\ :sub:`21[9:4]` + - Cr\ :sub:`21[9:2]` + * - start + 35: + - Cb\ :sub:`30[7:0]` + - Cr\ :sub:`30[5:0]`\ Cb\ :sub:`30[9:8]` + - Cb\ :sub:`31[3:0]`\ Cr\ :sub:`30[9:6]` + - Cr\ :sub:`31[1:0]`\ Cb\ :sub:`31[9:4]` + - Cr\ :sub:`31[9:2]` + + .. _V4L2-PIX-FMT-NV24: .. _V4L2-PIX-FMT-NV42: diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index b7e608f1145d..aa86b8c6aa75 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -277,8 +277,10 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) /* YUV planar formats */ { .format = V4L2_PIX_FMT_NV12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, { .format = V4L2_PIX_FMT_NV21, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 }, + { .format = V4L2_PIX_FMT_NV15, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2 }, { .format = V4L2_PIX_FMT_NV16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_NV61, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_NV20, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 1 }, { .format = V4L2_PIX_FMT_NV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_NV42, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_P010, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 }, diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index a16fb44c7246..e97881f74c0d 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1363,8 +1363,10 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_YUV48_12: descr = "12-bit YUV 4:4:4 Packed"; break; case V4L2_PIX_FMT_NV12: descr = "Y/UV 4:2:0"; break; case V4L2_PIX_FMT_NV21: descr = "Y/VU 4:2:0"; break; + case V4L2_PIX_FMT_NV15: descr = "10-bit Y/UV 4:2:0 (Packed)"; break; case V4L2_PIX_FMT_NV16: descr = "Y/UV 4:2:2"; break; case V4L2_PIX_FMT_NV61: descr = "Y/VU 4:2:2"; break; + case V4L2_PIX_FMT_NV20: descr = "10-bit Y/UV 4:2:2 (Packed)"; break; case V4L2_PIX_FMT_NV24: descr = "Y/UV 4:4:4"; break; case V4L2_PIX_FMT_NV42: descr = "Y/VU 4:4:4"; break; case V4L2_PIX_FMT_P010: descr = "10-bit Y/UV 4:2:0"; break; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index af86ece741e9..ca7b3e8863ca 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -651,8 +651,10 @@ struct v4l2_pix_format { /* two planes -- one Y, one Cr + Cb interleaved */ #define V4L2_PIX_FMT_NV12 v4l2_fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */ #define V4L2_PIX_FMT_NV21 v4l2_fourcc('N', 'V', '2', '1') /* 12 Y/CrCb 4:2:0 */ +#define V4L2_PIX_FMT_NV15 v4l2_fourcc('N', 'V', '1', '5') /* 15 Y/CbCr 4:2:0 10-bit packed */ #define V4L2_PIX_FMT_NV16 v4l2_fourcc('N', 'V', '1', '6') /* 16 Y/CbCr 4:2:2 */ #define V4L2_PIX_FMT_NV61 v4l2_fourcc('N', 'V', '6', '1') /* 16 Y/CrCb 4:2:2 */ +#define V4L2_PIX_FMT_NV20 v4l2_fourcc('N', 'V', '2', '0') /* 20 Y/CbCr 4:2:2 10-bit packed */ #define V4L2_PIX_FMT_NV24 v4l2_fourcc('N', 'V', '2', '4') /* 24 Y/CbCr 4:4:4 */ #define V4L2_PIX_FMT_NV42 v4l2_fourcc('N', 'V', '4', '2') /* 24 Y/CrCb 4:4:4 */ #define V4L2_PIX_FMT_P010 v4l2_fourcc('P', '0', '1', '0') /* 24 Y/CbCr 4:2:0 10-bit per component */ -- cgit v1.2.3-59-g8ed1b From d5e0aa61470c48ddc04d433a00e79cef8716377a Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Tue, 25 Feb 2025 10:40:24 +0100 Subject: media: rkvdec: h264: Use bytesperline and buffer height as virstride Use bytesperline and buffer height to calculate the strides configured. This does not really change anything other than ensuring the bytesperline that is signaled to userspace matches what is configured in HW. Signed-off-by: Jonas Karlman Reviewed-by: Nicolas Dufresne Tested-by: Nicolas Dufresne Tested-by: Christopher Obbard Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/rkvdec/rkvdec-h264.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c index 4fc167b42cf0..7a1e76d423df 100644 --- a/drivers/staging/media/rkvdec/rkvdec-h264.c +++ b/drivers/staging/media/rkvdec/rkvdec-h264.c @@ -896,9 +896,9 @@ static void config_registers(struct rkvdec_ctx *ctx, dma_addr_t rlc_addr; dma_addr_t refer_addr; u32 rlc_len; - u32 hor_virstride = 0; - u32 ver_virstride = 0; - u32 y_virstride = 0; + u32 hor_virstride; + u32 ver_virstride; + u32 y_virstride; u32 yuv_virstride = 0; u32 offset; dma_addr_t dst_addr; @@ -909,16 +909,16 @@ static void config_registers(struct rkvdec_ctx *ctx, f = &ctx->decoded_fmt; dst_fmt = &f->fmt.pix_mp; - hor_virstride = (sps->bit_depth_luma_minus8 + 8) * dst_fmt->width / 8; - ver_virstride = round_up(dst_fmt->height, 16); + hor_virstride = dst_fmt->plane_fmt[0].bytesperline; + ver_virstride = dst_fmt->height; y_virstride = hor_virstride * ver_virstride; if (sps->chroma_format_idc == 0) yuv_virstride = y_virstride; else if (sps->chroma_format_idc == 1) - yuv_virstride += y_virstride + y_virstride / 2; + yuv_virstride = y_virstride + y_virstride / 2; else if (sps->chroma_format_idc == 2) - yuv_virstride += 2 * y_virstride; + yuv_virstride = 2 * y_virstride; reg = RKVDEC_Y_HOR_VIRSTRIDE(hor_virstride / 16) | RKVDEC_UV_HOR_VIRSTRIDE(hor_virstride / 16) | -- cgit v1.2.3-59-g8ed1b From 137149c63993c235fa37624cac6368f3cb4affc9 Mon Sep 17 00:00:00 2001 From: Alex Bee Date: Tue, 25 Feb 2025 10:40:25 +0100 Subject: media: rkvdec: h264: Don't hardcode SPS/PPS parameters Some SPS/PPS parameters are currently hardcoded in the driver even though they exist in the stable uapi controls. Use values from SPS/PPS controls instead of hardcoding them. Signed-off-by: Alex Bee [jonas@kwiboo.se: constraint_set_flags condition, commit message] Signed-off-by: Jonas Karlman Reviewed-by: Nicolas Dufresne Tested-by: Nicolas Dufresne Tested-by: Christopher Obbard Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/rkvdec/rkvdec-h264.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c index 7a1e76d423df..8bce8902b8dd 100644 --- a/drivers/staging/media/rkvdec/rkvdec-h264.c +++ b/drivers/staging/media/rkvdec/rkvdec-h264.c @@ -655,13 +655,14 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, #define WRITE_PPS(value, field) set_ps_field(hw_ps->info, field, value) /* write sps */ - WRITE_PPS(0xf, SEQ_PARAMETER_SET_ID); - WRITE_PPS(0xff, PROFILE_IDC); - WRITE_PPS(1, CONSTRAINT_SET3_FLAG); + WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID); + WRITE_PPS(sps->profile_idc, PROFILE_IDC); + WRITE_PPS(!!(sps->constraint_set_flags & (1 << 3)), CONSTRAINT_SET3_FLAG); WRITE_PPS(sps->chroma_format_idc, CHROMA_FORMAT_IDC); WRITE_PPS(sps->bit_depth_luma_minus8, BIT_DEPTH_LUMA); WRITE_PPS(sps->bit_depth_chroma_minus8, BIT_DEPTH_CHROMA); - WRITE_PPS(0, QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG); + WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS), + QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG); WRITE_PPS(sps->log2_max_frame_num_minus4, LOG2_MAX_FRAME_NUM_MINUS4); WRITE_PPS(sps->max_num_ref_frames, MAX_NUM_REF_FRAMES); WRITE_PPS(sps->pic_order_cnt_type, PIC_ORDER_CNT_TYPE); @@ -688,8 +689,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, DIRECT_8X8_INFERENCE_FLAG); /* write pps */ - WRITE_PPS(0xff, PIC_PARAMETER_SET_ID); - WRITE_PPS(0x1f, PPS_SEQ_PARAMETER_SET_ID); + WRITE_PPS(pps->pic_parameter_set_id, PIC_PARAMETER_SET_ID); + WRITE_PPS(pps->seq_parameter_set_id, PPS_SEQ_PARAMETER_SET_ID); WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE), ENTROPY_CODING_MODE_FLAG); WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT), -- cgit v1.2.3-59-g8ed1b From c74b78193ffd0a5cfdc7e0c3fa1d128e6c0c42d6 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Tue, 25 Feb 2025 10:40:26 +0100 Subject: media: rkvdec: Extract rkvdec_fill_decoded_pixfmt into helper Extract call to v4l2_fill_pixfmt_mp() and ajusting of sizeimage into a helper. Replace current code with a call to the new helper. Signed-off-by: Jonas Karlman Reviewed-by: Nicolas Dufresne Tested-by: Nicolas Dufresne Tested-by: Christopher Obbard Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/rkvdec/rkvdec.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index f9bef5173bf2..e354360f4acc 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -27,6 +27,16 @@ #include "rkvdec.h" #include "rkvdec-regs.h" +static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx, + struct v4l2_pix_format_mplane *pix_mp) +{ + v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, + pix_mp->width, pix_mp->height); + pix_mp->plane_fmt[0].sizeimage += 128 * + DIV_ROUND_UP(pix_mp->width, 16) * + DIV_ROUND_UP(pix_mp->height, 16); +} + static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) { struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl); @@ -192,13 +202,9 @@ static void rkvdec_reset_decoded_fmt(struct rkvdec_ctx *ctx) rkvdec_reset_fmt(ctx, f, ctx->coded_fmt_desc->decoded_fmts[0]); f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - v4l2_fill_pixfmt_mp(&f->fmt.pix_mp, - ctx->coded_fmt_desc->decoded_fmts[0], - ctx->coded_fmt.fmt.pix_mp.width, - ctx->coded_fmt.fmt.pix_mp.height); - f->fmt.pix_mp.plane_fmt[0].sizeimage += 128 * - DIV_ROUND_UP(f->fmt.pix_mp.width, 16) * - DIV_ROUND_UP(f->fmt.pix_mp.height, 16); + f->fmt.pix_mp.width = ctx->coded_fmt.fmt.pix_mp.width; + f->fmt.pix_mp.height = ctx->coded_fmt.fmt.pix_mp.height; + rkvdec_fill_decoded_pixfmt(ctx, &f->fmt.pix_mp); } static int rkvdec_enum_framesizes(struct file *file, void *priv, @@ -264,12 +270,7 @@ static int rkvdec_try_capture_fmt(struct file *file, void *priv, &pix_mp->height, &coded_desc->frmsize); - v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, - pix_mp->width, pix_mp->height); - pix_mp->plane_fmt[0].sizeimage += - 128 * - DIV_ROUND_UP(pix_mp->width, 16) * - DIV_ROUND_UP(pix_mp->height, 16); + rkvdec_fill_decoded_pixfmt(ctx, pix_mp); pix_mp->field = V4L2_FIELD_NONE; return 0; -- cgit v1.2.3-59-g8ed1b From 98a0aa1b6910766a23b5162a1499696837e5d3ca Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Tue, 25 Feb 2025 10:40:27 +0100 Subject: media: rkvdec: Move rkvdec_reset_decoded_fmt helper Move rkvdec_reset_decoded_fmt() and the called rkvdec_reset_fmt() helper functions in preparation for adding a new caller in an upcoming patch. Signed-off-by: Jonas Karlman Reviewed-by: Nicolas Dufresne Tested-by: Nicolas Dufresne Tested-by: Christopher Obbard Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/rkvdec/rkvdec.c | 46 +++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index e354360f4acc..1f8f98cf91dc 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -37,6 +37,29 @@ static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx, DIV_ROUND_UP(pix_mp->height, 16); } +static void rkvdec_reset_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f, + u32 fourcc) +{ + memset(f, 0, sizeof(*f)); + f->fmt.pix_mp.pixelformat = fourcc; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; + f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; + f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static void rkvdec_reset_decoded_fmt(struct rkvdec_ctx *ctx) +{ + struct v4l2_format *f = &ctx->decoded_fmt; + + rkvdec_reset_fmt(ctx, f, ctx->coded_fmt_desc->decoded_fmts[0]); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + f->fmt.pix_mp.width = ctx->coded_fmt.fmt.pix_mp.width; + f->fmt.pix_mp.height = ctx->coded_fmt.fmt.pix_mp.height; + rkvdec_fill_decoded_pixfmt(ctx, &f->fmt.pix_mp); +} + static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) { struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl); @@ -169,18 +192,6 @@ rkvdec_find_coded_fmt_desc(u32 fourcc) return NULL; } -static void rkvdec_reset_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f, - u32 fourcc) -{ - memset(f, 0, sizeof(*f)); - f->fmt.pix_mp.pixelformat = fourcc; - f->fmt.pix_mp.field = V4L2_FIELD_NONE; - f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; - f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; - f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; -} - static void rkvdec_reset_coded_fmt(struct rkvdec_ctx *ctx) { struct v4l2_format *f = &ctx->coded_fmt; @@ -196,17 +207,6 @@ static void rkvdec_reset_coded_fmt(struct rkvdec_ctx *ctx) ctx->coded_fmt_desc->ops->adjust_fmt(ctx, f); } -static void rkvdec_reset_decoded_fmt(struct rkvdec_ctx *ctx) -{ - struct v4l2_format *f = &ctx->decoded_fmt; - - rkvdec_reset_fmt(ctx, f, ctx->coded_fmt_desc->decoded_fmts[0]); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - f->fmt.pix_mp.width = ctx->coded_fmt.fmt.pix_mp.width; - f->fmt.pix_mp.height = ctx->coded_fmt.fmt.pix_mp.height; - rkvdec_fill_decoded_pixfmt(ctx, &f->fmt.pix_mp); -} - static int rkvdec_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) { -- cgit v1.2.3-59-g8ed1b From e3f9e3a6a032f37d43c31f36330d04b609cb7fbd Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Tue, 25 Feb 2025 10:40:28 +0100 Subject: media: rkvdec: Extract decoded format enumeration into helper Add a rkvdec_is_valid_fmt() helper that check if a fourcc is a supported CAPTURE format, and a rkvdec_enum_decoded_fmt() helper that enumerates valid formats. This moves current code into helper functions in preparation for adding CAPTURE format filtering and validation in next patch. Signed-off-by: Jonas Karlman Reviewed-by: Nicolas Dufresne Tested-by: Nicolas Dufresne Tested-by: Christopher Obbard Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/rkvdec/rkvdec.c | 49 +++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index 1f8f98cf91dc..52e64b399dcc 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -27,6 +27,32 @@ #include "rkvdec.h" #include "rkvdec-regs.h" +static u32 rkvdec_enum_decoded_fmt(struct rkvdec_ctx *ctx, int index) +{ + const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; + + if (WARN_ON(!desc)) + return 0; + + if (index >= desc->num_decoded_fmts) + return 0; + + return desc->decoded_fmts[index]; +} + +static bool rkvdec_is_valid_fmt(struct rkvdec_ctx *ctx, u32 fourcc) +{ + const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; + unsigned int i; + + for (i = 0; i < desc->num_decoded_fmts; i++) { + if (desc->decoded_fmts[i] == fourcc) + return true; + } + + return false; +} + static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx, struct v4l2_pix_format_mplane *pix_mp) { @@ -52,8 +78,10 @@ static void rkvdec_reset_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f, static void rkvdec_reset_decoded_fmt(struct rkvdec_ctx *ctx) { struct v4l2_format *f = &ctx->decoded_fmt; + u32 fourcc; - rkvdec_reset_fmt(ctx, f, ctx->coded_fmt_desc->decoded_fmts[0]); + fourcc = rkvdec_enum_decoded_fmt(ctx, 0); + rkvdec_reset_fmt(ctx, f, fourcc); f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; f->fmt.pix_mp.width = ctx->coded_fmt.fmt.pix_mp.width; f->fmt.pix_mp.height = ctx->coded_fmt.fmt.pix_mp.height; @@ -244,7 +272,6 @@ static int rkvdec_try_capture_fmt(struct file *file, void *priv, struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); const struct rkvdec_coded_fmt_desc *coded_desc; - unsigned int i; /* * The codec context should point to a coded format desc, if the format @@ -255,13 +282,8 @@ static int rkvdec_try_capture_fmt(struct file *file, void *priv, if (WARN_ON(!coded_desc)) return -EINVAL; - for (i = 0; i < coded_desc->num_decoded_fmts; i++) { - if (coded_desc->decoded_fmts[i] == pix_mp->pixelformat) - break; - } - - if (i == coded_desc->num_decoded_fmts) - pix_mp->pixelformat = coded_desc->decoded_fmts[0]; + if (!rkvdec_is_valid_fmt(ctx, pix_mp->pixelformat)) + pix_mp->pixelformat = rkvdec_enum_decoded_fmt(ctx, 0); /* Always apply the frmsize constraint of the coded end. */ pix_mp->width = max(pix_mp->width, ctx->coded_fmt.fmt.pix_mp.width); @@ -425,14 +447,13 @@ static int rkvdec_enum_capture_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); + u32 fourcc; - if (WARN_ON(!ctx->coded_fmt_desc)) - return -EINVAL; - - if (f->index >= ctx->coded_fmt_desc->num_decoded_fmts) + fourcc = rkvdec_enum_decoded_fmt(ctx, f->index); + if (!fourcc) return -EINVAL; - f->pixelformat = ctx->coded_fmt_desc->decoded_fmts[f->index]; + f->pixelformat = fourcc; return 0; } -- cgit v1.2.3-59-g8ed1b From 774837ed8749fb58a5a4079d0750e151b1ed01a6 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Tue, 25 Feb 2025 10:40:29 +0100 Subject: media: rkvdec: Add image format concept Add an enum rkvdec_image_fmt used to signal an image format, e.g. 4:2:0 8-bit, 4:2:0 10-bit or any. Tag each supported CAPUTRE format with an image format and use this tag to filter out unsupported CAPTURE formats. Signed-off-by: Jonas Karlman Reviewed-by: Nicolas Dufresne Tested-by: Nicolas Dufresne Tested-by: Christopher Obbard Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/rkvdec/rkvdec.c | 48 ++++++++++++++++++++++++++--------- drivers/staging/media/rkvdec/rkvdec.h | 13 +++++++++- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index 52e64b399dcc..70154948b4e3 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -27,26 +27,45 @@ #include "rkvdec.h" #include "rkvdec-regs.h" -static u32 rkvdec_enum_decoded_fmt(struct rkvdec_ctx *ctx, int index) +static bool rkvdec_image_fmt_match(enum rkvdec_image_fmt fmt1, + enum rkvdec_image_fmt fmt2) +{ + return fmt1 == fmt2 || fmt2 == RKVDEC_IMG_FMT_ANY || + fmt1 == RKVDEC_IMG_FMT_ANY; +} + +static u32 rkvdec_enum_decoded_fmt(struct rkvdec_ctx *ctx, int index, + enum rkvdec_image_fmt image_fmt) { const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; + int fmt_idx = -1; + unsigned int i; if (WARN_ON(!desc)) return 0; - if (index >= desc->num_decoded_fmts) - return 0; + for (i = 0; i < desc->num_decoded_fmts; i++) { + if (!rkvdec_image_fmt_match(desc->decoded_fmts[i].image_fmt, + image_fmt)) + continue; + fmt_idx++; + if (index == fmt_idx) + return desc->decoded_fmts[i].fourcc; + } - return desc->decoded_fmts[index]; + return 0; } -static bool rkvdec_is_valid_fmt(struct rkvdec_ctx *ctx, u32 fourcc) +static bool rkvdec_is_valid_fmt(struct rkvdec_ctx *ctx, u32 fourcc, + enum rkvdec_image_fmt image_fmt) { const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; unsigned int i; for (i = 0; i < desc->num_decoded_fmts; i++) { - if (desc->decoded_fmts[i] == fourcc) + if (rkvdec_image_fmt_match(desc->decoded_fmts[i].image_fmt, + image_fmt) && + desc->decoded_fmts[i].fourcc == fourcc) return true; } @@ -80,7 +99,7 @@ static void rkvdec_reset_decoded_fmt(struct rkvdec_ctx *ctx) struct v4l2_format *f = &ctx->decoded_fmt; u32 fourcc; - fourcc = rkvdec_enum_decoded_fmt(ctx, 0); + fourcc = rkvdec_enum_decoded_fmt(ctx, 0, ctx->image_fmt); rkvdec_reset_fmt(ctx, f, fourcc); f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; f->fmt.pix_mp.width = ctx->coded_fmt.fmt.pix_mp.width; @@ -149,8 +168,11 @@ static const struct rkvdec_ctrls rkvdec_h264_ctrls = { .num_ctrls = ARRAY_SIZE(rkvdec_h264_ctrl_descs), }; -static const u32 rkvdec_h264_vp9_decoded_fmts[] = { - V4L2_PIX_FMT_NV12, +static const struct rkvdec_decoded_fmt_desc rkvdec_h264_vp9_decoded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .image_fmt = RKVDEC_IMG_FMT_420_8BIT, + }, }; static const struct rkvdec_ctrl_desc rkvdec_vp9_ctrl_descs[] = { @@ -282,8 +304,9 @@ static int rkvdec_try_capture_fmt(struct file *file, void *priv, if (WARN_ON(!coded_desc)) return -EINVAL; - if (!rkvdec_is_valid_fmt(ctx, pix_mp->pixelformat)) - pix_mp->pixelformat = rkvdec_enum_decoded_fmt(ctx, 0); + if (!rkvdec_is_valid_fmt(ctx, pix_mp->pixelformat, ctx->image_fmt)) + pix_mp->pixelformat = rkvdec_enum_decoded_fmt(ctx, 0, + ctx->image_fmt); /* Always apply the frmsize constraint of the coded end. */ pix_mp->width = max(pix_mp->width, ctx->coded_fmt.fmt.pix_mp.width); @@ -400,6 +423,7 @@ static int rkvdec_s_output_fmt(struct file *file, void *priv, * * Note that this will propagates any size changes to the decoded format. */ + ctx->image_fmt = RKVDEC_IMG_FMT_ANY; rkvdec_reset_decoded_fmt(ctx); /* Propagate colorspace information to capture. */ @@ -449,7 +473,7 @@ static int rkvdec_enum_capture_fmt(struct file *file, void *priv, struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); u32 fourcc; - fourcc = rkvdec_enum_decoded_fmt(ctx, f->index); + fourcc = rkvdec_enum_decoded_fmt(ctx, f->index, ctx->image_fmt); if (!fourcc) return -EINVAL; diff --git a/drivers/staging/media/rkvdec/rkvdec.h b/drivers/staging/media/rkvdec/rkvdec.h index 633335ebb9c4..6f8cf50c5d99 100644 --- a/drivers/staging/media/rkvdec/rkvdec.h +++ b/drivers/staging/media/rkvdec/rkvdec.h @@ -75,13 +75,23 @@ struct rkvdec_coded_fmt_ops { int (*try_ctrl)(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl); }; +enum rkvdec_image_fmt { + RKVDEC_IMG_FMT_ANY = 0, + RKVDEC_IMG_FMT_420_8BIT, +}; + +struct rkvdec_decoded_fmt_desc { + u32 fourcc; + enum rkvdec_image_fmt image_fmt; +}; + struct rkvdec_coded_fmt_desc { u32 fourcc; struct v4l2_frmsize_stepwise frmsize; const struct rkvdec_ctrls *ctrls; const struct rkvdec_coded_fmt_ops *ops; unsigned int num_decoded_fmts; - const u32 *decoded_fmts; + const struct rkvdec_decoded_fmt_desc *decoded_fmts; u32 subsystem_flags; }; @@ -104,6 +114,7 @@ struct rkvdec_ctx { const struct rkvdec_coded_fmt_desc *coded_fmt_desc; struct v4l2_ctrl_handler ctrl_hdl; struct rkvdec_dev *dev; + enum rkvdec_image_fmt image_fmt; void *priv; }; -- cgit v1.2.3-59-g8ed1b From f270005b99fa19fee9a6b4006e8dee37c10f1944 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Tue, 25 Feb 2025 10:40:33 +0100 Subject: media: rkvdec: Fix frame size enumeration The VIDIOC_ENUM_FRAMESIZES ioctl should return all frame sizes (i.e. width and height in pixels) that the device supports for the given pixel format. It doesn't make a lot of sense to return the frame-sizes in a stepwise manner, which is used to enforce hardware alignments requirements for CAPTURE buffers, for coded formats. Instead, applications should receive an indication, about the maximum supported frame size for that hardware decoder, via a continuous frame-size enumeration. Fixes: cd33c830448b ("media: rkvdec: Add the rkvdec driver") Suggested-by: Alex Bee Signed-off-by: Jonas Karlman Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/rkvdec/rkvdec.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index 70154948b4e3..dd7e57a90264 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -269,8 +269,14 @@ static int rkvdec_enum_framesizes(struct file *file, void *priv, if (!fmt) return -EINVAL; - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise = fmt->frmsize; + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = 1; + fsize->stepwise.max_width = fmt->frmsize.max_width; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = 1; + fsize->stepwise.max_height = fmt->frmsize.max_height; + fsize->stepwise.step_height = 1; + return 0; } -- cgit v1.2.3-59-g8ed1b From ebdcec10b652942651f0ef3cd7602279e85383c9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 28 Feb 2025 08:26:29 +0000 Subject: media: amphion: Fix spelling mistake "dismatch" -> "mismatch" There is a spelling mistake in a dev_err message. Fix it. Signed-off-by: Colin Ian King Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/amphion/vdec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c index 6a38a0fa0e2d..85d518823159 100644 --- a/drivers/media/platform/amphion/vdec.c +++ b/drivers/media/platform/amphion/vdec.c @@ -805,7 +805,7 @@ static void vdec_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame) cur_fmt = vpu_get_format(inst, inst->cap_format.type); vbuf = &vpu_buf->m2m_buf.vb; if (vbuf->vb2_buf.index != frame->id) - dev_err(inst->dev, "[%d] buffer id(%d, %d) dismatch\n", + dev_err(inst->dev, "[%d] buffer id(%d, %d) mismatch\n", inst->id, vbuf->vb2_buf.index, frame->id); if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_READY && vdec->params.display_delay_enable) -- cgit v1.2.3-59-g8ed1b From 9ddc3d6c16ea2587898a315f20f7b8fbd791dc1b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 2 Sep 2024 15:07:58 +0100 Subject: media: mediatek: vcodec: Remove trailing space after \n newline There is a extraneous space after a newline in a mtk_venc_debug message. Remove it. Signed-off-by: Colin Ian King Reviewed-by: Matthias Brugger Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c index 8522f71fc901..0f63657d8bad 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c @@ -515,7 +515,7 @@ static int h264_encode_frame(struct venc_h264_inst *inst, struct venc_frame_info frame_info; struct mtk_vcodec_enc_ctx *ctx = inst->ctx; - mtk_venc_debug(ctx, "frm_cnt = %d\n ", inst->frm_cnt); + mtk_venc_debug(ctx, "frm_cnt = %d\n", inst->frm_cnt); if (MTK_ENC_IOVA_IS_34BIT(ctx)) { gop_size = inst->vsi_34->config.gop_size; -- cgit v1.2.3-59-g8ed1b From 311e40e877bd980bc665e6c8d3b15d96f0ec2aa8 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Thu, 3 Apr 2025 15:07:41 -0400 Subject: media: verisilicon: Enable wide 4K in AV1 decoder Tested on RK3588, this decoder is capable of handling WUHD, so bump the maximum width and height accordingly. Reviewed-by: Benjamin Gaignard Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/rockchip_vpu_hw.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c index 964122e7c355..b64f0658f7f1 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c +++ b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c @@ -85,10 +85,10 @@ static const struct hantro_fmt rockchip_vpu981_postproc_fmts[] = { .postprocessed = true, .frmsize = { .min_width = ROCKCHIP_VPU981_MIN_SIZE, - .max_width = FMT_UHD_WIDTH, + .max_width = FMT_4K_WIDTH, .step_width = MB_DIM, .min_height = ROCKCHIP_VPU981_MIN_SIZE, - .max_height = FMT_UHD_HEIGHT, + .max_height = FMT_4K_HEIGHT, .step_height = MB_DIM, }, }, @@ -99,10 +99,10 @@ static const struct hantro_fmt rockchip_vpu981_postproc_fmts[] = { .postprocessed = true, .frmsize = { .min_width = ROCKCHIP_VPU981_MIN_SIZE, - .max_width = FMT_UHD_WIDTH, + .max_width = FMT_4K_WIDTH, .step_width = MB_DIM, .min_height = ROCKCHIP_VPU981_MIN_SIZE, - .max_height = FMT_UHD_HEIGHT, + .max_height = FMT_4K_HEIGHT, .step_height = MB_DIM, }, }, @@ -318,10 +318,10 @@ static const struct hantro_fmt rockchip_vpu981_dec_fmts[] = { .match_depth = true, .frmsize = { .min_width = ROCKCHIP_VPU981_MIN_SIZE, - .max_width = FMT_UHD_WIDTH, + .max_width = FMT_4K_WIDTH, .step_width = MB_DIM, .min_height = ROCKCHIP_VPU981_MIN_SIZE, - .max_height = FMT_UHD_HEIGHT, + .max_height = FMT_4K_HEIGHT, .step_height = MB_DIM, }, }, @@ -331,10 +331,10 @@ static const struct hantro_fmt rockchip_vpu981_dec_fmts[] = { .match_depth = true, .frmsize = { .min_width = ROCKCHIP_VPU981_MIN_SIZE, - .max_width = FMT_UHD_WIDTH, + .max_width = FMT_4K_WIDTH, .step_width = MB_DIM, .min_height = ROCKCHIP_VPU981_MIN_SIZE, - .max_height = FMT_UHD_HEIGHT, + .max_height = FMT_4K_HEIGHT, .step_height = MB_DIM, }, }, @@ -344,10 +344,10 @@ static const struct hantro_fmt rockchip_vpu981_dec_fmts[] = { .max_depth = 2, .frmsize = { .min_width = ROCKCHIP_VPU981_MIN_SIZE, - .max_width = FMT_UHD_WIDTH, + .max_width = FMT_4K_WIDTH, .step_width = MB_DIM, .min_height = ROCKCHIP_VPU981_MIN_SIZE, - .max_height = FMT_UHD_HEIGHT, + .max_height = FMT_4K_HEIGHT, .step_height = MB_DIM, }, }, -- cgit v1.2.3-59-g8ed1b From f19035b86382f635a0d13d177b601babaf263a12 Mon Sep 17 00:00:00 2001 From: Fei Shao Date: Fri, 14 Mar 2025 15:56:17 +0800 Subject: media: mediatek: vcodec: Correct vsi_core framebuffer size The framebuffer size for decoder instances was being incorrectly set - inst->vsi_core->fb.y.size was assigned twice consecutively. Assign the second picinfo framebuffer size to the C framebuffer instead, which appears to be the intended target based on the surrounding code. Fixes: 2674486aac7d ("media: mediatek: vcodec: support stateless hevc decoder") Cc: stable@vger.kernel.org Signed-off-by: Fei Shao Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c index aa721cc43647..2725db882e5b 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c @@ -821,7 +821,7 @@ static int vdec_hevc_slice_setup_core_buffer(struct vdec_hevc_slice_inst *inst, inst->vsi_core->fb.y.dma_addr = y_fb_dma; inst->vsi_core->fb.y.size = ctx->picinfo.fb_sz[0]; inst->vsi_core->fb.c.dma_addr = c_fb_dma; - inst->vsi_core->fb.y.size = ctx->picinfo.fb_sz[1]; + inst->vsi_core->fb.c.size = ctx->picinfo.fb_sz[1]; inst->vsi_core->dec.vdec_fb_va = (unsigned long)fb; -- cgit v1.2.3-59-g8ed1b From 80d45644d5f93d941ff127a0791362a0738c0745 Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Sat, 8 Mar 2025 15:47:55 +0800 Subject: media: mediatek: vcodec: remove vsi operation in common interface Extend the VSI (video shared information) struct to allow sending slice parameters to SCP, as the parameters have changed on MT8188 architecture. Remove VSI related information from the common interface to ensure that the interface is usable by architectures with and without the extended parameters. The new VSI extensions will be introduced in later patches. Signed-off-by: Yunfei Dong Reviewed-by: Chen-Yu Tsai Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c index 1ed0ccec5665..ab192ce0b851 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c @@ -171,9 +171,9 @@ struct vdec_h264_slice_inst { }; static int vdec_h264_slice_fill_decode_parameters(struct vdec_h264_slice_inst *inst, - struct vdec_h264_slice_share_info *share_info) + struct vdec_h264_slice_share_info *share_info, + struct vdec_h264_slice_lat_dec_param *slice_param) { - struct vdec_h264_slice_lat_dec_param *slice_param = &inst->vsi->h264_slice_params; const struct v4l2_ctrl_h264_decode_params *dec_params; const struct v4l2_ctrl_h264_scaling_matrix *src_matrix; const struct v4l2_ctrl_h264_sps *sps; @@ -266,9 +266,6 @@ static int get_vdec_sig_decode_parameters(struct vdec_h264_slice_inst *inst) mtk_vdec_h264_get_ref_list(b0_reflist, v4l2_b0_reflist, reflist_builder.num_valid); mtk_vdec_h264_get_ref_list(b1_reflist, v4l2_b1_reflist, reflist_builder.num_valid); - memcpy(&inst->vsi_ctx.h264_slice_params, slice_param, - sizeof(inst->vsi_ctx.h264_slice_params)); - return 0; } @@ -608,7 +605,8 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, lat_buf->src_buf_req = src_buf_info->m2m_buf.vb.vb2_buf.req_obj.req; v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &lat_buf->ts_info, true); - err = vdec_h264_slice_fill_decode_parameters(inst, share_info); + err = vdec_h264_slice_fill_decode_parameters(inst, share_info, + &inst->vsi->h264_slice_params); if (err) goto err_free_fb_out; @@ -749,6 +747,9 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs if (err) goto err_free_fb_out; + memcpy(&inst->vsi_ctx.h264_slice_params, &inst->h264_slice_param, + sizeof(inst->vsi_ctx.h264_slice_params)); + buf = (unsigned char *)bs->va; nal_start_idx = mtk_vdec_h264_find_start_code(buf, bs->size); if (nal_start_idx < 0) { -- cgit v1.2.3-59-g8ed1b From c8c3bb1e54457eec538907a614fab3c9c9295ff8 Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Sat, 8 Mar 2025 15:47:56 +0800 Subject: media: mediatek: vcodec: support extended h264 decode Add a new extended vsi_ext struct besides the existing vsi struct, to enable calculating the end of the address range of the current working buffer for architectures, where simply adding the buffer size to the start of the address range isn't sufficient. Additionally, on extended architectures, the NAL information can be fetched directly from the firmware, which allows skipping the parsing step within the kernel. Signed-off-by: Yunfei Dong Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h | 2 + .../vcodec/decoder/vdec/vdec_h264_req_multi_if.c | 634 ++++++++++++++++++--- 2 files changed, 552 insertions(+), 84 deletions(-) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h index ac568ed14fa2..aececca7ecf8 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h @@ -17,6 +17,7 @@ #define IS_VDEC_LAT_ARCH(hw_arch) ((hw_arch) >= MTK_VDEC_LAT_SINGLE_CORE) #define IS_VDEC_INNER_RACING(capability) ((capability) & MTK_VCODEC_INNER_RACING) +#define IS_VDEC_SUPPORT_EXT(capability) ((capability) & MTK_VDEC_IS_SUPPORT_EXT) enum mtk_vcodec_dec_chip_name { MTK_VDEC_INVAL = 0, @@ -42,6 +43,7 @@ enum mtk_vdec_format_types { MTK_VDEC_FORMAT_HEVC_FRAME = 0x1000, MTK_VCODEC_INNER_RACING = 0x20000, MTK_VDEC_IS_SUPPORT_10BIT = 0x40000, + MTK_VDEC_IS_SUPPORT_EXT = 0x80000, }; /* diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c index ab192ce0b851..13d14187e4f5 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c @@ -74,21 +74,20 @@ struct vdec_h264_slice_info { * struct vdec_h264_slice_vsi - shared memory for decode information exchange * between SCP and Host. * - * @wdma_err_addr: wdma error dma address - * @wdma_start_addr: wdma start dma address - * @wdma_end_addr: wdma end dma address - * @slice_bc_start_addr: slice bc start dma address - * @slice_bc_end_addr: slice bc end dma address - * @row_info_start_addr: row info start dma address - * @row_info_end_addr: row info end dma address - * @trans_start: trans start dma address - * @trans_end: trans end dma address - * @wdma_end_addr_offset: wdma end address offset + * @wdma_err_addr: wdma error dma address + * @wdma_start_addr: wdma start dma address + * @wdma_end_addr: wdma end dma address + * @slice_bc_start_addr: slice bc start dma address + * @slice_bc_end_addr: slice bc end dma address + * @row_info_start_addr: row info start dma address + * @row_info_end_addr: row info end dma address + * @trans_start: trans start dma address + * @trans_end: trans end dma address + * @wdma_end_addr_offset: wdma end address offset * - * @mv_buf_dma: HW working motion vector buffer - * dma address (AP-W, VPU-R) - * @dec: decode information (AP-R, VPU-W) - * @h264_slice_params: decode parameters for hw used + * @mv_buf_dma: HW working motion vector buffer + * @dec: decode information (AP-R, VPU-W) + * @h264_slice_params: decode parameters for hw used */ struct vdec_h264_slice_vsi { /* LAT dec addr */ @@ -112,12 +111,12 @@ struct vdec_h264_slice_vsi { * struct vdec_h264_slice_share_info - shared information used to exchange * message between lat and core * - * @sps: sequence header information from user space - * @dec_params: decoder params from user space - * @h264_slice_params: decoder params used for hardware - * @trans_start: trans start dma address - * @trans_end: trans end dma address - * @nal_info: nal info of current picture + * @sps: sequence header information from user space + * @dec_params: decoder params from user space + * @h264_slice_params: decoder params used for hardware + * @trans_start: trans start dma address + * @trans_end: trans end dma address + * @nal_info: nal info of current picture */ struct vdec_h264_slice_share_info { struct v4l2_ctrl_h264_sps sps; @@ -128,6 +127,86 @@ struct vdec_h264_slice_share_info { u16 nal_info; }; +/* + * struct vdec_h264_slice_mem - memory address and size + * (shared data between host and firmware) + */ +struct vdec_h264_slice_mem { + union { + u64 buf; + u64 dma_addr; + }; + union { + size_t size; + u64 dma_addr_end; + }; +}; + +/** + * struct vdec_h264_slice_fb - frame buffer for decoding + * (shared data between host and firmware) + * + * @y: current luma buffer address info + * @c: current chroma buffer address info + */ +struct vdec_h264_slice_fb { + struct vdec_h264_slice_mem y; + struct vdec_h264_slice_mem c; +}; + +/** + * struct vdec_h264_slice_info_ext - extend decode information + * (shared data between host and firmware) + * + * @wdma_end_addr_offset: offset from buffer start + * @nal_info: nal info of current picture + * @timeout: toggles whether a decode operation is timeout + * @reserved: reserved + * @vdec_fb_va: vdec frame buffer struct virtual address + * @crc: displays the hardware status + */ +struct vdec_h264_slice_info_ext { + u64 wdma_end_addr_offset; + u16 nal_info; + u16 timeout; + u32 reserved; + u64 vdec_fb_va; + u32 crc[8]; +}; + +/** + * struct vdec_h264_slice_vsi_ext - extend shared memory for decode information exchange + * between SCP and Host (shared data between host and firmware). + * + * @bs: input buffer info + * @fb: current y/c buffer + * + * @ube: buffer used to share date between lat and core + * @trans: transcoded buffer used for core decode + * @row_info: row info buffer + * @err_map: error map buffer + * @slice_bc: slice buffer + * + * @mv_buf_dma: store hardware motion vector data + * @dec: decode information (AP-R, VPU-W) + * @h264_slice_params: decode parameters used for the hw + */ +struct vdec_h264_slice_vsi_ext { + /* LAT dec addr */ + struct vdec_h264_slice_mem bs; + struct vdec_h264_slice_fb fb; + + struct vdec_h264_slice_mem ube; + struct vdec_h264_slice_mem trans; + struct vdec_h264_slice_mem row_info; + struct vdec_h264_slice_mem err_map; + struct vdec_h264_slice_mem slice_bc; + + struct vdec_h264_slice_mem mv_buf_dma[H264_MAX_MV_NUM]; + struct vdec_h264_slice_info_ext dec; + struct vdec_h264_slice_lat_dec_param h264_slice_params; +}; + /** * struct vdec_h264_slice_inst - h264 decoder instance * @@ -138,17 +217,21 @@ struct vdec_h264_slice_share_info { * @vpu: VPU instance * @vsi: vsi used for lat * @vsi_core: vsi used for core - * - * @vsi_ctx: Local VSI data for this decoding context + * @vsi_ctx: vsi data for this decoding context + * @vsi_ext: extended vsi used for lat + * @vsi_core_ext: extended vsi used for core + * @vsi_ctx_ext: extended vsi data for this decoding context * @h264_slice_param: the parameters that hardware use to decode * - * @resolution_changed:resolution changed + * @resolution_changed: resolution changed * @realloc_mv_buf: reallocate mv buffer * @cap_num_planes: number of capture queue plane * * @dpb: decoded picture buffer used to store reference * buffer information - *@is_field_bitstream: is field bitstream + * @is_field_bitstream: not support field bitstream, only support frame + * + * @decode: lat decoder pointer for different architectures */ struct vdec_h264_slice_inst { unsigned int slice_dec_num; @@ -156,10 +239,18 @@ struct vdec_h264_slice_inst { struct mtk_vcodec_mem pred_buf; struct mtk_vcodec_mem mv_buf[H264_MAX_MV_NUM]; struct vdec_vpu_inst vpu; - struct vdec_h264_slice_vsi *vsi; - struct vdec_h264_slice_vsi *vsi_core; - - struct vdec_h264_slice_vsi vsi_ctx; + union { + struct { + struct vdec_h264_slice_vsi *vsi; + struct vdec_h264_slice_vsi *vsi_core; + struct vdec_h264_slice_vsi vsi_ctx; + }; + struct { + struct vdec_h264_slice_vsi_ext *vsi_ext; + struct vdec_h264_slice_vsi_ext *vsi_core_ext; + struct vdec_h264_slice_vsi_ext vsi_ctx_ext; + }; + }; struct vdec_h264_slice_lat_dec_param h264_slice_param; unsigned int resolution_changed; @@ -168,6 +259,9 @@ struct vdec_h264_slice_inst { struct v4l2_h264_dpb_entry dpb[16]; bool is_field_bitstream; + + int (*decode)(void *h_vdec, struct mtk_vcodec_mem *bs, + struct vdec_fb *unused, bool *res_chg); }; static int vdec_h264_slice_fill_decode_parameters(struct vdec_h264_slice_inst *inst, @@ -389,68 +483,148 @@ static void vdec_h264_slice_get_crop_info(struct vdec_h264_slice_inst *inst, cr->left, cr->top, cr->width, cr->height); } -static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx) +static void vdec_h264_slice_setup_lat_buffer_ext(struct vdec_h264_slice_inst *inst, + struct mtk_vcodec_mem *bs, + struct vdec_lat_buf *lat_buf) { - struct vdec_h264_slice_inst *inst; - int err, vsi_size; + struct mtk_vcodec_mem *mem; + int i; - inst = kzalloc(sizeof(*inst), GFP_KERNEL); - if (!inst) - return -ENOMEM; + inst->vsi_ext->bs.dma_addr = (u64)bs->dma_addr; + inst->vsi_ext->bs.size = bs->size; - inst->ctx = ctx; + for (i = 0; i < H264_MAX_MV_NUM; i++) { + mem = &inst->mv_buf[i]; + inst->vsi_ext->mv_buf_dma[i].dma_addr = mem->dma_addr; + inst->vsi_ext->mv_buf_dma[i].size = mem->size; + } + inst->vsi_ext->ube.dma_addr = lat_buf->ctx->msg_queue.wdma_addr.dma_addr; + inst->vsi_ext->ube.size = lat_buf->ctx->msg_queue.wdma_addr.size; - inst->vpu.id = SCP_IPI_VDEC_LAT; - inst->vpu.core_id = SCP_IPI_VDEC_CORE; - inst->vpu.ctx = ctx; - inst->vpu.codec_type = ctx->current_codec; - inst->vpu.capture_type = ctx->capture_fourcc; + inst->vsi_ext->row_info.dma_addr = 0; + inst->vsi_ext->row_info.size = 0; - err = vpu_dec_init(&inst->vpu); - if (err) { - mtk_vdec_err(ctx, "vdec_h264 init err=%d", err); - goto error_free_inst; + inst->vsi_ext->err_map.dma_addr = lat_buf->wdma_err_addr.dma_addr; + inst->vsi_ext->err_map.size = lat_buf->wdma_err_addr.size; + + inst->vsi_ext->slice_bc.dma_addr = lat_buf->slice_bc_addr.dma_addr; + inst->vsi_ext->slice_bc.size = lat_buf->slice_bc_addr.size; + + inst->vsi_ext->trans.dma_addr_end = inst->ctx->msg_queue.wdma_rptr_addr; + inst->vsi_ext->trans.dma_addr = inst->ctx->msg_queue.wdma_wptr_addr; +} + +static int vdec_h264_slice_setup_core_buffer_ext(struct vdec_h264_slice_inst *inst, + struct vdec_h264_slice_share_info *share_info, + struct vdec_lat_buf *lat_buf) +{ + struct mtk_vcodec_mem *mem; + struct mtk_vcodec_dec_ctx *ctx = inst->ctx; + struct vb2_v4l2_buffer *vb2_v4l2; + struct vdec_fb *fb; + u64 y_fb_dma, c_fb_dma = 0; + int i; + + fb = ctx->dev->vdec_pdata->get_cap_buffer(ctx); + if (!fb) { + mtk_vdec_err(ctx, "Unable to get a CAPTURE buffer for CAPTURE queue is empty."); + return -EBUSY; } - vsi_size = round_up(sizeof(struct vdec_h264_slice_vsi), VCODEC_DEC_ALIGNED_64); - inst->vsi = inst->vpu.vsi; - inst->vsi_core = - (struct vdec_h264_slice_vsi *)(((char *)inst->vpu.vsi) + vsi_size); - inst->resolution_changed = true; - inst->realloc_mv_buf = true; + y_fb_dma = (u64)fb->base_y.dma_addr; + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 1) + c_fb_dma = y_fb_dma + ctx->picinfo.fb_sz[0]; + else + c_fb_dma = (u64)fb->base_c.dma_addr; - mtk_vdec_debug(ctx, "lat struct size = %d,%d,%d,%d vsi: %d\n", - (int)sizeof(struct mtk_h264_sps_param), - (int)sizeof(struct mtk_h264_pps_param), - (int)sizeof(struct vdec_h264_slice_lat_dec_param), - (int)sizeof(struct mtk_h264_dpb_info), - vsi_size); - mtk_vdec_debug(ctx, "lat H264 instance >> %p, codec_type = 0x%x", - inst, inst->vpu.codec_type); + mtk_vdec_debug(ctx, "[h264-core] y/c addr = 0x%llx 0x%llx", y_fb_dma, c_fb_dma); - ctx->drv_handle = inst; - return 0; + inst->vsi_core_ext->fb.y.dma_addr = y_fb_dma; + inst->vsi_core_ext->fb.y.size = ctx->picinfo.fb_sz[0]; + inst->vsi_core_ext->fb.c.dma_addr = c_fb_dma; + inst->vsi_core_ext->fb.c.size = ctx->picinfo.fb_sz[1]; -error_free_inst: - kfree(inst); - return err; + inst->vsi_core_ext->dec.vdec_fb_va = (unsigned long)fb; + inst->vsi_core_ext->dec.nal_info = share_info->nal_info; + + inst->vsi_core_ext->ube.dma_addr = lat_buf->ctx->msg_queue.wdma_addr.dma_addr; + inst->vsi_core_ext->ube.size = lat_buf->ctx->msg_queue.wdma_addr.size; + + inst->vsi_core_ext->err_map.dma_addr = lat_buf->wdma_err_addr.dma_addr; + inst->vsi_core_ext->err_map.size = lat_buf->wdma_err_addr.size; + + inst->vsi_core_ext->slice_bc.dma_addr = lat_buf->slice_bc_addr.dma_addr; + inst->vsi_core_ext->slice_bc.size = lat_buf->slice_bc_addr.size; + + inst->vsi_core_ext->row_info.dma_addr = 0; + inst->vsi_core_ext->row_info.size = 0; + + inst->vsi_core_ext->trans.dma_addr = share_info->trans_start; + inst->vsi_core_ext->trans.dma_addr_end = share_info->trans_end; + + for (i = 0; i < H264_MAX_MV_NUM; i++) { + mem = &inst->mv_buf[i]; + inst->vsi_core_ext->mv_buf_dma[i].dma_addr = mem->dma_addr; + inst->vsi_core_ext->mv_buf_dma[i].size = mem->size; + } + + vb2_v4l2 = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2, true); + + return 0; } -static void vdec_h264_slice_deinit(void *h_vdec) +static int vdec_h264_slice_core_decode_ext(struct vdec_lat_buf *lat_buf) { - struct vdec_h264_slice_inst *inst = h_vdec; + int err, timeout; + struct mtk_vcodec_dec_ctx *ctx = lat_buf->ctx; + struct vdec_h264_slice_inst *inst = ctx->drv_handle; + struct vdec_h264_slice_share_info *share_info = lat_buf->private_data; + struct vdec_vpu_inst *vpu = &inst->vpu; - vpu_dec_deinit(&inst->vpu); - vdec_h264_slice_free_mv_buf(inst); - vdec_msg_queue_deinit(&inst->ctx->msg_queue, inst->ctx); + memcpy(&inst->vsi_core_ext->h264_slice_params, &share_info->h264_slice_params, + sizeof(share_info->h264_slice_params)); - kfree(inst); + err = vdec_h264_slice_setup_core_buffer_ext(inst, share_info, lat_buf); + if (err) + goto vdec_dec_end; + + vdec_h264_slice_fill_decode_reflist(inst, &inst->vsi_core_ext->h264_slice_params, + share_info); + err = vpu_dec_core(vpu); + if (err) { + mtk_vdec_err(ctx, "core decode err=%d", err); + goto vdec_dec_end; + } + + /* wait decoder done interrupt */ + timeout = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE); + if (timeout) + mtk_vdec_err(ctx, "core decode timeout: pic_%d", ctx->decoded_frame_cnt); + inst->vsi_core_ext->dec.timeout = !!timeout; + + vpu_dec_core_end(vpu); + + /* crc is hardware checksum, can be used to check whether the decoder result is right.*/ + mtk_vdec_debug(ctx, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + ctx->decoded_frame_cnt, + inst->vsi_core_ext->dec.crc[0], inst->vsi_core_ext->dec.crc[1], + inst->vsi_core_ext->dec.crc[2], inst->vsi_core_ext->dec.crc[3], + inst->vsi_core_ext->dec.crc[4], inst->vsi_core_ext->dec.crc[5], + inst->vsi_core_ext->dec.crc[6], inst->vsi_core_ext->dec.crc[7]); + +vdec_dec_end: + vdec_msg_queue_update_ube_rptr(&lat_buf->ctx->msg_queue, share_info->trans_end); + ctx->dev->vdec_pdata->cap_to_disp(ctx, !!err, lat_buf->src_buf_req); + mtk_vdec_debug(ctx, "core decode done err=%d", err); + ctx->decoded_frame_cnt++; + return 0; } static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf) { struct vdec_fb *fb; - u64 vdec_fb_va; u64 y_fb_dma, c_fb_dma; int err, timeout, i; struct mtk_vcodec_dec_ctx *ctx = lat_buf->ctx; @@ -460,22 +634,19 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf) struct mtk_vcodec_mem *mem; struct vdec_vpu_inst *vpu = &inst->vpu; - mtk_vdec_debug(ctx, "[h264-core] vdec_h264 core decode"); memcpy(&inst->vsi_core->h264_slice_params, &share_info->h264_slice_params, sizeof(share_info->h264_slice_params)); fb = ctx->dev->vdec_pdata->get_cap_buffer(ctx); if (!fb) { err = -EBUSY; - mtk_vdec_err(ctx, "fb buffer is NULL"); + mtk_vdec_err(ctx, "Unable to get a CAPTURE buffer for CAPTURE queue is empty."); goto vdec_dec_end; } - vdec_fb_va = (unsigned long)fb; y_fb_dma = (u64)fb->base_y.dma_addr; if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 1) - c_fb_dma = - y_fb_dma + inst->ctx->picinfo.buf_w * inst->ctx->picinfo.buf_h; + c_fb_dma = y_fb_dma + ctx->picinfo.fb_sz[0]; else c_fb_dma = (u64)fb->base_c.dma_addr; @@ -483,7 +654,7 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf) inst->vsi_core->dec.y_fb_dma = y_fb_dma; inst->vsi_core->dec.c_fb_dma = c_fb_dma; - inst->vsi_core->dec.vdec_fb_va = vdec_fb_va; + inst->vsi_core->dec.vdec_fb_va = (unsigned long)fb; inst->vsi_core->dec.nal_info = share_info->nal_info; inst->vsi_core->wdma_start_addr = lat_buf->ctx->msg_queue.wdma_addr.dma_addr; @@ -521,6 +692,8 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf) inst->vsi_core->dec.timeout = !!timeout; vpu_dec_core_end(vpu); + + /* crc is hardware checksum, can be used to check whether the decoder result is right.*/ mtk_vdec_debug(ctx, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", ctx->decoded_frame_cnt, inst->vsi_core->dec.crc[0], inst->vsi_core->dec.crc[1], @@ -533,6 +706,7 @@ vdec_dec_end: ctx->dev->vdec_pdata->cap_to_disp(ctx, !!err, lat_buf->src_buf_req); mtk_vdec_debug(ctx, "core decode done err=%d", err); ctx->decoded_frame_cnt++; + return 0; } @@ -559,6 +733,128 @@ static void vdec_h264_insert_startcode(struct mtk_vcodec_dec_dev *vcodec_dev, un (*bs_size) += 4; } +static int vdec_h264_slice_lat_decode_ext(void *h_vdec, struct mtk_vcodec_mem *bs, + struct vdec_fb *fb, bool *res_chg) +{ + struct vdec_h264_slice_inst *inst = h_vdec; + struct vdec_vpu_inst *vpu = &inst->vpu; + struct mtk_video_dec_buf *src_buf_info; + int err, timeout = 0; + unsigned int data[2]; + struct vdec_lat_buf *lat_buf; + struct vdec_h264_slice_share_info *share_info; + + if (vdec_msg_queue_init(&inst->ctx->msg_queue, inst->ctx, + vdec_h264_slice_core_decode_ext, + sizeof(*share_info))) + return -ENOMEM; + + /* bs NULL means flush decoder */ + if (!bs) { + vdec_msg_queue_wait_lat_buf_full(&inst->ctx->msg_queue); + return vpu_dec_reset(vpu); + } + + if (inst->is_field_bitstream) + return -EINVAL; + + lat_buf = vdec_msg_queue_dqbuf(&inst->ctx->msg_queue.lat_ctx); + if (!lat_buf) { + mtk_vdec_debug(inst->ctx, "failed to get lat buffer"); + return -EAGAIN; + } + share_info = lat_buf->private_data; + src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer); + + lat_buf->src_buf_req = src_buf_info->m2m_buf.vb.vb2_buf.req_obj.req; + v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &lat_buf->ts_info, true); + + err = vdec_h264_slice_fill_decode_parameters(inst, share_info, + &inst->vsi_ext->h264_slice_params); + if (err) + goto err_free_fb_out; + + vdec_h264_insert_startcode(inst->ctx->dev, bs->va, &bs->size, + &share_info->h264_slice_params.pps); + + *res_chg = inst->resolution_changed; + if (inst->resolution_changed) { + mtk_vdec_debug(inst->ctx, "- resolution changed -"); + if (inst->realloc_mv_buf) { + err = vdec_h264_slice_alloc_mv_buf(inst, &inst->ctx->picinfo); + inst->realloc_mv_buf = false; + if (err) + goto err_free_fb_out; + } + inst->resolution_changed = false; + } + + vdec_h264_slice_setup_lat_buffer_ext(inst, bs, lat_buf); + mtk_vdec_debug(inst->ctx, "lat:trans(0x%llx 0x%lx) err:0x%llx", + inst->vsi_ext->ube.dma_addr, (unsigned long)inst->vsi_ext->ube.size, + inst->vsi_ext->err_map.dma_addr); + + mtk_vdec_debug(inst->ctx, "slice(0x%llx 0x%lx) rprt((0x%llx 0x%llx))", + inst->vsi_ext->slice_bc.dma_addr, + (unsigned long)inst->vsi_ext->slice_bc.size, + inst->vsi_ext->trans.dma_addr, inst->vsi_ext->trans.dma_addr_end); + + err = vpu_dec_start(vpu, data, 2); + if (err) { + mtk_vdec_debug(inst->ctx, "lat decode err: %d", err); + goto err_free_fb_out; + } + + share_info->trans_end = inst->ctx->msg_queue.wdma_addr.dma_addr + + inst->vsi_ext->dec.wdma_end_addr_offset; + + share_info->trans_start = inst->ctx->msg_queue.wdma_wptr_addr; + share_info->nal_info = inst->vsi_ext->dec.nal_info; + + if (IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) { + memcpy(&share_info->h264_slice_params, &inst->vsi_ext->h264_slice_params, + sizeof(share_info->h264_slice_params)); + vdec_msg_queue_qbuf(&inst->ctx->msg_queue.core_ctx, lat_buf); + } + + /* wait decoder done interrupt */ + timeout = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS, MTK_VDEC_LAT0); + if (timeout) + mtk_vdec_err(inst->ctx, "lat decode timeout: pic_%d", inst->slice_dec_num); + inst->vsi_ext->dec.timeout = !!timeout; + + err = vpu_dec_end(vpu); + if (err == SLICE_HEADER_FULL || err == TRANS_BUFFER_FULL) { + if (!IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) + vdec_msg_queue_qbuf(&inst->ctx->msg_queue.lat_ctx, lat_buf); + inst->slice_dec_num++; + mtk_vdec_err(inst->ctx, "lat dec fail: pic_%d err:%d", inst->slice_dec_num, err); + return -EINVAL; + } + + share_info->trans_end = inst->ctx->msg_queue.wdma_addr.dma_addr + + inst->vsi_ext->dec.wdma_end_addr_offset; + + vdec_msg_queue_update_ube_wptr(&lat_buf->ctx->msg_queue, share_info->trans_end); + + if (!IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) { + memcpy(&share_info->h264_slice_params, &inst->vsi_ext->h264_slice_params, + sizeof(share_info->h264_slice_params)); + vdec_msg_queue_qbuf(&inst->ctx->msg_queue.core_ctx, lat_buf); + } + mtk_vdec_debug(inst->ctx, "dec num: %d lat crc: 0x%x 0x%x 0x%x", inst->slice_dec_num, + inst->vsi_ext->dec.crc[0], inst->vsi_ext->dec.crc[1], + inst->vsi_ext->dec.crc[2]); + + inst->slice_dec_num++; + return 0; +err_free_fb_out: + vdec_msg_queue_qbuf(&inst->ctx->msg_queue.lat_ctx, lat_buf); + mtk_vdec_err(inst->ctx, "slice dec number: %d err: %d", inst->slice_dec_num, err); + return err; +} + static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, struct vdec_fb *fb, bool *res_chg) { @@ -704,6 +1000,101 @@ err_free_fb_out: return err; } +static int vdec_h264_slice_single_decode_ext(void *h_vdec, struct mtk_vcodec_mem *bs, + struct vdec_fb *unused, bool *res_chg) +{ + struct vdec_h264_slice_inst *inst = h_vdec; + struct vdec_vpu_inst *vpu = &inst->vpu; + struct mtk_video_dec_buf *src_buf_info, *dst_buf_info; + struct vdec_fb *fb; + unsigned int data[2], i; + u64 y_fb_dma, c_fb_dma; + struct mtk_vcodec_mem *mem; + int err; + + /* bs NULL means flush decoder */ + if (!bs) + return vpu_dec_reset(vpu); + + fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx); + if (!fb) { + mtk_vdec_err(inst->ctx, + "Unable to get a CAPTURE buffer for CAPTURE queue is empty."); + return -ENOMEM; + } + + src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer); + dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer); + + y_fb_dma = fb->base_y.dma_addr; + c_fb_dma = fb->base_c.dma_addr; + mtk_vdec_debug(inst->ctx, "[h264-dec] [%d] y_dma=%llx c_dma=%llx", + inst->ctx->decoded_frame_cnt, y_fb_dma, c_fb_dma); + + inst->vsi_ctx_ext.bs.dma_addr = (u64)bs->dma_addr; + inst->vsi_ctx_ext.bs.size = bs->size; + inst->vsi_ctx_ext.fb.y.dma_addr = y_fb_dma; + inst->vsi_ctx_ext.fb.c.dma_addr = c_fb_dma; + inst->vsi_ctx_ext.dec.vdec_fb_va = (u64)(uintptr_t)fb; + + v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, + &dst_buf_info->m2m_buf.vb, true); + err = get_vdec_sig_decode_parameters(inst); + if (err) + goto err_free_fb_out; + + memcpy(&inst->vsi_ctx_ext.h264_slice_params, &inst->h264_slice_param, + sizeof(inst->vsi_ctx_ext.h264_slice_params)); + + *res_chg = inst->resolution_changed; + if (inst->resolution_changed) { + mtk_vdec_debug(inst->ctx, "- resolution changed -"); + if (inst->realloc_mv_buf) { + err = vdec_h264_slice_alloc_mv_buf(inst, &inst->ctx->picinfo); + inst->realloc_mv_buf = false; + if (err) + goto err_free_fb_out; + } + inst->resolution_changed = false; + + for (i = 0; i < H264_MAX_MV_NUM; i++) { + mem = &inst->mv_buf[i]; + inst->vsi_ctx_ext.mv_buf_dma[i].dma_addr = mem->dma_addr; + } + } + + memcpy(inst->vpu.vsi, &inst->vsi_ctx_ext, sizeof(inst->vsi_ctx_ext)); + err = vpu_dec_start(vpu, data, 2); + if (err) + goto err_free_fb_out; + + /* wait decoder done interrupt */ + err = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE); + if (err) + mtk_vdec_err(inst->ctx, "decode timeout: pic_%d", inst->ctx->decoded_frame_cnt); + + inst->vsi_ext->dec.timeout = !!err; + err = vpu_dec_end(vpu); + if (err) + goto err_free_fb_out; + + memcpy(&inst->vsi_ctx_ext, inst->vpu.vsi, sizeof(inst->vsi_ctx_ext)); + mtk_vdec_debug(inst->ctx, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + inst->ctx->decoded_frame_cnt, + inst->vsi_ctx_ext.dec.crc[0], inst->vsi_ctx_ext.dec.crc[1], + inst->vsi_ctx_ext.dec.crc[2], inst->vsi_ctx_ext.dec.crc[3], + inst->vsi_ctx_ext.dec.crc[4], inst->vsi_ctx_ext.dec.crc[5], + inst->vsi_ctx_ext.dec.crc[6], inst->vsi_ctx_ext.dec.crc[7]); + + inst->ctx->decoded_frame_cnt++; + return 0; + +err_free_fb_out: + mtk_vdec_err(inst->ctx, "dec frame number: %d err: %d", inst->ctx->decoded_frame_cnt, err); + return err; +} + static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs, struct vdec_fb *unused, bool *res_chg) { @@ -723,7 +1114,8 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx); if (!fb) { - mtk_vdec_err(inst->ctx, "fb buffer is NULL"); + mtk_vdec_err(inst->ctx, + "Unable to get a CAPTURE buffer for CAPTURE queue is empty."); return -ENOMEM; } @@ -807,21 +1199,95 @@ err_free_fb_out: return err; } +static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx) +{ + struct vdec_h264_slice_inst *inst; + int err, vsi_size; + unsigned char *temp; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->ctx = ctx; + + inst->vpu.id = SCP_IPI_VDEC_LAT; + inst->vpu.core_id = SCP_IPI_VDEC_CORE; + inst->vpu.ctx = ctx; + inst->vpu.codec_type = ctx->current_codec; + inst->vpu.capture_type = ctx->capture_fourcc; + + err = vpu_dec_init(&inst->vpu); + if (err) { + mtk_vdec_err(ctx, "vdec_h264 init err=%d", err); + goto error_free_inst; + } + + if (IS_VDEC_SUPPORT_EXT(ctx->dev->dec_capability)) { + vsi_size = sizeof(struct vdec_h264_slice_vsi_ext); + + vsi_size = round_up(vsi_size, VCODEC_DEC_ALIGNED_64); + inst->vsi_ext = inst->vpu.vsi; + temp = (unsigned char *)inst->vsi_ext; + inst->vsi_core_ext = (struct vdec_h264_slice_vsi_ext *)(temp + vsi_size); + + if (inst->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_PURE_SINGLE_CORE) + inst->decode = vdec_h264_slice_single_decode_ext; + else + inst->decode = vdec_h264_slice_lat_decode_ext; + } else { + vsi_size = sizeof(struct vdec_h264_slice_vsi); + + vsi_size = round_up(vsi_size, VCODEC_DEC_ALIGNED_64); + inst->vsi = inst->vpu.vsi; + temp = (unsigned char *)inst->vsi; + inst->vsi_core = (struct vdec_h264_slice_vsi *)(temp + vsi_size); + + if (inst->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_PURE_SINGLE_CORE) + inst->decode = vdec_h264_slice_single_decode; + else + inst->decode = vdec_h264_slice_lat_decode; + } + inst->resolution_changed = true; + inst->realloc_mv_buf = true; + + mtk_vdec_debug(ctx, "lat struct size = %d,%d,%d,%d vsi: %d\n", + (int)sizeof(struct mtk_h264_sps_param), + (int)sizeof(struct mtk_h264_pps_param), + (int)sizeof(struct vdec_h264_slice_lat_dec_param), + (int)sizeof(struct mtk_h264_dpb_info), + vsi_size); + mtk_vdec_debug(ctx, "lat H264 instance >> %p, codec_type = 0x%x", + inst, inst->vpu.codec_type); + + ctx->drv_handle = inst; + return 0; + +error_free_inst: + kfree(inst); + return err; +} + +static void vdec_h264_slice_deinit(void *h_vdec) +{ + struct vdec_h264_slice_inst *inst = h_vdec; + + vpu_dec_deinit(&inst->vpu); + vdec_h264_slice_free_mv_buf(inst); + vdec_msg_queue_deinit(&inst->ctx->msg_queue, inst->ctx); + + kfree(inst); +} + static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, struct vdec_fb *unused, bool *res_chg) { struct vdec_h264_slice_inst *inst = h_vdec; - int ret; if (!h_vdec) return -EINVAL; - if (inst->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_PURE_SINGLE_CORE) - ret = vdec_h264_slice_single_decode(h_vdec, bs, unused, res_chg); - else - ret = vdec_h264_slice_lat_decode(h_vdec, bs, unused, res_chg); - - return ret; + return inst->decode(h_vdec, bs, unused, res_chg); } static int vdec_h264_slice_get_param(void *h_vdec, enum vdec_get_param_type type, -- cgit v1.2.3-59-g8ed1b From 4c3596d7e83a320ef7241de6e194d85bec5974ea Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Sat, 8 Mar 2025 15:47:57 +0800 Subject: media: mediatek: vcodec: add description for vsi struct The vsi (video shared information) struct needs to be synchronized between firmware and host, as a change that is only done in the host version of the struct but isn't synchronized to the firmware. This can lead to decoding issues with H264 bitstreams. Highlight this requirement within the struct descriptions. Signed-off-by: Yunfei Dong Reviewed-by: Chen-Yu Tsai Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c index 13d14187e4f5..5b25e1679b51 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c @@ -30,6 +30,7 @@ enum vdec_h264_core_dec_err_type { /** * struct vdec_h264_slice_lat_dec_param - parameters for decode current frame + * (shared data between host and firmware) * * @sps: h264 sps syntax parameters * @pps: h264 pps syntax parameters @@ -48,7 +49,7 @@ struct vdec_h264_slice_lat_dec_param { }; /** - * struct vdec_h264_slice_info - decode information + * struct vdec_h264_slice_info - decode information (shared data between host and firmware) * * @nal_info: nal info of current picture * @timeout: Decode timeout: 1 timeout, 0 no timeout @@ -72,7 +73,7 @@ struct vdec_h264_slice_info { /** * struct vdec_h264_slice_vsi - shared memory for decode information exchange - * between SCP and Host. + * between SCP and Host (shared data between host and firmware). * * @wdma_err_addr: wdma error dma address * @wdma_start_addr: wdma start dma address -- cgit v1.2.3-59-g8ed1b From 10c17af9666a7789f5f920f441f4fd215902cf81 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 2 Apr 2025 13:24:14 +0100 Subject: media: MAINTAINERS: Amend venus Maintainers and Reviewers Stan has stepped back from active venus development as a result I'd like to volunteer my help in keeping venus maintained upstream. Discussing with the qcom team on this we agreed +M for Dikshita +R for me Many thanks to Stan for his hard work over the years from originating this driver upstream to his many years of maintenance of it too. Acked-by: Neil Armstrong Acked-by: Dikshita Agarwal Acked-by: Stanimir Varbanov Signed-off-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 96b827049501..5dcef05ad7c3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20029,8 +20029,8 @@ F: Documentation/devicetree/bindings/usb/qcom,pmic-*.yaml F: drivers/usb/typec/tcpm/qcom/ QUALCOMM VENUS VIDEO ACCELERATOR DRIVER -M: Stanimir Varbanov M: Vikash Garodia +M: Dikshita Agarwal R: Bryan O'Donoghue L: linux-media@vger.kernel.org L: linux-arm-msm@vger.kernel.org -- cgit v1.2.3-59-g8ed1b From 4acbaa8794b3712246eaabb34094209bb6568c3f Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 2 Apr 2025 13:24:15 +0100 Subject: media: MAINTAINERS: Add myself to iris Reviewers There's some crossover between venus and iris, I'd like to help out with the reviews for iris to ensure we keep upstream chugging along. Acked-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5dcef05ad7c3..83f9f46a6bcb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19968,6 +19968,7 @@ QUALCOMM IRIS VIDEO ACCELERATOR DRIVER M: Vikash Garodia M: Dikshita Agarwal R: Abhinav Kumar +R: Bryan O'Donoghue L: linux-media@vger.kernel.org L: linux-arm-msm@vger.kernel.org S: Maintained -- cgit v1.2.3-59-g8ed1b From be526da77939920f7d8fa665889f41a0ad5b4d8d Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Mon, 9 Dec 2024 13:01:05 +0100 Subject: dt-bindings: media: camss: Restrict bus-type property The CSIPHY of Qualcomm SoCs support both D-PHY and C-PHY standards for CSI-2, but not any others so restrict the bus-type property describing this to the supported values. The only exception here is MSM8916 which only supports D-PHY. C-PHY was introduced with newer SoCs. Do note, that currently the Linux driver only supports D-PHY. Signed-off-by: Luca Weiss Reviewed-by: Krzysztof Kozlowski Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- .../bindings/media/qcom,msm8916-camss.yaml | 8 ++++++ .../bindings/media/qcom,msm8953-camss.yaml | 15 +++++++++++ .../bindings/media/qcom,msm8996-camss.yaml | 20 +++++++++++++++ .../bindings/media/qcom,sc8280xp-camss.yaml | 20 +++++++++++++++ .../bindings/media/qcom,sdm660-camss.yaml | 20 +++++++++++++++ .../bindings/media/qcom,sdm845-camss.yaml | 20 +++++++++++++++ .../bindings/media/qcom,sm8250-camss.yaml | 30 ++++++++++++++++++++++ 7 files changed, 133 insertions(+) diff --git a/Documentation/devicetree/bindings/media/qcom,msm8916-camss.yaml b/Documentation/devicetree/bindings/media/qcom,msm8916-camss.yaml index 3469a43f00d4..7c8e0a905d89 100644 --- a/Documentation/devicetree/bindings/media/qcom,msm8916-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,msm8916-camss.yaml @@ -93,6 +93,10 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes @@ -112,6 +116,10 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes diff --git a/Documentation/devicetree/bindings/media/qcom,msm8953-camss.yaml b/Documentation/devicetree/bindings/media/qcom,msm8953-camss.yaml index 8856fba385b1..6d776b0ca711 100644 --- a/Documentation/devicetree/bindings/media/qcom,msm8953-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,msm8953-camss.yaml @@ -112,6 +112,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes @@ -131,6 +136,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes @@ -150,6 +160,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes diff --git a/Documentation/devicetree/bindings/media/qcom,msm8996-camss.yaml b/Documentation/devicetree/bindings/media/qcom,msm8996-camss.yaml index 644646de338a..a2025952fe95 100644 --- a/Documentation/devicetree/bindings/media/qcom,msm8996-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,msm8996-camss.yaml @@ -115,6 +115,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes @@ -134,6 +139,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes @@ -153,6 +163,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes @@ -172,6 +187,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes diff --git a/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml index 9936f0132417..d195f1bfb23d 100644 --- a/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml @@ -143,6 +143,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - clock-lanes - data-lanes @@ -166,6 +171,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - clock-lanes - data-lanes @@ -189,6 +199,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - clock-lanes - data-lanes @@ -212,6 +227,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - clock-lanes - data-lanes diff --git a/Documentation/devicetree/bindings/media/qcom,sdm660-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sdm660-camss.yaml index 68d8670557f5..6e6ad8390e44 100644 --- a/Documentation/devicetree/bindings/media/qcom,sdm660-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sdm660-camss.yaml @@ -121,6 +121,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes @@ -140,6 +145,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes @@ -159,6 +169,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes @@ -178,6 +193,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes diff --git a/Documentation/devicetree/bindings/media/qcom,sdm845-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sdm845-camss.yaml index 289494f561e5..82bf4689d330 100644 --- a/Documentation/devicetree/bindings/media/qcom,sdm845-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sdm845-camss.yaml @@ -108,6 +108,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes @@ -127,6 +132,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes @@ -146,6 +156,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes @@ -165,6 +180,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - data-lanes diff --git a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml index a372d991e652..ebf68ff4ab96 100644 --- a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml @@ -128,6 +128,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - clock-lanes - data-lanes @@ -151,6 +156,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - clock-lanes - data-lanes @@ -174,6 +184,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - clock-lanes - data-lanes @@ -197,6 +212,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - clock-lanes - data-lanes @@ -220,6 +240,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - clock-lanes - data-lanes @@ -243,6 +268,11 @@ properties: minItems: 1 maxItems: 4 + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + required: - clock-lanes - data-lanes -- cgit v1.2.3-59-g8ed1b From 2ab7f87a7f4bf392e3836a2600f115a1baa1415c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 14 Mar 2025 13:14:00 +0000 Subject: dt-bindings: media: Add qcom,x1e80100-camss Add bindings for qcom,x1e80100-camss in order to support the camera subsystem for x1e80100 as found in various Co-Pilot laptops. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Vladimir Zapolskiy Signed-off-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- .../bindings/media/qcom,x1e80100-camss.yaml | 367 +++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml diff --git a/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml b/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml new file mode 100644 index 000000000000..113565cf2a99 --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml @@ -0,0 +1,367 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/qcom,x1e80100-camss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm X1E80100 Camera Subsystem (CAMSS) + +maintainers: + - Bryan O'Donoghue + +description: + The CAMSS IP is a CSI decoder and ISP present on Qualcomm platforms. + +properties: + compatible: + const: qcom,x1e80100-camss + + reg: + maxItems: 17 + + reg-names: + items: + - const: csid0 + - const: csid1 + - const: csid2 + - const: csid_lite0 + - const: csid_lite1 + - const: csid_wrapper + - const: csiphy0 + - const: csiphy1 + - const: csiphy2 + - const: csiphy4 + - const: csitpg0 + - const: csitpg1 + - const: csitpg2 + - const: vfe0 + - const: vfe1 + - const: vfe_lite0 + - const: vfe_lite1 + + clocks: + maxItems: 29 + + clock-names: + items: + - const: camnoc_nrt_axi + - const: camnoc_rt_axi + - const: core_ahb + - const: cpas_ahb + - const: cpas_fast_ahb + - const: cpas_vfe0 + - const: cpas_vfe1 + - const: cpas_vfe_lite + - const: cphy_rx_clk_src + - const: csid + - const: csid_csiphy_rx + - const: csiphy0 + - const: csiphy0_timer + - const: csiphy1 + - const: csiphy1_timer + - const: csiphy2 + - const: csiphy2_timer + - const: csiphy4 + - const: csiphy4_timer + - const: gcc_axi_hf + - const: gcc_axi_sf + - const: vfe0 + - const: vfe0_fast_ahb + - const: vfe1 + - const: vfe1_fast_ahb + - const: vfe_lite + - const: vfe_lite_ahb + - const: vfe_lite_cphy_rx + - const: vfe_lite_csid + + interrupts: + maxItems: 13 + + interrupt-names: + items: + - const: csid0 + - const: csid1 + - const: csid2 + - const: csid_lite0 + - const: csid_lite1 + - const: csiphy0 + - const: csiphy1 + - const: csiphy2 + - const: csiphy4 + - const: vfe0 + - const: vfe1 + - const: vfe_lite0 + - const: vfe_lite1 + + interconnects: + maxItems: 4 + + interconnect-names: + items: + - const: ahb + - const: hf_mnoc + - const: sf_mnoc + - const: sf_icp_mnoc + + iommus: + maxItems: 8 + + power-domains: + items: + - description: IFE0 GDSC - Image Front End, Global Distributed Switch Controller. + - description: IFE1 GDSC - Image Front End, Global Distributed Switch Controller. + - description: Titan Top GDSC - Titan ISP Block, Global Distributed Switch Controller. + + power-domain-names: + items: + - const: ife0 + - const: ife1 + - const: top + + vdd-csiphy-0p8-supply: + description: + Phandle to a 0.8V regulator supply to a PHY. + + vdd-csiphy-1p2-supply: + description: + Phandle to 1.8V regulator supply to a PHY. + + 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: + clock-lanes: + maxItems: 1 + + data-lanes: + minItems: 1 + maxItems: 4 + + required: + - clock-lanes + - data-lanes + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - interrupts + - interrupt-names + - interconnects + - interconnect-names + - iommus + - power-domains + - power-domain-names + - vdd-csiphy-0p8-supply + - vdd-csiphy-1p2-supply + - ports + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + camss: isp@acb6000 { + compatible = "qcom,x1e80100-camss"; + + reg = <0 0x0acb7000 0 0x2000>, + <0 0x0acb9000 0 0x2000>, + <0 0x0acbb000 0 0x2000>, + <0 0x0acc6000 0 0x1000>, + <0 0x0acca000 0 0x1000>, + <0 0x0acb6000 0 0x1000>, + <0 0x0ace4000 0 0x1000>, + <0 0x0ace6000 0 0x1000>, + <0 0x0ace8000 0 0x1000>, + <0 0x0acec000 0 0x4000>, + <0 0x0acf6000 0 0x1000>, + <0 0x0acf7000 0 0x1000>, + <0 0x0acf8000 0 0x1000>, + <0 0x0ac62000 0 0x4000>, + <0 0x0ac71000 0 0x4000>, + <0 0x0acc7000 0 0x2000>, + <0 0x0accb000 0 0x2000>; + + reg-names = "csid0", + "csid1", + "csid2", + "csid_lite0", + "csid_lite1", + "csid_wrapper", + "csiphy0", + "csiphy1", + "csiphy2", + "csiphy4", + "csitpg0", + "csitpg1", + "csitpg2", + "vfe0", + "vfe1", + "vfe_lite0", + "vfe_lite1"; + + clocks = <&camcc CAM_CC_CAMNOC_AXI_NRT_CLK>, + <&camcc CAM_CC_CAMNOC_AXI_RT_CLK>, + <&camcc CAM_CC_CORE_AHB_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CPAS_FAST_AHB_CLK>, + <&camcc CAM_CC_CPAS_IFE_0_CLK>, + <&camcc CAM_CC_CPAS_IFE_1_CLK>, + <&camcc CAM_CC_CPAS_IFE_LITE_CLK>, + <&camcc CAM_CC_CPHY_RX_CLK_SRC>, + <&camcc CAM_CC_CSID_CLK>, + <&camcc CAM_CC_CSID_CSIPHY_RX_CLK>, + <&camcc CAM_CC_CSIPHY0_CLK>, + <&camcc CAM_CC_CSI0PHYTIMER_CLK>, + <&camcc CAM_CC_CSIPHY1_CLK>, + <&camcc CAM_CC_CSI1PHYTIMER_CLK>, + <&camcc CAM_CC_CSIPHY2_CLK>, + <&camcc CAM_CC_CSI2PHYTIMER_CLK>, + <&camcc CAM_CC_CSIPHY4_CLK>, + <&camcc CAM_CC_CSI4PHYTIMER_CLK>, + <&gcc GCC_CAMERA_HF_AXI_CLK>, + <&gcc GCC_CAMERA_SF_AXI_CLK>, + <&camcc CAM_CC_IFE_0_CLK>, + <&camcc CAM_CC_IFE_0_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_1_CLK>, + <&camcc CAM_CC_IFE_1_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_LITE_CLK>, + <&camcc CAM_CC_IFE_LITE_AHB_CLK>, + <&camcc CAM_CC_IFE_LITE_CPHY_RX_CLK>, + <&camcc CAM_CC_IFE_LITE_CSID_CLK>; + + clock-names = "camnoc_nrt_axi", + "camnoc_rt_axi", + "core_ahb", + "cpas_ahb", + "cpas_fast_ahb", + "cpas_vfe0", + "cpas_vfe1", + "cpas_vfe_lite", + "cphy_rx_clk_src", + "csid", + "csid_csiphy_rx", + "csiphy0", + "csiphy0_timer", + "csiphy1", + "csiphy1_timer", + "csiphy2", + "csiphy2_timer", + "csiphy4", + "csiphy4_timer", + "gcc_axi_hf", + "gcc_axi_sf", + "vfe0", + "vfe0_fast_ahb", + "vfe1", + "vfe1_fast_ahb", + "vfe_lite", + "vfe_lite_ahb", + "vfe_lite_cphy_rx", + "vfe_lite_csid"; + + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + ; + + interrupt-names = "csid0", + "csid1", + "csid2", + "csid_lite0", + "csid_lite1", + "csiphy0", + "csiphy1", + "csiphy2", + "csiphy4", + "vfe0", + "vfe1", + "vfe_lite0", + "vfe_lite1"; + + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, + <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_CAMNOC_SF QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_CAMNOC_ICP QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + + interconnect-names = "ahb", + "hf_mnoc", + "sf_mnoc", + "sf_icp_mnoc"; + + iommus = <&apps_smmu 0x800 0x60>, + <&apps_smmu 0x860 0x60>, + <&apps_smmu 0x1800 0x60>, + <&apps_smmu 0x1860 0x60>, + <&apps_smmu 0x18e0 0x00>, + <&apps_smmu 0x1980 0x20>, + <&apps_smmu 0x1900 0x00>, + <&apps_smmu 0x19a0 0x20>; + + power-domains = <&camcc CAM_CC_IFE_0_GDSC>, + <&camcc CAM_CC_IFE_1_GDSC>, + <&camcc CAM_CC_TITAN_TOP_GDSC>; + + power-domain-names = "ife0", + "ife1", + "top"; + + vdd-csiphy-0p8-supply = <&csiphy_0p8_supply>; + vdd-csiphy-1p2-supply = <&csiphy_1p2_supply>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + csiphy_ep0: endpoint { + clock-lanes = <7>; + data-lanes = <0 1>; + remote-endpoint = <&sensor_ep>; + }; + }; + }; + }; + }; -- cgit v1.2.3-59-g8ed1b From bce4c094c27268b6148906a0ed43ff02a39522da Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Mon, 9 Dec 2024 13:01:06 +0100 Subject: media: qcom: camss: Restrict endpoint bus-type to D-PHY Currently the Qualcomm CAMSS driver only supports D-PHY while the hardware on most SoCs also supports C-PHY. Until this support is added, check for D-PHY to make it somewhat explicit that C-PHY won't work. Signed-off-by: Luca Weiss Tested-by: Vladimir Zapolskiy Reviewed-by: Vladimir Zapolskiy Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 6791dfea91b1..8fcfe06449bf 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -2663,6 +2663,15 @@ static int camss_of_parse_endpoint_node(struct device *dev, if (ret) return ret; + /* + * Most SoCs support both D-PHY and C-PHY standards, but currently only + * D-PHY is supported in the driver. + */ + if (vep.bus_type != V4L2_MBUS_CSI2_DPHY) { + dev_err(dev, "Unsupported bus type %d\n", vep.bus_type); + return -EINVAL; + } + csd->interface.csiphy_id = vep.base.port; mipi_csi2 = &vep.bus.mipi_csi2; -- cgit v1.2.3-59-g8ed1b From aef1d545989bc9e7f555af6b9f1be4963772192b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Apr 2025 10:51:25 +0200 Subject: media: qcom: camss: csid: suppress CSID log spam A recent commit refactored the printing of the CSID hardware version, but (without it being mentioned) also changed the log level from debug to info. This results in repeated log spam during use, for example, on the Lenovo ThinkPad X13s: qcom-camss ac5a000.camss: CSID:0 HW Version = 1.0.0 qcom-camss ac5a000.camss: CSID:0 HW Version = 1.0.0 qcom-camss ac5a000.camss: CSID:0 HW Version = 1.0.0 qcom-camss ac5a000.camss: CSID:0 HW Version = 1.0.0 qcom-camss ac5a000.camss: CSID:0 HW Version = 1.0.0 Suppress the version logging by demoting to debug level again. Fixes: f759b8fd3086 ("media: qcom: camss: csid: Move common code into csid core") Cc: stable@vger.kernel.org Cc: Depeng Shao Signed-off-by: Johan Hovold Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss-csid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index d08117f46f3b..5284b5857368 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -613,8 +613,8 @@ u32 csid_hw_version(struct csid_device *csid) hw_gen = (hw_version >> HW_VERSION_GENERATION) & 0xF; hw_rev = (hw_version >> HW_VERSION_REVISION) & 0xFFF; hw_step = (hw_version >> HW_VERSION_STEPPING) & 0xFFFF; - dev_info(csid->camss->dev, "CSID:%d HW Version = %u.%u.%u\n", - csid->id, hw_gen, hw_rev, hw_step); + dev_dbg(csid->camss->dev, "CSID:%d HW Version = %u.%u.%u\n", + csid->id, hw_gen, hw_rev, hw_step); return hw_version; } -- cgit v1.2.3-59-g8ed1b From b6fafb3941fa0f065def304d44d6c3c6d6ac0f64 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 7 Apr 2025 12:48:28 +0200 Subject: media: qcom: camss: vfe: suppress VFE version log spam A recent commit refactored the printing of the VFE hardware version, but (without it being mentioned) also changed the log level from debug to info. This results in several hundred lines of repeated log spam during boot and use, for example, on the Lenovo ThinkPad X13s: qcom-camss ac5a000.camss: VFE:1 HW Version = 1.2.2 qcom-camss ac5a000.camss: VFE:0 HW Version = 1.2.2 qcom-camss ac5a000.camss: VFE:2 HW Version = 1.2.2 qcom-camss ac5a000.camss: VFE:2 HW Version = 1.2.2 qcom-camss ac5a000.camss: VFE:3 HW Version = 1.2.2 qcom-camss ac5a000.camss: VFE:5 HW Version = 1.3.0 qcom-camss ac5a000.camss: VFE:6 HW Version = 1.3.0 qcom-camss ac5a000.camss: VFE:4 HW Version = 1.3.0 qcom-camss ac5a000.camss: VFE:5 HW Version = 1.3.0 qcom-camss ac5a000.camss: VFE:6 HW Version = 1.3.0 qcom-camss ac5a000.camss: VFE:7 HW Version = 1.3.0 qcom-camss ac5a000.camss: VFE:7 HW Version = 1.3.0 qcom-camss ac5a000.camss: VFE:7 HW Version = 1.3.0 ... Suppress the version logging by demoting to debug level again. Fixes: 10693fed125d ("media: qcom: camss: vfe: Move common code into vfe core") Cc: stable@vger.kernel.org Cc: Depeng Shao Reviewed-by: Bryan O'Donoghue Signed-off-by: Johan Hovold Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss-vfe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index cf0e8f5c004a..91bc0cb7781e 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -428,8 +428,8 @@ u32 vfe_hw_version(struct vfe_device *vfe) u32 rev = (hw_version >> HW_VERSION_REVISION) & 0xFFF; u32 step = (hw_version >> HW_VERSION_STEPPING) & 0xFFFF; - dev_info(vfe->camss->dev, "VFE:%d HW Version = %u.%u.%u\n", - vfe->id, gen, rev, step); + dev_dbg(vfe->camss->dev, "VFE:%d HW Version = %u.%u.%u\n", + vfe->id, gen, rev, step); return hw_version; } -- cgit v1.2.3-59-g8ed1b From 5af908c2c93d5bfeec72125901aff991e5268015 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 14 Mar 2025 23:35:54 +0000 Subject: media: qcom: camss: Add an id property to struct resources In various places in CAMSS we assume a linear set of declared devices {csiphy0, csiphy1, csiphy2} which currently works for upstream SoCs but for upcoming SoCs some of the SoC resources will result in a set such as {csiphy0, csiphy2} which will break the naive for() loops we have. Introduce an identity property which resource declarations can populate hence facilitating non-linear resource naming. Reviewed-by: Vladimir Zapolskiy Signed-off-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss-csiphy.h | 1 + drivers/media/platform/qcom/camss/camss.c | 41 +++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index 86b98b37838e..ab91273303b9 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -81,6 +81,7 @@ struct csiphy_hw_ops { }; struct csiphy_subdev_resources { + u8 id; const struct csiphy_hw_ops *hw_ops; const struct csiphy_formats *formats; }; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 8fcfe06449bf..d7e0e802852a 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -46,6 +46,7 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = { .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, .csiphy = { + .id = 0, .hw_ops = &csiphy_ops_2ph_1_0, .formats = &csiphy_formats_8x16 } @@ -62,6 +63,7 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = { .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, .csiphy = { + .id = 1, .hw_ops = &csiphy_ops_2ph_1_0, .formats = &csiphy_formats_8x16 } @@ -318,6 +320,7 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, .csiphy = { + .id = 0, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_8x96 } @@ -334,6 +337,7 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, .csiphy = { + .id = 1, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_8x96 } @@ -350,6 +354,7 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { .reg = { "csiphy2", "csiphy2_clk_mux" }, .interrupt = { "csiphy2" }, .csiphy = { + .id = 2, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_8x96 } @@ -524,6 +529,7 @@ static const struct camss_subdev_resources csiphy_res_660[] = { .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, .csiphy = { + .id = 0, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_8x96 } @@ -542,6 +548,7 @@ static const struct camss_subdev_resources csiphy_res_660[] = { .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, .csiphy = { + .id = 1, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_8x96 } @@ -560,6 +567,7 @@ static const struct camss_subdev_resources csiphy_res_660[] = { .reg = { "csiphy2", "csiphy2_clk_mux" }, .interrupt = { "csiphy2" }, .csiphy = { + .id = 2, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_8x96 } @@ -751,6 +759,7 @@ static const struct camss_subdev_resources csiphy_res_670[] = { .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, .csiphy = { + .id = 0, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -768,6 +777,7 @@ static const struct camss_subdev_resources csiphy_res_670[] = { .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, .csiphy = { + .id = 1, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -785,6 +795,7 @@ static const struct camss_subdev_resources csiphy_res_670[] = { .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, .csiphy = { + .id = 2, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -935,6 +946,7 @@ static const struct camss_subdev_resources csiphy_res_845[] = { .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, .csiphy = { + .id = 0, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -957,6 +969,7 @@ static const struct camss_subdev_resources csiphy_res_845[] = { .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, .csiphy = { + .id = 1, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -979,6 +992,7 @@ static const struct camss_subdev_resources csiphy_res_845[] = { .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, .csiphy = { + .id = 2, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -1001,6 +1015,7 @@ static const struct camss_subdev_resources csiphy_res_845[] = { .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, .csiphy = { + .id = 3, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -1179,6 +1194,7 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, .csiphy = { + .id = 0, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -1192,6 +1208,7 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, .csiphy = { + .id = 1, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -1205,6 +1222,7 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, .csiphy = { + .id = 2, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -1218,6 +1236,7 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, .csiphy = { + .id = 3, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -1231,6 +1250,7 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { .reg = { "csiphy4" }, .interrupt = { "csiphy4" }, .csiphy = { + .id = 4, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -1244,6 +1264,7 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { .reg = { "csiphy5" }, .interrupt = { "csiphy5" }, .csiphy = { + .id = 5, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -1458,6 +1479,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = { .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, .csiphy = { + .id = 0, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sc7280 } @@ -1472,6 +1494,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = { .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, .csiphy = { + .id = 1, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sc7280 } @@ -1486,6 +1509,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = { .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, .csiphy = { + .id = 2, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sc7280 } @@ -1500,6 +1524,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = { .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, .csiphy = { + .id = 3, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sc7280 } @@ -1514,6 +1539,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = { .reg = { "csiphy4" }, .interrupt = { "csiphy4" }, .csiphy = { + .id = 4, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sc7280 } @@ -1766,6 +1792,7 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, .csiphy = { + .id = 0, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -1779,6 +1806,7 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, .csiphy = { + .id = 1, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -1792,6 +1820,7 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, .csiphy = { + .id = 2, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -1805,6 +1834,7 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, .csiphy = { + .id = 3, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -2134,6 +2164,7 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, .csiphy = { + .id = 0, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -2147,6 +2178,7 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, .csiphy = { + .id = 1, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -2160,6 +2192,7 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, .csiphy = { + .id = 2, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -2173,6 +2206,7 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, .csiphy = { + .id = 3, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -2186,6 +2220,7 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { .reg = { "csiphy4" }, .interrupt = { "csiphy4" }, .csiphy = { + .id = 4, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -2199,6 +2234,7 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { .reg = { "csiphy5" }, .interrupt = { "csiphy5" }, .csiphy = { + .id = 5, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -2212,6 +2248,7 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { .reg = { "csiphy6" }, .interrupt = { "csiphy6" }, .csiphy = { + .id = 6, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -2225,6 +2262,7 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { .reg = { "csiphy7" }, .interrupt = { "csiphy7" }, .csiphy = { + .id = 7, .hw_ops = &csiphy_ops_3ph_1_0, .formats = &csiphy_formats_sdm845 } @@ -2758,7 +2796,8 @@ static int camss_init_subdevices(struct camss *camss) for (i = 0; i < camss->res->csiphy_num; i++) { ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i], - &res->csiphy_res[i], i); + &res->csiphy_res[i], + res->csiphy_res[i].csiphy.id); if (ret < 0) { dev_err(camss->dev, "Failed to init csiphy%d sub-device: %d\n", -- cgit v1.2.3-59-g8ed1b From 74cae7794341fa4f96fef0966f44471f7e2d322f Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 14 Mar 2025 23:35:55 +0000 Subject: media: qcom: camss: Use the CSIPHY id property to find clock names Use the CSIPHY id property to find clock names instead of relying on generating the clock names based on the control-loop index. x1e80100 has CSIPHY0, CSIPHY1, CSIPHY2 and CSIPHY4 so simple index naming won't work whereas and 'id' property allows any ordering and any stepping between the CSIPHY names. Reviewed-by: Vladimir Zapolskiy Signed-off-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss-csiphy.c | 28 +++++++++++------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index c053616558a7..c622efcc92ff 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -586,7 +586,7 @@ int msm_csiphy_subdev_init(struct camss *camss, { struct device *dev = camss->dev; struct platform_device *pdev = to_platform_device(dev); - int i, j, k; + int i, j; int ret; csiphy->camss = camss; @@ -680,23 +680,21 @@ int msm_csiphy_subdev_init(struct camss *camss, for (j = 0; j < clock->nfreqs; j++) clock->freq[j] = res->clock_rate[i][j]; - for (k = 0; k < camss->res->csiphy_num; k++) { - csiphy->rate_set[i] = csiphy_match_clock_name(clock->name, - "csiphy%d_timer", k); - if (csiphy->rate_set[i]) - break; - - if (camss->res->version == CAMSS_660) { - csiphy->rate_set[i] = csiphy_match_clock_name(clock->name, - "csi%d_phy", k); - if (csiphy->rate_set[i]) - break; - } + csiphy->rate_set[i] = csiphy_match_clock_name(clock->name, + "csiphy%d_timer", + csiphy->id); + if (csiphy->rate_set[i]) + continue; - csiphy->rate_set[i] = csiphy_match_clock_name(clock->name, "csiphy%d", k); + if (camss->res->version == CAMSS_660) { + csiphy->rate_set[i] = csiphy_match_clock_name(clock->name, + "csi%d_phy", + csiphy->id); if (csiphy->rate_set[i]) - break; + continue; } + + csiphy->rate_set[i] = csiphy_match_clock_name(clock->name, "csiphy%d", csiphy->id); } /* CSIPHY supplies */ -- cgit v1.2.3-59-g8ed1b From 253314b20408545c31499d7c97072cc7a30612a2 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 14 Mar 2025 23:35:56 +0000 Subject: media: qcom: camss: Add CSID 680 support Add CSI Decoder (CSID) 680 support to CAMSS. This version of CSID has been shipped with SM8450 and x1e chips. References work from Vladimir Zapolskiy Signed-off-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/Makefile | 1 + drivers/media/platform/qcom/camss/camss-csid-680.c | 422 +++++++++++++++++++++ drivers/media/platform/qcom/camss/camss-csid.h | 1 + 3 files changed, 424 insertions(+) create mode 100644 drivers/media/platform/qcom/camss/camss-csid-680.c diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index f6db5b3b5ace..71797745f2f7 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -6,6 +6,7 @@ qcom-camss-objs += \ camss-csid.o \ camss-csid-4-1.o \ camss-csid-4-7.o \ + camss-csid-680.o \ camss-csid-gen2.o \ camss-csid-780.o \ camss-csiphy-2ph-1-0.o \ diff --git a/drivers/media/platform/qcom/camss/camss-csid-680.c b/drivers/media/platform/qcom/camss/camss-csid-680.c new file mode 100644 index 000000000000..3ad3a174bcfb --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csid-680.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module + * + * Copyright (C) 2020-2025 Linaro Ltd. + */ +#include +#include +#include +#include + +#include "camss.h" +#include "camss-csid.h" +#include "camss-csid-gen2.h" + +#define CSID_TOP_IO_PATH_CFG0(csid) (0x4 * (csid)) +#define CSID_TOP_IO_PATH_CFG0_INTERNAL_CSID BIT(0) +#define CSID_TOP_IO_PATH_CFG0_SFE_0 BIT(1) +#define CSID_TOP_IO_PATH_CFG0_SFE_1 GENMASK(1, 0) +#define CSID_TOP_IO_PATH_CFG0_SBI_0 BIT(4) +#define CSID_TOP_IO_PATH_CFG0_SBI_1 GENMASK(3, 0) +#define CSID_TOP_IO_PATH_CFG0_SBI_2 GENMASK(3, 1) +#define CSID_TOP_IO_PATH_CFG0_OUTPUT_IFE_EN BIT(8) +#define CSID_TOP_IO_PATH_CFG0_SFE_OFFLINE_EN BIT(12) + +#define CSID_RESET_CMD 0x10 +#define CSID_RESET_CMD_HW_RESET BIT(0) +#define CSID_RESET_CMD_SW_RESET BIT(1) +#define CSID_RESET_CMD_IRQ_CTRL BIT(2) + +#define CSID_IRQ_CMD 0x14 +#define CSID_IRQ_CMD_CLEAR BIT(0) +#define CSID_IRQ_CMD_SET BIT(4) + +#define CSID_REG_UPDATE_CMD 0x18 + +#define CSID_CSI2_RDIN_IRQ_STATUS(rdi) (0xec + 0x10 * (rdi)) +#define CSID_CSI2_RDIN_CCIF_VIOLATION BIT(29) +#define CSID_CSI2_RDIN_SENSOR_SWITCH_OUT_OF_SYNC_FRAME_DROP BIT(28) +#define CSID_CSI2_RDIN_ERROR_REC_WIDTH_VIOLATION BIT(27) +#define CSID_CSI2_RDIN_ERROR_REC_HEIGHT_VIOLATION BIT(26) +#define CSID_CSI2_RDIN_BATCH_END_MISSING_VIOLATION BIT(25) +#define CSID_CSI2_RDIN_ILLEGAL_BATCH_ID_IRQ BIT(24) +#define CSID_CSI2_RDIN_RUP_DONE BIT(23) +#define CSID_CSI2_RDIN_CAMIF_EPOCH_1_IRQ BIT(22) +#define CSID_CSI2_RDIN_CAMIF_EPOCH_0_IRQ BIT(21) +#define CSID_CSI2_RDIN_ERROR_REC_OVERFLOW_IRQ BIT(19) +#define CSID_CSI2_RDIN_ERROR_REC_FRAME_DROP BIT(18) +#define CSID_CSI2_RDIN_VCDT_GRP_CHANG BIT(17) +#define CSID_CSI2_RDIN_VCDT_GRP_0_SEL BIT(16) +#define CSID_CSI2_RDIN_VCDT_GRP_1_SEL BIT(15) +#define CSID_CSI2_RDIN_ERROR_LINE_COUNT BIT(14) +#define CSID_CSI2_RDIN_ERROR_PIX_COUNT BIT(13) +#define CSID_CSI2_RDIN_INFO_INPUT_SOF BIT(12) +#define CSID_CSI2_RDIN_INFO_INPUT_SOL BIT(11) +#define CSID_CSI2_RDIN_INFO_INPUT_EOL BIT(10) +#define CSID_CSI2_RDIN_INFO_INPUT_EOF BIT(9) +#define CSID_CSI2_RDIN_INFO_FRAME_DROP_SOF BIT(8) +#define CSID_CSI2_RDIN_INFO_FRAME_DROP_SOL BIT(7) +#define CSID_CSI2_RDIN_INFO_FRAME_DROP_EOL BIT(6) +#define CSID_CSI2_RDIN_INFO_FRAME_DROP_EOF BIT(5) +#define CSID_CSI2_RDIN_INFO_CAMIF_SOF BIT(4) +#define CSID_CSI2_RDIN_INFO_CAMIF_EOF BIT(3) +#define CSID_CSI2_RDIN_INFO_FIFO_OVERFLOW BIT(2) +#define CSID_CSI2_RDIN_RES1 BIT(1) +#define CSID_CSI2_RDIN_RES0 BIT(0) + +#define CSID_CSI2_RDIN_IRQ_MASK(rdi) (0xf0 + 0x10 * (rdi)) +#define CSID_CSI2_RDIN_IRQ_CLEAR(rdi) (0xf4 + 0x10 * (rdi)) +#define CSID_CSI2_RDIN_IRQ_SET(rdi) (0xf8 + 0x10 * (rdi)) + +#define CSID_TOP_IRQ_STATUS 0x7c +#define CSID_TOP_IRQ_MASK 0x80 +#define CSID_TOP_IRQ_CLEAR 0x84 +#define CSID_TOP_IRQ_RESET BIT(0) +#define CSID_TOP_IRQ_RX BIT(2) +#define CSID_TOP_IRQ_LONG_PKT(rdi) (BIT(8) << (rdi)) +#define CSID_TOP_IRQ_BUF_DONE BIT(13) + +#define CSID_BUF_DONE_IRQ_STATUS 0x8c +#define BUF_DONE_IRQ_STATUS_RDI_OFFSET (csid_is_lite(csid) ? 1 : 14) +#define CSID_BUF_DONE_IRQ_MASK 0x90 +#define CSID_BUF_DONE_IRQ_CLEAR 0x94 + +#define CSID_CSI2_RX_IRQ_STATUS 0x9c +#define CSID_CSI2_RX_IRQ_MASK 0xa0 +#define CSID_CSI2_RX_IRQ_CLEAR 0xa4 + +#define CSID_RESET_CFG 0xc +#define CSID_RESET_CFG_MODE_IMMEDIATE BIT(0) +#define CSID_RESET_CFG_LOCATION_COMPLETE BIT(4) + +#define CSID_CSI2_RDI_IRQ_STATUS(rdi) (0xec + 0x10 * (rdi)) +#define CSID_CSI2_RDI_IRQ_MASK(rdi) (0xf0 + 0x10 * (rdi)) +#define CSID_CSI2_RDI_IRQ_CLEAR(rdi) (0xf4 + 0x10 * (rdi)) + +#define CSID_CSI2_RX_CFG0 0x200 +#define CSI2_RX_CFG0_NUM_ACTIVE_LANES 0 +#define CSI2_RX_CFG0_DL0_INPUT_SEL 4 +#define CSI2_RX_CFG0_DL1_INPUT_SEL 8 +#define CSI2_RX_CFG0_DL2_INPUT_SEL 12 +#define CSI2_RX_CFG0_DL3_INPUT_SEL 16 +#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 CSID_CSI2_RX_CFG1 0x204 +#define CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN BIT(0) +#define CSI2_RX_CFG1_DE_SCRAMBLE_EN BIT(1) +#define CSI2_RX_CFG1_VC_MODE BIT(2) +#define CSI2_RX_CFG1_COMPLETE_STREAM_EN BIT(4) +#define CSI2_RX_CFG1_COMPLETE_STREAM_FRAME_TIMING BIT(5) +#define CSI2_RX_CFG1_MISR_EN BIT(6) +#define CSI2_RX_CFG1_CGC_MODE BIT(7) + +#define CSID_CSI2_RX_CAPTURE_CTRL 0x208 +#define CSI2_RX_CAPTURE_CTRL_LONG_PKT_EN BIT(0) +#define CSI2_RX_CAPTURE_CTRL_SHORT_PKT_EN BIT(1) +#define CSI2_RX_CAPTURE_CTRL_CPHY_PKT_EN BIT(2) +#define CSI2_RX_CAPTURE_CTRL_LONG_PKT_DT GENMASK(9, 4) +#define CSI2_RX_CAPTURE_CTRL_LONG_PKT_VC GENMASK(14, 10) +#define CSI2_RX_CAPTURE_CTRL_SHORT_PKT_VC GENMASK(19, 15) +#define CSI2_RX_CAPTURE_CTRL_CPHY_PKT_DT GENMASK(20, 25) +#define CSI2_RX_CAPTURE_CTRL_CPHY_PKT_VC GENMASK(30, 26) + +#define CSID_CSI2_RX_TOTAL_PKTS_RCVD 0x240 +#define CSID_CSI2_RX_STATS_ECC 0x244 +#define CSID_CSI2_RX_CRC_ERRORS 0x248 + +#define CSID_RDI_CFG0(rdi) (0x500 + 0x100 * (rdi)) +#define RDI_CFG0_DECODE_FORMAT 12 +#define RDI_CFG0_DATA_TYPE 16 +#define RDI_CFG0_VIRTUAL_CHANNEL 22 +#define RDI_CFG0_DT_ID 27 +#define RDI_CFG0_ENABLE BIT(31) + +#define CSID_RDI_CTRL(rdi) (0x504 + 0x100 * (rdi)) +#define CSID_RDI_CTRL_HALT_CMD_HALT_AT_FRAME_BOUNDARY 0 +#define CSID_RDI_CTRL_HALT_CMD_RESUME_AT_FRAME_BOUNDARY 1 + +#define CSID_RDI_CFG1(rdi) (0x510 + 0x100 * (rdi)) +#define RDI_CFG1_TIMESTAMP_STB_FRAME BIT(0) +#define RDI_CFG1_TIMESTAMP_STB_IRQ BIT(1) +#define RDI_CFG1_BYTE_CNTR_EN BIT(2) +#define RDI_CFG1_TIMESTAMP_EN BIT(4) +#define RDI_CFG1_DROP_H_EN BIT(5) +#define RDI_CFG1_DROP_V_EN BIT(6) +#define RDI_CFG1_CROP_H_EN BIT(7) +#define RDI_CFG1_CROP_V_EN BIT(8) +#define RDI_CFG1_MISR_EN BIT(9) +#define RDI_CFG1_PLAIN_ALIGN_MSB BIT(11) +#define RDI_CFG1_EARLY_EOF_EN BIT(14) +#define RDI_CFG1_PACKING_MIPI BIT(15) + +#define CSID_RDI_ERR_RECOVERY_CFG0(rdi) (0x514 + 0x100 * (rdi)) +#define CSID_RDI_EPOCH_IRQ_CFG(rdi) (0x52c + 0x100 * (rdi)) +#define CSID_RDI_FRM_DROP_PATTERN(rdi) (0x540 + 0x100 * (rdi)) +#define CSID_RDI_FRM_DROP_PERIOD(rdi) (0x544 + 0x100 * (rdi)) +#define CSID_RDI_IRQ_SUBSAMPLE_PATTERN(rdi) (0x548 + 0x100 * (rdi)) +#define CSID_RDI_IRQ_SUBSAMPLE_PERIOD(rdi) (0x54c + 0x100 * (rdi)) +#define CSID_RDI_PIX_DROP_PATTERN(rdi) (0x558 + 0x100 * (rdi)) +#define CSID_RDI_PIX_DROP_PERIOD(rdi) (0x55c + 0x100 * (rdi)) +#define CSID_RDI_LINE_DROP_PATTERN(rdi) (0x560 + 0x100 * (rdi)) +#define CSID_RDI_LINE_DROP_PERIOD(rdi) (0x564 + 0x100 * (rdi)) + +static inline int reg_update_rdi(struct csid_device *csid, int n) +{ + return BIT(4 + n) + BIT(20 + n); +} + +static void csid_reg_update(struct csid_device *csid, int port_id) +{ + csid->reg_update |= reg_update_rdi(csid, port_id); + writel(csid->reg_update, csid->base + CSID_REG_UPDATE_CMD); +} + +static inline void csid_reg_update_clear(struct csid_device *csid, + int port_id) +{ + csid->reg_update &= ~reg_update_rdi(csid, port_id); + writel(csid->reg_update, csid->base + CSID_REG_UPDATE_CMD); +} + +static void __csid_configure_rx(struct csid_device *csid, + struct csid_phy_config *phy, int vc) +{ + u32 val; + + 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; + + writel(val, csid->base + CSID_CSI2_RX_CFG0); + + val = CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN; + if (vc > 3) + val |= CSI2_RX_CFG1_VC_MODE; + writel(val, csid->base + CSID_CSI2_RX_CFG1); +} + +static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) +{ + u32 val; + + if (enable) + val = CSID_RDI_CTRL_HALT_CMD_RESUME_AT_FRAME_BOUNDARY; + else + val = CSID_RDI_CTRL_HALT_CMD_HALT_AT_FRAME_BOUNDARY; + + writel(val, csid->base + CSID_RDI_CTRL(rdi)); +} + +static void __csid_configure_top(struct csid_device *csid) +{ + u32 val; + + val = CSID_TOP_IO_PATH_CFG0_OUTPUT_IFE_EN | CSID_TOP_IO_PATH_CFG0_INTERNAL_CSID; + writel(val, csid->camss->csid_wrapper_base + + CSID_TOP_IO_PATH_CFG0(csid->id)); +} + +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +{ + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, + input_format->code); + u8 lane_cnt = csid->phy.lane_cnt; + u8 dt_id; + u32 val; + + if (!lane_cnt) + lane_cnt = 4; + + val = 0; + writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + + /* + * 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) + * DT_ID : 28:27 + * VC : 26:22 + * DT : 21:16 + * + * CID : VC 3:0 << 2 | DT_ID 1:0 + */ + dt_id = vc & 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)); + + val = RDI_CFG1_TIMESTAMP_STB_FRAME; + val |= RDI_CFG1_BYTE_CNTR_EN; + val |= RDI_CFG1_TIMESTAMP_EN; + val |= RDI_CFG1_DROP_H_EN; + val |= RDI_CFG1_DROP_V_EN; + val |= RDI_CFG1_CROP_H_EN; + val |= RDI_CFG1_CROP_V_EN; + val |= RDI_CFG1_PACKING_MIPI; + + writel(val, csid->base + CSID_RDI_CFG1(vc)); + + val = 0; + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + + val = 1; + writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + + val = 0; + writel(val, csid->base + CSID_RDI_CTRL(vc)); + + val = readl(csid->base + CSID_RDI_CFG0(vc)); + if (enable) + val |= RDI_CFG0_ENABLE; + else + val &= ~RDI_CFG0_ENABLE; + writel(val, csid->base + CSID_RDI_CFG0(vc)); +} + +static void csid_configure_stream(struct csid_device *csid, u8 enable) +{ + int i; + + __csid_configure_top(csid); + + /* Loop through all enabled VCs and configure 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_ctrl_rdi(csid, enable, i); + } + } +} + +/* + * csid_reset - Trigger reset on CSID module and wait to complete + * @csid: CSID device + * + * Return 0 on success or a negative error code otherwise + */ +static int csid_reset(struct csid_device *csid) +{ + unsigned long time; + u32 val; + int i; + + reinit_completion(&csid->reset_complete); + + writel(CSID_IRQ_CMD_CLEAR, csid->base + CSID_IRQ_CMD); + + /* preserve registers */ + val = CSID_RESET_CFG_MODE_IMMEDIATE | CSID_RESET_CFG_LOCATION_COMPLETE; + writel(val, csid->base + CSID_RESET_CFG); + + val = CSID_RESET_CMD_HW_RESET | CSID_RESET_CMD_SW_RESET; + writel(val, csid->base + CSID_RESET_CMD); + + time = wait_for_completion_timeout(&csid->reset_complete, + msecs_to_jiffies(CSID_RESET_TIMEOUT_MS)); + if (!time) { + dev_err(csid->camss->dev, "CSID reset timeout\n"); + return -EIO; + } + + for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { + /* Enable RUP done for the client port */ + writel(CSID_CSI2_RDIN_RUP_DONE, csid->base + CSID_CSI2_RDIN_IRQ_MASK(i)); + } + + /* Clear RDI status */ + writel(~0u, csid->base + CSID_BUF_DONE_IRQ_CLEAR); + + /* Enable BUF_DONE bit for all write-master client ports */ + writel(~0u, csid->base + CSID_BUF_DONE_IRQ_MASK); + + /* Unmask all TOP interrupts */ + writel(~0u, csid->base + CSID_TOP_IRQ_MASK); + + return 0; +} + +static void csid_rup_complete(struct csid_device *csid, int rdi) +{ + csid_reg_update_clear(csid, rdi); +} + +/* + * csid_isr - CSID module interrupt service routine + * @irq: Interrupt line + * @dev: CSID device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t csid_isr(int irq, void *dev) +{ + struct csid_device *csid = dev; + u32 buf_done_val, val, val_top; + int i; + + /* Latch and clear TOP status */ + val_top = readl(csid->base + CSID_TOP_IRQ_STATUS); + writel(val_top, csid->base + CSID_TOP_IRQ_CLEAR); + + /* Latch and clear CSID_CSI2 status */ + val = readl(csid->base + CSID_CSI2_RX_IRQ_STATUS); + writel(val, csid->base + CSID_CSI2_RX_IRQ_CLEAR); + + /* Latch and clear top level BUF_DONE status */ + buf_done_val = readl(csid->base + CSID_BUF_DONE_IRQ_STATUS); + writel(buf_done_val, csid->base + CSID_BUF_DONE_IRQ_CLEAR); + + /* Process state for each RDI channel */ + for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { + val = readl(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(i)); + if (val) + writel(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(i)); + + if (val & CSID_CSI2_RDIN_RUP_DONE) + csid_rup_complete(csid, i); + + if (buf_done_val & BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + i)) + camss_buf_done(csid->camss, csid->id, i); + } + + /* Issue clear command */ + writel(CSID_IRQ_CMD_CLEAR, csid->base + CSID_IRQ_CMD); + + /* Reset complete */ + if (val_top & CSID_TOP_IRQ_RESET) + complete(&csid->reset_complete); + + return IRQ_HANDLED; +} + +static void csid_subdev_reg_update(struct csid_device *csid, int port_id, bool is_clear) +{ + if (is_clear) + csid_reg_update_clear(csid, port_id); + else + csid_reg_update(csid, port_id); +} + +static void csid_subdev_init(struct csid_device *csid) {} + +const struct csid_hw_ops csid_ops_680 = { + .configure_testgen_pattern = NULL, + .configure_stream = csid_configure_stream, + .hw_version = csid_hw_version, + .isr = csid_isr, + .reset = csid_reset, + .src_pad_code = csid_src_pad_code, + .subdev_init = csid_subdev_init, + .reg_update = csid_subdev_reg_update, +}; diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index 90b8fc5852be..9dc826d8c8f6 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -213,6 +213,7 @@ extern const struct csid_formats csid_formats_gen2; extern const struct csid_hw_ops csid_ops_4_1; extern const struct csid_hw_ops csid_ops_4_7; +extern const struct csid_hw_ops csid_ops_680; extern const struct csid_hw_ops csid_ops_gen2; extern const struct csid_hw_ops csid_ops_780; -- cgit v1.2.3-59-g8ed1b From 727970e9725c35366f501a890584e466ae34224a Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 14 Mar 2025 23:35:57 +0000 Subject: media: qcom: camss: Add VFE680 support Add silicon enabling support for VFE680 as found on sm8450, x1e and derivatives thereof. References work from Vladimir Zapolskiy Signed-off-by: Bryan O'Donoghue [bod: fix minor checkpatch linelenght splat @ lines 21, 22] Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/Makefile | 1 + drivers/media/platform/qcom/camss/camss-vfe-680.c | 244 ++++++++++++++++++++++ drivers/media/platform/qcom/camss/camss-vfe.h | 1 + 3 files changed, 246 insertions(+) create mode 100644 drivers/media/platform/qcom/camss/camss-vfe-680.c diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index 71797745f2f7..d26a9c24a430 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -18,6 +18,7 @@ qcom-camss-objs += \ camss-vfe-4-8.o \ camss-vfe-17x.o \ camss-vfe-480.o \ + camss-vfe-680.o \ camss-vfe-780.o \ camss-vfe-gen1.o \ camss-vfe.o \ diff --git a/drivers/media/platform/qcom/camss/camss-vfe-680.c b/drivers/media/platform/qcom/camss/camss-vfe-680.c new file mode 100644 index 000000000000..99036e7c1e76 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe-680.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-vfe-680.c + * + * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v680 + * + * Copyright (C) 2025 Linaro Ltd. + */ + +#include +#include +#include +#include + +#include "camss.h" +#include "camss-vfe.h" + +#define VFE_TOP_IRQn_STATUS(vfe, n) ((vfe_is_lite(vfe) ? 0x1c : 0x44) + (n) * 4) +#define VFE_TOP_IRQn_MASK(vfe, n) ((vfe_is_lite(vfe) ? 0x24 : 0x34) + (n) * 4) +#define VFE_TOP_IRQn_CLEAR(vfe, n) ((vfe_is_lite(vfe) ? 0x2c : 0x3c) + (n) * 4) +#define VFE_IRQ1_SOF(vfe, n) ((vfe_is_lite(vfe) ? BIT(2) : BIT(8)) << ((n) * 2)) +#define VFE_IRQ1_EOF(vfe, n) ((vfe_is_lite(vfe) ? BIT(3) : BIT(9)) << ((n) * 2)) +#define VFE_TOP_IRQ_CMD(vfe) (vfe_is_lite(vfe) ? 0x38 : 0x30) +#define VFE_TOP_IRQ_CMD_GLOBAL_CLEAR BIT(0) +#define VFE_TOP_DIAG_CONFIG (vfe_is_lite(vfe) ? 0x40 : 0x50) + +#define VFE_TOP_DEBUG_11(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xcc) +#define VFE_TOP_DEBUG_12(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xd0) +#define VFE_TOP_DEBUG_13(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xd4) + +#define VFE_BUS_IRQn_MASK(vfe, n) ((vfe_is_lite(vfe) ? 0x218 : 0xc18) + (n) * 4) +#define VFE_BUS_IRQn_CLEAR(vfe, n) ((vfe_is_lite(vfe) ? 0x220 : 0xc20) + (n) * 4) +#define VFE_BUS_IRQn_STATUS(vfe, n) ((vfe_is_lite(vfe) ? 0x228 : 0xc28) + (n) * 4) +#define VFE_BUS_IRQ_GLOBAL_CLEAR(vfe) (vfe_is_lite(vfe) ? 0x230 : 0xc30) +#define VFE_BUS_WR_VIOLATION_STATUS(vfe) (vfe_is_lite(vfe) ? 0x264 : 0xc64) +#define VFE_BUS_WR_OVERFLOW_STATUS(vfe) (vfe_is_lite(vfe) ? 0x268 : 0xc68) +#define VFE_BUS_WR_IMAGE_VIOLATION_STATUS(vfe) (vfe_is_lite(vfe) ? 0x270 : 0xc70) + +#define VFE_BUS_WRITE_CLIENT_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x400 : 0xe00) + (c) * 0x100) +#define VFE_BUS_WRITE_CLIENT_CFG_EN BIT(0) +#define VFE_BUS_IMAGE_ADDR(vfe, c) ((vfe_is_lite(vfe) ? 0x404 : 0xe04) + (c) * 0x100) +#define VFE_BUS_FRAME_INCR(vfe, c) ((vfe_is_lite(vfe) ? 0x408 : 0xe08) + (c) * 0x100) +#define VFE_BUS_IMAGE_CFG0(vfe, c) ((vfe_is_lite(vfe) ? 0x40c : 0xe0c) + (c) * 0x100) +#define VFE_BUS_IMAGE_CFG0_DATA(h, s) (((h) << 16) | ((s) >> 4)) +#define WM_IMAGE_CFG_0_DEFAULT_WIDTH (0xFFFF) + +#define VFE_BUS_IMAGE_CFG1(vfe, c) ((vfe_is_lite(vfe) ? 0x410 : 0xe10) + (c) * 0x100) +#define VFE_BUS_IMAGE_CFG2(vfe, c) ((vfe_is_lite(vfe) ? 0x414 : 0xe14) + (c) * 0x100) +#define VFE_BUS_PACKER_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x418 : 0xe18) + (c) * 0x100) +#define VFE_BUS_IRQ_SUBSAMPLE_PERIOD(vfe, c) ((vfe_is_lite(vfe) ? 0x430 : 0xe30) + (c) * 0x100) +#define VFE_BUS_IRQ_SUBSAMPLE_PATTERN(vfe, c) ((vfe_is_lite(vfe) ? 0x434 : 0xe34) + (c) * 0x100) +#define VFE_BUS_FRAMEDROP_PERIOD(vfe, c) ((vfe_is_lite(vfe) ? 0x438 : 0xe38) + (c) * 0x100) +#define VFE_BUS_FRAMEDROP_PATTERN(vfe, c) ((vfe_is_lite(vfe) ? 0x43c : 0xe3c) + (c) * 0x100) +#define VFE_BUS_MMU_PREFETCH_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x460 : 0xe60) + (c) * 0x100) +#define VFE_BUS_MMU_PREFETCH_CFG_EN BIT(0) +#define VFE_BUS_MMU_PREFETCH_MAX_OFFSET(vfe, c) ((vfe_is_lite(vfe) ? 0x464 : 0xe64) + (c) * 0x100) +#define VFE_BUS_ADDR_STATUS0(vfe, c) ((vfe_is_lite(vfe) ? 0x470 : 0xe70) + (c) * 0x100) + +/* + * TODO: differentiate the port id based on requested type of RDI, BHIST etc + * + * IFE write master IDs + * + * VIDEO_FULL_Y 0 + * VIDEO_FULL_C 1 + * VIDEO_DS_4:1 2 + * VIDEO_DS_16:1 3 + * DISPLAY_FULL_Y 4 + * DISPLAY_FULL_C 5 + * DISPLAY_DS_4:1 6 + * DISPLAY_DS_16:1 7 + * FD_Y 8 + * FD_C 9 + * PIXEL_RAW 10 + * STATS_BE0 11 + * STATS_BHIST0 12 + * STATS_TINTLESS_BG 13 + * STATS_AWB_BG 14 + * STATS_AWB_BFW 15 + * STATS_BAF 16 + * STATS_BHIST 17 + * STATS_RS 18 + * STATS_IHIST 19 + * SPARSE_PD 20 + * PDAF_V2.0_PD_DATA 21 + * PDAF_V2.0_SAD 22 + * LCR 23 + * RDI0 24 + * RDI1 25 + * RDI2 26 + * LTM_STATS 27 + * + * IFE Lite write master IDs + * + * RDI0 0 + * RDI1 1 + * RDI2 2 + * RDI3 3 + * GAMMA 4 + * BE 5 + */ + +/* TODO: assign an ENUM in resources and use the provided master + * id directly for RDI, STATS, AWB_BG, BHIST. + * This macro only works because RDI is all we support right now. + */ +#define RDI_WM(n) ((vfe_is_lite(vfe) ? 0 : 24) + (n)) + +static void vfe_global_reset(struct vfe_device *vfe) +{ + /* VFE680 has no global reset, simply report a completion */ + complete(&vfe->reset_complete); +} + +/* + * vfe_isr - VFE module interrupt handler + * @irq: Interrupt line + * @dev: VFE device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t vfe_isr(int irq, void *dev) +{ + return IRQ_HANDLED; +} + +/* + * vfe_halt - Trigger halt on VFE module and wait to complete + * @vfe: VFE device + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_halt(struct vfe_device *vfe) +{ + /* rely on vfe_disable_output() to stop the VFE */ + return 0; +} + +static void vfe_disable_irq(struct vfe_device *vfe) +{ + writel(0u, vfe->base + VFE_TOP_IRQn_MASK(vfe, 0)); + writel(0u, vfe->base + VFE_TOP_IRQn_MASK(vfe, 1)); + writel(0u, vfe->base + VFE_BUS_IRQn_MASK(vfe, 0)); + writel(0u, vfe->base + VFE_BUS_IRQn_MASK(vfe, 1)); +} + +static void vfe_wm_update(struct vfe_device *vfe, u8 rdi, u32 addr, + struct vfe_line *line) +{ + u8 wm = RDI_WM(rdi); + + writel(addr, vfe->base + VFE_BUS_IMAGE_ADDR(vfe, wm)); +} + +static void vfe_wm_start(struct vfe_device *vfe, u8 rdi, 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; + u32 cfg; + u8 wm; + + cfg = VFE_BUS_IMAGE_CFG0_DATA(pix->height, stride); + wm = RDI_WM(rdi); + + writel(cfg, vfe->base + VFE_BUS_IMAGE_CFG0(vfe, wm)); + writel(0, vfe->base + VFE_BUS_IMAGE_CFG1(vfe, wm)); + writel(stride, vfe->base + VFE_BUS_IMAGE_CFG2(vfe, wm)); + writel(0, vfe->base + VFE_BUS_PACKER_CFG(vfe, wm)); + + /* Set total frame increment value */ + writel(pix->plane_fmt[0].bytesperline * pix->height, + vfe->base + VFE_BUS_FRAME_INCR(vfe, wm)); + + /* MMU */ + writel(VFE_BUS_MMU_PREFETCH_CFG_EN, vfe->base + VFE_BUS_MMU_PREFETCH_CFG(vfe, wm)); + writel(~0u, vfe->base + VFE_BUS_MMU_PREFETCH_MAX_OFFSET(vfe, wm)); + + /* no dropped frames, one irq per frame */ + writel(1, vfe->base + VFE_BUS_FRAMEDROP_PATTERN(vfe, wm)); + writel(0, vfe->base + VFE_BUS_FRAMEDROP_PERIOD(vfe, wm)); + writel(1, vfe->base + VFE_BUS_IRQ_SUBSAMPLE_PATTERN(vfe, wm)); + writel(0, vfe->base + VFE_BUS_IRQ_SUBSAMPLE_PERIOD(vfe, wm)); + + /* We don't process IRQs for VFE in RDI mode at the moment */ + vfe_disable_irq(vfe); + + /* Enable WM */ + writel(VFE_BUS_WRITE_CLIENT_CFG_EN, + vfe->base + VFE_BUS_WRITE_CLIENT_CFG(vfe, wm)); + + dev_dbg(vfe->camss->dev, "RDI%d WM:%d width %d height %d stride %d\n", + rdi, wm, pix->width, pix->height, stride); +} + +static void vfe_wm_stop(struct vfe_device *vfe, u8 rdi) +{ + u8 wm = RDI_WM(rdi); + + writel(0, vfe->base + VFE_BUS_WRITE_CLIENT_CFG(vfe, wm)); +} + +static const struct camss_video_ops vfe_video_ops_680 = { + .queue_buffer = vfe_queue_buffer_v2, + .flush_buffers = vfe_flush_buffers, +}; + +static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe) +{ + vfe->video_ops = vfe_video_ops_680; +} + +static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + int port_id = line_id; + + camss_reg_update(vfe->camss, vfe->id, port_id, false); +} + +static inline void vfe_reg_update_clear(struct vfe_device *vfe, + enum vfe_line_id line_id) +{ + int port_id = line_id; + + camss_reg_update(vfe->camss, vfe->id, port_id, true); +} + +const struct vfe_hw_ops vfe_ops_680 = { + .global_reset = vfe_global_reset, + .hw_version = vfe_hw_version, + .isr = vfe_isr, + .pm_domain_off = vfe_pm_domain_off, + .pm_domain_on = vfe_pm_domain_on, + .subdev_init = vfe_subdev_init, + .vfe_disable = vfe_disable, + .vfe_enable = vfe_enable_v2, + .vfe_halt = vfe_halt, + .vfe_wm_start = vfe_wm_start, + .vfe_wm_stop = vfe_wm_stop, + .vfe_buf_done = vfe_buf_done, + .vfe_wm_update = vfe_wm_update, + .reg_update = vfe_reg_update, + .reg_update_clear = vfe_reg_update_clear, +}; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 9dec5bc0d1b1..a23f666be753 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -243,6 +243,7 @@ extern const struct vfe_hw_ops vfe_ops_4_7; extern const struct vfe_hw_ops vfe_ops_4_8; extern const struct vfe_hw_ops vfe_ops_170; extern const struct vfe_hw_ops vfe_ops_480; +extern const struct vfe_hw_ops vfe_ops_680; extern const struct vfe_hw_ops vfe_ops_780; int vfe_get(struct vfe_device *vfe); -- cgit v1.2.3-59-g8ed1b From 88655d64210e36c926d9c8a2617ad97e0bc7a4ad Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 14 Mar 2025 23:35:58 +0000 Subject: media: qcom: camss: Add support for 3ph CSIPHY write settle delay Currently we have an s32 value called delay which has been inherited from the CamX code for PHY init. This unused value relates to a post-write delay latching time. In the silicon test-bench which provides the basis for the CamX code the write settle times are specified in nanoseconds. In the upstream kernel we currently take no notice of the delay value and use all zero in any case. Nanosecond granularity timing from the perspective of the kernel is total overkill, however for some PHY init sequences introduction of a settle delay has a use. Add support to the 3ph init sequence for microsecond level delay. A readback of written data would probably accomplish the same thing but, since the PHY init sequences in the wild provide a delay value - we can just add support here for that delay and consume the values given. Generally these delays are probably not necessary but, they do speak to a theoretical delay that silicon test-benches utilise and therefore are worthwhile to replicate if the given PHY init sequence has the data. Reviewed-by: Vladimir Zapolskiy Signed-off-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 a6cc957b986e..b44939686e4b 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 @@ -59,7 +59,7 @@ struct csiphy_lane_regs { s32 reg_addr; s32 reg_data; - s32 delay; + u32 delay_us; u32 csiphy_param_type; }; @@ -600,6 +600,8 @@ static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy, break; } writel_relaxed(val, csiphy->base + r->reg_addr); + if (r->delay_us) + udelay(r->delay_us); } } -- cgit v1.2.3-59-g8ed1b From b8f781596da0e540797557c570163b5da0042070 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 14 Mar 2025 23:35:59 +0000 Subject: media: qcom: camss: csiphy-3ph: Add 4nm CSIPHY 2ph 5Gbps DPHY v2.1.2 init sequence For various SoC skews at 4nm CSIPHY 2.1.2 is used. Add in the init sequence with base control reg offset of 0x1000. This initial version will support X1E80100. Take the silicon verification PHY init parameters as a first/best guess pass. SKEW_CAL is included as received from the qcom silicon init sequence. Signed-off-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- .../platform/qcom/camss/camss-csiphy-3ph-1-0.c | 121 +++++++++++++++++++++ 1 file changed, 121 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 b44939686e4b..d5f717f6215c 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 @@ -55,6 +55,7 @@ #define CSIPHY_DNP_PARAMS 4 #define CSIPHY_2PH_REGS 5 #define CSIPHY_3PH_REGS 6 +#define CSIPHY_SKEW_CAL 7 struct csiphy_lane_regs { s32 reg_addr; @@ -423,6 +424,123 @@ csiphy_lane_regs lane_regs_sm8550[] = { {0x0C64, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, }; +/* 4nm 2PH v 2.1.2 2p5Gbps 4 lane DPHY mode */ +static const struct +csiphy_lane_regs lane_regs_x1e80100[] = { + /* Power up lanes 2ph mode */ + {0x1014, 0xD5, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x101C, 0x7A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x1018, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + + {0x0094, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x00A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0090, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0098, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0094, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x003C, 0xB8, 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}, + {0x0094, 0xD7, 0x00, CSIPHY_SKEW_CAL}, + {0x005C, 0x00, 0x00, CSIPHY_SKEW_CAL}, + {0x0060, 0xBD, 0x00, CSIPHY_SKEW_CAL}, + {0x0064, 0x7F, 0x00, CSIPHY_SKEW_CAL}, + + {0x0E94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0EA0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E90, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E94, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0E30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E28, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E00, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E0C, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E38, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E2C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E34, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E1C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E3C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E04, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E08, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0E10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + + {0x0494, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x04A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0490, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0498, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0494, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0400, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0420, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0408, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0494, 0xD7, 0x00, CSIPHY_SKEW_CAL}, + {0x045C, 0x00, 0x00, CSIPHY_SKEW_CAL}, + {0x0460, 0xBD, 0x00, CSIPHY_SKEW_CAL}, + {0x0464, 0x7F, 0x00, CSIPHY_SKEW_CAL}, + + {0x0894, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x08A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0890, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0898, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0894, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0830, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0800, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0838, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x082C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0834, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x081C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0814, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x083C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0804, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0820, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0808, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0810, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0894, 0xD7, 0x00, CSIPHY_SKEW_CAL}, + {0x085C, 0x00, 0x00, CSIPHY_SKEW_CAL}, + {0x0860, 0xBD, 0x00, CSIPHY_SKEW_CAL}, + {0x0864, 0x7F, 0x00, CSIPHY_SKEW_CAL}, + + {0x0C94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0CA0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C90, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C94, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0C30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C00, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C38, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C2C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C34, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C1C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C3C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C04, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C08, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0C10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C94, 0xD7, 0x00, CSIPHY_SKEW_CAL}, + {0x0C5C, 0x00, 0x00, CSIPHY_SKEW_CAL}, + {0x0C60, 0xBD, 0x00, CSIPHY_SKEW_CAL}, + {0x0C64, 0x7F, 0x00, CSIPHY_SKEW_CAL}, +}; + static void csiphy_hw_version_read(struct csiphy_device *csiphy, struct device *dev) { @@ -593,6 +711,9 @@ static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy, case CSIPHY_SETTLE_CNT_LOWER_BYTE: val = settle_cnt & 0xff; break; + case CSIPHY_SKEW_CAL: + /* TODO: support application of skew from dt flag */ + continue; case CSIPHY_DNP_PARAMS: continue; default: -- cgit v1.2.3-59-g8ed1b From 1830cf0f56c3ced98bab3aea7ca2a0fa76e3a49b Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Fri, 14 Mar 2025 23:36:00 +0000 Subject: media: qcom: camss: Add x1e80100 specific support Populate CAMSS with x1e80100 specific hooks. Signed-off-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- .../platform/qcom/camss/camss-csiphy-3ph-1-0.c | 6 + drivers/media/platform/qcom/camss/camss-vfe.c | 2 + drivers/media/platform/qcom/camss/camss.c | 309 +++++++++++++++++++++ drivers/media/platform/qcom/camss/camss.h | 1 + 4 files changed, 318 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 d5f717f6215c..f732a76de93e 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 @@ -749,6 +749,7 @@ static bool csiphy_is_gen2(u32 version) case CAMSS_8280XP: case CAMSS_845: case CAMSS_8550: + case CAMSS_X1E80100: ret = true; break; } @@ -837,6 +838,11 @@ static int csiphy_init(struct csiphy_device *csiphy) regs->lane_regs = &lane_regs_sc8280xp[0]; regs->lane_array_size = ARRAY_SIZE(lane_regs_sc8280xp); break; + case CAMSS_X1E80100: + regs->lane_regs = &lane_regs_x1e80100[0]; + regs->lane_array_size = ARRAY_SIZE(lane_regs_x1e80100); + regs->offset = 0x1000; + break; case CAMSS_8550: regs->lane_regs = &lane_regs_sm8550[0]; regs->lane_array_size = ARRAY_SIZE(lane_regs_sm8550); diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 91bc0cb7781e..4bca6c3abaff 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -346,6 +346,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, case CAMSS_8280XP: case CAMSS_845: case CAMSS_8550: + case CAMSS_X1E80100: switch (sink_code) { case MEDIA_BUS_FMT_YUYV8_1X16: { @@ -1973,6 +1974,7 @@ static int vfe_bpl_align(struct vfe_device *vfe) case CAMSS_8280XP: case CAMSS_845: case CAMSS_8550: + case CAMSS_X1E80100: ret = 16; break; default: diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index d7e0e802852a..06f42875702f 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -2483,6 +2483,299 @@ static const struct resources_icc icc_res_sm8550[] = { }, }; +static const struct camss_subdev_resources csiphy_res_x1e80100[] = { + /* CSIPHY0 */ + { + .regulators = { "vdd-csiphy-0p8-supply", + "vdd-csiphy-1p2-supply" }, + .clock = { "csiphy0", "csiphy0_timer" }, + .clock_rate = { { 300000000, 400000000, 480000000 }, + { 266666667, 400000000 } }, + .reg = { "csiphy0" }, + .interrupt = { "csiphy0" }, + .csiphy = { + .id = 0, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + }, + }, + /* CSIPHY1 */ + { + .regulators = { "vdd-csiphy-0p8-supply", + "vdd-csiphy-1p2-supply" }, + .clock = { "csiphy1", "csiphy1_timer" }, + .clock_rate = { { 300000000, 400000000, 480000000 }, + { 266666667, 400000000 } }, + .reg = { "csiphy1" }, + .interrupt = { "csiphy1" }, + .csiphy = { + .id = 1, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + }, + }, + /* CSIPHY2 */ + { + .regulators = { "vdd-csiphy-0p8-supply", + "vdd-csiphy-1p2-supply" }, + .clock = { "csiphy2", "csiphy2_timer" }, + .clock_rate = { { 300000000, 400000000, 480000000 }, + { 266666667, 400000000 } }, + .reg = { "csiphy2" }, + .interrupt = { "csiphy2" }, + .csiphy = { + .id = 2, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + }, + }, + /* CSIPHY4 */ + { + .regulators = { "vdd-csiphy-0p8-supply", + "vdd-csiphy-1p2-supply" }, + .clock = { "csiphy4", "csiphy4_timer" }, + .clock_rate = { { 300000000, 400000000, 480000000 }, + { 266666667, 400000000 } }, + .reg = { "csiphy4" }, + .interrupt = { "csiphy4" }, + .csiphy = { + .id = 4, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + }, + }, +}; + +static const struct camss_subdev_resources csid_res_x1e80100[] = { + /* CSID0 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", + "cpas_fast_ahb", "csid", "csid_csiphy_rx" }, + .clock_rate = { { 0 }, + { 0 }, + { 64000000, 80000000 }, + { 80000000, 100000000, 200000000, + 300000000, 400000000 }, + { 300000000, 400000000, 480000000 }, + { 300000000, 400000000, 480000000 }, }, + .reg = { "csid0" }, + .interrupt = { "csid0" }, + .csid = { + .hw_ops = &csid_ops_680, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + }, + }, + /* CSID1 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", + "cpas_fast_ahb", "csid", "csid_csiphy_rx" }, + .clock_rate = { { 0 }, + { 0 }, + { 64000000, 80000000 }, + { 80000000, 100000000, 200000000, + 300000000, 400000000 }, + { 300000000, 400000000, 480000000 }, + { 300000000, 400000000, 480000000 }, }, + .reg = { "csid1" }, + .interrupt = { "csid1" }, + .csid = { + .hw_ops = &csid_ops_680, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + }, + }, + /* CSID2 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", + "cpas_fast_ahb", "csid", "csid_csiphy_rx" }, + .clock_rate = { { 0 }, + { 0 }, + { 64000000, 80000000 }, + { 80000000, 100000000, 200000000, + 300000000, 400000000 }, + { 300000000, 400000000, 480000000 }, + { 300000000, 400000000, 480000000 }, }, + .reg = { "csid2" }, + .interrupt = { "csid2" }, + .csid = { + .hw_ops = &csid_ops_680, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + }, + }, + /* CSID_LITE0 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", + "cpas_fast_ahb", "csid", "csid_csiphy_rx" }, + .clock_rate = { { 0 }, + { 0 }, + { 64000000, 80000000 }, + { 80000000, 100000000, 200000000, + 300000000, 400000000 }, + { 300000000, 400000000, 480000000 }, + { 300000000, 400000000, 480000000 }, }, + .reg = { "csid_lite0" }, + .interrupt = { "csid_lite0" }, + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_680, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE1 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", + "cpas_fast_ahb", "csid", "csid_csiphy_rx" }, + .clock_rate = { { 0 }, + { 0 }, + { 64000000, 80000000 }, + { 80000000, 100000000, 200000000, + 300000000, 400000000 }, + { 300000000, 400000000, 480000000 }, + { 300000000, 400000000, 480000000 }, }, + + .reg = { "csid_lite1" }, + .interrupt = { "csid_lite1" }, + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_680, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, +}; + +static const struct camss_subdev_resources vfe_res_x1e80100[] = { + /* IFE0 */ + { + .regulators = {}, + .clock = {"camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", + "cpas_fast_ahb", "cpas_vfe0", "vfe0_fast_ahb", + "vfe0" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 345600000, 432000000, 594000000, 675000000, + 727000000 }, }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" }, + .vfe = { + .line_num = 4, + .pd_name = "ife0", + .hw_ops = &vfe_ops_680, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + }, + }, + /* IFE1 */ + { + .regulators = {}, + .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", + "cpas_fast_ahb", "cpas_vfe1", "vfe1_fast_ahb", + "vfe1" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 345600000, 432000000, 594000000, 675000000, + 727000000 }, }, + .reg = { "vfe1" }, + .interrupt = { "vfe1" }, + .vfe = { + .line_num = 4, + .pd_name = "ife1", + .hw_ops = &vfe_ops_680, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + }, + }, + /* IFE_LITE_0 */ + { + .regulators = {}, + .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", + "vfe_lite_ahb", "cpas_vfe_lite", "vfe_lite", + "vfe_lite_csid" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 266666667, 400000000, 480000000 }, }, + .reg = { "vfe_lite0" }, + .interrupt = { "vfe_lite0" }, + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_680, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + }, + }, + /* IFE_LITE_1 */ + { + .regulators = {}, + .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb", + "vfe_lite_ahb", "cpas_vfe_lite", "vfe_lite", + "vfe_lite_csid" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 266666667, 400000000, 480000000 }, }, + .reg = { "vfe_lite1" }, + .interrupt = { "vfe_lite1" }, + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_680, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + }, + }, +}; + +static const struct resources_icc icc_res_x1e80100[] = { + { + .name = "ahb", + .icc_bw_tbl.avg = 150000, + .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 resources_wrapper csid_wrapper_res_x1e80100 = { + .reg = "csid_wrapper", +}; + /* * camss_add_clock_margin - Add margin to clock frequency rate * @rate: Clock frequency rate @@ -3553,6 +3846,21 @@ static const struct camss_resources sm8550_resources = { .link_entities = camss_link_entities }; +static const struct camss_resources x1e80100_resources = { + .version = CAMSS_X1E80100, + .pd_name = "top", + .csiphy_res = csiphy_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), + .csid_num = ARRAY_SIZE(csid_res_x1e80100), + .vfe_num = ARRAY_SIZE(vfe_res_x1e80100), + .link_entities = camss_link_entities +}; + static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,msm8916-camss", .data = &msm8916_resources }, { .compatible = "qcom,msm8953-camss", .data = &msm8953_resources }, @@ -3564,6 +3872,7 @@ static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,sdm845-camss", .data = &sdm845_resources }, { .compatible = "qcom,sm8250-camss", .data = &sm8250_resources }, { .compatible = "qcom,sm8550-camss", .data = &sm8550_resources }, + { .compatible = "qcom,x1e80100-camss", .data = &x1e80100_resources }, { } }; diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index b284b910ce42..63c0afee154a 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -86,6 +86,7 @@ enum camss_version { CAMSS_8280XP, CAMSS_845, CAMSS_8550, + CAMSS_X1E80100, }; enum icc_count { -- cgit v1.2.3-59-g8ed1b From bf462ef8c5ad61616b54751915f3dd1d3301a505 Mon Sep 17 00:00:00 2001 From: Vikash Garodia Date: Fri, 21 Mar 2025 08:49:00 +0530 Subject: dt-bindings: media: qcom,sm8550-iris: document SA8775p IRIS accelerator Document the IRIS video decoder and encoder accelerator found in the SA8775p platform. SA8775p and SM8550 are irisv3 with same core and bindings, hence SA8775p is made fallback to SM8550. QCS8300 is a downscaled version of irisv3 and have different hardware capabilities. SM8650 is an irisv3 with different (higher) number of reset lines compared to SM8550. QCS8300 is yet to come in future posting, while SM8650 is posted as https://lore.kernel.org/all/20250305-topic-sm8x50-iris-v10-v2-1-bd65a3fc099e@linaro.org/ Reviewed-by: Bryan O'Donoghue Reviewed-by: Krzysztof Kozlowski Signed-off-by: Vikash Garodia Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml b/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml index e424ea84c211..6a89e9e38087 100644 --- a/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml @@ -19,7 +19,12 @@ allOf: properties: compatible: - const: qcom,sm8550-iris + oneOf: + - items: + - enum: + - qcom,sa8775p-iris + - const: qcom,sm8550-iris + - const: qcom,sm8550-iris power-domains: maxItems: 4 -- cgit v1.2.3-59-g8ed1b From e68c3c50a736490d9c07888fe525718d16ff9e9c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 17 Feb 2025 11:08:00 +0300 Subject: media: iris: fix error code in iris_load_fw_to_memory() Return -ENOMEM if memremap() fails. Don't return success. Fixes: d19b163356b8 ("media: iris: implement video firmware load/unload") Cc: stable@vger.kernel.org Signed-off-by: Dan Carpenter Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/iris/iris_firmware.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c index 7c493b4a75db..f1b5cd56db32 100644 --- a/drivers/media/platform/qcom/iris/iris_firmware.c +++ b/drivers/media/platform/qcom/iris/iris_firmware.c @@ -53,8 +53,10 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name) } mem_virt = memremap(mem_phys, res_size, MEMREMAP_WC); - if (!mem_virt) + if (!mem_virt) { + ret = -ENOMEM; goto err_release_fw; + } ret = qcom_mdt_load(dev, firmware, fw_name, pas_id, mem_virt, mem_phys, res_size, NULL); -- cgit v1.2.3-59-g8ed1b From 4edd34175e3dd91dc30b3091efdf3f9c7143d4c4 Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Thu, 19 Dec 2024 11:11:53 +0530 Subject: dt-bindings: media: add support for video hardware on QCS615 platform QCS615 uses the same video core as SC7180. Therefore, add qcom,qcs615-venus compatible to qcom,sc7180-venus.yaml to enable video hardware support on QCS615 platform. Make qcom,qcs615-venus fallback to qcom,sc7180-venus to ensure compatibility with existing configurations. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Renjiang Han Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml index 83c4a5d95f02..bfd8b1ad4731 100644 --- a/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml @@ -18,7 +18,12 @@ allOf: properties: compatible: - const: qcom,sc7180-venus + oneOf: + - items: + - enum: + - qcom,qcs615-venus + - const: qcom,sc7180-venus + - const: qcom,sc7180-venus power-domains: minItems: 2 -- cgit v1.2.3-59-g8ed1b From 523cea3a19f0b3b020a4745344c136a636e6ffd7 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Thu, 27 Mar 2025 13:53:04 +0100 Subject: media: venus: Fix probe error handling Video device registering has been moved earlier in the probe function, but the new order has not been propagated to error handling. This means we can end with unreleased resources on error (e.g dangling video device on missing firmware probe aborting). Fixes: 08b1cf474b7f7 ("media: venus: core, venc, vdec: Fix probe dependency error") Cc: stable@vger.kernel.org Signed-off-by: Loic Poulain Reviewed-by: Dikshita Agarwal Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/core.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 77d48578ecd2..d305d74bb152 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -438,7 +438,7 @@ static int venus_probe(struct platform_device *pdev) ret = v4l2_device_register(dev, &core->v4l2_dev); if (ret) - goto err_core_deinit; + goto err_hfi_destroy; platform_set_drvdata(pdev, core); @@ -476,24 +476,24 @@ static int venus_probe(struct platform_device *pdev) ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_DEC); if (ret) - goto err_venus_shutdown; + goto err_core_deinit; ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_ENC); if (ret) - goto err_venus_shutdown; + goto err_core_deinit; ret = pm_runtime_put_sync(dev); if (ret) { pm_runtime_get_noresume(dev); - goto err_dev_unregister; + goto err_core_deinit; } venus_dbgfs_init(core); return 0; -err_dev_unregister: - v4l2_device_unregister(&core->v4l2_dev); +err_core_deinit: + hfi_core_deinit(core, false); err_venus_shutdown: venus_shutdown(core); err_firmware_deinit: @@ -506,9 +506,9 @@ err_runtime_disable: pm_runtime_put_noidle(dev); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); + v4l2_device_unregister(&core->v4l2_dev); +err_hfi_destroy: hfi_destroy(core); -err_core_deinit: - hfi_core_deinit(core, false); err_core_put: if (core->pm_ops->core_put) core->pm_ops->core_put(core); -- cgit v1.2.3-59-g8ed1b From b588898880b02a8e50bcb72242585021d67f246d Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Thu, 27 Feb 2025 08:13:54 +0530 Subject: media: venus: vdec: queue dpb buffers to firmware for video seek For the seek case, the input port will be called stream_off and then stream_on in the driver. Firmware will flush all buffers during stream_off input port. Therefore, driver needs to queue DPB buffers to firmware during stream_on input port to ensure that decoder can decode normally when it receives enough input and output buffers. Otherwise, decoder will not be able to decode due to lack of DPB buffer even if there are enough input and output buffers. Signed-off-by: Renjiang Han Reviewed-by: Dikshita Agarwal [bod: added media prefix] Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/vdec.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 39d0556d7237..99ce5fd41577 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1110,10 +1110,20 @@ static int vdec_start_output(struct venus_inst *inst) if (inst->codec_state == VENUS_DEC_STATE_SEEK) { ret = venus_helper_process_initial_out_bufs(inst); - if (inst->next_buf_last) + if (ret) + return ret; + + if (inst->next_buf_last) { inst->codec_state = VENUS_DEC_STATE_DRC; - else + } else { inst->codec_state = VENUS_DEC_STATE_DECODING; + + if (inst->streamon_cap) { + ret = venus_helper_queue_dpb_bufs(inst); + if (ret) + return ret; + } + } goto done; } -- cgit v1.2.3-59-g8ed1b From 14423fc3a4a21fb436dda85450339ec2bf191b36 Mon Sep 17 00:00:00 2001 From: Renjiang Han Date: Tue, 18 Feb 2025 16:03:20 +0530 Subject: media: venus: pm_helpers: add compatibility for dev_pm_genpd_set_hwmode on V4 There are two ways to switch GDSC mode. One is to write the POWER_CONTROL register and the other is to use dev_pm_genpd_set_hwmode(). However, they rely on different clock driver flags. dev_pm_genpd_set_hwmode() depends on the HW_CTRL_TRIGGER flag and POWER_CONTROL register depends on the HW_CTRL flag. By default, the dev_pm_genpd_set_hwmode() is used to switch the GDSC mode. If it fails and dev_pm_genpd_set_hwmode() returns -EOPNOTSUPP, it means that the clock driver uses the HW_CTRL flag. At this time, the GDSC mode is switched to write the POWER_CONTROL register. Clock driver is using HW_CTRL_TRIGGER flag with V6. So hwmode_dev is always true on using V6 platform. Conversely, if hwmode_dev is false, this platform must be not using V6. Therefore, replace IS_V6 in poweroff_coreid with hwmode_dev. Also, with HW_CTRL_TRIGGER flag, the vcodec gdsc gets enabled in SW mode by default. Therefore, before disabling the GDSC, GDSC should be switched to SW mode so that GDSC gets enabled in SW mode in the next enable. Signed-off-by: Renjiang Han Reviewed-by: Bryan O'Donoghue Reviewed-by: Vikash Garodia [bod: added media prefix] Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/core.h | 2 ++ drivers/media/platform/qcom/venus/pm_helpers.c | 38 ++++++++++++++------------ 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index abeeafa86697..b412e0c5515a 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -172,6 +172,7 @@ struct venus_format { * @venus_ver: the venus firmware version * @dump_core: a flag indicating that a core dump is required * @ocs: OF changeset pointer + * @hwmode_dev: a flag indicating that HW_CTRL_TRIGGER is used in clock driver */ struct venus_core { void __iomem *base; @@ -235,6 +236,7 @@ struct venus_core { } venus_ver; unsigned long dump_core; struct of_changeset *ocs; + bool hwmode_dev; }; struct vdec_controls { diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index 33a5a659c0ad..409aa9bd0b5d 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -412,9 +412,17 @@ static int vcodec_control_v4(struct venus_core *core, u32 coreid, bool enable) u32 val; int ret; - if (IS_V6(core)) - return dev_pm_genpd_set_hwmode(core->pmdomains->pd_devs[coreid], !enable); - else if (coreid == VIDC_CORE_ID_1) { + ret = dev_pm_genpd_set_hwmode(core->pmdomains->pd_devs[coreid], !enable); + if (ret == -EOPNOTSUPP) { + core->hwmode_dev = false; + goto legacy; + } + + core->hwmode_dev = true; + return ret; + +legacy: + if (coreid == VIDC_CORE_ID_1) { ctrl = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL; stat = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_STATUS; } else { @@ -450,7 +458,7 @@ static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask) vcodec_clks_disable(core, core->vcodec0_clks); - if (!IS_V6(core)) { + if (!core->hwmode_dev) { ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false); if (ret) return ret; @@ -468,7 +476,7 @@ static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask) vcodec_clks_disable(core, core->vcodec1_clks); - if (!IS_V6(core)) { + if (!core->hwmode_dev) { ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false); if (ret) return ret; @@ -491,11 +499,9 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask) if (ret < 0) return ret; - if (!IS_V6(core)) { - ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); - if (ret) - return ret; - } + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); + if (ret) + return ret; ret = vcodec_clks_enable(core, core->vcodec0_clks); if (ret) @@ -511,11 +517,9 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask) if (ret < 0) return ret; - if (!IS_V6(core)) { - ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); - if (ret) - return ret; - } + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); + if (ret) + return ret; ret = vcodec_clks_enable(core, core->vcodec1_clks); if (ret) @@ -811,7 +815,7 @@ static int vdec_power_v4(struct device *dev, int on) else vcodec_clks_disable(core, core->vcodec0_clks); - vcodec_control_v4(core, VIDC_CORE_ID_1, false); + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false); return ret; } @@ -856,7 +860,7 @@ static int venc_power_v4(struct device *dev, int on) else vcodec_clks_disable(core, core->vcodec1_clks); - vcodec_control_v4(core, VIDC_CORE_ID_2, false); + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false); return ret; } -- cgit v1.2.3-59-g8ed1b From f6e9968aeb200aa6d399ac73338174b394677049 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 8 Apr 2025 20:31:57 +0100 Subject: media: dt-bindings: media: renesas,vsp1: Document RZ/V2H(P) The VSPD block on the RZ/V2H(P) 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/V2H(P) SoC. Signed-off-by: Lad Prabhakar Acked-by: Krzysztof Kozlowski Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20250408193158.80936-2-prabhakar.mahadev-lad.rj@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 1a03e67462a4..9d03b972f522 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,r9a09g057-vsp2 # RZ/V2H(P) - const: renesas,r9a07g044-vsp2 # RZ/G2L fallback reg: -- cgit v1.2.3-59-g8ed1b From 079afc0b7241e383cf61e5ede906104d85f7a6cb Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 8 Apr 2025 20:31:58 +0100 Subject: media: dt-bindings: media: renesas,fcp: Document RZ/V2H(P) SoC The FCPVD block on the RZ/V2H(P) 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/V2H(P) SoC. Signed-off-by: Lad Prabhakar Acked-by: Krzysztof Kozlowski Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20250408193158.80936-3-prabhakar.mahadev-lad.rj@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 f94dacd96278..5ed9427fb757 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,r9a09g057-fcpvd # RZ/V2H(P) - const: renesas,fcpv # Generic FCP for VSP fallback reg: @@ -66,6 +67,7 @@ allOf: - renesas,r9a07g043u-fcpvd - renesas,r9a07g044-fcpvd - renesas,r9a07g054-fcpvd + - renesas,r9a09g057-fcpvd then: properties: clocks: -- cgit v1.2.3-59-g8ed1b From e7376745ad5c8548e31d9ea58adfb5a847e017a4 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Wed, 2 Apr 2025 20:33:02 +0200 Subject: media: rcar-vin: Fix stride setting for RAW8 formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Earlier versions of the datasheet where unclear about the stride setting for RAW8 capture formats. Later datasheets clarifies that the stride only process in this mode for non-image data. For image data the full stride shall be used. Compare section "RAW: 8 Bits and Embedded 8-Bit Non-Image Data, User Defined 8-bit Data" vs "RAW: 8 Bits". Remove the special case from pixel formats that carry image data and treat it as any other image format. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20250402183302.140055-1-niklas.soderlund+renesas@ragnatech.se Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rcar-vin/rcar-dma.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index 8de871240440..edb06730bc7c 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -679,22 +679,6 @@ void rvin_crop_scale_comp(struct rvin_dev *vin) fmt = rvin_format_from_pixel(vin, vin->format.pixelformat); stride = vin->format.bytesperline / fmt->bpp; - - /* For RAW8 format bpp is 1, but the hardware process RAW8 - * format in 2 pixel unit hence configure VNIS_REG as stride / 2. - */ - switch (vin->format.pixelformat) { - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SGBRG8: - case V4L2_PIX_FMT_SGRBG8: - case V4L2_PIX_FMT_SRGGB8: - case V4L2_PIX_FMT_GREY: - stride /= 2; - break; - default: - break; - } - rvin_write(vin, stride, VNIS_REG); } -- cgit v1.2.3-59-g8ed1b From 52e39050616aa7e72ea0f6330501368d5f6925e1 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 11 Apr 2025 19:05:29 +0200 Subject: media: dt-bindings: renesas,rzg2l-csi2: Document Renesas RZ/V2H(P) SoC The MIPI CSI-2 block on the Renesas RZ/V2H(P) SoC is similar to the one found on the Renesas RZ/G2L SoC, with the following differences: - A different D-PHY - Additional registers for the MIPI CSI-2 link - Only two clocks Add a new compatible string, `renesas,r9a09g057-csi2`, for the RZ/V2H(P) SoC. Reviewed-by: Rob Herring (Arm) Reviewed-by: Laurent Pinchart Signed-off-by: Lad Prabhakar Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-2-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../bindings/media/renesas,rzg2l-csi2.yaml | 59 ++++++++++++++++------ 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/media/renesas,rzg2l-csi2.yaml b/Documentation/devicetree/bindings/media/renesas,rzg2l-csi2.yaml index 7faa12fecd5b..1f9ee37584b3 100644 --- a/Documentation/devicetree/bindings/media/renesas,rzg2l-csi2.yaml +++ b/Documentation/devicetree/bindings/media/renesas,rzg2l-csi2.yaml @@ -17,12 +17,14 @@ description: properties: compatible: - items: - - enum: - - renesas,r9a07g043-csi2 # RZ/G2UL - - renesas,r9a07g044-csi2 # RZ/G2{L,LC} - - renesas,r9a07g054-csi2 # RZ/V2L - - const: renesas,rzg2l-csi2 + oneOf: + - items: + - enum: + - renesas,r9a07g043-csi2 # RZ/G2UL + - renesas,r9a07g044-csi2 # RZ/G2{L,LC} + - renesas,r9a07g054-csi2 # RZ/V2L + - const: renesas,rzg2l-csi2 + - const: renesas,r9a09g057-csi2 # RZ/V2H(P) reg: maxItems: 1 @@ -31,16 +33,24 @@ properties: maxItems: 1 clocks: - items: - - description: Internal clock for connecting CRU and MIPI - - description: CRU Main clock - - description: CRU Register access clock + oneOf: + - items: + - description: Internal clock for connecting CRU and MIPI + - description: CRU Main clock + - description: CRU Register access clock + - items: + - description: CRU Main clock + - description: CRU Register access clock clock-names: - items: - - const: system - - const: video - - const: apb + oneOf: + - items: + - const: system + - const: video + - const: apb + - items: + - const: video + - const: apb power-domains: maxItems: 1 @@ -48,7 +58,7 @@ properties: resets: items: - description: CRU_PRESETN reset terminal - - description: CRU_CMN_RSTB reset terminal + - description: D-PHY reset (CRU_CMN_RSTB or CRU_n_S_RESETN) reset-names: items: @@ -101,6 +111,25 @@ required: - reset-names - ports +allOf: + - if: + properties: + compatible: + contains: + const: renesas,r9a09g057-csi2 + then: + properties: + clocks: + maxItems: 2 + clock-names: + maxItems: 2 + else: + properties: + clocks: + minItems: 3 + clock-names: + minItems: 3 + additionalProperties: false examples: -- cgit v1.2.3-59-g8ed1b From f1c83d2f2841e99f884104e912aa20fcc32b9c2d Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Fri, 11 Apr 2025 19:05:30 +0200 Subject: media: dt-bindings: renesas,rzg2l-csi2: Document Renesas RZ/G3E CSI-2 block Document the CSI-2 block which is part of CRU found in Renesas RZ/G3E SoC. The CSI-2 block on the RZ/G3E SoC is identical to one found on the RZ/V2H(P) SoC. Acked-by: Rob Herring (Arm) Reviewed-by: Lad Prabhakar Reviewed-by: Laurent Pinchart Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-3-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/renesas,rzg2l-csi2.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/media/renesas,rzg2l-csi2.yaml b/Documentation/devicetree/bindings/media/renesas,rzg2l-csi2.yaml index 1f9ee37584b3..c5c511c9f0db 100644 --- a/Documentation/devicetree/bindings/media/renesas,rzg2l-csi2.yaml +++ b/Documentation/devicetree/bindings/media/renesas,rzg2l-csi2.yaml @@ -24,6 +24,9 @@ properties: - renesas,r9a07g044-csi2 # RZ/G2{L,LC} - renesas,r9a07g054-csi2 # RZ/V2L - const: renesas,rzg2l-csi2 + - items: + - const: renesas,r9a09g047-csi2 # RZ/G3E + - const: renesas,r9a09g057-csi2 - const: renesas,r9a09g057-csi2 # RZ/V2H(P) reg: -- cgit v1.2.3-59-g8ed1b From d71be5add2f3fd4e11c11a21855df17c48088fc2 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Fri, 11 Apr 2025 19:05:31 +0200 Subject: media: dt-bindings: renesas,rzg2l-cru: Document Renesas RZ/G3E SoC The CRU block found on the Renesas RZ/G3E ("R9A09G047") SoC has five interrupts: - image_conv: image_conv irq - axi_mst_err: AXI master error level irq - vd_addr_wend: Video data AXI master addr 0 write end irq - sd_addr_wend: Statistics data AXI master addr 0 write end irq - vsd_addr_wend: Video statistics data AXI master addr 0 write end irq This IP has only one input port 'port@1' similar to the RZ/G2UL CRU. Document the CRU block found on the Renesas RZ/G3E ("R9A09G047") SoC. Reviewed-by: Rob Herring (Arm) Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-4-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../bindings/media/renesas,rzg2l-cru.yaml | 65 ++++++++++++++++++---- 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/Documentation/devicetree/bindings/media/renesas,rzg2l-cru.yaml b/Documentation/devicetree/bindings/media/renesas,rzg2l-cru.yaml index bc1245127025..47e18690fa57 100644 --- a/Documentation/devicetree/bindings/media/renesas,rzg2l-cru.yaml +++ b/Documentation/devicetree/bindings/media/renesas,rzg2l-cru.yaml @@ -17,24 +17,43 @@ description: properties: compatible: - items: - - enum: - - renesas,r9a07g043-cru # RZ/G2UL - - renesas,r9a07g044-cru # RZ/G2{L,LC} - - renesas,r9a07g054-cru # RZ/V2L - - const: renesas,rzg2l-cru + oneOf: + - items: + - enum: + - renesas,r9a07g043-cru # RZ/G2UL + - renesas,r9a07g044-cru # RZ/G2{L,LC} + - renesas,r9a07g054-cru # RZ/V2L + - const: renesas,rzg2l-cru + - const: renesas,r9a09g047-cru # RZ/G3E reg: maxItems: 1 interrupts: - maxItems: 3 + oneOf: + - items: + - description: CRU Interrupt for image_conv + - description: CRU Interrupt for image_conv_err + - description: CRU AXI master error interrupt + - items: + - description: CRU Interrupt for image_conv + - description: CRU AXI master error interrupt + - description: CRU Video Data AXI Master Address 0 Write End interrupt + - description: CRU Statistics data AXI master addr 0 write end interrupt + - description: CRU Video statistics data AXI master addr 0 write end interrupt interrupt-names: - items: - - const: image_conv - - const: image_conv_err - - const: axi_mst_err + oneOf: + - items: + - const: image_conv + - const: image_conv_err + - const: axi_mst_err + - items: + - const: image_conv + - const: axi_mst_err + - const: vd_addr_wend + - const: sd_addr_wend + - const: vsd_addr_wend clocks: items: @@ -109,6 +128,10 @@ allOf: - renesas,r9a07g054-cru then: properties: + interrupts: + maxItems: 3 + interrupt-names: + maxItems: 3 ports: required: - port@0 @@ -122,10 +145,30 @@ allOf: - renesas,r9a07g043-cru then: properties: + interrupts: + maxItems: 3 + interrupt-names: + maxItems: 3 ports: properties: port@0: false + required: + - port@1 + - if: + properties: + compatible: + contains: + const: renesas,r9a09g047-cru + then: + properties: + interrupts: + minItems: 5 + interrupt-names: + minItems: 5 + ports: + properties: + port@0: false required: - port@1 -- cgit v1.2.3-59-g8ed1b From 7c537ccfe8982b186a6becc646872cde6061c8a6 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 11 Apr 2025 19:05:32 +0200 Subject: media: rzg2l-cru: csi2: Use local variable for struct device in rzg2l_csi2_probe() Use a local variable for the struct device pointers. This increases code readability with shortened lines. Reviewed-by: Laurent Pinchart Reviewed-by: Biju Das Signed-off-by: Lad Prabhakar Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-5-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/rzg2l-cru/rzg2l-csi2.c | 31 +++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index 881e910dce02..948f1917b830 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -764,10 +764,11 @@ static const struct media_entity_operations rzg2l_csi2_entity_ops = { static int rzg2l_csi2_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct rzg2l_csi2 *csi2; int ret; - csi2 = devm_kzalloc(&pdev->dev, sizeof(*csi2), GFP_KERNEL); + csi2 = devm_kzalloc(dev, sizeof(*csi2), GFP_KERNEL); if (!csi2) return -ENOMEM; @@ -775,28 +776,28 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) if (IS_ERR(csi2->base)) return PTR_ERR(csi2->base); - csi2->cmn_rstb = devm_reset_control_get_exclusive(&pdev->dev, "cmn-rstb"); + csi2->cmn_rstb = devm_reset_control_get_exclusive(dev, "cmn-rstb"); if (IS_ERR(csi2->cmn_rstb)) - return dev_err_probe(&pdev->dev, PTR_ERR(csi2->cmn_rstb), + return dev_err_probe(dev, PTR_ERR(csi2->cmn_rstb), "Failed to get cpg cmn-rstb\n"); - csi2->presetn = devm_reset_control_get_shared(&pdev->dev, "presetn"); + csi2->presetn = devm_reset_control_get_shared(dev, "presetn"); if (IS_ERR(csi2->presetn)) - return dev_err_probe(&pdev->dev, PTR_ERR(csi2->presetn), + return dev_err_probe(dev, PTR_ERR(csi2->presetn), "Failed to get cpg presetn\n"); - csi2->sysclk = devm_clk_get(&pdev->dev, "system"); + csi2->sysclk = devm_clk_get(dev, "system"); if (IS_ERR(csi2->sysclk)) - return dev_err_probe(&pdev->dev, PTR_ERR(csi2->sysclk), + return dev_err_probe(dev, PTR_ERR(csi2->sysclk), "Failed to get system clk\n"); - csi2->vclk = devm_clk_get(&pdev->dev, "video"); + csi2->vclk = devm_clk_get(dev, "video"); if (IS_ERR(csi2->vclk)) - return dev_err_probe(&pdev->dev, PTR_ERR(csi2->vclk), + return dev_err_probe(dev, PTR_ERR(csi2->vclk), "Failed to get video clock\n"); csi2->vclk_rate = clk_get_rate(csi2->vclk); - csi2->dev = &pdev->dev; + csi2->dev = dev; platform_set_drvdata(pdev, csi2); @@ -804,18 +805,18 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) if (ret) return ret; - pm_runtime_enable(&pdev->dev); + pm_runtime_enable(dev); ret = rzg2l_validate_csi2_lanes(csi2); if (ret) goto error_pm; - csi2->subdev.dev = &pdev->dev; + csi2->subdev.dev = dev; v4l2_subdev_init(&csi2->subdev, &rzg2l_csi2_subdev_ops); csi2->subdev.internal_ops = &rzg2l_csi2_internal_ops; - v4l2_set_subdevdata(&csi2->subdev, &pdev->dev); + v4l2_set_subdevdata(&csi2->subdev, dev); snprintf(csi2->subdev.name, sizeof(csi2->subdev.name), - "csi-%s", dev_name(&pdev->dev)); + "csi-%s", dev_name(dev)); csi2->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; csi2->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; @@ -852,7 +853,7 @@ error_async: v4l2_async_nf_cleanup(&csi2->notifier); media_entity_cleanup(&csi2->subdev.entity); error_pm: - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); return ret; } -- cgit v1.2.3-59-g8ed1b From 198be9e98bda5b18d203134c293597e6add9f5c6 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Fri, 11 Apr 2025 19:05:33 +0200 Subject: media: rzg2l-cru: csi2: Use devm_pm_runtime_enable() Use newly added devm_pm_runtime_enable() into rzg2l_csi2_probe() and drop error path accordingly. Drop also unnecessary pm_runtime_disable() from rzg2l_csi2_remove(). Reviewed-by: Lad Prabhakar Reviewed-by: Laurent Pinchart Reviewed-by: Biju Das Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-6-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index 948f1917b830..4ccf7c5ea58b 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -805,11 +805,13 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) if (ret) return ret; - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; ret = rzg2l_validate_csi2_lanes(csi2); if (ret) - goto error_pm; + return ret; csi2->subdev.dev = dev; v4l2_subdev_init(&csi2->subdev, &rzg2l_csi2_subdev_ops); @@ -834,7 +836,7 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) ret = media_entity_pads_init(&csi2->subdev.entity, ARRAY_SIZE(csi2->pads), csi2->pads); if (ret) - goto error_pm; + return ret; ret = v4l2_subdev_init_finalize(&csi2->subdev); if (ret < 0) @@ -852,8 +854,6 @@ error_async: v4l2_async_nf_unregister(&csi2->notifier); v4l2_async_nf_cleanup(&csi2->notifier); media_entity_cleanup(&csi2->subdev.entity); -error_pm: - pm_runtime_disable(dev); return ret; } @@ -867,7 +867,6 @@ static void rzg2l_csi2_remove(struct platform_device *pdev) v4l2_async_unregister_subdev(&csi2->subdev); v4l2_subdev_cleanup(&csi2->subdev); media_entity_cleanup(&csi2->subdev.entity); - pm_runtime_disable(&pdev->dev); } static int rzg2l_csi2_pm_runtime_suspend(struct device *dev) -- cgit v1.2.3-59-g8ed1b From aed5bbaec5346147b8580878a32809fdb1bf46c8 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 11 Apr 2025 19:05:34 +0200 Subject: media: rzg2l-cru: rzg2l-core: Use local variable for struct device in rzg2l_cru_probe() Use a local variable for the struct device pointers. This increases code readability with shortened lines. Reviewed-by: Laurent Pinchart Reviewed-by: Biju Das Signed-off-by: Lad Prabhakar Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-7-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/rzg2l-cru/rzg2l-core.c | 29 +++++++++++----------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c index 89be584a4988..70fed0ce45ea 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -240,10 +240,11 @@ static int rzg2l_cru_media_init(struct rzg2l_cru_dev *cru) static int rzg2l_cru_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct rzg2l_cru_dev *cru; int irq, ret; - cru = devm_kzalloc(&pdev->dev, sizeof(*cru), GFP_KERNEL); + cru = devm_kzalloc(dev, sizeof(*cru), GFP_KERNEL); if (!cru) return -ENOMEM; @@ -251,32 +252,32 @@ static int rzg2l_cru_probe(struct platform_device *pdev) if (IS_ERR(cru->base)) return PTR_ERR(cru->base); - cru->presetn = devm_reset_control_get_shared(&pdev->dev, "presetn"); + cru->presetn = devm_reset_control_get_shared(dev, "presetn"); if (IS_ERR(cru->presetn)) - return dev_err_probe(&pdev->dev, PTR_ERR(cru->presetn), + return dev_err_probe(dev, PTR_ERR(cru->presetn), "Failed to get cpg presetn\n"); - cru->aresetn = devm_reset_control_get_exclusive(&pdev->dev, "aresetn"); + cru->aresetn = devm_reset_control_get_exclusive(dev, "aresetn"); if (IS_ERR(cru->aresetn)) - return dev_err_probe(&pdev->dev, PTR_ERR(cru->aresetn), + return dev_err_probe(dev, PTR_ERR(cru->aresetn), "Failed to get cpg aresetn\n"); - cru->vclk = devm_clk_get(&pdev->dev, "video"); + cru->vclk = devm_clk_get(dev, "video"); if (IS_ERR(cru->vclk)) - return dev_err_probe(&pdev->dev, PTR_ERR(cru->vclk), + return dev_err_probe(dev, PTR_ERR(cru->vclk), "Failed to get video clock\n"); - cru->dev = &pdev->dev; - cru->info = of_device_get_match_data(&pdev->dev); + cru->dev = dev; + cru->info = of_device_get_match_data(dev); irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; - ret = devm_request_irq(&pdev->dev, irq, rzg2l_cru_irq, 0, + ret = devm_request_irq(dev, irq, rzg2l_cru_irq, 0, KBUILD_MODNAME, cru); if (ret) - return dev_err_probe(&pdev->dev, ret, "failed to request irq\n"); + return dev_err_probe(dev, ret, "failed to request irq\n"); platform_set_drvdata(pdev, cru); @@ -285,8 +286,8 @@ static int rzg2l_cru_probe(struct platform_device *pdev) return ret; cru->num_buf = RZG2L_CRU_HW_BUFFER_DEFAULT; - pm_suspend_ignore_children(&pdev->dev, true); - pm_runtime_enable(&pdev->dev); + pm_suspend_ignore_children(dev, true); + pm_runtime_enable(dev); ret = rzg2l_cru_media_init(cru); if (ret) @@ -296,7 +297,7 @@ static int rzg2l_cru_probe(struct platform_device *pdev) error_dma_unregister: rzg2l_cru_dma_unregister(cru); - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); return ret; } -- cgit v1.2.3-59-g8ed1b From 2fc8cfe06e7628de825d53128fd38b3f0e19c989 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Fri, 11 Apr 2025 19:05:35 +0200 Subject: media: rzg2l-cru: rzg2l-core: Use devm_pm_runtime_enable() Use newly added devm_pm_runtime_enable() into rzg2l_cru_probe() and drop unnecessary pm_runtime_disable() from rzg2l_cru_probe() and rzg2l_csi2_remove(). Reviewed-by: Lad Prabhakar Reviewed-by: Laurent Pinchart Reviewed-by: Biju Das Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-8-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c index 70fed0ce45ea..eed9d2bd0841 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -287,7 +287,9 @@ static int rzg2l_cru_probe(struct platform_device *pdev) cru->num_buf = RZG2L_CRU_HW_BUFFER_DEFAULT; pm_suspend_ignore_children(dev, true); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + goto error_dma_unregister; ret = rzg2l_cru_media_init(cru); if (ret) @@ -297,7 +299,6 @@ static int rzg2l_cru_probe(struct platform_device *pdev) error_dma_unregister: rzg2l_cru_dma_unregister(cru); - pm_runtime_disable(dev); return ret; } @@ -306,8 +307,6 @@ static void rzg2l_cru_remove(struct platform_device *pdev) { struct rzg2l_cru_dev *cru = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); - v4l2_async_nf_unregister(&cru->notifier); v4l2_async_nf_cleanup(&cru->notifier); -- cgit v1.2.3-59-g8ed1b From 15cef2dc7d688d5fc4919aa3c5c272931a8cd087 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 11 Apr 2025 19:05:36 +0200 Subject: media: rzg2l-cru: csi2: Introduce SoC-specific D-PHY handling In preparation for adding support for the RZ/V2H(P) SoC, where the D-PHY differs from the existing RZ/G2L implementation, introduce a new rzg2l_csi2_info structure. This structure provides function pointers for SoC-specific D-PHY enable and disable operations. Modify rzg2l_csi2_dphy_setting() to use these function pointers instead of calling rzg2l_csi2_dphy_enable() and rzg2l_csi2_dphy_disable() directly. Update the device match table to store the appropriate function pointers for each compatible SoC. This change prepares the driver for future extensions without affecting the current functionality for RZ/G2L. Reviewed-by: Laurent Pinchart Signed-off-by: Lad Prabhakar Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-9-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/rzg2l-cru/rzg2l-csi2.c | 24 +++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index 4ccf7c5ea58b..4aa5d58dde5b 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -107,6 +107,7 @@ struct rzg2l_csi2 { void __iomem *base; struct reset_control *presetn; struct reset_control *cmn_rstb; + const struct rzg2l_csi2_info *info; struct clk *sysclk; struct clk *vclk; unsigned long vclk_rate; @@ -123,6 +124,11 @@ struct rzg2l_csi2 { bool dphy_enabled; }; +struct rzg2l_csi2_info { + int (*dphy_enable)(struct rzg2l_csi2 *csi2); + int (*dphy_disable)(struct rzg2l_csi2 *csi2); +}; + struct rzg2l_csi2_timings { u32 t_init; u32 tclk_miss; @@ -355,14 +361,19 @@ static int rzg2l_csi2_dphy_enable(struct rzg2l_csi2 *csi2) return ret; } +static const struct rzg2l_csi2_info rzg2l_csi2_info = { + .dphy_enable = rzg2l_csi2_dphy_enable, + .dphy_disable = rzg2l_csi2_dphy_disable, +}; + static int rzg2l_csi2_dphy_setting(struct v4l2_subdev *sd, bool on) { struct rzg2l_csi2 *csi2 = sd_to_csi2(sd); if (on) - return rzg2l_csi2_dphy_enable(csi2); + return csi2->info->dphy_enable(csi2); - return rzg2l_csi2_dphy_disable(csi2); + return csi2->info->dphy_disable(csi2); } static int rzg2l_csi2_mipi_link_enable(struct rzg2l_csi2 *csi2) @@ -772,6 +783,10 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) if (!csi2) return -ENOMEM; + csi2->info = of_device_get_match_data(dev); + if (!csi2->info) + return dev_err_probe(dev, -EINVAL, "Failed to get OF match data\n"); + csi2->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(csi2->base)) return PTR_ERR(csi2->base); @@ -891,7 +906,10 @@ static const struct dev_pm_ops rzg2l_csi2_pm_ops = { }; static const struct of_device_id rzg2l_csi2_of_table[] = { - { .compatible = "renesas,rzg2l-csi2", }, + { + .compatible = "renesas,rzg2l-csi2", + .data = &rzg2l_csi2_info, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rzg2l_csi2_of_table); -- cgit v1.2.3-59-g8ed1b From ed472263fcc48f72e32cb494061bf8b8c333891a Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Fri, 11 Apr 2025 19:05:37 +0200 Subject: media: rzg2l-cru: csi2: Skip system clock for RZ/V2H(P) SoC The RZ/V2H(P) SoC does not require a `system` clock for the CSI-2 interface. To accommodate this, introduce a `has_system_clk` bool flag in the `rzg2l_csi2_info` structure and update the rzg2l_csi2_probe() to conditionally request the clock only when needed. This patch is in preparation for adding support for RZ/V2H(P) SoC. Reviewed-by: Laurent Pinchart Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-10-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index 4aa5d58dde5b..e4781105eadc 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -127,6 +127,7 @@ struct rzg2l_csi2 { struct rzg2l_csi2_info { int (*dphy_enable)(struct rzg2l_csi2 *csi2); int (*dphy_disable)(struct rzg2l_csi2 *csi2); + bool has_system_clk; }; struct rzg2l_csi2_timings { @@ -364,6 +365,7 @@ static int rzg2l_csi2_dphy_enable(struct rzg2l_csi2 *csi2) static const struct rzg2l_csi2_info rzg2l_csi2_info = { .dphy_enable = rzg2l_csi2_dphy_enable, .dphy_disable = rzg2l_csi2_dphy_disable, + .has_system_clk = true, }; static int rzg2l_csi2_dphy_setting(struct v4l2_subdev *sd, bool on) @@ -801,10 +803,12 @@ static int rzg2l_csi2_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(csi2->presetn), "Failed to get cpg presetn\n"); - csi2->sysclk = devm_clk_get(dev, "system"); - if (IS_ERR(csi2->sysclk)) - return dev_err_probe(dev, PTR_ERR(csi2->sysclk), - "Failed to get system clk\n"); + if (csi2->info->has_system_clk) { + csi2->sysclk = devm_clk_get(dev, "system"); + if (IS_ERR(csi2->sysclk)) + return dev_err_probe(dev, PTR_ERR(csi2->sysclk), + "Failed to get system clk\n"); + } csi2->vclk = devm_clk_get(dev, "video"); if (IS_ERR(csi2->vclk)) -- cgit v1.2.3-59-g8ed1b From 995cfd09ff8f60f57b3e3d49c809921b59993bae Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 11 Apr 2025 19:05:38 +0200 Subject: media: rzg2l-cru: csi2: Add support for RZ/V2H(P) SoC The D-PHY on the RZ/V2H(P) SoC is different from the D-PHY on the RZ/G2L SoC. To handle this difference, function pointers for D-PHY enable/disable have been added, and the `struct rzg2l_csi2_info` pointer is passed as OF data. Reviewed-by: Laurent Pinchart Signed-off-by: Lad Prabhakar Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-11-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/rzg2l-cru/rzg2l-csi2.c | 95 ++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index e4781105eadc..9243306e2aa9 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -85,6 +85,15 @@ CSIDPHYSKW0_UTIL_DL2_SKW_ADJ(1) | \ CSIDPHYSKW0_UTIL_DL3_SKW_ADJ(1)) +/* DPHY registers on RZ/V2H(P) SoC */ +#define CRUm_S_TIMCTL 0x41c +#define CRUm_S_TIMCTL_S_HSSETTLECTL(x) ((x) << 8) + +#define CRUm_S_DPHYCTL_MSB 0x434 +#define CRUm_S_DPHYCTL_MSB_DESKEW BIT(1) + +#define CRUm_SWAPCTL 0x438 + #define VSRSTS_RETRIES 20 #define RZG2L_CSI2_MIN_WIDTH 320 @@ -140,6 +149,30 @@ struct rzg2l_csi2_timings { u32 max_hsfreq; }; +struct rzv2h_csi2_s_hssettlectl { + unsigned int hsfreq; + u16 s_hssettlectl; +}; + +static const struct rzv2h_csi2_s_hssettlectl rzv2h_s_hssettlectl[] = { + { 90, 1 }, { 130, 2 }, { 180, 3 }, + { 220, 4 }, { 270, 5 }, { 310, 6 }, + { 360, 7 }, { 400, 8 }, { 450, 9 }, + { 490, 10 }, { 540, 11 }, { 580, 12 }, + { 630, 13 }, { 670, 14 }, { 720, 15 }, + { 760, 16 }, { 810, 17 }, { 850, 18 }, + { 900, 19 }, { 940, 20 }, { 990, 21 }, + { 1030, 22 }, { 1080, 23 }, { 1120, 24 }, + { 1170, 25 }, { 1220, 26 }, { 1260, 27 }, + { 1310, 28 }, { 1350, 29 }, { 1400, 30 }, + { 1440, 31 }, { 1490, 32 }, { 1530, 33 }, + { 1580, 34 }, { 1620, 35 }, { 1670, 36 }, + { 1710, 37 }, { 1760, 38 }, { 1800, 39 }, + { 1850, 40 }, { 1890, 41 }, { 1940, 42 }, + { 1980, 43 }, { 2030, 44 }, { 2070, 45 }, + { 2100, 46 }, +}; + static const struct rzg2l_csi2_timings rzg2l_csi2_global_timings[] = { { .max_hsfreq = 80, @@ -434,6 +467,64 @@ static int rzg2l_csi2_mipi_link_disable(struct rzg2l_csi2 *csi2) return 0; } +static int rzv2h_csi2_dphy_disable(struct rzg2l_csi2 *csi2) +{ + int ret; + + /* Reset the CRU (D-PHY) */ + ret = reset_control_assert(csi2->cmn_rstb); + if (ret) + return ret; + + csi2->dphy_enabled = false; + + return 0; +} + +static int rzv2h_csi2_dphy_enable(struct rzg2l_csi2 *csi2) +{ + unsigned int i; + u16 hssettle; + int mbps; + + mbps = rzg2l_csi2_calc_mbps(csi2); + if (mbps < 0) + return mbps; + + csi2->hsfreq = mbps; + + for (i = 0; i < ARRAY_SIZE(rzv2h_s_hssettlectl); i++) { + if (csi2->hsfreq <= rzv2h_s_hssettlectl[i].hsfreq) + break; + } + + if (i == ARRAY_SIZE(rzv2h_s_hssettlectl)) + return -EINVAL; + + rzg2l_csi2_write(csi2, CRUm_SWAPCTL, 0); + + hssettle = rzv2h_s_hssettlectl[i].s_hssettlectl; + rzg2l_csi2_write(csi2, CRUm_S_TIMCTL, + CRUm_S_TIMCTL_S_HSSETTLECTL(hssettle)); + + if (csi2->hsfreq > 1500) + rzg2l_csi2_set(csi2, CRUm_S_DPHYCTL_MSB, + CRUm_S_DPHYCTL_MSB_DESKEW); + else + rzg2l_csi2_clr(csi2, CRUm_S_DPHYCTL_MSB, + CRUm_S_DPHYCTL_MSB_DESKEW); + + csi2->dphy_enabled = true; + + return 0; +} + +static const struct rzg2l_csi2_info rzv2h_csi2_info = { + .dphy_enable = rzv2h_csi2_dphy_enable, + .dphy_disable = rzv2h_csi2_dphy_disable, + .has_system_clk = false, +}; + static int rzg2l_csi2_mipi_link_setting(struct v4l2_subdev *sd, bool on) { struct rzg2l_csi2 *csi2 = sd_to_csi2(sd); @@ -910,6 +1001,10 @@ static const struct dev_pm_ops rzg2l_csi2_pm_ops = { }; static const struct of_device_id rzg2l_csi2_of_table[] = { + { + .compatible = "renesas,r9a09g057-csi2", + .data = &rzv2h_csi2_info, + }, { .compatible = "renesas,rzg2l-csi2", .data = &rzg2l_csi2_info, -- cgit v1.2.3-59-g8ed1b From d9063dc50255db21b5a5e6ceada1e3a8927e44a1 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 11 Apr 2025 19:05:39 +0200 Subject: media: rzg2l-cru: Add register mapping support Prepare for adding support for RZ/G3E and RZ/V2HP SoCs, which have a CRU-IP that is mostly identical to RZ/G2L but with different register offsets and additional registers. Introduce a flexible register mapping mechanism to handle these variations. Define the `rzg2l_cru_info` structure to store register mappings and pass it as part of the OF match data. Update the read/write functions to check out-of-bound accesses and use indexed register offsets from `rzg2l_cru_info`, ensuring compatibility across different SoC variants. Reviewed-by: Laurent Pinchart Signed-off-by: Lad Prabhakar Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-12-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/rzg2l-cru/rzg2l-core.c | 43 +++++++++++++- .../platform/renesas/rzg2l-cru/rzg2l-cru-regs.h | 66 ++++++++++++---------- .../media/platform/renesas/rzg2l-cru/rzg2l-cru.h | 4 ++ .../media/platform/renesas/rzg2l-cru/rzg2l-video.c | 58 +++++++++++++++++-- 4 files changed, 136 insertions(+), 35 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c index eed9d2bd0841..2da416f91449 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -22,6 +22,7 @@ #include #include "rzg2l-cru.h" +#include "rzg2l-cru-regs.h" static inline struct rzg2l_cru_dev *notifier_to_cru(struct v4l2_async_notifier *n) { @@ -317,8 +318,48 @@ static void rzg2l_cru_remove(struct platform_device *pdev) rzg2l_cru_dma_unregister(cru); } +static const u16 rzg2l_cru_regs[] = { + [CRUnCTRL] = 0x0, + [CRUnIE] = 0x4, + [CRUnINTS] = 0x8, + [CRUnRST] = 0xc, + [AMnMB1ADDRL] = 0x100, + [AMnMB1ADDRH] = 0x104, + [AMnMB2ADDRL] = 0x108, + [AMnMB2ADDRH] = 0x10c, + [AMnMB3ADDRL] = 0x110, + [AMnMB3ADDRH] = 0x114, + [AMnMB4ADDRL] = 0x118, + [AMnMB4ADDRH] = 0x11c, + [AMnMB5ADDRL] = 0x120, + [AMnMB5ADDRH] = 0x124, + [AMnMB6ADDRL] = 0x128, + [AMnMB6ADDRH] = 0x12c, + [AMnMB7ADDRL] = 0x130, + [AMnMB7ADDRH] = 0x134, + [AMnMB8ADDRL] = 0x138, + [AMnMB8ADDRH] = 0x13c, + [AMnMBVALID] = 0x148, + [AMnMBS] = 0x14c, + [AMnAXIATTR] = 0x158, + [AMnFIFOPNTR] = 0x168, + [AMnAXISTP] = 0x174, + [AMnAXISTPACK] = 0x178, + [ICnEN] = 0x200, + [ICnMC] = 0x208, + [ICnMS] = 0x254, + [ICnDMR] = 0x26c, +}; + +static const struct rzg2l_cru_info rzgl2_cru_info = { + .regs = rzg2l_cru_regs, +}; + static const struct of_device_id rzg2l_cru_of_id_table[] = { - { .compatible = "renesas,rzg2l-cru", }, + { + .compatible = "renesas,rzg2l-cru", + .data = &rzgl2_cru_info, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rzg2l_cru_of_id_table); 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 1c9f22118a5d..fc0fd0c97c86 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h @@ -10,71 +10,77 @@ /* HW CRU Registers Definition */ -/* CRU Control Register */ -#define CRUnCTRL 0x0 #define CRUnCTRL_VINSEL(x) ((x) << 0) -/* CRU Interrupt Enable Register */ -#define CRUnIE 0x4 #define CRUnIE_EFE BIT(17) -/* CRU Interrupt Status Register */ -#define CRUnINTS 0x8 #define CRUnINTS_SFS BIT(16) -/* CRU Reset Register */ -#define CRUnRST 0xc #define CRUnRST_VRESETN BIT(0) /* Memory Bank Base Address (Lower) Register for CRU Image Data */ -#define AMnMBxADDRL(x) (0x100 + ((x) * 8)) +#define AMnMBxADDRL(x) (AMnMB1ADDRL + (x) * 2) /* Memory Bank Base Address (Higher) Register for CRU Image Data */ -#define AMnMBxADDRH(x) (0x104 + ((x) * 8)) +#define AMnMBxADDRH(x) (AMnMB1ADDRH + (x) * 2) -/* Memory Bank Enable Register for CRU Image Data */ -#define AMnMBVALID 0x148 #define AMnMBVALID_MBVALID(x) GENMASK(x, 0) -/* Memory Bank Status Register for CRU Image Data */ -#define AMnMBS 0x14c #define AMnMBS_MBSTS 0x7 -/* AXI Master Transfer Setting Register for CRU Image Data */ -#define AMnAXIATTR 0x158 #define AMnAXIATTR_AXILEN_MASK GENMASK(3, 0) #define AMnAXIATTR_AXILEN (0xf) -/* AXI Master FIFO Pointer Register for CRU Image Data */ -#define AMnFIFOPNTR 0x168 #define AMnFIFOPNTR_FIFOWPNTR GENMASK(7, 0) #define AMnFIFOPNTR_FIFORPNTR_Y GENMASK(23, 16) -/* AXI Master Transfer Stop Register for CRU Image Data */ -#define AMnAXISTP 0x174 #define AMnAXISTP_AXI_STOP BIT(0) -/* AXI Master Transfer Stop Status Register for CRU Image Data */ -#define AMnAXISTPACK 0x178 #define AMnAXISTPACK_AXI_STOP_ACK BIT(0) -/* CRU Image Processing Enable Register */ -#define ICnEN 0x200 #define ICnEN_ICEN BIT(0) -/* CRU Image Processing Main Control Register */ -#define ICnMC 0x208 #define ICnMC_CSCTHR BIT(5) #define ICnMC_INF(x) ((x) << 16) #define ICnMC_VCSEL(x) ((x) << 22) #define ICnMC_INF_MASK GENMASK(21, 16) -/* CRU Module Status Register */ -#define ICnMS 0x254 #define ICnMS_IA BIT(2) -/* CRU Data Output Mode Register */ -#define ICnDMR 0x26c #define ICnDMR_YCMODE_UYVY (1 << 4) +enum rzg2l_cru_common_regs { + CRUnCTRL, /* CRU Control */ + CRUnIE, /* CRU Interrupt Enable */ + CRUnINTS, /* CRU Interrupt Status */ + CRUnRST, /* CRU Reset */ + AMnMB1ADDRL, /* Bank 1 Address (Lower) for CRU Image Data */ + AMnMB1ADDRH, /* Bank 1 Address (Higher) for CRU Image Data */ + AMnMB2ADDRL, /* Bank 2 Address (Lower) for CRU Image Data */ + AMnMB2ADDRH, /* Bank 2 Address (Higher) for CRU Image Data */ + AMnMB3ADDRL, /* Bank 3 Address (Lower) for CRU Image Data */ + AMnMB3ADDRH, /* Bank 3 Address (Higher) for CRU Image Data */ + AMnMB4ADDRL, /* Bank 4 Address (Lower) for CRU Image Data */ + AMnMB4ADDRH, /* Bank 4 Address (Higher) for CRU Image Data */ + AMnMB5ADDRL, /* Bank 5 Address (Lower) for CRU Image Data */ + AMnMB5ADDRH, /* Bank 5 Address (Higher) for CRU Image Data */ + AMnMB6ADDRL, /* Bank 6 Address (Lower) for CRU Image Data */ + AMnMB6ADDRH, /* Bank 6 Address (Higher) for CRU Image Data */ + AMnMB7ADDRL, /* Bank 7 Address (Lower) for CRU Image Data */ + AMnMB7ADDRH, /* Bank 7 Address (Higher) for CRU Image Data */ + AMnMB8ADDRL, /* Bank 8 Address (Lower) for CRU Image Data */ + AMnMB8ADDRH, /* Bank 8 Address (Higher) for CRU Image Data */ + AMnMBVALID, /* Memory Bank Enable for CRU Image Data */ + AMnMBS, /* Memory Bank Status for CRU Image Data */ + AMnAXIATTR, /* AXI Master Transfer Setting Register for CRU Image Data */ + AMnFIFOPNTR, /* AXI Master FIFO Pointer for CRU Image Data */ + AMnAXISTP, /* AXI Master Transfer Stop for CRU Image Data */ + AMnAXISTPACK, /* AXI Master Transfer Stop Status for CRU Image Data */ + ICnEN, /* CRU Image Processing Enable */ + ICnMC, /* CRU Image Processing Main Control */ + ICnMS, /* CRU Module Status */ + ICnDMR, /* CRU Data Output Mode */ + RZG2L_CRU_MAX_REG, +}; + #endif /* __RZG2L_CRU_REGS_H__ */ diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 8b898ce05b84..00c3f7458e20 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -80,6 +80,10 @@ struct rzg2l_cru_ip_format { bool yuv; }; +struct rzg2l_cru_info { + const u16 *regs; +}; + /** * struct rzg2l_cru_dev - Renesas CRU device structure * @dev: (OF) device diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index cd69c8a686d3..c82db80c3355 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -42,16 +42,66 @@ struct rzg2l_cru_buffer { /* ----------------------------------------------------------------------------- * DMA operations */ -static void rzg2l_cru_write(struct rzg2l_cru_dev *cru, u32 offset, u32 value) +static void __rzg2l_cru_write(struct rzg2l_cru_dev *cru, u32 offset, u32 value) { - iowrite32(value, cru->base + offset); + const u16 *regs = cru->info->regs; + + /* + * CRUnCTRL is a first register on all CRU supported SoCs so validate + * rest of the registers have valid offset being set in cru->info->regs. + */ + if (WARN_ON(offset >= RZG2L_CRU_MAX_REG) || + WARN_ON(offset != CRUnCTRL && regs[offset] == 0)) + return; + + iowrite32(value, cru->base + regs[offset]); +} + +static u32 __rzg2l_cru_read(struct rzg2l_cru_dev *cru, u32 offset) +{ + const u16 *regs = cru->info->regs; + + /* + * CRUnCTRL is a first register on all CRU supported SoCs so validate + * rest of the registers have valid offset being set in cru->info->regs. + */ + if (WARN_ON(offset >= RZG2L_CRU_MAX_REG) || + WARN_ON(offset != CRUnCTRL && regs[offset] == 0)) + return 0; + + return ioread32(cru->base + regs[offset]); } -static u32 rzg2l_cru_read(struct rzg2l_cru_dev *cru, u32 offset) +static __always_inline void +__rzg2l_cru_write_constant(struct rzg2l_cru_dev *cru, u32 offset, u32 value) { - return ioread32(cru->base + offset); + const u16 *regs = cru->info->regs; + + BUILD_BUG_ON(offset >= RZG2L_CRU_MAX_REG); + + iowrite32(value, cru->base + regs[offset]); } +static __always_inline u32 +__rzg2l_cru_read_constant(struct rzg2l_cru_dev *cru, u32 offset) +{ + const u16 *regs = cru->info->regs; + + BUILD_BUG_ON(offset >= RZG2L_CRU_MAX_REG); + + return ioread32(cru->base + regs[offset]); +} + +#define rzg2l_cru_write(cru, offset, value) \ + (__builtin_constant_p(offset) ? \ + __rzg2l_cru_write_constant(cru, offset, value) : \ + __rzg2l_cru_write(cru, offset, value)) + +#define rzg2l_cru_read(cru, offset) \ + (__builtin_constant_p(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) -- cgit v1.2.3-59-g8ed1b From 5f5ed645b31b3e0bbd1a1597cdb41c467f3c7776 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 11 Apr 2025 19:05:40 +0200 Subject: media: rzg2l-cru: Pass resolution limits via OF data Pass `max_width` and `max_height` as part of the OF data to facilitate the addition of support for RZ/G3E and RZ/V2H(P) SoCs. These SoCs have a maximum resolution of 4096x4096 as compared to 2800x4095 on RZ/G2L SoC. This change prepares the driver for easier integration of these SoCs by defining the resolution limits in the `rzg2l_cru_info` structure. Reviewed-by: Laurent Pinchart Signed-off-by: Lad Prabhakar Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-13-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c | 2 ++ drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h | 4 ++-- drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c | 13 +++++++++---- drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 5 +++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c index 2da416f91449..e179f8d29038 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -352,6 +352,8 @@ static const u16 rzg2l_cru_regs[] = { }; static const struct rzg2l_cru_info rzgl2_cru_info = { + .max_width = 2800, + .max_height = 4095, .regs = rzg2l_cru_regs, }; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 00c3f7458e20..6a621073948a 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -27,9 +27,7 @@ #define RZG2L_CRU_CSI2_VCHANNEL 4 #define RZG2L_CRU_MIN_INPUT_WIDTH 320 -#define RZG2L_CRU_MAX_INPUT_WIDTH 2800 #define RZG2L_CRU_MIN_INPUT_HEIGHT 240 -#define RZG2L_CRU_MAX_INPUT_HEIGHT 4095 enum rzg2l_csi2_pads { RZG2L_CRU_IP_SINK = 0, @@ -81,6 +79,8 @@ struct rzg2l_cru_ip_format { }; struct rzg2l_cru_info { + unsigned int max_width; + unsigned int max_height; const u16 *regs; }; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c index 76a2b451f1da..7836c7cd53dc 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c @@ -148,6 +148,8 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_format *fmt) { + struct rzg2l_cru_dev *cru = v4l2_get_subdevdata(sd); + const struct rzg2l_cru_info *info = cru->info; struct v4l2_mbus_framefmt *src_format; struct v4l2_mbus_framefmt *sink_format; @@ -170,9 +172,9 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd, sink_format->ycbcr_enc = fmt->format.ycbcr_enc; sink_format->quantization = fmt->format.quantization; sink_format->width = clamp_t(u32, fmt->format.width, - RZG2L_CRU_MIN_INPUT_WIDTH, RZG2L_CRU_MAX_INPUT_WIDTH); + RZG2L_CRU_MIN_INPUT_WIDTH, info->max_width); sink_format->height = clamp_t(u32, fmt->format.height, - RZG2L_CRU_MIN_INPUT_HEIGHT, RZG2L_CRU_MAX_INPUT_HEIGHT); + RZG2L_CRU_MIN_INPUT_HEIGHT, info->max_height); fmt->format = *sink_format; @@ -197,6 +199,9 @@ static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_frame_size_enum *fse) { + struct rzg2l_cru_dev *cru = v4l2_get_subdevdata(sd); + const struct rzg2l_cru_info *info = cru->info; + if (fse->index != 0) return -EINVAL; @@ -205,8 +210,8 @@ static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, fse->min_width = RZG2L_CRU_MIN_INPUT_WIDTH; fse->min_height = RZG2L_CRU_MIN_INPUT_HEIGHT; - fse->max_width = RZG2L_CRU_MAX_INPUT_WIDTH; - fse->max_height = RZG2L_CRU_MAX_INPUT_HEIGHT; + fse->max_width = info->max_width; + fse->max_height = info->max_height; return 0; } diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index c82db80c3355..395c4d3d0f0f 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -736,6 +736,7 @@ error: static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, struct v4l2_pix_format *pix) { + const struct rzg2l_cru_info *info = cru->info; const struct rzg2l_cru_ip_format *fmt; fmt = rzg2l_cru_ip_format_to_fmt(pix->pixelformat); @@ -758,8 +759,8 @@ static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, } /* Limit to CRU capabilities */ - v4l_bound_align_image(&pix->width, 320, RZG2L_CRU_MAX_INPUT_WIDTH, 1, - &pix->height, 240, RZG2L_CRU_MAX_INPUT_HEIGHT, 2, 0); + v4l_bound_align_image(&pix->width, 320, info->max_width, 1, + &pix->height, 240, info->max_height, 2, 0); pix->bytesperline = pix->width * fmt->bpp; pix->sizeimage = pix->bytesperline * pix->height; -- cgit v1.2.3-59-g8ed1b From 48ce5920da1d65f22b4808564f6f5c9c4ff9d976 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 11 Apr 2025 19:05:41 +0200 Subject: media: rzg2l-cru: Add image_conv offset to OF data Add `image_conv` field to the `rzg2l_cru_info` structure to store the register offset for image conversion control. RZ/G2L uses `ICnMC`, while RZ/G3E and RZ/V2H(P) use `ICnIPMC_C0`. Update `rzg2l_cru_initialize_image_conv()` and `rzg2l_cru_csi2_setup()` to use this `image_conv` offset from the OF data, facilitating future support for RZ/G3E and RZ/V2H(P) SoCs. Reviewed-by: Laurent Pinchart Signed-off-by: Lad Prabhakar Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-14-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c | 1 + drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h | 1 + drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 14 ++++++++------ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c index e179f8d29038..e7fef7f3f822 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -354,6 +354,7 @@ static const u16 rzg2l_cru_regs[] = { static const struct rzg2l_cru_info rzgl2_cru_info = { .max_width = 2800, .max_height = 4095, + .image_conv = ICnMC, .regs = rzg2l_cru_regs, }; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 6a621073948a..ca156772b949 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -81,6 +81,7 @@ struct rzg2l_cru_ip_format { struct rzg2l_cru_info { unsigned int max_width; unsigned int max_height; + u16 image_conv; const u16 *regs; }; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 395c4d3d0f0f..95cce250b327 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -246,20 +246,22 @@ static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, const struct rzg2l_cru_ip_format *ip_fmt, u8 csi_vc) { + const struct rzg2l_cru_info *info = cru->info; u32 icnmc = ICnMC_INF(ip_fmt->datatype); - icnmc |= (rzg2l_cru_read(cru, ICnMC) & ~ICnMC_INF_MASK); + icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK; /* Set virtual channel CSI2 */ icnmc |= ICnMC_VCSEL(csi_vc); - rzg2l_cru_write(cru, ICnMC, icnmc); + rzg2l_cru_write(cru, info->image_conv, icnmc); } static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, struct v4l2_mbus_framefmt *ip_sd_fmt, u8 csi_vc) { + const struct rzg2l_cru_info *info = cru->info; const struct rzg2l_cru_ip_format *cru_video_fmt; const struct rzg2l_cru_ip_format *cru_ip_fmt; @@ -276,11 +278,11 @@ static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, /* If input and output use same colorspace, do bypass mode */ if (cru_ip_fmt->yuv == cru_video_fmt->yuv) - rzg2l_cru_write(cru, ICnMC, - rzg2l_cru_read(cru, ICnMC) | ICnMC_CSCTHR); + rzg2l_cru_write(cru, info->image_conv, + rzg2l_cru_read(cru, info->image_conv) | ICnMC_CSCTHR); else - rzg2l_cru_write(cru, ICnMC, - rzg2l_cru_read(cru, ICnMC) & (~ICnMC_CSCTHR)); + rzg2l_cru_write(cru, info->image_conv, + rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_CSCTHR); /* Set output data format */ rzg2l_cru_write(cru, ICnDMR, cru_video_fmt->icndmr); -- cgit v1.2.3-59-g8ed1b From 2d9e3eb740b7d256d0ee53a6a242f0ffc60b0c9b Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 11 Apr 2025 19:05:42 +0200 Subject: media: rzg2l-cru: Add IRQ handler to OF data Add `irq_handler` to the `rzg2l_cru_info` structure and pass it as part of the OF data. This prepares for supporting RZ/G3E and RZ/V2H(P) SoCs, which require a different IRQ handler. Update the IRQ request code to use the handler from the OF data. Add `enable_interrupts` and `disable_interrupts` function pointers to the `rzg2l_cru_info` structure and pass them as part of the OF data. This prepares for supporting RZ/G3E and RZ/V2H(P) SoCs, which require different interrupt configurations. Implement `rzg2l_cru_enable_interrupts()` and `rzg2l_cru_disable_interrupts()` functions and update the code to use them instead of directly writing to interrupt registers. Reviewed-by: Laurent Pinchart Signed-off-by: Lad Prabhakar Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-15-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c | 5 ++++- drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h | 8 ++++++++ .../media/platform/renesas/rzg2l-cru/rzg2l-video.c | 19 ++++++++++++++----- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c index e7fef7f3f822..ae989fa2768b 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -275,7 +275,7 @@ static int rzg2l_cru_probe(struct platform_device *pdev) if (irq < 0) return irq; - ret = devm_request_irq(dev, irq, rzg2l_cru_irq, 0, + ret = devm_request_irq(dev, irq, cru->info->irq_handler, 0, KBUILD_MODNAME, cru); if (ret) return dev_err_probe(dev, ret, "failed to request irq\n"); @@ -356,6 +356,9 @@ static const struct rzg2l_cru_info rzgl2_cru_info = { .max_height = 4095, .image_conv = ICnMC, .regs = rzg2l_cru_regs, + .irq_handler = rzg2l_cru_irq, + .enable_interrupts = rzg2l_cru_enable_interrupts, + .disable_interrupts = rzg2l_cru_disable_interrupts, }; static const struct of_device_id rzg2l_cru_of_id_table[] = { diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index ca156772b949..3f694044d8cd 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -34,6 +34,8 @@ enum rzg2l_csi2_pads { RZG2L_CRU_IP_SOURCE, }; +struct rzg2l_cru_dev; + /** * enum rzg2l_cru_dma_state - DMA states * @RZG2L_CRU_DMA_STOPPED: No operation in progress @@ -83,6 +85,9 @@ struct rzg2l_cru_info { unsigned int max_height; u16 image_conv; const u16 *regs; + irqreturn_t (*irq_handler)(int irq, void *data); + void (*enable_interrupts)(struct rzg2l_cru_dev *cru); + void (*disable_interrupts)(struct rzg2l_cru_dev *cru); }; /** @@ -177,4 +182,7 @@ const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code); const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format); const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index); +void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru); +void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru); + #endif diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 95cce250b327..a104821d823f 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -300,8 +300,7 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) spin_lock_irqsave(&cru->qlock, flags); /* Disable and clear the interrupt */ - rzg2l_cru_write(cru, CRUnIE, 0); - rzg2l_cru_write(cru, CRUnINTS, 0x001F0F0F); + cru->info->disable_interrupts(cru); /* Stop the operation of image conversion */ rzg2l_cru_write(cru, ICnEN, 0); @@ -393,6 +392,17 @@ static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru) return fd.entry[0].bus.csi2.vc; } +void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru) +{ + rzg2l_cru_write(cru, CRUnIE, CRUnIE_EFE); +} + +void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru) +{ + rzg2l_cru_write(cru, CRUnIE, 0); + rzg2l_cru_write(cru, CRUnINTS, 0x001f000f); +} + int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) { struct v4l2_mbus_framefmt *fmt = rzg2l_cru_ip_get_src_fmt(cru); @@ -414,8 +424,7 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) rzg2l_cru_write(cru, CRUnRST, CRUnRST_VRESETN); /* Disable and clear the interrupt before using */ - rzg2l_cru_write(cru, CRUnIE, 0); - rzg2l_cru_write(cru, CRUnINTS, 0x001f000f); + cru->info->disable_interrupts(cru); /* Initialize the AXI master */ rzg2l_cru_initialize_axi(cru); @@ -428,7 +437,7 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) } /* Enable interrupt */ - rzg2l_cru_write(cru, CRUnIE, CRUnIE_EFE); + cru->info->enable_interrupts(cru); /* Enable image processing reception */ rzg2l_cru_write(cru, ICnEN, ICnEN_ICEN); -- cgit v1.2.3-59-g8ed1b From 446c645f7fe480a4420072728faa1f261530b984 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 11 Apr 2025 19:05:43 +0200 Subject: media: rzg2l-cru: Add function pointer to check if FIFO is empty Add a `fifo_empty` function pointer to the `rzg2l_cru_info` structure and pass it as part of the OF data. On RZ/G3E and RZ/V2H(P) SoCs, checking if the FIFO is empty requires a different register configuration. Implement `rzg2l_fifo_empty()` and update the code to use it from the function pointer. Reviewed-by: Laurent Pinchart Signed-off-by: Lad Prabhakar Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-16-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/rzg2l-cru/rzg2l-core.c | 1 + .../media/platform/renesas/rzg2l-cru/rzg2l-cru.h | 3 +++ .../media/platform/renesas/rzg2l-cru/rzg2l-video.c | 23 +++++++++++++++------- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c index ae989fa2768b..3b5de8453319 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -359,6 +359,7 @@ static const struct rzg2l_cru_info rzgl2_cru_info = { .irq_handler = rzg2l_cru_irq, .enable_interrupts = rzg2l_cru_enable_interrupts, .disable_interrupts = rzg2l_cru_disable_interrupts, + .fifo_empty = rzg2l_fifo_empty, }; static const struct of_device_id rzg2l_cru_of_id_table[] = { diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 3f694044d8cd..2e17bfef43ce 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -88,6 +88,7 @@ struct rzg2l_cru_info { irqreturn_t (*irq_handler)(int irq, void *data); void (*enable_interrupts)(struct rzg2l_cru_dev *cru); void (*disable_interrupts)(struct rzg2l_cru_dev *cru); + bool (*fifo_empty)(struct rzg2l_cru_dev *cru); }; /** @@ -185,4 +186,6 @@ const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index); void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru); void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru); +bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru); + #endif diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index a104821d823f..d35e9b207493 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -290,9 +290,23 @@ static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, return 0; } -void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) +bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru) { u32 amnfifopntr, amnfifopntr_w, amnfifopntr_r_y; + + amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR); + + amnfifopntr_w = amnfifopntr & AMnFIFOPNTR_FIFOWPNTR; + amnfifopntr_r_y = + (amnfifopntr & AMnFIFOPNTR_FIFORPNTR_Y) >> 16; + if (amnfifopntr_w == amnfifopntr_r_y) + return true; + + return amnfifopntr_w == amnfifopntr_r_y; +} + +void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) +{ unsigned int retries = 0; unsigned long flags; u32 icnms; @@ -320,12 +334,7 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) /* Wait until the FIFO becomes empty */ for (retries = 5; retries > 0; retries--) { - amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR); - - amnfifopntr_w = amnfifopntr & AMnFIFOPNTR_FIFOWPNTR; - amnfifopntr_r_y = - (amnfifopntr & AMnFIFOPNTR_FIFORPNTR_Y) >> 16; - if (amnfifopntr_w == amnfifopntr_r_y) + if (cru->info->fifo_empty(cru)) break; usleep_range(10, 20); -- cgit v1.2.3-59-g8ed1b From 3c3433c5b3a0c34dd36d770e2950ad3377d97372 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 11 Apr 2025 19:05:44 +0200 Subject: media: rzg2l-cru: Add function pointer to configure CSI Add a `csi_setup` function pointer to the `rzg2l_cru_info` structure and pass it as part of the OF data. On RZ/G3E and RZ/V2H(P) SoCs, additional register configurations are required compared to the RZ/G2L SoC. Modify `rzg2l_cru_csi2_setup()` to be referenced through this function pointer and update the code to use it accordingly. This change is in preparation for adding support for RZ/G3E and RZ/V2H(P) SoCs. Reviewed-by: Laurent Pinchart Signed-off-by: Lad Prabhakar Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-17-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c | 1 + drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h | 6 ++++++ drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 8 ++++---- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c index 3b5de8453319..e0f04f7e19df 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -360,6 +360,7 @@ static const struct rzg2l_cru_info rzgl2_cru_info = { .enable_interrupts = rzg2l_cru_enable_interrupts, .disable_interrupts = rzg2l_cru_disable_interrupts, .fifo_empty = rzg2l_fifo_empty, + .csi_setup = rzg2l_cru_csi2_setup, }; static const struct of_device_id rzg2l_cru_of_id_table[] = { diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 2e17bfef43ce..ccaba5220f1c 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -89,6 +89,9 @@ struct rzg2l_cru_info { void (*enable_interrupts)(struct rzg2l_cru_dev *cru); void (*disable_interrupts)(struct rzg2l_cru_dev *cru); bool (*fifo_empty)(struct rzg2l_cru_dev *cru); + void (*csi_setup)(struct rzg2l_cru_dev *cru, + const struct rzg2l_cru_ip_format *ip_fmt, + u8 csi_vc); }; /** @@ -187,5 +190,8 @@ void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru); void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru); bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru); +void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, + const struct rzg2l_cru_ip_format *ip_fmt, + u8 csi_vc); #endif diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index d35e9b207493..809c43d686e2 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -242,9 +242,9 @@ static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) rzg2l_cru_write(cru, AMnAXIATTR, amnaxiattr); } -static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, - const struct rzg2l_cru_ip_format *ip_fmt, - u8 csi_vc) +void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, + const struct rzg2l_cru_ip_format *ip_fmt, + u8 csi_vc) { const struct rzg2l_cru_info *info = cru->info; u32 icnmc = ICnMC_INF(ip_fmt->datatype); @@ -266,7 +266,7 @@ static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, const struct rzg2l_cru_ip_format *cru_ip_fmt; cru_ip_fmt = rzg2l_cru_ip_code_to_fmt(ip_sd_fmt->code); - rzg2l_cru_csi2_setup(cru, cru_ip_fmt, csi_vc); + info->csi_setup(cru, cru_ip_fmt, csi_vc); /* Output format */ cru_video_fmt = rzg2l_cru_ip_format_to_fmt(cru->format.pixelformat); -- cgit v1.2.3-59-g8ed1b From 1d1e564fce1bc361af1a1980a7f915a0475a008a Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Fri, 11 Apr 2025 19:05:45 +0200 Subject: media: rzg2l-cru: Add support for RZ/G3E SoC The CRU block on the Renesas RZ/G3E SoC is similar to the one found on the Renesas RZ/G2L SoC, with the following differences: - Additional registers rzg3e_cru_regs. - A different irq handler rzg3e_cru_irq. - A different rzg3e_cru_csi2_setup. - A different max input width. - Additional stride register. Introduce rzg3e_cru_info struct to handle differences between RZ/G2L and RZ/G3E and related RZ/G3E functions: - rzg3e_cru_enable_interrupts() - rzg3e_cru_enable_interrupts() - rz3e_fifo_empty() - rzg3e_cru_csi2_setup() - rzg3e_cru_get_current_slot() Add then support for the RZ/G3E SoC CRU block with the new compatible string "renesas,r9a09g047-cru". Reviewed-by: Laurent Pinchart Signed-off-by: Lad Prabhakar Signed-off-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250411170624.472257-18-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/renesas/rzg2l-cru/rzg2l-core.c | 56 +++++++ .../platform/renesas/rzg2l-cru/rzg2l-cru-regs.h | 25 +++ .../media/platform/renesas/rzg2l-cru/rzg2l-cru.h | 13 ++ .../media/platform/renesas/rzg2l-cru/rzg2l-video.c | 168 ++++++++++++++++++++- 4 files changed, 261 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c index e0f04f7e19df..5fa73ab2db53 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -318,6 +318,58 @@ static void rzg2l_cru_remove(struct platform_device *pdev) rzg2l_cru_dma_unregister(cru); } +static const u16 rzg3e_cru_regs[] = { + [CRUnCTRL] = 0x0, + [CRUnIE] = 0x4, + [CRUnIE2] = 0x8, + [CRUnINTS] = 0xc, + [CRUnINTS2] = 0x10, + [CRUnRST] = 0x18, + [AMnMB1ADDRL] = 0x40, + [AMnMB1ADDRH] = 0x44, + [AMnMB2ADDRL] = 0x48, + [AMnMB2ADDRH] = 0x4c, + [AMnMB3ADDRL] = 0x50, + [AMnMB3ADDRH] = 0x54, + [AMnMB4ADDRL] = 0x58, + [AMnMB4ADDRH] = 0x5c, + [AMnMB5ADDRL] = 0x60, + [AMnMB5ADDRH] = 0x64, + [AMnMB6ADDRL] = 0x68, + [AMnMB6ADDRH] = 0x6c, + [AMnMB7ADDRL] = 0x70, + [AMnMB7ADDRH] = 0x74, + [AMnMB8ADDRL] = 0x78, + [AMnMB8ADDRH] = 0x7c, + [AMnMBVALID] = 0x88, + [AMnMADRSL] = 0x8c, + [AMnMADRSH] = 0x90, + [AMnAXIATTR] = 0xec, + [AMnFIFOPNTR] = 0xf8, + [AMnAXISTP] = 0x110, + [AMnAXISTPACK] = 0x114, + [AMnIS] = 0x128, + [ICnEN] = 0x1f0, + [ICnSVCNUM] = 0x1f8, + [ICnSVC] = 0x1fc, + [ICnIPMC_C0] = 0x200, + [ICnMS] = 0x2d8, + [ICnDMR] = 0x304, +}; + +static const struct rzg2l_cru_info rzg3e_cru_info = { + .max_width = 4095, + .max_height = 4095, + .image_conv = ICnIPMC_C0, + .has_stride = true, + .regs = rzg3e_cru_regs, + .irq_handler = rzg3e_cru_irq, + .enable_interrupts = rzg3e_cru_enable_interrupts, + .disable_interrupts = rzg3e_cru_disable_interrupts, + .fifo_empty = rz3e_fifo_empty, + .csi_setup = rzg3e_cru_csi2_setup, +}; + static const u16 rzg2l_cru_regs[] = { [CRUnCTRL] = 0x0, [CRUnIE] = 0x4, @@ -364,6 +416,10 @@ static const struct rzg2l_cru_info rzgl2_cru_info = { }; static const struct of_device_id rzg2l_cru_of_id_table[] = { + { + .compatible = "renesas,r9a09g047-cru", + .data = &rzg3e_cru_info, + }, { .compatible = "renesas,rzg2l-cru", .data = &rzgl2_cru_info, 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 fc0fd0c97c86..a5a57369ef0e 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h @@ -14,8 +14,13 @@ #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 CRUnRST_VRESETN BIT(0) /* Memory Bank Base Address (Lower) Register for CRU Image Data */ @@ -32,7 +37,14 @@ #define AMnAXIATTR_AXILEN (0xf) #define AMnFIFOPNTR_FIFOWPNTR GENMASK(7, 0) +#define AMnFIFOPNTR_FIFOWPNTR_B0 AMnFIFOPNTR_FIFOWPNTR +#define AMnFIFOPNTR_FIFOWPNTR_B1 GENMASK(15, 8) #define AMnFIFOPNTR_FIFORPNTR_Y GENMASK(23, 16) +#define AMnFIFOPNTR_FIFORPNTR_B0 AMnFIFOPNTR_FIFORPNTR_Y +#define AMnFIFOPNTR_FIFORPNTR_B1 GENMASK(31, 24) + +#define AMnIS_IS_MASK GENMASK(14, 7) +#define AMnIS_IS(x) ((x) << 7) #define AMnAXISTP_AXI_STOP BIT(0) @@ -40,6 +52,11 @@ #define ICnEN_ICEN BIT(0) +#define ICnSVC_SVC0(x) (x) +#define ICnSVC_SVC1(x) ((x) << 4) +#define ICnSVC_SVC2(x) ((x) << 8) +#define ICnSVC_SVC3(x) ((x) << 12) + #define ICnMC_CSCTHR BIT(5) #define ICnMC_INF(x) ((x) << 16) #define ICnMC_VCSEL(x) ((x) << 22) @@ -52,7 +69,9 @@ enum rzg2l_cru_common_regs { CRUnCTRL, /* CRU Control */ CRUnIE, /* CRU Interrupt Enable */ + CRUnIE2, /* CRU Interrupt Enable(2) */ CRUnINTS, /* CRU Interrupt Status */ + CRUnINTS2, /* CRU Interrupt Status(2) */ CRUnRST, /* CRU Reset */ AMnMB1ADDRL, /* Bank 1 Address (Lower) for CRU Image Data */ AMnMB1ADDRH, /* Bank 1 Address (Higher) for CRU Image Data */ @@ -72,12 +91,18 @@ enum rzg2l_cru_common_regs { AMnMB8ADDRH, /* Bank 8 Address (Higher) for CRU Image Data */ AMnMBVALID, /* Memory Bank Enable for CRU Image Data */ AMnMBS, /* Memory Bank Status for CRU Image Data */ + AMnMADRSL, /* VD Memory Address Lower Status Register */ + AMnMADRSH, /* VD Memory Address Higher Status Register */ AMnAXIATTR, /* AXI Master Transfer Setting Register for CRU Image Data */ AMnFIFOPNTR, /* AXI Master FIFO Pointer for CRU Image Data */ AMnAXISTP, /* AXI Master Transfer Stop for CRU Image Data */ AMnAXISTPACK, /* AXI Master Transfer Stop Status for CRU Image Data */ + AMnIS, /* Image Stride Setting Register */ ICnEN, /* CRU Image Processing Enable */ + ICnSVCNUM, /* CRU SVC Number Register */ + ICnSVC, /* CRU VC Select Register */ ICnMC, /* CRU Image Processing Main Control */ + ICnIPMC_C0, /* CRU Image Converter Main Control 0 */ ICnMS, /* CRU Module Status */ ICnDMR, /* CRU Data Output Mode */ RZG2L_CRU_MAX_REG, diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index ccaba5220f1c..c30f3b281284 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -85,6 +85,7 @@ struct rzg2l_cru_info { unsigned int max_height; u16 image_conv; const u16 *regs; + bool has_stride; irqreturn_t (*irq_handler)(int irq, void *data); void (*enable_interrupts)(struct rzg2l_cru_dev *cru); void (*disable_interrupts)(struct rzg2l_cru_dev *cru); @@ -108,6 +109,8 @@ struct rzg2l_cru_info { * @vdev: V4L2 video device associated with CRU * @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 @@ -144,6 +147,9 @@ struct rzg2l_cru_dev { struct v4l2_device v4l2_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; @@ -175,6 +181,7 @@ void rzg2l_cru_dma_unregister(struct rzg2l_cru_dev *cru); int rzg2l_cru_video_register(struct rzg2l_cru_dev *cru); void rzg2l_cru_video_unregister(struct rzg2l_cru_dev *cru); irqreturn_t rzg2l_cru_irq(int irq, void *data); +irqreturn_t rzg3e_cru_irq(int irq, void *data); const struct v4l2_format_info *rzg2l_cru_format_from_pixel(u32 format); @@ -188,10 +195,16 @@ const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index); void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru); void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru); +void rzg3e_cru_enable_interrupts(struct rzg2l_cru_dev *cru); +void rzg3e_cru_disable_interrupts(struct rzg2l_cru_dev *cru); bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru); +bool rz3e_fifo_empty(struct rzg2l_cru_dev *cru); void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, const struct rzg2l_cru_ip_format *ip_fmt, u8 csi_vc); +void rzg3e_cru_csi2_setup(struct rzg2l_cru_dev *cru, + const struct rzg2l_cru_ip_format *ip_fmt, + u8 csi_vc); #endif diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 809c43d686e2..067c6af14e95 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -31,6 +31,9 @@ #define RZG2L_CRU_DEFAULT_FIELD V4L2_FIELD_NONE #define RZG2L_CRU_DEFAULT_COLORSPACE V4L2_COLORSPACE_SRGB +#define RZG2L_CRU_STRIDE_MAX 32640 +#define RZG2L_CRU_STRIDE_ALIGN 128 + struct rzg2l_cru_buffer { struct vb2_v4l2_buffer vb; struct list_head list; @@ -184,6 +187,8 @@ static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, /* Currently, we just use the buffer in 32 bits address */ rzg2l_cru_write(cru, AMnMBxADDRL(slot), addr); rzg2l_cru_write(cru, AMnMBxADDRH(slot), 0); + + cru->buf_addr[slot] = addr; } /* @@ -224,6 +229,7 @@ static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot) static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) { + const struct rzg2l_cru_info *info = cru->info; unsigned int slot; u32 amnaxiattr; @@ -236,12 +242,39 @@ static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) for (slot = 0; slot < cru->num_buf; slot++) rzg2l_cru_fill_hw_slot(cru, slot); + if (info->has_stride) { + u32 stride = cru->format.bytesperline; + u32 amnis; + + stride /= RZG2L_CRU_STRIDE_ALIGN; + amnis = rzg2l_cru_read(cru, AMnIS) & ~AMnIS_IS_MASK; + rzg2l_cru_write(cru, AMnIS, amnis | AMnIS_IS(stride)); + } + /* Set AXI burst max length to recommended setting */ amnaxiattr = rzg2l_cru_read(cru, AMnAXIATTR) & ~AMnAXIATTR_AXILEN_MASK; amnaxiattr |= AMnAXIATTR_AXILEN; rzg2l_cru_write(cru, AMnAXIATTR, amnaxiattr); } +void rzg3e_cru_csi2_setup(struct rzg2l_cru_dev *cru, + const struct rzg2l_cru_ip_format *ip_fmt, + u8 csi_vc) +{ + const struct rzg2l_cru_info *info = cru->info; + u32 icnmc = ICnMC_INF(ip_fmt->datatype); + + icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK; + + /* Set virtual channel CSI2 */ + icnmc |= ICnMC_VCSEL(csi_vc); + + rzg2l_cru_write(cru, ICnSVCNUM, csi_vc); + rzg2l_cru_write(cru, ICnSVC, ICnSVC_SVC0(0) | ICnSVC_SVC1(1) | + ICnSVC_SVC2(2) | ICnSVC_SVC3(3)); + rzg2l_cru_write(cru, info->image_conv, icnmc); +} + void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, const struct rzg2l_cru_ip_format *ip_fmt, u8 csi_vc) @@ -290,6 +323,19 @@ static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, return 0; } +bool rz3e_fifo_empty(struct rzg2l_cru_dev *cru) +{ + u32 amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR); + + if ((((amnfifopntr & AMnFIFOPNTR_FIFORPNTR_B1) >> 24) == + ((amnfifopntr & AMnFIFOPNTR_FIFOWPNTR_B1) >> 8)) && + (((amnfifopntr & AMnFIFOPNTR_FIFORPNTR_B0) >> 16) == + (amnfifopntr & AMnFIFOPNTR_FIFOWPNTR_B0))) + return true; + + return false; +} + bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru) { u32 amnfifopntr, amnfifopntr_w, amnfifopntr_r_y; @@ -401,6 +447,20 @@ static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru) return fd.entry[0].bus.csi2.vc; } +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)); +} + +void rzg3e_cru_disable_interrupts(struct rzg2l_cru_dev *cru) +{ + rzg2l_cru_write(cru, CRUnIE, 0); + rzg2l_cru_write(cru, CRUnIE2, 0); + rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS)); + rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2)); +} + void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru) { rzg2l_cru_write(cru, CRUnIE, CRUnIE_EFE); @@ -423,6 +483,7 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) if (ret < 0) return ret; csi_vc = ret; + cru->svc_channel = csi_vc; spin_lock_irqsave(&cru->qlock, flags); @@ -601,6 +662,104 @@ done: 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; + u32 irq_status; + int slot; + + scoped_guard(spinlock, &cru->qlock) { + 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_FSxS(0) || + irq_status & CRUnINTS2_FSxS(1) || + irq_status & CRUnINTS2_FSxS(2) || + irq_status & CRUnINTS2_FSxS(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; +} + static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count) { struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); @@ -782,7 +941,14 @@ static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, v4l_bound_align_image(&pix->width, 320, info->max_width, 1, &pix->height, 240, info->max_height, 2, 0); - pix->bytesperline = pix->width * fmt->bpp; + if (info->has_stride) { + u32 stride = clamp(pix->bytesperline, pix->width * fmt->bpp, + RZG2L_CRU_STRIDE_MAX); + pix->bytesperline = round_up(stride, RZG2L_CRU_STRIDE_ALIGN); + } else { + pix->bytesperline = pix->width * fmt->bpp; + } + pix->sizeimage = pix->bytesperline * pix->height; dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n", -- cgit v1.2.3-59-g8ed1b From 3f5f28084eb9dbb408cf5429018a9b7a2859616f Mon Sep 17 00:00:00 2001 From: Nathan Hebert Date: Tue, 11 Feb 2025 16:34:51 -0800 Subject: media: mediatek: vcodec: Enable HEVC main still picture decode Mediatek devices that support HEVC also support the main still picture profile, but today, the main still picture profile is excluded. This removes the skip mask for HEVC, and enables the main still picture profile decoding. Signed-off-by: Nathan Hebert Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c index afa224da0f41..d873159b9b30 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c @@ -152,8 +152,6 @@ static const struct mtk_stateless_control mtk_stateless_controls[] = { .id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, .def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, - .menu_skip_mask = - BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE), }, .codec_type = V4L2_PIX_FMT_HEVC_SLICE, }, -- cgit v1.2.3-59-g8ed1b From d52b9b7e2f10d22a49468128540533e8d76910cd Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Mon, 21 Apr 2025 15:06:12 +0800 Subject: media: imx-jpeg: Drop the first error frames When an output buffer contains error frame header, v4l2_jpeg_parse_header() will return error, then driver will mark this buffer and a capture buffer done with error flag in device_run(). But if the error occurs in the first frames, before setup the capture queue, there is no chance to schedule device_run(), and there may be no capture to mark error. So we need to drop this buffer with error flag, and make the decoding can continue. Fixes: 2db16c6ed72c ("media: imx-jpeg: Add V4L2 driver for i.MX8 JPEG Encoder/Decoder") Cc: stable@vger.kernel.org Signed-off-by: Ming Qian Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index 1221b309a916..840dd62c2531 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -1918,9 +1918,19 @@ static void mxc_jpeg_buf_queue(struct vb2_buffer *vb) jpeg_src_buf = vb2_to_mxc_buf(vb); jpeg_src_buf->jpeg_parse_error = false; ret = mxc_jpeg_parse(ctx, vb); - if (ret) + if (ret) { jpeg_src_buf->jpeg_parse_error = true; + /* + * if the capture queue is not setup, the device_run() won't be scheduled, + * need to drop the error buffer, so that the decoding can continue + */ + if (!vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx))) { + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + return; + } + } + end: v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } -- cgit v1.2.3-59-g8ed1b From 7713800a6cc94b89caab13355b956bc5423250c8 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Wed, 9 Apr 2025 15:30:09 -0400 Subject: media: verisilicon: Enable NV15 support for Rockchip VDPU981 This is a "customer" format, though on Rockchip RK3588 it has been verified to be NV15 format, which matches what the GPU and display handles has 10bit pixel formats. Reviewed-by: Benjamin Gaignard Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/hantro_v4l2.c | 1 + .../platform/verisilicon/rockchip_vpu981_hw_av1_dec.c | 4 ++++ drivers/media/platform/verisilicon/rockchip_vpu_hw.c | 14 ++++++++++++++ 3 files changed, 19 insertions(+) diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c index 2bce940a5822..7c3515cf7d64 100644 --- a/drivers/media/platform/verisilicon/hantro_v4l2.c +++ b/drivers/media/platform/verisilicon/hantro_v4l2.c @@ -77,6 +77,7 @@ int hantro_get_format_depth(u32 fourcc) switch (fourcc) { case V4L2_PIX_FMT_P010: case V4L2_PIX_FMT_P010_4L4: + case V4L2_PIX_FMT_NV15: case V4L2_PIX_FMT_NV15_4L4: return 10; default: diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c index 69b5d9e12926..e4703bb6be7c 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c +++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c @@ -2202,6 +2202,10 @@ static void rockchip_vpu981_postproc_enable(struct hantro_ctx *ctx) case V4L2_PIX_FMT_NV12: hantro_reg_write(vpu, &av1_pp_out_format, 3); break; + case V4L2_PIX_FMT_NV15: + /* this mapping is RK specific */ + hantro_reg_write(vpu, &av1_pp_out_format, 10); + break; default: hantro_reg_write(vpu, &av1_pp_out_format, 0); } diff --git a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c index b64f0658f7f1..acd29fa41d2d 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c +++ b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c @@ -92,6 +92,20 @@ static const struct hantro_fmt rockchip_vpu981_postproc_fmts[] = { .step_height = MB_DIM, }, }, + { + .fourcc = V4L2_PIX_FMT_NV15, + .codec_mode = HANTRO_MODE_NONE, + .match_depth = true, + .postprocessed = true, + .frmsize = { + .min_width = ROCKCHIP_VPU981_MIN_SIZE, + .max_width = FMT_4K_WIDTH, + .step_width = MB_DIM, + .min_height = ROCKCHIP_VPU981_MIN_SIZE, + .max_height = FMT_4K_HEIGHT, + .step_height = MB_DIM, + }, + }, { .fourcc = V4L2_PIX_FMT_P010, .codec_mode = HANTRO_MODE_NONE, -- cgit v1.2.3-59-g8ed1b From 208699afb9f22f4d903ebdbdae45c2c6f2e308e0 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 18 Apr 2025 00:01:58 +0200 Subject: media: amphion: Slightly simplify vpu_core_register() "vpu_core->msg_buffer_size" is unused out-side of vpu_core_register(). There is no need to save this value in struct vpu_core. Remove it and use a local variable instead. Signed-off-by: Christophe JAILLET Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/amphion/vpu.h | 1 - drivers/media/platform/amphion/vpu_core.c | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h index 22f0da26ccec..1451549c9dd2 100644 --- a/drivers/media/platform/amphion/vpu.h +++ b/drivers/media/platform/amphion/vpu.h @@ -162,7 +162,6 @@ struct vpu_core { struct delayed_work msg_delayed_work; struct kfifo msg_fifo; void *msg_buffer; - unsigned int msg_buffer_size; struct vpu_dev *vpu; void *iface; diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c index 8df85c14ab3f..da00f5fc0e5d 100644 --- a/drivers/media/platform/amphion/vpu_core.c +++ b/drivers/media/platform/amphion/vpu_core.c @@ -250,6 +250,7 @@ static void vpu_core_get_vpu(struct vpu_core *core) static int vpu_core_register(struct device *dev, struct vpu_core *core) { struct vpu_dev *vpu = dev_get_drvdata(dev); + unsigned int buffer_size; int ret = 0; dev_dbg(core->dev, "register core %s\n", vpu_core_type_desc(core->type)); @@ -263,14 +264,14 @@ static int vpu_core_register(struct device *dev, struct vpu_core *core) } INIT_WORK(&core->msg_work, vpu_msg_run_work); INIT_DELAYED_WORK(&core->msg_delayed_work, vpu_msg_delayed_work); - core->msg_buffer_size = roundup_pow_of_two(VPU_MSG_BUFFER_SIZE); - core->msg_buffer = vzalloc(core->msg_buffer_size); + buffer_size = roundup_pow_of_two(VPU_MSG_BUFFER_SIZE); + core->msg_buffer = vzalloc(buffer_size); if (!core->msg_buffer) { dev_err(core->dev, "failed allocate buffer for fifo\n"); ret = -ENOMEM; goto error; } - ret = kfifo_init(&core->msg_fifo, core->msg_buffer, core->msg_buffer_size); + ret = kfifo_init(&core->msg_fifo, core->msg_buffer, buffer_size); if (ret) { dev_err(core->dev, "failed init kfifo\n"); goto error; -- cgit v1.2.3-59-g8ed1b From 609ba05b9484856b08869f827a6edee51d51b5f3 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 26 Feb 2025 20:49:22 +0800 Subject: media: imagination: fix a potential memory leak in e5010_probe() Add video_device_release() to release the memory allocated by video_device_alloc() if something goes wrong. Fixes: a1e294045885 ("media: imagination: Add E5010 JPEG Encoder driver") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/imagination/e5010-jpeg-enc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.c b/drivers/media/platform/imagination/e5010-jpeg-enc.c index c194f830577f..ae868d9f73e1 100644 --- a/drivers/media/platform/imagination/e5010-jpeg-enc.c +++ b/drivers/media/platform/imagination/e5010-jpeg-enc.c @@ -1057,8 +1057,11 @@ static int e5010_probe(struct platform_device *pdev) e5010->vdev->lock = &e5010->mutex; ret = v4l2_device_register(dev, &e5010->v4l2_dev); - if (ret) - return dev_err_probe(dev, ret, "failed to register v4l2 device\n"); + if (ret) { + dev_err_probe(dev, ret, "failed to register v4l2 device\n"); + goto fail_after_video_device_alloc; + } + e5010->m2m_dev = v4l2_m2m_init(&e5010_m2m_ops); if (IS_ERR(e5010->m2m_dev)) { @@ -1118,6 +1121,8 @@ fail_after_video_register_device: v4l2_m2m_release(e5010->m2m_dev); fail_after_v4l2_register: v4l2_device_unregister(&e5010->v4l2_dev); +fail_after_video_device_alloc: + video_device_release(e5010->vdev); return ret; } -- cgit v1.2.3-59-g8ed1b From 46e9c092f850bd7b4d06de92d3d21877f49a3fcb Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Mon, 21 Apr 2025 16:12:52 +0800 Subject: media: imx-jpeg: Move mxc_jpeg_free_slot_data() ahead Move function mxc_jpeg_free_slot_data() above mxc_jpeg_alloc_slot_data() allowing to call that function during allocation failures. No functional changes are made. Fixes: 2db16c6ed72c ("media: imx-jpeg: Add V4L2 driver for i.MX8 JPEG Encoder/Decoder") Cc: stable@vger.kernel.org Signed-off-by: Ming Qian Reviewed-by: Nicolas Dufresne Reviewed-by: Frank Li Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index 840dd62c2531..ad2284e87985 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -752,6 +752,26 @@ static int mxc_get_free_slot(struct mxc_jpeg_slot_data *slot_data) return -1; } +static void mxc_jpeg_free_slot_data(struct mxc_jpeg_dev *jpeg) +{ + /* free descriptor for decoding/encoding phase */ + dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc), + jpeg->slot_data.desc, + jpeg->slot_data.desc_handle); + + /* free descriptor for encoder configuration phase / decoder DHT */ + dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc), + jpeg->slot_data.cfg_desc, + jpeg->slot_data.cfg_desc_handle); + + /* free configuration stream */ + dma_free_coherent(jpeg->dev, MXC_JPEG_MAX_CFG_STREAM, + jpeg->slot_data.cfg_stream_vaddr, + jpeg->slot_data.cfg_stream_handle); + + jpeg->slot_data.used = false; +} + static bool mxc_jpeg_alloc_slot_data(struct mxc_jpeg_dev *jpeg) { struct mxc_jpeg_desc *desc; @@ -798,26 +818,6 @@ err: return false; } -static void mxc_jpeg_free_slot_data(struct mxc_jpeg_dev *jpeg) -{ - /* free descriptor for decoding/encoding phase */ - dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc), - jpeg->slot_data.desc, - jpeg->slot_data.desc_handle); - - /* free descriptor for encoder configuration phase / decoder DHT */ - dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc), - jpeg->slot_data.cfg_desc, - jpeg->slot_data.cfg_desc_handle); - - /* free configuration stream */ - dma_free_coherent(jpeg->dev, MXC_JPEG_MAX_CFG_STREAM, - jpeg->slot_data.cfg_stream_vaddr, - jpeg->slot_data.cfg_stream_handle); - - jpeg->slot_data.used = false; -} - static void mxc_jpeg_check_and_set_last_buffer(struct mxc_jpeg_ctx *ctx, struct vb2_v4l2_buffer *src_buf, struct vb2_v4l2_buffer *dst_buf) -- cgit v1.2.3-59-g8ed1b From faa8051b128f4b34277ea8a026d02d83826f8122 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Mon, 21 Apr 2025 16:12:53 +0800 Subject: media: imx-jpeg: Reset slot data pointers when freed Ensure that the slot data pointers are reset to NULL and handles are set to 0 after freeing the coherent memory. This makes he function mxc_jpeg_alloc_slot_data() and mxc_jpeg_free_slot_data() safe to be called multiple times. Fixes: 2db16c6ed72c ("media: imx-jpeg: Add V4L2 driver for i.MX8 JPEG Encoder/Decoder") Cc: stable@vger.kernel.org Signed-off-by: Ming Qian Reviewed-by: Nicolas Dufresne Reviewed-by: Frank Li Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index ad2284e87985..29d3d4b08dd1 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -758,16 +758,22 @@ static void mxc_jpeg_free_slot_data(struct mxc_jpeg_dev *jpeg) dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc), jpeg->slot_data.desc, jpeg->slot_data.desc_handle); + jpeg->slot_data.desc = NULL; + jpeg->slot_data.desc_handle = 0; /* free descriptor for encoder configuration phase / decoder DHT */ dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc), jpeg->slot_data.cfg_desc, jpeg->slot_data.cfg_desc_handle); + jpeg->slot_data.cfg_desc_handle = 0; + jpeg->slot_data.cfg_desc = NULL; /* free configuration stream */ dma_free_coherent(jpeg->dev, MXC_JPEG_MAX_CFG_STREAM, jpeg->slot_data.cfg_stream_vaddr, jpeg->slot_data.cfg_stream_handle); + jpeg->slot_data.cfg_stream_vaddr = NULL; + jpeg->slot_data.cfg_stream_handle = 0; jpeg->slot_data.used = false; } -- cgit v1.2.3-59-g8ed1b From 7500bb9cf164edbb2c8117d57620227b1a4a8369 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Mon, 21 Apr 2025 16:12:54 +0800 Subject: media: imx-jpeg: Cleanup after an allocation error When allocation failures are not cleaned up by the driver, further allocation errors will be false-positives, which will cause buffers to remain uninitialized and cause NULL pointer dereferences. Ensure proper cleanup of failed allocations to prevent these issues. Fixes: 2db16c6ed72c ("media: imx-jpeg: Add V4L2 driver for i.MX8 JPEG Encoder/Decoder") Cc: stable@vger.kernel.org Signed-off-by: Ming Qian Reviewed-by: Frank Li Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index 29d3d4b08dd1..8a25ea8905ae 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -820,6 +820,7 @@ skip_alloc: return true; err: dev_err(jpeg->dev, "Could not allocate descriptors for slot %d", jpeg->slot_data.slot); + mxc_jpeg_free_slot_data(jpeg); return false; } -- cgit v1.2.3-59-g8ed1b From f65fbf8c3d6711325f947b49d4067e7d05fd79de Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Mon, 21 Apr 2025 16:12:55 +0800 Subject: media: imx-jpeg: Change the pattern size to 128x64 In order to decode a motion-jpeg bitstream, which doesn't provide a DHT, the driver will first decode a pattern jpeg and use the DHT found in the pattern to decode the first actual frame. This mode is called "repeat-mode" and it utilizes linked descriptors. The smallest supported resolution of 64x64 was used for that pattern to not cause unneeded performance delay. This choice, however, can cause a corrupted decoded picture of the first frame after the pattern, when the resolution of that frame is larger than the pattern and is not aligned to 64. By altering the pattern size to 128x64, this corruption can be avoided. That size has been confirmed to be safe by the hardware designers. Additionally, a DMA buffer needs to be allocated to store the decoded picture of the pattern image. Signed-off-by: Ming Qian Reviewed-by: Frank Li Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c | 42 +++++++++++++++++++++----- drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h | 5 +++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index 8a25ea8905ae..d72e15ee6d32 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -535,7 +535,18 @@ static const unsigned char jpeg_sos_maximal[] = { }; static const unsigned char jpeg_image_red[] = { - 0xFC, 0x5F, 0xA2, 0xBF, 0xCA, 0x73, 0xFE, 0xFE, + 0xF9, 0xFE, 0x8A, 0xFC, 0x34, 0xFD, 0xC4, 0x28, + 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, + 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, + 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, + 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, + 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, + 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, + 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, + 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, + 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, + 0x8A, 0x00, 0x28, 0xA0, 0x0F, 0xFF, 0xD0, 0xF9, + 0xFE, 0x8A, 0xFC, 0x34, 0xFD, 0xC4, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, @@ -545,7 +556,7 @@ static const unsigned char jpeg_image_red[] = { 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, - 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00 + 0x00, 0x28, 0xA0, 0x0F }; static const unsigned char jpeg_eoi[] = { @@ -775,6 +786,13 @@ static void mxc_jpeg_free_slot_data(struct mxc_jpeg_dev *jpeg) jpeg->slot_data.cfg_stream_vaddr = NULL; jpeg->slot_data.cfg_stream_handle = 0; + dma_free_coherent(jpeg->dev, jpeg->slot_data.cfg_dec_size, + jpeg->slot_data.cfg_dec_vaddr, + jpeg->slot_data.cfg_dec_daddr); + jpeg->slot_data.cfg_dec_size = 0; + jpeg->slot_data.cfg_dec_vaddr = NULL; + jpeg->slot_data.cfg_dec_daddr = 0; + jpeg->slot_data.used = false; } @@ -814,6 +832,14 @@ static bool mxc_jpeg_alloc_slot_data(struct mxc_jpeg_dev *jpeg) goto err; jpeg->slot_data.cfg_stream_vaddr = cfg_stm; + jpeg->slot_data.cfg_dec_size = MXC_JPEG_PATTERN_WIDTH * MXC_JPEG_PATTERN_HEIGHT * 2; + jpeg->slot_data.cfg_dec_vaddr = dma_alloc_coherent(jpeg->dev, + jpeg->slot_data.cfg_dec_size, + &jpeg->slot_data.cfg_dec_daddr, + GFP_ATOMIC); + if (!jpeg->slot_data.cfg_dec_vaddr) + goto err; + skip_alloc: jpeg->slot_data.used = true; @@ -1216,14 +1242,14 @@ static void mxc_jpeg_config_dec_desc(struct vb2_buffer *out_buf, */ *cfg_size = mxc_jpeg_setup_cfg_stream(cfg_stream_vaddr, V4L2_PIX_FMT_YUYV, - MXC_JPEG_MIN_WIDTH, - MXC_JPEG_MIN_HEIGHT); + MXC_JPEG_PATTERN_WIDTH, + MXC_JPEG_PATTERN_HEIGHT); cfg_desc->next_descpt_ptr = desc_handle | MXC_NXT_DESCPT_EN; - cfg_desc->buf_base0 = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + cfg_desc->buf_base0 = jpeg->slot_data.cfg_dec_daddr; cfg_desc->buf_base1 = 0; - cfg_desc->imgsize = MXC_JPEG_MIN_WIDTH << 16; - cfg_desc->imgsize |= MXC_JPEG_MIN_HEIGHT; - cfg_desc->line_pitch = MXC_JPEG_MIN_WIDTH * 2; + cfg_desc->imgsize = MXC_JPEG_PATTERN_WIDTH << 16; + cfg_desc->imgsize |= MXC_JPEG_PATTERN_HEIGHT; + cfg_desc->line_pitch = MXC_JPEG_PATTERN_WIDTH * 2; cfg_desc->stm_ctrl = STM_CTRL_IMAGE_FORMAT(MXC_JPEG_YUV422); cfg_desc->stm_ctrl |= STM_CTRL_BITBUF_PTR_CLR(1); cfg_desc->stm_bufbase = cfg_stream_handle; diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h index 86e324b21aed..fdde45f7e163 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h @@ -28,6 +28,8 @@ #define MXC_JPEG_W_ALIGN 3 #define MXC_JPEG_MAX_SIZEIMAGE 0xFFFFFC00 #define MXC_JPEG_MAX_PLANES 2 +#define MXC_JPEG_PATTERN_WIDTH 128 +#define MXC_JPEG_PATTERN_HEIGHT 64 enum mxc_jpeg_enc_state { MXC_JPEG_ENCODING = 0, /* jpeg encode phase */ @@ -117,6 +119,9 @@ struct mxc_jpeg_slot_data { dma_addr_t desc_handle; dma_addr_t cfg_desc_handle; // configuration descriptor dma address dma_addr_t cfg_stream_handle; // configuration bitstream dma address + dma_addr_t cfg_dec_size; + void *cfg_dec_vaddr; + dma_addr_t cfg_dec_daddr; }; struct mxc_jpeg_dev { -- cgit v1.2.3-59-g8ed1b From fd5b6cd730676940df63b0970bb1ba30bca1aac3 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Mon, 21 Apr 2025 16:12:56 +0800 Subject: media: imx-jpeg: Check decoding is ongoing for motion-jpeg As the first frame in "repeat-mode" is the pattern, the pattern done interrupt is ignored by the driver. With small resolution bitstreams, the interrupts might fire too quickly and hardware combine two irqs to once because irq handle have latency. Thus the driver might miss the frame decode done interrupt from the first actual frame. In order to avoid the driver wait for the frame done interrupt that has been combined to the pattern done interrupt and been ignored, driver will check the curr_desc and slot_status registers to figure out if the decoding of actual frame is finished or not. Firstly we check the curr_desc register, - if it is still pointing to the pattern descriptor, the second actual frame is not started, we can wait for its frame-done interrupt. - if the curr_desc has pointed to the frame descriptor, then we check the ongoing bit of slot_status register. - if the ongoing bit is set to 1, the decoding of the actual frame is not finished, we can wait for its frame-done interrupt. - if the ongoing bit is set to 0, the decoding of the actual frame is finished, we can't wait for the second interrupt, but mark it as done. But there is still a small problem, that the curr_desc and slot_status registers are not synchronous. curr_desc is updated when the next_descpt_ptr is loaded, but the ongoing bit of slot_status is set after the 32 bytes descriptor is loaded, there will be a short time interval in between, which may cause fake false. Consider read register is quite slow compared with IP read 32byte from memory, read twice slot_status can avoid this situation. Signed-off-by: Ming Qian Reviewed-by: Frank Li Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h | 1 + drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c | 31 ++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h index d579c804b047..adb93e977be9 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h @@ -89,6 +89,7 @@ /* SLOT_STATUS fields for slots 0..3 */ #define SLOT_STATUS_FRMDONE (0x1 << 3) #define SLOT_STATUS_ENC_CONFIG_ERR (0x1 << 8) +#define SLOT_STATUS_ONGOING (0x1 << 31) /* SLOT_IRQ_EN fields TBD */ diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index d72e15ee6d32..5c17bc58181e 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -910,6 +910,34 @@ static u32 mxc_jpeg_get_plane_size(struct mxc_jpeg_q_data *q_data, u32 plane_no) return size; } +static bool mxc_dec_is_ongoing(struct mxc_jpeg_ctx *ctx) +{ + struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg; + u32 curr_desc; + u32 slot_status; + + curr_desc = readl(jpeg->base_reg + MXC_SLOT_OFFSET(ctx->slot, SLOT_CUR_DESCPT_PTR)); + if (curr_desc == jpeg->slot_data.cfg_desc_handle) + return true; + + slot_status = readl(jpeg->base_reg + MXC_SLOT_OFFSET(ctx->slot, SLOT_STATUS)); + if (slot_status & SLOT_STATUS_ONGOING) + return true; + + /* + * The curr_desc register is updated when next_descpt_ptr is loaded, + * the ongoing bit of slot_status is set when the 32 bytes descriptor is loaded. + * So there will be a short time interval in between, which may cause fake false. + * Consider read register is quite slow compared with IP read 32byte from memory, + * read twice slot_status can avoid this situation. + */ + slot_status = readl(jpeg->base_reg + MXC_SLOT_OFFSET(ctx->slot, SLOT_STATUS)); + if (slot_status & SLOT_STATUS_ONGOING) + return true; + + return false; +} + static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv) { struct mxc_jpeg_dev *jpeg = priv; @@ -979,7 +1007,8 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv) mxc_jpeg_enc_mode_go(dev, reg, mxc_jpeg_is_extended_sequential(q_data->fmt)); goto job_unlock; } - if (jpeg->mode == MXC_JPEG_DECODE && jpeg_src_buf->dht_needed) { + if (jpeg->mode == MXC_JPEG_DECODE && jpeg_src_buf->dht_needed && + mxc_dec_is_ongoing(ctx)) { jpeg_src_buf->dht_needed = false; dev_dbg(dev, "Decoder DHT cfg finished. Start decoding...\n"); goto job_unlock; -- cgit v1.2.3-59-g8ed1b From 59b24c0047a2e662325fdffeea4d9c9db2d31916 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Mon, 14 Apr 2025 21:12:29 +0200 Subject: media: dt-bindings: media: i2c: align filenames format with standard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Append missing vendor and align with other sony definitions. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Signed-off-by: David Heidelberg Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../devicetree/bindings/media/i2c/adi,adp1653.txt | 38 ++++ .../devicetree/bindings/media/i2c/adi,adv7180.yaml | 189 ++++++++++++++++++ .../devicetree/bindings/media/i2c/adi,adv7343.txt | 48 +++++ .../devicetree/bindings/media/i2c/adi,adv748x.yaml | 212 +++++++++++++++++++++ .../devicetree/bindings/media/i2c/adi,adv7604.yaml | 160 ++++++++++++++++ .../devicetree/bindings/media/i2c/adp1653.txt | 38 ---- .../devicetree/bindings/media/i2c/adv7180.yaml | 189 ------------------ .../devicetree/bindings/media/i2c/adv7343.txt | 48 ----- .../devicetree/bindings/media/i2c/adv748x.yaml | 212 --------------------- .../devicetree/bindings/media/i2c/adv7604.yaml | 160 ---------------- .../bindings/media/i2c/aptina,mt9v032.txt | 41 ++++ .../devicetree/bindings/media/i2c/imx219.yaml | 109 ----------- .../devicetree/bindings/media/i2c/max2175.txt | 59 ------ .../bindings/media/i2c/maxim,max2175.txt | 59 ++++++ .../bindings/media/i2c/micron,mt9m111.txt | 37 ++++ .../devicetree/bindings/media/i2c/mt9m001.txt | 38 ---- .../devicetree/bindings/media/i2c/mt9m111.txt | 37 ---- .../devicetree/bindings/media/i2c/mt9v032.txt | 41 ---- .../devicetree/bindings/media/i2c/nxp,tda1997x.txt | 178 +++++++++++++++++ .../devicetree/bindings/media/i2c/onnn,mt9m001.txt | 38 ++++ .../devicetree/bindings/media/i2c/ov2640.txt | 41 ---- .../devicetree/bindings/media/i2c/ov2659.txt | 47 ----- .../devicetree/bindings/media/i2c/ov7670.txt | 55 ------ .../devicetree/bindings/media/i2c/ov7740.txt | 47 ----- .../devicetree/bindings/media/i2c/ov9650.txt | 36 ---- .../devicetree/bindings/media/i2c/ovti,ov2640.txt | 41 ++++ .../devicetree/bindings/media/i2c/ovti,ov2659.txt | 47 +++++ .../devicetree/bindings/media/i2c/ovti,ov7670.txt | 55 ++++++ .../devicetree/bindings/media/i2c/ovti,ov7740.txt | 47 +++++ .../devicetree/bindings/media/i2c/ovti,ov9650.txt | 36 ++++ .../devicetree/bindings/media/i2c/sony,imx219.yaml | 109 +++++++++++ .../devicetree/bindings/media/i2c/tc358743.txt | 48 ----- .../devicetree/bindings/media/i2c/tda1997x.txt | 178 ----------------- .../devicetree/bindings/media/i2c/ths8200.txt | 19 -- .../devicetree/bindings/media/i2c/ti,ths8200.txt | 19 ++ .../devicetree/bindings/media/i2c/ti,tvp514x.txt | 44 +++++ .../devicetree/bindings/media/i2c/ti,tvp5150.txt | 157 +++++++++++++++ .../devicetree/bindings/media/i2c/ti,tvp7002.txt | 53 ++++++ .../bindings/media/i2c/toshiba,tc358743.txt | 48 +++++ .../devicetree/bindings/media/i2c/tvp514x.txt | 44 ----- .../devicetree/bindings/media/i2c/tvp5150.txt | 157 --------------- .../devicetree/bindings/media/i2c/tvp7002.txt | 53 ------ MAINTAINERS | 21 +- 43 files changed, 1668 insertions(+), 1665 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/i2c/adi,adp1653.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/adi,adv7180.yaml create mode 100644 Documentation/devicetree/bindings/media/i2c/adi,adv7343.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/adi,adv748x.yaml create mode 100644 Documentation/devicetree/bindings/media/i2c/adi,adv7604.yaml delete mode 100644 Documentation/devicetree/bindings/media/i2c/adp1653.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/adv7180.yaml delete mode 100644 Documentation/devicetree/bindings/media/i2c/adv7343.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/adv748x.yaml delete mode 100644 Documentation/devicetree/bindings/media/i2c/adv7604.yaml create mode 100644 Documentation/devicetree/bindings/media/i2c/aptina,mt9v032.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/imx219.yaml delete mode 100644 Documentation/devicetree/bindings/media/i2c/max2175.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/maxim,max2175.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/micron,mt9m111.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/mt9m001.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/mt9m111.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/mt9v032.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/nxp,tda1997x.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/onnn,mt9m001.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/ov2640.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/ov2659.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/ov7670.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/ov7740.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/ov9650.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov2640.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov2659.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov7670.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov7740.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov9650.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx219.yaml delete mode 100644 Documentation/devicetree/bindings/media/i2c/tc358743.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/tda1997x.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/ths8200.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/ti,ths8200.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/ti,tvp514x.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/ti,tvp5150.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/ti,tvp7002.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/tvp514x.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/tvp5150.txt delete mode 100644 Documentation/devicetree/bindings/media/i2c/tvp7002.txt diff --git a/Documentation/devicetree/bindings/media/i2c/adi,adp1653.txt b/Documentation/devicetree/bindings/media/i2c/adi,adp1653.txt new file mode 100644 index 000000000000..4cce0de40ee9 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/adi,adp1653.txt @@ -0,0 +1,38 @@ +* Analog Devices ADP1653 flash LED driver + +Required Properties: + + - compatible: Must contain "adi,adp1653" + + - reg: I2C slave address + + - enable-gpios: Specifier of the GPIO connected to EN pin + +There are two LED outputs available - flash and indicator. One LED is +represented by one child node, nodes need to be named "flash" and "indicator". + +Required properties of the LED child node: +- led-max-microamp : see Documentation/devicetree/bindings/leds/common.txt + +Required properties of the flash LED child node: + +- flash-max-microamp : see Documentation/devicetree/bindings/leds/common.txt +- flash-timeout-us : see Documentation/devicetree/bindings/leds/common.txt +- led-max-microamp : see Documentation/devicetree/bindings/leds/common.txt + +Example: + + adp1653: led-controller@30 { + compatible = "adi,adp1653"; + reg = <0x30>; + enable-gpios = <&gpio3 24 GPIO_ACTIVE_HIGH>; /* 88 */ + + flash { + flash-timeout-us = <500000>; + flash-max-microamp = <320000>; + led-max-microamp = <50000>; + }; + indicator { + led-max-microamp = <17500>; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/adi,adv7180.yaml b/Documentation/devicetree/bindings/media/i2c/adi,adv7180.yaml new file mode 100644 index 000000000000..dee8ce7cb7ba --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/adi,adv7180.yaml @@ -0,0 +1,189 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/adi,adv7180.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADV7180 analog video decoder family + +maintainers: + - Lars-Peter Clausen + +description: + The adv7180 family devices are used to capture analog video to different + digital interfaces like MIPI CSI-2 or parallel video. + +properties: + compatible: + items: + - enum: + - adi,adv7180 + - adi,adv7180cp + - adi,adv7180st + - adi,adv7182 + - adi,adv7280 + - adi,adv7280-m + - adi,adv7281 + - adi,adv7281-m + - adi,adv7281-ma + - adi,adv7282 + - adi,adv7282-m + + reg: + maxItems: 1 + + powerdown-gpios: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + adv,force-bt656-4: + deprecated: true + description: + Indicates that the output is a BT.656-4 compatible stream. + type: boolean + + adi,force-bt656-4: + description: + Indicates that the output is a BT.656-4 compatible stream. + type: boolean + + interrupts: + items: + - description: The GPIO connected to the INTRQ pin. + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + ports: true + +additionalProperties: false + +required: + - compatible + - reg + +allOf: + - if: + properties: + compatible: + enum: + - adi,adv7180 + - adi,adv7182 + - adi,adv7280 + - adi,adv7280-m + - adi,adv7281 + - adi,adv7281-m + - adi,adv7281-ma + - adi,adv7282 + - adi,adv7282-m + then: + required: + - port + + - if: + properties: + compatible: + contains: + const: adi,adv7180cp + then: + properties: + ports: + $ref: /schemas/graph.yaml#/properties/ports + properties: + port@3: + $ref: /schemas/graph.yaml#/properties/port + description: Output port + + patternProperties: + "^port@[0-2]$": + $ref: /schemas/graph.yaml#/properties/port + description: Input port + + required: + - port@3 + + required: + - ports + + - if: + properties: + compatible: + contains: + const: adi,adv7180st + then: + properties: + ports: + $ref: /schemas/graph.yaml#/properties/ports + properties: + port@6: + $ref: /schemas/graph.yaml#/properties/port + description: Output port + + patternProperties: + "^port@[0-5]$": + $ref: /schemas/graph.yaml#/properties/port + description: Input port + + required: + - port@6 + + required: + - ports + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + composite-in@20 { + compatible = "adi,adv7180"; + reg = <0x20>; + + port { + adv7180: endpoint { + bus-width = <8>; + remote-endpoint = <&vin1ep>; + }; + }; + }; + + }; + + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + composite-in@20 { + compatible = "adi,adv7180cp"; + reg = <0x20>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + adv7180_in: endpoint { + remote-endpoint = <&composite_con_in>; + }; + }; + + port@3 { + reg = <3>; + adv7180_out: endpoint { + remote-endpoint = <&vin4_in>; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/adi,adv7343.txt b/Documentation/devicetree/bindings/media/i2c/adi,adv7343.txt new file mode 100644 index 000000000000..5653bc2428b8 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/adi,adv7343.txt @@ -0,0 +1,48 @@ +* Analog Devices adv7343 video encoder + +The ADV7343 are high speed, digital-to-analog video encoders in a 64-lead LQFP +package. Six high speed, 3.3 V, 11-bit video DACs provide support for composite +(CVBS), S-Video (Y-C), and component (YPrPb/RGB) analog outputs in standard +definition (SD), enhanced definition (ED), or high definition (HD) video +formats. + +Required Properties : +- compatible: Must be "adi,adv7343" + +Optional Properties : +- adi,power-mode-sleep-mode: on enable the current consumption is reduced to + micro ampere level. All DACs and the internal PLL + circuit are disabled. +- adi,power-mode-pll-ctrl: PLL and oversampling control. This control allows + internal PLL 1 circuit to be powered down and the + oversampling to be switched off. +- ad,adv7343-power-mode-dac: array configuring the power on/off DAC's 1..6, + 0 = OFF and 1 = ON, Default value when this + property is not specified is <0 0 0 0 0 0>. +- ad,adv7343-sd-config-dac-out: array configure SD DAC Output's 1 and 2, 0 = OFF + and 1 = ON, Default value when this property is + not specified is <0 0>. + +Example: + +i2c0@1c22000 { + ... + ... + + adv7343@2a { + compatible = "adi,adv7343"; + reg = <0x2a>; + + port { + adv7343_1: endpoint { + adi,power-mode-sleep-mode; + adi,power-mode-pll-ctrl; + /* Use DAC1..3, DAC6 */ + adi,dac-enable = <1 1 1 0 0 1>; + /* Use SD DAC output 1 */ + adi,sd-dac-enable = <1 0>; + }; + }; + }; + ... +}; diff --git a/Documentation/devicetree/bindings/media/i2c/adi,adv748x.yaml b/Documentation/devicetree/bindings/media/i2c/adi,adv748x.yaml new file mode 100644 index 000000000000..254987350321 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/adi,adv748x.yaml @@ -0,0 +1,212 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/adi,adv748x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADV748X video decoder with HDMI receiver + +maintainers: + - Kieran Bingham + - Niklas Söderlund + +description: + The ADV7481 and ADV7482 are multi format video decoders with an integrated + HDMI receiver. They can output CSI-2 on two independent outputs TXA and TXB + from three input sources HDMI, analog and TTL. + +properties: + compatible: + items: + - enum: + - adi,adv7481 + - adi,adv7482 + + reg: + minItems: 1 + maxItems: 12 + description: + The ADV748x has up to twelve 256-byte maps that can be accessed via the + main I2C ports. Each map has it own I2C address and acts as a standard + slave device on the I2C bus. The main address is mandatory, others are + optional and remain at default values if not specified. + + reg-names: + minItems: 1 + items: + - const: main + - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] + - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] + - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] + - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] + - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] + - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] + - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] + - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] + - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] + - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] + - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] + + interrupts: true + + interrupt-names: true + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + patternProperties: + "^port@[0-7]$": + $ref: /schemas/graph.yaml#/properties/port + description: Input port nodes for analog inputs AIN[0-7]. + + properties: + port@8: + $ref: /schemas/graph.yaml#/properties/port + description: Input port node for HDMI. + + port@9: + $ref: /schemas/graph.yaml#/properties/port + description: Input port node for TTL. + + port@a: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Output port node, single endpoint describing the CSI-2 transmitter TXA. + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + clock-lanes: + maxItems: 1 + + data-lanes: + minItems: 1 + maxItems: 4 + + required: + - clock-lanes + - data-lanes + + port@b: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Output port node, single endpoint describing the CSI-2 transmitter TXB. + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + clock-lanes: + maxItems: 1 + + data-lanes: + maxItems: 1 + + required: + - clock-lanes + - data-lanes + +allOf: + - if: + properties: + compatible: + contains: + const: adi,adv7481 + then: + properties: + interrupts: + minItems: 1 + maxItems: 3 + + interrupt-names: + minItems: 1 + maxItems: 3 + items: + enum: [ intrq1, intrq2, intrq3 ] + else: + properties: + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + minItems: 1 + maxItems: 2 + items: + enum: [ intrq1, intrq2 ] + +additionalProperties: false + +required: + - compatible + - reg + - ports + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + video-receiver@70 { + compatible = "adi,adv7482"; + reg = <0x70 0x71 0x72 0x73 0x74 0x75 + 0x60 0x61 0x62 0x63 0x64 0x65>; + reg-names = "main", "dpll", "cp", "hdmi", "edid", "repeater", + "infoframe", "cbus", "cec", "sdp", "txa", "txb"; + + interrupt-parent = <&gpio6>; + interrupts = <30 IRQ_TYPE_LEVEL_LOW>, <31 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "intrq1", "intrq2"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@7 { + reg = <7>; + + adv7482_ain7: endpoint { + remote-endpoint = <&cvbs_in>; + }; + }; + + port@8 { + reg = <8>; + + adv7482_hdmi: endpoint { + remote-endpoint = <&hdmi_in>; + }; + }; + + port@a { + reg = <10>; + + adv7482_txa: endpoint { + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&csi40_in>; + }; + }; + + port@b { + reg = <11>; + + adv7482_txb: endpoint { + clock-lanes = <0>; + data-lanes = <1>; + remote-endpoint = <&csi20_in>; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/adi,adv7604.yaml b/Documentation/devicetree/bindings/media/i2c/adi,adv7604.yaml new file mode 100644 index 000000000000..6c403003cdda --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/adi,adv7604.yaml @@ -0,0 +1,160 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/adi,adv7604.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADV7604/10/11/12 video decoder with HDMI receiver + +maintainers: + - Hans Verkuil + +description: + The ADV7604 and ADV7610/11/12 are multiformat video decoders with + an integrated HDMI receiver. The ADV7604 has four multiplexed HDMI inputs + and one analog input, and the ADV7610/11 have one HDMI input and no analog + input. The ADV7612 is similar to the ADV7610/11 but has 2 HDMI inputs. + + These device tree bindings support the ADV7610/11/12 only at the moment. + +properties: + compatible: + items: + - enum: + - adi,adv7610 + - adi,adv7611 + - adi,adv7612 + + reg: + minItems: 1 + maxItems: 13 + + reg-names: + minItems: 1 + items: + - const: main + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + + interrupts: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + hpd-gpios: + minItems: 1 + description: + References to the GPIOs that control the HDMI hot-plug detection pins, + one per HDMI input. The active flag indicates the GPIO level that + enables hot-plug detection. + + default-input: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: + Select which input is selected after reset. + + ports: true + +required: + - compatible + - reg + - ports + +additionalProperties: false + +allOf: + - if: + properties: + compatible: + contains: + const: adi,adv7611 + then: + properties: + ports: + $ref: /schemas/graph.yaml#/properties/ports + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: Input port + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: Output port + + required: + - port@1 + + - if: + properties: + compatible: + contains: + const: adi,adv7612 + then: + properties: + ports: + $ref: /schemas/graph.yaml#/properties/ports + properties: + port@2: + $ref: /schemas/graph.yaml#/properties/port + description: Output port + + patternProperties: + "^port@[0-1]$": + $ref: /schemas/graph.yaml#/properties/port + description: Input port + + required: + - port@2 + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + hdmi_receiver@4c { + compatible = "adi,adv7611"; + /* + * The edid page will be accessible @ 0x66 on the I2C bus. All + * other maps will retain their default addresses. + */ + reg = <0x4c>, <0x66>; + reg-names = "main", "edid"; + + reset-gpios = <&ioexp 0 GPIO_ACTIVE_LOW>; + hpd-gpios = <&ioexp 2 GPIO_ACTIVE_HIGH>; + default-input = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + }; + + port@1 { + reg = <1>; + hdmi_in: endpoint { + remote-endpoint = <&ccdc_in>; + }; + }; + }; + + + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/adp1653.txt b/Documentation/devicetree/bindings/media/i2c/adp1653.txt deleted file mode 100644 index 4cce0de40ee9..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/adp1653.txt +++ /dev/null @@ -1,38 +0,0 @@ -* Analog Devices ADP1653 flash LED driver - -Required Properties: - - - compatible: Must contain "adi,adp1653" - - - reg: I2C slave address - - - enable-gpios: Specifier of the GPIO connected to EN pin - -There are two LED outputs available - flash and indicator. One LED is -represented by one child node, nodes need to be named "flash" and "indicator". - -Required properties of the LED child node: -- led-max-microamp : see Documentation/devicetree/bindings/leds/common.txt - -Required properties of the flash LED child node: - -- flash-max-microamp : see Documentation/devicetree/bindings/leds/common.txt -- flash-timeout-us : see Documentation/devicetree/bindings/leds/common.txt -- led-max-microamp : see Documentation/devicetree/bindings/leds/common.txt - -Example: - - adp1653: led-controller@30 { - compatible = "adi,adp1653"; - reg = <0x30>; - enable-gpios = <&gpio3 24 GPIO_ACTIVE_HIGH>; /* 88 */ - - flash { - flash-timeout-us = <500000>; - flash-max-microamp = <320000>; - led-max-microamp = <50000>; - }; - indicator { - led-max-microamp = <17500>; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/adv7180.yaml b/Documentation/devicetree/bindings/media/i2c/adv7180.yaml deleted file mode 100644 index 9ee1483775f6..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/adv7180.yaml +++ /dev/null @@ -1,189 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/media/i2c/adv7180.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Analog Devices ADV7180 analog video decoder family - -maintainers: - - Lars-Peter Clausen - -description: - The adv7180 family devices are used to capture analog video to different - digital interfaces like MIPI CSI-2 or parallel video. - -properties: - compatible: - items: - - enum: - - adi,adv7180 - - adi,adv7180cp - - adi,adv7180st - - adi,adv7182 - - adi,adv7280 - - adi,adv7280-m - - adi,adv7281 - - adi,adv7281-m - - adi,adv7281-ma - - adi,adv7282 - - adi,adv7282-m - - reg: - maxItems: 1 - - powerdown-gpios: - maxItems: 1 - - reset-gpios: - maxItems: 1 - - adv,force-bt656-4: - deprecated: true - description: - Indicates that the output is a BT.656-4 compatible stream. - type: boolean - - adi,force-bt656-4: - description: - Indicates that the output is a BT.656-4 compatible stream. - type: boolean - - interrupts: - items: - - description: The GPIO connected to the INTRQ pin. - - port: - $ref: /schemas/graph.yaml#/$defs/port-base - unevaluatedProperties: false - - properties: - endpoint: - $ref: /schemas/media/video-interfaces.yaml# - unevaluatedProperties: false - - ports: true - -additionalProperties: false - -required: - - compatible - - reg - -allOf: - - if: - properties: - compatible: - enum: - - adi,adv7180 - - adi,adv7182 - - adi,adv7280 - - adi,adv7280-m - - adi,adv7281 - - adi,adv7281-m - - adi,adv7281-ma - - adi,adv7282 - - adi,adv7282-m - then: - required: - - port - - - if: - properties: - compatible: - contains: - const: adi,adv7180cp - then: - properties: - ports: - $ref: /schemas/graph.yaml#/properties/ports - properties: - port@3: - $ref: /schemas/graph.yaml#/properties/port - description: Output port - - patternProperties: - "^port@[0-2]$": - $ref: /schemas/graph.yaml#/properties/port - description: Input port - - required: - - port@3 - - required: - - ports - - - if: - properties: - compatible: - contains: - const: adi,adv7180st - then: - properties: - ports: - $ref: /schemas/graph.yaml#/properties/ports - properties: - port@6: - $ref: /schemas/graph.yaml#/properties/port - description: Output port - - patternProperties: - "^port@[0-5]$": - $ref: /schemas/graph.yaml#/properties/port - description: Input port - - required: - - port@6 - - required: - - ports - -examples: - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - - composite-in@20 { - compatible = "adi,adv7180"; - reg = <0x20>; - - port { - adv7180: endpoint { - bus-width = <8>; - remote-endpoint = <&vin1ep>; - }; - }; - }; - - }; - - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - - composite-in@20 { - compatible = "adi,adv7180cp"; - reg = <0x20>; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - adv7180_in: endpoint { - remote-endpoint = <&composite_con_in>; - }; - }; - - port@3 { - reg = <3>; - adv7180_out: endpoint { - remote-endpoint = <&vin4_in>; - }; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/adv7343.txt b/Documentation/devicetree/bindings/media/i2c/adv7343.txt deleted file mode 100644 index 5653bc2428b8..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/adv7343.txt +++ /dev/null @@ -1,48 +0,0 @@ -* Analog Devices adv7343 video encoder - -The ADV7343 are high speed, digital-to-analog video encoders in a 64-lead LQFP -package. Six high speed, 3.3 V, 11-bit video DACs provide support for composite -(CVBS), S-Video (Y-C), and component (YPrPb/RGB) analog outputs in standard -definition (SD), enhanced definition (ED), or high definition (HD) video -formats. - -Required Properties : -- compatible: Must be "adi,adv7343" - -Optional Properties : -- adi,power-mode-sleep-mode: on enable the current consumption is reduced to - micro ampere level. All DACs and the internal PLL - circuit are disabled. -- adi,power-mode-pll-ctrl: PLL and oversampling control. This control allows - internal PLL 1 circuit to be powered down and the - oversampling to be switched off. -- ad,adv7343-power-mode-dac: array configuring the power on/off DAC's 1..6, - 0 = OFF and 1 = ON, Default value when this - property is not specified is <0 0 0 0 0 0>. -- ad,adv7343-sd-config-dac-out: array configure SD DAC Output's 1 and 2, 0 = OFF - and 1 = ON, Default value when this property is - not specified is <0 0>. - -Example: - -i2c0@1c22000 { - ... - ... - - adv7343@2a { - compatible = "adi,adv7343"; - reg = <0x2a>; - - port { - adv7343_1: endpoint { - adi,power-mode-sleep-mode; - adi,power-mode-pll-ctrl; - /* Use DAC1..3, DAC6 */ - adi,dac-enable = <1 1 1 0 0 1>; - /* Use SD DAC output 1 */ - adi,sd-dac-enable = <1 0>; - }; - }; - }; - ... -}; diff --git a/Documentation/devicetree/bindings/media/i2c/adv748x.yaml b/Documentation/devicetree/bindings/media/i2c/adv748x.yaml deleted file mode 100644 index d6353081402b..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/adv748x.yaml +++ /dev/null @@ -1,212 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/media/i2c/adv748x.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Analog Devices ADV748X video decoder with HDMI receiver - -maintainers: - - Kieran Bingham - - Niklas Söderlund - -description: - The ADV7481 and ADV7482 are multi format video decoders with an integrated - HDMI receiver. They can output CSI-2 on two independent outputs TXA and TXB - from three input sources HDMI, analog and TTL. - -properties: - compatible: - items: - - enum: - - adi,adv7481 - - adi,adv7482 - - reg: - minItems: 1 - maxItems: 12 - description: - The ADV748x has up to twelve 256-byte maps that can be accessed via the - main I2C ports. Each map has it own I2C address and acts as a standard - slave device on the I2C bus. The main address is mandatory, others are - optional and remain at default values if not specified. - - reg-names: - minItems: 1 - items: - - const: main - - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] - - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] - - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] - - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] - - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] - - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] - - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] - - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] - - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] - - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] - - enum: [ dpll, cp, hdmi, edid, repeater, infoframe, cbus, cec, sdp, txa, txb ] - - interrupts: true - - interrupt-names: true - - ports: - $ref: /schemas/graph.yaml#/properties/ports - - patternProperties: - "^port@[0-7]$": - $ref: /schemas/graph.yaml#/properties/port - description: Input port nodes for analog inputs AIN[0-7]. - - properties: - port@8: - $ref: /schemas/graph.yaml#/properties/port - description: Input port node for HDMI. - - port@9: - $ref: /schemas/graph.yaml#/properties/port - description: Input port node for TTL. - - port@a: - $ref: /schemas/graph.yaml#/$defs/port-base - unevaluatedProperties: false - description: - Output port node, single endpoint describing the CSI-2 transmitter TXA. - - properties: - endpoint: - $ref: /schemas/media/video-interfaces.yaml# - unevaluatedProperties: false - - properties: - clock-lanes: - maxItems: 1 - - data-lanes: - minItems: 1 - maxItems: 4 - - required: - - clock-lanes - - data-lanes - - port@b: - $ref: /schemas/graph.yaml#/$defs/port-base - unevaluatedProperties: false - description: - Output port node, single endpoint describing the CSI-2 transmitter TXB. - - properties: - endpoint: - $ref: /schemas/media/video-interfaces.yaml# - unevaluatedProperties: false - - properties: - clock-lanes: - maxItems: 1 - - data-lanes: - maxItems: 1 - - required: - - clock-lanes - - data-lanes - -allOf: - - if: - properties: - compatible: - contains: - const: adi,adv7481 - then: - properties: - interrupts: - minItems: 1 - maxItems: 3 - - interrupt-names: - minItems: 1 - maxItems: 3 - items: - enum: [ intrq1, intrq2, intrq3 ] - else: - properties: - interrupts: - minItems: 1 - maxItems: 2 - - interrupt-names: - minItems: 1 - maxItems: 2 - items: - enum: [ intrq1, intrq2 ] - -additionalProperties: false - -required: - - compatible - - reg - - ports - -examples: - - | - #include - - i2c { - #address-cells = <1>; - #size-cells = <0>; - - video-receiver@70 { - compatible = "adi,adv7482"; - reg = <0x70 0x71 0x72 0x73 0x74 0x75 - 0x60 0x61 0x62 0x63 0x64 0x65>; - reg-names = "main", "dpll", "cp", "hdmi", "edid", "repeater", - "infoframe", "cbus", "cec", "sdp", "txa", "txb"; - - interrupt-parent = <&gpio6>; - interrupts = <30 IRQ_TYPE_LEVEL_LOW>, <31 IRQ_TYPE_LEVEL_LOW>; - interrupt-names = "intrq1", "intrq2"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@7 { - reg = <7>; - - adv7482_ain7: endpoint { - remote-endpoint = <&cvbs_in>; - }; - }; - - port@8 { - reg = <8>; - - adv7482_hdmi: endpoint { - remote-endpoint = <&hdmi_in>; - }; - }; - - port@a { - reg = <10>; - - adv7482_txa: endpoint { - clock-lanes = <0>; - data-lanes = <1 2 3 4>; - remote-endpoint = <&csi40_in>; - }; - }; - - port@b { - reg = <11>; - - adv7482_txb: endpoint { - clock-lanes = <0>; - data-lanes = <1>; - remote-endpoint = <&csi20_in>; - }; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/adv7604.yaml b/Documentation/devicetree/bindings/media/i2c/adv7604.yaml deleted file mode 100644 index 7589d377c686..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/adv7604.yaml +++ /dev/null @@ -1,160 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/media/i2c/adv7604.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Analog Devices ADV7604/10/11/12 video decoder with HDMI receiver - -maintainers: - - Hans Verkuil - -description: - The ADV7604 and ADV7610/11/12 are multiformat video decoders with - an integrated HDMI receiver. The ADV7604 has four multiplexed HDMI inputs - and one analog input, and the ADV7610/11 have one HDMI input and no analog - input. The ADV7612 is similar to the ADV7610/11 but has 2 HDMI inputs. - - These device tree bindings support the ADV7610/11/12 only at the moment. - -properties: - compatible: - items: - - enum: - - adi,adv7610 - - adi,adv7611 - - adi,adv7612 - - reg: - minItems: 1 - maxItems: 13 - - reg-names: - minItems: 1 - items: - - const: main - - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] - - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] - - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] - - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] - - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] - - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] - - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] - - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] - - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] - - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] - - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] - - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] - - interrupts: - maxItems: 1 - - reset-gpios: - maxItems: 1 - - hpd-gpios: - minItems: 1 - description: - References to the GPIOs that control the HDMI hot-plug detection pins, - one per HDMI input. The active flag indicates the GPIO level that - enables hot-plug detection. - - default-input: - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [ 0, 1 ] - description: - Select which input is selected after reset. - - ports: true - -required: - - compatible - - reg - - ports - -additionalProperties: false - -allOf: - - if: - properties: - compatible: - contains: - const: adi,adv7611 - then: - properties: - ports: - $ref: /schemas/graph.yaml#/properties/ports - properties: - port@0: - $ref: /schemas/graph.yaml#/properties/port - description: Input port - - port@1: - $ref: /schemas/graph.yaml#/properties/port - description: Output port - - required: - - port@1 - - - if: - properties: - compatible: - contains: - const: adi,adv7612 - then: - properties: - ports: - $ref: /schemas/graph.yaml#/properties/ports - properties: - port@2: - $ref: /schemas/graph.yaml#/properties/port - description: Output port - - patternProperties: - "^port@[0-1]$": - $ref: /schemas/graph.yaml#/properties/port - description: Input port - - required: - - port@2 - -examples: - - | - #include - - i2c { - #address-cells = <1>; - #size-cells = <0>; - - hdmi_receiver@4c { - compatible = "adi,adv7611"; - /* - * The edid page will be accessible @ 0x66 on the I2C bus. All - * other maps will retain their default addresses. - */ - reg = <0x4c>, <0x66>; - reg-names = "main", "edid"; - - reset-gpios = <&ioexp 0 GPIO_ACTIVE_LOW>; - hpd-gpios = <&ioexp 2 GPIO_ACTIVE_HIGH>; - default-input = <0>; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - }; - - port@1 { - reg = <1>; - hdmi_in: endpoint { - remote-endpoint = <&ccdc_in>; - }; - }; - }; - - - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/aptina,mt9v032.txt b/Documentation/devicetree/bindings/media/i2c/aptina,mt9v032.txt new file mode 100644 index 000000000000..100f0ae43269 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/aptina,mt9v032.txt @@ -0,0 +1,41 @@ +* Aptina 1/3-Inch WVGA CMOS Digital Image Sensor + +The Aptina MT9V032 is a 1/3-inch CMOS active pixel digital image sensor with +an active array size of 752H x 480V. It is programmable through a simple +two-wire serial interface. + +Required Properties: + +- compatible: value should be either one among the following + (a) "aptina,mt9v022" for MT9V022 color sensor + (b) "aptina,mt9v022m" for MT9V022 monochrome sensor + (c) "aptina,mt9v024" for MT9V024 color sensor + (d) "aptina,mt9v024m" for MT9V024 monochrome sensor + (e) "aptina,mt9v032" for MT9V032 color sensor + (f) "aptina,mt9v032m" for MT9V032 monochrome sensor + (g) "aptina,mt9v034" for MT9V034 color sensor + (h) "aptina,mt9v034m" for MT9V034 monochrome sensor + +Optional Properties: + +- link-frequencies: List of allowed link frequencies in Hz. Each frequency is + expressed as a 64-bit big-endian integer. +- reset-gpios: GPIO handle which is connected to the reset pin of the chip. +- standby-gpios: GPIO handle which is connected to the standby pin of the chip. + +For further reading on port node refer to +Documentation/devicetree/bindings/media/video-interfaces.txt. + +Example: + + mt9v032@5c { + compatible = "aptina,mt9v032"; + reg = <0x5c>; + + port { + mt9v032_out: endpoint { + link-frequencies = /bits/ 64 + <13000000 26600000 27000000>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/imx219.yaml b/Documentation/devicetree/bindings/media/i2c/imx219.yaml deleted file mode 100644 index 07d088cf66e0..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/imx219.yaml +++ /dev/null @@ -1,109 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/media/i2c/imx219.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Sony 1/4.0-Inch 8Mpixel CMOS Digital Image Sensor - -maintainers: - - Dave Stevenson - -description: |- - The Sony imx219 is a 1/4.0-inch CMOS active pixel digital image sensor - with an active array size of 3280H x 2464V. It is programmable through - I2C interface. The I2C address is fixed to 0x10 as per sensor data sheet. - Image data is sent through MIPI CSI-2, which is configured as either 2 or - 4 data lanes. - -properties: - compatible: - const: sony,imx219 - - reg: - description: I2C device address - maxItems: 1 - - clocks: - maxItems: 1 - - VDIG-supply: - description: - Digital I/O voltage supply, 1.8 volts - - VANA-supply: - description: - Analog voltage supply, 2.8 volts - - VDDL-supply: - description: - Digital core voltage supply, 1.2 volts - - reset-gpios: - maxItems: 1 - description: |- - Reference to the GPIO connected to the xclr pin, if any. - Must be released (set high) after all supplies are applied. - - port: - $ref: /schemas/graph.yaml#/$defs/port-base - additionalProperties: false - - properties: - endpoint: - $ref: /schemas/media/video-interfaces.yaml# - unevaluatedProperties: false - - properties: - data-lanes: - description: |- - The sensor supports either two-lane, or four-lane operation. - If this property is omitted four-lane operation is assumed. - For two-lane operation the property must be set to <1 2>. - items: - - const: 1 - - const: 2 - - clock-noncontinuous: true - link-frequencies: true - - required: - - link-frequencies - -required: - - compatible - - reg - - clocks - - VANA-supply - - VDIG-supply - - VDDL-supply - - port - -additionalProperties: false - -examples: - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - - imx219: sensor@10 { - compatible = "sony,imx219"; - reg = <0x10>; - clocks = <&imx219_clk>; - VANA-supply = <&imx219_vana>; /* 2.8v */ - VDIG-supply = <&imx219_vdig>; /* 1.8v */ - VDDL-supply = <&imx219_vddl>; /* 1.2v */ - - port { - imx219_0: endpoint { - remote-endpoint = <&csi1_ep>; - data-lanes = <1 2>; - clock-noncontinuous; - link-frequencies = /bits/ 64 <456000000>; - }; - }; - }; - }; - -... diff --git a/Documentation/devicetree/bindings/media/i2c/max2175.txt b/Documentation/devicetree/bindings/media/i2c/max2175.txt deleted file mode 100644 index 02b4e9cd7b1b..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/max2175.txt +++ /dev/null @@ -1,59 +0,0 @@ -Maxim Integrated MAX2175 RF to Bits tuner ------------------------------------------ - -The MAX2175 IC is an advanced analog/digital hybrid-radio receiver with -RF to Bits® front-end designed for software-defined radio solutions. - -Required properties: --------------------- -- compatible: "maxim,max2175" for MAX2175 RF-to-bits tuner. -- clocks: clock specifier. -- port: child port node corresponding to the I2S output, in accordance with - the video interface bindings defined in - Documentation/devicetree/bindings/media/video-interfaces.txt. The port - node must contain at least one endpoint. - -Optional properties: --------------------- -- maxim,master : phandle to the master tuner if it is a slave. This - is used to define two tuners in diversity mode - (1 master, 1 slave). By default each tuner is an - individual master. -- maxim,refout-load : load capacitance value (in picofarads) on reference - output drive level. The possible load values are: - 0 (default - refout disabled) - 10 - 20 - 30 - 40 - 60 - 70 -- maxim,am-hiz-filter : empty property indicates the AM Hi-Z filter is used - in this hardware for AM antenna input. - -Example: --------- - -Board specific DTS file - -/* Fixed XTAL clock node */ -maxim_xtal: clock { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <36864000>; -}; - -/* A tuner device instance under i2c bus */ -max2175_0: tuner@60 { - compatible = "maxim,max2175"; - reg = <0x60>; - clocks = <&maxim_xtal>; - maxim,refout-load = <10>; - - port { - max2175_0_ep: endpoint { - remote-endpoint = <&slave_rx_device>; - }; - }; - -}; diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max2175.txt b/Documentation/devicetree/bindings/media/i2c/maxim,max2175.txt new file mode 100644 index 000000000000..02b4e9cd7b1b --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max2175.txt @@ -0,0 +1,59 @@ +Maxim Integrated MAX2175 RF to Bits tuner +----------------------------------------- + +The MAX2175 IC is an advanced analog/digital hybrid-radio receiver with +RF to Bits® front-end designed for software-defined radio solutions. + +Required properties: +-------------------- +- compatible: "maxim,max2175" for MAX2175 RF-to-bits tuner. +- clocks: clock specifier. +- port: child port node corresponding to the I2S output, in accordance with + the video interface bindings defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. The port + node must contain at least one endpoint. + +Optional properties: +-------------------- +- maxim,master : phandle to the master tuner if it is a slave. This + is used to define two tuners in diversity mode + (1 master, 1 slave). By default each tuner is an + individual master. +- maxim,refout-load : load capacitance value (in picofarads) on reference + output drive level. The possible load values are: + 0 (default - refout disabled) + 10 + 20 + 30 + 40 + 60 + 70 +- maxim,am-hiz-filter : empty property indicates the AM Hi-Z filter is used + in this hardware for AM antenna input. + +Example: +-------- + +Board specific DTS file + +/* Fixed XTAL clock node */ +maxim_xtal: clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <36864000>; +}; + +/* A tuner device instance under i2c bus */ +max2175_0: tuner@60 { + compatible = "maxim,max2175"; + reg = <0x60>; + clocks = <&maxim_xtal>; + maxim,refout-load = <10>; + + port { + max2175_0_ep: endpoint { + remote-endpoint = <&slave_rx_device>; + }; + }; + +}; diff --git a/Documentation/devicetree/bindings/media/i2c/micron,mt9m111.txt b/Documentation/devicetree/bindings/media/i2c/micron,mt9m111.txt new file mode 100644 index 000000000000..d0bed6fa901a --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/micron,mt9m111.txt @@ -0,0 +1,37 @@ +Micron 1.3Mp CMOS Digital Image Sensor + +The Micron MT9M111 is a CMOS active pixel digital image sensor with an active +array size of 1280H x 1024V. It is programmable through a simple two-wire serial +interface. + +Required Properties: +- compatible: value should be "micron,mt9m111" +- clocks: reference to the master clock. +- clock-names: shall be "mclk". + +The device node must contain one 'port' child node with one 'endpoint' child +sub-node for its digital output video port, in accordance with the video +interface bindings defined in: +Documentation/devicetree/bindings/media/video-interfaces.txt + +Optional endpoint properties: +- pclk-sample: For information see ../video-interfaces.txt. The value is set to + 0 if it isn't specified. + +Example: + + i2c_master { + mt9m111@5d { + compatible = "micron,mt9m111"; + reg = <0x5d>; + clocks = <&mclk>; + clock-names = "mclk"; + + port { + mt9m111_1: endpoint { + remote-endpoint = <&pxa_camera>; + pclk-sample = <1>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/mt9m001.txt b/Documentation/devicetree/bindings/media/i2c/mt9m001.txt deleted file mode 100644 index c920552b03ef..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/mt9m001.txt +++ /dev/null @@ -1,38 +0,0 @@ -MT9M001: 1/2-Inch Megapixel Digital Image Sensor - -The MT9M001 is an SXGA-format with a 1/2-inch CMOS active-pixel digital -image sensor. It is programmable through I2C interface. - -Required Properties: - -- compatible: shall be "onnn,mt9m001". -- clocks: reference to the master clock into sensor - -Optional Properties: - -- reset-gpios: GPIO handle which is connected to the reset pin of the chip. - Active low. -- standby-gpios: GPIO handle which is connected to the standby pin of the chip. - Active high. - -The device node must contain one 'port' child node with one 'endpoint' child -sub-node for its digital output video port, in accordance with the video -interface bindings defined in: -Documentation/devicetree/bindings/media/video-interfaces.txt - -Example: - - &i2c1 { - camera-sensor@5d { - compatible = "onnn,mt9m001"; - reg = <0x5d>; - reset-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; - standby-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; - clocks = <&camera_clk>; - port { - mt9m001_out: endpoint { - remote-endpoint = <&vcap_in>; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/mt9m111.txt b/Documentation/devicetree/bindings/media/i2c/mt9m111.txt deleted file mode 100644 index d0bed6fa901a..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/mt9m111.txt +++ /dev/null @@ -1,37 +0,0 @@ -Micron 1.3Mp CMOS Digital Image Sensor - -The Micron MT9M111 is a CMOS active pixel digital image sensor with an active -array size of 1280H x 1024V. It is programmable through a simple two-wire serial -interface. - -Required Properties: -- compatible: value should be "micron,mt9m111" -- clocks: reference to the master clock. -- clock-names: shall be "mclk". - -The device node must contain one 'port' child node with one 'endpoint' child -sub-node for its digital output video port, in accordance with the video -interface bindings defined in: -Documentation/devicetree/bindings/media/video-interfaces.txt - -Optional endpoint properties: -- pclk-sample: For information see ../video-interfaces.txt. The value is set to - 0 if it isn't specified. - -Example: - - i2c_master { - mt9m111@5d { - compatible = "micron,mt9m111"; - reg = <0x5d>; - clocks = <&mclk>; - clock-names = "mclk"; - - port { - mt9m111_1: endpoint { - remote-endpoint = <&pxa_camera>; - pclk-sample = <1>; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/mt9v032.txt b/Documentation/devicetree/bindings/media/i2c/mt9v032.txt deleted file mode 100644 index 100f0ae43269..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/mt9v032.txt +++ /dev/null @@ -1,41 +0,0 @@ -* Aptina 1/3-Inch WVGA CMOS Digital Image Sensor - -The Aptina MT9V032 is a 1/3-inch CMOS active pixel digital image sensor with -an active array size of 752H x 480V. It is programmable through a simple -two-wire serial interface. - -Required Properties: - -- compatible: value should be either one among the following - (a) "aptina,mt9v022" for MT9V022 color sensor - (b) "aptina,mt9v022m" for MT9V022 monochrome sensor - (c) "aptina,mt9v024" for MT9V024 color sensor - (d) "aptina,mt9v024m" for MT9V024 monochrome sensor - (e) "aptina,mt9v032" for MT9V032 color sensor - (f) "aptina,mt9v032m" for MT9V032 monochrome sensor - (g) "aptina,mt9v034" for MT9V034 color sensor - (h) "aptina,mt9v034m" for MT9V034 monochrome sensor - -Optional Properties: - -- link-frequencies: List of allowed link frequencies in Hz. Each frequency is - expressed as a 64-bit big-endian integer. -- reset-gpios: GPIO handle which is connected to the reset pin of the chip. -- standby-gpios: GPIO handle which is connected to the standby pin of the chip. - -For further reading on port node refer to -Documentation/devicetree/bindings/media/video-interfaces.txt. - -Example: - - mt9v032@5c { - compatible = "aptina,mt9v032"; - reg = <0x5c>; - - port { - mt9v032_out: endpoint { - link-frequencies = /bits/ 64 - <13000000 26600000 27000000>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/nxp,tda1997x.txt b/Documentation/devicetree/bindings/media/i2c/nxp,tda1997x.txt new file mode 100644 index 000000000000..e76167999d76 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/nxp,tda1997x.txt @@ -0,0 +1,178 @@ +Device-Tree bindings for the NXP TDA1997x HDMI receiver + +The TDA19971/73 are HDMI video receivers. + +The TDA19971 Video port output pins can be used as follows: + - RGB 8bit per color (24 bits total): R[11:4] B[11:4] G[11:4] + - YUV444 8bit per color (24 bits total): Y[11:4] Cr[11:4] Cb[11:4] + - YUV422 semi-planar 8bit per component (16 bits total): Y[11:4] CbCr[11:4] + - YUV422 semi-planar 10bit per component (20 bits total): Y[11:2] CbCr[11:2] + - YUV422 semi-planar 12bit per component (24 bits total): - Y[11:0] CbCr[11:0] + - YUV422 BT656 8bit per component (8 bits total): YCbCr[11:4] (2-cycles) + - YUV422 BT656 10bit per component (10 bits total): YCbCr[11:2] (2-cycles) + - YUV422 BT656 12bit per component (12 bits total): YCbCr[11:0] (2-cycles) + +The TDA19973 Video port output pins can be used as follows: + - RGB 12bit per color (36 bits total): R[11:0] B[11:0] G[11:0] + - YUV444 12bit per color (36 bits total): Y[11:0] Cb[11:0] Cr[11:0] + - YUV422 semi-planar 12bit per component (24 bits total): Y[11:0] CbCr[11:0] + - YUV422 BT656 12bit per component (12 bits total): YCbCr[11:0] (2-cycles) + +The Video port output pins are mapped via 4-bit 'pin groups' allowing +for a variety of connection possibilities including swapping pin order within +pin groups. The video_portcfg device-tree property consists of register mapping +pairs which map a chip-specific VP output register to a 4-bit pin group. If +the pin group needs to be bit-swapped you can use the *_S pin-group defines. + +Required Properties: + - compatible : + - "nxp,tda19971" for the TDA19971 + - "nxp,tda19973" for the TDA19973 + - reg : I2C slave address + - interrupts : The interrupt number + - DOVDD-supply : Digital I/O supply + - DVDD-supply : Digital Core supply + - AVDD-supply : Analog supply + - nxp,vidout-portcfg : array of pairs mapping VP output pins to pin groups. + +Optional Properties: + - nxp,audout-format : DAI bus format: "i2s" or "spdif". + - nxp,audout-width : width of audio output data bus (1-4). + - nxp,audout-layout : data layout (0=AP0 used, 1=AP0/AP1/AP2/AP3 used). + - nxp,audout-mclk-fs : Multiplication factor between stream rate and codec + mclk. + +The port node shall contain one endpoint child node for its digital +output video port, in accordance with the video interface bindings defined in +Documentation/devicetree/bindings/media/video-interfaces.txt. + +Optional Endpoint Properties: + The following three properties are defined in video-interfaces.txt and + are valid for the output parallel bus endpoint: + - hsync-active: Horizontal synchronization polarity. Defaults to active high. + - vsync-active: Vertical synchronization polarity. Defaults to active high. + - data-active: Data polarity. Defaults to active high. + +Examples: + - VP[15:0] connected to IMX6 CSI_DATA[19:4] for 16bit YUV422 + 16bit I2S layout0 with a 128*fs clock (A_WS, AP0, A_CLK pins) + hdmi-receiver@48 { + compatible = "nxp,tda19971"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_tda1997x>; + reg = <0x48>; + interrupt-parent = <&gpio1>; + interrupts = <7 IRQ_TYPE_LEVEL_LOW>; + DOVDD-supply = <®_3p3v>; + AVDD-supply = <®_1p8v>; + DVDD-supply = <®_1p8v>; + /* audio */ + #sound-dai-cells = <0>; + nxp,audout-format = "i2s"; + nxp,audout-layout = <0>; + nxp,audout-width = <16>; + nxp,audout-mclk-fs = <128>; + /* + * The 8bpp YUV422 semi-planar mode outputs CbCr[11:4] + * and Y[11:4] across 16bits in the same pixclk cycle. + */ + nxp,vidout-portcfg = + /* Y[11:8]<->VP[15:12]<->CSI_DATA[19:16] */ + < TDA1997X_VP24_V15_12 TDA1997X_G_Y_11_8 >, + /* Y[7:4]<->VP[11:08]<->CSI_DATA[15:12] */ + < TDA1997X_VP24_V11_08 TDA1997X_G_Y_7_4 >, + /* CbCc[11:8]<->VP[07:04]<->CSI_DATA[11:8] */ + < TDA1997X_VP24_V07_04 TDA1997X_R_CR_CBCR_11_8 >, + /* CbCr[7:4]<->VP[03:00]<->CSI_DATA[7:4] */ + < TDA1997X_VP24_V03_00 TDA1997X_R_CR_CBCR_7_4 >; + + port { + tda1997x_to_ipu1_csi0_mux: endpoint { + remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>; + bus-width = <16>; + hsync-active = <1>; + vsync-active = <1>; + data-active = <1>; + }; + }; + }; + - VP[15:8] connected to IMX6 CSI_DATA[19:12] for 8bit BT656 + 16bit I2S layout0 with a 128*fs clock (A_WS, AP0, A_CLK pins) + hdmi-receiver@48 { + compatible = "nxp,tda19971"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_tda1997x>; + reg = <0x48>; + interrupt-parent = <&gpio1>; + interrupts = <7 IRQ_TYPE_LEVEL_LOW>; + DOVDD-supply = <®_3p3v>; + AVDD-supply = <®_1p8v>; + DVDD-supply = <®_1p8v>; + /* audio */ + #sound-dai-cells = <0>; + nxp,audout-format = "i2s"; + nxp,audout-layout = <0>; + nxp,audout-width = <16>; + nxp,audout-mclk-fs = <128>; + /* + * The 8bpp YUV422 semi-planar mode outputs CbCr[11:4] + * and Y[11:4] across 16bits in the same pixclk cycle. + */ + nxp,vidout-portcfg = + /* Y[11:8]<->VP[15:12]<->CSI_DATA[19:16] */ + < TDA1997X_VP24_V15_12 TDA1997X_G_Y_11_8 >, + /* Y[7:4]<->VP[11:08]<->CSI_DATA[15:12] */ + < TDA1997X_VP24_V11_08 TDA1997X_G_Y_7_4 >, + /* CbCc[11:8]<->VP[07:04]<->CSI_DATA[11:8] */ + < TDA1997X_VP24_V07_04 TDA1997X_R_CR_CBCR_11_8 >, + /* CbCr[7:4]<->VP[03:00]<->CSI_DATA[7:4] */ + < TDA1997X_VP24_V03_00 TDA1997X_R_CR_CBCR_7_4 >; + + port { + tda1997x_to_ipu1_csi0_mux: endpoint { + remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>; + bus-width = <16>; + hsync-active = <1>; + vsync-active = <1>; + data-active = <1>; + }; + }; + }; + - VP[15:8] connected to IMX6 CSI_DATA[19:12] for 8bit BT656 + 16bit I2S layout0 with a 128*fs clock (A_WS, AP0, A_CLK pins) + hdmi-receiver@48 { + compatible = "nxp,tda19971"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_tda1997x>; + reg = <0x48>; + interrupt-parent = <&gpio1>; + interrupts = <7 IRQ_TYPE_LEVEL_LOW>; + DOVDD-supply = <®_3p3v>; + AVDD-supply = <®_1p8v>; + DVDD-supply = <®_1p8v>; + /* audio */ + #sound-dai-cells = <0>; + nxp,audout-format = "i2s"; + nxp,audout-layout = <0>; + nxp,audout-width = <16>; + nxp,audout-mclk-fs = <128>; + /* + * The 8bpp BT656 mode outputs YCbCr[11:4] across 8bits over + * 2 pixclk cycles. + */ + nxp,vidout-portcfg = + /* YCbCr[11:8]<->VP[15:12]<->CSI_DATA[19:16] */ + < TDA1997X_VP24_V15_12 TDA1997X_R_CR_CBCR_11_8 >, + /* YCbCr[7:4]<->VP[11:08]<->CSI_DATA[15:12] */ + < TDA1997X_VP24_V11_08 TDA1997X_R_CR_CBCR_7_4 >, + + port { + tda1997x_to_ipu1_csi0_mux: endpoint { + remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>; + bus-width = <16>; + hsync-active = <1>; + vsync-active = <1>; + data-active = <1>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/onnn,mt9m001.txt b/Documentation/devicetree/bindings/media/i2c/onnn,mt9m001.txt new file mode 100644 index 000000000000..c920552b03ef --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/onnn,mt9m001.txt @@ -0,0 +1,38 @@ +MT9M001: 1/2-Inch Megapixel Digital Image Sensor + +The MT9M001 is an SXGA-format with a 1/2-inch CMOS active-pixel digital +image sensor. It is programmable through I2C interface. + +Required Properties: + +- compatible: shall be "onnn,mt9m001". +- clocks: reference to the master clock into sensor + +Optional Properties: + +- reset-gpios: GPIO handle which is connected to the reset pin of the chip. + Active low. +- standby-gpios: GPIO handle which is connected to the standby pin of the chip. + Active high. + +The device node must contain one 'port' child node with one 'endpoint' child +sub-node for its digital output video port, in accordance with the video +interface bindings defined in: +Documentation/devicetree/bindings/media/video-interfaces.txt + +Example: + + &i2c1 { + camera-sensor@5d { + compatible = "onnn,mt9m001"; + reg = <0x5d>; + reset-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; + standby-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; + clocks = <&camera_clk>; + port { + mt9m001_out: endpoint { + remote-endpoint = <&vcap_in>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/ov2640.txt b/Documentation/devicetree/bindings/media/i2c/ov2640.txt deleted file mode 100644 index 989ce6cb6ac3..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/ov2640.txt +++ /dev/null @@ -1,41 +0,0 @@ -* Omnivision OV2640 CMOS sensor - -The Omnivision OV2640 sensor supports multiple resolutions output, such as -CIF, SVGA, UXGA. It also can support the YUV422/420, RGB565/555 or raw RGB -output formats. - -Required Properties: -- compatible: should be "ovti,ov2640" -- clocks: reference to the xvclk input clock. -- clock-names: should be "xvclk". - -Optional Properties: -- resetb-gpios: reference to the GPIO connected to the resetb pin, if any. -- pwdn-gpios: reference to the GPIO connected to the pwdn pin, if any. - -The device node must contain one 'port' child node for its digital output -video port, in accordance with the video interface bindings defined in -Documentation/devicetree/bindings/media/video-interfaces.txt. - -Example: - - i2c1: i2c@f0018000 { - ov2640: camera@30 { - compatible = "ovti,ov2640"; - reg = <0x30>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_pck0_as_isi_mck &pinctrl_sensor_power &pinctrl_sensor_reset>; - resetb-gpios = <&pioE 11 GPIO_ACTIVE_LOW>; - pwdn-gpios = <&pioE 13 GPIO_ACTIVE_HIGH>; - clocks = <&pck0>; - clock-names = "xvclk"; - assigned-clocks = <&pck0>; - assigned-clock-rates = <25000000>; - - port { - ov2640_0: endpoint { - remote-endpoint = <&isi_0>; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/ov2659.txt b/Documentation/devicetree/bindings/media/i2c/ov2659.txt deleted file mode 100644 index 92989a619f29..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/ov2659.txt +++ /dev/null @@ -1,47 +0,0 @@ -* OV2659 1/5-Inch 2Mp SOC Camera - -The Omnivision OV2659 is a 1/5-inch SOC camera, with an active array size of -1632H x 1212V. It is programmable through a SCCB. The OV2659 sensor supports -multiple resolutions output, such as UXGA, SVGA, 720p. It also can support -YUV422, RGB565/555 or raw RGB output formats. - -Required Properties: -- compatible: Must be "ovti,ov2659" -- reg: I2C slave address -- clocks: reference to the xvclk input clock. -- clock-names: should be "xvclk". -- link-frequencies: target pixel clock frequency. - -Optional Properties: -- powerdown-gpios: reference to the GPIO connected to the pwdn pin, if any. - Active high with internal pull down resistor. -- reset-gpios: reference to the GPIO connected to the resetb pin, if any. - Active low with internal pull up resistor. - -For further reading on port node refer to -Documentation/devicetree/bindings/media/video-interfaces.txt. - -Example: - - i2c0@1c22000 { - ... - ... - ov2659@30 { - compatible = "ovti,ov2659"; - reg = <0x30>; - - clocks = <&clk_ov2659 0>; - clock-names = "xvclk"; - - powerdown-gpios = <&gpio6 14 GPIO_ACTIVE_HIGH>; - reset-gpios = <&gpio6 15 GPIO_ACTIVE_LOW>; - - port { - ov2659_0: endpoint { - remote-endpoint = <&vpfe_ep>; - link-frequencies = /bits/ 64 <70000000>; - }; - }; - }; - ... - }; diff --git a/Documentation/devicetree/bindings/media/i2c/ov7670.txt b/Documentation/devicetree/bindings/media/i2c/ov7670.txt deleted file mode 100644 index 2c972a56f3cb..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/ov7670.txt +++ /dev/null @@ -1,55 +0,0 @@ -* Omnivision OV7670 CMOS sensor - -The Omnivision OV7670 sensor supports multiple resolutions output, such as -CIF, SVGA, UXGA. It also can support the YUV422/420, RGB565/555 or raw RGB -output formats. - -Required Properties: -- compatible: should be "ovti,ov7670" -- clocks: reference to the xclk input clock. -- clock-names: should be "xclk". - -Required Endpoint Properties: -- hsync-active: active state of the HSYNC signal, 0/1 for LOW/HIGH respectively. -- vsync-active: active state of the VSYNC signal, 0/1 for LOW/HIGH respectively. - -Optional Properties: -- reset-gpios: reference to the GPIO connected to the resetb pin, if any. - Active is low. -- powerdown-gpios: reference to the GPIO connected to the pwdn pin, if any. - Active is high. -- ov7670,pclk-hb-disable: a boolean property to suppress pixel clock output - signal during horizontal blankings. - -The device node must contain one 'port' child node with one 'endpoint' child -sub-node for its digital output video port, in accordance with the video -interface bindings defined in: -Documentation/devicetree/bindings/media/video-interfaces.txt. - -Example: - - i2c1: i2c@f0018000 { - ov7670: camera@21 { - compatible = "ovti,ov7670"; - reg = <0x21>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_pck0_as_isi_mck &pinctrl_sensor_power &pinctrl_sensor_reset>; - reset-gpios = <&pioE 11 GPIO_ACTIVE_LOW>; - powerdown-gpios = <&pioE 13 GPIO_ACTIVE_HIGH>; - clocks = <&pck0>; - clock-names = "xclk"; - assigned-clocks = <&pck0>; - assigned-clock-rates = <25000000>; - - ov7670,pclk-hb-disable; - - port { - ov7670_0: endpoint { - hsync-active = <0>; - vsync-active = <0>; - - remote-endpoint = <&isi_0>; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/ov7740.txt b/Documentation/devicetree/bindings/media/i2c/ov7740.txt deleted file mode 100644 index af781c3a5f0e..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/ov7740.txt +++ /dev/null @@ -1,47 +0,0 @@ -* Omnivision OV7740 CMOS image sensor - -The Omnivision OV7740 image sensor supports multiple output image -size, such as VGA, and QVGA, CIF and any size smaller. It also -supports the RAW RGB and YUV output formats. - -The common video interfaces bindings (see video-interfaces.txt) should -be used to specify link to the image data receiver. The OV7740 device -node should contain one 'port' child node with an 'endpoint' subnode. - -Required Properties: -- compatible: "ovti,ov7740". -- reg: I2C slave address of the sensor. -- clocks: Reference to the xvclk input clock. -- clock-names: "xvclk". - -Optional Properties: -- reset-gpios: Rreference to the GPIO connected to the reset_b pin, - if any. Active low with pull-ip resistor. -- powerdown-gpios: Reference to the GPIO connected to the pwdn pin, - if any. Active high with pull-down resistor. - -Endpoint node mandatory properties: -- remote-endpoint: A phandle to the bus receiver's endpoint node. - -Example: - - i2c1: i2c@fc028000 { - ov7740: camera@21 { - compatible = "ovti,ov7740"; - reg = <0x21>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_sensor_power &pinctrl_sensor_reset>; - clocks = <&isc>; - clock-names = "xvclk"; - assigned-clocks = <&isc>; - assigned-clock-rates = <24000000>; - reset-gpios = <&pioA 43 GPIO_ACTIVE_LOW>; - powerdown-gpios = <&pioA 44 GPIO_ACTIVE_HIGH>; - - port { - ov7740_0: endpoint { - remote-endpoint = <&isc_0>; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/ov9650.txt b/Documentation/devicetree/bindings/media/i2c/ov9650.txt deleted file mode 100644 index 506dfc52872a..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/ov9650.txt +++ /dev/null @@ -1,36 +0,0 @@ -* Omnivision OV9650/OV9652 CMOS sensor - -Required Properties: -- compatible: shall be one of - "ovti,ov9650" - "ovti,ov9652" -- clocks: reference to the xvclk input clock. - -Optional Properties: -- reset-gpios: reference to the GPIO connected to the resetb pin, if any. - Active is high. -- powerdown-gpios: reference to the GPIO connected to the pwdn pin, if any. - Active is high. - -The device node shall contain one 'port' child node with one child 'endpoint' -subnode for its digital output video port, in accordance with the video -interface bindings defined in Documentation/devicetree/bindings/media/ -video-interfaces.txt. - -Example: - -&i2c0 { - ov9650: camera@30 { - compatible = "ovti,ov9650"; - reg = <0x30>; - reset-gpios = <&axi_gpio_0 0 GPIO_ACTIVE_HIGH>; - powerdown-gpios = <&axi_gpio_0 1 GPIO_ACTIVE_HIGH>; - clocks = <&xclk>; - - port { - ov9650_0: endpoint { - remote-endpoint = <&vcap1_in0>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov2640.txt b/Documentation/devicetree/bindings/media/i2c/ovti,ov2640.txt new file mode 100644 index 000000000000..989ce6cb6ac3 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov2640.txt @@ -0,0 +1,41 @@ +* Omnivision OV2640 CMOS sensor + +The Omnivision OV2640 sensor supports multiple resolutions output, such as +CIF, SVGA, UXGA. It also can support the YUV422/420, RGB565/555 or raw RGB +output formats. + +Required Properties: +- compatible: should be "ovti,ov2640" +- clocks: reference to the xvclk input clock. +- clock-names: should be "xvclk". + +Optional Properties: +- resetb-gpios: reference to the GPIO connected to the resetb pin, if any. +- pwdn-gpios: reference to the GPIO connected to the pwdn pin, if any. + +The device node must contain one 'port' child node for its digital output +video port, in accordance with the video interface bindings defined in +Documentation/devicetree/bindings/media/video-interfaces.txt. + +Example: + + i2c1: i2c@f0018000 { + ov2640: camera@30 { + compatible = "ovti,ov2640"; + reg = <0x30>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pck0_as_isi_mck &pinctrl_sensor_power &pinctrl_sensor_reset>; + resetb-gpios = <&pioE 11 GPIO_ACTIVE_LOW>; + pwdn-gpios = <&pioE 13 GPIO_ACTIVE_HIGH>; + clocks = <&pck0>; + clock-names = "xvclk"; + assigned-clocks = <&pck0>; + assigned-clock-rates = <25000000>; + + port { + ov2640_0: endpoint { + remote-endpoint = <&isi_0>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov2659.txt b/Documentation/devicetree/bindings/media/i2c/ovti,ov2659.txt new file mode 100644 index 000000000000..92989a619f29 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov2659.txt @@ -0,0 +1,47 @@ +* OV2659 1/5-Inch 2Mp SOC Camera + +The Omnivision OV2659 is a 1/5-inch SOC camera, with an active array size of +1632H x 1212V. It is programmable through a SCCB. The OV2659 sensor supports +multiple resolutions output, such as UXGA, SVGA, 720p. It also can support +YUV422, RGB565/555 or raw RGB output formats. + +Required Properties: +- compatible: Must be "ovti,ov2659" +- reg: I2C slave address +- clocks: reference to the xvclk input clock. +- clock-names: should be "xvclk". +- link-frequencies: target pixel clock frequency. + +Optional Properties: +- powerdown-gpios: reference to the GPIO connected to the pwdn pin, if any. + Active high with internal pull down resistor. +- reset-gpios: reference to the GPIO connected to the resetb pin, if any. + Active low with internal pull up resistor. + +For further reading on port node refer to +Documentation/devicetree/bindings/media/video-interfaces.txt. + +Example: + + i2c0@1c22000 { + ... + ... + ov2659@30 { + compatible = "ovti,ov2659"; + reg = <0x30>; + + clocks = <&clk_ov2659 0>; + clock-names = "xvclk"; + + powerdown-gpios = <&gpio6 14 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio6 15 GPIO_ACTIVE_LOW>; + + port { + ov2659_0: endpoint { + remote-endpoint = <&vpfe_ep>; + link-frequencies = /bits/ 64 <70000000>; + }; + }; + }; + ... + }; diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov7670.txt b/Documentation/devicetree/bindings/media/i2c/ovti,ov7670.txt new file mode 100644 index 000000000000..2c972a56f3cb --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov7670.txt @@ -0,0 +1,55 @@ +* Omnivision OV7670 CMOS sensor + +The Omnivision OV7670 sensor supports multiple resolutions output, such as +CIF, SVGA, UXGA. It also can support the YUV422/420, RGB565/555 or raw RGB +output formats. + +Required Properties: +- compatible: should be "ovti,ov7670" +- clocks: reference to the xclk input clock. +- clock-names: should be "xclk". + +Required Endpoint Properties: +- hsync-active: active state of the HSYNC signal, 0/1 for LOW/HIGH respectively. +- vsync-active: active state of the VSYNC signal, 0/1 for LOW/HIGH respectively. + +Optional Properties: +- reset-gpios: reference to the GPIO connected to the resetb pin, if any. + Active is low. +- powerdown-gpios: reference to the GPIO connected to the pwdn pin, if any. + Active is high. +- ov7670,pclk-hb-disable: a boolean property to suppress pixel clock output + signal during horizontal blankings. + +The device node must contain one 'port' child node with one 'endpoint' child +sub-node for its digital output video port, in accordance with the video +interface bindings defined in: +Documentation/devicetree/bindings/media/video-interfaces.txt. + +Example: + + i2c1: i2c@f0018000 { + ov7670: camera@21 { + compatible = "ovti,ov7670"; + reg = <0x21>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pck0_as_isi_mck &pinctrl_sensor_power &pinctrl_sensor_reset>; + reset-gpios = <&pioE 11 GPIO_ACTIVE_LOW>; + powerdown-gpios = <&pioE 13 GPIO_ACTIVE_HIGH>; + clocks = <&pck0>; + clock-names = "xclk"; + assigned-clocks = <&pck0>; + assigned-clock-rates = <25000000>; + + ov7670,pclk-hb-disable; + + port { + ov7670_0: endpoint { + hsync-active = <0>; + vsync-active = <0>; + + remote-endpoint = <&isi_0>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov7740.txt b/Documentation/devicetree/bindings/media/i2c/ovti,ov7740.txt new file mode 100644 index 000000000000..af781c3a5f0e --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov7740.txt @@ -0,0 +1,47 @@ +* Omnivision OV7740 CMOS image sensor + +The Omnivision OV7740 image sensor supports multiple output image +size, such as VGA, and QVGA, CIF and any size smaller. It also +supports the RAW RGB and YUV output formats. + +The common video interfaces bindings (see video-interfaces.txt) should +be used to specify link to the image data receiver. The OV7740 device +node should contain one 'port' child node with an 'endpoint' subnode. + +Required Properties: +- compatible: "ovti,ov7740". +- reg: I2C slave address of the sensor. +- clocks: Reference to the xvclk input clock. +- clock-names: "xvclk". + +Optional Properties: +- reset-gpios: Rreference to the GPIO connected to the reset_b pin, + if any. Active low with pull-ip resistor. +- powerdown-gpios: Reference to the GPIO connected to the pwdn pin, + if any. Active high with pull-down resistor. + +Endpoint node mandatory properties: +- remote-endpoint: A phandle to the bus receiver's endpoint node. + +Example: + + i2c1: i2c@fc028000 { + ov7740: camera@21 { + compatible = "ovti,ov7740"; + reg = <0x21>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sensor_power &pinctrl_sensor_reset>; + clocks = <&isc>; + clock-names = "xvclk"; + assigned-clocks = <&isc>; + assigned-clock-rates = <24000000>; + reset-gpios = <&pioA 43 GPIO_ACTIVE_LOW>; + powerdown-gpios = <&pioA 44 GPIO_ACTIVE_HIGH>; + + port { + ov7740_0: endpoint { + remote-endpoint = <&isc_0>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov9650.txt b/Documentation/devicetree/bindings/media/i2c/ovti,ov9650.txt new file mode 100644 index 000000000000..506dfc52872a --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov9650.txt @@ -0,0 +1,36 @@ +* Omnivision OV9650/OV9652 CMOS sensor + +Required Properties: +- compatible: shall be one of + "ovti,ov9650" + "ovti,ov9652" +- clocks: reference to the xvclk input clock. + +Optional Properties: +- reset-gpios: reference to the GPIO connected to the resetb pin, if any. + Active is high. +- powerdown-gpios: reference to the GPIO connected to the pwdn pin, if any. + Active is high. + +The device node shall contain one 'port' child node with one child 'endpoint' +subnode for its digital output video port, in accordance with the video +interface bindings defined in Documentation/devicetree/bindings/media/ +video-interfaces.txt. + +Example: + +&i2c0 { + ov9650: camera@30 { + compatible = "ovti,ov9650"; + reg = <0x30>; + reset-gpios = <&axi_gpio_0 0 GPIO_ACTIVE_HIGH>; + powerdown-gpios = <&axi_gpio_0 1 GPIO_ACTIVE_HIGH>; + clocks = <&xclk>; + + port { + ov9650_0: endpoint { + remote-endpoint = <&vcap1_in0>; + }; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx219.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx219.yaml new file mode 100644 index 000000000000..8b23e5fc6a24 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx219.yaml @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/sony,imx219.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony 1/4.0-Inch 8Mpixel CMOS Digital Image Sensor + +maintainers: + - Dave Stevenson + +description: |- + The Sony imx219 is a 1/4.0-inch CMOS active pixel digital image sensor + with an active array size of 3280H x 2464V. It is programmable through + I2C interface. The I2C address is fixed to 0x10 as per sensor data sheet. + Image data is sent through MIPI CSI-2, which is configured as either 2 or + 4 data lanes. + +properties: + compatible: + const: sony,imx219 + + reg: + description: I2C device address + maxItems: 1 + + clocks: + maxItems: 1 + + VDIG-supply: + description: + Digital I/O voltage supply, 1.8 volts + + VANA-supply: + description: + Analog voltage supply, 2.8 volts + + VDDL-supply: + description: + Digital core voltage supply, 1.2 volts + + reset-gpios: + maxItems: 1 + description: |- + Reference to the GPIO connected to the xclr pin, if any. + Must be released (set high) after all supplies are applied. + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + description: |- + The sensor supports either two-lane, or four-lane operation. + If this property is omitted four-lane operation is assumed. + For two-lane operation the property must be set to <1 2>. + items: + - const: 1 + - const: 2 + + clock-noncontinuous: true + link-frequencies: true + + required: + - link-frequencies + +required: + - compatible + - reg + - clocks + - VANA-supply + - VDIG-supply + - VDDL-supply + - port + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + imx219: sensor@10 { + compatible = "sony,imx219"; + reg = <0x10>; + clocks = <&imx219_clk>; + VANA-supply = <&imx219_vana>; /* 2.8v */ + VDIG-supply = <&imx219_vdig>; /* 1.8v */ + VDDL-supply = <&imx219_vddl>; /* 1.2v */ + + port { + imx219_0: endpoint { + remote-endpoint = <&csi1_ep>; + data-lanes = <1 2>; + clock-noncontinuous; + link-frequencies = /bits/ 64 <456000000>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/i2c/tc358743.txt b/Documentation/devicetree/bindings/media/i2c/tc358743.txt deleted file mode 100644 index 59102edcf01e..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/tc358743.txt +++ /dev/null @@ -1,48 +0,0 @@ -* Toshiba TC358743 HDMI-RX to MIPI CSI2-TX Bridge - -The Toshiba TC358743 HDMI-RX to MIPI CSI2-TX (H2C) is a bridge that converts -a HDMI stream to MIPI CSI-2 TX. It is programmable through I2C. - -Required Properties: - -- compatible: value should be "toshiba,tc358743" -- clocks, clock-names: should contain a phandle link to the reference clock - source, the clock input is named "refclk". - -Optional Properties: - -- reset-gpios: gpio phandle GPIO connected to the reset pin -- interrupts: GPIO connected to the interrupt pin -- data-lanes: should be <1 2 3 4> for four-lane operation, - or <1 2> for two-lane operation -- clock-lanes: should be <0> -- clock-noncontinuous: Presence of this boolean property decides whether the - MIPI CSI-2 clock is continuous or non-continuous. -- link-frequencies: List of allowed link frequencies in Hz. Each frequency is - expressed as a 64-bit big-endian integer. The frequency - is half of the bps per lane due to DDR transmission. - -For further information on the MIPI CSI-2 endpoint node properties, see -Documentation/devicetree/bindings/media/video-interfaces.txt. - -Example: - - tc358743@f { - compatible = "toshiba,tc358743"; - reg = <0x0f>; - clocks = <&hdmi_osc>; - clock-names = "refclk"; - reset-gpios = <&gpio6 9 GPIO_ACTIVE_LOW>; - interrupt-parent = <&gpio2>; - interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; - - port { - tc358743_out: endpoint { - remote-endpoint = <&mipi_csi2_in>; - data-lanes = <1 2 3 4>; - clock-lanes = <0>; - clock-noncontinuous; - link-frequencies = /bits/ 64 <297000000>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/tda1997x.txt b/Documentation/devicetree/bindings/media/i2c/tda1997x.txt deleted file mode 100644 index e76167999d76..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/tda1997x.txt +++ /dev/null @@ -1,178 +0,0 @@ -Device-Tree bindings for the NXP TDA1997x HDMI receiver - -The TDA19971/73 are HDMI video receivers. - -The TDA19971 Video port output pins can be used as follows: - - RGB 8bit per color (24 bits total): R[11:4] B[11:4] G[11:4] - - YUV444 8bit per color (24 bits total): Y[11:4] Cr[11:4] Cb[11:4] - - YUV422 semi-planar 8bit per component (16 bits total): Y[11:4] CbCr[11:4] - - YUV422 semi-planar 10bit per component (20 bits total): Y[11:2] CbCr[11:2] - - YUV422 semi-planar 12bit per component (24 bits total): - Y[11:0] CbCr[11:0] - - YUV422 BT656 8bit per component (8 bits total): YCbCr[11:4] (2-cycles) - - YUV422 BT656 10bit per component (10 bits total): YCbCr[11:2] (2-cycles) - - YUV422 BT656 12bit per component (12 bits total): YCbCr[11:0] (2-cycles) - -The TDA19973 Video port output pins can be used as follows: - - RGB 12bit per color (36 bits total): R[11:0] B[11:0] G[11:0] - - YUV444 12bit per color (36 bits total): Y[11:0] Cb[11:0] Cr[11:0] - - YUV422 semi-planar 12bit per component (24 bits total): Y[11:0] CbCr[11:0] - - YUV422 BT656 12bit per component (12 bits total): YCbCr[11:0] (2-cycles) - -The Video port output pins are mapped via 4-bit 'pin groups' allowing -for a variety of connection possibilities including swapping pin order within -pin groups. The video_portcfg device-tree property consists of register mapping -pairs which map a chip-specific VP output register to a 4-bit pin group. If -the pin group needs to be bit-swapped you can use the *_S pin-group defines. - -Required Properties: - - compatible : - - "nxp,tda19971" for the TDA19971 - - "nxp,tda19973" for the TDA19973 - - reg : I2C slave address - - interrupts : The interrupt number - - DOVDD-supply : Digital I/O supply - - DVDD-supply : Digital Core supply - - AVDD-supply : Analog supply - - nxp,vidout-portcfg : array of pairs mapping VP output pins to pin groups. - -Optional Properties: - - nxp,audout-format : DAI bus format: "i2s" or "spdif". - - nxp,audout-width : width of audio output data bus (1-4). - - nxp,audout-layout : data layout (0=AP0 used, 1=AP0/AP1/AP2/AP3 used). - - nxp,audout-mclk-fs : Multiplication factor between stream rate and codec - mclk. - -The port node shall contain one endpoint child node for its digital -output video port, in accordance with the video interface bindings defined in -Documentation/devicetree/bindings/media/video-interfaces.txt. - -Optional Endpoint Properties: - The following three properties are defined in video-interfaces.txt and - are valid for the output parallel bus endpoint: - - hsync-active: Horizontal synchronization polarity. Defaults to active high. - - vsync-active: Vertical synchronization polarity. Defaults to active high. - - data-active: Data polarity. Defaults to active high. - -Examples: - - VP[15:0] connected to IMX6 CSI_DATA[19:4] for 16bit YUV422 - 16bit I2S layout0 with a 128*fs clock (A_WS, AP0, A_CLK pins) - hdmi-receiver@48 { - compatible = "nxp,tda19971"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_tda1997x>; - reg = <0x48>; - interrupt-parent = <&gpio1>; - interrupts = <7 IRQ_TYPE_LEVEL_LOW>; - DOVDD-supply = <®_3p3v>; - AVDD-supply = <®_1p8v>; - DVDD-supply = <®_1p8v>; - /* audio */ - #sound-dai-cells = <0>; - nxp,audout-format = "i2s"; - nxp,audout-layout = <0>; - nxp,audout-width = <16>; - nxp,audout-mclk-fs = <128>; - /* - * The 8bpp YUV422 semi-planar mode outputs CbCr[11:4] - * and Y[11:4] across 16bits in the same pixclk cycle. - */ - nxp,vidout-portcfg = - /* Y[11:8]<->VP[15:12]<->CSI_DATA[19:16] */ - < TDA1997X_VP24_V15_12 TDA1997X_G_Y_11_8 >, - /* Y[7:4]<->VP[11:08]<->CSI_DATA[15:12] */ - < TDA1997X_VP24_V11_08 TDA1997X_G_Y_7_4 >, - /* CbCc[11:8]<->VP[07:04]<->CSI_DATA[11:8] */ - < TDA1997X_VP24_V07_04 TDA1997X_R_CR_CBCR_11_8 >, - /* CbCr[7:4]<->VP[03:00]<->CSI_DATA[7:4] */ - < TDA1997X_VP24_V03_00 TDA1997X_R_CR_CBCR_7_4 >; - - port { - tda1997x_to_ipu1_csi0_mux: endpoint { - remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>; - bus-width = <16>; - hsync-active = <1>; - vsync-active = <1>; - data-active = <1>; - }; - }; - }; - - VP[15:8] connected to IMX6 CSI_DATA[19:12] for 8bit BT656 - 16bit I2S layout0 with a 128*fs clock (A_WS, AP0, A_CLK pins) - hdmi-receiver@48 { - compatible = "nxp,tda19971"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_tda1997x>; - reg = <0x48>; - interrupt-parent = <&gpio1>; - interrupts = <7 IRQ_TYPE_LEVEL_LOW>; - DOVDD-supply = <®_3p3v>; - AVDD-supply = <®_1p8v>; - DVDD-supply = <®_1p8v>; - /* audio */ - #sound-dai-cells = <0>; - nxp,audout-format = "i2s"; - nxp,audout-layout = <0>; - nxp,audout-width = <16>; - nxp,audout-mclk-fs = <128>; - /* - * The 8bpp YUV422 semi-planar mode outputs CbCr[11:4] - * and Y[11:4] across 16bits in the same pixclk cycle. - */ - nxp,vidout-portcfg = - /* Y[11:8]<->VP[15:12]<->CSI_DATA[19:16] */ - < TDA1997X_VP24_V15_12 TDA1997X_G_Y_11_8 >, - /* Y[7:4]<->VP[11:08]<->CSI_DATA[15:12] */ - < TDA1997X_VP24_V11_08 TDA1997X_G_Y_7_4 >, - /* CbCc[11:8]<->VP[07:04]<->CSI_DATA[11:8] */ - < TDA1997X_VP24_V07_04 TDA1997X_R_CR_CBCR_11_8 >, - /* CbCr[7:4]<->VP[03:00]<->CSI_DATA[7:4] */ - < TDA1997X_VP24_V03_00 TDA1997X_R_CR_CBCR_7_4 >; - - port { - tda1997x_to_ipu1_csi0_mux: endpoint { - remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>; - bus-width = <16>; - hsync-active = <1>; - vsync-active = <1>; - data-active = <1>; - }; - }; - }; - - VP[15:8] connected to IMX6 CSI_DATA[19:12] for 8bit BT656 - 16bit I2S layout0 with a 128*fs clock (A_WS, AP0, A_CLK pins) - hdmi-receiver@48 { - compatible = "nxp,tda19971"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_tda1997x>; - reg = <0x48>; - interrupt-parent = <&gpio1>; - interrupts = <7 IRQ_TYPE_LEVEL_LOW>; - DOVDD-supply = <®_3p3v>; - AVDD-supply = <®_1p8v>; - DVDD-supply = <®_1p8v>; - /* audio */ - #sound-dai-cells = <0>; - nxp,audout-format = "i2s"; - nxp,audout-layout = <0>; - nxp,audout-width = <16>; - nxp,audout-mclk-fs = <128>; - /* - * The 8bpp BT656 mode outputs YCbCr[11:4] across 8bits over - * 2 pixclk cycles. - */ - nxp,vidout-portcfg = - /* YCbCr[11:8]<->VP[15:12]<->CSI_DATA[19:16] */ - < TDA1997X_VP24_V15_12 TDA1997X_R_CR_CBCR_11_8 >, - /* YCbCr[7:4]<->VP[11:08]<->CSI_DATA[15:12] */ - < TDA1997X_VP24_V11_08 TDA1997X_R_CR_CBCR_7_4 >, - - port { - tda1997x_to_ipu1_csi0_mux: endpoint { - remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>; - bus-width = <16>; - hsync-active = <1>; - vsync-active = <1>; - data-active = <1>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/ths8200.txt b/Documentation/devicetree/bindings/media/i2c/ths8200.txt deleted file mode 100644 index 285f6ae7dfa9..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/ths8200.txt +++ /dev/null @@ -1,19 +0,0 @@ -* Texas Instruments THS8200 video encoder - -The ths8200 device is a digital to analog converter used in DVD players, video -recorders, set-top boxes. - -Required Properties : -- compatible : value must be "ti,ths8200" - -Example: - - i2c0@1c22000 { - ... - ... - ths8200@5c { - compatible = "ti,ths8200"; - reg = <0x5c>; - }; - ... - }; diff --git a/Documentation/devicetree/bindings/media/i2c/ti,ths8200.txt b/Documentation/devicetree/bindings/media/i2c/ti,ths8200.txt new file mode 100644 index 000000000000..285f6ae7dfa9 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ti,ths8200.txt @@ -0,0 +1,19 @@ +* Texas Instruments THS8200 video encoder + +The ths8200 device is a digital to analog converter used in DVD players, video +recorders, set-top boxes. + +Required Properties : +- compatible : value must be "ti,ths8200" + +Example: + + i2c0@1c22000 { + ... + ... + ths8200@5c { + compatible = "ti,ths8200"; + reg = <0x5c>; + }; + ... + }; diff --git a/Documentation/devicetree/bindings/media/i2c/ti,tvp514x.txt b/Documentation/devicetree/bindings/media/i2c/ti,tvp514x.txt new file mode 100644 index 000000000000..46752cc71f2e --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ti,tvp514x.txt @@ -0,0 +1,44 @@ +* Texas Instruments TVP514x video decoder + +The TVP5146/TVP5146m2/TVP5147/TVP5147m1 device is high quality, single-chip +digital video decoder that digitizes and decodes all popular baseband analog +video formats into digital video component. The tvp514x decoder supports analog- +to-digital (A/D) conversion of component RGB and YPbPr signals as well as A/D +conversion and decoding of NTSC, PAL and SECAM composite and S-video into +component YCbCr. + +Required Properties : +- compatible : value should be either one among the following + (a) "ti,tvp5146" for tvp5146 decoder. + (b) "ti,tvp5146m2" for tvp5146m2 decoder. + (c) "ti,tvp5147" for tvp5147 decoder. + (d) "ti,tvp5147m1" for tvp5147m1 decoder. + +- hsync-active: HSYNC Polarity configuration for endpoint. + +- vsync-active: VSYNC Polarity configuration for endpoint. + +- pclk-sample: Clock polarity of the endpoint. + +For further reading on port node refer to Documentation/devicetree/bindings/ +media/video-interfaces.txt. + +Example: + + i2c0@1c22000 { + ... + ... + tvp514x@5c { + compatible = "ti,tvp5146"; + reg = <0x5c>; + + port { + tvp514x_1: endpoint { + hsync-active = <1>; + vsync-active = <1>; + pclk-sample = <0>; + }; + }; + }; + ... + }; diff --git a/Documentation/devicetree/bindings/media/i2c/ti,tvp5150.txt b/Documentation/devicetree/bindings/media/i2c/ti,tvp5150.txt new file mode 100644 index 000000000000..94b908ace53c --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ti,tvp5150.txt @@ -0,0 +1,157 @@ +* Texas Instruments TVP5150 and TVP5151 video decoders + +The TVP5150 and TVP5151 are video decoders that convert baseband NTSC and PAL +(and also SECAM in the TVP5151 case) video signals to either 8-bit 4:2:2 YUV +with discrete syncs or 8-bit ITU-R BT.656 with embedded syncs output formats. + +Required Properties: +==================== +- compatible: Value must be "ti,tvp5150". +- reg: I2C slave address. + +Optional Properties: +==================== +- pdn-gpios: Phandle for the GPIO connected to the PDN pin, if any. +- reset-gpios: Phandle for the GPIO connected to the RESETB pin, if any. + +The device node must contain one 'port' child node per device physical input +and output port, in accordance with the video interface bindings defined in +Documentation/devicetree/bindings/media/video-interfaces.txt. The port nodes +are numbered as follows + + Name Type Port + -------------------------------------- + AIP1A sink 0 + AIP1B sink 1 + Y-OUT src 2 + +The device node must contain at least one sink port and the src port. Each input +port must be linked to an endpoint defined in [1]. The port/connector layout is +as follows + +tvp-5150 port@0 (AIP1A) + endpoint@0 -----------> Comp0-Con port + endpoint@1 ------+----> Svideo-Con port +tvp-5150 port@1 (AIP1B) | + endpoint@1 ------+ + endpoint@0 -----------> Comp1-Con port +tvp-5150 port@2 + endpoint (video bitstream output at YOUT[0-7] parallel bus) + +Required Endpoint Properties for parallel synchronization on output port: +========================================================================= + +- hsync-active: Active state of the HSYNC signal. Must be <1> (HIGH). +- vsync-active: Active state of the VSYNC signal. Must be <1> (HIGH). +- field-even-active: Field signal level during the even field data + transmission. Must be <0>. + +Note: Do not specify any of these properties if you want to use the embedded + BT.656 synchronization. + +Optional Connector Properties: +============================== + +- sdtv-standards: Set the possible signals to which the hardware tries to lock + instead of using the autodetection mechanism. Please look at + [1] for more information. + +[1] Documentation/devicetree/bindings/display/connector/analog-tv-connector.yaml. + +Example - three input sources: +#include + +comp_connector_0 { + compatible = "composite-video-connector"; + label = "Composite0"; + sdtv-standards = ; /* limit to pal-m signals */ + + port { + composite0_to_tvp5150: endpoint { + remote-endpoint = <&tvp5150_to_composite0>; + }; + }; +}; + +comp_connector_1 { + compatible = "composite-video-connector"; + label = "Composite1"; + sdtv-standards = ; /* limit to ntsc-m signals */ + + port { + composite1_to_tvp5150: endpoint { + remote-endpoint = <&tvp5150_to_composite1>; + }; + }; +}; + +svideo_connector { + compatible = "svideo-connector"; + label = "S-Video"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + svideo_luma_to_tvp5150: endpoint@0 { + reg = <0>; + remote-endpoint = <&tvp5150_to_svideo_luma>; + }; + + svideo_chroma_to_tvp5150: endpoint@1 { + reg = <1>; + remote-endpoint = <&tvp5150_to_svideo_chroma>; + }; + }; +}; + +&i2c2 { + tvp5150@5c { + compatible = "ti,tvp5150"; + reg = <0x5c>; + pdn-gpios = <&gpio4 30 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio6 7 GPIO_ACTIVE_LOW>; + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + tvp5150_to_composite0: endpoint@0 { + reg = <0>; + remote-endpoint = <&composite0_to_tvp5150>; + }; + + tvp5150_to_svideo_luma: endpoint@1 { + reg = <1>; + remote-endpoint = <&svideo_luma_to_tvp5150>; + }; + }; + + port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + tvp5150_to_composite1: endpoint@0 { + reg = <0>; + remote-endpoint = <&composite1_to_tvp5150>; + }; + + tvp5150_to_svideo_chroma: endpoint@1 { + reg = <1>; + remote-endpoint = <&svideo_chroma_to_tvp5150>; + }; + }; + + port@2 { + reg = <2>; + + tvp5150_1: endpoint { + remote-endpoint = <&ccdc_ep>; + }; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/media/i2c/ti,tvp7002.txt b/Documentation/devicetree/bindings/media/i2c/ti,tvp7002.txt new file mode 100644 index 000000000000..5f28b5d9abcc --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ti,tvp7002.txt @@ -0,0 +1,53 @@ +* Texas Instruments TV7002 video decoder + +The TVP7002 device supports digitizing of video and graphics signal in RGB and +YPbPr color space. + +Required Properties : +- compatible : Must be "ti,tvp7002" + +Optional Properties: +- hsync-active: HSYNC Polarity configuration for the bus. Default value when + this property is not specified is <0>. + +- vsync-active: VSYNC Polarity configuration for the bus. Default value when + this property is not specified is <0>. + +- pclk-sample: Clock polarity of the bus. Default value when this property is + not specified is <0>. + +- sync-on-green-active: Active state of Sync-on-green signal property of the + endpoint. + 0 = Normal Operation (Active Low, Default) + 1 = Inverted operation + +- field-even-active: Active-high Field ID output polarity control of the bus. + Under normal operation, the field ID output is set to logic 1 for an odd field + (field 1) and set to logic 0 for an even field (field 0). + 0 = Normal Operation (Active Low, Default) + 1 = FID output polarity inverted + +For further reading of port node refer Documentation/devicetree/bindings/media/ +video-interfaces.txt. + +Example: + + i2c0@1c22000 { + ... + ... + tvp7002@5c { + compatible = "ti,tvp7002"; + reg = <0x5c>; + + port { + tvp7002_1: endpoint { + hsync-active = <1>; + vsync-active = <1>; + pclk-sample = <0>; + sync-on-green-active = <1>; + field-even-active = <0>; + }; + }; + }; + ... + }; diff --git a/Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.txt b/Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.txt new file mode 100644 index 000000000000..59102edcf01e --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.txt @@ -0,0 +1,48 @@ +* Toshiba TC358743 HDMI-RX to MIPI CSI2-TX Bridge + +The Toshiba TC358743 HDMI-RX to MIPI CSI2-TX (H2C) is a bridge that converts +a HDMI stream to MIPI CSI-2 TX. It is programmable through I2C. + +Required Properties: + +- compatible: value should be "toshiba,tc358743" +- clocks, clock-names: should contain a phandle link to the reference clock + source, the clock input is named "refclk". + +Optional Properties: + +- reset-gpios: gpio phandle GPIO connected to the reset pin +- interrupts: GPIO connected to the interrupt pin +- data-lanes: should be <1 2 3 4> for four-lane operation, + or <1 2> for two-lane operation +- clock-lanes: should be <0> +- clock-noncontinuous: Presence of this boolean property decides whether the + MIPI CSI-2 clock is continuous or non-continuous. +- link-frequencies: List of allowed link frequencies in Hz. Each frequency is + expressed as a 64-bit big-endian integer. The frequency + is half of the bps per lane due to DDR transmission. + +For further information on the MIPI CSI-2 endpoint node properties, see +Documentation/devicetree/bindings/media/video-interfaces.txt. + +Example: + + tc358743@f { + compatible = "toshiba,tc358743"; + reg = <0x0f>; + clocks = <&hdmi_osc>; + clock-names = "refclk"; + reset-gpios = <&gpio6 9 GPIO_ACTIVE_LOW>; + interrupt-parent = <&gpio2>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + + port { + tc358743_out: endpoint { + remote-endpoint = <&mipi_csi2_in>; + data-lanes = <1 2 3 4>; + clock-lanes = <0>; + clock-noncontinuous; + link-frequencies = /bits/ 64 <297000000>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/tvp514x.txt b/Documentation/devicetree/bindings/media/i2c/tvp514x.txt deleted file mode 100644 index 46752cc71f2e..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/tvp514x.txt +++ /dev/null @@ -1,44 +0,0 @@ -* Texas Instruments TVP514x video decoder - -The TVP5146/TVP5146m2/TVP5147/TVP5147m1 device is high quality, single-chip -digital video decoder that digitizes and decodes all popular baseband analog -video formats into digital video component. The tvp514x decoder supports analog- -to-digital (A/D) conversion of component RGB and YPbPr signals as well as A/D -conversion and decoding of NTSC, PAL and SECAM composite and S-video into -component YCbCr. - -Required Properties : -- compatible : value should be either one among the following - (a) "ti,tvp5146" for tvp5146 decoder. - (b) "ti,tvp5146m2" for tvp5146m2 decoder. - (c) "ti,tvp5147" for tvp5147 decoder. - (d) "ti,tvp5147m1" for tvp5147m1 decoder. - -- hsync-active: HSYNC Polarity configuration for endpoint. - -- vsync-active: VSYNC Polarity configuration for endpoint. - -- pclk-sample: Clock polarity of the endpoint. - -For further reading on port node refer to Documentation/devicetree/bindings/ -media/video-interfaces.txt. - -Example: - - i2c0@1c22000 { - ... - ... - tvp514x@5c { - compatible = "ti,tvp5146"; - reg = <0x5c>; - - port { - tvp514x_1: endpoint { - hsync-active = <1>; - vsync-active = <1>; - pclk-sample = <0>; - }; - }; - }; - ... - }; diff --git a/Documentation/devicetree/bindings/media/i2c/tvp5150.txt b/Documentation/devicetree/bindings/media/i2c/tvp5150.txt deleted file mode 100644 index 94b908ace53c..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/tvp5150.txt +++ /dev/null @@ -1,157 +0,0 @@ -* Texas Instruments TVP5150 and TVP5151 video decoders - -The TVP5150 and TVP5151 are video decoders that convert baseband NTSC and PAL -(and also SECAM in the TVP5151 case) video signals to either 8-bit 4:2:2 YUV -with discrete syncs or 8-bit ITU-R BT.656 with embedded syncs output formats. - -Required Properties: -==================== -- compatible: Value must be "ti,tvp5150". -- reg: I2C slave address. - -Optional Properties: -==================== -- pdn-gpios: Phandle for the GPIO connected to the PDN pin, if any. -- reset-gpios: Phandle for the GPIO connected to the RESETB pin, if any. - -The device node must contain one 'port' child node per device physical input -and output port, in accordance with the video interface bindings defined in -Documentation/devicetree/bindings/media/video-interfaces.txt. The port nodes -are numbered as follows - - Name Type Port - -------------------------------------- - AIP1A sink 0 - AIP1B sink 1 - Y-OUT src 2 - -The device node must contain at least one sink port and the src port. Each input -port must be linked to an endpoint defined in [1]. The port/connector layout is -as follows - -tvp-5150 port@0 (AIP1A) - endpoint@0 -----------> Comp0-Con port - endpoint@1 ------+----> Svideo-Con port -tvp-5150 port@1 (AIP1B) | - endpoint@1 ------+ - endpoint@0 -----------> Comp1-Con port -tvp-5150 port@2 - endpoint (video bitstream output at YOUT[0-7] parallel bus) - -Required Endpoint Properties for parallel synchronization on output port: -========================================================================= - -- hsync-active: Active state of the HSYNC signal. Must be <1> (HIGH). -- vsync-active: Active state of the VSYNC signal. Must be <1> (HIGH). -- field-even-active: Field signal level during the even field data - transmission. Must be <0>. - -Note: Do not specify any of these properties if you want to use the embedded - BT.656 synchronization. - -Optional Connector Properties: -============================== - -- sdtv-standards: Set the possible signals to which the hardware tries to lock - instead of using the autodetection mechanism. Please look at - [1] for more information. - -[1] Documentation/devicetree/bindings/display/connector/analog-tv-connector.yaml. - -Example - three input sources: -#include - -comp_connector_0 { - compatible = "composite-video-connector"; - label = "Composite0"; - sdtv-standards = ; /* limit to pal-m signals */ - - port { - composite0_to_tvp5150: endpoint { - remote-endpoint = <&tvp5150_to_composite0>; - }; - }; -}; - -comp_connector_1 { - compatible = "composite-video-connector"; - label = "Composite1"; - sdtv-standards = ; /* limit to ntsc-m signals */ - - port { - composite1_to_tvp5150: endpoint { - remote-endpoint = <&tvp5150_to_composite1>; - }; - }; -}; - -svideo_connector { - compatible = "svideo-connector"; - label = "S-Video"; - - port { - #address-cells = <1>; - #size-cells = <0>; - - svideo_luma_to_tvp5150: endpoint@0 { - reg = <0>; - remote-endpoint = <&tvp5150_to_svideo_luma>; - }; - - svideo_chroma_to_tvp5150: endpoint@1 { - reg = <1>; - remote-endpoint = <&tvp5150_to_svideo_chroma>; - }; - }; -}; - -&i2c2 { - tvp5150@5c { - compatible = "ti,tvp5150"; - reg = <0x5c>; - pdn-gpios = <&gpio4 30 GPIO_ACTIVE_LOW>; - reset-gpios = <&gpio6 7 GPIO_ACTIVE_LOW>; - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - #address-cells = <1>; - #size-cells = <0>; - reg = <0>; - - tvp5150_to_composite0: endpoint@0 { - reg = <0>; - remote-endpoint = <&composite0_to_tvp5150>; - }; - - tvp5150_to_svideo_luma: endpoint@1 { - reg = <1>; - remote-endpoint = <&svideo_luma_to_tvp5150>; - }; - }; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - reg = <1>; - - tvp5150_to_composite1: endpoint@0 { - reg = <0>; - remote-endpoint = <&composite1_to_tvp5150>; - }; - - tvp5150_to_svideo_chroma: endpoint@1 { - reg = <1>; - remote-endpoint = <&svideo_chroma_to_tvp5150>; - }; - }; - - port@2 { - reg = <2>; - - tvp5150_1: endpoint { - remote-endpoint = <&ccdc_ep>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/media/i2c/tvp7002.txt b/Documentation/devicetree/bindings/media/i2c/tvp7002.txt deleted file mode 100644 index 5f28b5d9abcc..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/tvp7002.txt +++ /dev/null @@ -1,53 +0,0 @@ -* Texas Instruments TV7002 video decoder - -The TVP7002 device supports digitizing of video and graphics signal in RGB and -YPbPr color space. - -Required Properties : -- compatible : Must be "ti,tvp7002" - -Optional Properties: -- hsync-active: HSYNC Polarity configuration for the bus. Default value when - this property is not specified is <0>. - -- vsync-active: VSYNC Polarity configuration for the bus. Default value when - this property is not specified is <0>. - -- pclk-sample: Clock polarity of the bus. Default value when this property is - not specified is <0>. - -- sync-on-green-active: Active state of Sync-on-green signal property of the - endpoint. - 0 = Normal Operation (Active Low, Default) - 1 = Inverted operation - -- field-even-active: Active-high Field ID output polarity control of the bus. - Under normal operation, the field ID output is set to logic 1 for an odd field - (field 1) and set to logic 0 for an even field (field 0). - 0 = Normal Operation (Active Low, Default) - 1 = FID output polarity inverted - -For further reading of port node refer Documentation/devicetree/bindings/media/ -video-interfaces.txt. - -Example: - - i2c0@1c22000 { - ... - ... - tvp7002@5c { - compatible = "ti,tvp7002"; - reg = <0x5c>; - - port { - tvp7002_1: endpoint { - hsync-active = <1>; - vsync-active = <1>; - pclk-sample = <0>; - sync-on-green-active = <1>; - field-even-active = <0>; - }; - }; - }; - ... - }; diff --git a/MAINTAINERS b/MAINTAINERS index 83f9f46a6bcb..393d92c2e610 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -528,6 +528,7 @@ ADP1653 FLASH CONTROLLER DRIVER M: Sakari Ailus L: linux-media@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/media/i2c/adi,adp1653.txt F: drivers/media/i2c/adp1653.c F: include/media/i2c/adp1653.h @@ -1587,14 +1588,14 @@ M: Lars-Peter Clausen L: linux-media@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers -F: Documentation/devicetree/bindings/media/i2c/adv7180.yaml +F: Documentation/devicetree/bindings/media/i2c/adi,adv7180.yaml F: drivers/media/i2c/adv7180.c ANALOG DEVICES INC ADV748X DRIVER M: Kieran Bingham L: linux-media@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/media/i2c/adv748x.yaml +F: Documentation/devicetree/bindings/media/i2c/adi,adv748x.yaml F: drivers/media/i2c/adv748x/* ANALOG DEVICES INC ADV7511 DRIVER @@ -1607,7 +1608,7 @@ ANALOG DEVICES INC ADV7604 DRIVER M: Hans Verkuil L: linux-media@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/media/i2c/adv7604.yaml +F: Documentation/devicetree/bindings/media/i2c/adi,adv7604.yaml F: drivers/media/i2c/adv7604* ANALOG DEVICES INC ADV7842 DRIVER @@ -14448,7 +14449,7 @@ M: Ramesh Shanmugasundaram L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git -F: Documentation/devicetree/bindings/media/i2c/max2175.txt +F: Documentation/devicetree/bindings/media/i2c/maxim,max2175.txt F: Documentation/userspace-api/media/drivers/max2175.rst F: drivers/media/i2c/max2175* F: include/uapi/linux/max2175.h @@ -16491,7 +16492,7 @@ M: Laurent Pinchart L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git -F: Documentation/devicetree/bindings/media/i2c/mt9v032.txt +F: Documentation/devicetree/bindings/media/i2c/aptina,mt9v032.txt F: drivers/media/i2c/mt9v032.c F: include/media/i2c/mt9v032.h @@ -17945,7 +17946,7 @@ OMNIVISION OV7740 SENSOR DRIVER L: linux-media@vger.kernel.org S: Orphan T: git git://linuxtv.org/media.git -F: Documentation/devicetree/bindings/media/i2c/ov7740.txt +F: Documentation/devicetree/bindings/media/i2c/ovti,ov7740.txt F: drivers/media/i2c/ov7740.c OMNIVISION OV8856 SENSOR DRIVER @@ -17986,7 +17987,7 @@ R: Sylwester Nawrocki L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git -F: Documentation/devicetree/bindings/media/i2c/ov9650.txt +F: Documentation/devicetree/bindings/media/i2c/ovti,ov9650.txt F: drivers/media/i2c/ov9650.c OMNIVISION OV9734 SENSOR DRIVER @@ -18191,6 +18192,7 @@ S: Maintained W: https://linuxtv.org Q: http://patchwork.linuxtv.org/project/linux-media/list/ T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git +F: Documentation/devicetree/bindings/media/i2c/ovti,ov2659.txt F: drivers/media/i2c/ov2659.c F: include/media/i2c/ov2659.h @@ -22482,7 +22484,7 @@ M: Dave Stevenson L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git -F: Documentation/devicetree/bindings/media/i2c/imx219.yaml +F: Documentation/devicetree/bindings/media/i2c/sony,imx219.yaml F: drivers/media/i2c/imx219.c SONY IMX258 SENSOR DRIVER @@ -23614,6 +23616,7 @@ L: linux-media@vger.kernel.org S: Maintained W: https://linuxtv.org Q: http://patchwork.linuxtv.org/project/linux-media/list/ +F: Documentation/devicetree/bindings/media/i2c/nxp,tda1997x.txt F: drivers/media/i2c/tda1997x.* TDA827x MEDIA DRIVER @@ -24451,7 +24454,7 @@ TOSHIBA TC358743 DRIVER M: Hans Verkuil L: linux-media@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/media/i2c/tc358743.txt +F: Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.txt F: drivers/media/i2c/tc358743* F: include/media/i2c/tc358743.h -- cgit v1.2.3-59-g8ed1b From 671b550fe6282b936d79953731126ddffd75b520 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:04 +0530 Subject: media: i2c: ds90ub953: Fix error prints ub953_read_ind() and ub953_write_ind() have broken error prints, and the register address is printed incorrectly. Fix the prints. Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub953.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index 46569381b332..7b33b8cc83c1 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -258,7 +258,7 @@ static int ub953_read_ind(struct ub953_data *priv, u8 block, u8 reg, u8 *val) ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_ADDR, reg); if (ret) { dev_err(&priv->client->dev, - "Write to IND_ACC_ADDR failed when reading %u:%x02x: %d\n", + "Write to IND_ACC_ADDR failed when reading %u:0x%02x: %d\n", block, reg, ret); goto out_unlock; } @@ -266,7 +266,7 @@ static int ub953_read_ind(struct ub953_data *priv, u8 block, u8 reg, u8 *val) ret = regmap_read(priv->regmap, UB953_REG_IND_ACC_DATA, &v); if (ret) { dev_err(&priv->client->dev, - "Write to IND_ACC_DATA failed when reading %u:%x02x: %d\n", + "Write to IND_ACC_DATA failed when reading %u:0x%02x: %d\n", block, reg, ret); goto out_unlock; } @@ -293,7 +293,7 @@ static int ub953_write_ind(struct ub953_data *priv, u8 block, u8 reg, u8 val) ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_ADDR, reg); if (ret) { dev_err(&priv->client->dev, - "Write to IND_ACC_ADDR failed when writing %u:%x02x: %d\n", + "Write to IND_ACC_ADDR failed when writing %u:0x%02x: %d\n", block, reg, ret); goto out_unlock; } @@ -301,7 +301,7 @@ static int ub953_write_ind(struct ub953_data *priv, u8 block, u8 reg, u8 val) ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_DATA, val); if (ret) { dev_err(&priv->client->dev, - "Write to IND_ACC_DATA failed when writing %u:%x02x\n: %d\n", + "Write to IND_ACC_DATA failed when writing %u:0x%02x: %d\n", block, reg, ret); } -- cgit v1.2.3-59-g8ed1b From ef205273132bdc9bcfa1540eef8105475a453300 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:05 +0530 Subject: media: i2c: ds90ub913: Fix returned fmt from .set_fmt() When setting the sink pad's stream format, set_fmt accidentally changes the returned format's code to 'outcode', while the purpose is to only use the 'outcode' for the propagated source stream format. Fixes: c158d0d4ff15 ("media: i2c: add DS90UB913 driver") Cc: stable@vger.kernel.org Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub913.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index fd2d2d5272bf..1445ebbcc9ca 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -450,10 +450,10 @@ static int ub913_set_fmt(struct v4l2_subdev *sd, if (!fmt) return -EINVAL; - format->format.code = finfo->outcode; - *fmt = format->format; + fmt->code = finfo->outcode; + return 0; } -- cgit v1.2.3-59-g8ed1b From 3e80dbb464eb8bb2f45c7c83ec379818d611c358 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:06 +0530 Subject: media: i2c: ds90ub913: Align ub913_read() with other similar functions Adjust the ub913_read() to have similar form than the other similar functions in ub9xx drivers. This makes it easier to deal with all the read/write functions with a semantic patch. Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub913.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index 1445ebbcc9ca..facbee79164e 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -125,14 +125,16 @@ static int ub913_read(const struct ub913_data *priv, u8 reg, u8 *val) int ret; ret = regmap_read(priv->regmap, reg, &v); - if (ret < 0) { + if (ret) { dev_err(&priv->client->dev, "Cannot read register 0x%02x: %d!\n", reg, ret); - return ret; + goto out; } *val = v; - return 0; + +out: + return ret; } static int ub913_write(const struct ub913_data *priv, u8 reg, u8 val) -- cgit v1.2.3-59-g8ed1b From 24868501a74402e8b48810dd7223bdc3d7508b3e Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:07 +0530 Subject: media: i2c: ds90ub9xx: Add err parameter to read/write funcs To make future error handling in the drivers easier, add "int *err" parameter to all the i2c register access functions. It functions the same was as with e.g. CCI reg write/read helpers. This was accomplished with the following semantic patch: @@ identifier FUNC =~ "^ub9.._(rxport_|txport_|ind_)?(read|write|update_bits)(16|_ind)?$"; @@ FUNC(... + , int *err ) { ... int ret; + + if (err && *err) + return *err; ... + if (ret && err) + *err = ret; + return ret; } @@ identifier FUNC =~ "^ub9.._(rxport_|txport_|ind_)?(read|write|update_bits)(16|_ind)?$"; @@ FUNC(... + , NULL ) Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub913.c | 52 +++-- drivers/media/i2c/ds90ub953.c | 94 +++++---- drivers/media/i2c/ds90ub960.c | 441 ++++++++++++++++++++++++++++-------------- 3 files changed, 390 insertions(+), 197 deletions(-) diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index facbee79164e..f38421d34d20 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -119,11 +119,15 @@ static const struct ub913_format_info *ub913_find_format(u32 incode) return NULL; } -static int ub913_read(const struct ub913_data *priv, u8 reg, u8 *val) +static int ub913_read(const struct ub913_data *priv, u8 reg, u8 *val, + int *err) { unsigned int v; int ret; + if (err && *err) + return *err; + ret = regmap_read(priv->regmap, reg, &v); if (ret) { dev_err(&priv->client->dev, @@ -134,31 +138,47 @@ static int ub913_read(const struct ub913_data *priv, u8 reg, u8 *val) *val = v; out: + if (ret && err) + *err = ret; + return ret; } -static int ub913_write(const struct ub913_data *priv, u8 reg, u8 val) +static int ub913_write(const struct ub913_data *priv, u8 reg, u8 val, + int *err) { int ret; + if (err && *err) + return *err; + ret = regmap_write(priv->regmap, reg, val); if (ret < 0) dev_err(&priv->client->dev, "Cannot write register 0x%02x: %d!\n", reg, ret); + if (ret && err) + *err = ret; + return ret; } static int ub913_update_bits(const struct ub913_data *priv, u8 reg, u8 mask, - u8 val) + u8 val, int *err) { int ret; + if (err && *err) + return *err; + ret = regmap_update_bits(priv->regmap, reg, mask, val); if (ret < 0) dev_err(&priv->client->dev, "Cannot update register 0x%02x %d!\n", reg, ret); + if (ret && err) + *err = ret; + return ret; } @@ -206,7 +226,7 @@ static int ub913_gpiochip_probe(struct ub913_data *priv) int ret; /* Initialize GPIOs 0 and 1 to local control, tri-state */ - ub913_write(priv, UB913_REG_GPIO_CFG(0), 0); + ub913_write(priv, UB913_REG_GPIO_CFG(0), 0, NULL); gc->label = dev_name(dev); gc->parent = dev; @@ -486,23 +506,23 @@ static int ub913_log_status(struct v4l2_subdev *sd) struct device *dev = &priv->client->dev; u8 v = 0, v1 = 0, v2 = 0; - ub913_read(priv, UB913_REG_MODE_SEL, &v); + ub913_read(priv, UB913_REG_MODE_SEL, &v, NULL); dev_info(dev, "MODE_SEL %#02x\n", v); - ub913_read(priv, UB913_REG_CRC_ERRORS_LSB, &v1); - ub913_read(priv, UB913_REG_CRC_ERRORS_MSB, &v2); + ub913_read(priv, UB913_REG_CRC_ERRORS_LSB, &v1, NULL); + ub913_read(priv, UB913_REG_CRC_ERRORS_MSB, &v2, NULL); dev_info(dev, "CRC errors %u\n", v1 | (v2 << 8)); /* clear CRC errors */ - ub913_read(priv, UB913_REG_GENERAL_CFG, &v); + ub913_read(priv, UB913_REG_GENERAL_CFG, &v, NULL); ub913_write(priv, UB913_REG_GENERAL_CFG, - v | UB913_REG_GENERAL_CFG_CRC_ERR_RESET); - ub913_write(priv, UB913_REG_GENERAL_CFG, v); + v | UB913_REG_GENERAL_CFG_CRC_ERR_RESET, NULL); + ub913_write(priv, UB913_REG_GENERAL_CFG, v, NULL); - ub913_read(priv, UB913_REG_GENERAL_STATUS, &v); + ub913_read(priv, UB913_REG_GENERAL_STATUS, &v, NULL); dev_info(dev, "GENERAL_STATUS %#02x\n", v); - ub913_read(priv, UB913_REG_PLL_OVR, &v); + ub913_read(priv, UB913_REG_PLL_OVR, &v, NULL); dev_info(dev, "PLL_OVR %#02x\n", v); return 0; @@ -658,11 +678,11 @@ static int ub913_i2c_master_init(struct ub913_data *priv) scl_high = div64_u64((u64)scl_high * ref, 1000000000); scl_low = div64_u64((u64)scl_low * ref, 1000000000); - ret = ub913_write(priv, UB913_REG_SCL_HIGH_TIME, scl_high); + ret = ub913_write(priv, UB913_REG_SCL_HIGH_TIME, scl_high, NULL); if (ret) return ret; - ret = ub913_write(priv, UB913_REG_SCL_LOW_TIME, scl_low); + ret = ub913_write(priv, UB913_REG_SCL_LOW_TIME, scl_low, NULL); if (ret) return ret; @@ -731,7 +751,7 @@ static int ub913_hw_init(struct ub913_data *priv) int ret; u8 v; - ret = ub913_read(priv, UB913_REG_MODE_SEL, &v); + ret = ub913_read(priv, UB913_REG_MODE_SEL, &v, NULL); if (ret) return ret; @@ -752,7 +772,7 @@ static int ub913_hw_init(struct ub913_data *priv) ret = ub913_update_bits(priv, UB913_REG_GENERAL_CFG, UB913_REG_GENERAL_CFG_PCLK_RISING, FIELD_PREP(UB913_REG_GENERAL_CFG_PCLK_RISING, - priv->pclk_polarity_rising)); + priv->pclk_polarity_rising), NULL); if (ret) return ret; diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index 7b33b8cc83c1..fbd977760c6b 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -185,11 +185,14 @@ static inline struct ub953_data *sd_to_ub953(struct v4l2_subdev *sd) * HW Access */ -static int ub953_read(struct ub953_data *priv, u8 reg, u8 *val) +static int ub953_read(struct ub953_data *priv, u8 reg, u8 *val, int *err) { unsigned int v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = regmap_read(priv->regmap, reg, &v); @@ -204,13 +207,19 @@ static int ub953_read(struct ub953_data *priv, u8 reg, u8 *val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } -static int ub953_write(struct ub953_data *priv, u8 reg, u8 val) +static int ub953_write(struct ub953_data *priv, u8 reg, u8 val, int *err) { int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = regmap_write(priv->regmap, reg, val); @@ -220,6 +229,9 @@ static int ub953_write(struct ub953_data *priv, u8 reg, u8 val) mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } @@ -244,11 +256,15 @@ static int ub953_select_ind_reg_block(struct ub953_data *priv, u8 block) } __maybe_unused -static int ub953_read_ind(struct ub953_data *priv, u8 block, u8 reg, u8 *val) +static int ub953_read_ind(struct ub953_data *priv, u8 block, u8 reg, u8 *val, + int *err) { unsigned int v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub953_select_ind_reg_block(priv, block); @@ -276,14 +292,21 @@ static int ub953_read_ind(struct ub953_data *priv, u8 block, u8 reg, u8 *val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } __maybe_unused -static int ub953_write_ind(struct ub953_data *priv, u8 block, u8 reg, u8 val) +static int ub953_write_ind(struct ub953_data *priv, u8 block, u8 reg, u8 val, + int *err) { int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub953_select_ind_reg_block(priv, block); @@ -308,6 +331,9 @@ static int ub953_write_ind(struct ub953_data *priv, u8 block, u8 reg, u8 val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } @@ -320,7 +346,7 @@ static int ub953_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) int ret; u8 v; - ret = ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &v); + ret = ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &v, NULL); if (ret) return ret; @@ -366,7 +392,7 @@ static int ub953_gpio_get(struct gpio_chip *gc, unsigned int offset) int ret; u8 v; - ret = ub953_read(priv, UB953_REG_GPIO_PIN_STS, &v); + ret = ub953_read(priv, UB953_REG_GPIO_PIN_STS, &v, NULL); if (ret) return ret; @@ -400,11 +426,11 @@ static int ub953_gpiochip_probe(struct ub953_data *priv) int ret; /* Set all GPIOs to local input mode */ - ret = ub953_write(priv, UB953_REG_LOCAL_GPIO_DATA, 0); + ret = ub953_write(priv, UB953_REG_LOCAL_GPIO_DATA, 0, NULL); if (ret) return ret; - ret = ub953_write(priv, UB953_REG_GPIO_INPUT_CTRL, 0xf); + ret = ub953_write(priv, UB953_REG_GPIO_INPUT_CTRL, 0xf, NULL); if (ret) return ret; @@ -615,15 +641,15 @@ static int ub953_log_status(struct v4l2_subdev *sd) u8 gpio_pin_sts = 0; for (i = 0; i < sizeof(id); i++) - ub953_read(priv, UB953_REG_FPD3_RX_ID(i), &id[i]); + ub953_read(priv, UB953_REG_FPD3_RX_ID(i), &id[i], NULL); dev_info(dev, "ID '%.*s'\n", (int)sizeof(id), id); - ub953_read(priv, UB953_REG_GENERAL_STATUS, &v); + ub953_read(priv, UB953_REG_GENERAL_STATUS, &v, NULL); dev_info(dev, "GENERAL_STATUS %#02x\n", v); - ub953_read(priv, UB953_REG_CRC_ERR_CNT1, &v1); - ub953_read(priv, UB953_REG_CRC_ERR_CNT2, &v2); + ub953_read(priv, UB953_REG_CRC_ERR_CNT1, &v1, NULL); + ub953_read(priv, UB953_REG_CRC_ERR_CNT2, &v2, NULL); dev_info(dev, "CRC error count %u\n", v1 | (v2 << 8)); /* Clear CRC error counter */ @@ -632,34 +658,34 @@ static int ub953_log_status(struct v4l2_subdev *sd) UB953_REG_BC_CTRL_CRC_ERR_CLR, UB953_REG_BC_CTRL_CRC_ERR_CLR); - ub953_read(priv, UB953_REG_CSI_ERR_CNT, &v); + ub953_read(priv, UB953_REG_CSI_ERR_CNT, &v, NULL); dev_info(dev, "CSI error count %u\n", v); - ub953_read(priv, UB953_REG_CSI_ERR_STATUS, &v); + ub953_read(priv, UB953_REG_CSI_ERR_STATUS, &v, NULL); dev_info(dev, "CSI_ERR_STATUS %#02x\n", v); - ub953_read(priv, UB953_REG_CSI_ERR_DLANE01, &v); + ub953_read(priv, UB953_REG_CSI_ERR_DLANE01, &v, NULL); dev_info(dev, "CSI_ERR_DLANE01 %#02x\n", v); - ub953_read(priv, UB953_REG_CSI_ERR_DLANE23, &v); + ub953_read(priv, UB953_REG_CSI_ERR_DLANE23, &v, NULL); dev_info(dev, "CSI_ERR_DLANE23 %#02x\n", v); - ub953_read(priv, UB953_REG_CSI_ERR_CLK_LANE, &v); + ub953_read(priv, UB953_REG_CSI_ERR_CLK_LANE, &v, NULL); dev_info(dev, "CSI_ERR_CLK_LANE %#02x\n", v); - ub953_read(priv, UB953_REG_CSI_PKT_HDR_VC_ID, &v); + ub953_read(priv, UB953_REG_CSI_PKT_HDR_VC_ID, &v, NULL); dev_info(dev, "CSI packet header VC %u ID %u\n", v >> 6, v & 0x3f); - ub953_read(priv, UB953_REG_PKT_HDR_WC_LSB, &v1); - ub953_read(priv, UB953_REG_PKT_HDR_WC_MSB, &v2); + ub953_read(priv, UB953_REG_PKT_HDR_WC_LSB, &v1, NULL); + ub953_read(priv, UB953_REG_PKT_HDR_WC_MSB, &v2, NULL); dev_info(dev, "CSI packet header WC %u\n", (v2 << 8) | v1); - ub953_read(priv, UB953_REG_CSI_ECC, &v); + ub953_read(priv, UB953_REG_CSI_ECC, &v, NULL); dev_info(dev, "CSI ECC %#02x\n", v); - ub953_read(priv, UB953_REG_LOCAL_GPIO_DATA, &gpio_local_data); - ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &gpio_input_ctrl); - ub953_read(priv, UB953_REG_GPIO_PIN_STS, &gpio_pin_sts); + ub953_read(priv, UB953_REG_LOCAL_GPIO_DATA, &gpio_local_data, NULL); + ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &gpio_input_ctrl, NULL); + ub953_read(priv, UB953_REG_GPIO_PIN_STS, &gpio_pin_sts, NULL); for (i = 0; i < UB953_NUM_GPIOS; i++) { dev_info(dev, @@ -843,11 +869,11 @@ static int ub953_i2c_master_init(struct ub953_data *priv) scl_high = div64_u64((u64)scl_high * ref, 1000000000) - 5; scl_low = div64_u64((u64)scl_low * ref, 1000000000) - 5; - ret = ub953_write(priv, UB953_REG_SCL_HIGH_TIME, scl_high); + ret = ub953_write(priv, UB953_REG_SCL_HIGH_TIME, scl_high, NULL); if (ret) return ret; - ret = ub953_write(priv, UB953_REG_SCL_LOW_TIME, scl_low); + ret = ub953_write(priv, UB953_REG_SCL_LOW_TIME, scl_low, NULL); if (ret) return ret; @@ -986,11 +1012,11 @@ static int ub953_write_clkout_regs(struct ub953_data *priv, clkout_ctrl1 = clkout_data->n; - ret = ub953_write(priv, UB953_REG_CLKOUT_CTRL0, clkout_ctrl0); + ret = ub953_write(priv, UB953_REG_CLKOUT_CTRL0, clkout_ctrl0, NULL); if (ret) return ret; - ret = ub953_write(priv, UB953_REG_CLKOUT_CTRL1, clkout_ctrl1); + ret = ub953_write(priv, UB953_REG_CLKOUT_CTRL1, clkout_ctrl1, NULL); if (ret) return ret; @@ -1009,13 +1035,13 @@ static unsigned long ub953_clkout_recalc_rate(struct clk_hw *hw, u64 rate; int ret; - ret = ub953_read(priv, UB953_REG_CLKOUT_CTRL0, &ctrl0); + ret = ub953_read(priv, UB953_REG_CLKOUT_CTRL0, &ctrl0, NULL); if (ret) { dev_err(dev, "Failed to read CLKOUT_CTRL0: %d\n", ret); return 0; } - ret = ub953_read(priv, UB953_REG_CLKOUT_CTRL1, &ctrl1); + ret = ub953_read(priv, UB953_REG_CLKOUT_CTRL1, &ctrl1, NULL); if (ret) { dev_err(dev, "Failed to read CLKOUT_CTRL1: %d\n", ret); return 0; @@ -1191,7 +1217,7 @@ static int ub953_hw_init(struct ub953_data *priv) int ret; u8 v; - ret = ub953_read(priv, UB953_REG_MODE_SEL, &v); + ret = ub953_read(priv, UB953_REG_MODE_SEL, &v, NULL); if (ret) return ret; @@ -1231,13 +1257,13 @@ static int ub953_hw_init(struct ub953_data *priv) return dev_err_probe(dev, -EINVAL, "clkin required for non-sync ext mode\n"); - ret = ub953_read(priv, UB953_REG_REV_MASK_ID, &v); + ret = ub953_read(priv, UB953_REG_REV_MASK_ID, &v, NULL); if (ret) return dev_err_probe(dev, ret, "Failed to read revision"); dev_info(dev, "Found %s rev/mask %#04x\n", priv->hw_data->model, v); - ret = ub953_read(priv, UB953_REG_GENERAL_CFG, &v); + ret = ub953_read(priv, UB953_REG_GENERAL_CFG, &v, NULL); if (ret) return ret; @@ -1254,7 +1280,7 @@ static int ub953_hw_init(struct ub953_data *priv) UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT; v |= UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE; - ret = ub953_write(priv, UB953_REG_GENERAL_CFG, v); + ret = ub953_write(priv, UB953_REG_GENERAL_CFG, v, NULL); if (ret) return ret; diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 5dde8452739b..09e6d820edc1 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -618,12 +618,15 @@ static const struct ub960_format_info *ub960_find_format(u32 code) * Basic device access */ -static int ub960_read(struct ub960_data *priv, u8 reg, u8 *val) +static int ub960_read(struct ub960_data *priv, u8 reg, u8 *val, int *err) { struct device *dev = &priv->client->dev; unsigned int v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = regmap_read(priv->regmap, reg, &v); @@ -638,14 +641,20 @@ static int ub960_read(struct ub960_data *priv, u8 reg, u8 *val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } -static int ub960_write(struct ub960_data *priv, u8 reg, u8 val) +static int ub960_write(struct ub960_data *priv, u8 reg, u8 val, int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = regmap_write(priv->regmap, reg, val); @@ -655,14 +664,21 @@ static int ub960_write(struct ub960_data *priv, u8 reg, u8 val) mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } -static int ub960_update_bits(struct ub960_data *priv, u8 reg, u8 mask, u8 val) +static int ub960_update_bits(struct ub960_data *priv, u8 reg, u8 mask, u8 val, + int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = regmap_update_bits(priv->regmap, reg, mask, val); @@ -672,15 +688,21 @@ static int ub960_update_bits(struct ub960_data *priv, u8 reg, u8 mask, u8 val) mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } -static int ub960_read16(struct ub960_data *priv, u8 reg, u16 *val) +static int ub960_read16(struct ub960_data *priv, u8 reg, u16 *val, int *err) { struct device *dev = &priv->client->dev; __be16 __v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = regmap_bulk_read(priv->regmap, reg, &__v, sizeof(__v)); @@ -695,6 +717,9 @@ static int ub960_read16(struct ub960_data *priv, u8 reg, u16 *val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } @@ -721,12 +746,16 @@ static int ub960_rxport_select(struct ub960_data *priv, u8 nport) return 0; } -static int ub960_rxport_read(struct ub960_data *priv, u8 nport, u8 reg, u8 *val) +static int ub960_rxport_read(struct ub960_data *priv, u8 nport, u8 reg, + u8 *val, int *err) { struct device *dev = &priv->client->dev; unsigned int v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_rxport_select(priv, nport); @@ -745,14 +774,21 @@ static int ub960_rxport_read(struct ub960_data *priv, u8 nport, u8 reg, u8 *val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } -static int ub960_rxport_write(struct ub960_data *priv, u8 nport, u8 reg, u8 val) +static int ub960_rxport_write(struct ub960_data *priv, u8 nport, u8 reg, + u8 val, int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_rxport_select(priv, nport); @@ -767,15 +803,21 @@ static int ub960_rxport_write(struct ub960_data *priv, u8 nport, u8 reg, u8 val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } static int ub960_rxport_update_bits(struct ub960_data *priv, u8 nport, u8 reg, - u8 mask, u8 val) + u8 mask, u8 val, int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_rxport_select(priv, nport); @@ -790,16 +832,22 @@ static int ub960_rxport_update_bits(struct ub960_data *priv, u8 nport, u8 reg, out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } static int ub960_rxport_read16(struct ub960_data *priv, u8 nport, u8 reg, - u16 *val) + u16 *val, int *err) { struct device *dev = &priv->client->dev; __be16 __v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_rxport_select(priv, nport); @@ -818,6 +866,9 @@ static int ub960_rxport_read16(struct ub960_data *priv, u8 nport, u8 reg, out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } @@ -844,12 +895,16 @@ static int ub960_txport_select(struct ub960_data *priv, u8 nport) return 0; } -static int ub960_txport_read(struct ub960_data *priv, u8 nport, u8 reg, u8 *val) +static int ub960_txport_read(struct ub960_data *priv, u8 nport, u8 reg, + u8 *val, int *err) { struct device *dev = &priv->client->dev; unsigned int v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_txport_select(priv, nport); @@ -868,14 +923,21 @@ static int ub960_txport_read(struct ub960_data *priv, u8 nport, u8 reg, u8 *val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } -static int ub960_txport_write(struct ub960_data *priv, u8 nport, u8 reg, u8 val) +static int ub960_txport_write(struct ub960_data *priv, u8 nport, u8 reg, + u8 val, int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_txport_select(priv, nport); @@ -890,15 +952,21 @@ static int ub960_txport_write(struct ub960_data *priv, u8 nport, u8 reg, u8 val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } static int ub960_txport_update_bits(struct ub960_data *priv, u8 nport, u8 reg, - u8 mask, u8 val) + u8 mask, u8 val, int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_txport_select(priv, nport); @@ -913,6 +981,9 @@ static int ub960_txport_update_bits(struct ub960_data *priv, u8 nport, u8 reg, out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } @@ -938,12 +1009,16 @@ static int ub960_select_ind_reg_block(struct ub960_data *priv, u8 block) return 0; } -static int ub960_read_ind(struct ub960_data *priv, u8 block, u8 reg, u8 *val) +static int ub960_read_ind(struct ub960_data *priv, u8 block, u8 reg, u8 *val, + int *err) { struct device *dev = &priv->client->dev; unsigned int v; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_select_ind_reg_block(priv, block); @@ -971,14 +1046,21 @@ static int ub960_read_ind(struct ub960_data *priv, u8 block, u8 reg, u8 *val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } -static int ub960_write_ind(struct ub960_data *priv, u8 block, u8 reg, u8 val) +static int ub960_write_ind(struct ub960_data *priv, u8 block, u8 reg, u8 val, + int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_select_ind_reg_block(priv, block); @@ -1004,15 +1086,21 @@ static int ub960_write_ind(struct ub960_data *priv, u8 block, u8 reg, u8 val) out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } static int ub960_ind_update_bits(struct ub960_data *priv, u8 block, u8 reg, - u8 mask, u8 val) + u8 mask, u8 val, int *err) { struct device *dev = &priv->client->dev; int ret; + if (err && *err) + return *err; + mutex_lock(&priv->reg_lock); ret = ub960_select_ind_reg_block(priv, block); @@ -1039,6 +1127,9 @@ static int ub960_ind_update_bits(struct ub960_data *priv, u8 block, u8 reg, out_unlock: mutex_unlock(&priv->reg_lock); + if (ret && err) + *err = ret; + return ret; } @@ -1067,9 +1158,9 @@ static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id, rxport->aliased_clients[reg_idx] = client; ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ID(reg_idx), - client->addr << 1); + client->addr << 1, NULL); ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), - alias << 1); + alias << 1, NULL); dev_dbg(dev, "rx%u: client 0x%02x assigned alias 0x%02x at slot %u\n", rxport->nport, client->addr, alias, reg_idx); @@ -1098,7 +1189,8 @@ static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id, rxport->aliased_clients[reg_idx] = NULL; - ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), 0); + ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), 0, + NULL); dev_dbg(dev, "rx%u: client 0x%02x released at slot %u\n", rxport->nport, client->addr, reg_idx); @@ -1199,7 +1291,8 @@ static void ub960_csi_handle_events(struct ub960_data *priv, u8 nport) u8 csi_tx_isr; int ret; - ret = ub960_txport_read(priv, nport, UB960_TR_CSI_TX_ISR, &csi_tx_isr); + ret = ub960_txport_read(priv, nport, UB960_TR_CSI_TX_ISR, &csi_tx_isr, + NULL); if (ret) return; @@ -1264,15 +1357,15 @@ static void ub960_rxport_clear_errors(struct ub960_data *priv, { u8 v; - ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v); - ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v); - ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &v); - ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &v); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v, NULL); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v, NULL); + ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &v, NULL); + ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &v, NULL); - ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v); - ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_LO, &v); + ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v, NULL); + ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_LO, &v, NULL); - ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v); + ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v, NULL); } static void ub960_clear_rx_errors(struct ub960_data *priv) @@ -1291,24 +1384,24 @@ static int ub960_rxport_get_strobe_pos(struct ub960_data *priv, int ret; ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_CLK, &v); + UB960_IR_RX_ANA_STROBE_SET_CLK, &v, NULL); clk_delay = (v & UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY) ? 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_DATA, &v); + UB960_IR_RX_ANA_STROBE_SET_DATA, &v, NULL); data_delay = (v & UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) ? 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; - ret = ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_0, &v); + ret = ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_0, &v, NULL); if (ret) return ret; clk_delay += v & UB960_IR_RX_ANA_STROBE_SET_CLK_DELAY_MASK; - ret = ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_1, &v); + ret = ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_1, &v, NULL); if (ret) return ret; @@ -1337,10 +1430,10 @@ static void ub960_rxport_set_strobe_pos(struct ub960_data *priv, data_delay = strobe_pos | UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY; ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay); + UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay, NULL); ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_DATA, data_delay); + UB960_IR_RX_ANA_STROBE_SET_DATA, data_delay, NULL); } static void ub960_rxport_set_strobe_range(struct ub960_data *priv, @@ -1352,7 +1445,8 @@ static void ub960_rxport_set_strobe_range(struct ub960_data *priv, ub960_write(priv, UB960_XR_SFILTER_CFG, ((u8)strobe_min << UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) | - ((u8)strobe_max << UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT)); + ((u8)strobe_max << UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT), + NULL); } static int ub960_rxport_get_eq_level(struct ub960_data *priv, @@ -1361,7 +1455,7 @@ static int ub960_rxport_get_eq_level(struct ub960_data *priv, int ret; u8 v; - ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_STATUS, &v); + ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_STATUS, &v, NULL); if (ret) return ret; @@ -1386,7 +1480,7 @@ static void ub960_rxport_set_eq_level(struct ub960_data *priv, eq_stage_2_select_value = eq_level - eq_stage_max; } - ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v); + ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v, NULL); v &= ~(UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_MASK | UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_MASK); @@ -1394,7 +1488,7 @@ static void ub960_rxport_set_eq_level(struct ub960_data *priv, v |= eq_stage_2_select_value << UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_SHIFT; v |= UB960_RR_AEQ_BYPASS_ENABLE; - ub960_rxport_write(priv, nport, UB960_RR_AEQ_BYPASS, v); + ub960_rxport_write(priv, nport, UB960_RR_AEQ_BYPASS, v, NULL); } static void ub960_rxport_set_eq_range(struct ub960_data *priv, @@ -1402,12 +1496,13 @@ static void ub960_rxport_set_eq_range(struct ub960_data *priv, { ub960_rxport_write(priv, nport, UB960_RR_AEQ_MIN_MAX, (eq_min << UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) | - (eq_max << UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT)); + (eq_max << UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT), + NULL); /* Enable AEQ min setting */ ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_CTL2, UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR, - UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR); + UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR, NULL); } static void ub960_rxport_config_eq(struct ub960_data *priv, unsigned int nport) @@ -1419,12 +1514,12 @@ static void ub960_rxport_config_eq(struct ub960_data *priv, unsigned int nport) if (priv->strobe.manual) { /* Disable AEQ_SFILTER_EN */ ub960_update_bits(priv, UB960_XR_AEQ_CTL1, - UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN, 0); + UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN, 0, NULL); } else { /* Enable SFILTER and error control */ ub960_write(priv, UB960_XR_AEQ_CTL1, UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_MASK | - UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN); + UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN, NULL); /* Set AEQ strobe range */ ub960_rxport_set_strobe_range(priv, priv->strobe.min, @@ -1445,7 +1540,7 @@ static void ub960_rxport_config_eq(struct ub960_data *priv, unsigned int nport) /* Enable AEQ Bypass */ ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS, UB960_RR_AEQ_BYPASS_ENABLE, - UB960_RR_AEQ_BYPASS_ENABLE); + UB960_RR_AEQ_BYPASS_ENABLE, NULL); } else { ub960_rxport_set_eq_range(priv, nport, rxport->eq.aeq.eq_level_min, @@ -1453,7 +1548,7 @@ static void ub960_rxport_config_eq(struct ub960_data *priv, unsigned int nport) /* Disable AEQ Bypass */ ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS, - UB960_RR_AEQ_BYPASS_ENABLE, 0); + UB960_RR_AEQ_BYPASS_ENABLE, 0, NULL); } } @@ -1469,7 +1564,7 @@ static int ub960_rxport_link_ok(struct ub960_data *priv, unsigned int nport, bool errors; ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, - &rx_port_sts1); + &rx_port_sts1, NULL); if (ret) return ret; @@ -1479,25 +1574,27 @@ static int ub960_rxport_link_ok(struct ub960_data *priv, unsigned int nport, } ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, - &rx_port_sts2); + &rx_port_sts2, NULL); if (ret) return ret; - ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &csi_rx_sts); + ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &csi_rx_sts, + NULL); if (ret) return ret; ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, - &csi_err_cnt); + &csi_err_cnt, NULL); if (ret) return ret; - ret = ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &bcc_sts); + ret = ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &bcc_sts, + NULL); if (ret) return ret; ret = ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, - &parity_errors); + &parity_errors, NULL); if (ret) return ret; @@ -1600,7 +1697,8 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv, continue; } - ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v); + ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v, + NULL); if (priv->hw_data->is_ub9702) { dev_dbg(dev, "\trx%u: locked, freq %llu Hz\n", @@ -1787,7 +1885,7 @@ static void ub960_init_tx_port(struct ub960_data *priv, if (!txport->non_continous_clk) csi_ctl |= UB960_TR_CSI_CTL_CSI_CONTS_CLOCK; - ub960_txport_write(priv, nport, UB960_TR_CSI_CTL, csi_ctl); + ub960_txport_write(priv, nport, UB960_TR_CSI_CTL, csi_ctl, NULL); } static int ub960_init_tx_ports(struct ub960_data *priv) @@ -1818,24 +1916,30 @@ static int ub960_init_tx_ports(struct ub960_data *priv) break; } - ub960_write(priv, UB960_SR_CSI_PLL_CTL, speed_select); + ub960_write(priv, UB960_SR_CSI_PLL_CTL, speed_select, NULL); if (priv->hw_data->is_ub9702) { - ub960_write(priv, UB960_SR_CSI_PLL_DIV, pll_div); + ub960_write(priv, UB960_SR_CSI_PLL_DIV, pll_div, NULL); switch (priv->tx_data_rate) { case MHZ(1600): default: - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0x80); - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, 0x2a); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, + 0x80, NULL); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, + 0x2a, NULL); break; case MHZ(800): - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0x90); - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4f, 0x2a); - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, 0x2a); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, + 0x90, NULL); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4f, + 0x2a, NULL); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, + 0x2a, NULL); break; case MHZ(400): - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0xa0); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, + 0xa0, NULL); break; } } @@ -1890,21 +1994,22 @@ static void ub960_init_rx_port_ub960(struct ub960_data *priv, ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK, - bc_freq_val); + bc_freq_val, NULL); switch (rxport->rx_mode) { case RXPORT_MODE_RAW10: /* FPD3_MODE = RAW10 Mode (DS90UB913A-Q1 / DS90UB933-Q1 compatible) */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG, UB960_RR_PORT_CONFIG_FPD3_MODE_MASK, - 0x3); + 0x3, NULL); /* * RAW10_8BIT_CTL = 0b10 : 8-bit processing using upper 8 bits */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_MASK, - 0x2 << UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_SHIFT); + 0x2 << UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_SHIFT, + NULL); break; @@ -1917,33 +2022,34 @@ static void ub960_init_rx_port_ub960(struct ub960_data *priv, case RXPORT_MODE_CSI2_NONSYNC: /* CSI-2 Mode (DS90UB953-Q1 compatible) */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG, 0x3, - 0x0); + 0x0, NULL); break; } /* LV_POLARITY & FV_POLARITY */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, - rxport->lv_fv_pol); + rxport->lv_fv_pol, NULL); /* Enable all interrupt sources from this port */ - ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07); - ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07, NULL); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f, NULL); /* Enable I2C_PASS_THROUGH */ ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, - UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH); + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, NULL); /* Enable I2C communication to the serializer via the alias addr */ ub960_rxport_write(priv, nport, UB960_RR_SER_ALIAS_ID, - rxport->ser.alias << 1); + rxport->ser.alias << 1, NULL); /* Configure EQ related settings */ ub960_rxport_config_eq(priv, nport); /* Enable RX port */ - ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport)); + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport), + NULL); } static void ub960_init_rx_port_ub9702_fpd3(struct ub960_data *priv, @@ -1984,31 +2090,36 @@ static void ub960_init_rx_port_ub9702_fpd3(struct ub960_data *priv, } ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, 0x7, - bc_freq_val); - ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, fpd_func_mode); + bc_freq_val, NULL); + ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, fpd_func_mode, + NULL); /* set serdes_eq_mode = 1 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xa8, 0x80); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xa8, 0x80, + NULL); /* enable serdes driver */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x0d, 0x7f); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x0d, 0x7f, + NULL); /* set serdes_eq_offset=4 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04, + NULL); /* init default serdes_eq_max in 0xa9 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xa9, 0x23); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xa9, 0x23, + NULL); /* init serdes_eq_min in 0xaa */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xaa, 0); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xaa, 0, NULL); /* serdes_driver_ctl2 control: DS90UB953-Q1/DS90UB933-Q1/DS90UB913A-Q1 */ ub960_ind_update_bits(priv, UB960_IND_TARGET_RX_ANA(nport), 0x1b, - BIT(3), BIT(3)); + BIT(3), BIT(3), NULL); /* RX port to half-rate */ ub960_update_bits(priv, UB960_SR_FPD_RATE_CFG, 0x3 << (nport * 2), - BIT(nport * 2)); + BIT(nport * 2), NULL); } static void ub960_init_rx_port_ub9702_fpd4_aeq(struct ub960_data *priv, @@ -2021,28 +2132,37 @@ static void ub960_init_rx_port_ub9702_fpd4_aeq(struct ub960_data *priv, u8 v; /* AEQ init */ - ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2c, &v); + ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2c, &v, + NULL); - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, v); - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x28, v + 1); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, v, + NULL); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x28, + v + 1, NULL); - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x00); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, + 0x00, NULL); } /* enable serdes_eq_ctl2 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x9e, 0x00); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x9e, 0x00, + NULL); /* enable serdes_eq_ctl1 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x90, 0x40); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x90, 0x40, + NULL); /* enable serdes_eq_en */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2e, 0x40); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2e, 0x40, + NULL); /* disable serdes_eq_override */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xf0, 0x00); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xf0, 0x00, + NULL); /* disable serdes_gain_override */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x71, 0x00); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x71, 0x00, + NULL); } static void ub960_init_rx_port_ub9702_fpd4(struct ub960_data *priv, @@ -2077,32 +2197,38 @@ static void ub960_init_rx_port_ub9702_fpd4(struct ub960_data *priv, } ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, 0x7, - bc_freq_val); + bc_freq_val, NULL); /* FPD4 Sync Mode */ - ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, 0); + ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, 0, NULL); /* add serdes_eq_offset of 4 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04, + NULL); /* FPD4 serdes_start_eq in 0x27: assign default */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, 0x0); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, 0x0, NULL); /* FPD4 serdes_end_eq in 0x28: assign default */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x28, 0x23); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x28, 0x23, + NULL); /* set serdes_driver_mode into FPD IV mode */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x04, 0x00); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x04, 0x00, + NULL); /* set FPD PBC drv into FPD IV mode */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x1b, 0x00); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x1b, 0x00, + NULL); /* set serdes_system_init to 0x2f */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x21, 0x2f); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x21, 0x2f, + NULL); /* set serdes_system_rst in reset mode */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x25, 0xc1); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x25, 0xc1, + NULL); /* RX port to 7.55G mode */ ub960_update_bits(priv, UB960_SR_FPD_RATE_CFG, 0x3 << (nport * 2), - 0 << (nport * 2)); + 0 << (nport * 2), NULL); ub960_init_rx_port_ub9702_fpd4_aeq(priv, rxport); } @@ -2124,7 +2250,7 @@ static void ub960_init_rx_port_ub9702(struct ub960_data *priv, * 0b10 : 8-bit processing using upper 8 bits */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, - 0x3 << 6, 0x2 << 6); + 0x3 << 6, 0x2 << 6, NULL); break; @@ -2141,27 +2267,29 @@ static void ub960_init_rx_port_ub9702(struct ub960_data *priv, /* LV_POLARITY & FV_POLARITY */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, - rxport->lv_fv_pol); + rxport->lv_fv_pol, NULL); /* Enable all interrupt sources from this port */ - ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07); - ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07, NULL); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f, NULL); /* Enable I2C_PASS_THROUGH */ ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, - UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH); + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, NULL); /* Enable I2C communication to the serializer via the alias addr */ ub960_rxport_write(priv, nport, UB960_RR_SER_ALIAS_ID, - rxport->ser.alias << 1); + rxport->ser.alias << 1, NULL); /* Enable RX port */ - ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport)); + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport), + NULL); if (rxport->cdr_mode == RXPORT_CDR_FPD4) { /* unreset 960 AEQ */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x25, 0x41); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x25, + 0x41, NULL); } } @@ -2196,16 +2324,16 @@ static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) /* Read interrupts (also clears most of them) */ if (!ret) ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, - &rx_port_sts1); + &rx_port_sts1, NULL); if (!ret) ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, - &rx_port_sts2); + &rx_port_sts2, NULL); if (!ret) ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, - &csi_rx_sts); + &csi_rx_sts, NULL); if (!ret) ret = ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, - &bcc_sts); + &bcc_sts, NULL); if (ret) return; @@ -2214,7 +2342,7 @@ static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) u16 v; ret = ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, - &v); + &v, NULL); if (!ret) dev_err(dev, "rx%u parity errors: %u\n", nport, v); } @@ -2273,7 +2401,8 @@ static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) if (rx_port_sts2 & UB960_RR_RX_PORT_STS2_LINE_LEN_CHG) { u16 v; - ret = ub960_rxport_read16(priv, nport, UB960_RR_LINE_LEN_1, &v); + ret = ub960_rxport_read16(priv, nport, UB960_RR_LINE_LEN_1, + &v, NULL); if (!ret) dev_dbg(dev, "rx%u line len changed: %u\n", nport, v); } @@ -2282,7 +2411,7 @@ static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) u16 v; ret = ub960_rxport_read16(priv, nport, UB960_RR_LINE_COUNT_HI, - &v); + &v, NULL); if (!ret) dev_dbg(dev, "rx%u line count changed: %u\n", nport, v); } @@ -2354,7 +2483,7 @@ static int ub960_enable_tx_port(struct ub960_data *priv, unsigned int nport) return ub960_txport_update_bits(priv, nport, UB960_TR_CSI_CTL, UB960_TR_CSI_CTL_CSI_ENABLE, - UB960_TR_CSI_CTL_CSI_ENABLE); + UB960_TR_CSI_CTL_CSI_ENABLE, NULL); } static void ub960_disable_tx_port(struct ub960_data *priv, unsigned int nport) @@ -2364,7 +2493,7 @@ static void ub960_disable_tx_port(struct ub960_data *priv, unsigned int nport) dev_dbg(dev, "disable TX port %u\n", nport); ub960_txport_update_bits(priv, nport, UB960_TR_CSI_CTL, - UB960_TR_CSI_CTL_CSI_ENABLE, 0); + UB960_TR_CSI_CTL_CSI_ENABLE, 0, NULL); } static int ub960_enable_rx_port(struct ub960_data *priv, unsigned int nport) @@ -2375,7 +2504,7 @@ static int ub960_enable_rx_port(struct ub960_data *priv, unsigned int nport) /* Enable forwarding */ return ub960_update_bits(priv, UB960_SR_FWD_CTL1, - UB960_SR_FWD_CTL1_PORT_DIS(nport), 0); + UB960_SR_FWD_CTL1_PORT_DIS(nport), 0, NULL); } static void ub960_disable_rx_port(struct ub960_data *priv, unsigned int nport) @@ -2387,7 +2516,7 @@ static void ub960_disable_rx_port(struct ub960_data *priv, unsigned int nport) /* Disable forwarding */ ub960_update_bits(priv, UB960_SR_FWD_CTL1, UB960_SR_FWD_CTL1_PORT_DIS(nport), - UB960_SR_FWD_CTL1_PORT_DIS(nport)); + UB960_SR_FWD_CTL1_PORT_DIS(nport), NULL); } /* @@ -2528,12 +2657,13 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, switch (rxport->rx_mode) { case RXPORT_MODE_RAW10: ub960_rxport_write(priv, nport, UB960_RR_RAW10_ID, - rx_data[nport].pixel_dt | (vc << UB960_RR_RAW10_ID_VC_SHIFT)); + rx_data[nport].pixel_dt | (vc << UB960_RR_RAW10_ID_VC_SHIFT), + NULL); ub960_rxport_write(priv, rxport->nport, UB960_RR_RAW_EMBED_DTYPE, (rx_data[nport].meta_lines << UB960_RR_RAW_EMBED_DTYPE_LINES_SHIFT) | - rx_data[nport].meta_dt); + rx_data[nport].meta_dt, NULL); break; @@ -2550,7 +2680,8 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, (vc << UB960_RR_CSI_VC_MAP_SHIFT(3)) | (vc << UB960_RR_CSI_VC_MAP_SHIFT(2)) | (vc << UB960_RR_CSI_VC_MAP_SHIFT(1)) | - (vc << UB960_RR_CSI_VC_MAP_SHIFT(0))); + (vc << UB960_RR_CSI_VC_MAP_SHIFT(0)), + NULL); } else { unsigned int i; @@ -2558,7 +2689,8 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, for (i = 0; i < 8; i++) ub960_rxport_write(priv, nport, UB960_RR_VC_ID_MAP(i), - (nport << 4) | nport); + (nport << 4) | nport, + NULL); } break; @@ -2570,7 +2702,7 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, fwd_ctl &= ~BIT(nport); /* forward to TX0 */ } - ub960_write(priv, UB960_SR_FWD_CTL1, fwd_ctl); + ub960_write(priv, UB960_SR_FWD_CTL1, fwd_ctl, NULL); return 0; } @@ -2986,7 +3118,7 @@ static void ub960_log_status_ub960_sp_eq(struct ub960_data *priv, /* Strobe */ - ret = ub960_read(priv, UB960_XR_AEQ_CTL1, &v); + ret = ub960_read(priv, UB960_XR_AEQ_CTL1, &v, NULL); if (ret) return; @@ -2995,7 +3127,7 @@ static void ub960_log_status_ub960_sp_eq(struct ub960_data *priv, "Manual"); if (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) { - ret = ub960_read(priv, UB960_XR_SFILTER_CFG, &v); + ret = ub960_read(priv, UB960_XR_SFILTER_CFG, &v, NULL); if (ret) return; @@ -3012,7 +3144,7 @@ static void ub960_log_status_ub960_sp_eq(struct ub960_data *priv, /* EQ */ - ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v); + ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v, NULL); if (ret) return; @@ -3021,7 +3153,8 @@ static void ub960_log_status_ub960_sp_eq(struct ub960_data *priv, "Adaptive"); if (!(v & UB960_RR_AEQ_BYPASS_ENABLE)) { - ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_MIN_MAX, &v); + ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_MIN_MAX, &v, + NULL); if (ret) return; @@ -3047,7 +3180,7 @@ static int ub960_log_status(struct v4l2_subdev *sd) state = v4l2_subdev_lock_and_get_active_state(sd); for (unsigned int i = 0; i < sizeof(id); i++) - ub960_read(priv, UB960_SR_FPD3_RX_ID(i), &id[i]); + ub960_read(priv, UB960_SR_FPD3_RX_ID(i), &id[i], NULL); dev_info(dev, "ID '%.*s'\n", (int)sizeof(id), id); @@ -3061,20 +3194,24 @@ static int ub960_log_status(struct v4l2_subdev *sd) continue; } - ub960_txport_read(priv, nport, UB960_TR_CSI_STS, &v); + ub960_txport_read(priv, nport, UB960_TR_CSI_STS, &v, NULL); dev_info(dev, "\tsync %u, pass %u\n", v & (u8)BIT(1), v & (u8)BIT(0)); - ub960_read16(priv, UB960_SR_CSI_FRAME_COUNT_HI(nport), &v16); + ub960_read16(priv, UB960_SR_CSI_FRAME_COUNT_HI(nport), &v16, + NULL); dev_info(dev, "\tframe counter %u\n", v16); - ub960_read16(priv, UB960_SR_CSI_FRAME_ERR_COUNT_HI(nport), &v16); + ub960_read16(priv, UB960_SR_CSI_FRAME_ERR_COUNT_HI(nport), + &v16, NULL); dev_info(dev, "\tframe error counter %u\n", v16); - ub960_read16(priv, UB960_SR_CSI_LINE_COUNT_HI(nport), &v16); + ub960_read16(priv, UB960_SR_CSI_LINE_COUNT_HI(nport), &v16, + NULL); dev_info(dev, "\tline counter %u\n", v16); - ub960_read16(priv, UB960_SR_CSI_LINE_ERR_COUNT_HI(nport), &v16); + ub960_read16(priv, UB960_SR_CSI_LINE_ERR_COUNT_HI(nport), + &v16, NULL); dev_info(dev, "\tline error counter %u\n", v16); } @@ -3088,7 +3225,8 @@ static int ub960_log_status(struct v4l2_subdev *sd) continue; } - ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v, + NULL); if (v & UB960_RR_RX_PORT_STS1_LOCK_STS) dev_info(dev, "\tLocked\n"); @@ -3096,22 +3234,28 @@ static int ub960_log_status(struct v4l2_subdev *sd) dev_info(dev, "\tNot locked\n"); dev_info(dev, "\trx_port_sts1 %#02x\n", v); - ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v, + NULL); dev_info(dev, "\trx_port_sts2 %#02x\n", v); - ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v16); + ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v16, + NULL); dev_info(dev, "\tlink freq %llu Hz\n", ((u64)v16 * HZ_PER_MHZ) >> 8); - ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v16); + ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v16, + NULL); dev_info(dev, "\tparity errors %u\n", v16); - ub960_rxport_read16(priv, nport, UB960_RR_LINE_COUNT_HI, &v16); + ub960_rxport_read16(priv, nport, UB960_RR_LINE_COUNT_HI, &v16, + NULL); dev_info(dev, "\tlines per frame %u\n", v16); - ub960_rxport_read16(priv, nport, UB960_RR_LINE_LEN_1, &v16); + ub960_rxport_read16(priv, nport, UB960_RR_LINE_LEN_1, &v16, + NULL); dev_info(dev, "\tbytes per line %u\n", v16); - ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v); + ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v, + NULL); dev_info(dev, "\tcsi_err_counter %u\n", v); if (!priv->hw_data->is_ub9702) @@ -3125,7 +3269,7 @@ static int ub960_log_status(struct v4l2_subdev *sd) ctl_reg = UB960_RR_BC_GPIO_CTL(i / 2); ctl_shift = (i % 2) * 4; - ub960_rxport_read(priv, nport, ctl_reg, &v); + ub960_rxport_read(priv, nport, ctl_reg, &v, NULL); dev_info(dev, "\tGPIO%u: mode %u\n", i, (v >> ctl_shift) & 0xf); @@ -3168,13 +3312,13 @@ static irqreturn_t ub960_handle_events(int irq, void *arg) u8 fwd_sts; int ret; - ret = ub960_read(priv, UB960_SR_INTERRUPT_STS, &int_sts); + ret = ub960_read(priv, UB960_SR_INTERRUPT_STS, &int_sts, NULL); if (ret || !int_sts) return IRQ_NONE; dev_dbg(&priv->client->dev, "INTERRUPT_STS %x\n", int_sts); - ret = ub960_read(priv, UB960_SR_FWD_STS, &fwd_sts); + ret = ub960_read(priv, UB960_SR_FWD_STS, &fwd_sts, NULL); if (ret) return IRQ_NONE; @@ -3804,7 +3948,7 @@ static void ub960_reset(struct ub960_data *priv, bool reset_regs) bit = reset_regs ? UB960_SR_RESET_DIGITAL_RESET1 : UB960_SR_RESET_DIGITAL_RESET0; - ub960_write(priv, UB960_SR_RESET, bit); + ub960_write(priv, UB960_SR_RESET, bit, NULL); mutex_lock(&priv->reg_lock); @@ -3876,7 +4020,7 @@ static int ub960_enable_core_hw(struct ub960_data *priv) ub960_reset(priv, true); /* Runtime check register accessibility */ - ret = ub960_read(priv, UB960_SR_REV_MASK, &rev_mask); + ret = ub960_read(priv, UB960_SR_REV_MASK, &rev_mask, NULL); if (ret) { dev_err_probe(dev, ret, "Cannot read first register, abort\n"); goto err_pd_gpio; @@ -3885,14 +4029,16 @@ static int ub960_enable_core_hw(struct ub960_data *priv) dev_dbg(dev, "Found %s (rev/mask %#04x)\n", priv->hw_data->model, rev_mask); - ret = ub960_read(priv, UB960_SR_DEVICE_STS, &dev_sts); + ret = ub960_read(priv, UB960_SR_DEVICE_STS, &dev_sts, NULL); if (ret) goto err_pd_gpio; if (priv->hw_data->is_ub9702) - ret = ub960_read(priv, UB9702_SR_REFCLK_FREQ, &refclk_freq); + ret = ub960_read(priv, UB9702_SR_REFCLK_FREQ, &refclk_freq, + NULL); else - ret = ub960_read(priv, UB960_XR_REFCLK_FREQ, &refclk_freq); + ret = ub960_read(priv, UB960_XR_REFCLK_FREQ, &refclk_freq, + NULL); if (ret) goto err_pd_gpio; @@ -3901,7 +4047,7 @@ static int ub960_enable_core_hw(struct ub960_data *priv) clk_get_rate(priv->refclk) / HZ_PER_MHZ); /* Disable all RX ports by default */ - ret = ub960_write(priv, UB960_SR_RX_PORT_CTL, 0); + ret = ub960_write(priv, UB960_SR_RX_PORT_CTL, 0, NULL); if (ret) goto err_pd_gpio; @@ -3909,7 +4055,8 @@ static int ub960_enable_core_hw(struct ub960_data *priv) if (priv->hw_data->is_ub9702) { ret = ub960_update_bits(priv, UB960_SR_RESET, UB960_SR_RESET_GPIO_LOCK_RELEASE, - UB960_SR_RESET_GPIO_LOCK_RELEASE); + UB960_SR_RESET_GPIO_LOCK_RELEASE, + NULL); if (ret) goto err_pd_gpio; } @@ -4035,7 +4182,7 @@ static int ub960_probe(struct i2c_client *client) #ifdef UB960_DEBUG_I2C_RX_ID for (unsigned int i = 0; i < priv->hw_data->num_rxports; i++) ub960_write(priv, UB960_SR_I2C_RX_ID(i), - (UB960_DEBUG_I2C_RX_ID + i) << 1); + (UB960_DEBUG_I2C_RX_ID + i) << 1, NULL); #endif return 0; -- cgit v1.2.3-59-g8ed1b From 8f512c3113756c751811392bc48e146b6a0ba2fd Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:08 +0530 Subject: media: i2c: ds90ub960: Add error handling to multiple places The driver is missing checks for i2c read/write errors in many places. Now that we have added the err parameter to the read/write functions in the previous patch, add error handling to all the missing places. Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub960.c | 589 +++++++++++++++++++++++++++--------------- 1 file changed, 376 insertions(+), 213 deletions(-) diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 09e6d820edc1..086aa8cc78fa 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -1144,6 +1144,7 @@ static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id, struct ub960_rxport *rxport = priv->rxports[chan_id]; struct device *dev = &priv->client->dev; unsigned int reg_idx; + int ret = 0; for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_clients); reg_idx++) { if (!rxport->aliased_clients[reg_idx]) @@ -1158,9 +1159,12 @@ static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id, rxport->aliased_clients[reg_idx] = client; ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ID(reg_idx), - client->addr << 1, NULL); + client->addr << 1, &ret); ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), - alias << 1, NULL); + alias << 1, &ret); + + if (ret) + return ret; dev_dbg(dev, "rx%u: client 0x%02x assigned alias 0x%02x at slot %u\n", rxport->nport, client->addr, alias, reg_idx); @@ -1175,6 +1179,7 @@ static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id, struct ub960_rxport *rxport = priv->rxports[chan_id]; struct device *dev = &priv->client->dev; unsigned int reg_idx; + int ret; for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_clients); reg_idx++) { if (rxport->aliased_clients[reg_idx] == client) @@ -1189,8 +1194,13 @@ static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id, rxport->aliased_clients[reg_idx] = NULL; - ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), 0, - NULL); + ret = ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), + 0, NULL); + if (ret) { + dev_err(dev, "rx%u: unable to fully unmap client 0x%02x: %d\n", + rxport->nport, client->addr, ret); + return; + } dev_dbg(dev, "rx%u: client 0x%02x released at slot %u\n", rxport->nport, client->addr, reg_idx); @@ -1285,7 +1295,7 @@ err_free_txport: return ret; } -static void ub960_csi_handle_events(struct ub960_data *priv, u8 nport) +static int ub960_csi_handle_events(struct ub960_data *priv, u8 nport) { struct device *dev = &priv->client->dev; u8 csi_tx_isr; @@ -1294,13 +1304,15 @@ static void ub960_csi_handle_events(struct ub960_data *priv, u8 nport) ret = ub960_txport_read(priv, nport, UB960_TR_CSI_TX_ISR, &csi_tx_isr, NULL); if (ret) - return; + return ret; if (csi_tx_isr & UB960_TR_CSI_TX_ISR_IS_CSI_SYNC_ERROR) dev_warn(dev, "TX%u: CSI_SYNC_ERROR\n", nport); if (csi_tx_isr & UB960_TR_CSI_TX_ISR_IS_CSI_PASS_ERROR) dev_warn(dev, "TX%u: CSI_PASS_ERROR\n", nport); + + return 0; } /* ----------------------------------------------------------------------------- @@ -1352,28 +1364,38 @@ static void ub960_rxport_disable_vpocs(struct ub960_data *priv) } } -static void ub960_rxport_clear_errors(struct ub960_data *priv, - unsigned int nport) +static int ub960_rxport_clear_errors(struct ub960_data *priv, + unsigned int nport) { + int ret = 0; u8 v; - ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v, NULL); - ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v, NULL); - ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &v, NULL); - ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &v, NULL); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v, &ret); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v, &ret); + ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &v, &ret); + ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &v, &ret); - ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v, NULL); - ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_LO, &v, NULL); + ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v, &ret); + ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_LO, &v, &ret); - ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v, NULL); + ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v, &ret); + + return ret; } -static void ub960_clear_rx_errors(struct ub960_data *priv) +static int ub960_clear_rx_errors(struct ub960_data *priv) { unsigned int nport; - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) - ub960_rxport_clear_errors(priv, nport); + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + int ret; + + ret = ub960_rxport_clear_errors(priv, nport); + if (ret) + return ret; + } + + return 0; } static int ub960_rxport_get_strobe_pos(struct ub960_data *priv, @@ -1383,14 +1405,18 @@ static int ub960_rxport_get_strobe_pos(struct ub960_data *priv, u8 clk_delay, data_delay; int ret; - ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_CLK, &v, NULL); + ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_CLK, &v, NULL); + if (ret) + return ret; clk_delay = (v & UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY) ? 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; - ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_DATA, &v, NULL); + ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_DATA, &v, NULL); + if (ret) + return ret; data_delay = (v & UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) ? 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; @@ -1412,10 +1438,11 @@ static int ub960_rxport_get_strobe_pos(struct ub960_data *priv, return 0; } -static void ub960_rxport_set_strobe_pos(struct ub960_data *priv, - unsigned int nport, s8 strobe_pos) +static int ub960_rxport_set_strobe_pos(struct ub960_data *priv, + unsigned int nport, s8 strobe_pos) { u8 clk_delay, data_delay; + int ret = 0; clk_delay = UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY; data_delay = UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY; @@ -1430,23 +1457,25 @@ static void ub960_rxport_set_strobe_pos(struct ub960_data *priv, data_delay = strobe_pos | UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY; ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay, NULL); + UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay, &ret); ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), - UB960_IR_RX_ANA_STROBE_SET_DATA, data_delay, NULL); + UB960_IR_RX_ANA_STROBE_SET_DATA, data_delay, &ret); + + return ret; } -static void ub960_rxport_set_strobe_range(struct ub960_data *priv, - s8 strobe_min, s8 strobe_max) +static int ub960_rxport_set_strobe_range(struct ub960_data *priv, s8 strobe_min, + s8 strobe_max) { /* Convert the signed strobe pos to positive zero based value */ strobe_min -= UB960_MIN_AEQ_STROBE_POS; strobe_max -= UB960_MIN_AEQ_STROBE_POS; - ub960_write(priv, UB960_XR_SFILTER_CFG, - ((u8)strobe_min << UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) | - ((u8)strobe_max << UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT), - NULL); + return ub960_write(priv, UB960_XR_SFILTER_CFG, + ((u8)strobe_min << UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) | + ((u8)strobe_max << UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT), + NULL); } static int ub960_rxport_get_eq_level(struct ub960_data *priv, @@ -1465,11 +1494,12 @@ static int ub960_rxport_get_eq_level(struct ub960_data *priv, return 0; } -static void ub960_rxport_set_eq_level(struct ub960_data *priv, - unsigned int nport, u8 eq_level) +static int ub960_rxport_set_eq_level(struct ub960_data *priv, + unsigned int nport, u8 eq_level) { u8 eq_stage_1_select_value, eq_stage_2_select_value; const unsigned int eq_stage_max = 7; + int ret; u8 v; if (eq_level <= eq_stage_max) { @@ -1480,7 +1510,9 @@ static void ub960_rxport_set_eq_level(struct ub960_data *priv, eq_stage_2_select_value = eq_level - eq_stage_max; } - ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v, NULL); + ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v, NULL); + if (ret) + return ret; v &= ~(UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_MASK | UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_MASK); @@ -1488,68 +1520,102 @@ static void ub960_rxport_set_eq_level(struct ub960_data *priv, v |= eq_stage_2_select_value << UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_SHIFT; v |= UB960_RR_AEQ_BYPASS_ENABLE; - ub960_rxport_write(priv, nport, UB960_RR_AEQ_BYPASS, v, NULL); + ret = ub960_rxport_write(priv, nport, UB960_RR_AEQ_BYPASS, v, NULL); + if (ret) + return ret; + + return 0; } -static void ub960_rxport_set_eq_range(struct ub960_data *priv, - unsigned int nport, u8 eq_min, u8 eq_max) +static int ub960_rxport_set_eq_range(struct ub960_data *priv, + unsigned int nport, u8 eq_min, u8 eq_max) { + int ret = 0; + ub960_rxport_write(priv, nport, UB960_RR_AEQ_MIN_MAX, (eq_min << UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) | (eq_max << UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT), - NULL); + &ret); /* Enable AEQ min setting */ ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_CTL2, UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR, - UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR, NULL); + UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR, &ret); + + return ret; } -static void ub960_rxport_config_eq(struct ub960_data *priv, unsigned int nport) +static int ub960_rxport_config_eq(struct ub960_data *priv, unsigned int nport) { struct ub960_rxport *rxport = priv->rxports[nport]; + int ret; /* We also set common settings here. Should be moved elsewhere. */ if (priv->strobe.manual) { /* Disable AEQ_SFILTER_EN */ - ub960_update_bits(priv, UB960_XR_AEQ_CTL1, - UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN, 0, NULL); + ret = ub960_update_bits(priv, UB960_XR_AEQ_CTL1, + UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN, 0, + NULL); + if (ret) + return ret; } else { /* Enable SFILTER and error control */ - ub960_write(priv, UB960_XR_AEQ_CTL1, - UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_MASK | - UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN, NULL); + ret = ub960_write(priv, UB960_XR_AEQ_CTL1, + UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_MASK | + UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN, + NULL); + + if (ret) + return ret; /* Set AEQ strobe range */ - ub960_rxport_set_strobe_range(priv, priv->strobe.min, - priv->strobe.max); + ret = ub960_rxport_set_strobe_range(priv, priv->strobe.min, + priv->strobe.max); + if (ret) + return ret; } /* The rest are port specific */ if (priv->strobe.manual) - ub960_rxport_set_strobe_pos(priv, nport, rxport->eq.strobe_pos); + ret = ub960_rxport_set_strobe_pos(priv, nport, + rxport->eq.strobe_pos); else - ub960_rxport_set_strobe_pos(priv, nport, 0); + ret = ub960_rxport_set_strobe_pos(priv, nport, 0); + + if (ret) + return ret; if (rxport->eq.manual_eq) { - ub960_rxport_set_eq_level(priv, nport, - rxport->eq.manual.eq_level); + ret = ub960_rxport_set_eq_level(priv, nport, + rxport->eq.manual.eq_level); + if (ret) + return ret; /* Enable AEQ Bypass */ - ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS, - UB960_RR_AEQ_BYPASS_ENABLE, - UB960_RR_AEQ_BYPASS_ENABLE, NULL); + ret = ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS, + UB960_RR_AEQ_BYPASS_ENABLE, + UB960_RR_AEQ_BYPASS_ENABLE, + NULL); + if (ret) + return ret; } else { - ub960_rxport_set_eq_range(priv, nport, - rxport->eq.aeq.eq_level_min, - rxport->eq.aeq.eq_level_max); + ret = ub960_rxport_set_eq_range(priv, nport, + rxport->eq.aeq.eq_level_min, + rxport->eq.aeq.eq_level_max); + if (ret) + return ret; /* Disable AEQ Bypass */ - ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS, - UB960_RR_AEQ_BYPASS_ENABLE, 0, NULL); + ret = ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS, + UB960_RR_AEQ_BYPASS_ENABLE, 0, + NULL); + if (ret) + return ret; } + + return 0; } static int ub960_rxport_link_ok(struct ub960_data *priv, unsigned int nport, @@ -1697,8 +1763,11 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv, continue; } - ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v, - NULL); + ret = ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, + &v, NULL); + + if (ret) + return ret; if (priv->hw_data->is_ub9702) { dev_dbg(dev, "\trx%u: locked, freq %llu Hz\n", @@ -1867,8 +1936,8 @@ static void ub960_rxport_remove_serializers(struct ub960_data *priv) } } -static void ub960_init_tx_port(struct ub960_data *priv, - struct ub960_txport *txport) +static int ub960_init_tx_port(struct ub960_data *priv, + struct ub960_txport *txport) { unsigned int nport = txport->nport; u8 csi_ctl = 0; @@ -1885,7 +1954,7 @@ static void ub960_init_tx_port(struct ub960_data *priv, if (!txport->non_continous_clk) csi_ctl |= UB960_TR_CSI_CTL_CSI_CONTS_CLOCK; - ub960_txport_write(priv, nport, UB960_TR_CSI_CTL, csi_ctl, NULL); + return ub960_txport_write(priv, nport, UB960_TR_CSI_CTL, csi_ctl, NULL); } static int ub960_init_tx_ports(struct ub960_data *priv) @@ -1893,6 +1962,7 @@ static int ub960_init_tx_ports(struct ub960_data *priv) unsigned int nport; u8 speed_select; u8 pll_div; + int ret = 0; /* TX ports */ @@ -1916,51 +1986,57 @@ static int ub960_init_tx_ports(struct ub960_data *priv) break; } - ub960_write(priv, UB960_SR_CSI_PLL_CTL, speed_select, NULL); + ub960_write(priv, UB960_SR_CSI_PLL_CTL, speed_select, &ret); if (priv->hw_data->is_ub9702) { - ub960_write(priv, UB960_SR_CSI_PLL_DIV, pll_div, NULL); + ub960_write(priv, UB960_SR_CSI_PLL_DIV, pll_div, &ret); switch (priv->tx_data_rate) { case MHZ(1600): default: ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, - 0x80, NULL); + 0x80, &ret); ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, - 0x2a, NULL); + 0x2a, &ret); break; case MHZ(800): ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, - 0x90, NULL); + 0x90, &ret); ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4f, - 0x2a, NULL); + 0x2a, &ret); ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, - 0x2a, NULL); + 0x2a, &ret); break; case MHZ(400): ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, - 0xa0, NULL); + 0xa0, &ret); break; } } + if (ret) + return ret; + for (nport = 0; nport < priv->hw_data->num_txports; nport++) { struct ub960_txport *txport = priv->txports[nport]; if (!txport) continue; - ub960_init_tx_port(priv, txport); + ret = ub960_init_tx_port(priv, txport); + if (ret) + return ret; } return 0; } -static void ub960_init_rx_port_ub960(struct ub960_data *priv, - struct ub960_rxport *rxport) +static int ub960_init_rx_port_ub960(struct ub960_data *priv, + struct ub960_rxport *rxport) { unsigned int nport = rxport->nport; u32 bc_freq_val; + int ret = 0; /* * Back channel frequency select. @@ -1989,19 +2065,19 @@ static void ub960_init_rx_port_ub960(struct ub960_data *priv, break; default: - return; + return -EINVAL; } ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK, - bc_freq_val, NULL); + bc_freq_val, &ret); switch (rxport->rx_mode) { case RXPORT_MODE_RAW10: /* FPD3_MODE = RAW10 Mode (DS90UB913A-Q1 / DS90UB933-Q1 compatible) */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG, UB960_RR_PORT_CONFIG_FPD3_MODE_MASK, - 0x3, NULL); + 0x3, &ret); /* * RAW10_8BIT_CTL = 0b10 : 8-bit processing using upper 8 bits @@ -2009,55 +2085,58 @@ static void ub960_init_rx_port_ub960(struct ub960_data *priv, ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_MASK, 0x2 << UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_SHIFT, - NULL); + &ret); break; case RXPORT_MODE_RAW12_HF: case RXPORT_MODE_RAW12_LF: /* Not implemented */ - return; + return -EINVAL; case RXPORT_MODE_CSI2_SYNC: case RXPORT_MODE_CSI2_NONSYNC: /* CSI-2 Mode (DS90UB953-Q1 compatible) */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG, 0x3, - 0x0, NULL); + 0x0, &ret); break; } /* LV_POLARITY & FV_POLARITY */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, - rxport->lv_fv_pol, NULL); + rxport->lv_fv_pol, &ret); /* Enable all interrupt sources from this port */ - ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07, NULL); - ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f, NULL); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07, &ret); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f, &ret); /* Enable I2C_PASS_THROUGH */ ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, - UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, NULL); + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, &ret); /* Enable I2C communication to the serializer via the alias addr */ ub960_rxport_write(priv, nport, UB960_RR_SER_ALIAS_ID, - rxport->ser.alias << 1, NULL); + rxport->ser.alias << 1, &ret); /* Configure EQ related settings */ ub960_rxport_config_eq(priv, nport); /* Enable RX port */ ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport), - NULL); + &ret); + + return ret; } -static void ub960_init_rx_port_ub9702_fpd3(struct ub960_data *priv, - struct ub960_rxport *rxport) +static int ub960_init_rx_port_ub9702_fpd3(struct ub960_data *priv, + struct ub960_rxport *rxport) { unsigned int nport = rxport->nport; u8 bc_freq_val; u8 fpd_func_mode; + int ret = 0; switch (rxport->rx_mode) { case RXPORT_MODE_RAW10: @@ -2086,90 +2165,96 @@ static void ub960_init_rx_port_ub9702_fpd3(struct ub960_data *priv, break; default: - return; + return -EINVAL; } ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, 0x7, - bc_freq_val, NULL); + bc_freq_val, &ret); ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, fpd_func_mode, - NULL); + &ret); /* set serdes_eq_mode = 1 */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xa8, 0x80, - NULL); + &ret); /* enable serdes driver */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x0d, 0x7f, - NULL); + &ret); /* set serdes_eq_offset=4 */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04, - NULL); + &ret); /* init default serdes_eq_max in 0xa9 */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xa9, 0x23, - NULL); + &ret); /* init serdes_eq_min in 0xaa */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xaa, 0, NULL); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xaa, 0, &ret); /* serdes_driver_ctl2 control: DS90UB953-Q1/DS90UB933-Q1/DS90UB913A-Q1 */ ub960_ind_update_bits(priv, UB960_IND_TARGET_RX_ANA(nport), 0x1b, - BIT(3), BIT(3), NULL); + BIT(3), BIT(3), &ret); /* RX port to half-rate */ ub960_update_bits(priv, UB960_SR_FPD_RATE_CFG, 0x3 << (nport * 2), - BIT(nport * 2), NULL); + BIT(nport * 2), &ret); + + return ret; } -static void ub960_init_rx_port_ub9702_fpd4_aeq(struct ub960_data *priv, - struct ub960_rxport *rxport) +static int ub960_init_rx_port_ub9702_fpd4_aeq(struct ub960_data *priv, + struct ub960_rxport *rxport) { unsigned int nport = rxport->nport; bool first_time_power_up = true; + int ret = 0; if (first_time_power_up) { u8 v; /* AEQ init */ ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2c, &v, - NULL); + &ret); ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, v, - NULL); + &ret); ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x28, - v + 1, NULL); + v + 1, &ret); ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, - 0x00, NULL); + 0x00, &ret); } /* enable serdes_eq_ctl2 */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x9e, 0x00, - NULL); + &ret); /* enable serdes_eq_ctl1 */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x90, 0x40, - NULL); + &ret); /* enable serdes_eq_en */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2e, 0x40, - NULL); + &ret); /* disable serdes_eq_override */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xf0, 0x00, - NULL); + &ret); /* disable serdes_gain_override */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x71, 0x00, - NULL); + &ret); + + return ret; } -static void ub960_init_rx_port_ub9702_fpd4(struct ub960_data *priv, - struct ub960_rxport *rxport) +static int ub960_init_rx_port_ub9702_fpd4(struct ub960_data *priv, + struct ub960_rxport *rxport) { unsigned int nport = rxport->nport; u8 bc_freq_val; + int ret = 0; switch (rxport->rx_mode) { case RXPORT_MODE_RAW10: @@ -2193,55 +2278,66 @@ static void ub960_init_rx_port_ub9702_fpd4(struct ub960_data *priv, break; default: - return; + return -EINVAL; } ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, 0x7, - bc_freq_val, NULL); + bc_freq_val, &ret); /* FPD4 Sync Mode */ - ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, 0, NULL); + ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, 0, &ret); /* add serdes_eq_offset of 4 */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04, - NULL); + &ret); /* FPD4 serdes_start_eq in 0x27: assign default */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, 0x0, NULL); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, 0x0, &ret); /* FPD4 serdes_end_eq in 0x28: assign default */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x28, 0x23, - NULL); + &ret); /* set serdes_driver_mode into FPD IV mode */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x04, 0x00, - NULL); + &ret); /* set FPD PBC drv into FPD IV mode */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x1b, 0x00, - NULL); + &ret); /* set serdes_system_init to 0x2f */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x21, 0x2f, - NULL); + &ret); /* set serdes_system_rst in reset mode */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x25, 0xc1, - NULL); + &ret); /* RX port to 7.55G mode */ ub960_update_bits(priv, UB960_SR_FPD_RATE_CFG, 0x3 << (nport * 2), - 0 << (nport * 2), NULL); + 0 << (nport * 2), &ret); + + if (ret) + return ret; - ub960_init_rx_port_ub9702_fpd4_aeq(priv, rxport); + ret = ub960_init_rx_port_ub9702_fpd4_aeq(priv, rxport); + if (ret) + return ret; + + return 0; } -static void ub960_init_rx_port_ub9702(struct ub960_data *priv, - struct ub960_rxport *rxport) +static int ub960_init_rx_port_ub9702(struct ub960_data *priv, + struct ub960_rxport *rxport) { unsigned int nport = rxport->nport; + int ret; if (rxport->cdr_mode == RXPORT_CDR_FPD3) - ub960_init_rx_port_ub9702_fpd3(priv, rxport); + ret = ub960_init_rx_port_ub9702_fpd3(priv, rxport); else /* RXPORT_CDR_FPD4 */ - ub960_init_rx_port_ub9702_fpd4(priv, rxport); + ret = ub960_init_rx_port_ub9702_fpd4(priv, rxport); + + if (ret) + return ret; switch (rxport->rx_mode) { case RXPORT_MODE_RAW10: @@ -2250,14 +2346,14 @@ static void ub960_init_rx_port_ub9702(struct ub960_data *priv, * 0b10 : 8-bit processing using upper 8 bits */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, - 0x3 << 6, 0x2 << 6, NULL); + 0x3 << 6, 0x2 << 6, &ret); break; case RXPORT_MODE_RAW12_HF: case RXPORT_MODE_RAW12_LF: /* Not implemented */ - return; + return -EINVAL; case RXPORT_MODE_CSI2_SYNC: case RXPORT_MODE_CSI2_NONSYNC: @@ -2267,30 +2363,32 @@ static void ub960_init_rx_port_ub9702(struct ub960_data *priv, /* LV_POLARITY & FV_POLARITY */ ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, - rxport->lv_fv_pol, NULL); + rxport->lv_fv_pol, &ret); /* Enable all interrupt sources from this port */ - ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07, NULL); - ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f, NULL); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07, &ret); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f, &ret); /* Enable I2C_PASS_THROUGH */ ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, - UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, NULL); + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, &ret); /* Enable I2C communication to the serializer via the alias addr */ ub960_rxport_write(priv, nport, UB960_RR_SER_ALIAS_ID, - rxport->ser.alias << 1, NULL); + rxport->ser.alias << 1, &ret); /* Enable RX port */ ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport), - NULL); + &ret); if (rxport->cdr_mode == RXPORT_CDR_FPD4) { /* unreset 960 AEQ */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x25, - 0x41, NULL); + 0x41, &ret); } + + return ret; } static int ub960_init_rx_ports(struct ub960_data *priv) @@ -2299,20 +2397,24 @@ static int ub960_init_rx_ports(struct ub960_data *priv) for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { struct ub960_rxport *rxport = priv->rxports[nport]; + int ret; if (!rxport) continue; if (priv->hw_data->is_ub9702) - ub960_init_rx_port_ub9702(priv, rxport); + ret = ub960_init_rx_port_ub9702(priv, rxport); else - ub960_init_rx_port_ub960(priv, rxport); + ret = ub960_init_rx_port_ub960(priv, rxport); + + if (ret) + return ret; } return 0; } -static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) +static int ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) { struct device *dev = &priv->client->dev; u8 rx_port_sts1; @@ -2322,21 +2424,15 @@ static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) int ret = 0; /* Read interrupts (also clears most of them) */ - if (!ret) - ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, - &rx_port_sts1, NULL); - if (!ret) - ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, - &rx_port_sts2, NULL); - if (!ret) - ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, - &csi_rx_sts, NULL); - if (!ret) - ret = ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, - &bcc_sts, NULL); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &rx_port_sts1, + &ret); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &rx_port_sts2, + &ret); + ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &csi_rx_sts, &ret); + ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &bcc_sts, &ret); if (ret) - return; + return ret; if (rx_port_sts1 & UB960_RR_RX_PORT_STS1_PARITY_ERROR) { u16 v; @@ -2431,6 +2527,8 @@ static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) "stable freq" : "unstable freq"); } + + return 0; } /* ----------------------------------------------------------------------------- @@ -2486,14 +2584,14 @@ static int ub960_enable_tx_port(struct ub960_data *priv, unsigned int nport) UB960_TR_CSI_CTL_CSI_ENABLE, NULL); } -static void ub960_disable_tx_port(struct ub960_data *priv, unsigned int nport) +static int ub960_disable_tx_port(struct ub960_data *priv, unsigned int nport) { struct device *dev = &priv->client->dev; dev_dbg(dev, "disable TX port %u\n", nport); - ub960_txport_update_bits(priv, nport, UB960_TR_CSI_CTL, - UB960_TR_CSI_CTL_CSI_ENABLE, 0, NULL); + return ub960_txport_update_bits(priv, nport, UB960_TR_CSI_CTL, + UB960_TR_CSI_CTL_CSI_ENABLE, 0, NULL); } static int ub960_enable_rx_port(struct ub960_data *priv, unsigned int nport) @@ -2507,16 +2605,16 @@ static int ub960_enable_rx_port(struct ub960_data *priv, unsigned int nport) UB960_SR_FWD_CTL1_PORT_DIS(nport), 0, NULL); } -static void ub960_disable_rx_port(struct ub960_data *priv, unsigned int nport) +static int ub960_disable_rx_port(struct ub960_data *priv, unsigned int nport) { struct device *dev = &priv->client->dev; dev_dbg(dev, "disable RX port %u\n", nport); /* Disable forwarding */ - ub960_update_bits(priv, UB960_SR_FWD_CTL1, - UB960_SR_FWD_CTL1_PORT_DIS(nport), - UB960_SR_FWD_CTL1_PORT_DIS(nport), NULL); + return ub960_update_bits(priv, UB960_SR_FWD_CTL1, + UB960_SR_FWD_CTL1_PORT_DIS(nport), + UB960_SR_FWD_CTL1_PORT_DIS(nport), NULL); } /* @@ -2658,12 +2756,12 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, case RXPORT_MODE_RAW10: ub960_rxport_write(priv, nport, UB960_RR_RAW10_ID, rx_data[nport].pixel_dt | (vc << UB960_RR_RAW10_ID_VC_SHIFT), - NULL); + &ret); ub960_rxport_write(priv, rxport->nport, UB960_RR_RAW_EMBED_DTYPE, (rx_data[nport].meta_lines << UB960_RR_RAW_EMBED_DTYPE_LINES_SHIFT) | - rx_data[nport].meta_dt, NULL); + rx_data[nport].meta_dt, &ret); break; @@ -2681,7 +2779,7 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, (vc << UB960_RR_CSI_VC_MAP_SHIFT(2)) | (vc << UB960_RR_CSI_VC_MAP_SHIFT(1)) | (vc << UB960_RR_CSI_VC_MAP_SHIFT(0)), - NULL); + &ret); } else { unsigned int i; @@ -2690,7 +2788,7 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, ub960_rxport_write(priv, nport, UB960_RR_VC_ID_MAP(i), (nport << 4) | nport, - NULL); + &ret); } break; @@ -2702,9 +2800,9 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, fwd_ctl &= ~BIT(nport); /* forward to TX0 */ } - ub960_write(priv, UB960_SR_FWD_CTL1, fwd_ctl, NULL); + ub960_write(priv, UB960_SR_FWD_CTL1, fwd_ctl, &ret); - return 0; + return ret; } static void ub960_update_streaming_status(struct ub960_data *priv) @@ -3107,8 +3205,8 @@ static const struct v4l2_subdev_pad_ops ub960_pad_ops = { .set_fmt = ub960_set_fmt, }; -static void ub960_log_status_ub960_sp_eq(struct ub960_data *priv, - unsigned int nport) +static int ub960_log_status_ub960_sp_eq(struct ub960_data *priv, + unsigned int nport) { struct device *dev = &priv->client->dev; u8 eq_level; @@ -3120,7 +3218,7 @@ static void ub960_log_status_ub960_sp_eq(struct ub960_data *priv, ret = ub960_read(priv, UB960_XR_AEQ_CTL1, &v, NULL); if (ret) - return; + return ret; dev_info(dev, "\t%s strobe\n", (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) ? "Adaptive" : @@ -3129,7 +3227,7 @@ static void ub960_log_status_ub960_sp_eq(struct ub960_data *priv, if (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) { ret = ub960_read(priv, UB960_XR_SFILTER_CFG, &v, NULL); if (ret) - return; + return ret; dev_info(dev, "\tStrobe range [%d, %d]\n", ((v >> UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) & 0xf) - 7, @@ -3138,7 +3236,7 @@ static void ub960_log_status_ub960_sp_eq(struct ub960_data *priv, ret = ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos); if (ret) - return; + return ret; dev_info(dev, "\tStrobe pos %d\n", strobe_pos); @@ -3146,7 +3244,7 @@ static void ub960_log_status_ub960_sp_eq(struct ub960_data *priv, ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v, NULL); if (ret) - return; + return ret; dev_info(dev, "\t%s EQ\n", (v & UB960_RR_AEQ_BYPASS_ENABLE) ? "Manual" : @@ -3156,15 +3254,20 @@ static void ub960_log_status_ub960_sp_eq(struct ub960_data *priv, ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_MIN_MAX, &v, NULL); if (ret) - return; + return ret; dev_info(dev, "\tEQ range [%u, %u]\n", (v >> UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) & 0xf, (v >> UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT) & 0xf); } - if (ub960_rxport_get_eq_level(priv, nport, &eq_level) == 0) - dev_info(dev, "\tEQ level %u\n", eq_level); + ret = ub960_rxport_get_eq_level(priv, nport, &eq_level); + if (ret) + return ret; + + dev_info(dev, "\tEQ level %u\n", eq_level); + + return 0; } static int ub960_log_status(struct v4l2_subdev *sd) @@ -3176,11 +3279,15 @@ static int ub960_log_status(struct v4l2_subdev *sd) u16 v16 = 0; u8 v = 0; u8 id[UB960_SR_FPD3_RX_ID_LEN]; + int ret = 0; state = v4l2_subdev_lock_and_get_active_state(sd); - for (unsigned int i = 0; i < sizeof(id); i++) - ub960_read(priv, UB960_SR_FPD3_RX_ID(i), &id[i], NULL); + for (unsigned int i = 0; i < sizeof(id); i++) { + ret = ub960_read(priv, UB960_SR_FPD3_RX_ID(i), &id[i], NULL); + if (ret) + return ret; + } dev_info(dev, "ID '%.*s'\n", (int)sizeof(id), id); @@ -3194,24 +3301,39 @@ static int ub960_log_status(struct v4l2_subdev *sd) continue; } - ub960_txport_read(priv, nport, UB960_TR_CSI_STS, &v, NULL); + ret = ub960_txport_read(priv, nport, UB960_TR_CSI_STS, &v, NULL); + if (ret) + return ret; + dev_info(dev, "\tsync %u, pass %u\n", v & (u8)BIT(1), v & (u8)BIT(0)); - ub960_read16(priv, UB960_SR_CSI_FRAME_COUNT_HI(nport), &v16, - NULL); + ret = ub960_read16(priv, UB960_SR_CSI_FRAME_COUNT_HI(nport), + &v16, NULL); + if (ret) + return ret; + dev_info(dev, "\tframe counter %u\n", v16); - ub960_read16(priv, UB960_SR_CSI_FRAME_ERR_COUNT_HI(nport), - &v16, NULL); + ret = ub960_read16(priv, UB960_SR_CSI_FRAME_ERR_COUNT_HI(nport), + &v16, NULL); + if (ret) + return ret; + dev_info(dev, "\tframe error counter %u\n", v16); - ub960_read16(priv, UB960_SR_CSI_LINE_COUNT_HI(nport), &v16, - NULL); + ret = ub960_read16(priv, UB960_SR_CSI_LINE_COUNT_HI(nport), + &v16, NULL); + if (ret) + return ret; + dev_info(dev, "\tline counter %u\n", v16); - ub960_read16(priv, UB960_SR_CSI_LINE_ERR_COUNT_HI(nport), - &v16, NULL); + ret = ub960_read16(priv, UB960_SR_CSI_LINE_ERR_COUNT_HI(nport), + &v16, NULL); + if (ret) + return ret; + dev_info(dev, "\tline error counter %u\n", v16); } @@ -3225,8 +3347,10 @@ static int ub960_log_status(struct v4l2_subdev *sd) continue; } - ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v, - NULL); + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v, + NULL); + if (ret) + return ret; if (v & UB960_RR_RX_PORT_STS1_LOCK_STS) dev_info(dev, "\tLocked\n"); @@ -3234,32 +3358,53 @@ static int ub960_log_status(struct v4l2_subdev *sd) dev_info(dev, "\tNot locked\n"); dev_info(dev, "\trx_port_sts1 %#02x\n", v); - ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v, - NULL); + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v, + NULL); + if (ret) + return ret; + dev_info(dev, "\trx_port_sts2 %#02x\n", v); - ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v16, - NULL); + ret = ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, + &v16, NULL); + if (ret) + return ret; + dev_info(dev, "\tlink freq %llu Hz\n", ((u64)v16 * HZ_PER_MHZ) >> 8); - ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v16, - NULL); + ret = ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, + &v16, NULL); + if (ret) + return ret; + dev_info(dev, "\tparity errors %u\n", v16); - ub960_rxport_read16(priv, nport, UB960_RR_LINE_COUNT_HI, &v16, - NULL); + ret = ub960_rxport_read16(priv, nport, UB960_RR_LINE_COUNT_HI, + &v16, NULL); + if (ret) + return ret; + dev_info(dev, "\tlines per frame %u\n", v16); - ub960_rxport_read16(priv, nport, UB960_RR_LINE_LEN_1, &v16, - NULL); + ret = ub960_rxport_read16(priv, nport, UB960_RR_LINE_LEN_1, + &v16, NULL); + if (ret) + return ret; + dev_info(dev, "\tbytes per line %u\n", v16); - ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v, - NULL); + ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, + &v, NULL); + if (ret) + return ret; + dev_info(dev, "\tcsi_err_counter %u\n", v); - if (!priv->hw_data->is_ub9702) - ub960_log_status_ub960_sp_eq(priv, nport); + if (!priv->hw_data->is_ub9702) { + ret = ub960_log_status_ub960_sp_eq(priv, nport); + if (ret) + return ret; + } /* GPIOs */ for (unsigned int i = 0; i < UB960_NUM_BC_GPIOS; i++) { @@ -3269,7 +3414,9 @@ static int ub960_log_status(struct v4l2_subdev *sd) ctl_reg = UB960_RR_BC_GPIO_CTL(i / 2); ctl_shift = (i % 2) * 4; - ub960_rxport_read(priv, nport, ctl_reg, &v, NULL); + ret = ub960_rxport_read(priv, nport, ctl_reg, &v, NULL); + if (ret) + return ret; dev_info(dev, "\tGPIO%u: mode %u\n", i, (v >> ctl_shift) & 0xf); @@ -3325,16 +3472,22 @@ static irqreturn_t ub960_handle_events(int irq, void *arg) dev_dbg(&priv->client->dev, "FWD_STS %#02x\n", fwd_sts); for (i = 0; i < priv->hw_data->num_txports; i++) { - if (int_sts & UB960_SR_INTERRUPT_STS_IS_CSI_TX(i)) - ub960_csi_handle_events(priv, i); + if (int_sts & UB960_SR_INTERRUPT_STS_IS_CSI_TX(i)) { + ret = ub960_csi_handle_events(priv, i); + if (ret) + return IRQ_NONE; + } } for (i = 0; i < priv->hw_data->num_rxports; i++) { if (!priv->rxports[i]) continue; - if (int_sts & UB960_SR_INTERRUPT_STS_IS_RX(i)) - ub960_rxport_handle_events(priv, i); + if (int_sts & UB960_SR_INTERRUPT_STS_IS_RX(i)) { + ret = ub960_rxport_handle_events(priv, i); + if (ret) + return IRQ_NONE; + } } return IRQ_HANDLED; @@ -3938,7 +4091,7 @@ static const struct regmap_config ub960_regmap_config = { .disable_locking = true, }; -static void ub960_reset(struct ub960_data *priv, bool reset_regs) +static int ub960_reset(struct ub960_data *priv, bool reset_regs) { struct device *dev = &priv->client->dev; unsigned int v; @@ -3948,7 +4101,9 @@ static void ub960_reset(struct ub960_data *priv, bool reset_regs) bit = reset_regs ? UB960_SR_RESET_DIGITAL_RESET1 : UB960_SR_RESET_DIGITAL_RESET0; - ub960_write(priv, UB960_SR_RESET, bit, NULL); + ret = ub960_write(priv, UB960_SR_RESET, bit, NULL); + if (ret) + return ret; mutex_lock(&priv->reg_lock); @@ -3959,6 +4114,8 @@ static void ub960_reset(struct ub960_data *priv, bool reset_regs) if (ret) dev_err(dev, "reset failed: %d\n", ret); + + return ret; } static int ub960_get_hw_resources(struct ub960_data *priv) @@ -4017,7 +4174,9 @@ static int ub960_enable_core_hw(struct ub960_data *priv) fsleep(2000); } - ub960_reset(priv, true); + ret = ub960_reset(priv, true); + if (ret) + goto err_pd_gpio; /* Runtime check register accessibility */ ret = ub960_read(priv, UB960_SR_REV_MASK, &rev_mask, NULL); @@ -4132,7 +4291,9 @@ static int ub960_probe(struct i2c_client *client) if (ret) goto err_disable_vpocs; - ub960_reset(priv, false); + ret = ub960_reset(priv, false); + if (ret) + goto err_disable_vpocs; port_mask = 0; @@ -4159,7 +4320,9 @@ static int ub960_probe(struct i2c_client *client) * Clear any errors caused by switching the RX port settings while * probing. */ - ub960_clear_rx_errors(priv); + ret = ub960_clear_rx_errors(priv); + if (ret) + goto err_disable_vpocs; ret = ub960_init_atr(priv); if (ret) -- cgit v1.2.3-59-g8ed1b From fe591fb5ed225ce8c6a122d7b8baa670bd8542d4 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:09 +0530 Subject: media: i2c: ds90ub953: Add error handling to ub953_log_status() Add error handling to ub953_log_status(). Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub953.c | 80 +++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 22 deletions(-) diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index fbd977760c6b..a08aad3f7fe0 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -633,23 +633,33 @@ static int ub953_log_status(struct v4l2_subdev *sd) { struct ub953_data *priv = sd_to_ub953(sd); struct device *dev = &priv->client->dev; - u8 v = 0, v1 = 0, v2 = 0; - unsigned int i; char id[UB953_REG_FPD3_RX_ID_LEN]; - u8 gpio_local_data = 0; - u8 gpio_input_ctrl = 0; - u8 gpio_pin_sts = 0; + u8 gpio_local_data; + u8 gpio_input_ctrl; + u8 gpio_pin_sts; + unsigned int i; + u8 v, v1, v2; + int ret; - for (i = 0; i < sizeof(id); i++) - ub953_read(priv, UB953_REG_FPD3_RX_ID(i), &id[i], NULL); + for (i = 0; i < sizeof(id); i++) { + ret = ub953_read(priv, UB953_REG_FPD3_RX_ID(i), &id[i], NULL); + if (ret) + return ret; + } dev_info(dev, "ID '%.*s'\n", (int)sizeof(id), id); - ub953_read(priv, UB953_REG_GENERAL_STATUS, &v, NULL); + ret = ub953_read(priv, UB953_REG_GENERAL_STATUS, &v, NULL); + if (ret) + return ret; + dev_info(dev, "GENERAL_STATUS %#02x\n", v); - ub953_read(priv, UB953_REG_CRC_ERR_CNT1, &v1, NULL); - ub953_read(priv, UB953_REG_CRC_ERR_CNT2, &v2, NULL); + ub953_read(priv, UB953_REG_CRC_ERR_CNT1, &v1, &ret); + ub953_read(priv, UB953_REG_CRC_ERR_CNT2, &v2, &ret); + if (ret) + return ret; + dev_info(dev, "CRC error count %u\n", v1 | (v2 << 8)); /* Clear CRC error counter */ @@ -658,34 +668,60 @@ static int ub953_log_status(struct v4l2_subdev *sd) UB953_REG_BC_CTRL_CRC_ERR_CLR, UB953_REG_BC_CTRL_CRC_ERR_CLR); - ub953_read(priv, UB953_REG_CSI_ERR_CNT, &v, NULL); + ret = ub953_read(priv, UB953_REG_CSI_ERR_CNT, &v, NULL); + if (ret) + return ret; + dev_info(dev, "CSI error count %u\n", v); - ub953_read(priv, UB953_REG_CSI_ERR_STATUS, &v, NULL); + ret = ub953_read(priv, UB953_REG_CSI_ERR_STATUS, &v, NULL); + if (ret) + return ret; + dev_info(dev, "CSI_ERR_STATUS %#02x\n", v); - ub953_read(priv, UB953_REG_CSI_ERR_DLANE01, &v, NULL); + ret = ub953_read(priv, UB953_REG_CSI_ERR_DLANE01, &v, NULL); + if (ret) + return ret; + dev_info(dev, "CSI_ERR_DLANE01 %#02x\n", v); - ub953_read(priv, UB953_REG_CSI_ERR_DLANE23, &v, NULL); + ret = ub953_read(priv, UB953_REG_CSI_ERR_DLANE23, &v, NULL); + if (ret) + return ret; + dev_info(dev, "CSI_ERR_DLANE23 %#02x\n", v); - ub953_read(priv, UB953_REG_CSI_ERR_CLK_LANE, &v, NULL); + ret = ub953_read(priv, UB953_REG_CSI_ERR_CLK_LANE, &v, NULL); + if (ret) + return ret; + dev_info(dev, "CSI_ERR_CLK_LANE %#02x\n", v); - ub953_read(priv, UB953_REG_CSI_PKT_HDR_VC_ID, &v, NULL); + ret = ub953_read(priv, UB953_REG_CSI_PKT_HDR_VC_ID, &v, NULL); + if (ret) + return ret; + dev_info(dev, "CSI packet header VC %u ID %u\n", v >> 6, v & 0x3f); - ub953_read(priv, UB953_REG_PKT_HDR_WC_LSB, &v1, NULL); - ub953_read(priv, UB953_REG_PKT_HDR_WC_MSB, &v2, NULL); + ub953_read(priv, UB953_REG_PKT_HDR_WC_LSB, &v1, &ret); + ub953_read(priv, UB953_REG_PKT_HDR_WC_MSB, &v2, &ret); + if (ret) + return ret; + dev_info(dev, "CSI packet header WC %u\n", (v2 << 8) | v1); - ub953_read(priv, UB953_REG_CSI_ECC, &v, NULL); + ret = ub953_read(priv, UB953_REG_CSI_ECC, &v, NULL); + if (ret) + return ret; + dev_info(dev, "CSI ECC %#02x\n", v); - ub953_read(priv, UB953_REG_LOCAL_GPIO_DATA, &gpio_local_data, NULL); - ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &gpio_input_ctrl, NULL); - ub953_read(priv, UB953_REG_GPIO_PIN_STS, &gpio_pin_sts, NULL); + ub953_read(priv, UB953_REG_LOCAL_GPIO_DATA, &gpio_local_data, &ret); + ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &gpio_input_ctrl, &ret); + ub953_read(priv, UB953_REG_GPIO_PIN_STS, &gpio_pin_sts, &ret); + if (ret) + return ret; for (i = 0; i < UB953_NUM_GPIOS; i++) { dev_info(dev, -- cgit v1.2.3-59-g8ed1b From d3be2fcde66d38c5f229c0f74cf3616f3453179e Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:10 +0530 Subject: media: i2c: ds90ub913: Add error handling to ub913_log_status() Add error handling to ub913_log_status(). Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub913.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index f38421d34d20..401cc2a10c3c 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -504,25 +504,41 @@ static int ub913_log_status(struct v4l2_subdev *sd) { struct ub913_data *priv = sd_to_ub913(sd); struct device *dev = &priv->client->dev; - u8 v = 0, v1 = 0, v2 = 0; + u8 v, v1, v2; + int ret; + + ret = ub913_read(priv, UB913_REG_MODE_SEL, &v, NULL); + if (ret) + return ret; - ub913_read(priv, UB913_REG_MODE_SEL, &v, NULL); dev_info(dev, "MODE_SEL %#02x\n", v); - ub913_read(priv, UB913_REG_CRC_ERRORS_LSB, &v1, NULL); - ub913_read(priv, UB913_REG_CRC_ERRORS_MSB, &v2, NULL); + ub913_read(priv, UB913_REG_CRC_ERRORS_LSB, &v1, &ret); + ub913_read(priv, UB913_REG_CRC_ERRORS_MSB, &v2, &ret); + if (ret) + return ret; + dev_info(dev, "CRC errors %u\n", v1 | (v2 << 8)); /* clear CRC errors */ - ub913_read(priv, UB913_REG_GENERAL_CFG, &v, NULL); + ub913_read(priv, UB913_REG_GENERAL_CFG, &v, &ret); ub913_write(priv, UB913_REG_GENERAL_CFG, - v | UB913_REG_GENERAL_CFG_CRC_ERR_RESET, NULL); - ub913_write(priv, UB913_REG_GENERAL_CFG, v, NULL); + v | UB913_REG_GENERAL_CFG_CRC_ERR_RESET, &ret); + ub913_write(priv, UB913_REG_GENERAL_CFG, v, &ret); + + if (ret) + return ret; + + ret = ub913_read(priv, UB913_REG_GENERAL_STATUS, &v, NULL); + if (ret) + return ret; - ub913_read(priv, UB913_REG_GENERAL_STATUS, &v, NULL); dev_info(dev, "GENERAL_STATUS %#02x\n", v); - ub913_read(priv, UB913_REG_PLL_OVR, &v, NULL); + ret = ub913_read(priv, UB913_REG_PLL_OVR, &v, NULL); + if (ret) + return ret; + dev_info(dev, "PLL_OVR %#02x\n", v); return 0; -- cgit v1.2.3-59-g8ed1b From dbad194b0bffbacd896ed9dec7fd3556720b8049 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Mon, 3 Mar 2025 21:32:11 +0530 Subject: media: i2c: ds90ub953: Speed-up I2C watchdog timer On the I2C bus for remote clients (sensors), by default the watchdog timer expires in 1s. To allow for a quicker system bring-up time, TI recommends to speed it up to 50us [1]. [1]: Section 7.3.1.1 - https://www.ti.com/lit/gpn/ds90ub953-q1 Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub953.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index a08aad3f7fe0..a5c23e94f4ea 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -54,6 +54,10 @@ #define UB953_REG_CLKOUT_CTRL0 0x06 #define UB953_REG_CLKOUT_CTRL1 0x07 +#define UB953_REG_I2C_CONTROL2 0x0a +#define UB953_REG_I2C_CONTROL2_SDA_OUTPUT_SETUP_SHIFT 4 +#define UB953_REG_I2C_CONTROL2_BUS_SPEEDUP BIT(1) + #define UB953_REG_SCL_HIGH_TIME 0x0b #define UB953_REG_SCL_LOW_TIME 0x0c @@ -1320,7 +1324,12 @@ static int ub953_hw_init(struct ub953_data *priv) if (ret) return ret; - return 0; + v = 1U << UB953_REG_I2C_CONTROL2_SDA_OUTPUT_SETUP_SHIFT; + v |= UB953_REG_I2C_CONTROL2_BUS_SPEEDUP; + + ret = ub953_write(priv, UB953_REG_I2C_CONTROL2, v, NULL); + + return ret; } static int ub953_subdev_init(struct ub953_data *priv) -- cgit v1.2.3-59-g8ed1b From 675bc338ea476e7e3a290bf5d6e9bb2455ec5402 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:12 +0530 Subject: media: i2c: ds90ub960: Move UB9702 registers to a separate section The driver supports both UB960 and UB9702. While devices work in similar ways and have a lot of identical registers, there are also plenty of differences. To clarify the situation a bit, move the UB9702 registers to a separate section and prefix them with UB9702. Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub960.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 086aa8cc78fa..f9af6d643ac8 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -307,8 +307,6 @@ #define UB960_XR_REFCLK_FREQ 0xa5 /* UB960 */ -#define UB960_RR_VC_ID_MAP(x) (0xa0 + (x)) /* UB9702 */ - #define UB960_SR_IND_ACC_CTL 0xb0 #define UB960_SR_IND_ACC_CTL_IA_AUTO_INC BIT(1) @@ -321,9 +319,6 @@ #define UB960_SR_FV_MIN_TIME 0xbc #define UB960_SR_GPIO_PD_CTL 0xbe -#define UB960_SR_FPD_RATE_CFG 0xc2 /* UB9702 */ -#define UB960_SR_CSI_PLL_DIV 0xc9 /* UB9702 */ - #define UB960_RR_PORT_DEBUG 0xd0 #define UB960_RR_AEQ_CTL2 0xd2 #define UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR BIT(2) @@ -354,15 +349,12 @@ #define UB960_RR_SEN_INT_RISE_STS 0xde #define UB960_RR_SEN_INT_FALL_STS 0xdf -#define UB960_RR_CHANNEL_MODE 0xe4 /* UB9702 */ #define UB960_SR_FPD3_RX_ID(n) (0xf0 + (n)) #define UB960_SR_FPD3_RX_ID_LEN 6 #define UB960_SR_I2C_RX_ID(n) (0xf8 + (n)) -#define UB9702_SR_REFCLK_FREQ 0x3d - /* Indirect register blocks */ #define UB960_IND_TARGET_PAT_GEN 0x00 #define UB960_IND_TARGET_RX_ANA(n) (0x01 + (n)) @@ -397,6 +389,14 @@ #define UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY BIT(3) #define UB960_IR_RX_ANA_STROBE_SET_DATA_DELAY_MASK GENMASK(2, 0) +/* UB9702 Registers */ + +#define UB9702_SR_REFCLK_FREQ 0x3d +#define UB9702_RR_VC_ID_MAP(x) (0xa0 + (x)) +#define UB9702_SR_FPD_RATE_CFG 0xc2 +#define UB9702_SR_CSI_PLL_DIV 0xc9 +#define UB9702_RR_CHANNEL_MODE 0xe4 + /* EQ related */ #define UB960_MIN_AEQ_STROBE_POS -7 @@ -1989,7 +1989,7 @@ static int ub960_init_tx_ports(struct ub960_data *priv) ub960_write(priv, UB960_SR_CSI_PLL_CTL, speed_select, &ret); if (priv->hw_data->is_ub9702) { - ub960_write(priv, UB960_SR_CSI_PLL_DIV, pll_div, &ret); + ub960_write(priv, UB9702_SR_CSI_PLL_DIV, pll_div, &ret); switch (priv->tx_data_rate) { case MHZ(1600): @@ -2170,7 +2170,7 @@ static int ub960_init_rx_port_ub9702_fpd3(struct ub960_data *priv, ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, 0x7, bc_freq_val, &ret); - ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, fpd_func_mode, + ub960_rxport_write(priv, nport, UB9702_RR_CHANNEL_MODE, fpd_func_mode, &ret); /* set serdes_eq_mode = 1 */ @@ -2197,7 +2197,7 @@ static int ub960_init_rx_port_ub9702_fpd3(struct ub960_data *priv, BIT(3), BIT(3), &ret); /* RX port to half-rate */ - ub960_update_bits(priv, UB960_SR_FPD_RATE_CFG, 0x3 << (nport * 2), + ub960_update_bits(priv, UB9702_SR_FPD_RATE_CFG, 0x3 << (nport * 2), BIT(nport * 2), &ret); return ret; @@ -2285,7 +2285,7 @@ static int ub960_init_rx_port_ub9702_fpd4(struct ub960_data *priv, bc_freq_val, &ret); /* FPD4 Sync Mode */ - ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, 0, &ret); + ub960_rxport_write(priv, nport, UB9702_RR_CHANNEL_MODE, 0, &ret); /* add serdes_eq_offset of 4 */ ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04, @@ -2312,7 +2312,7 @@ static int ub960_init_rx_port_ub9702_fpd4(struct ub960_data *priv, &ret); /* RX port to 7.55G mode */ - ub960_update_bits(priv, UB960_SR_FPD_RATE_CFG, 0x3 << (nport * 2), + ub960_update_bits(priv, UB9702_SR_FPD_RATE_CFG, 0x3 << (nport * 2), 0 << (nport * 2), &ret); if (ret) @@ -2786,7 +2786,7 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, /* Map all VCs from this port to VC(nport) */ for (i = 0; i < 8; i++) ub960_rxport_write(priv, nport, - UB960_RR_VC_ID_MAP(i), + UB9702_RR_VC_ID_MAP(i), (nport << 4) | nport, &ret); } -- cgit v1.2.3-59-g8ed1b From 21a22b0febad2d029d5c23947751dabb9973e83f Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:13 +0530 Subject: media: i2c: ds90ub960: Add UB9702 specific registers Add UB9702 specific registers which will be used in the following patches. Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub960.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index f9af6d643ac8..c56398aa895f 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -391,12 +391,47 @@ /* UB9702 Registers */ +#define UB9702_SR_CSI_EXCLUSIVE_FWD2 0x3c #define UB9702_SR_REFCLK_FREQ 0x3d +#define UB9702_RR_RX_CTL_1 0x80 +#define UB9702_RR_RX_CTL_2 0x87 #define UB9702_RR_VC_ID_MAP(x) (0xa0 + (x)) #define UB9702_SR_FPD_RATE_CFG 0xc2 #define UB9702_SR_CSI_PLL_DIV 0xc9 +#define UB9702_RR_RX_SM_SEL_2 0xd4 #define UB9702_RR_CHANNEL_MODE 0xe4 +#define UB9702_IND_TARGET_SAR_ADC 0x0a + +#define UB9702_IR_RX_ANA_FPD_BC_CTL0 0x04 +#define UB9702_IR_RX_ANA_FPD_BC_CTL1 0x0d +#define UB9702_IR_RX_ANA_FPD_BC_CTL2 0x1b +#define UB9702_IR_RX_ANA_SYSTEM_INIT_REG0 0x21 +#define UB9702_IR_RX_ANA_AEQ_ALP_SEL6 0x27 +#define UB9702_IR_RX_ANA_AEQ_ALP_SEL7 0x28 +#define UB9702_IR_RX_ANA_AEQ_ALP_SEL10 0x2b +#define UB9702_IR_RX_ANA_AEQ_ALP_SEL11 0x2c +#define UB9702_IR_RX_ANA_EQ_ADAPT_CTRL 0x2e +#define UB9702_IR_RX_ANA_AEQ_CFG_1 0x34 +#define UB9702_IR_RX_ANA_AEQ_CFG_2 0x4d +#define UB9702_IR_RX_ANA_GAIN_CTRL_0 0x71 +#define UB9702_IR_RX_ANA_GAIN_CTRL_0 0x71 +#define UB9702_IR_RX_ANA_VGA_CTRL_SEL_1 0x72 +#define UB9702_IR_RX_ANA_VGA_CTRL_SEL_2 0x73 +#define UB9702_IR_RX_ANA_VGA_CTRL_SEL_3 0x74 +#define UB9702_IR_RX_ANA_VGA_CTRL_SEL_6 0x77 +#define UB9702_IR_RX_ANA_AEQ_CFG_3 0x79 +#define UB9702_IR_RX_ANA_AEQ_CFG_4 0x85 +#define UB9702_IR_RX_ANA_EQ_CTRL_SEL_15 0x87 +#define UB9702_IR_RX_ANA_EQ_CTRL_SEL_24 0x90 +#define UB9702_IR_RX_ANA_EQ_CTRL_SEL_38 0x9e +#define UB9702_IR_RX_ANA_FPD3_CDR_CTRL_SEL_5 0xa5 +#define UB9702_IR_RX_ANA_FPD3_AEQ_CTRL_SEL_1 0xa8 +#define UB9702_IR_RX_ANA_EQ_OVERRIDE_CTRL 0xf0 +#define UB9702_IR_RX_ANA_VGA_CTRL_SEL_8 0xf1 + +#define UB9702_IR_CSI_ANA_CSIPLL_REG_1 0x92 + /* EQ related */ #define UB960_MIN_AEQ_STROBE_POS -7 -- cgit v1.2.3-59-g8ed1b From 43635b661ef9931b57e33593d13570b0ab5ccec1 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:14 +0530 Subject: media: i2c: ds90ub960: Split ub960_init_tx_ports() Split ub960_init_tx_ports() to a UB960 and a UB9702 versions to make it easier to update the UB9702 version in the following patch. No funcional changes. Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub960.c | 105 +++++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 37 deletions(-) diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index c56398aa895f..579ca570a543 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -1992,67 +1992,98 @@ static int ub960_init_tx_port(struct ub960_data *priv, return ub960_txport_write(priv, nport, UB960_TR_CSI_CTL, csi_ctl, NULL); } -static int ub960_init_tx_ports(struct ub960_data *priv) +static int ub960_init_tx_ports_ub960(struct ub960_data *priv) { - unsigned int nport; u8 speed_select; - u8 pll_div; - int ret = 0; - - /* TX ports */ switch (priv->tx_data_rate) { + case MHZ(400): + speed_select = 3; + break; + case MHZ(800): + speed_select = 2; + break; + case MHZ(1200): + speed_select = 1; + break; case MHZ(1600): default: speed_select = 0; - pll_div = 0x10; break; - case MHZ(1200): - speed_select = 1; - pll_div = 0x18; + } + + return ub960_write(priv, UB960_SR_CSI_PLL_CTL, speed_select, NULL); +} + +static int ub960_init_tx_ports_ub9702(struct ub960_data *priv) +{ + u8 speed_select; + u8 pll_div; + int ret = 0; + + switch (priv->tx_data_rate) { + case MHZ(400): + speed_select = 3; + pll_div = 0x10; break; case MHZ(800): speed_select = 2; pll_div = 0x10; break; - case MHZ(400): - speed_select = 3; + case MHZ(1200): + speed_select = 1; + pll_div = 0x18; + break; + case MHZ(1600): + default: + speed_select = 0; pll_div = 0x10; break; } ub960_write(priv, UB960_SR_CSI_PLL_CTL, speed_select, &ret); - if (priv->hw_data->is_ub9702) { - ub960_write(priv, UB9702_SR_CSI_PLL_DIV, pll_div, &ret); - - switch (priv->tx_data_rate) { - case MHZ(1600): - default: - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, - 0x80, &ret); - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, - 0x2a, &ret); - break; - case MHZ(800): - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, - 0x90, &ret); - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4f, - 0x2a, &ret); - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, - 0x2a, &ret); - break; - case MHZ(400): - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, - 0xa0, &ret); - break; - } + ub960_write(priv, UB9702_SR_CSI_PLL_DIV, pll_div, &ret); + + switch (priv->tx_data_rate) { + case MHZ(1600): + default: + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0x80, + &ret); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, 0x2a, + &ret); + break; + case MHZ(800): + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0x90, + &ret); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4f, 0x2a, + &ret); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, 0x2a, + &ret); + break; + case MHZ(400): + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0xa0, + &ret); + break; } + return ret; +} + +static int ub960_init_tx_ports(struct ub960_data *priv) +{ + int ret; + + if (priv->hw_data->is_ub9702) + ret = ub960_init_tx_ports_ub9702(priv); + else + ret = ub960_init_tx_ports_ub960(priv); + if (ret) return ret; - for (nport = 0; nport < priv->hw_data->num_txports; nport++) { + for (unsigned int nport = 0; nport < priv->hw_data->num_txports; + nport++) { struct ub960_txport *txport = priv->txports[nport]; if (!txport) -- cgit v1.2.3-59-g8ed1b From 42a44838d5b50d75e534efa29aca2b49e8de3400 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:15 +0530 Subject: media: i2c: ds90ub960: Refresh ub960_init_tx_ports_ub9702() Refresh the ub960_init_tx_ports_ub9702() using the latest version of the (non-public) hardware documentation. Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub960.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 579ca570a543..ed49508d2b79 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -2018,6 +2018,7 @@ static int ub960_init_tx_ports_ub960(struct ub960_data *priv) static int ub960_init_tx_ports_ub9702(struct ub960_data *priv) { u8 speed_select; + u8 ana_pll_div; u8 pll_div; int ret = 0; @@ -2025,47 +2026,40 @@ static int ub960_init_tx_ports_ub9702(struct ub960_data *priv) case MHZ(400): speed_select = 3; pll_div = 0x10; + ana_pll_div = 0xa2; break; case MHZ(800): speed_select = 2; pll_div = 0x10; + ana_pll_div = 0x92; break; case MHZ(1200): speed_select = 1; pll_div = 0x18; + ana_pll_div = 0x90; + break; + case MHZ(1500): + speed_select = 0; + pll_div = 0x0f; + ana_pll_div = 0x82; break; case MHZ(1600): default: speed_select = 0; pll_div = 0x10; + ana_pll_div = 0x82; + break; + case MHZ(2500): + speed_select = 0x10; + pll_div = 0x19; + ana_pll_div = 0x80; break; } ub960_write(priv, UB960_SR_CSI_PLL_CTL, speed_select, &ret); - ub960_write(priv, UB9702_SR_CSI_PLL_DIV, pll_div, &ret); - - switch (priv->tx_data_rate) { - case MHZ(1600): - default: - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0x80, - &ret); - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, 0x2a, - &ret); - break; - case MHZ(800): - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0x90, - &ret); - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4f, 0x2a, - &ret); - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, 0x2a, - &ret); - break; - case MHZ(400): - ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0xa0, - &ret); - break; - } + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, + UB9702_IR_CSI_ANA_CSIPLL_REG_1, ana_pll_div, &ret); return ret; } -- cgit v1.2.3-59-g8ed1b From 2ca499384e98e79f02bea6cc4bde9b3612bace04 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:16 +0530 Subject: media: i2c: ds90ub960: Add RX port iteration support The driver does a lot of iteration over the RX ports with for loops. In most cases the driver will skip unused RX ports. Also, in the future patches the FPD-Link IV support will be refreshed with TI's latest init sequences which involves a lot of additional iterations over the RX ports, often only for FPD-Link IV ports. To make the iteration simpler and to make it clearer what we're iterating over (all or only-active, all or only-fpd4), add macros and support functions for iterating the RX ports. Use the macros in the driver, replacing the for loops. Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub960.c | 260 ++++++++++++++++++++++-------------------- 1 file changed, 135 insertions(+), 125 deletions(-) diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index ed49508d2b79..af7ba1c824b1 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -649,6 +649,63 @@ static const struct ub960_format_info *ub960_find_format(u32 code) return NULL; } +struct ub960_rxport_iter { + unsigned int nport; + struct ub960_rxport *rxport; +}; + +enum ub960_iter_flags { + UB960_ITER_ACTIVE_ONLY = BIT(0), + UB960_ITER_FPD4_ONLY = BIT(1), +}; + +static struct ub960_rxport_iter ub960_iter_rxport(struct ub960_data *priv, + struct ub960_rxport_iter it, + enum ub960_iter_flags flags) +{ + for (; it.nport < priv->hw_data->num_rxports; it.nport++) { + it.rxport = priv->rxports[it.nport]; + + if ((flags & UB960_ITER_ACTIVE_ONLY) && !it.rxport) + continue; + + if ((flags & UB960_ITER_FPD4_ONLY) && + it.rxport->cdr_mode != RXPORT_CDR_FPD4) + continue; + + return it; + } + + it.rxport = NULL; + + return it; +} + +#define for_each_rxport(priv, it) \ + for (struct ub960_rxport_iter it = \ + ub960_iter_rxport(priv, (struct ub960_rxport_iter){ 0 }, \ + 0); \ + it.nport < (priv)->hw_data->num_rxports; \ + it.nport++, it = ub960_iter_rxport(priv, it, 0)) + +#define for_each_active_rxport(priv, it) \ + for (struct ub960_rxport_iter it = \ + ub960_iter_rxport(priv, (struct ub960_rxport_iter){ 0 }, \ + UB960_ITER_ACTIVE_ONLY); \ + it.nport < (priv)->hw_data->num_rxports; \ + it.nport++, it = ub960_iter_rxport(priv, it, \ + UB960_ITER_ACTIVE_ONLY)) + +#define for_each_active_rxport_fpd4(priv, it) \ + for (struct ub960_rxport_iter it = \ + ub960_iter_rxport(priv, (struct ub960_rxport_iter){ 0 }, \ + UB960_ITER_ACTIVE_ONLY | \ + UB960_ITER_FPD4_ONLY); \ + it.nport < (priv)->hw_data->num_rxports; \ + it.nport++, it = ub960_iter_rxport(priv, it, \ + UB960_ITER_ACTIVE_ONLY | \ + UB960_ITER_FPD4_ONLY)) + /* ----------------------------------------------------------------------------- * Basic device access */ @@ -1356,25 +1413,25 @@ static int ub960_csi_handle_events(struct ub960_data *priv, u8 nport) static int ub960_rxport_enable_vpocs(struct ub960_data *priv) { - unsigned int nport; + unsigned int failed_nport; int ret; - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; - - if (!rxport || !rxport->vpoc) + for_each_active_rxport(priv, it) { + if (!it.rxport->vpoc) continue; - ret = regulator_enable(rxport->vpoc); - if (ret) + ret = regulator_enable(it.rxport->vpoc); + if (ret) { + failed_nport = it.nport; goto err_disable_vpocs; + } } return 0; err_disable_vpocs: - while (nport--) { - struct ub960_rxport *rxport = priv->rxports[nport]; + while (failed_nport--) { + struct ub960_rxport *rxport = priv->rxports[failed_nport]; if (!rxport || !rxport->vpoc) continue; @@ -1387,15 +1444,11 @@ err_disable_vpocs: static void ub960_rxport_disable_vpocs(struct ub960_data *priv) { - unsigned int nport; - - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; - - if (!rxport || !rxport->vpoc) + for_each_active_rxport(priv, it) { + if (!it.rxport->vpoc) continue; - regulator_disable(rxport->vpoc); + regulator_disable(it.rxport->vpoc); } } @@ -1420,12 +1473,10 @@ static int ub960_rxport_clear_errors(struct ub960_data *priv, static int ub960_clear_rx_errors(struct ub960_data *priv) { - unsigned int nport; - - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - int ret; + int ret; - ret = ub960_rxport_clear_errors(priv, nport); + for_each_rxport(priv, it) { + ret = ub960_rxport_clear_errors(priv, it.nport); if (ret) return ret; } @@ -1928,30 +1979,27 @@ static void ub960_rxport_remove_serializer(struct ub960_data *priv, u8 nport) /* Add serializer i2c devices for all initialized ports */ static int ub960_rxport_add_serializers(struct ub960_data *priv) { - unsigned int nport; + unsigned int failed_nport; int ret; - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; - - if (!rxport) - continue; - - ret = ub960_rxport_add_serializer(priv, nport); - if (ret) + for_each_active_rxport(priv, it) { + ret = ub960_rxport_add_serializer(priv, it.nport); + if (ret) { + failed_nport = it.nport; goto err_remove_sers; + } } return 0; err_remove_sers: - while (nport--) { - struct ub960_rxport *rxport = priv->rxports[nport]; + while (failed_nport--) { + struct ub960_rxport *rxport = priv->rxports[failed_nport]; if (!rxport) continue; - ub960_rxport_remove_serializer(priv, nport); + ub960_rxport_remove_serializer(priv, failed_nport); } return ret; @@ -1959,16 +2007,8 @@ err_remove_sers: static void ub960_rxport_remove_serializers(struct ub960_data *priv) { - unsigned int nport; - - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; - - if (!rxport) - continue; - - ub960_rxport_remove_serializer(priv, nport); - } + for_each_active_rxport(priv, it) + ub960_rxport_remove_serializer(priv, it.nport); } static int ub960_init_tx_port(struct ub960_data *priv, @@ -2453,19 +2493,13 @@ static int ub960_init_rx_port_ub9702(struct ub960_data *priv, static int ub960_init_rx_ports(struct ub960_data *priv) { - unsigned int nport; - - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; + for_each_active_rxport(priv, it) { int ret; - if (!rxport) - continue; - if (priv->hw_data->is_ub9702) - ret = ub960_init_rx_port_ub9702(priv, rxport); + ret = ub960_init_rx_port_ub9702(priv, it.rxport); else - ret = ub960_init_rx_port_ub960(priv, rxport); + ret = ub960_init_rx_port_ub960(priv, it.rxport); if (ret) return ret; @@ -2683,20 +2717,14 @@ static int ub960_disable_rx_port(struct ub960_data *priv, unsigned int nport) */ static int ub960_validate_stream_vcs(struct ub960_data *priv) { - unsigned int nport; - unsigned int i; - - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; + for_each_active_rxport(priv, it) { struct v4l2_mbus_frame_desc desc; int ret; u8 vc; - if (!rxport) - continue; - - ret = v4l2_subdev_call(rxport->source.sd, pad, get_frame_desc, - rxport->source.pad, &desc); + ret = v4l2_subdev_call(it.rxport->source.sd, pad, + get_frame_desc, it.rxport->source.pad, + &desc); if (ret) return ret; @@ -2708,13 +2736,13 @@ static int ub960_validate_stream_vcs(struct ub960_data *priv) vc = desc.entry[0].bus.csi2.vc; - for (i = 1; i < desc.num_entries; i++) { + for (unsigned int i = 1; i < desc.num_entries; i++) { if (vc == desc.entry[i].bus.csi2.vc) continue; dev_err(&priv->client->dev, "rx%u: source with multiple virtual-channels is not supported\n", - nport); + it.nport); return -ENODEV; } } @@ -2804,21 +2832,21 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, */ fwd_ctl = GENMASK(7, 4); - for (unsigned int nport = 0; nport < priv->hw_data->num_rxports; - nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; + for_each_active_rxport(priv, it) { + unsigned long nport = it.nport; + u8 vc = vc_map[nport]; if (rx_data[nport].num_streams == 0) continue; - switch (rxport->rx_mode) { + switch (it.rxport->rx_mode) { case RXPORT_MODE_RAW10: ub960_rxport_write(priv, nport, UB960_RR_RAW10_ID, rx_data[nport].pixel_dt | (vc << UB960_RR_RAW10_ID_VC_SHIFT), &ret); - ub960_rxport_write(priv, rxport->nport, + ub960_rxport_write(priv, nport, UB960_RR_RAW_EMBED_DTYPE, (rx_data[nport].meta_lines << UB960_RR_RAW_EMBED_DTYPE_LINES_SHIFT) | rx_data[nport].meta_dt, &ret); @@ -2886,7 +2914,6 @@ static int ub960_enable_streams(struct v4l2_subdev *sd, u64 sink_streams[UB960_MAX_RX_NPORTS] = {}; struct v4l2_subdev_route *route; unsigned int failed_port; - unsigned int nport; int ret; if (!priv->streaming) { @@ -2908,6 +2935,8 @@ static int ub960_enable_streams(struct v4l2_subdev *sd, /* Collect sink streams per pad which we need to enable */ for_each_active_route(&state->routing, route) { + unsigned int nport; + if (route->source_pad != source_pad) continue; @@ -2919,7 +2948,9 @@ static int ub960_enable_streams(struct v4l2_subdev *sd, sink_streams[nport] |= BIT_ULL(route->sink_stream); } - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + for_each_rxport(priv, it) { + unsigned int nport = it.nport; + if (!sink_streams[nport]) continue; @@ -2957,7 +2988,7 @@ static int ub960_enable_streams(struct v4l2_subdev *sd, return 0; err: - for (nport = 0; nport < failed_port; nport++) { + for (unsigned int nport = 0; nport < failed_port; nport++) { if (!sink_streams[nport]) continue; @@ -2997,11 +3028,12 @@ static int ub960_disable_streams(struct v4l2_subdev *sd, struct device *dev = &priv->client->dev; u64 sink_streams[UB960_MAX_RX_NPORTS] = {}; struct v4l2_subdev_route *route; - unsigned int nport; int ret; /* Collect sink streams per pad which we need to disable */ for_each_active_route(&state->routing, route) { + unsigned int nport; + if (route->source_pad != source_pad) continue; @@ -3013,7 +3045,9 @@ static int ub960_disable_streams(struct v4l2_subdev *sd, sink_streams[nport] |= BIT_ULL(route->sink_stream); } - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + for_each_rxport(priv, it) { + unsigned int nport = it.nport; + if (!sink_streams[nport]) continue; @@ -3335,7 +3369,6 @@ static int ub960_log_status(struct v4l2_subdev *sd) struct ub960_data *priv = sd_to_ub960(sd); struct device *dev = &priv->client->dev; struct v4l2_subdev_state *state; - unsigned int nport; u16 v16 = 0; u8 v = 0; u8 id[UB960_SR_FPD3_RX_ID_LEN]; @@ -3351,7 +3384,8 @@ static int ub960_log_status(struct v4l2_subdev *sd) dev_info(dev, "ID '%.*s'\n", (int)sizeof(id), id); - for (nport = 0; nport < priv->hw_data->num_txports; nport++) { + for (unsigned int nport = 0; nport < priv->hw_data->num_txports; + nport++) { struct ub960_txport *txport = priv->txports[nport]; dev_info(dev, "TX %u\n", nport); @@ -3397,12 +3431,12 @@ static int ub960_log_status(struct v4l2_subdev *sd) dev_info(dev, "\tline error counter %u\n", v16); } - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; + for_each_rxport(priv, it) { + unsigned int nport = it.nport; dev_info(dev, "RX %u\n", nport); - if (!rxport) { + if (!it.rxport) { dev_info(dev, "\tNot initialized\n"); continue; } @@ -3514,7 +3548,6 @@ static const struct media_entity_operations ub960_entity_ops = { static irqreturn_t ub960_handle_events(int irq, void *arg) { struct ub960_data *priv = arg; - unsigned int i; u8 int_sts; u8 fwd_sts; int ret; @@ -3531,7 +3564,7 @@ static irqreturn_t ub960_handle_events(int irq, void *arg) dev_dbg(&priv->client->dev, "FWD_STS %#02x\n", fwd_sts); - for (i = 0; i < priv->hw_data->num_txports; i++) { + for (unsigned int i = 0; i < priv->hw_data->num_txports; i++) { if (int_sts & UB960_SR_INTERRUPT_STS_IS_CSI_TX(i)) { ret = ub960_csi_handle_events(priv, i); if (ret) @@ -3539,12 +3572,9 @@ static irqreturn_t ub960_handle_events(int irq, void *arg) } } - for (i = 0; i < priv->hw_data->num_rxports; i++) { - if (!priv->rxports[i]) - continue; - - if (int_sts & UB960_SR_INTERRUPT_STS_IS_RX(i)) { - ret = ub960_rxport_handle_events(priv, i); + for_each_active_rxport(priv, it) { + if (int_sts & UB960_SR_INTERRUPT_STS_IS_RX(it.nport)) { + ret = ub960_rxport_handle_events(priv, it.nport); if (ret) return IRQ_NONE; } @@ -3582,19 +3612,12 @@ static void ub960_txport_free_ports(struct ub960_data *priv) static void ub960_rxport_free_ports(struct ub960_data *priv) { - unsigned int nport; - - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; - - if (!rxport) - continue; - - fwnode_handle_put(rxport->source.ep_fwnode); - fwnode_handle_put(rxport->ser.fwnode); + for_each_active_rxport(priv, it) { + fwnode_handle_put(it.rxport->source.ep_fwnode); + fwnode_handle_put(it.rxport->ser.fwnode); - kfree(rxport); - priv->rxports[nport] = NULL; + kfree(it.rxport); + priv->rxports[it.nport] = NULL; } } @@ -3853,7 +3876,6 @@ static int ub960_parse_dt_rxports(struct ub960_data *priv) { struct device *dev = &priv->client->dev; struct fwnode_handle *links_fwnode; - unsigned int nport; int ret; links_fwnode = fwnode_get_named_child_node(dev_fwnode(dev), "links"); @@ -3868,9 +3890,10 @@ static int ub960_parse_dt_rxports(struct ub960_data *priv) priv->strobe.manual = fwnode_property_read_bool(links_fwnode, "ti,manual-strobe"); - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + for_each_rxport(priv, it) { struct fwnode_handle *link_fwnode; struct fwnode_handle *ep_fwnode; + unsigned int nport = it.nport; link_fwnode = ub960_fwnode_get_link_by_regs(links_fwnode, nport); if (!link_fwnode) @@ -3959,7 +3982,6 @@ static int ub960_notify_bound(struct v4l2_async_notifier *notifier, struct ub960_rxport *rxport = to_ub960_asd(asd)->rxport; struct device *dev = &priv->client->dev; u8 nport = rxport->nport; - unsigned int i; int ret; ret = media_entity_get_fwnode_pad(&subdev->entity, @@ -3984,8 +4006,8 @@ static int ub960_notify_bound(struct v4l2_async_notifier *notifier, return ret; } - for (i = 0; i < priv->hw_data->num_rxports; i++) { - if (priv->rxports[i] && !priv->rxports[i]->source.sd) { + for_each_active_rxport(priv, it) { + if (!it.rxport->source.sd) { dev_dbg(dev, "Waiting for more subdevs to be bound\n"); return 0; } @@ -4011,29 +4033,24 @@ static const struct v4l2_async_notifier_operations ub960_notify_ops = { static int ub960_v4l2_notifier_register(struct ub960_data *priv) { struct device *dev = &priv->client->dev; - unsigned int i; int ret; v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); - for (i = 0; i < priv->hw_data->num_rxports; i++) { - struct ub960_rxport *rxport = priv->rxports[i]; + for_each_active_rxport(priv, it) { struct ub960_asd *asd; - if (!rxport) - continue; - asd = v4l2_async_nf_add_fwnode(&priv->notifier, - rxport->source.ep_fwnode, + it.rxport->source.ep_fwnode, struct ub960_asd); if (IS_ERR(asd)) { dev_err(dev, "Failed to add subdev for source %u: %pe", - i, asd); + it.nport, asd); v4l2_async_nf_cleanup(&priv->notifier); return PTR_ERR(asd); } - asd->rxport = rxport; + asd->rxport = it.rxport; } priv->notifier.ops = &ub960_notify_ops; @@ -4304,7 +4321,6 @@ static int ub960_probe(struct i2c_client *client) struct ub960_data *priv; unsigned int port_lock_mask; unsigned int port_mask; - unsigned int nport; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -4357,14 +4373,8 @@ static int ub960_probe(struct i2c_client *client) port_mask = 0; - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { - struct ub960_rxport *rxport = priv->rxports[nport]; - - if (!rxport) - continue; - - port_mask |= BIT(nport); - } + for_each_active_rxport(priv, it) + port_mask |= BIT(it.nport); ret = ub960_rxport_wait_locks(priv, port_mask, &port_lock_mask); if (ret) @@ -4403,9 +4413,9 @@ static int ub960_probe(struct i2c_client *client) msecs_to_jiffies(UB960_POLL_TIME_MS)); #ifdef UB960_DEBUG_I2C_RX_ID - for (unsigned int i = 0; i < priv->hw_data->num_rxports; i++) - ub960_write(priv, UB960_SR_I2C_RX_ID(i), - (UB960_DEBUG_I2C_RX_ID + i) << 1, NULL); + for_each_rxport(priv, it) + ub960_write(priv, UB960_SR_I2C_RX_ID(it.nport), + (UB960_DEBUG_I2C_RX_ID + it.nport) << 1, NULL); #endif return 0; -- cgit v1.2.3-59-g8ed1b From ac7c808b1cb2b6745a5204c91f170634974c9e24 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:17 +0530 Subject: media: i2c: ds90ub960: Move all RX port init code into ub960_init_rx_ports() We have some code in probe() which is related to RX port initialization, and should be in ub960_init_rx_ports(). Move the code there. We also move ub960_reset() so that it is accessible from ub960_init_rx_ports(). Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub960.c | 115 ++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index af7ba1c824b1..130bf4cc0a74 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -1225,6 +1225,33 @@ out_unlock: return ret; } +static int ub960_reset(struct ub960_data *priv, bool reset_regs) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + u8 bit; + + bit = reset_regs ? UB960_SR_RESET_DIGITAL_RESET1 : + UB960_SR_RESET_DIGITAL_RESET0; + + ret = ub960_write(priv, UB960_SR_RESET, bit, NULL); + if (ret) + return ret; + + mutex_lock(&priv->reg_lock); + + ret = regmap_read_poll_timeout(priv->regmap, UB960_SR_RESET, v, + (v & bit) == 0, 2000, 100000); + + mutex_unlock(&priv->reg_lock); + + if (ret) + dev_err(dev, "reset failed: %d\n", ret); + + return ret; +} + /* ----------------------------------------------------------------------------- * I2C-ATR (address translator) */ @@ -2493,6 +2520,11 @@ static int ub960_init_rx_port_ub9702(struct ub960_data *priv, static int ub960_init_rx_ports(struct ub960_data *priv) { + struct device *dev = &priv->client->dev; + unsigned int port_lock_mask; + unsigned int port_mask; + int ret; + for_each_active_rxport(priv, it) { int ret; @@ -2505,6 +2537,33 @@ static int ub960_init_rx_ports(struct ub960_data *priv) return ret; } + ret = ub960_reset(priv, false); + if (ret) + return ret; + + port_mask = 0; + + for_each_active_rxport(priv, it) + port_mask |= BIT(it.nport); + + ret = ub960_rxport_wait_locks(priv, port_mask, &port_lock_mask); + if (ret) + return ret; + + if (port_mask != port_lock_mask) { + ret = -EIO; + dev_err_probe(dev, ret, "Failed to lock all RX ports\n"); + return ret; + } + + /* + * Clear any errors caused by switching the RX port settings while + * probing. + */ + ret = ub960_clear_rx_errors(priv); + if (ret) + return ret; + return 0; } @@ -4168,33 +4227,6 @@ static const struct regmap_config ub960_regmap_config = { .disable_locking = true, }; -static int ub960_reset(struct ub960_data *priv, bool reset_regs) -{ - struct device *dev = &priv->client->dev; - unsigned int v; - int ret; - u8 bit; - - bit = reset_regs ? UB960_SR_RESET_DIGITAL_RESET1 : - UB960_SR_RESET_DIGITAL_RESET0; - - ret = ub960_write(priv, UB960_SR_RESET, bit, NULL); - if (ret) - return ret; - - mutex_lock(&priv->reg_lock); - - ret = regmap_read_poll_timeout(priv->regmap, UB960_SR_RESET, v, - (v & bit) == 0, 2000, 100000); - - mutex_unlock(&priv->reg_lock); - - if (ret) - dev_err(dev, "reset failed: %d\n", ret); - - return ret; -} - static int ub960_get_hw_resources(struct ub960_data *priv) { struct device *dev = &priv->client->dev; @@ -4319,8 +4351,6 @@ static int ub960_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ub960_data *priv; - unsigned int port_lock_mask; - unsigned int port_mask; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -4367,33 +4397,6 @@ static int ub960_probe(struct i2c_client *client) if (ret) goto err_disable_vpocs; - ret = ub960_reset(priv, false); - if (ret) - goto err_disable_vpocs; - - port_mask = 0; - - for_each_active_rxport(priv, it) - port_mask |= BIT(it.nport); - - ret = ub960_rxport_wait_locks(priv, port_mask, &port_lock_mask); - if (ret) - goto err_disable_vpocs; - - if (port_mask != port_lock_mask) { - ret = -EIO; - dev_err_probe(dev, ret, "Failed to lock all RX ports\n"); - goto err_disable_vpocs; - } - - /* - * Clear any errors caused by switching the RX port settings while - * probing. - */ - ret = ub960_clear_rx_errors(priv); - if (ret) - goto err_disable_vpocs; - ret = ub960_init_atr(priv); if (ret) goto err_disable_vpocs; -- cgit v1.2.3-59-g8ed1b From 32cc18622d026b21ed328ad992180dc2393d1fdd Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:18 +0530 Subject: media: i2c: ds90ub960: Update UB9702 init sequences Update ub9702 RX port init sequence according to TI's latest (non-public) documentation. The sequence is based on a Python script provided by TI. Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub960.c | 848 ++++++++++++++++++++++++++++++++---------- 1 file changed, 658 insertions(+), 190 deletions(-) diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 130bf4cc0a74..56b1a88c1c29 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -243,6 +243,7 @@ #define UB960_RR_BIST_ERR_COUNT 0x57 #define UB960_RR_BCC_CONFIG 0x58 +#define UB960_RR_BCC_CONFIG_BC_ALWAYS_ON BIT(4) #define UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH BIT(6) #define UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK GENMASK(2, 0) @@ -1788,6 +1789,23 @@ static int ub960_rxport_link_ok(struct ub960_data *priv, unsigned int nport, return 0; } +static int ub960_rxport_lockup_wa_ub9702(struct ub960_data *priv) +{ + int ret; + + /* Toggle PI_MODE to avoid possible FPD RX lockup */ + + ret = ub960_update_bits(priv, UB9702_RR_CHANNEL_MODE, GENMASK(4, 3), + 2 << 3, NULL); + if (ret) + return ret; + + usleep_range(1000, 5000); + + return ub960_update_bits(priv, UB9702_RR_CHANNEL_MODE, GENMASK(4, 3), + 0, NULL); +} + /* * Wait for the RX ports to lock, have no errors and have stable strobe position * and EQ level. @@ -1818,6 +1836,7 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv, link_ok_mask = 0; while (time_before(jiffies, timeout)) { + bool fpd4_wa = false; missing = 0; for_each_set_bit(nport, &port_mask, @@ -1832,6 +1851,9 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv, if (ret) return ret; + if (!ok && rxport->cdr_mode == RXPORT_CDR_FPD4) + fpd4_wa = true; + /* * We want the link to be ok for two consecutive loops, * as a link could get established just before our test @@ -1851,6 +1873,12 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv, if (missing == 0) break; + if (fpd4_wa) { + ret = ub960_rxport_lockup_wa_ub9702(priv); + if (ret) + return ret; + } + /* * The sleep time of 10 ms was found by testing to give a lock * with a few iterations. It can be decreased if on some setups @@ -2257,294 +2285,691 @@ static int ub960_init_rx_port_ub960(struct ub960_data *priv, return ret; } -static int ub960_init_rx_port_ub9702_fpd3(struct ub960_data *priv, - struct ub960_rxport *rxport) +static int ub960_init_rx_ports_ub960(struct ub960_data *priv) { - unsigned int nport = rxport->nport; - u8 bc_freq_val; - u8 fpd_func_mode; - int ret = 0; + struct device *dev = &priv->client->dev; + unsigned int port_lock_mask; + unsigned int port_mask; + int ret; - switch (rxport->rx_mode) { - case RXPORT_MODE_RAW10: - bc_freq_val = 0; - fpd_func_mode = 5; - break; + for_each_active_rxport(priv, it) { + ret = ub960_init_rx_port_ub960(priv, it.rxport); + if (ret) + return ret; + } - case RXPORT_MODE_RAW12_HF: - bc_freq_val = 0; - fpd_func_mode = 4; - break; + ret = ub960_reset(priv, false); + if (ret) + return ret; - case RXPORT_MODE_RAW12_LF: - bc_freq_val = 0; - fpd_func_mode = 6; - break; + port_mask = 0; - case RXPORT_MODE_CSI2_SYNC: - bc_freq_val = 6; - fpd_func_mode = 2; - break; + for_each_active_rxport(priv, it) + port_mask |= BIT(it.nport); - case RXPORT_MODE_CSI2_NONSYNC: - bc_freq_val = 2; - fpd_func_mode = 2; - break; + ret = ub960_rxport_wait_locks(priv, port_mask, &port_lock_mask); + if (ret) + return ret; - default: - return -EINVAL; + if (port_mask != port_lock_mask) { + ret = -EIO; + dev_err_probe(dev, ret, "Failed to lock all RX ports\n"); + return ret; } - ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, 0x7, - bc_freq_val, &ret); - ub960_rxport_write(priv, nport, UB9702_RR_CHANNEL_MODE, fpd_func_mode, - &ret); + /* + * Clear any errors caused by switching the RX port settings while + * probing. + */ + ret = ub960_clear_rx_errors(priv); + if (ret) + return ret; - /* set serdes_eq_mode = 1 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xa8, 0x80, - &ret); + return 0; +} - /* enable serdes driver */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x0d, 0x7f, - &ret); +/* + * UB9702 specific initial RX port configuration + */ - /* set serdes_eq_offset=4 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04, - &ret); +static int ub960_turn_off_rxport_ub9702(struct ub960_data *priv, + unsigned int nport) +{ + int ret = 0; - /* init default serdes_eq_max in 0xa9 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xa9, 0x23, - &ret); + /* Disable RX port */ + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), 0, &ret); + + /* Disable FPD Rx and FPD BC CMR */ + ub960_rxport_write(priv, nport, UB9702_RR_RX_CTL_2, 0x1b, &ret); + + /* Disable FPD BC Tx */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, BIT(4), 0, + &ret); + + /* Disable internal RX blocks */ + ub960_rxport_write(priv, nport, UB9702_RR_RX_CTL_1, 0x15, &ret); + + /* Disable AEQ */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_CFG_2, 0x03, &ret); - /* init serdes_eq_min in 0xaa */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xaa, 0, &ret); + /* PI disabled and oDAC disabled */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_CFG_4, 0x09, &ret); - /* serdes_driver_ctl2 control: DS90UB953-Q1/DS90UB933-Q1/DS90UB913A-Q1 */ - ub960_ind_update_bits(priv, UB960_IND_TARGET_RX_ANA(nport), 0x1b, - BIT(3), BIT(3), &ret); + /* AEQ configured for disabled link */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_CFG_1, 0x20, &ret); - /* RX port to half-rate */ - ub960_update_bits(priv, UB9702_SR_FPD_RATE_CFG, 0x3 << (nport * 2), - BIT(nport * 2), &ret); + /* disable AEQ clock and DFE */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_CFG_3, 0x45, &ret); + + /* Powerdown FPD3 CDR */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_FPD3_CDR_CTRL_SEL_5, 0x82, &ret); return ret; } -static int ub960_init_rx_port_ub9702_fpd4_aeq(struct ub960_data *priv, - struct ub960_rxport *rxport) +static int ub960_set_bc_drv_config_ub9702(struct ub960_data *priv, + unsigned int nport) { - unsigned int nport = rxport->nport; - bool first_time_power_up = true; + u8 fpd_bc_ctl0; + u8 fpd_bc_ctl1; + u8 fpd_bc_ctl2; int ret = 0; - if (first_time_power_up) { - u8 v; - - /* AEQ init */ - ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2c, &v, - &ret); + if (priv->rxports[nport]->cdr_mode == RXPORT_CDR_FPD4) { + /* Set FPD PBC drv into FPD IV mode */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, v, - &ret); - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x28, - v + 1, &ret); + fpd_bc_ctl0 = 0; + fpd_bc_ctl1 = 0; + fpd_bc_ctl2 = 0; + } else { + /* Set FPD PBC drv into FPD III mode */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, - 0x00, &ret); + fpd_bc_ctl0 = 2; + fpd_bc_ctl1 = 1; + fpd_bc_ctl2 = 5; } - /* enable serdes_eq_ctl2 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x9e, 0x00, - &ret); + ub960_ind_update_bits(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_FPD_BC_CTL0, GENMASK(7, 5), + fpd_bc_ctl0 << 5, &ret); - /* enable serdes_eq_ctl1 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x90, 0x40, - &ret); + ub960_ind_update_bits(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_FPD_BC_CTL1, BIT(6), + fpd_bc_ctl1 << 6, &ret); - /* enable serdes_eq_en */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2e, 0x40, - &ret); + ub960_ind_update_bits(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_FPD_BC_CTL2, GENMASK(6, 3), + fpd_bc_ctl2 << 3, &ret); - /* disable serdes_eq_override */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xf0, 0x00, - &ret); + return ret; +} - /* disable serdes_gain_override */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x71, 0x00, - &ret); +static int ub960_set_fpd4_sync_mode_ub9702(struct ub960_data *priv, + unsigned int nport) +{ + int ret = 0; + + /* FPD4 Sync Mode */ + ub960_rxport_write(priv, nport, UB9702_RR_CHANNEL_MODE, 0x0, &ret); + + /* BC_FREQ_SELECT = (PLL_FREQ/3200) Mbps */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK, 6, &ret); + + if (ret) + return ret; + + ret = ub960_set_bc_drv_config_ub9702(priv, nport); + if (ret) + return ret; + + /* Set AEQ timer to 400us/step */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_SYSTEM_INIT_REG0, 0x2f, &ret); + + /* Disable FPD4 Auto Recovery */ + ub960_update_bits(priv, UB9702_SR_CSI_EXCLUSIVE_FWD2, GENMASK(5, 4), 0, + &ret); + + /* Enable RX port */ + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport), + &ret); + + /* Enable FPD4 Auto Recovery */ + ub960_update_bits(priv, UB9702_SR_CSI_EXCLUSIVE_FWD2, GENMASK(5, 4), + BIT(4), &ret); return ret; } -static int ub960_init_rx_port_ub9702_fpd4(struct ub960_data *priv, - struct ub960_rxport *rxport) +static int ub960_set_fpd4_async_mode_ub9702(struct ub960_data *priv, + unsigned int nport) { - unsigned int nport = rxport->nport; - u8 bc_freq_val; int ret = 0; - switch (rxport->rx_mode) { - case RXPORT_MODE_RAW10: - bc_freq_val = 0; - break; + /* FPD4 ASync Mode */ + ub960_rxport_write(priv, nport, UB9702_RR_CHANNEL_MODE, 0x1, &ret); - case RXPORT_MODE_RAW12_HF: - bc_freq_val = 0; - break; + /* 10Mbps w/ BC enabled */ + /* BC_FREQ_SELECT=(PLL_FREQ/3200) Mbps */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK, 2, &ret); - case RXPORT_MODE_RAW12_LF: - bc_freq_val = 0; - break; + if (ret) + return ret; - case RXPORT_MODE_CSI2_SYNC: - bc_freq_val = 6; - break; + ret = ub960_set_bc_drv_config_ub9702(priv, nport); + if (ret) + return ret; - case RXPORT_MODE_CSI2_NONSYNC: - bc_freq_val = 2; - break; + /* Set AEQ timer to 400us/step */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_SYSTEM_INIT_REG0, 0x2f, &ret); - default: - return -EINVAL; - } + /* Disable FPD4 Auto Recover */ + ub960_update_bits(priv, UB9702_SR_CSI_EXCLUSIVE_FWD2, GENMASK(5, 4), 0, + &ret); - ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, 0x7, - bc_freq_val, &ret); + /* Enable RX port */ + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport), + &ret); - /* FPD4 Sync Mode */ - ub960_rxport_write(priv, nport, UB9702_RR_CHANNEL_MODE, 0, &ret); + /* Enable FPD4 Auto Recovery */ + ub960_update_bits(priv, UB9702_SR_CSI_EXCLUSIVE_FWD2, GENMASK(5, 4), + BIT(4), &ret); - /* add serdes_eq_offset of 4 */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04, - &ret); + return ret; +} - /* FPD4 serdes_start_eq in 0x27: assign default */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, 0x0, &ret); - /* FPD4 serdes_end_eq in 0x28: assign default */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x28, 0x23, - &ret); +static int ub960_set_fpd3_sync_mode_ub9702(struct ub960_data *priv, + unsigned int nport) +{ + int ret = 0; - /* set serdes_driver_mode into FPD IV mode */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x04, 0x00, - &ret); - /* set FPD PBC drv into FPD IV mode */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x1b, 0x00, - &ret); + /* FPD3 Sync Mode */ + ub960_rxport_write(priv, nport, UB9702_RR_CHANNEL_MODE, 0x2, &ret); - /* set serdes_system_init to 0x2f */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x21, 0x2f, - &ret); - /* set serdes_system_rst in reset mode */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x25, 0xc1, - &ret); + /* BC_FREQ_SELECT=(PLL_FREQ/3200) Mbps */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK, 6, &ret); - /* RX port to 7.55G mode */ - ub960_update_bits(priv, UB9702_SR_FPD_RATE_CFG, 0x3 << (nport * 2), - 0 << (nport * 2), &ret); + /* Set AEQ_LOCK_MODE = 1 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_FPD3_AEQ_CTRL_SEL_1, BIT(7), &ret); if (ret) return ret; - ret = ub960_init_rx_port_ub9702_fpd4_aeq(priv, rxport); + ret = ub960_set_bc_drv_config_ub9702(priv, nport); if (ret) return ret; - return 0; + /* Enable RX port */ + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport), + &ret); + + return ret; } -static int ub960_init_rx_port_ub9702(struct ub960_data *priv, - struct ub960_rxport *rxport) +static int ub960_set_raw10_dvp_mode_ub9702(struct ub960_data *priv, + unsigned int nport) { - unsigned int nport = rxport->nport; - int ret; + int ret = 0; - if (rxport->cdr_mode == RXPORT_CDR_FPD3) - ret = ub960_init_rx_port_ub9702_fpd3(priv, rxport); - else /* RXPORT_CDR_FPD4 */ - ret = ub960_init_rx_port_ub9702_fpd4(priv, rxport); + /* FPD3 RAW10 Mode */ + ub960_rxport_write(priv, nport, UB9702_RR_CHANNEL_MODE, 0x5, &ret); + + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK, 0, &ret); + + /* Set AEQ_LOCK_MODE = 1 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_FPD3_AEQ_CTRL_SEL_1, BIT(7), &ret); + + /* + * RAW10_8BIT_CTL = 0b11 : 8-bit processing using lower 8 bits + * 0b10 : 8-bit processing using upper 8 bits + */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3 << 6, + 0x2 << 6, &ret); + + /* LV_POLARITY & FV_POLARITY */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, + priv->rxports[nport]->lv_fv_pol, &ret); if (ret) return ret; - switch (rxport->rx_mode) { - case RXPORT_MODE_RAW10: - /* - * RAW10_8BIT_CTL = 0b11 : 8-bit processing using lower 8 bits - * 0b10 : 8-bit processing using upper 8 bits - */ - ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, - 0x3 << 6, 0x2 << 6, &ret); + ret = ub960_set_bc_drv_config_ub9702(priv, nport); + if (ret) + return ret; + + /* Enable RX port */ + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport), + &ret); + + return ret; +} + +static int ub960_configure_rx_port_ub9702(struct ub960_data *priv, + unsigned int nport) +{ + struct device *dev = &priv->client->dev; + struct ub960_rxport *rxport = priv->rxports[nport]; + int ret; + + if (!rxport) { + ret = ub960_turn_off_rxport_ub9702(priv, nport); + if (ret) + return ret; + dev_dbg(dev, "rx%u: disabled\n", nport); + return 0; + } + + switch (rxport->cdr_mode) { + case RXPORT_CDR_FPD4: + switch (rxport->rx_mode) { + case RXPORT_MODE_CSI2_SYNC: + ret = ub960_set_fpd4_sync_mode_ub9702(priv, nport); + if (ret) + return ret; + + dev_dbg(dev, "rx%u: FPD-Link IV SYNC mode\n", nport); + break; + case RXPORT_MODE_CSI2_NONSYNC: + ret = ub960_set_fpd4_async_mode_ub9702(priv, nport); + if (ret) + return ret; + + dev_dbg(dev, "rx%u: FPD-Link IV ASYNC mode\n", nport); + break; + default: + dev_err(dev, "rx%u: unsupported FPD4 mode %u\n", nport, + rxport->rx_mode); + return -EINVAL; + } break; - case RXPORT_MODE_RAW12_HF: - case RXPORT_MODE_RAW12_LF: - /* Not implemented */ - return -EINVAL; + case RXPORT_CDR_FPD3: + switch (rxport->rx_mode) { + case RXPORT_MODE_CSI2_SYNC: + ret = ub960_set_fpd3_sync_mode_ub9702(priv, nport); + if (ret) + return ret; - case RXPORT_MODE_CSI2_SYNC: - case RXPORT_MODE_CSI2_NONSYNC: + dev_dbg(dev, "rx%u: FPD-Link III SYNC mode\n", nport); + break; + case RXPORT_MODE_RAW10: + ret = ub960_set_raw10_dvp_mode_ub9702(priv, nport); + if (ret) + return ret; + dev_dbg(dev, "rx%u: FPD-Link III RAW10 DVP mode\n", + nport); + break; + default: + dev_err(&priv->client->dev, + "rx%u: unsupported FPD3 mode %u\n", nport, + rxport->rx_mode); + return -EINVAL; + } break; + + default: + dev_err(&priv->client->dev, "rx%u: unsupported CDR mode %u\n", + nport, rxport->cdr_mode); + return -EINVAL; } - /* LV_POLARITY & FV_POLARITY */ - ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, - rxport->lv_fv_pol, &ret); + return 0; +} - /* Enable all interrupt sources from this port */ - ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07, &ret); - ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f, &ret); +static int ub960_lock_recovery_ub9702(struct ub960_data *priv, + unsigned int nport) +{ + struct device *dev = &priv->client->dev; + /* Assumption that max AEQ should be under 16 */ + const u8 rx_aeq_limit = 16; + u8 prev_aeq = 0xff; + bool rx_lock; + + for (unsigned int retry = 0; retry < 3; ++retry) { + u8 port_sts1; + u8 rx_aeq; + int ret; - /* Enable I2C_PASS_THROUGH */ - ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, - UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, - UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, &ret); + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, + &port_sts1, NULL); + if (ret) + return ret; - /* Enable I2C communication to the serializer via the alias addr */ - ub960_rxport_write(priv, nport, UB960_RR_SER_ALIAS_ID, - rxport->ser.alias << 1, &ret); + rx_lock = port_sts1 & UB960_RR_RX_PORT_STS1_PORT_PASS; - /* Enable RX port */ - ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport), - &ret); + if (!rx_lock) { + ret = ub960_rxport_lockup_wa_ub9702(priv); + if (ret) + return ret; + + /* Restart AEQ by changing max to 0 --> 0x23 */ + ret = ub960_write_ind(priv, + UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL7, 0, + NULL); + if (ret) + return ret; + + msleep(20); + + /* AEQ Restart */ + ret = ub960_write_ind(priv, + UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL7, + 0x23, NULL); + + if (ret) + return ret; - if (rxport->cdr_mode == RXPORT_CDR_FPD4) { - /* unreset 960 AEQ */ - ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x25, - 0x41, &ret); + msleep(20); + dev_dbg(dev, "rx%u: no lock, retry = %u\n", nport, + retry); + + continue; + } + + ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL11, &rx_aeq, + NULL); + if (ret) + return ret; + + if (rx_aeq < rx_aeq_limit) { + dev_dbg(dev, + "rx%u: locked and AEQ normal before setting AEQ window\n", + nport); + return 0; + } + + if (rx_aeq != prev_aeq) { + ret = ub960_rxport_lockup_wa_ub9702(priv); + if (ret) + return ret; + + /* Restart AEQ by changing max to 0 --> 0x23 */ + ret = ub960_write_ind(priv, + UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL7, + 0, NULL); + if (ret) + return ret; + + msleep(20); + + /* AEQ Restart */ + ret = ub960_write_ind(priv, + UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL7, + 0x23, NULL); + if (ret) + return ret; + + msleep(20); + + dev_dbg(dev, + "rx%u: high AEQ at initial check recovery loop, retry=%u\n", + nport, retry); + + prev_aeq = rx_aeq; + } else { + dev_dbg(dev, + "rx%u: lossy cable detected, RX_AEQ %#x, RX_AEQ_LIMIT %#x, retry %u\n", + nport, rx_aeq, rx_aeq_limit, retry); + dev_dbg(dev, + "rx%u: will continue with initiation sequence but high AEQ\n", + nport); + return 0; + } } - return ret; + dev_err(dev, "rx%u: max number of retries: %s\n", nport, + rx_lock ? "unstable AEQ" : "no lock"); + + return -EIO; } -static int ub960_init_rx_ports(struct ub960_data *priv) +static int ub960_enable_aeq_lms_ub9702(struct ub960_data *priv, + unsigned int nport) +{ + struct device *dev = &priv->client->dev; + u8 read_aeq_init; + int ret; + + ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL11, &read_aeq_init, + NULL); + if (ret) + return ret; + + dev_dbg(dev, "rx%u: initial AEQ = %#x\n", nport, read_aeq_init); + + /* Set AEQ Min */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL6, read_aeq_init, &ret); + /* Set AEQ Max */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL7, read_aeq_init + 1, &ret); + /* Set AEQ offset to 0 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL10, 0x0, &ret); + + /* Enable AEQ tap2 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_EQ_CTRL_SEL_38, 0x00, &ret); + /* Set VGA Gain 1 Gain 2 override to 0 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_VGA_CTRL_SEL_8, 0x00, &ret); + /* Set VGA Initial Sweep Gain to 0 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_VGA_CTRL_SEL_6, 0x80, &ret); + /* Set VGA_Adapt (VGA Gain) override to 0 (thermometer encoded) */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_VGA_CTRL_SEL_3, 0x00, &ret); + /* Enable VGA_SWEEP */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_EQ_ADAPT_CTRL, 0x40, &ret); + /* Disable VGA_SWEEP_GAIN_OV, disable VGA_TUNE_OV */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_EQ_OVERRIDE_CTRL, 0x00, &ret); + + /* Set VGA HIGH Threshold to 43 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_VGA_CTRL_SEL_1, 0x2b, &ret); + /* Set VGA LOW Threshold to 18 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_VGA_CTRL_SEL_2, 0x12, &ret); + /* Set vga_sweep_th to 32 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_EQ_CTRL_SEL_15, 0x20, &ret); + /* Set AEQ timer to 400us/step and parity threshold to 7 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_SYSTEM_INIT_REG0, 0xef, &ret); + + if (ret) + return ret; + + dev_dbg(dev, "rx%u: enable FPD-Link IV AEQ LMS\n", nport); + + return 0; +} + +static int ub960_enable_dfe_lms_ub9702(struct ub960_data *priv, + unsigned int nport) +{ + struct device *dev = &priv->client->dev; + int ret = 0; + + /* Enable DFE LMS */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_EQ_CTRL_SEL_24, 0x40, &ret); + /* Disable VGA Gain1 override */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_GAIN_CTRL_0, 0x20, &ret); + + if (ret) + return ret; + + usleep_range(1000, 5000); + + /* Disable VGA Gain2 override */ + ret = ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB9702_IR_RX_ANA_GAIN_CTRL_0, 0x00, NULL); + if (ret) + return ret; + + dev_dbg(dev, "rx%u: enabled FPD-Link IV DFE LMS", nport); + + return 0; +} + +static int ub960_init_rx_ports_ub9702(struct ub960_data *priv) { struct device *dev = &priv->client->dev; unsigned int port_lock_mask; - unsigned int port_mask; + unsigned int port_mask = 0; + bool have_fpd4 = false; int ret; for_each_active_rxport(priv, it) { - int ret; + ret = ub960_rxport_update_bits(priv, it.nport, + UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_BC_ALWAYS_ON, + UB960_RR_BCC_CONFIG_BC_ALWAYS_ON, + NULL); + if (ret) + return ret; + } - if (priv->hw_data->is_ub9702) - ret = ub960_init_rx_port_ub9702(priv, it.rxport); - else - ret = ub960_init_rx_port_ub960(priv, it.rxport); + /* Disable FPD4 Auto Recovery */ + ret = ub960_write(priv, UB9702_SR_CSI_EXCLUSIVE_FWD2, 0x0f, NULL); + if (ret) + return ret; + + for_each_active_rxport_fpd4(priv, it) { + /* Hold state machine in reset */ + ub960_rxport_write(priv, it.nport, UB9702_RR_RX_SM_SEL_2, 0x10, + &ret); + + /* Set AEQ max to 0 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(it.nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL7, 0, &ret); if (ret) return ret; + + dev_dbg(dev, + "rx%u: holding state machine and adjusting AEQ max to 0", + it.nport); + } + + for_each_active_rxport(priv, it) { + port_mask |= BIT(it.nport); + + if (it.rxport->cdr_mode == RXPORT_CDR_FPD4) + have_fpd4 = true; + } + + for_each_rxport(priv, it) { + ret = ub960_configure_rx_port_ub9702(priv, it.nport); + if (ret) + return ret; } ret = ub960_reset(priv, false); if (ret) return ret; - port_mask = 0; + if (have_fpd4) { + for_each_active_rxport_fpd4(priv, it) { + /* Release state machine */ + ret = ub960_rxport_write(priv, it.nport, + UB9702_RR_RX_SM_SEL_2, 0x0, + NULL); + if (ret) + return ret; - for_each_active_rxport(priv, it) - port_mask |= BIT(it.nport); + dev_dbg(dev, "rx%u: state machine released\n", + it.nport); + } + + /* Wait for SM to resume */ + fsleep(5000); + + for_each_active_rxport_fpd4(priv, it) { + ret = ub960_write_ind(priv, + UB960_IND_TARGET_RX_ANA(it.nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL7, + 0x23, NULL); + if (ret) + return ret; + + dev_dbg(dev, "rx%u: AEQ restart\n", it.nport); + } + + /* Wait for lock */ + fsleep(20000); + + for_each_active_rxport_fpd4(priv, it) { + ret = ub960_lock_recovery_ub9702(priv, it.nport); + if (ret) + return ret; + } + + for_each_active_rxport_fpd4(priv, it) { + ret = ub960_enable_aeq_lms_ub9702(priv, it.nport); + if (ret) + return ret; + } + + for_each_active_rxport_fpd4(priv, it) { + /* Hold state machine in reset */ + ret = ub960_rxport_write(priv, it.nport, + UB9702_RR_RX_SM_SEL_2, 0x10, + NULL); + if (ret) + return ret; + } + + ret = ub960_reset(priv, false); + if (ret) + return ret; + + for_each_active_rxport_fpd4(priv, it) { + /* Release state machine */ + ret = ub960_rxport_write(priv, it.nport, + UB9702_RR_RX_SM_SEL_2, 0, + NULL); + if (ret) + return ret; + } + } + + /* Wait time for stable lock */ + fsleep(15000); + + for_each_active_rxport_fpd4(priv, it) { + ret = ub960_enable_dfe_lms_ub9702(priv, it.nport); + if (ret) + return ret; + } + + /* Wait for DFE and LMS to adapt */ + fsleep(5000); ret = ub960_rxport_wait_locks(priv, port_mask, &port_lock_mask); if (ret) @@ -2556,10 +2981,49 @@ static int ub960_init_rx_ports(struct ub960_data *priv) return ret; } + for_each_active_rxport(priv, it) { + /* Enable all interrupt sources from this port */ + ub960_rxport_write(priv, it.nport, UB960_RR_PORT_ICR_HI, 0x07, + &ret); + ub960_rxport_write(priv, it.nport, UB960_RR_PORT_ICR_LO, 0x7f, + &ret); + + /* Enable I2C_PASS_THROUGH */ + ub960_rxport_update_bits(priv, it.nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + &ret); + + /* Enable I2C communication to the serializer via the alias */ + ub960_rxport_write(priv, it.nport, UB960_RR_SER_ALIAS_ID, + it.rxport->ser.alias << 1, &ret); + + if (ret) + return ret; + } + + /* Enable FPD4 Auto Recovery, Recovery loop active */ + ret = ub960_write(priv, UB9702_SR_CSI_EXCLUSIVE_FWD2, 0x18, NULL); + if (ret) + return ret; + + for_each_active_rxport_fpd4(priv, it) { + u8 final_aeq; + + ret = ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(it.nport), + UB9702_IR_RX_ANA_AEQ_ALP_SEL11, &final_aeq, + NULL); + if (ret) + return ret; + + dev_dbg(dev, "rx%u: final AEQ = %#x\n", it.nport, final_aeq); + } + /* * Clear any errors caused by switching the RX port settings while * probing. */ + ret = ub960_clear_rx_errors(priv); if (ret) return ret; @@ -4393,7 +4857,11 @@ static int ub960_probe(struct i2c_client *client) if (ret) goto err_free_ports; - ret = ub960_init_rx_ports(priv); + if (priv->hw_data->is_ub9702) + ret = ub960_init_rx_ports_ub9702(priv); + else + ret = ub960_init_rx_ports_ub960(priv); + if (ret) goto err_disable_vpocs; -- cgit v1.2.3-59-g8ed1b From ca26126a6c3fb6eb4f30ceb6a41ff975117ec94a Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Mon, 3 Mar 2025 21:32:19 +0530 Subject: media: dt-bindings: ti,ds90ub960: Allow setting serializer address The serializer's I2C address on the FPD-Link bus is usually communicated to the deserializer once the forward-channel is established. But in some cases it might be necessary to program the serializer (over the back-channel) before the forward-channel is established. This can be used e.g. to correct serializer configuration which otherwise would prevent the FC to be enabled. To be able to communicate to the serializer before the forward-channel is up, the deserializer driver neds to know the default i2c address of the serializer. Allow setting the serializer i2c address using the 'reg' property. This is optional, and usually not needed. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../bindings/media/i2c/ti,ds90ub953.yaml | 77 +++++++++++++--------- .../bindings/media/i2c/ti,ds90ub960.yaml | 16 ++++- 2 files changed, 58 insertions(+), 35 deletions(-) diff --git a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml index 2030366994d1..2e129bf573b7 100644 --- a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml +++ b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml @@ -38,6 +38,13 @@ properties: '#clock-cells': const: 0 + reg: + maxItems: 1 + description: + The strap I2C address of the serializer. Can be used by the deserializer + to communicate over back-channel when the forward-channel is not yet + active. + ports: $ref: /schemas/graph.yaml#/properties/ports @@ -81,51 +88,57 @@ examples: - | #include - serializer { - compatible = "ti,ds90ub953-q1"; + link { + #address-cells = <1>; + #size-cells = <0>; + + serializer@18 { + compatible = "ti,ds90ub953-q1"; + reg = <0x18>; - gpio-controller; - #gpio-cells = <2>; + gpio-controller; + #gpio-cells = <2>; - #clock-cells = <0>; + #clock-cells = <0>; - ports { - #address-cells = <1>; - #size-cells = <0>; + ports { + #address-cells = <1>; + #size-cells = <0>; - port@0 { - reg = <0>; - ub953_in: endpoint { - clock-lanes = <0>; - data-lanes = <1 2 3 4>; - remote-endpoint = <&sensor_out>; + port@0 { + reg = <0>; + ub953_in: endpoint { + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&sensor_out>; + }; }; - }; - port@1 { - reg = <1>; - endpoint { - remote-endpoint = <&deser_fpd_in>; + port@1 { + reg = <1>; + endpoint { + remote-endpoint = <&deser_fpd_in>; + }; }; }; - }; - i2c { - #address-cells = <1>; - #size-cells = <0>; + i2c { + #address-cells = <1>; + #size-cells = <0>; - sensor@1a { - compatible = "sony,imx274"; - reg = <0x1a>; + sensor@1a { + compatible = "sony,imx274"; + reg = <0x1a>; - reset-gpios = <&serializer 0 GPIO_ACTIVE_LOW>; + reset-gpios = <&serializer 0 GPIO_ACTIVE_LOW>; - clocks = <&serializer>; - clock-names = "inck"; + clocks = <&serializer>; + clock-names = "inck"; - port { - sensor_out: endpoint { - remote-endpoint = <&ub953_in>; + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; }; }; }; diff --git a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml index 0b71e6f911a8..4dcbd2b039a5 100644 --- a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml +++ b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml @@ -68,6 +68,12 @@ properties: description: The link number maxItems: 1 + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + i2c-alias: $ref: /schemas/types.yaml#/definitions/uint32 description: @@ -107,7 +113,8 @@ properties: maximum: 14 description: Manual EQ level - serializer: + patternProperties: + '^serializer(@[0-9a-f]+)*$': type: object description: FPD-Link Serializer node @@ -115,7 +122,6 @@ properties: - reg - i2c-alias - ti,rx-mode - - serializer ports: $ref: /schemas/graph.yaml#/properties/ports @@ -309,13 +315,17 @@ examples: /* Link 0 has DS90UB953 serializer and IMX274 sensor */ link@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; i2c-alias = <0x44>; ti,rx-mode = <3>; - serializer1: serializer { + serializer1: serializer@30 { compatible = "ti,ds90ub953-q1"; + reg = <0x30>; gpio-controller; #gpio-cells = <2>; -- cgit v1.2.3-59-g8ed1b From b8e2193b1e367ac8169b97703dc2fc666637ae84 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 3 Mar 2025 21:32:20 +0530 Subject: media: i2c: ds90ub953: Move reg defines to a header file Move UB953 register defines to a header file. This is done so that the deserializer driver can access the defines, and do some early serializer configuration. Signed-off-by: Tomi Valkeinen Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub953.c | 89 +--------------------------------------- drivers/media/i2c/ds90ub953.h | 95 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 87 deletions(-) create mode 100644 drivers/media/i2c/ds90ub953.h diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index a5c23e94f4ea..c305b4e03e07 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -28,6 +28,8 @@ #include #include +#include "ds90ub953.h" + #define UB953_PAD_SINK 0 #define UB953_PAD_SOURCE 1 @@ -35,93 +37,6 @@ #define UB953_DEFAULT_CLKOUT_RATE 25000000UL -#define UB953_REG_RESET_CTL 0x01 -#define UB953_REG_RESET_CTL_DIGITAL_RESET_1 BIT(1) -#define UB953_REG_RESET_CTL_DIGITAL_RESET_0 BIT(0) - -#define UB953_REG_GENERAL_CFG 0x02 -#define UB953_REG_GENERAL_CFG_CONT_CLK BIT(6) -#define UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT 4 -#define UB953_REG_GENERAL_CFG_CSI_LANE_SEL_MASK GENMASK(5, 4) -#define UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE BIT(1) -#define UB953_REG_GENERAL_CFG_I2C_STRAP_MODE BIT(0) - -#define UB953_REG_MODE_SEL 0x03 -#define UB953_REG_MODE_SEL_MODE_DONE BIT(3) -#define UB953_REG_MODE_SEL_MODE_OVERRIDE BIT(4) -#define UB953_REG_MODE_SEL_MODE_MASK GENMASK(2, 0) - -#define UB953_REG_CLKOUT_CTRL0 0x06 -#define UB953_REG_CLKOUT_CTRL1 0x07 - -#define UB953_REG_I2C_CONTROL2 0x0a -#define UB953_REG_I2C_CONTROL2_SDA_OUTPUT_SETUP_SHIFT 4 -#define UB953_REG_I2C_CONTROL2_BUS_SPEEDUP BIT(1) - -#define UB953_REG_SCL_HIGH_TIME 0x0b -#define UB953_REG_SCL_LOW_TIME 0x0c - -#define UB953_REG_LOCAL_GPIO_DATA 0x0d -#define UB953_REG_LOCAL_GPIO_DATA_GPIO_RMTEN(n) BIT(4 + (n)) -#define UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(n) BIT(0 + (n)) - -#define UB953_REG_GPIO_INPUT_CTRL 0x0e -#define UB953_REG_GPIO_INPUT_CTRL_OUT_EN(n) BIT(4 + (n)) -#define UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(n) BIT(0 + (n)) - -#define UB953_REG_BC_CTRL 0x49 -#define UB953_REG_BC_CTRL_CRC_ERR_CLR BIT(3) - -#define UB953_REG_REV_MASK_ID 0x50 -#define UB953_REG_GENERAL_STATUS 0x52 - -#define UB953_REG_GPIO_PIN_STS 0x53 -#define UB953_REG_GPIO_PIN_STS_GPIO_STS(n) BIT(0 + (n)) - -#define UB953_REG_BIST_ERR_CNT 0x54 -#define UB953_REG_CRC_ERR_CNT1 0x55 -#define UB953_REG_CRC_ERR_CNT2 0x56 - -#define UB953_REG_CSI_ERR_CNT 0x5c -#define UB953_REG_CSI_ERR_STATUS 0x5d -#define UB953_REG_CSI_ERR_DLANE01 0x5e -#define UB953_REG_CSI_ERR_DLANE23 0x5f -#define UB953_REG_CSI_ERR_CLK_LANE 0x60 -#define UB953_REG_CSI_PKT_HDR_VC_ID 0x61 -#define UB953_REG_PKT_HDR_WC_LSB 0x62 -#define UB953_REG_PKT_HDR_WC_MSB 0x63 -#define UB953_REG_CSI_ECC 0x64 - -#define UB953_REG_IND_ACC_CTL 0xb0 -#define UB953_REG_IND_ACC_ADDR 0xb1 -#define UB953_REG_IND_ACC_DATA 0xb2 - -#define UB953_REG_FPD3_RX_ID(n) (0xf0 + (n)) -#define UB953_REG_FPD3_RX_ID_LEN 6 - -/* Indirect register blocks */ -#define UB953_IND_TARGET_PAT_GEN 0x00 -#define UB953_IND_TARGET_FPD3_TX 0x01 -#define UB953_IND_TARGET_DIE_ID 0x02 - -#define UB953_IND_PGEN_CTL 0x01 -#define UB953_IND_PGEN_CTL_PGEN_ENABLE BIT(0) -#define UB953_IND_PGEN_CFG 0x02 -#define UB953_IND_PGEN_CSI_DI 0x03 -#define UB953_IND_PGEN_LINE_SIZE1 0x04 -#define UB953_IND_PGEN_LINE_SIZE0 0x05 -#define UB953_IND_PGEN_BAR_SIZE1 0x06 -#define UB953_IND_PGEN_BAR_SIZE0 0x07 -#define UB953_IND_PGEN_ACT_LPF1 0x08 -#define UB953_IND_PGEN_ACT_LPF0 0x09 -#define UB953_IND_PGEN_TOT_LPF1 0x0a -#define UB953_IND_PGEN_TOT_LPF0 0x0b -#define UB953_IND_PGEN_LINE_PD1 0x0c -#define UB953_IND_PGEN_LINE_PD0 0x0d -#define UB953_IND_PGEN_VBP 0x0e -#define UB953_IND_PGEN_VFP 0x0f -#define UB953_IND_PGEN_COLOR(n) (0x10 + (n)) /* n <= 15 */ - /* Note: Only sync mode supported for now */ enum ub953_mode { /* FPD-Link III CSI-2 synchronous mode */ diff --git a/drivers/media/i2c/ds90ub953.h b/drivers/media/i2c/ds90ub953.h new file mode 100644 index 000000000000..8bb28f0daee9 --- /dev/null +++ b/drivers/media/i2c/ds90ub953.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __MEDIA_I2C_DS90UB953_H__ +#define __MEDIA_I2C_DS90UB953_H__ + +#include + +#define UB953_REG_RESET_CTL 0x01 +#define UB953_REG_RESET_CTL_DIGITAL_RESET_1 BIT(1) +#define UB953_REG_RESET_CTL_DIGITAL_RESET_0 BIT(0) + +#define UB953_REG_GENERAL_CFG 0x02 +#define UB953_REG_GENERAL_CFG_CONT_CLK BIT(6) +#define UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT 4 +#define UB953_REG_GENERAL_CFG_CSI_LANE_SEL_MASK GENMASK(5, 4) +#define UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE BIT(1) +#define UB953_REG_GENERAL_CFG_I2C_STRAP_MODE BIT(0) + +#define UB953_REG_MODE_SEL 0x03 +#define UB953_REG_MODE_SEL_MODE_DONE BIT(3) +#define UB953_REG_MODE_SEL_MODE_OVERRIDE BIT(4) +#define UB953_REG_MODE_SEL_MODE_MASK GENMASK(2, 0) + +#define UB953_REG_CLKOUT_CTRL0 0x06 +#define UB953_REG_CLKOUT_CTRL1 0x07 + +#define UB953_REG_I2C_CONTROL2 0x0a +#define UB953_REG_I2C_CONTROL2_SDA_OUTPUT_SETUP_SHIFT 4 +#define UB953_REG_I2C_CONTROL2_BUS_SPEEDUP BIT(1) + +#define UB953_REG_SCL_HIGH_TIME 0x0b +#define UB953_REG_SCL_LOW_TIME 0x0c + +#define UB953_REG_LOCAL_GPIO_DATA 0x0d +#define UB953_REG_LOCAL_GPIO_DATA_GPIO_RMTEN(n) BIT(4 + (n)) +#define UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(n) BIT(0 + (n)) + +#define UB953_REG_GPIO_INPUT_CTRL 0x0e +#define UB953_REG_GPIO_INPUT_CTRL_OUT_EN(n) BIT(4 + (n)) +#define UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(n) BIT(0 + (n)) + +#define UB953_REG_BC_CTRL 0x49 +#define UB953_REG_BC_CTRL_CRC_ERR_CLR BIT(3) + +#define UB953_REG_REV_MASK_ID 0x50 +#define UB953_REG_GENERAL_STATUS 0x52 + +#define UB953_REG_GPIO_PIN_STS 0x53 +#define UB953_REG_GPIO_PIN_STS_GPIO_STS(n) BIT(0 + (n)) + +#define UB953_REG_BIST_ERR_CNT 0x54 +#define UB953_REG_CRC_ERR_CNT1 0x55 +#define UB953_REG_CRC_ERR_CNT2 0x56 + +#define UB953_REG_CSI_ERR_CNT 0x5c +#define UB953_REG_CSI_ERR_STATUS 0x5d +#define UB953_REG_CSI_ERR_DLANE01 0x5e +#define UB953_REG_CSI_ERR_DLANE23 0x5f +#define UB953_REG_CSI_ERR_CLK_LANE 0x60 +#define UB953_REG_CSI_PKT_HDR_VC_ID 0x61 +#define UB953_REG_PKT_HDR_WC_LSB 0x62 +#define UB953_REG_PKT_HDR_WC_MSB 0x63 +#define UB953_REG_CSI_ECC 0x64 + +#define UB953_REG_IND_ACC_CTL 0xb0 +#define UB953_REG_IND_ACC_ADDR 0xb1 +#define UB953_REG_IND_ACC_DATA 0xb2 + +#define UB953_REG_FPD3_RX_ID(n) (0xf0 + (n)) +#define UB953_REG_FPD3_RX_ID_LEN 6 + +/* Indirect register blocks */ +#define UB953_IND_TARGET_PAT_GEN 0x00 +#define UB953_IND_TARGET_FPD3_TX 0x01 +#define UB953_IND_TARGET_DIE_ID 0x02 + +#define UB953_IND_PGEN_CTL 0x01 +#define UB953_IND_PGEN_CTL_PGEN_ENABLE BIT(0) +#define UB953_IND_PGEN_CFG 0x02 +#define UB953_IND_PGEN_CSI_DI 0x03 +#define UB953_IND_PGEN_LINE_SIZE1 0x04 +#define UB953_IND_PGEN_LINE_SIZE0 0x05 +#define UB953_IND_PGEN_BAR_SIZE1 0x06 +#define UB953_IND_PGEN_BAR_SIZE0 0x07 +#define UB953_IND_PGEN_ACT_LPF1 0x08 +#define UB953_IND_PGEN_ACT_LPF0 0x09 +#define UB953_IND_PGEN_TOT_LPF1 0x0a +#define UB953_IND_PGEN_TOT_LPF0 0x0b +#define UB953_IND_PGEN_LINE_PD1 0x0c +#define UB953_IND_PGEN_LINE_PD0 0x0d +#define UB953_IND_PGEN_VBP 0x0e +#define UB953_IND_PGEN_VFP 0x0f +#define UB953_IND_PGEN_COLOR(n) (0x10 + (n)) /* n <= 15 */ + +#endif /* __MEDIA_I2C_DS90UB953_H__ */ -- cgit v1.2.3-59-g8ed1b From e2a3b695bc5f343391c003ecda98701808a9c433 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Mon, 3 Mar 2025 21:32:21 +0530 Subject: media: i2c: ds90ub960: Configure serializer using back-channel For DS90UB9702-Q1, it is recommended to configure some serializer settings over the back-channel before the forward-channel is active. This can only be done if the serializer's I2C address on the FPD-Link bus is populated in the device tree node. Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub953.h | 4 ++ drivers/media/i2c/ds90ub960.c | 126 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 126 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ds90ub953.h b/drivers/media/i2c/ds90ub953.h index 8bb28f0daee9..de606474493f 100644 --- a/drivers/media/i2c/ds90ub953.h +++ b/drivers/media/i2c/ds90ub953.h @@ -92,4 +92,8 @@ #define UB953_IND_PGEN_VFP 0x0f #define UB953_IND_PGEN_COLOR(n) (0x10 + (n)) /* n <= 15 */ +/* UB971 Registers */ + +#define UB971_ENH_BC_CHK 0x4b + #endif /* __MEDIA_I2C_DS90UB953_H__ */ diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 56b1a88c1c29..cad25dcbca11 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -52,6 +52,8 @@ #include #include +#include "ds90ub953.h" + #define MHZ(v) ((u32)((v) * HZ_PER_MHZ)) /* @@ -244,13 +246,16 @@ #define UB960_RR_BCC_CONFIG 0x58 #define UB960_RR_BCC_CONFIG_BC_ALWAYS_ON BIT(4) +#define UB960_RR_BCC_CONFIG_AUTO_ACK_ALL BIT(5) #define UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH BIT(6) #define UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK GENMASK(2, 0) #define UB960_RR_DATAPATH_CTL1 0x59 #define UB960_RR_DATAPATH_CTL2 0x5a #define UB960_RR_SER_ID 0x5b +#define UB960_RR_SER_ID_FREEZE_DEVICE_ID BIT(0) #define UB960_RR_SER_ALIAS_ID 0x5c +#define UB960_RR_SER_ALIAS_ID_AUTO_ACK BIT(0) /* For these two register sets: n < UB960_MAX_PORT_ALIASES */ #define UB960_RR_SLAVE_ID(n) (0x5d + (n)) @@ -486,7 +491,9 @@ struct ub960_rxport { struct fwnode_handle *fwnode; struct i2c_client *client; unsigned short alias; /* I2C alias (lower 7 bits) */ + short addr; /* Local I2C address (lower 7 bits) */ struct ds90ub9xx_platform_data pdata; + struct regmap *regmap; } ser; enum ub960_rxport_mode rx_mode; @@ -1984,6 +1991,78 @@ static unsigned long ub960_calc_bc_clk_rate_ub9702(struct ub960_data *priv, } } +static int ub960_rxport_serializer_write(struct ub960_rxport *rxport, u8 reg, + u8 val, int *err) +{ + struct ub960_data *priv = rxport->priv; + struct device *dev = &priv->client->dev; + union i2c_smbus_data data; + int ret; + + if (err && *err) + return *err; + + data.byte = val; + + ret = i2c_smbus_xfer(priv->client->adapter, rxport->ser.alias, 0, + I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, &data); + if (ret) + dev_err(dev, + "rx%u: cannot write serializer register 0x%02x (%d)!\n", + rxport->nport, reg, ret); + + if (ret && err) + *err = ret; + + return ret; +} + +static int ub960_rxport_bc_ser_config(struct ub960_rxport *rxport) +{ + struct ub960_data *priv = rxport->priv; + struct device *dev = &priv->client->dev; + u8 nport = rxport->nport; + int ret; + + /* Skip port if serializer's address is not known */ + if (rxport->ser.addr < 0) { + dev_dbg(dev, + "rx%u: serializer address missing, skip configuration\n", + nport); + return 0; + } + + /* + * Note: the code here probably only works for CSI-2 serializers in + * sync mode. To support other serializers the BC related configuration + * should be done before calling this function. + */ + + /* Enable I2C passthrough and auto-ack on BC */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH | + UB960_RR_BCC_CONFIG_AUTO_ACK_ALL, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH | + UB960_RR_BCC_CONFIG_AUTO_ACK_ALL, + &ret); + + if (ret) + return ret; + + /* Disable BC alternate mode auto detect */ + ub960_rxport_serializer_write(rxport, UB971_ENH_BC_CHK, 0x02, &ret); + /* Decrease link detect timer */ + ub960_rxport_serializer_write(rxport, UB953_REG_BC_CTRL, 0x06, &ret); + + /* Disable I2C passthrough and auto-ack on BC */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH | + UB960_RR_BCC_CONFIG_AUTO_ACK_ALL, + 0x0, &ret); + + return ret; +} + static int ub960_rxport_add_serializer(struct ub960_data *priv, u8 nport) { struct ub960_rxport *rxport = priv->rxports[nport]; @@ -2860,6 +2939,36 @@ static int ub960_init_rx_ports_ub9702(struct ub960_data *priv) if (ret) return ret; + for_each_active_rxport(priv, it) { + if (it.rxport->ser.addr >= 0) { + /* + * Set serializer's I2C address if set in the dts file, + * and freeze it to prevent updates from the FC. + */ + ub960_rxport_write(priv, it.nport, UB960_RR_SER_ID, + it.rxport->ser.addr << 1 | + UB960_RR_SER_ID_FREEZE_DEVICE_ID, + &ret); + } + + /* Set serializer I2C alias with auto-ack */ + ub960_rxport_write(priv, it.nport, UB960_RR_SER_ALIAS_ID, + it.rxport->ser.alias << 1 | + UB960_RR_SER_ALIAS_ID_AUTO_ACK, &ret); + + if (ret) + return ret; + } + + for_each_active_rxport(priv, it) { + if (fwnode_device_is_compatible(it.rxport->ser.fwnode, + "ti,ds90ub971-q1")) { + ret = ub960_rxport_bc_ser_config(it.rxport); + if (ret) + return ret; + } + } + for_each_active_rxport_fpd4(priv, it) { /* Hold state machine in reset */ ub960_rxport_write(priv, it.nport, UB9702_RR_RX_SM_SEL_2, 0x10, @@ -2988,16 +3097,17 @@ static int ub960_init_rx_ports_ub9702(struct ub960_data *priv) ub960_rxport_write(priv, it.nport, UB960_RR_PORT_ICR_LO, 0x7f, &ret); + /* Clear serializer I2C alias auto-ack */ + ub960_rxport_update_bits(priv, it.nport, UB960_RR_SER_ALIAS_ID, + UB960_RR_SER_ALIAS_ID_AUTO_ACK, 0, + &ret); + /* Enable I2C_PASS_THROUGH */ ub960_rxport_update_bits(priv, it.nport, UB960_RR_BCC_CONFIG, UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, &ret); - /* Enable I2C communication to the serializer via the alias */ - ub960_rxport_write(priv, it.nport, UB960_RR_SER_ALIAS_ID, - it.rxport->ser.alias << 1, &ret); - if (ret) return ret; } @@ -4156,6 +4266,7 @@ ub960_parse_dt_rxport_link_properties(struct ub960_data *priv, s32 strobe_pos; u32 eq_level; u32 ser_i2c_alias; + u32 ser_i2c_addr; int ret; cdr_mode = RXPORT_CDR_FPD3; @@ -4267,6 +4378,13 @@ ub960_parse_dt_rxport_link_properties(struct ub960_data *priv, return -EINVAL; } + ret = fwnode_property_read_u32(rxport->ser.fwnode, "reg", + &ser_i2c_addr); + if (ret) + rxport->ser.addr = -EINVAL; + else + rxport->ser.addr = ser_i2c_addr; + return 0; } -- cgit v1.2.3-59-g8ed1b From a05744749600007f56efb16c6de727d9d541d475 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Mon, 3 Mar 2025 21:32:22 +0530 Subject: media: i2c: ds90ub9xx: Set serializer temperature ramp For continuous PLL lock, it is recommended to extend the temperature ramp down range of the DS90UB953-Q1 serializer based on the device's initial temperature [1]. The serializer's die temperature is reported only to the deserializer through the sensor status registers, and for UB9702, it is recommended to set the temperature ramp during the link setup sequence, i.e. before we even probe the ub953 driver. Add support to the deserializer driver to configure ub953's temperature ramp. [1]: Section 7.3.1.1 - https://www.ti.com/lit/gpn/ds90ub953-q1 Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub953.h | 7 ++- drivers/media/i2c/ds90ub960.c | 125 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ds90ub953.h b/drivers/media/i2c/ds90ub953.h index de606474493f..97a6b3af326e 100644 --- a/drivers/media/i2c/ds90ub953.h +++ b/drivers/media/i2c/ds90ub953.h @@ -71,7 +71,7 @@ /* Indirect register blocks */ #define UB953_IND_TARGET_PAT_GEN 0x00 -#define UB953_IND_TARGET_FPD3_TX 0x01 +#define UB953_IND_TARGET_ANALOG 0x01 #define UB953_IND_TARGET_DIE_ID 0x02 #define UB953_IND_PGEN_CTL 0x01 @@ -92,6 +92,11 @@ #define UB953_IND_PGEN_VFP 0x0f #define UB953_IND_PGEN_COLOR(n) (0x10 + (n)) /* n <= 15 */ +#define UB953_IND_ANA_TEMP_DYNAMIC_CFG 0x4b +#define UB953_IND_ANA_TEMP_DYNAMIC_CFG_OV BIT(5) +#define UB953_IND_ANA_TEMP_STATIC_CFG 0x4c +#define UB953_IND_ANA_TEMP_STATIC_CFG_MASK GENMASK(6, 4) + /* UB971 Registers */ #define UB971_ENH_BC_CHK 0x4b diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index cad25dcbca11..1877eb735cc7 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -2017,6 +2017,110 @@ static int ub960_rxport_serializer_write(struct ub960_rxport *rxport, u8 reg, return ret; } +static int ub960_rxport_serializer_read(struct ub960_rxport *rxport, u8 reg, + u8 *val, int *err) +{ + struct ub960_data *priv = rxport->priv; + struct device *dev = &priv->client->dev; + union i2c_smbus_data data = { 0 }; + int ret; + + if (err && *err) + return *err; + + ret = i2c_smbus_xfer(priv->client->adapter, rxport->ser.alias, + priv->client->flags, I2C_SMBUS_READ, reg, + I2C_SMBUS_BYTE_DATA, &data); + if (ret) + dev_err(dev, + "rx%u: cannot read serializer register 0x%02x (%d)!\n", + rxport->nport, reg, ret); + else + *val = data.byte; + + if (ret && err) + *err = ret; + + return ret; +} + +static int ub960_serializer_temp_ramp(struct ub960_rxport *rxport) +{ + struct ub960_data *priv = rxport->priv; + short temp_dynamic_offset[] = {-1, -1, 0, 0, 1, 1, 1, 3}; + u8 temp_dynamic_cfg; + u8 nport = rxport->nport; + u8 ser_temp_code; + int ret; + + /* Configure temp ramp only on UB953 */ + if (!fwnode_device_is_compatible(rxport->ser.fwnode, "ti,ds90ub953-q1")) + return 0; + + /* Read current serializer die temperature */ + ub960_rxport_read(priv, nport, UB960_RR_SENSOR_STS_2, &ser_temp_code, + &ret); + + /* Enable I2C passthrough on back channel */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, &ret); + + if (ret) + return ret; + + /* Select indirect page for analog regs on the serializer */ + ub960_rxport_serializer_write(rxport, UB953_REG_IND_ACC_CTL, + UB953_IND_TARGET_ANALOG << 2, &ret); + + /* Set temperature ramp dynamic and static config */ + ub960_rxport_serializer_write(rxport, UB953_REG_IND_ACC_ADDR, + UB953_IND_ANA_TEMP_DYNAMIC_CFG, &ret); + ub960_rxport_serializer_read(rxport, UB953_REG_IND_ACC_DATA, + &temp_dynamic_cfg, &ret); + + if (ret) + return ret; + + temp_dynamic_cfg |= UB953_IND_ANA_TEMP_DYNAMIC_CFG_OV; + temp_dynamic_cfg += temp_dynamic_offset[ser_temp_code]; + + /* Update temp static config */ + ub960_rxport_serializer_write(rxport, UB953_REG_IND_ACC_ADDR, + UB953_IND_ANA_TEMP_STATIC_CFG, &ret); + ub960_rxport_serializer_write(rxport, UB953_REG_IND_ACC_DATA, + UB953_IND_ANA_TEMP_STATIC_CFG_MASK, &ret); + + /* Update temperature ramp dynamic config */ + ub960_rxport_serializer_write(rxport, UB953_REG_IND_ACC_ADDR, + UB953_IND_ANA_TEMP_DYNAMIC_CFG, &ret); + + /* Enable I2C auto ack on BC before we set dynamic cfg and reset */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_AUTO_ACK_ALL, + UB960_RR_BCC_CONFIG_AUTO_ACK_ALL, &ret); + + ub960_rxport_serializer_write(rxport, UB953_REG_IND_ACC_DATA, + temp_dynamic_cfg, &ret); + + if (ret) + return ret; + + /* Soft reset to apply PLL updates */ + ub960_rxport_serializer_write(rxport, UB953_REG_RESET_CTL, + UB953_REG_RESET_CTL_DIGITAL_RESET_0, + &ret); + msleep(20); + + /* Disable I2C passthrough and auto-ack on BC */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH | + UB960_RR_BCC_CONFIG_AUTO_ACK_ALL, + 0x0, &ret); + + return ret; +} + static int ub960_rxport_bc_ser_config(struct ub960_rxport *rxport) { struct ub960_data *priv = rxport->priv; @@ -2396,6 +2500,20 @@ static int ub960_init_rx_ports_ub960(struct ub960_data *priv) return ret; } + /* Set temperature ramp on serializer */ + for_each_active_rxport(priv, it) { + ret = ub960_serializer_temp_ramp(it.rxport); + if (ret) + return ret; + + ub960_rxport_update_bits(priv, it.nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + &ret); + if (ret) + return ret; + } + /* * Clear any errors caused by switching the RX port settings while * probing. @@ -3071,6 +3189,13 @@ static int ub960_init_rx_ports_ub9702(struct ub960_data *priv) /* Wait time for stable lock */ fsleep(15000); + /* Set temperature ramp on serializer */ + for_each_active_rxport(priv, it) { + ret = ub960_serializer_temp_ramp(it.rxport); + if (ret) + return ret; + } + for_each_active_rxport_fpd4(priv, it) { ret = ub960_enable_dfe_lms_ub9702(priv, it.nport); if (ret) -- cgit v1.2.3-59-g8ed1b From d471fb06b21ae54bf76464731ae1dcb26ef1ca68 Mon Sep 17 00:00:00 2001 From: Hao Yao Date: Tue, 11 Mar 2025 16:41:55 +0800 Subject: media: ipu6: Remove workaround for Meteor Lake ES2 There was a hardware bug which need IPU6 driver to disable the ATS. This workaround is not needed anymore as the bug was fixed in hardware level. Additionally, Arrow Lake has the same IPU6 PCI ID and x86 stepping but does not have the bug. Removing the Meteor Lake workaround is also required for the driver to function on Arrow Lake. Signed-off-by: Hao Yao Reviewed-by: Stanislaw Gruszka Fixes: 25fedc021985 ("media: intel/ipu6: add Intel IPU6 PCI device driver") Cc: stable@vger.kernel.org [Sakari Ailus: Added tags and explanation of what is fixed.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c index 277af7cda8ee..b00d0705fefa 100644 --- a/drivers/media/pci/intel/ipu6/ipu6.c +++ b/drivers/media/pci/intel/ipu6/ipu6.c @@ -464,11 +464,6 @@ static int ipu6_pci_config_setup(struct pci_dev *dev, u8 hw_ver) { int ret; - /* disable IPU6 PCI ATS on mtl ES2 */ - if (is_ipu6ep_mtl(hw_ver) && boot_cpu_data.x86_stepping == 0x2 && - pci_ats_supported(dev)) - pci_disable_ats(dev); - /* No PCI msi capability for IPU6EP */ if (is_ipu6ep(hw_ver) || is_ipu6ep_mtl(hw_ver)) { /* likely do nothing as msi not enabled by default */ -- cgit v1.2.3-59-g8ed1b From bd5bae761f1813567388791eab7afaa7c287e936 Mon Sep 17 00:00:00 2001 From: Shravan Chippa Date: Wed, 5 Mar 2025 10:44:39 +0530 Subject: media: i2c: imx334: Optimized 4k and 2k mode register arrays Optimized the resolution arrays by integrating a common register array. Adjusted the register array values for 1920x1080@30 and 3840x2160@30 resolutions to align with the common register array values. Signed-off-by: Shravan Chippa Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 131 +++++++++++---------------------------------- 1 file changed, 30 insertions(+), 101 deletions(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index a544fc3df39c..db61b298ceb3 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -167,8 +167,8 @@ static const s64 link_freq[] = { IMX334_LINK_FREQ_445M, }; -/* Sensor mode registers for 1920x1080@30fps */ -static const struct imx334_reg mode_1920x1080_regs[] = { +/* Sensor common mode registers values */ +static const struct imx334_reg common_mode_regs[] = { {0x3000, 0x01}, {0x3018, 0x04}, {0x3030, 0xca}, @@ -176,26 +176,10 @@ static const struct imx334_reg mode_1920x1080_regs[] = { {0x3032, 0x00}, {0x3034, 0x4c}, {0x3035, 0x04}, - {0x302c, 0xf0}, - {0x302d, 0x03}, - {0x302e, 0x80}, - {0x302f, 0x07}, - {0x3074, 0xcc}, - {0x3075, 0x02}, - {0x308e, 0xcd}, - {0x308f, 0x02}, - {0x3076, 0x38}, - {0x3077, 0x04}, - {0x3090, 0x38}, - {0x3091, 0x04}, - {0x3308, 0x38}, - {0x3309, 0x04}, - {0x30C6, 0x00}, + {0x30c6, 0x00}, {0x30c7, 0x00}, {0x30ce, 0x00}, {0x30cf, 0x00}, - {0x30d8, 0x18}, - {0x30d9, 0x0a}, {0x304c, 0x00}, {0x304e, 0x00}, {0x304f, 0x00}, @@ -330,24 +314,31 @@ static const struct imx334_reg mode_1920x1080_regs[] = { {0x3002, 0x00}, }; +/* Sensor mode registers for 1920x1080@30fps */ +static const struct imx334_reg mode_1920x1080_regs[] = { + {0x302c, 0xf0}, + {0x302d, 0x03}, + {0x302e, 0x80}, + {0x302f, 0x07}, + {0x3074, 0xcc}, + {0x3075, 0x02}, + {0x308e, 0xcd}, + {0x308f, 0x02}, + {0x3076, 0x38}, + {0x3077, 0x04}, + {0x3090, 0x38}, + {0x3091, 0x04}, + {0x3308, 0x38}, + {0x3309, 0x04}, + {0x30d8, 0x18}, + {0x30d9, 0x0a}, +}; + /* Sensor mode registers for 3840x2160@30fps */ static const struct imx334_reg mode_3840x2160_regs[] = { - {0x3000, 0x01}, - {0x3002, 0x00}, - {0x3018, 0x04}, - {0x37b0, 0x36}, - {0x304c, 0x00}, - {0x300c, 0x3b}, - {0x300d, 0x2a}, {0x3034, 0x26}, {0x3035, 0x02}, - {0x314c, 0x29}, - {0x314d, 0x01}, {0x315a, 0x02}, - {0x3168, 0xa0}, - {0x316a, 0x7e}, - {0x3288, 0x21}, - {0x328a, 0x02}, {0x302c, 0x3c}, {0x302d, 0x00}, {0x302e, 0x00}, @@ -356,82 +347,13 @@ static const struct imx334_reg mode_3840x2160_regs[] = { {0x3077, 0x08}, {0x3090, 0x70}, {0x3091, 0x08}, - {0x30d8, 0x20}, - {0x30d9, 0x12}, {0x3308, 0x70}, {0x3309, 0x08}, - {0x3414, 0x05}, - {0x3416, 0x18}, - {0x35ac, 0x0e}, - {0x3648, 0x01}, - {0x364a, 0x04}, - {0x364c, 0x04}, - {0x3678, 0x01}, - {0x367c, 0x31}, - {0x367e, 0x31}, - {0x3708, 0x02}, - {0x3714, 0x01}, - {0x3715, 0x02}, - {0x3716, 0x02}, - {0x3717, 0x02}, - {0x371c, 0x3d}, - {0x371d, 0x3f}, - {0x372c, 0x00}, - {0x372d, 0x00}, - {0x372e, 0x46}, - {0x372f, 0x00}, - {0x3730, 0x89}, - {0x3731, 0x00}, - {0x3732, 0x08}, - {0x3733, 0x01}, - {0x3734, 0xfe}, - {0x3735, 0x05}, - {0x375d, 0x00}, - {0x375e, 0x00}, - {0x375f, 0x61}, - {0x3760, 0x06}, - {0x3768, 0x1b}, - {0x3769, 0x1b}, - {0x376a, 0x1a}, - {0x376b, 0x19}, - {0x376c, 0x18}, - {0x376d, 0x14}, - {0x376e, 0x0f}, - {0x3776, 0x00}, - {0x3777, 0x00}, - {0x3778, 0x46}, - {0x3779, 0x00}, - {0x377a, 0x08}, - {0x377b, 0x01}, - {0x377c, 0x45}, - {0x377d, 0x01}, - {0x377e, 0x23}, - {0x377f, 0x02}, - {0x3780, 0xd9}, - {0x3781, 0x03}, - {0x3782, 0xf5}, - {0x3783, 0x06}, - {0x3784, 0xa5}, - {0x3788, 0x0f}, - {0x378a, 0xd9}, - {0x378b, 0x03}, - {0x378c, 0xeb}, - {0x378d, 0x05}, - {0x378e, 0x87}, - {0x378f, 0x06}, - {0x3790, 0xf5}, - {0x3792, 0x43}, - {0x3794, 0x7a}, - {0x3796, 0xa1}, - {0x3e04, 0x0e}, {0x319e, 0x00}, {0x3a00, 0x01}, {0x3a18, 0xbf}, - {0x3a19, 0x00}, {0x3a1a, 0x67}, - {0x3a1b, 0x00}, {0x3a1c, 0x6f}, - {0x3a1d, 0x00}, {0x3a1e, 0xd7}, {0x3a1f, 0x01}, {0x3a20, 0x6f}, @@ -989,6 +911,13 @@ static int imx334_start_streaming(struct imx334 *imx334) const struct imx334_reg_list *reg_list; int ret; + ret = imx334_write_regs(imx334, common_mode_regs, + ARRAY_SIZE(common_mode_regs)); + if (ret) { + dev_err(imx334->dev, "fail to write common registers"); + return ret; + } + /* Write sensor mode registers */ reg_list = &imx334->cur_mode->reg_list; ret = imx334_write_regs(imx334, reg_list->regs, -- cgit v1.2.3-59-g8ed1b From 35132d039c566b0e9d8e53f76f512b22607c2405 Mon Sep 17 00:00:00 2001 From: Shravan Chippa Date: Wed, 5 Mar 2025 10:44:41 +0530 Subject: media: i2c: imx334: update mode_3840x2160_regs array The 3840x2160 mode operates with the imx334 reset values. If we switch to other modes and then return to the 3840x2160 mode, it should function correctly. so updated the mode_3840x2160_regs array with the imx334 reset values. Signed-off-by: Shravan Chippa Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index db61b298ceb3..5ba832591a9c 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -343,6 +343,12 @@ static const struct imx334_reg mode_3840x2160_regs[] = { {0x302d, 0x00}, {0x302e, 0x00}, {0x302f, 0x0f}, + {0x3074, 0xb0}, + {0x3075, 0x00}, + {0x308e, 0xb1}, + {0x308f, 0x00}, + {0x30d8, 0x20}, + {0x30d9, 0x12}, {0x3076, 0x70}, {0x3077, 0x08}, {0x3090, 0x70}, -- cgit v1.2.3-59-g8ed1b From 7dced52992887a712d41913b39c5000b2ebf8c4f Mon Sep 17 00:00:00 2001 From: Shravan Chippa Date: Wed, 5 Mar 2025 10:44:42 +0530 Subject: media: i2c: imx334: add modes for 720p and 480p resolutions Added support for 1280x720@30 and 640x480@30 resolutions Signed-off-by: Shravan Chippa Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 5ba832591a9c..84a2313d0c8d 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -314,6 +314,46 @@ static const struct imx334_reg common_mode_regs[] = { {0x3002, 0x00}, }; +/* Sensor mode registers for 640x480@30fps */ +static const struct imx334_reg mode_640x480_regs[] = { + {0x302c, 0x70}, + {0x302d, 0x06}, + {0x302e, 0x80}, + {0x302f, 0x02}, + {0x3074, 0x48}, + {0x3075, 0x07}, + {0x308e, 0x49}, + {0x308f, 0x07}, + {0x3076, 0xe0}, + {0x3077, 0x01}, + {0x3090, 0xe0}, + {0x3091, 0x01}, + {0x3308, 0xe0}, + {0x3309, 0x01}, + {0x30d8, 0x30}, + {0x30d9, 0x0b}, +}; + +/* Sensor mode registers for 1280x720@30fps */ +static const struct imx334_reg mode_1280x720_regs[] = { + {0x302c, 0x30}, + {0x302d, 0x05}, + {0x302e, 0x00}, + {0x302f, 0x05}, + {0x3074, 0x84}, + {0x3075, 0x03}, + {0x308e, 0x85}, + {0x308f, 0x03}, + {0x3076, 0xd0}, + {0x3077, 0x02}, + {0x3090, 0xd0}, + {0x3091, 0x02}, + {0x3308, 0xd0}, + {0x3309, 0x02}, + {0x30d8, 0x30}, + {0x30d9, 0x0b}, +}; + /* Sensor mode registers for 1920x1080@30fps */ static const struct imx334_reg mode_1920x1080_regs[] = { {0x302c, 0xf0}, @@ -433,6 +473,32 @@ static const struct imx334_mode supported_modes[] = { .num_of_regs = ARRAY_SIZE(mode_1920x1080_regs), .regs = mode_1920x1080_regs, }, + }, { + .width = 1280, + .height = 720, + .hblank = 2480, + .vblank = 1170, + .vblank_min = 45, + .vblank_max = 132840, + .pclk = 297000000, + .link_freq_idx = 1, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), + .regs = mode_1280x720_regs, + }, + }, { + .width = 640, + .height = 480, + .hblank = 2480, + .vblank = 1170, + .vblank_min = 45, + .vblank_max = 132840, + .pclk = 297000000, + .link_freq_idx = 1, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_640x480_regs), + .regs = mode_640x480_regs, + }, }, }; -- cgit v1.2.3-59-g8ed1b From 267836bcc808928afa2a92d4088c41f488ebf047 Mon Sep 17 00:00:00 2001 From: Shravan Chippa Date: Wed, 5 Mar 2025 10:44:40 +0530 Subject: media: i2c: imx334: common reg value correction correcting the CPWAIT_TIME value as per the data sheet for the link frequency and input clock Signed-off-by: Shravan Chippa Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 84a2313d0c8d..8cd1eecd0143 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -191,7 +191,7 @@ static const struct imx334_reg common_mode_regs[] = { {0x31a0, 0x20}, {0x31a1, 0x0f}, {0x300c, 0x3b}, - {0x300d, 0x29}, + {0x300d, 0x2a}, {0x314c, 0x29}, {0x314d, 0x01}, {0x315a, 0x06}, -- cgit v1.2.3-59-g8ed1b From 77aed862c34f192f9d4b80d5288263b22b50ca98 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 11 Mar 2025 12:48:44 +0100 Subject: media: ov08x40: Extend sleep after reset to 5 ms Some users are reporting that ov08x40_identify_module() fails to identify the chip reading 0x00 as value for OV08X40_REG_CHIP_ID. Intel's out of tree IPU6 drivers include some ov08x40 changes including adding support for the reset GPIO for older kernels and Intel's patch for this uses 5 ms. Extend the sleep to 5 ms following Intel's example, this fixes the ov08x40_identify_module() problem. Link: https://github.com/intel/ipu6-drivers/blob/c09e2198d801e1eb701984d2948373123ba92a56/patch/v6.12/0008-media-ov08x40-Add-support-for-2-4-lanes-support-at-1.patch#L4607 Fixes: df1ae2251a50 ("media: ov08x40: Add OF probe support") Cc: stable@vger.kernel.org Signed-off-by: Hans de Goede Reviewed-by: Bryan O'Donoghue Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov08x40.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c index cf0e41fc3071..54575eea3c49 100644 --- a/drivers/media/i2c/ov08x40.c +++ b/drivers/media/i2c/ov08x40.c @@ -1341,7 +1341,7 @@ static int ov08x40_power_on(struct device *dev) } gpiod_set_value_cansleep(ov08x->reset_gpio, 0); - usleep_range(1500, 1800); + usleep_range(5000, 5500); return 0; -- cgit v1.2.3-59-g8ed1b From 660e613d05e449766784c549faf5927ffaf281f1 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 18 Feb 2025 23:43:58 +0200 Subject: media: ccs-pll: Start OP pre-PLL multiplier search from correct value The ccs_pll_calculate() function does a search over possible PLL configurations to find the "best" one. If the sensor does not support odd pre-PLL divisors and the minimum value (with constraints) isn't 1, other odd values could be errorneously searched (and selected) for the pre-PLL divisor. Fix this. Fixes: 415ddd993978 ("media: ccs-pll: Split limits and PLL configuration into front and back parts") Cc: stable@vger.kernel.org Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs-pll.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index 34ccda666524..e516ed23e899 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -815,6 +815,8 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, one_or_more( DIV_ROUND_UP(op_lim_fr->max_pll_op_clk_freq_hz, pll->ext_clk_freq_hz)))); + if (!(pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER)) + min_op_pre_pll_clk_div = clk_div_even(min_op_pre_pll_clk_div); dev_dbg(dev, "pll_op check: min / max op_pre_pll_clk_div: %u / %u\n", min_op_pre_pll_clk_div, max_op_pre_pll_clk_div); -- cgit v1.2.3-59-g8ed1b From 06d2d478b09e6764fb6161d1621fc10d9f0f2860 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 18 Feb 2025 23:47:13 +0200 Subject: media: ccs-pll: Start VT pre-PLL multiplier search from correct value The ccs_pll_calculate_vt_tree() function does a search over possible VT PLL configurations to find the "best" one. If the sensor does not support odd pre-PLL divisors and the minimum value (with constraints) isn't 1, other odd values could be errorneously searched (and selected) for the pre-PLL divisor. Fix this. Fixes: 415ddd993978 ("media: ccs-pll: Split limits and PLL configuration into front and back parts") Cc: stable@vger.kernel.org Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs-pll.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index e516ed23e899..2399cd6509b7 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -397,6 +397,8 @@ static int ccs_pll_calculate_vt_tree(struct device *dev, min_pre_pll_clk_div = max_t(u16, min_pre_pll_clk_div, pll->ext_clk_freq_hz / lim_fr->max_pll_ip_clk_freq_hz); + if (!(pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER)) + min_pre_pll_clk_div = clk_div_even(min_pre_pll_clk_div); dev_dbg(dev, "vt min/max_pre_pll_clk_div: %u,%u\n", min_pre_pll_clk_div, max_pre_pll_clk_div); -- cgit v1.2.3-59-g8ed1b From 6868b955acd6e5d7405a2b730c2ffb692ad50d2c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 20 Feb 2025 10:54:44 +0200 Subject: media: ccs-pll: Check for too high VT PLL multiplier in dual PLL case The check for VT PLL upper limit in dual PLL case was missing. Add it now. Fixes: 6c7469e46b60 ("media: ccs-pll: Add trivial dual PLL support") Cc: stable@vger.kernel.org Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs-pll.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index 2399cd6509b7..266fcd160da6 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -312,6 +312,11 @@ __ccs_pll_calculate_vt_tree(struct device *dev, dev_dbg(dev, "more_mul2: %u\n", more_mul); pll_fr->pll_multiplier = mul * more_mul; + if (pll_fr->pll_multiplier > lim_fr->max_pll_multiplier) { + dev_dbg(dev, "pll multiplier %u too high\n", + pll_fr->pll_multiplier); + return -EINVAL; + } if (pll_fr->pll_multiplier * pll_fr->pll_ip_clk_freq_hz > lim_fr->max_pll_op_clk_freq_hz) -- cgit v1.2.3-59-g8ed1b From f639494db450770fa30d6845d9c84b9cb009758f Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 19 Feb 2025 15:06:11 +0200 Subject: media: ccs-pll: Correct the upper limit of maximum op_pre_pll_clk_div The PLL calculator does a search of the PLL configuration space for all valid OP pre-PLL clock dividers. The maximum did not take into account the CCS PLL flag CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER in which case also odd PLL dividers (other than 1) are valid. Do that now. Fixes: 4e1e8d240dff ("media: ccs-pll: Add support for extended input PLL clock divider") Cc: stable@vger.kernel.org Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs-pll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index 266fcd160da6..d985686b0a36 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -799,7 +799,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, op_lim_fr->min_pre_pll_clk_div, op_lim_fr->max_pre_pll_clk_div); max_op_pre_pll_clk_div = min_t(u16, op_lim_fr->max_pre_pll_clk_div, - clk_div_even(pll->ext_clk_freq_hz / + DIV_ROUND_UP(pll->ext_clk_freq_hz, op_lim_fr->min_pll_ip_clk_freq_hz)); min_op_pre_pll_clk_div = max_t(u16, op_lim_fr->min_pre_pll_clk_div, -- cgit v1.2.3-59-g8ed1b From 34af05f857cb8317ce3c19f00f68af4c14329dbe Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 20 Feb 2025 11:53:20 +0200 Subject: media: ccs-pll: Print a debug message on too high VT PLL OP clock In general the CCS PLL calculator prints debugging information on the process to ease debugging. This case was not annotated, do that now. Remove an extra multiplication while at it. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs-pll.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index d985686b0a36..66d046d576f7 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -318,12 +318,13 @@ __ccs_pll_calculate_vt_tree(struct device *dev, return -EINVAL; } - if (pll_fr->pll_multiplier * pll_fr->pll_ip_clk_freq_hz > - lim_fr->max_pll_op_clk_freq_hz) - return -EINVAL; - pll_fr->pll_op_clk_freq_hz = pll_fr->pll_ip_clk_freq_hz * pll_fr->pll_multiplier; + if (pll_fr->pll_op_clk_freq_hz > lim_fr->max_pll_op_clk_freq_hz) { + dev_dbg(dev, "too high OP clock %u\n", + pll_fr->pll_op_clk_freq_hz); + return -EINVAL; + } vt_div = div * more_mul; -- cgit v1.2.3-59-g8ed1b From 2ab7b3d07d2549686a8088f3de6e477b6ceb0483 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 16 Apr 2025 13:54:00 +0300 Subject: media: ccs-pll: Drop LINK_DECOUPLED flag The LINK_DECOUPLED flag isn't used by the PLL calculator other than printing it. The number of OP/VT lanes are already printed in any case. Thus drop the flag as it has no function. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs-pll.c | 3 +-- drivers/media/i2c/ccs-pll.h | 1 - drivers/media/i2c/ccs/ccs-core.c | 1 - drivers/media/i2c/ccs/ccs-quirk.c | 3 +-- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index 66d046d576f7..16eb09462c8b 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -124,9 +124,8 @@ static void print_pll(struct device *dev, const struct ccs_pll *pll) dev_dbg(dev, "pixel rate on CSI-2 bus:\t%u\n", pll->pixel_rate_csi); - dev_dbg(dev, "flags%s%s%s%s%s%s%s%s%s\n", + dev_dbg(dev, "flags%s%s%s%s%s%s%s%s\n", pll->flags & PLL_FL(LANE_SPEED_MODEL) ? " lane-speed" : "", - pll->flags & PLL_FL(LINK_DECOUPLED) ? " link-decoupled" : "", pll->flags & PLL_FL(EXT_IP_PLL_DIVIDER) ? " ext-ip-pll-divider" : "", pll->flags & PLL_FL(FLEXIBLE_OP_PIX_CLK_DIV) ? diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h index 6eb1b1c68e1e..ee206e5b287b 100644 --- a/drivers/media/i2c/ccs-pll.h +++ b/drivers/media/i2c/ccs-pll.h @@ -24,7 +24,6 @@ #define CCS_PLL_FLAG_NO_OP_CLOCKS BIT(1) /* CCS PLL flags */ #define CCS_PLL_FLAG_LANE_SPEED_MODEL BIT(2) -#define CCS_PLL_FLAG_LINK_DECOUPLED BIT(3) #define CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER BIT(4) #define CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV BIT(5) #define CCS_PLL_FLAG_FIFO_DERATING BIT(6) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 004d28c33287..06e0ba53f2a8 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -3451,7 +3451,6 @@ static int ccs_probe(struct i2c_client *client) CCS_LIM(sensor, NUM_OF_VT_LANES) + 1; sensor->pll.op_lanes = CCS_LIM(sensor, NUM_OF_OP_LANES) + 1; - sensor->pll.flags |= CCS_PLL_FLAG_LINK_DECOUPLED; } else { sensor->pll.vt_lanes = sensor->pll.csi2.lanes; sensor->pll.op_lanes = sensor->pll.csi2.lanes; diff --git a/drivers/media/i2c/ccs/ccs-quirk.c b/drivers/media/i2c/ccs/ccs-quirk.c index e3d4c7a275bc..e48a4fa1f5dd 100644 --- a/drivers/media/i2c/ccs/ccs-quirk.c +++ b/drivers/media/i2c/ccs/ccs-quirk.c @@ -190,8 +190,7 @@ static int jt8ev1_post_streamoff(struct ccs_sensor *sensor) static int jt8ev1_init(struct ccs_sensor *sensor) { - sensor->pll.flags |= CCS_PLL_FLAG_LANE_SPEED_MODEL | - CCS_PLL_FLAG_LINK_DECOUPLED; + sensor->pll.flags |= CCS_PLL_FLAG_LANE_SPEED_MODEL; sensor->pll.vt_lanes = 1; sensor->pll.op_lanes = sensor->pll.csi2.lanes; -- cgit v1.2.3-59-g8ed1b From 715f84c1e54efd123da761bb3db204e76e051041 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 16 Apr 2025 14:18:45 +0300 Subject: media: ccs-pll: Print missing PLL flags Printing the OP_PIX_CLOCK_PER_LANE and NO_OP_CLOCKS CCS PLL flags is missing, add them now. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs-pll.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index 16eb09462c8b..ebbc5e323244 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -124,7 +124,9 @@ static void print_pll(struct device *dev, const struct ccs_pll *pll) dev_dbg(dev, "pixel rate on CSI-2 bus:\t%u\n", pll->pixel_rate_csi); - dev_dbg(dev, "flags%s%s%s%s%s%s%s%s\n", + dev_dbg(dev, "flags%s%s%s%s%s%s%s%s%s%s\n", + pll->flags & PLL_FL(OP_PIX_CLOCK_PER_LANE) ? " op-pix-clock-per-lane" : "", + pll->flags & PLL_FL(NO_OP_CLOCKS) ? " no-op-clocks" : "", pll->flags & PLL_FL(LANE_SPEED_MODEL) ? " lane-speed" : "", pll->flags & PLL_FL(EXT_IP_PLL_DIVIDER) ? " ext-ip-pll-divider" : "", -- cgit v1.2.3-59-g8ed1b From 2f19528845b80f8299fbe28b23c162ba9389ebad Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 14 Feb 2025 12:36:18 +0200 Subject: media: ccs-pll: Add a flag for even PLL multipliers Some devices (not entirely CCS compliant) only support even PLL multipliers. Add support for this through a PLL flag. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs-pll.c | 11 ++++++++++- drivers/media/i2c/ccs-pll.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index ebbc5e323244..f4be4e9f6777 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -124,9 +124,10 @@ static void print_pll(struct device *dev, const struct ccs_pll *pll) dev_dbg(dev, "pixel rate on CSI-2 bus:\t%u\n", pll->pixel_rate_csi); - dev_dbg(dev, "flags%s%s%s%s%s%s%s%s%s%s\n", + dev_dbg(dev, "flags%s%s%s%s%s%s%s%s%s%s%s\n", pll->flags & PLL_FL(OP_PIX_CLOCK_PER_LANE) ? " op-pix-clock-per-lane" : "", pll->flags & PLL_FL(NO_OP_CLOCKS) ? " no-op-clocks" : "", + pll->flags & PLL_FL(EVEN_PLL_MULTIPLIER) ? " even-pll-multiplier" : "", pll->flags & PLL_FL(LANE_SPEED_MODEL) ? " lane-speed" : "", pll->flags & PLL_FL(EXT_IP_PLL_DIVIDER) ? " ext-ip-pll-divider" : "", @@ -312,6 +313,10 @@ __ccs_pll_calculate_vt_tree(struct device *dev, more_mul *= DIV_ROUND_UP(lim_fr->min_pll_multiplier, mul * more_mul); dev_dbg(dev, "more_mul2: %u\n", more_mul); + if (pll->flags & CCS_PLL_FLAG_EVEN_PLL_MULTIPLIER && + (mul & 1) && (more_mul & 1)) + more_mul <<= 1; + pll_fr->pll_multiplier = mul * more_mul; if (pll_fr->pll_multiplier > lim_fr->max_pll_multiplier) { dev_dbg(dev, "pll multiplier %u too high\n", @@ -668,6 +673,10 @@ ccs_pll_calculate_op(struct device *dev, const struct ccs_pll_limits *lim, if (!is_one_or_even(i)) i <<= 1; + if (pll->flags & CCS_PLL_FLAG_EVEN_PLL_MULTIPLIER && + mul & 1 && i & 1) + i <<= 1; + dev_dbg(dev, "final more_mul: %u\n", i); if (i > more_mul_max) { dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max); diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h index ee206e5b287b..754eb5f52cc4 100644 --- a/drivers/media/i2c/ccs-pll.h +++ b/drivers/media/i2c/ccs-pll.h @@ -22,6 +22,7 @@ /* op pix clock is for all lanes in total normally */ #define CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE BIT(0) #define CCS_PLL_FLAG_NO_OP_CLOCKS BIT(1) +#define CCS_PLL_FLAG_EVEN_PLL_MULTIPLIER BIT(3) /* CCS PLL flags */ #define CCS_PLL_FLAG_LANE_SPEED_MODEL BIT(2) #define CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER BIT(4) -- cgit v1.2.3-59-g8ed1b From cd9cb0313a42ae029cd5af9293b0add984ed252e Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 26 Feb 2025 14:27:58 +0200 Subject: media: ccs-pll: Better validate VT PLL branch Check that the VT PLL dividers are actually found, don't trust they always are even though they should be. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs-pll.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index f4be4e9f6777..8de8158a413b 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -449,7 +449,7 @@ static int ccs_pll_calculate_vt_tree(struct device *dev, return -EINVAL; } -static void +static int ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim, const struct ccs_pll_branch_limits_bk *op_lim_bk, struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr, @@ -572,6 +572,8 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim, if (best_pix_div < SHRT_MAX >> 1) break; } + if (best_pix_div == SHRT_MAX >> 1) + return -EINVAL; pll->vt_bk.sys_clk_div = DIV_ROUND_UP(vt_div, best_pix_div); pll->vt_bk.pix_clk_div = best_pix_div; @@ -584,6 +586,8 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim, out_calc_pixel_rate: pll->pixel_rate_pixel_array = pll->vt_bk.pix_clk_freq_hz * pll->vt_lanes; + + return 0; } /* @@ -863,8 +867,10 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, if (pll->flags & CCS_PLL_FLAG_DUAL_PLL) break; - ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr, - op_pll_bk, cphy, phy_const); + rval = ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr, + op_pll_bk, cphy, phy_const); + if (rval) + continue; rval = check_bk_bounds(dev, lim, pll, PLL_VT); if (rval) -- cgit v1.2.3-59-g8ed1b From de6514694b54bdaf1a6e6cde6575871330013cb2 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 5 Mar 2025 13:30:41 +0200 Subject: media: ccs-pll: Print PLL calculator flags in the beginning Print the PLL calculator flags right away when the PLL calculator is called. Previously this was done only in a successful case and that didn't really help solving a problem when one happened. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs-pll.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index 8de8158a413b..87798616b76d 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -123,8 +123,11 @@ static void print_pll(struct device *dev, const struct ccs_pll *pll) pll->pixel_rate_pixel_array); dev_dbg(dev, "pixel rate on CSI-2 bus:\t%u\n", pll->pixel_rate_csi); +} - dev_dbg(dev, "flags%s%s%s%s%s%s%s%s%s%s%s\n", +static void print_pll_flags(struct device *dev, struct ccs_pll *pll) +{ + dev_dbg(dev, "PLL flags%s%s%s%s%s%s%s%s%s%s%s\n", pll->flags & PLL_FL(OP_PIX_CLOCK_PER_LANE) ? " op-pix-clock-per-lane" : "", pll->flags & PLL_FL(NO_OP_CLOCKS) ? " no-op-clocks" : "", pll->flags & PLL_FL(EVEN_PLL_MULTIPLIER) ? " even-pll-multiplier" : "", @@ -738,6 +741,8 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, u32 i; int rval = -EINVAL; + print_pll_flags(dev, pll); + if (!(pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)) { pll->op_lanes = 1; pll->vt_lanes = 1; -- cgit v1.2.3-59-g8ed1b From 220ea1432a6d317272d94184bf5f98b317f514b9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 16 Apr 2025 14:08:56 +0300 Subject: media: ccs-pll: Document the CCS PLL flags Document the CCS PLL flags with short comments. The CCS spec has more information on them while the added documentation helps finding the relevant information in the CCS spec. Signed-off-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs-pll.c | 2 +- drivers/media/i2c/ccs-pll.h | 27 ++++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index 87798616b76d..8f9a695bd9e5 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -129,8 +129,8 @@ static void print_pll_flags(struct device *dev, struct ccs_pll *pll) { dev_dbg(dev, "PLL flags%s%s%s%s%s%s%s%s%s%s%s\n", pll->flags & PLL_FL(OP_PIX_CLOCK_PER_LANE) ? " op-pix-clock-per-lane" : "", - pll->flags & PLL_FL(NO_OP_CLOCKS) ? " no-op-clocks" : "", pll->flags & PLL_FL(EVEN_PLL_MULTIPLIER) ? " even-pll-multiplier" : "", + pll->flags & PLL_FL(NO_OP_CLOCKS) ? " no-op-clocks" : "", pll->flags & PLL_FL(LANE_SPEED_MODEL) ? " lane-speed" : "", pll->flags & PLL_FL(EXT_IP_PLL_DIVIDER) ? " ext-ip-pll-divider" : "", diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h index 754eb5f52cc4..e22903931e72 100644 --- a/drivers/media/i2c/ccs-pll.h +++ b/drivers/media/i2c/ccs-pll.h @@ -18,19 +18,40 @@ #define CCS_PLL_BUS_TYPE_CSI2_DPHY 0x00 #define CCS_PLL_BUS_TYPE_CSI2_CPHY 0x01 -/* Old SMIA and implementation specific flags */ -/* op pix clock is for all lanes in total normally */ +/* Old SMIA and implementation specific flags. */ +/* OP PIX clock is for all lanes in total normally. */ #define CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE BIT(0) -#define CCS_PLL_FLAG_NO_OP_CLOCKS BIT(1) +/* If set, the PLL multipliers are required to be even. */ #define CCS_PLL_FLAG_EVEN_PLL_MULTIPLIER BIT(3) + /* CCS PLL flags */ + +/* The sensor doesn't have OP clocks at all. */ +#define CCS_PLL_FLAG_NO_OP_CLOCKS BIT(1) +/* System speed model if this flag is unset. */ #define CCS_PLL_FLAG_LANE_SPEED_MODEL BIT(2) +/* If set, the pre-PLL divider may have odd values, too. */ #define CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER BIT(4) +/* + * If set, the OP PIX clock doesn't have to exactly match with data rate, it may + * be higher. See "OP Domain Formulas" in MIPI CCS 1.1 spec. + */ #define CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV BIT(5) +/* If set, the VT domain may run faster than the OP domain. */ #define CCS_PLL_FLAG_FIFO_DERATING BIT(6) +/* If set, the VT domain may run slower than the OP domain. */ #define CCS_PLL_FLAG_FIFO_OVERRATING BIT(7) +/* If set, the PLL tree has two PLLs instead of one. */ #define CCS_PLL_FLAG_DUAL_PLL BIT(8) +/* + * If set, the OP SYS clock is a dual data rate clock, transferring two bits per + * cycle instead of one. + */ #define CCS_PLL_FLAG_OP_SYS_DDR BIT(9) +/* + * If set, the OP PIX clock is a dual data rate clock, transferring two pixels + * per cycle instead of one. + */ #define CCS_PLL_FLAG_OP_PIX_DDR BIT(10) /** -- cgit v1.2.3-59-g8ed1b From c3d8e388ac9dd56fd99dbe71874aa67097840a57 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 13 Mar 2025 11:17:42 +0100 Subject: media: intel/ipu6: Remove unused IPU6_BUS_NAME Remove unused define. Reviewed-by: Hans de Goede Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-bus.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-bus.h b/drivers/media/pci/intel/ipu6/ipu6-bus.h index bb4926dfdf08..ebf470806a74 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-bus.h +++ b/drivers/media/pci/intel/ipu6/ipu6-bus.h @@ -15,8 +15,6 @@ struct firmware; struct pci_dev; -#define IPU6_BUS_NAME IPU6_NAME "-bus" - struct ipu6_buttress_ctrl; struct ipu6_bus_device { -- cgit v1.2.3-59-g8ed1b From be1534a2e71452afbab7c69cf59f783bb33f644a Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 13 Mar 2025 11:17:43 +0100 Subject: media: intel/ipu6: Remove ipu6_buttress_ctrl started field We assign to ->started field but newer read back, the field can be removed. Reviewed-by: Hans de Goede Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-buttress.c | 2 -- drivers/media/pci/intel/ipu6/ipu6-buttress.h | 1 - 2 files changed, 3 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.c b/drivers/media/pci/intel/ipu6/ipu6-buttress.c index d8db5aa5d528..787fcbd1df09 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-buttress.c +++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.c @@ -478,8 +478,6 @@ int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl, dev_err(&isp->pdev->dev, "Change power status timeout with 0x%x\n", val); - ctrl->started = !ret && on; - mutex_unlock(&isp->buttress.power_mutex); return ret; diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.h b/drivers/media/pci/intel/ipu6/ipu6-buttress.h index 482978c2a09d..4b9763acdfdd 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-buttress.h +++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.h @@ -26,7 +26,6 @@ struct ipu6_buttress_ctrl { u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off; unsigned int ratio; unsigned int qos_floor; - bool started; }; struct ipu6_buttress_ipc { -- cgit v1.2.3-59-g8ed1b From 6ad57f8f86de024d10c764f829a50530e7121958 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 13 Mar 2025 11:17:44 +0100 Subject: media: intel/ipu6: Constify ipu6_buttress_ctrl structure Make ipu6_buttress_ctrl constant since it is not modified any longer. Reviewed-by: Hans de Goede Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-bus.c | 2 +- drivers/media/pci/intel/ipu6/ipu6-bus.h | 4 ++-- drivers/media/pci/intel/ipu6/ipu6-buttress.c | 4 ++-- drivers/media/pci/intel/ipu6/ipu6-buttress.h | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-bus.c b/drivers/media/pci/intel/ipu6/ipu6-bus.c index 37d88ddb6ee7..5cee2748983b 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-bus.c +++ b/drivers/media/pci/intel/ipu6/ipu6-bus.c @@ -82,7 +82,7 @@ static void ipu6_bus_release(struct device *dev) struct ipu6_bus_device * ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent, - void *pdata, struct ipu6_buttress_ctrl *ctrl, + void *pdata, const struct ipu6_buttress_ctrl *ctrl, char *name) { struct auxiliary_device *auxdev; diff --git a/drivers/media/pci/intel/ipu6/ipu6-bus.h b/drivers/media/pci/intel/ipu6/ipu6-bus.h index ebf470806a74..b790f9cc37e3 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-bus.h +++ b/drivers/media/pci/intel/ipu6/ipu6-bus.h @@ -25,7 +25,7 @@ struct ipu6_bus_device { void *pdata; struct ipu6_mmu *mmu; struct ipu6_device *isp; - struct ipu6_buttress_ctrl *ctrl; + const struct ipu6_buttress_ctrl *ctrl; u64 dma_mask; const struct firmware *fw; struct sg_table fw_sgt; @@ -48,7 +48,7 @@ struct ipu6_auxdrv_data { struct ipu6_bus_device * ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent, - void *pdata, struct ipu6_buttress_ctrl *ctrl, + void *pdata, const struct ipu6_buttress_ctrl *ctrl, char *name); int ipu6_bus_add_device(struct ipu6_bus_device *adev); void ipu6_bus_del_devices(struct pci_dev *pdev); diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.c b/drivers/media/pci/intel/ipu6/ipu6-buttress.c index 787fcbd1df09..103386c4f6ae 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-buttress.c +++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.c @@ -443,8 +443,8 @@ irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr) return ret; } -int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl, - bool on) +int ipu6_buttress_power(struct device *dev, + const struct ipu6_buttress_ctrl *ctrl, bool on) { struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp; u32 pwr_sts, val; diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.h b/drivers/media/pci/intel/ipu6/ipu6-buttress.h index 4b9763acdfdd..51e5ad48db82 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-buttress.h +++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.h @@ -65,8 +65,8 @@ int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys, struct sg_table *sgt); void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys, struct sg_table *sgt); -int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl, - bool on); +int ipu6_buttress_power(struct device *dev, + const struct ipu6_buttress_ctrl *ctrl, bool on); bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp); int ipu6_buttress_authenticate(struct ipu6_device *isp); int ipu6_buttress_reset_authentication(struct ipu6_device *isp); -- cgit v1.2.3-59-g8ed1b From 94a6c188b89b8099e3abe78ebb6916a534690e21 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 17 Mar 2025 15:28:42 +0100 Subject: media: intel/ipu6: Remove unused ipu6_isys_subdev_link_validate() Remove unused declaration. Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h index 9ef8d95464f5..268dfa01e903 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h @@ -37,10 +37,6 @@ int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code); -int ipu6_isys_subdev_link_validate(struct v4l2_subdev *sd, - struct media_link *link, - struct v4l2_subdev_format *source_fmt, - struct v4l2_subdev_format *sink_fmt); u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad); int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, struct v4l2_mbus_framefmt *format); -- cgit v1.2.3-59-g8ed1b From adcdf4160a6cca8a8dcda853e07590c32566d6fd Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 19 Mar 2025 13:10:40 +0100 Subject: media: intel/ipu6: Add missing new line character in error message End error message with new line. Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys-queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c index 72f5f987ef48..9a19695abbaf 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c @@ -797,7 +797,7 @@ void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, return; } - dev_err(dev, "Failed to find a matching video buffer"); + dev_err(dev, "Failed to find a matching video buffer\n"); spin_unlock_irqrestore(&aq->lock, flags); } -- cgit v1.2.3-59-g8ed1b From 4fa1d8d81d7224a82c7ae07b9262a8751c4e85f2 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 19 Mar 2025 13:10:41 +0100 Subject: media: intel/ipu6: Make two functions static Make function used only in one file static and remove from header file. Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys-queue.c | 7 ++++--- drivers/media/pci/intel/ipu6/ipu6-isys-queue.h | 4 ---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c index 9a19695abbaf..3984b9d43919 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c @@ -697,8 +697,9 @@ static u64 get_sof_ns_delta(struct ipu6_isys_video *av, return ipu6_buttress_tsc_ticks_to_ns(delta, isp); } -void ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib, - struct ipu6_fw_isys_resp_info_abi *info) +static void +ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib, + struct ipu6_fw_isys_resp_info_abi *info) { struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib); struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); @@ -721,7 +722,7 @@ void ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib, vbuf->vb2_buf.timestamp); } -void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib) +static void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib) { struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib); diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h index fe8fc796a58f..1a277b0fb4b5 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h @@ -69,10 +69,6 @@ void ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set, struct ipu6_isys_stream *stream, struct ipu6_isys_buffer_list *bl); -void -ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib, - struct ipu6_fw_isys_resp_info_abi *info); -void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib); void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, struct ipu6_fw_isys_resp_info_abi *info); int ipu6_isys_queue_init(struct ipu6_isys_queue *aq); -- cgit v1.2.3-59-g8ed1b From 36b9d0521e4b10c9fab9bef0796d6374125b7b57 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 19 Mar 2025 13:10:42 +0100 Subject: media: intel/ipu6: Use timestamp value directly Remove pointer for fw abi structure when setting frame sequence and time, use timestamp value from the structure directly. Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys-queue.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c index 3984b9d43919..c68dc577daf0 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c @@ -652,10 +652,8 @@ static void stop_streaming(struct vb2_queue *q) } static unsigned int -get_sof_sequence_by_timestamp(struct ipu6_isys_stream *stream, - struct ipu6_fw_isys_resp_info_abi *info) +get_sof_sequence_by_timestamp(struct ipu6_isys_stream *stream, u64 time) { - u64 time = (u64)info->timestamp[1] << 32 | info->timestamp[0]; struct ipu6_isys *isys = stream->isys; struct device *dev = &isys->adev->auxdev.dev; unsigned int i; @@ -681,8 +679,7 @@ get_sof_sequence_by_timestamp(struct ipu6_isys_stream *stream, return 0; } -static u64 get_sof_ns_delta(struct ipu6_isys_video *av, - struct ipu6_fw_isys_resp_info_abi *info) +static u64 get_sof_ns_delta(struct ipu6_isys_video *av, u64 timestamp) { struct ipu6_bus_device *adev = av->isys->adev; struct ipu6_device *isp = adev->isp; @@ -692,14 +689,13 @@ static u64 get_sof_ns_delta(struct ipu6_isys_video *av, if (!tsc_now) return 0; - delta = tsc_now - ((u64)info->timestamp[1] << 32 | info->timestamp[0]); + delta = tsc_now - timestamp; return ipu6_buttress_tsc_ticks_to_ns(delta, isp); } static void -ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib, - struct ipu6_fw_isys_resp_info_abi *info) +ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib, u64 time) { struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib); struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); @@ -710,8 +706,8 @@ ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib, u64 ns; u32 sequence; - ns = ktime_get_ns() - get_sof_ns_delta(av, info); - sequence = get_sof_sequence_by_timestamp(stream, info); + ns = ktime_get_ns() - get_sof_ns_delta(av, time); + sequence = get_sof_sequence_by_timestamp(stream, time); vbuf->vb2_buf.timestamp = ns; vbuf->sequence = sequence; @@ -749,6 +745,7 @@ void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, unsigned long flags; bool first = true; struct vb2_v4l2_buffer *buf; + u64 time = (u64)info->timestamp[1] << 32 | info->timestamp[0]; spin_lock_irqsave(&aq->lock, flags); if (list_empty(&aq->active)) { @@ -791,7 +788,7 @@ void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, list_del(&ib->head); spin_unlock_irqrestore(&aq->lock, flags); - ipu6_isys_buf_calc_sequence_time(ib, info); + ipu6_isys_buf_calc_sequence_time(ib, time); ipu6_isys_queue_buf_done(ib); -- cgit v1.2.3-59-g8ed1b From 95d1033c8da0b3307a40bd3dd95ddf85ad171dbc Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 19 Mar 2025 13:10:43 +0100 Subject: media: intel/ipu6: Abstract buf ready function Extract values needed by ipu6_queue_buf_ready() function from fw abi structure. This will allow to reuse same buf ready code when fw abi change. Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys-queue.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c index c68dc577daf0..55d45d2d8768 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c @@ -734,10 +734,11 @@ static void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib) } } -void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, - struct ipu6_fw_isys_resp_info_abi *info) +static void +ipu6_stream_buf_ready(struct ipu6_isys_stream *stream, u8 pin_id, u32 pin_addr, + u64 time, bool error_check) { - struct ipu6_isys_queue *aq = stream->output_pins[info->pin_id].aq; + struct ipu6_isys_queue *aq = stream->output_pins[pin_id].aq; struct ipu6_isys *isys = stream->isys; struct device *dev = &isys->adev->auxdev.dev; struct ipu6_isys_buffer *ib; @@ -745,7 +746,6 @@ void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, unsigned long flags; bool first = true; struct vb2_v4l2_buffer *buf; - u64 time = (u64)info->timestamp[1] << 32 | info->timestamp[0]; spin_lock_irqsave(&aq->lock, flags); if (list_empty(&aq->active)) { @@ -764,7 +764,7 @@ void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, ivb = vb2_buffer_to_ipu6_isys_video_buffer(vvb); addr = ivb->dma_addr; - if (info->pin.addr != addr) { + if (pin_addr != addr) { if (first) dev_err(dev, "Unexpected buffer address %pad\n", &addr); @@ -772,8 +772,7 @@ void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, continue; } - if (info->error_info.error == - IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO) { + if (error_check) { /* * Check for error message: * 'IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO' @@ -800,6 +799,15 @@ void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, spin_unlock_irqrestore(&aq->lock, flags); } +void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream, + struct ipu6_fw_isys_resp_info_abi *info) +{ + u64 time = (u64)info->timestamp[1] << 32 | info->timestamp[0]; + bool err = info->error_info.error == IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO; + + ipu6_stream_buf_ready(stream, info->pin_id, info->pin.addr, time, err); +} + static const struct vb2_ops ipu6_isys_queue_ops = { .queue_setup = ipu6_isys_queue_setup, .buf_init = ipu6_isys_buf_init, -- cgit v1.2.3-59-g8ed1b From 1acf9fee6f117fcb91da3c8f36545d7051c78f52 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 19 Mar 2025 13:10:44 +0100 Subject: media: intel/ipu6: Remove unused dev field from ipu6_isys_queue With current code base dev is only written but never read. Remove it as redundant. Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys-queue.c | 1 - drivers/media/pci/intel/ipu6/ipu6-isys-queue.h | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c index 55d45d2d8768..a9127b1c4d45 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c @@ -841,7 +841,6 @@ int ipu6_isys_queue_init(struct ipu6_isys_queue *aq) if (ret) return ret; - aq->dev = &adev->auxdev.dev; aq->vbq.dev = &adev->isp->pdev->dev; spin_lock_init(&aq->lock); INIT_LIST_HEAD(&aq->active); diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h index 1a277b0fb4b5..b865428a0fce 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h @@ -20,7 +20,6 @@ struct ipu6_isys_stream; struct ipu6_isys_queue { struct vb2_queue vbq; struct list_head node; - struct device *dev; /* * @lock: serialise access to queued and pre_streamon_queued */ -- cgit v1.2.3-59-g8ed1b From 81cf4f46a03a07b0b86f9d677c34ba782df7d65e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 24 Mar 2025 14:01:09 +0100 Subject: media: ov2740: Move pm-runtime cleanup on probe-errors to proper place When v4l2_subdev_init_finalize() fails no changes have been made to the runtime-pm device state yet, so the probe_error_media_entity_cleanup rollback path should not touch the runtime-pm device state. Instead this should be done from the probe_error_v4l2_subdev_cleanup rollback path. Note the pm_runtime_xxx() calls are put above the v4l2_subdev_cleanup() call to have the reverse call order of probe(). Signed-off-by: Hans de Goede Reviewed-by: Bingbu Cao Fixes: 289c25923ecd ("media: ov2740: Use sub-device active state") Cc: stable@vger.kernel.org Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov2740.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 80d151e8ae29..6cf461e3373c 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -1456,12 +1456,12 @@ static int ov2740_probe(struct i2c_client *client) return 0; probe_error_v4l2_subdev_cleanup: + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); v4l2_subdev_cleanup(&ov2740->sd); probe_error_media_entity_cleanup: media_entity_cleanup(&ov2740->sd.entity); - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); probe_error_v4l2_ctrl_handler_free: v4l2_ctrl_handler_free(ov2740->sd.ctrl_handler); -- cgit v1.2.3-59-g8ed1b From e0f6bc693b3f1c0cc3a2d8d67a764ecb0aac2145 Mon Sep 17 00:00:00 2001 From: Dongcheng Yan Date: Wed, 26 Mar 2025 13:24:47 +0800 Subject: media: ipu-bridge: add ACPI HID for lt6911uxe bridge Lontium lt6911uxe is a HDMI to CSI-2 bridge, without a fixed link frequency. Signed-off-by: Dongcheng Yan Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu-bridge.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 1cb745855600..83e682e1a4b7 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -66,6 +66,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { IPU_SENSOR_CONFIG("INT347E", 1, 319200000), /* Hynix Hi-556 */ IPU_SENSOR_CONFIG("INT3537", 1, 437000000), + /* Lontium lt6911uxe */ + IPU_SENSOR_CONFIG("INTC10C5", 0), /* Omnivision OV01A10 / OV01A1S */ IPU_SENSOR_CONFIG("OVTI01A0", 1, 400000000), IPU_SENSOR_CONFIG("OVTI01AS", 1, 400000000), -- cgit v1.2.3-59-g8ed1b From 0c1ab3ce0492a579cf8c3b7c0a6c6ab05e47b4bb Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 26 Mar 2025 13:34:01 +0200 Subject: media: ti: cal: Use printk's fourcc formatting Use printk's fourcc formatting instead of a custom one. Reviewed-by: Kieran Bingham Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/cal/cal-video.c | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index e29743ae61e2..4eb77f46f030 100644 --- a/drivers/media/platform/ti/cal/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -25,20 +25,6 @@ #include "cal.h" -/* Print Four-character-code (FOURCC) */ -static char *fourcc_to_str(u32 fmt) -{ - static char code[5]; - - code[0] = (unsigned char)(fmt & 0xff); - code[1] = (unsigned char)((fmt >> 8) & 0xff); - code[2] = (unsigned char)((fmt >> 16) & 0xff); - code[3] = (unsigned char)((fmt >> 24) & 0xff); - code[4] = '\0'; - - return code; -} - /* ------------------------------------------------------------------ * V4L2 Common IOCTLs * ------------------------------------------------------------------ @@ -180,8 +166,8 @@ static void cal_calc_format_size(struct cal_ctx *ctx, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", - __func__, fourcc_to_str(f->fmt.pix.pixelformat), + ctx_dbg(3, ctx, "%s: fourcc: %p4cc size: %dx%d bpl:%d img_size:%d\n", + __func__, &f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); } @@ -509,8 +495,8 @@ static void cal_mc_try_fmt(struct cal_ctx *ctx, struct v4l2_format *f, if (info) *info = fmtinfo; - ctx_dbg(3, ctx, "%s: %s %ux%u (bytesperline %u sizeimage %u)\n", - __func__, fourcc_to_str(format->pixelformat), + ctx_dbg(3, ctx, "%s: %p4cc %ux%u (bytesperline %u sizeimage %u)\n", + __func__, &format->pixelformat, format->width, format->height, format->bytesperline, format->sizeimage); } @@ -866,8 +852,8 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) if (mbus_code.code == fmtinfo->code) { ctx->active_fmt[i] = fmtinfo; ctx_dbg(2, ctx, - "matched fourcc: %s: code: %04x idx: %u\n", - fourcc_to_str(fmtinfo->fourcc), + "matched fourcc: %p4cc: code: %04x idx: %u\n", + &fmtinfo->fourcc, fmtinfo->code, i); ctx->num_active_fmt = ++i; } -- cgit v1.2.3-59-g8ed1b From a5b18fd769b7dc2e77a9e6a390844cbf50626ae8 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 26 Mar 2025 13:34:02 +0200 Subject: media: ti: cal: Fix wrong goto on error path If pm_runtime_resume_and_get() fails, we should unprepare the context, but currently we skip that as we goto to a later line. Reviewed-by: Kieran Bingham Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/cal/cal-video.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index 4eb77f46f030..29c38bf8d7a1 100644 --- a/drivers/media/platform/ti/cal/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -744,7 +744,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) ret = pm_runtime_resume_and_get(ctx->cal->dev); if (ret < 0) - goto error_pipeline; + goto error_unprepare; cal_ctx_set_dma_addr(ctx, addr); cal_ctx_start(ctx); @@ -761,8 +761,8 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) error_stop: cal_ctx_stop(ctx); pm_runtime_put_sync(ctx->cal->dev); +error_unprepare: cal_ctx_unprepare(ctx); - error_pipeline: video_device_pipeline_stop(&ctx->vdev); error_release_buffers: -- cgit v1.2.3-59-g8ed1b From c57e372b7b4230d8c4fbaf08704bc4c492a67684 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 26 Mar 2025 13:34:03 +0200 Subject: media: ti: cal: Add streams support Add multiplexed streams support. CAL has 8 DMA-engines and can capture 8 separate streams at the same time. The driver filters the streams based on CSI-2 virtual channel number and datatype. CAL may have (depending on the SoC) two CSI-2 RX blocks, which share the 8 DMA-engines, so the number of capturable streams does not change even if there are two CSI-2 RX blocks. Add 8 video device nodes, each representing a single DMA-engine, and set the number of source pads on CSI-2 RX blocks to 8. Each video node can be connected to any of the source pads on either of the CSI-2 RX instances using media links. CSI-2 RX block's subdevice internal routing is used to route the incoming CSI-2 streams to one of the 8 source pads. Only video data streams are supported at the moment. Signed-off-by: Tomi Valkeinen Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/cal/cal-camerarx.c | 266 +++++++++++++++++++++------ drivers/media/platform/ti/cal/cal-video.c | 127 +++++++++---- drivers/media/platform/ti/cal/cal.c | 45 +++-- drivers/media/platform/ti/cal/cal.h | 3 +- 4 files changed, 337 insertions(+), 104 deletions(-) diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c index 9cc875665695..00a71dac0ff4 100644 --- a/drivers/media/platform/ti/cal/cal-camerarx.c +++ b/drivers/media/platform/ti/cal/cal-camerarx.c @@ -49,21 +49,38 @@ static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy) { struct v4l2_mbus_config_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2; u32 num_lanes = mipi_csi2->num_data_lanes; - const struct cal_format_info *fmtinfo; struct v4l2_subdev_state *state; - struct v4l2_mbus_framefmt *fmt; u32 bpp; s64 freq; + /* + * v4l2_get_link_freq() uses V4L2_CID_LINK_FREQ first, and falls back + * to V4L2_CID_PIXEL_RATE if V4L2_CID_LINK_FREQ is not available. + * + * With multistream input there is no single pixel rate, and thus we + * cannot use V4L2_CID_PIXEL_RATE, so we pass 0 as the bpp which + * causes v4l2_get_link_freq() to return an error if it falls back to + * V4L2_CID_PIXEL_RATE. + */ + state = v4l2_subdev_get_locked_active_state(&phy->subdev); - fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK); + if (state->routing.num_routes > 1) { + bpp = 0; + } else { + struct v4l2_subdev_route *route = &state->routing.routes[0]; + const struct cal_format_info *fmtinfo; + struct v4l2_mbus_framefmt *fmt; - fmtinfo = cal_format_by_code(fmt->code); - if (!fmtinfo) - return -EINVAL; + fmt = v4l2_subdev_state_get_format(state, route->sink_pad, + route->sink_stream); - bpp = fmtinfo->bpp; + fmtinfo = cal_format_by_code(fmt->code); + if (!fmtinfo) + return -EINVAL; + + bpp = fmtinfo->bpp; + } freq = v4l2_get_link_freq(&phy->source->entity.pads[phy->source_pad], bpp, 2 * num_lanes); @@ -285,15 +302,32 @@ static void cal_camerarx_ppi_disable(struct cal_camerarx *phy) 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } -static int cal_camerarx_start(struct cal_camerarx *phy) +static int cal_camerarx_start(struct cal_camerarx *phy, u32 sink_stream) { + struct media_pad *remote_pad; s64 link_freq; u32 sscounter; u32 val; int ret; + remote_pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]); + + /* + * We need to enable the PHY hardware when enabling the first stream, + * but for the following streams we just propagate the enable_streams + * to the source. + */ + if (phy->enable_count > 0) { + ret = v4l2_subdev_enable_streams(phy->source, remote_pad->index, + BIT(sink_stream)); + if (ret) { + phy_err(phy, "enable streams failed in source: %d\n", ret); + return ret; + } + phy->enable_count++; + return 0; } @@ -395,7 +429,8 @@ static int cal_camerarx_start(struct cal_camerarx *phy) * Start the source to enable the CSI-2 HS clock. We can now wait for * CSI-2 PHY reset to complete. */ - ret = v4l2_subdev_call(phy->source, video, s_stream, 1); + ret = v4l2_subdev_enable_streams(phy->source, remote_pad->index, + BIT(sink_stream)); if (ret) { v4l2_subdev_call(phy->source, core, s_power, 0); cal_camerarx_disable_irqs(phy); @@ -426,12 +461,22 @@ static int cal_camerarx_start(struct cal_camerarx *phy) return 0; } -static void cal_camerarx_stop(struct cal_camerarx *phy) +static void cal_camerarx_stop(struct cal_camerarx *phy, u32 sink_stream) { + struct media_pad *remote_pad; int ret; - if (--phy->enable_count > 0) + remote_pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]); + + if (--phy->enable_count > 0) { + ret = v4l2_subdev_disable_streams(phy->source, + remote_pad->index, + BIT(sink_stream)); + if (ret) + phy_err(phy, "stream off failed in subdev\n"); + return; + } cal_camerarx_ppi_disable(phy); @@ -451,7 +496,9 @@ static void cal_camerarx_stop(struct cal_camerarx *phy) /* Disable the phy */ cal_camerarx_disable(phy); - if (v4l2_subdev_call(phy->source, video, s_stream, 0)) + ret = v4l2_subdev_disable_streams(phy->source, remote_pad->index, + BIT(sink_stream)); + if (ret) phy_err(phy, "stream off failed in subdev\n"); ret = v4l2_subdev_call(phy->source, core, s_power, 0); @@ -600,22 +647,50 @@ static inline struct cal_camerarx *to_cal_camerarx(struct v4l2_subdev *sd) return container_of(sd, struct cal_camerarx, subdev); } -static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable) +struct cal_camerarx * +cal_camerarx_get_phy_from_entity(struct media_entity *entity) +{ + struct v4l2_subdev *sd; + + sd = media_entity_to_v4l2_subdev(entity); + if (!sd) + return NULL; + + return to_cal_camerarx(sd); +} + +static int cal_camerarx_sd_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) { struct cal_camerarx *phy = to_cal_camerarx(sd); - struct v4l2_subdev_state *state; - int ret = 0; + u32 sink_stream; + int ret; - state = v4l2_subdev_lock_and_get_active_state(sd); + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0, + NULL, &sink_stream); + if (ret) + return ret; - if (enable) - ret = cal_camerarx_start(phy); - else - cal_camerarx_stop(phy); + return cal_camerarx_start(phy, sink_stream); +} - v4l2_subdev_unlock_state(state); +static int cal_camerarx_sd_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct cal_camerarx *phy = to_cal_camerarx(sd); + u32 sink_stream; + int ret; - return ret; + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0, + NULL, &sink_stream); + if (ret) + return ret; + + cal_camerarx_stop(phy, sink_stream); + + return 0; } static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, @@ -629,8 +704,12 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - fmt = v4l2_subdev_state_get_format(state, - CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_opposite_stream_format(state, + code->pad, + code->stream); + if (!fmt) + return -EINVAL; + code->code = fmt->code; } else { if (code->index >= cal_num_formats) @@ -655,8 +734,12 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, if (cal_rx_pad_is_source(fse->pad)) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_state_get_format(state, - CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_opposite_stream_format(state, + fse->pad, + fse->stream); + if (!fmt) + return -EINVAL; + if (fse->code != fmt->code) return -EINVAL; @@ -712,36 +795,77 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, /* Store the format and propagate it to the source pad. */ - fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK); + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); + if (!fmt) + return -EINVAL; + *fmt = format->format; - fmt = v4l2_subdev_state_get_format(state, - CAL_CAMERARX_PAD_FIRST_SOURCE); + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + *fmt = format->format; return 0; } +static int cal_camerarx_set_routing(struct v4l2_subdev *sd, + 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(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | + V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING); + if (ret) + return ret; + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); + if (ret) + return ret; + + return 0; +} + +static int cal_camerarx_sd_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + return cal_camerarx_set_routing(sd, state, routing); +} + static int cal_camerarx_sd_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state) { - struct v4l2_subdev_format format = { - .which = state ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = CAL_CAMERARX_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 = 0, + .sink_stream = 0, + .source_pad = 1, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + } }; + + struct v4l2_subdev_krouting routing = { + .num_routes = 1, + .routes = routes, }; - return cal_camerarx_sd_set_fmt(sd, state, &format); + /* Initialize routing to single route to the fist source pad */ + return cal_camerarx_set_routing(sd, state, &routing); } static int cal_camerarx_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, @@ -750,48 +874,71 @@ static int cal_camerarx_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, struct cal_camerarx *phy = to_cal_camerarx(sd); struct v4l2_mbus_frame_desc remote_desc; const struct media_pad *remote_pad; + struct v4l2_subdev_state *state; + u32 sink_stream; + unsigned int i; int ret; + state = v4l2_subdev_lock_and_get_active_state(sd); + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + pad, 0, + NULL, &sink_stream); + if (ret) + goto out_unlock; + remote_pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]); - if (!remote_pad) - return -EPIPE; + if (!remote_pad) { + ret = -EPIPE; + goto out_unlock; + } ret = v4l2_subdev_call(phy->source, pad, get_frame_desc, remote_pad->index, &remote_desc); if (ret) - return ret; + goto out_unlock; if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { cal_err(phy->cal, "Frame descriptor does not describe CSI-2 link"); - return -EINVAL; + ret = -EINVAL; + goto out_unlock; } - if (remote_desc.num_entries > 1) - cal_err(phy->cal, - "Multiple streams not supported in remote frame descriptor, using the first one\n"); + for (i = 0; i < remote_desc.num_entries; i++) { + if (remote_desc.entry[i].stream == sink_stream) + break; + } + + if (i == remote_desc.num_entries) { + cal_err(phy->cal, "Stream %u not found in remote frame desc\n", + sink_stream); + ret = -EINVAL; + goto out_unlock; + } fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; fd->num_entries = 1; - fd->entry[0] = remote_desc.entry[0]; + fd->entry[0] = remote_desc.entry[i]; - return 0; -} +out_unlock: + v4l2_subdev_unlock_state(state); -static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = { - .s_stream = cal_camerarx_sd_s_stream, -}; + return ret; +} static const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = { + .enable_streams = cal_camerarx_sd_enable_streams, + .disable_streams = cal_camerarx_sd_disable_streams, .enum_mbus_code = cal_camerarx_sd_enum_mbus_code, .enum_frame_size = cal_camerarx_sd_enum_frame_size, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = cal_camerarx_sd_set_fmt, + .set_routing = cal_camerarx_sd_set_routing, .get_frame_desc = cal_camerarx_get_frame_desc, }; static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = { - .video = &cal_camerarx_video_ops, .pad = &cal_camerarx_pad_ops, }; @@ -801,6 +948,7 @@ static const struct v4l2_subdev_internal_ops cal_camerarx_internal_ops = { static const struct media_entity_operations cal_camerarx_media_ops = { .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, }; /* ------------------------------------------------------------------ @@ -852,7 +1000,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, v4l2_subdev_init(sd, &cal_camerarx_subdev_ops); sd->internal_ops = &cal_camerarx_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; snprintf(sd->name, sizeof(sd->name), "CAMERARX%u", instance); sd->dev = cal->dev; diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index 29c38bf8d7a1..d40e24ab1127 100644 --- a/drivers/media/platform/ti/cal/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -108,9 +108,10 @@ static int __subdev_get_format(struct cal_ctx *ctx, .pad = 0, }; struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + struct v4l2_subdev *sd = ctx->phy->source; int ret; - ret = v4l2_subdev_call(ctx->phy->source, pad, get_fmt, NULL, &sd_fmt); + ret = v4l2_subdev_call_state_active(sd, pad, get_fmt, &sd_fmt); if (ret) return ret; @@ -130,11 +131,12 @@ static int __subdev_set_format(struct cal_ctx *ctx, .pad = 0, }; struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + struct v4l2_subdev *sd = ctx->phy->source; int ret; *mbus_fmt = *fmt; - ret = v4l2_subdev_call(ctx->phy->source, pad, set_fmt, NULL, &sd_fmt); + ret = v4l2_subdev_call_state_active(sd, pad, set_fmt, &sd_fmt); if (ret) return ret; @@ -176,6 +178,7 @@ static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cal_ctx *ctx = video_drvdata(file); + struct v4l2_subdev *sd = ctx->phy->source; const struct cal_format_info *fmtinfo; struct v4l2_subdev_frame_size_enum fse = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, @@ -201,8 +204,8 @@ static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv, for (fse.index = 0; ; fse.index++) { int ret; - ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size, - NULL, &fse); + ret = v4l2_subdev_call_state_active(sd, pad, enum_frame_size, + &fse); if (ret) break; @@ -238,6 +241,7 @@ static int cal_legacy_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cal_ctx *ctx = video_drvdata(file); + struct v4l2_subdev *sd = &ctx->phy->subdev; struct vb2_queue *q = &ctx->vb_vidq; struct v4l2_subdev_format sd_fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, @@ -277,7 +281,7 @@ static int cal_legacy_s_fmt_vid_cap(struct file *file, void *priv, ctx->v_fmt.fmt.pix.field = sd_fmt.format.field; cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt); - v4l2_subdev_call(&ctx->phy->subdev, pad, set_fmt, NULL, &sd_fmt); + v4l2_subdev_call_state_active(sd, pad, set_fmt, &sd_fmt); ctx->fmtinfo = fmtinfo; *f = ctx->v_fmt; @@ -289,6 +293,7 @@ static int cal_legacy_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { struct cal_ctx *ctx = video_drvdata(file); + struct v4l2_subdev *sd = ctx->phy->source; const struct cal_format_info *fmtinfo; struct v4l2_subdev_frame_size_enum fse = { .index = fsize->index, @@ -307,8 +312,7 @@ static int cal_legacy_enum_framesizes(struct file *file, void *fh, fse.code = fmtinfo->code; - ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size, NULL, - &fse); + ret = v4l2_subdev_call_state_active(sd, pad, enum_frame_size, &fse); if (ret) return ret; @@ -350,6 +354,7 @@ static int cal_legacy_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *fival) { struct cal_ctx *ctx = video_drvdata(file); + struct v4l2_subdev *sd = ctx->phy->source; const struct cal_format_info *fmtinfo; struct v4l2_subdev_frame_interval_enum fie = { .index = fival->index, @@ -364,8 +369,8 @@ static int cal_legacy_enum_frameintervals(struct file *file, void *priv, return -EINVAL; fie.code = fmtinfo->code; - ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_interval, - NULL, &fie); + + ret = v4l2_subdev_call_state_active(sd, pad, enum_frame_interval, &fie); if (ret) return ret; fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; @@ -432,6 +437,9 @@ static int cal_mc_enum_fmt_vid_cap(struct file *file, void *priv, idx = 0; for (i = 0; i < cal_num_formats; ++i) { + if (cal_formats[i].meta) + continue; + if (f->mbus_code && cal_formats[i].code != f->mbus_code) continue; @@ -459,7 +467,7 @@ static void cal_mc_try_fmt(struct cal_ctx *ctx, struct v4l2_format *f, * supported. */ fmtinfo = cal_format_by_fourcc(f->fmt.pix.pixelformat); - if (!fmtinfo) + if (!fmtinfo || fmtinfo->meta) fmtinfo = &cal_formats[0]; /* @@ -675,16 +683,16 @@ static int cal_video_check_format(struct cal_ctx *ctx) { const struct v4l2_mbus_framefmt *format; struct v4l2_subdev_state *state; - struct media_pad *remote_pad; + struct media_pad *phy_source_pad; int ret = 0; - remote_pad = media_pad_remote_pad_first(&ctx->pad); - if (!remote_pad) + phy_source_pad = media_pad_remote_pad_first(&ctx->pad); + if (!phy_source_pad) return -ENODEV; state = v4l2_subdev_lock_and_get_active_state(&ctx->phy->subdev); - format = v4l2_subdev_state_get_format(state, remote_pad->index); + format = v4l2_subdev_state_get_format(state, phy_source_pad->index, 0); if (!format) { ret = -EINVAL; goto out; @@ -707,16 +715,28 @@ out: static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) { struct cal_ctx *ctx = vb2_get_drv_priv(vq); + struct media_pad *phy_source_pad; struct cal_buffer *buf; dma_addr_t addr; int ret; + phy_source_pad = media_pad_remote_pad_first(&ctx->pad); + if (!phy_source_pad) { + ctx_err(ctx, "Context not connected\n"); + ret = -ENODEV; + goto error_release_buffers; + } + ret = video_device_pipeline_alloc_start(&ctx->vdev); if (ret < 0) { ctx_err(ctx, "Failed to start media pipeline: %d\n", ret); goto error_release_buffers; } + /* Find the PHY connected to this video device */ + if (cal_mc_api) + ctx->phy = cal_camerarx_get_phy_from_entity(phy_source_pad->entity); + /* * Verify that the currently configured format matches the output of * the connected CAMERARX. @@ -749,7 +769,8 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) cal_ctx_set_dma_addr(ctx, addr); cal_ctx_start(ctx); - ret = v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 1); + ret = v4l2_subdev_enable_streams(&ctx->phy->subdev, + phy_source_pad->index, BIT(0)); if (ret) goto error_stop; @@ -774,10 +795,14 @@ error_release_buffers: static void cal_stop_streaming(struct vb2_queue *vq) { struct cal_ctx *ctx = vb2_get_drv_priv(vq); + struct media_pad *phy_source_pad; cal_ctx_stop(ctx); - v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 0); + phy_source_pad = media_pad_remote_pad_first(&ctx->pad); + + v4l2_subdev_disable_streams(&ctx->phy->subdev, phy_source_pad->index, + BIT(0)); pm_runtime_put_sync(ctx->cal->dev); @@ -786,6 +811,9 @@ static void cal_stop_streaming(struct vb2_queue *vq) cal_release_buffers(ctx, VB2_BUF_STATE_ERROR); video_device_pipeline_stop(&ctx->vdev); + + if (cal_mc_api) + ctx->phy = NULL; } static const struct vb2_ops cal_video_qops = { @@ -812,6 +840,7 @@ static const struct v4l2_file_operations cal_fops = { static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) { + struct v4l2_subdev *sd = ctx->phy->source; struct v4l2_mbus_framefmt mbus_fmt; const struct cal_format_info *fmtinfo; unsigned int i, j, k; @@ -831,20 +860,20 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; - ret = v4l2_subdev_call(ctx->phy->source, pad, enum_mbus_code, - NULL, &mbus_code); + ret = v4l2_subdev_call_state_active(sd, pad, enum_mbus_code, + &mbus_code); if (ret == -EINVAL) break; if (ret) { ctx_err(ctx, "Error enumerating mbus codes in subdev %s: %d\n", - ctx->phy->source->name, ret); + sd->name, ret); return ret; } ctx_dbg(2, ctx, "subdev %s: code: %04x idx: %u\n", - ctx->phy->source->name, mbus_code.code, j); + sd->name, mbus_code.code, j); for (k = 0; k < cal_num_formats; k++) { fmtinfo = &cal_formats[k]; @@ -862,7 +891,7 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) if (i == 0) { ctx_err(ctx, "No suitable format reported by subdev %s\n", - ctx->phy->source->name); + sd->name); return -EINVAL; } @@ -948,16 +977,52 @@ int cal_ctx_v4l2_register(struct cal_ctx *ctx) return ret; } - ret = media_create_pad_link(&ctx->phy->subdev.entity, - CAL_CAMERARX_PAD_FIRST_SOURCE, - &vfd->entity, 0, - MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (ret) { - ctx_err(ctx, "Failed to create media link for context %u\n", - ctx->dma_ctx); - video_unregister_device(vfd); - return ret; + if (cal_mc_api) { + u16 phy_idx; + u16 pad_idx; + + /* Create links from all video nodes to all PHYs */ + + for (phy_idx = 0; phy_idx < ctx->cal->data->num_csi2_phy; + ++phy_idx) { + struct media_entity *phy_entity = + &ctx->cal->phy[phy_idx]->subdev.entity; + + for (pad_idx = 1; pad_idx < CAL_CAMERARX_NUM_PADS; + ++pad_idx) { + /* + * Enable only links from video0 to PHY0 pad 1, + * and video1 to PHY1 pad 1. + */ + bool enable = (ctx->dma_ctx == 0 && + phy_idx == 0 && pad_idx == 1) || + (ctx->dma_ctx == 1 && + phy_idx == 1 && pad_idx == 1); + + ret = media_create_pad_link(phy_entity, pad_idx, + &vfd->entity, 0, + enable ? MEDIA_LNK_FL_ENABLED : 0); + if (ret) { + ctx_err(ctx, + "Failed to create media link for context %u\n", + ctx->dma_ctx); + video_unregister_device(vfd); + return ret; + } + } + } + } else { + ret = media_create_pad_link(&ctx->phy->subdev.entity, + CAL_CAMERARX_PAD_FIRST_SOURCE, + &vfd->entity, 0, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (ret) { + ctx_err(ctx, + "Failed to create media link for context %u\n", + ctx->dma_ctx); + video_unregister_device(vfd); + return ret; + } } ctx_info(ctx, "V4L2 device registered as %s\n", diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c index 6cb3e5f49686..b644ed890412 100644 --- a/drivers/media/platform/ti/cal/cal.c +++ b/drivers/media/platform/ti/cal/cal.c @@ -481,8 +481,9 @@ int cal_ctx_prepare(struct cal_ctx *ctx) ctx->vc = 0; ctx->datatype = CAL_CSI2_CTX_DT_ANY; } else if (!ret) { - ctx_dbg(2, ctx, "Framedesc: len %u, vc %u, dt %#x\n", - entry.length, entry.bus.csi2.vc, entry.bus.csi2.dt); + ctx_dbg(2, ctx, "Framedesc: stream %u, len %u, vc %u, dt %#x\n", + entry.stream, entry.length, entry.bus.csi2.vc, + entry.bus.csi2.dt); ctx->vc = entry.bus.csi2.vc; ctx->datatype = entry.bus.csi2.dt; @@ -490,7 +491,7 @@ int cal_ctx_prepare(struct cal_ctx *ctx) return ret; } - ctx->use_pix_proc = !ctx->fmtinfo->meta; + ctx->use_pix_proc = ctx->vb_vidq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ctx->use_pix_proc) { ret = cal_reserve_pix_proc(ctx->cal); @@ -1016,7 +1017,6 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) return NULL; ctx->cal = cal; - ctx->phy = cal->phy[inst]; ctx->dma_ctx = inst; ctx->csi2_ctx = inst; ctx->cport = inst; @@ -1228,18 +1228,37 @@ static int cal_probe(struct platform_device *pdev) } /* Create contexts. */ - for (i = 0; i < cal->data->num_csi2_phy; ++i) { - if (!cal->phy[i]->source_node) - continue; + if (!cal_mc_api) { + for (i = 0; i < cal->data->num_csi2_phy; ++i) { + struct cal_ctx *ctx; + + if (!cal->phy[i]->source_node) + continue; + + ctx = cal_ctx_create(cal, i); + if (!ctx) { + cal_err(cal, "Failed to create context %u\n", cal->num_contexts); + ret = -ENODEV; + goto error_context; + } + + ctx->phy = cal->phy[i]; - cal->ctx[cal->num_contexts] = cal_ctx_create(cal, i); - if (!cal->ctx[cal->num_contexts]) { - cal_err(cal, "Failed to create context %u\n", cal->num_contexts); - ret = -ENODEV; - goto error_context; + cal->ctx[cal->num_contexts++] = ctx; } + } else { + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { + struct cal_ctx *ctx; + + ctx = cal_ctx_create(cal, i); + if (!ctx) { + cal_err(cal, "Failed to create context %u\n", i); + ret = -ENODEV; + goto error_context; + } - cal->num_contexts++; + cal->ctx[cal->num_contexts++] = ctx; + } } /* Register the media device. */ diff --git a/drivers/media/platform/ti/cal/cal.h b/drivers/media/platform/ti/cal/cal.h index 72a246a64d9e..33b1885aafe1 100644 --- a/drivers/media/platform/ti/cal/cal.h +++ b/drivers/media/platform/ti/cal/cal.h @@ -45,7 +45,7 @@ #define CAL_CAMERARX_PAD_SINK 0 #define CAL_CAMERARX_PAD_FIRST_SOURCE 1 -#define CAL_CAMERARX_NUM_SOURCE_PADS 1 +#define CAL_CAMERARX_NUM_SOURCE_PADS 8 #define CAL_CAMERARX_NUM_PADS (1 + CAL_CAMERARX_NUM_SOURCE_PADS) static inline bool cal_rx_pad_is_sink(u32 pad) @@ -320,6 +320,7 @@ const struct cal_format_info *cal_format_by_code(u32 code); void cal_quickdump_regs(struct cal_dev *cal); +struct cal_camerarx *cal_camerarx_get_phy_from_entity(struct media_entity *entity); void cal_camerarx_disable(struct cal_camerarx *phy); void cal_camerarx_i913_errata(struct cal_camerarx *phy); struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, -- cgit v1.2.3-59-g8ed1b From f2f0cd892515eda18857f47718eb7be93f9ddaca Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Mon, 10 Feb 2025 18:56:11 +0100 Subject: media: rcar-vin: Remove emulated SEQ_{TB,BT} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the driver was converted from soc_camera software support for V4L2_FIELD_SEQ_TB and V4L2_FIELD_SEQ_BT were added. This was done by capturing twice to the same VB2 buffer, but at different offsets. This turned out to be a bad idea and it never really worked properly in all situations. As the hardware can't support this mode natively remove trying to emulate it in software. It's still possible to capture TOP or BOTTOM fields separately or both ALTERNATING. If user-space wants the same fields in the same buffer the same hack to capture twice to the same buffer can be done. Removing this error prone emulated support pave ways in future work to simplify the internal buffer handling and making it less fragile, while enabling adding support for other features the hardware actually supports. Signed-off-by: Niklas Söderlund Reviewed-by: Tomi Valkeinen Tested-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rcar-vin/rcar-dma.c | 58 +++------------------- .../media/platform/renesas/rcar-vin/rcar-v4l2.c | 7 --- drivers/media/platform/renesas/rcar-vin/rcar-vin.h | 18 ------- 3 files changed, 7 insertions(+), 76 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index edb06730bc7c..fc64f013ec05 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -642,8 +642,6 @@ void rvin_scaler_gen3(struct rvin_dev *vin) case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_SEQ_TB: - case V4L2_FIELD_SEQ_BT: clip_size |= vin->compose.height / 2; break; default: @@ -711,8 +709,6 @@ static int rvin_setup(struct rvin_dev *vin) case V4L2_FIELD_INTERLACED_BT: vnmc = VNMC_IM_FULL | VNMC_FOC; break; - case V4L2_FIELD_SEQ_TB: - case V4L2_FIELD_SEQ_BT: case V4L2_FIELD_NONE: case V4L2_FIELD_ALTERNATE: vnmc = VNMC_IM_ODD_EVEN; @@ -1005,33 +1001,14 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) struct rvin_buffer *buf; struct vb2_v4l2_buffer *vbuf; dma_addr_t phys_addr; - int prev; /* A already populated slot shall never be overwritten. */ if (WARN_ON(vin->buf_hw[slot].buffer)) return; - prev = (slot == 0 ? HW_BUFFER_NUM : slot) - 1; - - if (vin->buf_hw[prev].type == HALF_TOP) { - vbuf = vin->buf_hw[prev].buffer; - vin->buf_hw[slot].buffer = vbuf; - vin->buf_hw[slot].type = HALF_BOTTOM; - switch (vin->format.pixelformat) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV16: - phys_addr = vin->buf_hw[prev].phys + - vin->format.sizeimage / 4; - break; - default: - phys_addr = vin->buf_hw[prev].phys + - vin->format.sizeimage / 2; - break; - } - } else if ((vin->state != STOPPED && vin->state != RUNNING) || - list_empty(&vin->buf_list)) { + if ((vin->state != STOPPED && vin->state != RUNNING) || + list_empty(&vin->buf_list)) { vin->buf_hw[slot].buffer = NULL; - vin->buf_hw[slot].type = FULL; phys_addr = vin->scratch_phys; } else { /* Keep track of buffer we give to HW */ @@ -1040,16 +1017,12 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) list_del_init(to_buf_list(vbuf)); vin->buf_hw[slot].buffer = vbuf; - vin->buf_hw[slot].type = - V4L2_FIELD_IS_SEQUENTIAL(vin->format.field) ? - HALF_TOP : FULL; - /* Setup DMA */ phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); } - vin_dbg(vin, "Filling HW slot: %d type: %d buffer: %p\n", - slot, vin->buf_hw[slot].type, vin->buf_hw[slot].buffer); + vin_dbg(vin, "Filling HW slot: %d buffer: %p\n", + slot, vin->buf_hw[slot].buffer); vin->buf_hw[slot].phys = phys_addr; rvin_set_slot_addr(vin, slot, phys_addr); @@ -1057,15 +1030,12 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) static int rvin_capture_start(struct rvin_dev *vin) { - int slot, ret; + int ret; - for (slot = 0; slot < HW_BUFFER_NUM; slot++) { + for (unsigned int slot = 0; slot < HW_BUFFER_NUM; slot++) { vin->buf_hw[slot].buffer = NULL; - vin->buf_hw[slot].type = FULL; - } - - for (slot = 0; slot < HW_BUFFER_NUM; slot++) rvin_fill_hw_slot(vin, slot); + } ret = rvin_setup(vin); if (ret) @@ -1146,16 +1116,6 @@ static irqreturn_t rvin_irq(int irq, void *data) /* Capture frame */ if (vin->buf_hw[slot].buffer) { - /* - * Nothing to do but refill the hardware slot if - * capture only filled first half of vb2 buffer. - */ - if (vin->buf_hw[slot].type == HALF_TOP) { - vin->buf_hw[slot].buffer = NULL; - rvin_fill_hw_slot(vin, slot); - goto done; - } - vin->buf_hw[slot].buffer->field = rvin_get_active_field(vin, vnms); vin->buf_hw[slot].buffer->sequence = vin->sequence; @@ -1306,8 +1266,6 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_SEQ_TB: - case V4L2_FIELD_SEQ_BT: /* Supported natively */ break; case V4L2_FIELD_ALTERNATE: @@ -1320,8 +1278,6 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_SEQ_TB: - case V4L2_FIELD_SEQ_BT: /* Use VIN hardware to combine the two fields */ fmt.format.height *= 2; break; diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c index 756fdfdbce61..a5763f1c5784 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c @@ -161,9 +161,6 @@ static u32 rvin_format_bytesperline(struct rvin_dev *vin, break; } - if (V4L2_FIELD_IS_SEQUENTIAL(pix->field)) - align = 0x80; - return ALIGN(pix->width, align) * fmt->bpp; } @@ -194,8 +191,6 @@ static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix) case V4L2_FIELD_INTERLACED_BT: case V4L2_FIELD_INTERLACED: case V4L2_FIELD_ALTERNATE: - case V4L2_FIELD_SEQ_TB: - case V4L2_FIELD_SEQ_BT: break; default: pix->field = RVIN_DEFAULT_FIELD; @@ -504,8 +499,6 @@ static int rvin_remote_rectangle(struct rvin_dev *vin, struct v4l2_rect *rect) case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_SEQ_TB: - case V4L2_FIELD_SEQ_BT: rect->height *= 2; break; } diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h index f87d4bc9e53e..d5763462809a 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h @@ -77,23 +77,6 @@ enum rvin_dma_state { SUSPENDED, }; -/** - * enum rvin_buffer_type - * - * Describes how a buffer is given to the hardware. To be able - * to capture SEQ_TB/BT it's needed to capture to the same vb2 - * buffer twice so the type of buffer needs to be kept. - * - * @FULL: One capture fills the whole vb2 buffer - * @HALF_TOP: One capture fills the top half of the vb2 buffer - * @HALF_BOTTOM: One capture fills the bottom half of the vb2 buffer - */ -enum rvin_buffer_type { - FULL, - HALF_TOP, - HALF_BOTTOM, -}; - /** * struct rvin_video_format - Data format stored in memory * @fourcc: Pixelformat @@ -237,7 +220,6 @@ struct rvin_dev { spinlock_t qlock; struct { struct vb2_v4l2_buffer *buffer; - enum rvin_buffer_type type; dma_addr_t phys; } buf_hw[HW_BUFFER_NUM]; struct list_head buf_list; -- cgit v1.2.3-59-g8ed1b From 1dadd89b58108d3df701d6d9e19cd34818f97f7f Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Mon, 10 Feb 2025 18:56:12 +0100 Subject: media: rcar-vin: Remove superfluous suspended state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VIN state of suspended is superfluous. The logic was that when the device were suspended and in a RUNNING state the state was set to SUSPENDED. And when resuming it checked if the state is SUSPENDED and if so started the device and changed it to RUNNING. This can be avoided by simply checking if the device is in a RUNNING state at both suspend and resume callbacks. Remove the unneeded complexity. Signed-off-by: Niklas Söderlund Reviewed-by: Tomi Valkeinen Tested-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rcar-vin/rcar-core.c | 4 +--- drivers/media/platform/renesas/rcar-vin/rcar-vin.h | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c index ddfb18e6e7a4..b8e35ef4d9d8 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c @@ -1085,8 +1085,6 @@ static int __maybe_unused rvin_suspend(struct device *dev) rvin_stop_streaming(vin); - vin->state = SUSPENDED; - return 0; } @@ -1094,7 +1092,7 @@ static int __maybe_unused rvin_resume(struct device *dev) { struct rvin_dev *vin = dev_get_drvdata(dev); - if (vin->state != SUSPENDED) + if (vin->state != RUNNING) return 0; /* diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h index d5763462809a..4cb25d8bbf32 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h @@ -67,14 +67,12 @@ enum rvin_isp_id { * @STARTING: Capture starting up * @RUNNING: Operation in progress have buffers * @STOPPING: Stopping operation - * @SUSPENDED: Capture is suspended */ enum rvin_dma_state { STOPPED = 0, STARTING, RUNNING, STOPPING, - SUSPENDED, }; /** -- cgit v1.2.3-59-g8ed1b From 25482a986e44fddd3af77458071fbbd222795917 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Mon, 10 Feb 2025 18:56:13 +0100 Subject: media: rcar-vin: Remove superfluous starting state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The STARTING state is superfluous and can be replaced with a check of the sequence counter. The design idea is that the first buffer returned from the driver have to come from the first hardware buffer slot. Failing this the first 3 buffers queued to the device can be returned out-of-order. But it's much clearer to check the sequence counter to only return the first buffer if it comes from hardware slot 0 then it is to carry around an extra state just for this. Remove the unneeded state and replace it with a simpler check. Signed-off-by: Niklas Söderlund Reviewed-by: Tomi Valkeinen Tested-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rcar-vin/rcar-dma.c | 5 ++--- drivers/media/platform/renesas/rcar-vin/rcar-vin.h | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index fc64f013ec05..125af7ae9fbf 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -1048,7 +1048,7 @@ static int rvin_capture_start(struct rvin_dev *vin) /* Continuous Frame Capture Mode */ rvin_write(vin, VNFC_C_FRAME, VNFC_REG); - vin->state = STARTING; + vin->state = RUNNING; return 0; } @@ -1104,14 +1104,13 @@ static irqreturn_t rvin_irq(int irq, void *data) * To hand buffers back in a known order to userspace start * to capture first from slot 0. */ - if (vin->state == STARTING) { + if (!vin->sequence) { if (slot != 0) { vin_dbg(vin, "Starting sync slot: %d\n", slot); goto done; } vin_dbg(vin, "Capture start synced!\n"); - vin->state = RUNNING; } /* Capture frame */ diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h index 4cb25d8bbf32..f13ef379d095 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h @@ -64,13 +64,11 @@ enum rvin_isp_id { /** * enum rvin_dma_state - DMA states * @STOPPED: No operation in progress - * @STARTING: Capture starting up * @RUNNING: Operation in progress have buffers * @STOPPING: Stopping operation */ enum rvin_dma_state { STOPPED = 0, - STARTING, RUNNING, STOPPING, }; -- cgit v1.2.3-59-g8ed1b From c1eefe88c3954390473ed092f0d4718a810bd61c Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Mon, 10 Feb 2025 18:56:14 +0100 Subject: media: rcar-vin: Simplify the shutdown process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When shutting down capture extra care was needed to try and complete a buffer that was involved in the emulated support for SEQ_{TB,BT}. This was needed as a buffer might be queued once to the driver, but two times to the hardware using different offsets. As support for SEQ_{TB,BT} is now removed this shutdown process can be greatly simplified. And in addition the state keeping of the VIN device can be reduced to a single boolean value instead of keeping track of this SEQ_{TB,BT} stopping dance. Signed-off-by: Niklas Söderlund Reviewed-by: Tomi Valkeinen Tested-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../media/platform/renesas/rcar-vin/rcar-core.c | 4 +- drivers/media/platform/renesas/rcar-vin/rcar-dma.c | 75 ++++++---------------- drivers/media/platform/renesas/rcar-vin/rcar-vin.h | 18 +----- 3 files changed, 26 insertions(+), 71 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c index b8e35ef4d9d8..cfbc9ec27706 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c @@ -1080,7 +1080,7 @@ static int __maybe_unused rvin_suspend(struct device *dev) { struct rvin_dev *vin = dev_get_drvdata(dev); - if (vin->state != RUNNING) + if (!vin->running) return 0; rvin_stop_streaming(vin); @@ -1092,7 +1092,7 @@ static int __maybe_unused rvin_resume(struct device *dev) { struct rvin_dev *vin = dev_get_drvdata(dev); - if (vin->state != RUNNING) + if (!vin->running) return 0; /* diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index 125af7ae9fbf..f7b80e61a987 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -1006,8 +1006,7 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) if (WARN_ON(vin->buf_hw[slot].buffer)) return; - if ((vin->state != STOPPED && vin->state != RUNNING) || - list_empty(&vin->buf_list)) { + if (list_empty(&vin->buf_list)) { vin->buf_hw[slot].buffer = NULL; phys_addr = vin->scratch_phys; } else { @@ -1048,8 +1047,6 @@ static int rvin_capture_start(struct rvin_dev *vin) /* Continuous Frame Capture Mode */ rvin_write(vin, VNFC_C_FRAME, VNFC_REG); - vin->state = RUNNING; - return 0; } @@ -1090,9 +1087,9 @@ static irqreturn_t rvin_irq(int irq, void *data) if (!(int_status & VNINTS_FIS)) goto done; - /* Nothing to do if capture status is 'STOPPED' */ - if (vin->state == STOPPED) { - vin_dbg(vin, "IRQ while state stopped\n"); + /* Nothing to do if not running. */ + if (!vin->running) { + vin_dbg(vin, "IRQ while not running, ignoring\n"); goto done; } @@ -1373,6 +1370,8 @@ int rvin_start_streaming(struct rvin_dev *vin) if (ret) rvin_set_stream(vin, 0); + vin->running = true; + spin_unlock_irqrestore(&vin->qlock, flags); return ret; @@ -1405,44 +1404,21 @@ err_scratch: void rvin_stop_streaming(struct rvin_dev *vin) { - unsigned int i, retries; unsigned long flags; - bool buffersFreed; spin_lock_irqsave(&vin->qlock, flags); - if (vin->state == STOPPED) { + if (!vin->running) { spin_unlock_irqrestore(&vin->qlock, flags); return; } - vin->state = STOPPING; - - /* Wait until only scratch buffer is used, max 3 interrupts. */ - retries = 0; - while (retries++ < RVIN_RETRIES) { - buffersFreed = true; - for (i = 0; i < HW_BUFFER_NUM; i++) - if (vin->buf_hw[i].buffer) - buffersFreed = false; - - if (buffersFreed) - break; - - spin_unlock_irqrestore(&vin->qlock, flags); - msleep(RVIN_TIMEOUT_MS); - spin_lock_irqsave(&vin->qlock, flags); - } - /* Wait for streaming to stop */ - retries = 0; - while (retries++ < RVIN_RETRIES) { - + for (unsigned int i = 0; i < RVIN_RETRIES; i++) { rvin_capture_stop(vin); /* Check if HW is stopped */ if (!rvin_capture_active(vin)) { - vin->state = STOPPED; break; } @@ -1451,32 +1427,25 @@ void rvin_stop_streaming(struct rvin_dev *vin) spin_lock_irqsave(&vin->qlock, flags); } - if (!buffersFreed || vin->state != STOPPED) { - /* - * If this happens something have gone horribly wrong. - * Set state to stopped to prevent the interrupt handler - * to make things worse... - */ - vin_err(vin, "Failed stop HW, something is seriously broken\n"); - vin->state = STOPPED; - } + if (rvin_capture_active(vin)) + vin_err(vin, "Hardware did not stop\n"); - spin_unlock_irqrestore(&vin->qlock, flags); + vin->running = false; - /* If something went wrong, free buffers with an error. */ - if (!buffersFreed) { - return_unused_buffers(vin, VB2_BUF_STATE_ERROR); - for (i = 0; i < HW_BUFFER_NUM; i++) { - if (vin->buf_hw[i].buffer) - vb2_buffer_done(&vin->buf_hw[i].buffer->vb2_buf, - VB2_BUF_STATE_ERROR); - } - } + spin_unlock_irqrestore(&vin->qlock, flags); rvin_set_stream(vin, 0); /* disable interrupts */ rvin_disable_interrupts(vin); + + /* Return unprocessed buffers from hardware. */ + for (unsigned int i = 0; i < HW_BUFFER_NUM; i++) { + if (vin->buf_hw[i].buffer) + vb2_buffer_done(&vin->buf_hw[i].buffer->vb2_buf, + VB2_BUF_STATE_ERROR); + } + } static void rvin_stop_streaming_vq(struct vb2_queue *vq) @@ -1522,8 +1491,6 @@ int rvin_dma_register(struct rvin_dev *vin, int irq) spin_lock_init(&vin->qlock); - vin->state = STOPPED; - for (i = 0; i < HW_BUFFER_NUM; i++) vin->buf_hw[i].buffer = NULL; @@ -1626,7 +1593,7 @@ void rvin_set_alpha(struct rvin_dev *vin, unsigned int alpha) vin->alpha = alpha; - if (vin->state == STOPPED) + if (!vin->running) goto out; switch (vin->format.pixelformat) { diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h index f13ef379d095..934474d2334a 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h @@ -61,18 +61,6 @@ enum rvin_isp_id { (((unsigned int)RVIN_CSI_MAX) > ((unsigned int)RVIN_ISP_MAX) ? \ (unsigned int)RVIN_CSI_MAX : (unsigned int)RVIN_ISP_MAX) -/** - * enum rvin_dma_state - DMA states - * @STOPPED: No operation in progress - * @RUNNING: Operation in progress have buffers - * @STOPPING: Stopping operation - */ -enum rvin_dma_state { - STOPPED = 0, - RUNNING, - STOPPING, -}; - /** * struct rvin_video_format - Data format stored in memory * @fourcc: Pixelformat @@ -173,11 +161,11 @@ struct rvin_info { * @scratch: cpu address for scratch buffer * @scratch_phys: physical address of the scratch buffer * - * @qlock: protects @buf_hw, @buf_list, @sequence and @state + * @qlock: Protects @buf_hw, @buf_list, @sequence and @running * @buf_hw: Keeps track of buffers given to HW slot * @buf_list: list of queued buffers * @sequence: V4L2 buffers sequence number - * @state: keeps track of operation state + * @running: Keeps track of if the VIN is running * * @is_csi: flag to mark the VIN as using a CSI-2 subdevice * @chsel: Cached value of the current CSI-2 channel selection @@ -220,7 +208,7 @@ struct rvin_dev { } buf_hw[HW_BUFFER_NUM]; struct list_head buf_list; unsigned int sequence; - enum rvin_dma_state state; + bool running; bool is_csi; unsigned int chsel; -- cgit v1.2.3-59-g8ed1b From 9396770c3add2142e21813ce8bc152e5e6524557 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Mon, 10 Feb 2025 18:56:15 +0100 Subject: media: rcar-csi2: Remove hack to detect NTSC content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In an effort to emulate support for SEQ_{TB,BT} in the R-Car VIN driver on data captured from a CVBS input a hack was added to detect NTSC vs PAL. This is ugly and as support for emulated SEQ_{TB,BT} have been removed from the VIN driver remove the ugly hack. Signed-off-by: Niklas Söderlund Reviewed-by: Tomi Valkeinen Tested-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rcar-csi2.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c index 38a3149f9724..9979de4f6ef1 100644 --- a/drivers/media/platform/renesas/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-csi2.c @@ -1075,16 +1075,10 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv, vcdt2 |= vcdt_part << ((i % 2) * 16); } - if (fmt->field == V4L2_FIELD_ALTERNATE) { + if (fmt->field == V4L2_FIELD_ALTERNATE) fld = FLD_DET_SEL(1) | FLD_FLD_EN4 | FLD_FLD_EN3 | FLD_FLD_EN2 | FLD_FLD_EN; - if (fmt->height == 240) - fld |= FLD_FLD_NUM(0); - else - fld |= FLD_FLD_NUM(1); - } - /* * Get the number of active data lanes inspecting the remote mbus * configuration. -- cgit v1.2.3-59-g8ed1b From 4e228c365d85a03d08c7f555011919f4b6353dcd Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Fri, 4 Apr 2025 15:53:41 +0200 Subject: media: ipu3-cio2: Replace deprecated PCI functions pcim_iomap_table() and pcim_iomap_regions() have been deprecated. Replace them with pcim_iomap_region(), and pass the actual driver name to that function. Signed-off-by: Philipp Stanner Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu3/ipu3-cio2.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 0c365eb59085..16fde96c9fb2 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1702,14 +1702,13 @@ static int cio2_pci_probe(struct pci_dev *pci_dev, dev_info(dev, "device 0x%x (rev: 0x%x)\n", pci_dev->device, pci_dev->revision); - r = pcim_iomap_regions(pci_dev, 1 << CIO2_PCI_BAR, pci_name(pci_dev)); + cio2->base = pcim_iomap_region(pci_dev, CIO2_PCI_BAR, CIO2_NAME); + r = PTR_ERR_OR_ZERO(cio2->base); if (r) { dev_err(dev, "failed to remap I/O memory (%d)\n", r); return -ENODEV; } - cio2->base = pcim_iomap_table(pci_dev)[CIO2_PCI_BAR]; - pci_set_drvdata(pci_dev, cio2); pci_set_master(pci_dev); -- cgit v1.2.3-59-g8ed1b From 3bb6339e2a133abf8e48d12114e6bb11820e36c0 Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Fri, 4 Apr 2025 15:53:43 +0200 Subject: media: intel/ipu6: Replace deprecated PCI functions pcim_iomap_table() and pcim_iomap_regions() have been deprecated. Furthermore, the "name" parameter in pcim_iomap_regions() and its successor, pcim_iomap_region(), should always reflect the driver name, whereas currently it is the device's name. Replace the deprecated functions with pcim_iomap_region() and pass the actual driver name. Signed-off-by: Philipp Stanner Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c index b00d0705fefa..1f4f20b9c94d 100644 --- a/drivers/media/pci/intel/ipu6/ipu6.c +++ b/drivers/media/pci/intel/ipu6/ipu6.c @@ -520,11 +520,11 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) phys = pci_resource_start(pdev, IPU6_PCI_BAR); dev_dbg(dev, "IPU6 PCI bar[%u] = %pa\n", IPU6_PCI_BAR, &phys); - ret = pcim_iomap_regions(pdev, 1 << IPU6_PCI_BAR, pci_name(pdev)); - if (ret) - return dev_err_probe(dev, ret, "Failed to I/O mem remapping\n"); + isp->base = pcim_iomap_region(pdev, IPU6_PCI_BAR, IPU6_NAME); + if (IS_ERR(isp->base)) + return dev_err_probe(dev, PTR_ERR(isp->base), + "Failed to I/O mem remapping\n"); - isp->base = pcim_iomap_table(pdev)[IPU6_PCI_BAR]; pci_set_drvdata(pdev, isp); pci_set_master(pdev); -- cgit v1.2.3-59-g8ed1b From df78f5928c4b699d0699ec0e2554571a45e38626 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 7 Apr 2025 10:13:07 +0100 Subject: media: dt-bindings: Add OmniVision OV02E10 Add bindings for OVO2E10 a two lane MIPI CSI, two megapixel 1080p RGB sensor. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bryan O'Donoghue Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../bindings/media/i2c/ovti,ov02e10.yaml | 113 +++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov02e10.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov02e10.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov02e10.yaml new file mode 100644 index 000000000000..4ac4e11a16c8 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov02e10.yaml @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (c) 2025 Linaro Ltd. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ovti,ov02e10.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Omnivision OV02E10 CMOS Sensor + +maintainers: + - Bryan O'Donoghue + +description: | + The Omnivision OV02E10 is a 2 megapixel, CMOS image sensor which supports: + - Automatic black level calibration (ABLC) + - Programmable controls for frame rate, mirror and flip, binning, cropping + and windowing + - Output formats 10-bit 4C RGB RAW, 10-bit Bayer RAW + - 2-lane MIPI D-PHY TX @ 720 Mbps per lane + - Dynamic defect pixel cancellation + - Standard SCCB command interface + +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + +properties: + compatible: + const: ovti,ov02e10 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + avdd-supply: + description: Analogue circuit voltage supply. + + dovdd-supply: + description: I/O circuit voltage supply. + + dvdd-supply: + description: Digital circuit voltage supply. + + reset-gpios: + description: Active low GPIO connected to XSHUTDOWN pad of the sensor. + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + additionalProperties: false + + properties: + data-lanes: + items: + - const: 1 + - const: 2 + link-frequencies: true + remote-endpoint: true + + required: + - data-lanes + - link-frequencies + - remote-endpoint + +required: + - compatible + - reg + - clocks + - port + +unevaluatedProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + ov02e10: camera@10 { + compatible = "ovti,ov02e10"; + reg = <0x10>; + + reset-gpios = <&tlmm 237 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&cam_rgb_defaultt>; + + clocks = <&ov02e10_clk>; + + assigned-clocks = <&ov02e10_clk>; + assigned-clock-parents = <&ov02e10_clk_parent>; + assigned-clock-rates = <19200000>; + + avdd-supply = <&vreg_l7b_2p8>; + dvdd-supply = <&vreg_l7b_1p8>; + dovdd-supply = <&vreg_l3m_1p8>; + + port { + ov02e10_ep: endpoint { + remote-endpoint = <&csiphy4_ep>; + data-lanes = <1 2>; + link-frequencies = /bits/ 64 <400000000>; + }; + }; + }; + }; +... -- cgit v1.2.3-59-g8ed1b From 1c734f8ab070716604d0794094e18de3475c8eeb Mon Sep 17 00:00:00 2001 From: Jingjing Xiong Date: Mon, 7 Apr 2025 10:13:08 +0100 Subject: media: i2c: ov02e10: add OV02E10 image sensor driver Add in the ov02e10 driver from the Intel IPU6 repository. Signed-off-by: Jingjing Xiong Co-developed-by: Hao Yao Signed-off-by: Hao Yao Co-developed-by: Jim Lai Signed-off-by: Jim Lai Co-developed-by: You-Sheng Yang Signed-off-by: You-Sheng Yang Co-developed-by: Alan Stern Signed-off-by: Alan Stern Co-developed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Bryan O'Donoghue Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- MAINTAINERS | 9 + drivers/media/i2c/Kconfig | 10 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov02e10.c | 969 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 989 insertions(+) create mode 100644 drivers/media/i2c/ov02e10.c diff --git a/MAINTAINERS b/MAINTAINERS index 393d92c2e610..7a76cb72487b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17808,6 +17808,15 @@ T: git git://linuxtv.org/media.git F: Documentation/devicetree/bindings/media/i2c/ovti,ov02a10.yaml F: drivers/media/i2c/ov02a10.c +OMNIVISION OV02E10 SENSOR DRIVER +M: Bryan O'Donoghue +M: Hans de Goede +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media.git +F: Documentation/devicetree/bindings/media/i2c/ovti,ov02e10.yaml +F: drivers/media/i2c/ov02e10.c + OMNIVISION OV08D10 SENSOR DRIVER M: Jimmy Su L: linux-media@vger.kernel.org diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index e576b213084d..7b8af1c87a0e 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -356,6 +356,16 @@ config VIDEO_OV02A10 To compile this driver as a module, choose M here: the module will be called ov02a10. +config VIDEO_OV02E10 + tristate "OmniVision OV02E10 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the OmniVision + OV02E10 camera. + + To compile this driver as a module, choose M here: the + module will be called ov02e10. + config VIDEO_OV08D10 tristate "OmniVision OV08D10 sensor support" help diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 6c23a4463527..ed5e62fd6199 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -83,6 +83,7 @@ obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o +obj-$(CONFIG_VIDEO_OV02E10) += ov02e10.o obj-$(CONFIG_VIDEO_OV08D10) += ov08d10.o obj-$(CONFIG_VIDEO_OV08X40) += ov08x40.o obj-$(CONFIG_VIDEO_OV13858) += ov13858.o diff --git a/drivers/media/i2c/ov02e10.c b/drivers/media/i2c/ov02e10.c new file mode 100644 index 000000000000..d74dc62e189d --- /dev/null +++ b/drivers/media/i2c/ov02e10.c @@ -0,0 +1,969 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2023 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OV02E10_LINK_FREQ_360MHZ 360000000ULL +#define OV02E10_SCLK 36000000LL +#define OV02E10_MCLK 19200000 +#define OV02E10_DATA_LANES 2 +#define OV02E10_RGB_DEPTH 10 + +#define OV02E10_REG_PAGE_FLAG CCI_REG8(0xfd) +#define OV02E10_PAGE_0 0x0 +#define OV02E10_PAGE_1 0x1 +#define OV02E10_PAGE_2 0x2 +#define OV02E10_PAGE_3 0x3 +#define OV02E10_PAGE_5 0x4 +#define OV02E10_PAGE_7 0x5 +#define OV02E10_PAGE_8 0x6 +#define OV02E10_PAGE_9 0xF +#define OV02E10_PAGE_D 0x8 +#define OV02E10_PAGE_E 0x9 +#define OV02E10_PAGE_F 0xA + +#define OV02E10_REG_CHIP_ID CCI_REG32(0x00) +#define OV02E10_CHIP_ID 0x45025610 + +/* Horizontal and vertical flip */ +#define OV02E10_REG_ORIENTATION CCI_REG8(0x32) + +/* vertical-timings from sensor */ +#define OV02E10_REG_VTS CCI_REG16(0x35) +#define OV02E10_VTS_DEF 2244 +#define OV02E10_VTS_MIN 2244 +#define OV02E10_VTS_MAX 0x7fff + +/* horizontal-timings from sensor */ +#define OV02E10_REG_HTS CCI_REG16(0x37) + +/* Exposure controls from sensor */ +#define OV02E10_REG_EXPOSURE CCI_REG16(0x03) +#define OV02E10_EXPOSURE_MIN 1 +#define OV02E10_EXPOSURE_MAX_MARGIN 2 +#define OV02E10_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define OV02E10_REG_ANALOG_GAIN CCI_REG8(0x24) +#define OV02E10_ANAL_GAIN_MIN 0x10 +#define OV02E10_ANAL_GAIN_MAX 0xf8 +#define OV02E10_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define OV02E10_REG_DIGITAL_GAIN CCI_REG16(0x21) +#define OV02E10_DGTL_GAIN_MIN 256 +#define OV02E10_DGTL_GAIN_MAX 1020 +#define OV02E10_DGTL_GAIN_STEP 1 +#define OV02E10_DGTL_GAIN_DEFAULT 256 + +/* Register update control */ +#define OV02E10_REG_COMMAND_UPDATE CCI_REG8(0xE7) +#define OV02E10_COMMAND_UPDATE 0x00 +#define OV02E10_COMMAND_HOLD 0x01 + +/* Test Pattern Control */ +#define OV02E10_REG_TEST_PATTERN CCI_REG8(0x12) +#define OV02E10_TEST_PATTERN_ENABLE BIT(0) +#define OV02E10_TEST_PATTERN_BAR_SHIFT 1 + +struct reg_sequence_list { + u32 num_regs; + const struct reg_sequence *regs; +}; + +struct ov02e10_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 hts; + + /* Default vertical timing */ + u32 vts_def; + + /* Min vertical timining size */ + u32 vts_min; + + /* Sensor register settings for this resolution */ + const struct reg_sequence_list reg_list; +}; + +static const struct reg_sequence mode_1928x1088_30fps_2lane[] = { + { 0xfd, 0x00 }, + { 0x20, 0x00 }, + { 0x20, 0x0b }, + { 0x21, 0x02 }, + { 0x10, 0x23 }, + { 0xc5, 0x04 }, + { 0x21, 0x00 }, + { 0x14, 0x96 }, + { 0x17, 0x01 }, + { 0xfd, 0x01 }, + { 0x03, 0x00 }, + { 0x04, 0x04 }, + { 0x05, 0x04 }, + { 0x06, 0x62 }, + { 0x07, 0x01 }, + { 0x22, 0x80 }, + { 0x24, 0xff }, + { 0x40, 0xc6 }, + { 0x41, 0x18 }, + { 0x45, 0x3f }, + { 0x48, 0x0c }, + { 0x4c, 0x08 }, + { 0x51, 0x12 }, + { 0x52, 0x10 }, + { 0x57, 0x98 }, + { 0x59, 0x06 }, + { 0x5a, 0x04 }, + { 0x5c, 0x38 }, + { 0x5e, 0x10 }, + { 0x67, 0x11 }, + { 0x7b, 0x04 }, + { 0x81, 0x12 }, + { 0x90, 0x51 }, + { 0x91, 0x09 }, + { 0x92, 0x21 }, + { 0x93, 0x28 }, + { 0x95, 0x54 }, + { 0x9d, 0x20 }, + { 0x9e, 0x04 }, + { 0xb1, 0x9a }, + { 0xb2, 0x86 }, + { 0xb6, 0x3f }, + { 0xb9, 0x30 }, + { 0xc1, 0x01 }, + { 0xc5, 0xa0 }, + { 0xc6, 0x73 }, + { 0xc7, 0x04 }, + { 0xc8, 0x25 }, + { 0xc9, 0x05 }, + { 0xca, 0x28 }, + { 0xcb, 0x00 }, + { 0xcf, 0x16 }, + { 0xd2, 0xd0 }, + { 0xd7, 0x3f }, + { 0xd8, 0x40 }, + { 0xd9, 0x40 }, + { 0xda, 0x44 }, + { 0xdb, 0x3d }, + { 0xdc, 0x3d }, + { 0xdd, 0x3d }, + { 0xde, 0x3d }, + { 0xdf, 0xf0 }, + { 0xea, 0x0f }, + { 0xeb, 0x04 }, + { 0xec, 0x29 }, + { 0xee, 0x47 }, + { 0xfd, 0x01 }, + { 0x31, 0x01 }, + { 0x27, 0x00 }, + { 0x2f, 0x41 }, + { 0xfd, 0x02 }, + { 0xa1, 0x01 }, + { 0xfd, 0x02 }, + { 0x9a, 0x03 }, + { 0xfd, 0x03 }, + { 0x9d, 0x0f }, + { 0xfd, 0x07 }, + { 0x42, 0x00 }, + { 0x43, 0xad }, + { 0x44, 0x00 }, + { 0x45, 0xa8 }, + { 0x46, 0x00 }, + { 0x47, 0xa8 }, + { 0x48, 0x00 }, + { 0x49, 0xad }, + { 0xfd, 0x00 }, + { 0xc4, 0x01 }, + { 0xfd, 0x01 }, + { 0x33, 0x03 }, + { 0xfd, 0x00 }, + { 0x20, 0x1f }, +}; + +static const char *const ov02e10_test_pattern_menu[] = { + "Disabled", + "Color Bar", +}; + +static const s64 link_freq_menu_items[] = { + OV02E10_LINK_FREQ_360MHZ, +}; + +static const struct ov02e10_mode supported_modes[] = { + { + .width = 1928, + .height = 1088, + .hts = 534, + .vts_def = 2244, + .vts_min = 2244, + .reg_list = { + .num_regs = ARRAY_SIZE(mode_1928x1088_30fps_2lane), + .regs = mode_1928x1088_30fps_2lane, + }, + }, +}; + +static const char * const ov02e10_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + +struct ov02e10 { + struct regmap *regmap; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + + struct clk *img_clk; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov02e10_supply_names)]; + struct gpio_desc *reset; + + /* Current mode */ + const struct ov02e10_mode *cur_mode; + + /* MIPI lanes info */ + u32 link_freq_index; + u8 mipi_lanes; +}; + +static inline struct ov02e10 *to_ov02e10(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct ov02e10, sd); +} + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OV02E10_DATA_LANES; + + do_div(pixel_rate, OV02E10_RGB_DEPTH); + + return pixel_rate; +} + +static u64 to_pixels_per_line(u32 hts, u32 f_index) +{ + u64 ppl = hts * to_pixel_rate(f_index); + + do_div(ppl, OV02E10_SCLK); + + return ppl; +} + +static void ov02e10_test_pattern(struct ov02e10 *ov02e10, u32 pattern, int *pret) +{ + if (pattern) + pattern = pattern << OV02E10_TEST_PATTERN_BAR_SHIFT | + OV02E10_TEST_PATTERN_ENABLE; + + cci_write(ov02e10->regmap, OV02E10_REG_TEST_PATTERN, pattern, pret); +} + +static int ov02e10_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov02e10 *ov02e10 = container_of(ctrl->handler, + struct ov02e10, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&ov02e10->sd); + s64 exposure_max; + int ret; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = ov02e10->cur_mode->height + ctrl->val - + OV02E10_EXPOSURE_MAX_MARGIN; + ret = __v4l2_ctrl_modify_range(ov02e10->exposure, + ov02e10->exposure->minimum, + exposure_max, + ov02e10->exposure->step, + exposure_max); + if (ret) + return ret; + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + ret = cci_write(ov02e10->regmap, OV02E10_REG_COMMAND_UPDATE, + OV02E10_COMMAND_HOLD, NULL); + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, + OV02E10_PAGE_1, &ret); + cci_write(ov02e10->regmap, OV02E10_REG_ANALOG_GAIN, + ctrl->val, &ret); + break; + + case V4L2_CID_DIGITAL_GAIN: + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, + OV02E10_PAGE_1, &ret); + cci_write(ov02e10->regmap, OV02E10_REG_DIGITAL_GAIN, + ctrl->val, &ret); + break; + + case V4L2_CID_EXPOSURE: + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, + OV02E10_PAGE_1, &ret); + cci_write(ov02e10->regmap, OV02E10_REG_EXPOSURE, + ctrl->val, &ret); + break; + + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, + OV02E10_PAGE_1, &ret); + cci_write(ov02e10->regmap, OV02E10_REG_ORIENTATION, + ov02e10->hflip->val | ov02e10->vflip->val << 1, &ret); + break; + case V4L2_CID_VBLANK: + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, + OV02E10_PAGE_1, &ret); + cci_write(ov02e10->regmap, OV02E10_REG_VTS, + ov02e10->cur_mode->height + ctrl->val, &ret); + break; + + case V4L2_CID_TEST_PATTERN: + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, + OV02E10_PAGE_1, &ret); + ov02e10_test_pattern(ov02e10, ctrl->val, &ret); + break; + + default: + ret = -EINVAL; + break; + } + + cci_write(ov02e10->regmap, OV02E10_REG_COMMAND_UPDATE, + OV02E10_COMMAND_UPDATE, &ret); + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov02e10_ctrl_ops = { + .s_ctrl = ov02e10_set_ctrl, +}; + +static int ov02e10_init_controls(struct ov02e10 *ov02e10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02e10->sd); + struct v4l2_ctrl_handler *ctrl_hdlr = &ov02e10->ctrl_handler; + const struct ov02e10_mode *mode = ov02e10->cur_mode; + u32 vblank_min, vblank_max, vblank_def; + struct v4l2_fwnode_device_properties props; + s64 exposure_max, h_blank, pixel_rate; + int ret; + + v4l2_ctrl_handler_init(ctrl_hdlr, 12); + + ov02e10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &ov02e10_ctrl_ops, + V4L2_CID_LINK_FREQ, + ov02e10->link_freq_index, + 0, link_freq_menu_items); + if (ov02e10->link_freq) + ov02e10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate = to_pixel_rate(ov02e10->link_freq_index); + ov02e10->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + pixel_rate, 1, pixel_rate); + + vblank_min = mode->vts_min - mode->height; + vblank_max = OV02E10_VTS_MAX - mode->height; + vblank_def = mode->vts_def - mode->height; + ov02e10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_VBLANK, vblank_min, + vblank_max, 1, vblank_def); + + h_blank = mode->hts - mode->width; + ov02e10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, + 1, h_blank); + if (ov02e10->hblank) + ov02e10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV02E10_ANAL_GAIN_MIN, OV02E10_ANAL_GAIN_MAX, + OV02E10_ANAL_GAIN_STEP, OV02E10_ANAL_GAIN_MIN); + + v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV02E10_DGTL_GAIN_MIN, OV02E10_DGTL_GAIN_MAX, + OV02E10_DGTL_GAIN_STEP, OV02E10_DGTL_GAIN_DEFAULT); + + exposure_max = mode->vts_def - OV02E10_EXPOSURE_MAX_MARGIN; + ov02e10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_EXPOSURE, + OV02E10_EXPOSURE_MIN, + exposure_max, + OV02E10_EXPOSURE_STEP, + exposure_max); + + ov02e10->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (ov02e10->hflip) + ov02e10->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + ov02e10->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (ov02e10->vflip) + ov02e10->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov02e10_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov02e10_test_pattern_menu) - 1, + 0, 0, ov02e10_test_pattern_menu); + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + return ret; + + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov02e10_ctrl_ops, &props); + + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ov02e10->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void ov02e10_update_pad_format(const struct ov02e10_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int ov02e10_set_stream_mode(struct ov02e10 *ov02e10, u8 val) +{ + int ret = 0; + + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, OV02E10_PAGE_0, &ret); + cci_write(ov02e10->regmap, CCI_REG8(0xa0), val, &ret); + cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, OV02E10_PAGE_1, &ret); + cci_write(ov02e10->regmap, CCI_REG8(0x01), 0x02, &ret); + + return ret; +} + +static int ov02e10_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + const struct reg_sequence_list *reg_list; + int ret; + + ret = pm_runtime_resume_and_get(&client->dev); + if (ret) + return ret; + + reg_list = &ov02e10->cur_mode->reg_list; + ret = regmap_multi_reg_write(ov02e10->regmap, reg_list->regs, + reg_list->num_regs); + if (ret) { + dev_err(&client->dev, "failed to set mode\n"); + goto out; + } + + ret = __v4l2_ctrl_handler_setup(ov02e10->sd.ctrl_handler); + if (ret) + goto out; + + ret = ov02e10_set_stream_mode(ov02e10, 1); + +out: + if (ret) + pm_runtime_put(&client->dev); + + return ret; +} + +static int ov02e10_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + + ov02e10_set_stream_mode(ov02e10, 0); + pm_runtime_put(&client->dev); + + return 0; +} + +static int ov02e10_get_pm_resources(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + int i; + + ov02e10->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ov02e10->reset)) + return dev_err_probe(dev, PTR_ERR(ov02e10->reset), + "failed to get reset gpio\n"); + + for (i = 0; i < ARRAY_SIZE(ov02e10_supply_names); i++) + ov02e10->supplies[i].supply = ov02e10_supply_names[i]; + + return devm_regulator_bulk_get(dev, ARRAY_SIZE(ov02e10_supply_names), + ov02e10->supplies); +} + +static int ov02e10_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + + if (ov02e10->reset) + gpiod_set_value_cansleep(ov02e10->reset, 1); + + regulator_bulk_disable(ARRAY_SIZE(ov02e10_supply_names), + ov02e10->supplies); + + clk_disable_unprepare(ov02e10->img_clk); + + return 0; +} + +static int ov02e10_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02e10 *ov02e10 = to_ov02e10(sd); + int ret; + + ret = clk_prepare_enable(ov02e10->img_clk); + if (ret < 0) { + dev_err(dev, "failed to enable imaging clock: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(ov02e10_supply_names), + ov02e10->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators\n"); + goto disable_clk; + } + + if (ov02e10->reset) { + usleep_range(5000, 5100); + gpiod_set_value_cansleep(ov02e10->reset, 0); + usleep_range(8000, 8100); + } + + return 0; + +disable_clk: + clk_disable_unprepare(ov02e10->img_clk); + + return ret; +} + +static int ov02e10_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov02e10 *ov02e10 = to_ov02e10(sd); + const struct ov02e10_mode *mode; + s32 vblank_def, h_blank; + int ret = 0; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, fmt->format.width, + fmt->format.height); + + ov02e10_update_pad_format(mode, &fmt->format); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; + } else { + ov02e10->cur_mode = mode; + ret = __v4l2_ctrl_s_ctrl(ov02e10->link_freq, + ov02e10->link_freq_index); + if (ret) + return ret; + + ret = __v4l2_ctrl_s_ctrl_int64(ov02e10->pixel_rate, + to_pixel_rate(ov02e10->link_freq_index)); + if (ret) + return ret; + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_def - mode->height; + ret = __v4l2_ctrl_modify_range(ov02e10->vblank, + mode->vts_min - mode->height, + OV02E10_VTS_MAX - mode->height, + 1, vblank_def); + if (ret) + return ret; + + ret = __v4l2_ctrl_s_ctrl(ov02e10->vblank, vblank_def); + if (ret) + return ret; + + h_blank = to_pixels_per_line(mode->hts, ov02e10->link_freq_index); + h_blank -= mode->width; + ret = __v4l2_ctrl_modify_range(ov02e10->hblank, h_blank, + h_blank, 1, h_blank); + } + + return ret; +} + +static int ov02e10_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov02e10 *ov02e10 = to_ov02e10(sd); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); + else + ov02e10_update_pad_format(ov02e10->cur_mode, &fmt->format); + + return 0; +} + +static int ov02e10_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov02e10_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int ov02e10_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + ov02e10_update_pad_format(&supported_modes[0], + v4l2_subdev_state_get_format(sd_state, 0)); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov02e10_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops ov02e10_pad_ops = { + .set_fmt = ov02e10_set_format, + .get_fmt = ov02e10_get_format, + .enum_mbus_code = ov02e10_enum_mbus_code, + .enum_frame_size = ov02e10_enum_frame_size, + .enable_streams = ov02e10_enable_streams, + .disable_streams = ov02e10_disable_streams, +}; + +static const struct v4l2_subdev_ops ov02e10_subdev_ops = { + .video = &ov02e10_video_ops, + .pad = &ov02e10_pad_ops, +}; + +static const struct media_entity_operations ov02e10_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ov02e10_internal_ops = { + .init_state = ov02e10_init_state, +}; + +static int ov02e10_identify_module(struct ov02e10 *ov02e10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02e10->sd); + int ret; + u64 val; + + ret = cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, + OV02E10_PAGE_0, NULL); + cci_read(ov02e10->regmap, OV02E10_REG_CHIP_ID, &val, &ret); + if (ret) + return ret; + + if (val != OV02E10_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + OV02E10_CHIP_ID, (u32)val); + return -ENXIO; + } + + return 0; +} + +static int ov02e10_check_hwcfg(struct device *dev, struct ov02e10 *ov02e10) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + unsigned long link_freq_bitmap; + u32 ext_clk; + int ret; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return dev_err_probe(dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return dev_err_probe(dev, ret, "parsing endpoint failed\n"); + + ov02e10->img_clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(ov02e10->img_clk)) { + ret = dev_err_probe(dev, PTR_ERR(ov02e10->img_clk), + "failed to get imaging clock\n"); + goto out_err; + } + + if (ov02e10->img_clk) { + ext_clk = clk_get_rate(ov02e10->img_clk); + } else { + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &ext_clk); + if (ret) { + dev_err(dev, "can't get clock frequency\n"); + goto out_err; + } + } + + if (ext_clk != OV02E10_MCLK) { + dev_err(dev, "external clock %d is not supported\n", + ext_clk); + ret = -EINVAL; + goto out_err; + } + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV02E10_DATA_LANES) { + dev_err(dev, "number of CSI2 data lanes %d is not supported\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto out_err; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined\n"); + ret = -EINVAL; + goto out_err; + } + + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &link_freq_bitmap); + if (ret) + goto out_err; + + /* v4l2_link_freq_to_bitmap() guarantees at least 1 bit is set */ + ov02e10->link_freq_index = ffs(link_freq_bitmap) - 1; + ov02e10->mipi_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + +out_err: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static void ov02e10_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + + if (!pm_runtime_status_suspended(&client->dev)) { + ov02e10_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + } +} + +static int ov02e10_probe(struct i2c_client *client) +{ + struct ov02e10 *ov02e10; + int ret; + + ov02e10 = devm_kzalloc(&client->dev, sizeof(*ov02e10), GFP_KERNEL); + if (!ov02e10) + return -ENOMEM; + + v4l2_i2c_subdev_init(&ov02e10->sd, client, &ov02e10_subdev_ops); + + /* Check HW config */ + ret = ov02e10_check_hwcfg(&client->dev, ov02e10); + if (ret) + return ret; + + /* Initialize subdev */ + ov02e10->regmap = devm_cci_regmap_init_i2c(client, 8); + if (IS_ERR(ov02e10->regmap)) + return PTR_ERR(ov02e10->regmap); + + ret = ov02e10_get_pm_resources(&client->dev); + if (ret) + return ret; + + ret = ov02e10_power_on(&client->dev); + if (ret) { + dev_err_probe(&client->dev, ret, "failed to power on\n"); + return ret; + } + + /* Check module identity */ + ret = ov02e10_identify_module(ov02e10); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d\n", ret); + goto probe_error_power_off; + } + + ov02e10->cur_mode = &supported_modes[0]; + ret = ov02e10_init_controls(ov02e10); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d\n", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + /* Initialize subdev */ + ov02e10->sd.internal_ops = &ov02e10_internal_ops; + ov02e10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov02e10->sd.entity.ops = &ov02e10_subdev_entity_ops; + ov02e10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + ov02e10->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov02e10->sd.entity, 1, &ov02e10->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov02e10->sd.state_lock = ov02e10->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&ov02e10->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to init subdev: %d", ret); + goto probe_error_media_entity_cleanup; + } + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + + ret = v4l2_async_register_subdev_sensor(&ov02e10->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_v4l2_subdev_cleanup; + } + + pm_runtime_idle(&client->dev); + return 0; + +probe_error_v4l2_subdev_cleanup: + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + v4l2_subdev_cleanup(&ov02e10->sd); + +probe_error_media_entity_cleanup: + media_entity_cleanup(&ov02e10->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(ov02e10->sd.ctrl_handler); + +probe_error_power_off: + ov02e10_power_off(&client->dev); + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ov02e10_pm_ops, ov02e10_power_off, + ov02e10_power_on, NULL); + +static const struct acpi_device_id ov02e10_acpi_ids[] = { + { "OVTI02E1" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(acpi, ov02e10_acpi_ids); + +static const struct of_device_id ov02e10_of_match[] = { + { .compatible = "ovti,ov02e10" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov02e10_of_match); + +static struct i2c_driver ov02e10_i2c_driver = { + .driver = { + .name = "ov02e10", + .pm = pm_sleep_ptr(&ov02e10_pm_ops), + .acpi_match_table = ov02e10_acpi_ids, + .of_match_table = ov02e10_of_match, + }, + .probe = ov02e10_probe, + .remove = ov02e10_remove, +}; + +module_i2c_driver(ov02e10_i2c_driver); + +MODULE_AUTHOR("Jingjing Xiong "); +MODULE_AUTHOR("Hans de Goede "); +MODULE_AUTHOR("Alan Stern "); +MODULE_AUTHOR("Bryan O'Donoghue "); +MODULE_DESCRIPTION("OmniVision OV02E10 sensor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 25259379bc796bf718eae395ffe05e76d42cbe5b Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Mon, 21 Apr 2025 10:20:15 +0200 Subject: media: dt-bindings: Convert Analog Devices ad5820 to DT schema Convert the Analog Devices ad5820 to DT schema format. Acked-by: Pavel Machek Signed-off-by: David Heidelberg Reviewed-by: Rob Herring (Arm) Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../devicetree/bindings/media/i2c/ad5820.txt | 28 ----------- .../devicetree/bindings/media/i2c/adi,ad5820.yaml | 56 ++++++++++++++++++++++ MAINTAINERS | 1 + 3 files changed, 57 insertions(+), 28 deletions(-) delete mode 100644 Documentation/devicetree/bindings/media/i2c/ad5820.txt create mode 100644 Documentation/devicetree/bindings/media/i2c/adi,ad5820.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/ad5820.txt b/Documentation/devicetree/bindings/media/i2c/ad5820.txt deleted file mode 100644 index 5764cbedf9b7..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/ad5820.txt +++ /dev/null @@ -1,28 +0,0 @@ -* Analog Devices AD5820 autofocus coil - -Required Properties: - - - compatible: Must contain one of: - - "adi,ad5820" - - "adi,ad5821" - - "adi,ad5823" - - - reg: I2C slave address - - - VANA-supply: supply of voltage for VANA pin - -Optional properties: - - - enable-gpios : GPIO spec for the XSHUTDOWN pin. The XSHUTDOWN signal is -active low, a high level on the pin enables the device. - -Example: - - ad5820: coil@c { - compatible = "adi,ad5820"; - reg = <0x0c>; - - VANA-supply = <&vaux4>; - enable-gpios = <&msmgpio 26 GPIO_ACTIVE_HIGH>; - }; - diff --git a/Documentation/devicetree/bindings/media/i2c/adi,ad5820.yaml b/Documentation/devicetree/bindings/media/i2c/adi,ad5820.yaml new file mode 100644 index 000000000000..0c8f24f692ca --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/adi,ad5820.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/adi,ad5820.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD5820 autofocus coil + +maintainers: + - Pavel Machek + +description: + The AD5820 is a current sink driver designed for precise control of + voice coil motors (VCMs) in camera autofocus systems. + +properties: + compatible: + enum: + - adi,ad5820 + - adi,ad5821 + - adi,ad5823 + + reg: + maxItems: 1 + + enable-gpios: + maxItems: 1 + description: + GPIO spec for the XSHUTDOWN pin. The XSHUTDOWN signal is active low, + a high level on the pin enables the device. + + VANA-supply: + description: supply of voltage for VANA pin + +required: + - compatible + - reg + - VANA-supply + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + coil@c { + compatible = "adi,ad5820"; + reg = <0x0c>; + + enable-gpios = <&msmgpio 26 GPIO_ACTIVE_HIGH>; + VANA-supply = <&vaux4>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 7a76cb72487b..1b5e8ec57851 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17213,6 +17213,7 @@ M: Pavel Machek M: Sakari Ailus L: linux-media@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/media/i2c/adi,ad5820.yaml F: drivers/media/i2c/ad5820.c F: drivers/media/i2c/et8ek8 -- cgit v1.2.3-59-g8ed1b From 838a5255698b414897c3b237c00b21f3f1620fef Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Thu, 6 Mar 2025 11:04:43 +0530 Subject: media: i2c: imx219: switch to {enable,disable}_streams Switch from s_stream to enable_streams and disable_streams callbacks. Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 04262bbf6306..1b4eb531b315 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -718,9 +718,11 @@ static int imx219_configure_lanes(struct imx219 *imx219) ARRAY_SIZE(imx219_4lane_regs), NULL); }; -static int imx219_start_streaming(struct imx219 *imx219, - struct v4l2_subdev_state *state) +static int imx219_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { + struct imx219 *imx219 = to_imx219(sd); struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); int ret; @@ -773,8 +775,11 @@ err_rpm_put: return ret; } -static void imx219_stop_streaming(struct imx219 *imx219) +static int imx219_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { + struct imx219 *imx219 = to_imx219(sd); struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); int ret; @@ -788,22 +793,7 @@ static void imx219_stop_streaming(struct imx219 *imx219) __v4l2_ctrl_grab(imx219->hflip, false); pm_runtime_put(&client->dev); -} - -static int imx219_set_stream(struct v4l2_subdev *sd, int enable) -{ - struct imx219 *imx219 = to_imx219(sd); - struct v4l2_subdev_state *state; - int ret = 0; - - state = v4l2_subdev_lock_and_get_active_state(sd); - - if (enable) - ret = imx219_start_streaming(imx219, state); - else - imx219_stop_streaming(imx219); - v4l2_subdev_unlock_state(state); return ret; } @@ -995,7 +985,7 @@ static int imx219_init_state(struct v4l2_subdev *sd, } static const struct v4l2_subdev_video_ops imx219_video_ops = { - .s_stream = imx219_set_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_pad_ops imx219_pad_ops = { @@ -1004,6 +994,8 @@ static const struct v4l2_subdev_pad_ops imx219_pad_ops = { .set_fmt = imx219_set_pad_format, .get_selection = imx219_get_selection, .enum_frame_size = imx219_enum_frame_size, + .enable_streams = imx219_enable_streams, + .disable_streams = imx219_disable_streams, }; static const struct v4l2_subdev_ops imx219_subdev_ops = { -- cgit v1.2.3-59-g8ed1b From 5bd6b8c1bb2d49d5ff15c7c47d6b33d2d38785f0 Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Thu, 6 Mar 2025 11:04:44 +0530 Subject: media: i2c: imx219: media: i2c: imx219: Enable runtime PM autosuspend Use pm_runtime_put_autosuspend() instead of pm_runtime_put() to allow autosuspend. Set a 1000ms autosuspend delay in imx219_probe() to improve power efficiency. Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 1b4eb531b315..3b4f68543342 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -771,7 +771,8 @@ static int imx219_enable_streams(struct v4l2_subdev *sd, return 0; err_rpm_put: - pm_runtime_put(&client->dev); + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); return ret; } @@ -792,7 +793,8 @@ static int imx219_disable_streams(struct v4l2_subdev *sd, __v4l2_ctrl_grab(imx219->vflip, false); __v4l2_ctrl_grab(imx219->hflip, false); - pm_runtime_put(&client->dev); + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); return ret; } @@ -1272,6 +1274,8 @@ static int imx219_probe(struct i2c_client *client) } pm_runtime_idle(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); return 0; -- cgit v1.2.3-59-g8ed1b From e848475c33fc42e8bd7052227e65ca35d6ff046f Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 15 Oct 2024 09:59:52 +0300 Subject: media: ccs: Try a little longer to access the sensor before giving up Some sensors take longer to respond after reset than the spec-required time. Try up to 1 s for the sensor to become accessible. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs/ccs-core.c | 26 +++++++++++++------------- drivers/media/i2c/ccs/ccs.h | 2 ++ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 06e0ba53f2a8..a47c56d9779f 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -1354,8 +1354,10 @@ static int ccs_change_cci_addr(struct ccs_sensor *sensor) client->addr = sensor->hwcfg.i2c_addr_dfl; - rval = ccs_write(sensor, CCI_ADDRESS_CTRL, - sensor->hwcfg.i2c_addr_alt << 1); + rval = read_poll_timeout(ccs_write, rval, !rval, CCS_RESET_DELAY_US, + CCS_RESET_TIMEOUT_US, false, sensor, + CCI_ADDRESS_CTRL, + sensor->hwcfg.i2c_addr_alt << 1); if (rval) return rval; @@ -1575,27 +1577,25 @@ static int ccs_power_on(struct device *dev) if (ccsdev->flags & CCS_DEVICE_FLAG_IS_SMIA) sleep = SMIAPP_RESET_DELAY(sensor->hwcfg.ext_clk); else - sleep = 5000; + sleep = CCS_RESET_DELAY_US; usleep_range(sleep, sleep); } /* - * Failures to respond to the address change command have been noticed. - * Those failures seem to be caused by the sensor requiring a longer - * boot time than advertised. An additional 10ms delay seems to work - * around the issue, but the SMIA++ I2C write retry hack makes the delay - * unnecessary. The failures need to be investigated to find a proper - * fix, and a delay will likely need to be added here if the I2C write - * retry hack is reverted before the root cause of the boot time issue - * is found. + * Some devices take longer than the spec-defined time to respond + * after reset. Try until some time has passed before flagging it + * an error. */ - if (!sensor->reset && !sensor->xshutdown) { u8 retry = 100; u32 reset; - rval = ccs_write(sensor, SOFTWARE_RESET, CCS_SOFTWARE_RESET_ON); + rval = read_poll_timeout(ccs_write, rval, !rval, + CCS_RESET_DELAY_US, + CCS_RESET_TIMEOUT_US, + false, sensor, SOFTWARE_RESET, + CCS_SOFTWARE_RESET_ON); if (rval < 0) { dev_err(dev, "software reset failed\n"); goto out_cci_addr_fail; diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h index 096573845a10..0726c4687f0f 100644 --- a/drivers/media/i2c/ccs/ccs.h +++ b/drivers/media/i2c/ccs/ccs.h @@ -43,6 +43,8 @@ #define SMIAPP_RESET_DELAY(clk) \ (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \ + (clk) / 1000 - 1) / ((clk) / 1000)) +#define CCS_RESET_DELAY_US 5000 +#define CCS_RESET_TIMEOUT_US 1000000 #define CCS_COLOUR_COMPONENTS 4 -- cgit v1.2.3-59-g8ed1b From bb468fc5a4d902c9202f32ed4ee1f0980c17fe52 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 15 Oct 2024 10:07:02 +0300 Subject: media: ccs: Use read_poll_timeout() in reset polling Use read_poll_timeout() in polling the device after a reset, either hard or soft. While at it, improve the related error message. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs/ccs-core.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index a47c56d9779f..01744ebd3e06 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -1588,7 +1588,6 @@ static int ccs_power_on(struct device *dev) * an error. */ if (!sensor->reset && !sensor->xshutdown) { - u8 retry = 100; u32 reset; rval = read_poll_timeout(ccs_write, rval, !rval, @@ -1601,18 +1600,15 @@ static int ccs_power_on(struct device *dev) goto out_cci_addr_fail; } - do { - rval = ccs_read(sensor, SOFTWARE_RESET, &reset); - reset = !rval && reset == CCS_SOFTWARE_RESET_OFF; - if (reset) - break; - - usleep_range(1000, 2000); - } while (--retry); - - if (!reset) { - dev_err(dev, "software reset failed\n"); - rval = -EIO; + rval = read_poll_timeout(ccs_read, rval, + !rval && + reset == CCS_SOFTWARE_RESET_OFF, + CCS_RESET_DELAY_US, + CCS_RESET_TIMEOUT_US, false, sensor, + SOFTWARE_RESET, &reset); + if (rval < 0) { + dev_err_probe(dev, rval, + "failed to respond after reset\n"); goto out_cci_addr_fail; } } -- cgit v1.2.3-59-g8ed1b From 932518f6f8713d9a23b2ec3e0ebf5ab9e17af728 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 15 Oct 2024 10:28:42 +0300 Subject: media: ccs: Remove I²C write retry hack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The I²C retry hack has been there in order to address transient I²C register write access issues on a few very old sensors and possibly it has addressed also first I²C access problems (device not responding until a certain amount of time has passed) but that is now separately handled. The retry hack has a good potential for introducing hard to debug problems in updating sensor settings while streaming. Remove it and instead pass those rare errors to the user space -- which is also what virtually all other drivers do. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs/ccs-reg-access.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-reg-access.c b/drivers/media/i2c/ccs/ccs-reg-access.c index a696a0ec8ff5..fd36889ccc1d 100644 --- a/drivers/media/i2c/ccs/ccs-reg-access.c +++ b/drivers/media/i2c/ccs/ccs-reg-access.c @@ -210,7 +210,6 @@ int ccs_read_addr_noconv(struct ccs_sensor *sensor, u32 reg, u32 *val) */ int ccs_write_addr(struct ccs_sensor *sensor, u32 reg, u32 val) { - unsigned int retries = 10; int rval; rval = ccs_call_quirk(sensor, reg_access, true, ®, &val); @@ -219,13 +218,7 @@ int ccs_write_addr(struct ccs_sensor *sensor, u32 reg, u32 val) if (rval < 0) return rval; - rval = 0; - do { - if (cci_write(sensor->regmap, reg, val, &rval)) - fsleep(1000); - } while (rval && --retries); - - return rval; + return cci_write(sensor->regmap, reg, val, NULL); } #define MAX_WRITE_LEN 32U -- cgit v1.2.3-59-g8ed1b From 980d2c914cbe0af7c9239dacb89ae083a2094d83 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 15 Oct 2024 13:38:01 +0300 Subject: media: ccs: Don't complain about lack of quirks Generally any deviance from the standard is handled via CCS static data nowadays and so not having quirks is expected. Don't warn about it. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs/ccs-core.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 01744ebd3e06..27b94a399b17 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -2853,10 +2853,6 @@ static int ccs_identify_module(struct ccs_sensor *sensor) break; } - if (i >= ARRAY_SIZE(ccs_module_idents)) - dev_warn(&client->dev, - "no quirks for this module; let's hope it's fully compliant\n"); - dev_dbg(&client->dev, "the sensor is called %s\n", minfo->name); return 0; -- cgit v1.2.3-59-g8ed1b From 5050bc60cc16ffa35a962a53271210d427a11535 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 11 Oct 2024 13:56:44 +0300 Subject: media: ccs: Don't complain about missing "clock-frequency" property The clock frequency is often available via the clock itself and not read by the driver from the "clock-frequency" property. Don't complain if the property doesn't exist. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs/ccs-core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 27b94a399b17..487bcabb4a19 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -3123,8 +3123,6 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev) rval = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", &hwcfg->ext_clk); - if (rval) - dev_info(dev, "can't get clock-frequency\n"); dev_dbg(dev, "clk %u, mode %u\n", hwcfg->ext_clk, hwcfg->csi_signalling_mode); -- cgit v1.2.3-59-g8ed1b From 1284c9693953aed2a9974a5a384ce2a42a1b9ae8 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 10 Apr 2025 11:52:13 +0200 Subject: media: intel/ipu6: Minor dma_mask clenaup Remove unused dma_mask field and ipu-dma.h includes. Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-bus.h | 1 - drivers/media/pci/intel/ipu6/ipu6-dma.h | 3 --- 2 files changed, 4 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-bus.h b/drivers/media/pci/intel/ipu6/ipu6-bus.h index b790f9cc37e3..a08c5468d536 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-bus.h +++ b/drivers/media/pci/intel/ipu6/ipu6-bus.h @@ -26,7 +26,6 @@ struct ipu6_bus_device { struct ipu6_mmu *mmu; struct ipu6_device *isp; const struct ipu6_buttress_ctrl *ctrl; - u64 dma_mask; const struct firmware *fw; struct sg_table fw_sgt; u64 *pkg_dir; diff --git a/drivers/media/pci/intel/ipu6/ipu6-dma.h b/drivers/media/pci/intel/ipu6/ipu6-dma.h index 2882850d9366..ae9b9a5df57f 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-dma.h +++ b/drivers/media/pci/intel/ipu6/ipu6-dma.h @@ -4,9 +4,6 @@ #ifndef IPU6_DMA_H #define IPU6_DMA_H -#include -#include -#include #include #include #include -- cgit v1.2.3-59-g8ed1b From 0209916ebe2475079ce6d8dc4114afbc0ccad1c2 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 10 Apr 2025 11:47:06 +0200 Subject: media: intel/ipu6: Fix dma mask for non-secure mode We use dma_get_mask() of auxdev device for calculate iova pfn limit. This is always 32 bit mask as we do not initialize the mask (and we can not do so, since dev->dev_mask is NULL anyways for auxdev). Since we need 31 bit mask for non-secure mode use mmu_info->aperture_end which is properly initialized to correct mask for both modes. Fixes: daabc5c64703 ("media: ipu6: not override the dma_ops of device in driver") Cc: stable@vger.kernel.org Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-dma.c b/drivers/media/pci/intel/ipu6/ipu6-dma.c index 1ca60ca79dba..7296373d36b0 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-dma.c +++ b/drivers/media/pci/intel/ipu6/ipu6-dma.c @@ -172,7 +172,7 @@ void *ipu6_dma_alloc(struct ipu6_bus_device *sys, size_t size, count = PHYS_PFN(size); iova = alloc_iova(&mmu->dmap->iovad, count, - PHYS_PFN(dma_get_mask(dev)), 0); + PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); if (!iova) goto out_kfree; @@ -398,7 +398,7 @@ int ipu6_dma_map_sg(struct ipu6_bus_device *sys, struct scatterlist *sglist, nents, npages); iova = alloc_iova(&mmu->dmap->iovad, npages, - PHYS_PFN(dma_get_mask(dev)), 0); + PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); if (!iova) return 0; -- cgit v1.2.3-59-g8ed1b From 78bc2ff83c76db622ae52c2bbf3113a5c6df580a Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:23 +0530 Subject: media: i2c: imx334: Simplify with dev_err_probe() Error handling in probe() can be a bit simpler with dev_err_probe(). also, Added missing newline characters (\n) in error messages. Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 61 +++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 8cd1eecd0143..ad0b03a3f573 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -658,7 +658,7 @@ static int imx334_update_exp_gain(struct imx334 *imx334, u32 exposure, u32 gain) lpfr = imx334->vblank + imx334->cur_mode->height; shutter = lpfr - exposure; - dev_dbg(imx334->dev, "Set long exp %u analog gain %u sh0 %u lpfr %u", + dev_dbg(imx334->dev, "Set long exp %u analog gain %u sh0 %u lpfr %u\n", exposure, gain, shutter, lpfr); ret = imx334_write_reg(imx334, IMX334_REG_HOLD, 1, 1); @@ -705,7 +705,7 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_VBLANK: imx334->vblank = imx334->vblank_ctrl->val; - dev_dbg(imx334->dev, "Received vblank %u, new lpfr %u", + dev_dbg(imx334->dev, "Received vblank %u, new lpfr %u\n", imx334->vblank, imx334->vblank + imx334->cur_mode->height); @@ -725,7 +725,7 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) exposure = ctrl->val; analog_gain = imx334->again_ctrl->val; - dev_dbg(imx334->dev, "Received exp %u analog gain %u", + dev_dbg(imx334->dev, "Received exp %u analog gain %u\n", exposure, analog_gain); ret = imx334_update_exp_gain(imx334, exposure, analog_gain); @@ -759,7 +759,7 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) ret = 0; break; default: - dev_err(imx334->dev, "Invalid control %d", ctrl->id); + dev_err(imx334->dev, "Invalid control %d\n", ctrl->id); ret = -EINVAL; } @@ -986,7 +986,7 @@ static int imx334_start_streaming(struct imx334 *imx334) ret = imx334_write_regs(imx334, common_mode_regs, ARRAY_SIZE(common_mode_regs)); if (ret) { - dev_err(imx334->dev, "fail to write common registers"); + dev_err(imx334->dev, "fail to write common registers\n"); return ret; } @@ -995,7 +995,7 @@ static int imx334_start_streaming(struct imx334 *imx334) ret = imx334_write_regs(imx334, reg_list->regs, reg_list->num_of_regs); if (ret) { - dev_err(imx334->dev, "fail to write initial registers"); + dev_err(imx334->dev, "fail to write initial registers\n"); return ret; } @@ -1009,7 +1009,7 @@ static int imx334_start_streaming(struct imx334 *imx334) /* Setup handler will write actual exposure and gain */ ret = __v4l2_ctrl_handler_setup(imx334->sd.ctrl_handler); if (ret) { - dev_err(imx334->dev, "fail to setup handler"); + dev_err(imx334->dev, "fail to setup handler\n"); return ret; } @@ -1017,7 +1017,7 @@ static int imx334_start_streaming(struct imx334 *imx334) ret = imx334_write_reg(imx334, IMX334_REG_MODE_SELECT, 1, IMX334_MODE_STREAMING); if (ret) { - dev_err(imx334->dev, "fail to start streaming"); + dev_err(imx334->dev, "fail to start streaming\n"); return ret; } @@ -1091,7 +1091,7 @@ static int imx334_detect(struct imx334 *imx334) return ret; if (val != IMX334_ID) { - dev_err(imx334->dev, "chip id mismatch: %x!=%x", + dev_err(imx334->dev, "chip id mismatch: %x!=%x\n", IMX334_ID, val); return -ENXIO; } @@ -1121,24 +1121,20 @@ static int imx334_parse_hw_config(struct imx334 *imx334) /* Request optional reset pin */ imx334->reset_gpio = devm_gpiod_get_optional(imx334->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(imx334->reset_gpio)) { - dev_err(imx334->dev, "failed to get reset gpio %ld", - PTR_ERR(imx334->reset_gpio)); - return PTR_ERR(imx334->reset_gpio); - } + if (IS_ERR(imx334->reset_gpio)) + return dev_err_probe(imx334->dev, PTR_ERR(imx334->reset_gpio), + "failed to get reset gpio\n"); /* Get sensor input clock */ imx334->inclk = devm_clk_get(imx334->dev, NULL); - if (IS_ERR(imx334->inclk)) { - dev_err(imx334->dev, "could not get inclk"); - return PTR_ERR(imx334->inclk); - } + if (IS_ERR(imx334->inclk)) + return dev_err_probe(imx334->dev, PTR_ERR(imx334->inclk), + "could not get inclk\n"); rate = clk_get_rate(imx334->inclk); - if (rate != IMX334_INCLK_RATE) { - dev_err(imx334->dev, "inclk frequency mismatch"); - return -EINVAL; - } + if (rate != IMX334_INCLK_RATE) + return dev_err_probe(imx334->dev, -EINVAL, + "inclk frequency mismatch\n"); ep = fwnode_graph_get_next_endpoint(fwnode, NULL); if (!ep) @@ -1151,7 +1147,7 @@ static int imx334_parse_hw_config(struct imx334 *imx334) if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX334_NUM_DATA_LANES) { dev_err(imx334->dev, - "number of CSI2 data lanes %d is not supported", + "number of CSI2 data lanes %d is not supported\n", bus_cfg.bus.mipi_csi2.num_data_lanes); ret = -EINVAL; goto done_endpoint_free; @@ -1205,7 +1201,7 @@ static int imx334_power_on(struct device *dev) ret = clk_prepare_enable(imx334->inclk); if (ret) { - dev_err(imx334->dev, "fail to enable inclk"); + dev_err(imx334->dev, "fail to enable inclk\n"); goto error_reset; } @@ -1349,23 +1345,22 @@ static int imx334_probe(struct i2c_client *client) imx334->sd.internal_ops = &imx334_internal_ops; ret = imx334_parse_hw_config(imx334); - if (ret) { - dev_err(imx334->dev, "HW configuration is not supported"); - return ret; - } + if (ret) + return dev_err_probe(imx334->dev, ret, + "HW configuration is not supported\n"); mutex_init(&imx334->mutex); ret = imx334_power_on(imx334->dev); if (ret) { - dev_err(imx334->dev, "failed to power-on the sensor"); + dev_err_probe(imx334->dev, ret, "failed to power-on the sensor\n"); goto error_mutex_destroy; } /* Check module identity */ ret = imx334_detect(imx334); if (ret) { - dev_err(imx334->dev, "failed to find sensor: %d", ret); + dev_err(imx334->dev, "failed to find sensor: %d\n", ret); goto error_power_off; } @@ -1376,7 +1371,7 @@ static int imx334_probe(struct i2c_client *client) ret = imx334_init_controls(imx334); if (ret) { - dev_err(imx334->dev, "failed to init controls: %d", ret); + dev_err(imx334->dev, "failed to init controls: %d\n", ret); goto error_power_off; } @@ -1388,14 +1383,14 @@ static int imx334_probe(struct i2c_client *client) imx334->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&imx334->sd.entity, 1, &imx334->pad); if (ret) { - dev_err(imx334->dev, "failed to init entity pads: %d", ret); + dev_err(imx334->dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; } ret = v4l2_async_register_subdev_sensor(&imx334->sd); if (ret < 0) { dev_err(imx334->dev, - "failed to register async subdev: %d", ret); + "failed to register async subdev: %d\n", ret); goto error_media_entity; } -- cgit v1.2.3-59-g8ed1b From 7b19b0fc8ac8466b9140df17e0c7fcf135f1f063 Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:24 +0530 Subject: media: i2c: imx334: Convert to CCI register access helpers Use the new common CCI register access helpers to replace the private register access helpers in the imx334 driver. This simplifies the driver by reducing the amount of code. Acked-by: Shravan Chippa Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/imx334.c | 746 +++++++++++++++++++-------------------------- 2 files changed, 319 insertions(+), 428 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 7b8af1c87a0e..205360c3ae52 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -217,6 +217,7 @@ config VIDEO_IMX319 config VIDEO_IMX334 tristate "Sony IMX334 sensor support" depends on OF_GPIO + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony IMX334 camera. diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index ad0b03a3f573..0785bf213d91 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -12,77 +12,123 @@ #include #include +#include #include #include #include /* Streaming Mode */ -#define IMX334_REG_MODE_SELECT 0x3000 -#define IMX334_MODE_STANDBY 0x01 -#define IMX334_MODE_STREAMING 0x00 +#define IMX334_REG_MODE_SELECT CCI_REG8(0x3000) +#define IMX334_MODE_STANDBY 0x01 +#define IMX334_MODE_STREAMING 0x00 /* Lines per frame */ -#define IMX334_REG_LPFR 0x3030 +#define IMX334_REG_VMAX CCI_REG24_LE(0x3030) + +#define IMX334_REG_HMAX CCI_REG16_LE(0x3034) + +#define IMX334_REG_OPB_SIZE_V CCI_REG8(0x304c) +#define IMX334_REG_ADBIT CCI_REG8(0x3050) +#define IMX334_REG_MDBIT CCI_REG8(0x319d) +#define IMX334_REG_ADBIT1 CCI_REG16_LE(0x341c) +#define IMX334_REG_Y_OUT_SIZE CCI_REG16_LE(0x3308) +#define IMX334_REG_XVS_XHS_OUTSEL CCI_REG8(0x31a0) +#define IMX334_REG_XVS_XHS_DRV CCI_REG8(0x31a1) /* Chip ID */ -#define IMX334_REG_ID 0x3044 -#define IMX334_ID 0x1e +#define IMX334_REG_ID CCI_REG8(0x3044) +#define IMX334_ID 0x1e /* Exposure control */ -#define IMX334_REG_SHUTTER 0x3058 -#define IMX334_EXPOSURE_MIN 1 -#define IMX334_EXPOSURE_OFFSET 5 -#define IMX334_EXPOSURE_STEP 1 -#define IMX334_EXPOSURE_DEFAULT 0x0648 +#define IMX334_REG_SHUTTER CCI_REG24_LE(0x3058) +#define IMX334_EXPOSURE_MIN 1 +#define IMX334_EXPOSURE_OFFSET 5 +#define IMX334_EXPOSURE_STEP 1 +#define IMX334_EXPOSURE_DEFAULT 0x0648 + +#define IMX334_REG_LANEMODE CCI_REG8(0x3a01) + +/* Window cropping Settings */ +#define IMX334_REG_AREA3_ST_ADR_1 CCI_REG16_LE(0x3074) +#define IMX334_REG_AREA3_ST_ADR_2 CCI_REG16_LE(0x308e) +#define IMX334_REG_UNREAD_PARAM5 CCI_REG16_LE(0x30b6) +#define IMX334_REG_AREA3_WIDTH_1 CCI_REG16_LE(0x3076) +#define IMX334_REG_AREA3_WIDTH_2 CCI_REG16_LE(0x3090) +#define IMX334_REG_BLACK_OFSET_ADR CCI_REG16_LE(0x30c6) +#define IMX334_REG_UNRD_LINE_MAX CCI_REG16_LE(0x30ce) +#define IMX334_REG_UNREAD_ED_ADR CCI_REG16_LE(0x30d8) +#define IMX334_REG_UNREAD_PARAM6 CCI_REG16_LE(0x3116) + +#define IMX334_REG_VREVERSE CCI_REG8(0x304f) +#define IMX334_REG_HREVERSE CCI_REG8(0x304e) + +/* Binning Settings */ +#define IMX334_REG_HADD_VADD CCI_REG8(0x3199) +#define IMX334_REG_VALID_EXPAND CCI_REG8(0x31dd) +#define IMX334_REG_TCYCLE CCI_REG8(0x3300) /* Analog gain control */ -#define IMX334_REG_AGAIN 0x30e8 -#define IMX334_AGAIN_MIN 0 -#define IMX334_AGAIN_MAX 240 -#define IMX334_AGAIN_STEP 1 -#define IMX334_AGAIN_DEFAULT 0 +#define IMX334_REG_AGAIN CCI_REG16_LE(0x30e8) +#define IMX334_AGAIN_MIN 0 +#define IMX334_AGAIN_MAX 240 +#define IMX334_AGAIN_STEP 1 +#define IMX334_AGAIN_DEFAULT 0 /* Group hold register */ -#define IMX334_REG_HOLD 0x3001 +#define IMX334_REG_HOLD CCI_REG8(0x3001) + +#define IMX334_REG_MASTER_MODE CCI_REG8(0x3002) +#define IMX334_REG_WINMODE CCI_REG8(0x3018) +#define IMX334_REG_HTRIMMING_START CCI_REG16_LE(0x302c) +#define IMX334_REG_HNUM CCI_REG16_LE(0x302e) /* Input clock rate */ -#define IMX334_INCLK_RATE 24000000 +#define IMX334_INCLK_RATE 24000000 + +/* INCK Setting Register */ +#define IMX334_REG_BCWAIT_TIME CCI_REG8(0x300c) +#define IMX334_REG_CPWAIT_TIME CCI_REG8(0x300d) +#define IMX334_REG_INCKSEL1 CCI_REG16_LE(0x314c) +#define IMX334_REG_INCKSEL2 CCI_REG8(0x315a) +#define IMX334_REG_INCKSEL3 CCI_REG8(0x3168) +#define IMX334_REG_INCKSEL4 CCI_REG8(0x316a) +#define IMX334_REG_SYS_MODE CCI_REG8(0x319e) + +#define IMX334_REG_TCLKPOST CCI_REG16_LE(0x3a18) +#define IMX334_REG_TCLKPREPARE CCI_REG16_LE(0x3a1a) +#define IMX334_REG_TCLKTRAIL CCI_REG16_LE(0x3a1c) +#define IMX334_REG_TCLKZERO CCI_REG16_LE(0x3a1e) +#define IMX334_REG_THSPREPARE CCI_REG16_LE(0x3a20) +#define IMX334_REG_THSZERO CCI_REG16_LE(0x3a22) +#define IMX334_REG_THSTRAIL CCI_REG16_LE(0x3a24) +#define IMX334_REG_THSEXIT CCI_REG16_LE(0x3a26) +#define IMX334_REG_TPLX CCI_REG16_LE(0x3a28) /* CSI2 HW configuration */ -#define IMX334_LINK_FREQ_891M 891000000 -#define IMX334_LINK_FREQ_445M 445500000 -#define IMX334_NUM_DATA_LANES 4 +#define IMX334_LINK_FREQ_891M 891000000 +#define IMX334_LINK_FREQ_445M 445500000 +#define IMX334_NUM_DATA_LANES 4 -#define IMX334_REG_MIN 0x00 -#define IMX334_REG_MAX 0xfffff +#define IMX334_REG_MIN 0x00 +#define IMX334_REG_MAX 0xfffff /* Test Pattern Control */ -#define IMX334_REG_TP 0x329e -#define IMX334_TP_COLOR_HBARS 0xA -#define IMX334_TP_COLOR_VBARS 0xB +#define IMX334_REG_TP CCI_REG8(0x329e) +#define IMX334_TP_COLOR_HBARS 0xa +#define IMX334_TP_COLOR_VBARS 0xb -#define IMX334_TPG_EN_DOUT 0x329c -#define IMX334_TP_ENABLE 0x1 -#define IMX334_TP_DISABLE 0x0 +#define IMX334_TPG_EN_DOUT CCI_REG8(0x329c) +#define IMX334_TP_ENABLE 0x1 +#define IMX334_TP_DISABLE 0x0 -#define IMX334_TPG_COLORW 0x32a0 -#define IMX334_TPG_COLORW_120P 0x13 +#define IMX334_TPG_COLORW CCI_REG8(0x32a0) +#define IMX334_TPG_COLORW_120P 0x13 -#define IMX334_TP_CLK_EN 0x3148 -#define IMX334_TP_CLK_EN_VAL 0x10 -#define IMX334_TP_CLK_DIS_VAL 0x0 +#define IMX334_TP_CLK_EN CCI_REG8(0x3148) +#define IMX334_TP_CLK_EN_VAL 0x10 +#define IMX334_TP_CLK_DIS_VAL 0x0 -#define IMX334_DIG_CLP_MODE 0x3280 - -/** - * struct imx334_reg - imx334 sensor register - * @address: Register address - * @val: Register value - */ -struct imx334_reg { - u16 address; - u8 val; -}; +#define IMX334_DIG_CLP_MODE CCI_REG8(0x3280) /** * struct imx334_reg_list - imx334 sensor register list @@ -91,7 +137,7 @@ struct imx334_reg { */ struct imx334_reg_list { u32 num_of_regs; - const struct imx334_reg *regs; + const struct cci_reg_sequence *regs; }; /** @@ -121,6 +167,7 @@ struct imx334_mode { /** * struct imx334 - imx334 sensor device structure * @dev: Pointer to generic device + * @cci: CCI register map * @client: Pointer to i2c client * @sd: V4L2 sub-device * @pad: Media pad. Only one pad supported @@ -141,6 +188,7 @@ struct imx334_mode { */ struct imx334 { struct device *dev; + struct regmap *cci; struct i2c_client *client; struct v4l2_subdev sd; struct media_pad pad; @@ -168,250 +216,191 @@ static const s64 link_freq[] = { }; /* Sensor common mode registers values */ -static const struct imx334_reg common_mode_regs[] = { - {0x3000, 0x01}, - {0x3018, 0x04}, - {0x3030, 0xca}, - {0x3031, 0x08}, - {0x3032, 0x00}, - {0x3034, 0x4c}, - {0x3035, 0x04}, - {0x30c6, 0x00}, - {0x30c7, 0x00}, - {0x30ce, 0x00}, - {0x30cf, 0x00}, - {0x304c, 0x00}, - {0x304e, 0x00}, - {0x304f, 0x00}, - {0x3050, 0x00}, - {0x30b6, 0x00}, - {0x30b7, 0x00}, - {0x3116, 0x08}, - {0x3117, 0x00}, - {0x31a0, 0x20}, - {0x31a1, 0x0f}, - {0x300c, 0x3b}, - {0x300d, 0x2a}, - {0x314c, 0x29}, - {0x314d, 0x01}, - {0x315a, 0x06}, - {0x3168, 0xa0}, - {0x316a, 0x7e}, - {0x319e, 0x02}, - {0x3199, 0x00}, - {0x319d, 0x00}, - {0x31dd, 0x03}, - {0x3300, 0x00}, - {0x341c, 0xff}, - {0x341d, 0x01}, - {0x3a01, 0x03}, - {0x3a18, 0x7f}, - {0x3a19, 0x00}, - {0x3a1a, 0x37}, - {0x3a1b, 0x00}, - {0x3a1c, 0x37}, - {0x3a1d, 0x00}, - {0x3a1e, 0xf7}, - {0x3a1f, 0x00}, - {0x3a20, 0x3f}, - {0x3a21, 0x00}, - {0x3a20, 0x6f}, - {0x3a21, 0x00}, - {0x3a20, 0x3f}, - {0x3a21, 0x00}, - {0x3a20, 0x5f}, - {0x3a21, 0x00}, - {0x3a20, 0x2f}, - {0x3a21, 0x00}, - {0x3078, 0x02}, - {0x3079, 0x00}, - {0x307a, 0x00}, - {0x307b, 0x00}, - {0x3080, 0x02}, - {0x3081, 0x00}, - {0x3082, 0x00}, - {0x3083, 0x00}, - {0x3088, 0x02}, - {0x3094, 0x00}, - {0x3095, 0x00}, - {0x3096, 0x00}, - {0x309b, 0x02}, - {0x309c, 0x00}, - {0x309d, 0x00}, - {0x309e, 0x00}, - {0x30a4, 0x00}, - {0x30a5, 0x00}, - {0x3288, 0x21}, - {0x328a, 0x02}, - {0x3414, 0x05}, - {0x3416, 0x18}, - {0x35Ac, 0x0e}, - {0x3648, 0x01}, - {0x364a, 0x04}, - {0x364c, 0x04}, - {0x3678, 0x01}, - {0x367c, 0x31}, - {0x367e, 0x31}, - {0x3708, 0x02}, - {0x3714, 0x01}, - {0x3715, 0x02}, - {0x3716, 0x02}, - {0x3717, 0x02}, - {0x371c, 0x3d}, - {0x371d, 0x3f}, - {0x372c, 0x00}, - {0x372d, 0x00}, - {0x372e, 0x46}, - {0x372f, 0x00}, - {0x3730, 0x89}, - {0x3731, 0x00}, - {0x3732, 0x08}, - {0x3733, 0x01}, - {0x3734, 0xfe}, - {0x3735, 0x05}, - {0x375d, 0x00}, - {0x375e, 0x00}, - {0x375f, 0x61}, - {0x3760, 0x06}, - {0x3768, 0x1b}, - {0x3769, 0x1b}, - {0x376a, 0x1a}, - {0x376b, 0x19}, - {0x376c, 0x18}, - {0x376d, 0x14}, - {0x376e, 0x0f}, - {0x3776, 0x00}, - {0x3777, 0x00}, - {0x3778, 0x46}, - {0x3779, 0x00}, - {0x377a, 0x08}, - {0x377b, 0x01}, - {0x377c, 0x45}, - {0x377d, 0x01}, - {0x377e, 0x23}, - {0x377f, 0x02}, - {0x3780, 0xd9}, - {0x3781, 0x03}, - {0x3782, 0xf5}, - {0x3783, 0x06}, - {0x3784, 0xa5}, - {0x3788, 0x0f}, - {0x378a, 0xd9}, - {0x378b, 0x03}, - {0x378c, 0xeb}, - {0x378d, 0x05}, - {0x378e, 0x87}, - {0x378f, 0x06}, - {0x3790, 0xf5}, - {0x3792, 0x43}, - {0x3794, 0x7a}, - {0x3796, 0xa1}, - {0x37b0, 0x37}, - {0x3e04, 0x0e}, - {0x30e8, 0x50}, - {0x30e9, 0x00}, - {0x3e04, 0x0e}, - {0x3002, 0x00}, +static const struct cci_reg_sequence common_mode_regs[] = { + { IMX334_REG_MODE_SELECT, IMX334_MODE_STANDBY }, + { IMX334_REG_WINMODE, 0x04 }, + { IMX334_REG_VMAX, 0x0008ca }, + { IMX334_REG_HMAX, 0x044c }, + { IMX334_REG_BLACK_OFSET_ADR, 0x0000 }, + { IMX334_REG_UNRD_LINE_MAX, 0x0000 }, + { IMX334_REG_OPB_SIZE_V, 0x00 }, + { IMX334_REG_HREVERSE, 0x00 }, + { IMX334_REG_VREVERSE, 0x00 }, + { IMX334_REG_ADBIT, 0x00 }, + { IMX334_REG_UNREAD_PARAM5, 0x0000 }, + { IMX334_REG_UNREAD_PARAM6, 0x0008 }, + { IMX334_REG_XVS_XHS_OUTSEL, 0x20 }, + { IMX334_REG_XVS_XHS_DRV, 0x0f }, + { IMX334_REG_BCWAIT_TIME, 0x3b }, + { IMX334_REG_CPWAIT_TIME, 0x2a }, + { IMX334_REG_INCKSEL1, 0x0129 }, + { IMX334_REG_INCKSEL2, 0x06 }, + { IMX334_REG_INCKSEL3, 0xa0 }, + { IMX334_REG_INCKSEL4, 0x7e }, + { IMX334_REG_SYS_MODE, 0x02 }, + { IMX334_REG_HADD_VADD, 0x00 }, + { IMX334_REG_MDBIT, 0x00 }, + { IMX334_REG_VALID_EXPAND, 0x03 }, + { IMX334_REG_TCYCLE, 0x00 }, + { IMX334_REG_ADBIT1, 0x01ff }, + { IMX334_REG_LANEMODE, 0x03 }, + { IMX334_REG_TCLKPOST, 0x007f }, + { IMX334_REG_TCLKPREPARE, 0x0037 }, + { IMX334_REG_TCLKTRAIL, 0x0037 }, + { IMX334_REG_TCLKZERO, 0xf7 }, + { IMX334_REG_THSPREPARE, 0x003f }, + { IMX334_REG_THSPREPARE, 0x006f }, + { IMX334_REG_THSPREPARE, 0x003f }, + { IMX334_REG_THSPREPARE, 0x005f }, + { IMX334_REG_THSPREPARE, 0x002f }, + { CCI_REG8(0x3078), 0x02 }, + { CCI_REG8(0x3079), 0x00 }, + { CCI_REG8(0x307a), 0x00 }, + { CCI_REG8(0x307b), 0x00 }, + { CCI_REG8(0x3080), 0x02 }, + { CCI_REG8(0x3081), 0x00 }, + { CCI_REG8(0x3082), 0x00 }, + { CCI_REG8(0x3083), 0x00 }, + { CCI_REG8(0x3088), 0x02 }, + { CCI_REG8(0x3094), 0x00 }, + { CCI_REG8(0x3095), 0x00 }, + { CCI_REG8(0x3096), 0x00 }, + { CCI_REG8(0x309b), 0x02 }, + { CCI_REG8(0x309c), 0x00 }, + { CCI_REG8(0x309d), 0x00 }, + { CCI_REG8(0x309e), 0x00 }, + { CCI_REG8(0x30a4), 0x00 }, + { CCI_REG8(0x30a5), 0x00 }, + { CCI_REG8(0x3288), 0x21 }, + { CCI_REG8(0x328a), 0x02 }, + { CCI_REG8(0x3414), 0x05 }, + { CCI_REG8(0x3416), 0x18 }, + { CCI_REG8(0x35Ac), 0x0e }, + { CCI_REG8(0x3648), 0x01 }, + { CCI_REG8(0x364a), 0x04 }, + { CCI_REG8(0x364c), 0x04 }, + { CCI_REG8(0x3678), 0x01 }, + { CCI_REG8(0x367c), 0x31 }, + { CCI_REG8(0x367e), 0x31 }, + { CCI_REG8(0x3708), 0x02 }, + { CCI_REG8(0x3714), 0x01 }, + { CCI_REG8(0x3715), 0x02 }, + { CCI_REG8(0x3716), 0x02 }, + { CCI_REG8(0x3717), 0x02 }, + { CCI_REG8(0x371c), 0x3d }, + { CCI_REG8(0x371d), 0x3f }, + { CCI_REG8(0x372c), 0x00 }, + { CCI_REG8(0x372d), 0x00 }, + { CCI_REG8(0x372e), 0x46 }, + { CCI_REG8(0x372f), 0x00 }, + { CCI_REG8(0x3730), 0x89 }, + { CCI_REG8(0x3731), 0x00 }, + { CCI_REG8(0x3732), 0x08 }, + { CCI_REG8(0x3733), 0x01 }, + { CCI_REG8(0x3734), 0xfe }, + { CCI_REG8(0x3735), 0x05 }, + { CCI_REG8(0x375d), 0x00 }, + { CCI_REG8(0x375e), 0x00 }, + { CCI_REG8(0x375f), 0x61 }, + { CCI_REG8(0x3760), 0x06 }, + { CCI_REG8(0x3768), 0x1b }, + { CCI_REG8(0x3769), 0x1b }, + { CCI_REG8(0x376a), 0x1a }, + { CCI_REG8(0x376b), 0x19 }, + { CCI_REG8(0x376c), 0x18 }, + { CCI_REG8(0x376d), 0x14 }, + { CCI_REG8(0x376e), 0x0f }, + { CCI_REG8(0x3776), 0x00 }, + { CCI_REG8(0x3777), 0x00 }, + { CCI_REG8(0x3778), 0x46 }, + { CCI_REG8(0x3779), 0x00 }, + { CCI_REG8(0x377a), 0x08 }, + { CCI_REG8(0x377b), 0x01 }, + { CCI_REG8(0x377c), 0x45 }, + { CCI_REG8(0x377d), 0x01 }, + { CCI_REG8(0x377e), 0x23 }, + { CCI_REG8(0x377f), 0x02 }, + { CCI_REG8(0x3780), 0xd9 }, + { CCI_REG8(0x3781), 0x03 }, + { CCI_REG8(0x3782), 0xf5 }, + { CCI_REG8(0x3783), 0x06 }, + { CCI_REG8(0x3784), 0xa5 }, + { CCI_REG8(0x3788), 0x0f }, + { CCI_REG8(0x378a), 0xd9 }, + { CCI_REG8(0x378b), 0x03 }, + { CCI_REG8(0x378c), 0xeb }, + { CCI_REG8(0x378d), 0x05 }, + { CCI_REG8(0x378e), 0x87 }, + { CCI_REG8(0x378f), 0x06 }, + { CCI_REG8(0x3790), 0xf5 }, + { CCI_REG8(0x3792), 0x43 }, + { CCI_REG8(0x3794), 0x7a }, + { CCI_REG8(0x3796), 0xa1 }, + { CCI_REG8(0x37b0), 0x37 }, + { CCI_REG8(0x3e04), 0x0e }, + { IMX334_REG_AGAIN, 0x0050 }, + { CCI_REG8(0x3e04), 0x0e }, + { IMX334_REG_MASTER_MODE, 0x00 }, }; /* Sensor mode registers for 640x480@30fps */ -static const struct imx334_reg mode_640x480_regs[] = { - {0x302c, 0x70}, - {0x302d, 0x06}, - {0x302e, 0x80}, - {0x302f, 0x02}, - {0x3074, 0x48}, - {0x3075, 0x07}, - {0x308e, 0x49}, - {0x308f, 0x07}, - {0x3076, 0xe0}, - {0x3077, 0x01}, - {0x3090, 0xe0}, - {0x3091, 0x01}, - {0x3308, 0xe0}, - {0x3309, 0x01}, - {0x30d8, 0x30}, - {0x30d9, 0x0b}, +static const struct cci_reg_sequence mode_640x480_regs[] = { + { IMX334_REG_HTRIMMING_START, 0x0670 }, + { IMX334_REG_HNUM, 0x0280 }, + { IMX334_REG_AREA3_ST_ADR_1, 0x0748 }, + { IMX334_REG_AREA3_ST_ADR_2, 0x0749 }, + { IMX334_REG_AREA3_WIDTH_1, 0x01e0 }, + { IMX334_REG_AREA3_WIDTH_2, 0x01e0 }, + { IMX334_REG_Y_OUT_SIZE, 0x01e0 }, + { IMX334_REG_UNREAD_ED_ADR, 0x0b30 }, }; /* Sensor mode registers for 1280x720@30fps */ -static const struct imx334_reg mode_1280x720_regs[] = { - {0x302c, 0x30}, - {0x302d, 0x05}, - {0x302e, 0x00}, - {0x302f, 0x05}, - {0x3074, 0x84}, - {0x3075, 0x03}, - {0x308e, 0x85}, - {0x308f, 0x03}, - {0x3076, 0xd0}, - {0x3077, 0x02}, - {0x3090, 0xd0}, - {0x3091, 0x02}, - {0x3308, 0xd0}, - {0x3309, 0x02}, - {0x30d8, 0x30}, - {0x30d9, 0x0b}, +static const struct cci_reg_sequence mode_1280x720_regs[] = { + { IMX334_REG_HTRIMMING_START, 0x0530 }, + { IMX334_REG_HNUM, 0x0500 }, + { IMX334_REG_AREA3_ST_ADR_1, 0x0384 }, + { IMX334_REG_AREA3_ST_ADR_2, 0x0385 }, + { IMX334_REG_AREA3_WIDTH_1, 0x02d0 }, + { IMX334_REG_AREA3_WIDTH_2, 0x02d0 }, + { IMX334_REG_Y_OUT_SIZE, 0x02d0 }, + { IMX334_REG_UNREAD_ED_ADR, 0x0b30 }, }; /* Sensor mode registers for 1920x1080@30fps */ -static const struct imx334_reg mode_1920x1080_regs[] = { - {0x302c, 0xf0}, - {0x302d, 0x03}, - {0x302e, 0x80}, - {0x302f, 0x07}, - {0x3074, 0xcc}, - {0x3075, 0x02}, - {0x308e, 0xcd}, - {0x308f, 0x02}, - {0x3076, 0x38}, - {0x3077, 0x04}, - {0x3090, 0x38}, - {0x3091, 0x04}, - {0x3308, 0x38}, - {0x3309, 0x04}, - {0x30d8, 0x18}, - {0x30d9, 0x0a}, +static const struct cci_reg_sequence mode_1920x1080_regs[] = { + { IMX334_REG_HTRIMMING_START, 0x03f0 }, + { IMX334_REG_HNUM, 0x0780 }, + { IMX334_REG_AREA3_ST_ADR_1, 0x02cc }, + { IMX334_REG_AREA3_ST_ADR_2, 0x02cd }, + { IMX334_REG_AREA3_WIDTH_1, 0x0438 }, + { IMX334_REG_AREA3_WIDTH_2, 0x0438 }, + { IMX334_REG_Y_OUT_SIZE, 0x0438 }, + { IMX334_REG_UNREAD_ED_ADR, 0x0a18 }, }; /* Sensor mode registers for 3840x2160@30fps */ -static const struct imx334_reg mode_3840x2160_regs[] = { - {0x3034, 0x26}, - {0x3035, 0x02}, - {0x315a, 0x02}, - {0x302c, 0x3c}, - {0x302d, 0x00}, - {0x302e, 0x00}, - {0x302f, 0x0f}, - {0x3074, 0xb0}, - {0x3075, 0x00}, - {0x308e, 0xb1}, - {0x308f, 0x00}, - {0x30d8, 0x20}, - {0x30d9, 0x12}, - {0x3076, 0x70}, - {0x3077, 0x08}, - {0x3090, 0x70}, - {0x3091, 0x08}, - {0x3308, 0x70}, - {0x3309, 0x08}, - {0x319e, 0x00}, - {0x3a00, 0x01}, - {0x3a18, 0xbf}, - {0x3a1a, 0x67}, - {0x3a1c, 0x6f}, - {0x3a1e, 0xd7}, - {0x3a1f, 0x01}, - {0x3a20, 0x6f}, - {0x3a21, 0x00}, - {0x3a22, 0xcf}, - {0x3a23, 0x00}, - {0x3a24, 0x6f}, - {0x3a25, 0x00}, - {0x3a26, 0xb7}, - {0x3a27, 0x00}, - {0x3a28, 0x5f}, - {0x3a29, 0x00}, +static const struct cci_reg_sequence mode_3840x2160_regs[] = { + { IMX334_REG_HMAX, 0x0226 }, + { IMX334_REG_INCKSEL2, 0x02 }, + { IMX334_REG_HTRIMMING_START, 0x003c }, + { IMX334_REG_HNUM, 0x0f00 }, + { IMX334_REG_AREA3_ST_ADR_1, 0x00b0 }, + { IMX334_REG_AREA3_ST_ADR_2, 0x00b1 }, + { IMX334_REG_UNREAD_ED_ADR, 0x1220 }, + { IMX334_REG_AREA3_WIDTH_1, 0x0870 }, + { IMX334_REG_AREA3_WIDTH_2, 0x0870 }, + { IMX334_REG_Y_OUT_SIZE, 0x0870 }, + { IMX334_REG_SYS_MODE, 0x0100 }, + { IMX334_REG_TCLKPOST, 0x00bf }, + { IMX334_REG_TCLKPREPARE, 0x0067 }, + { IMX334_REG_TCLKTRAIL, 0x006f }, + { IMX334_REG_TCLKZERO, 0x1d7 }, + { IMX334_REG_THSPREPARE, 0x006f }, + { IMX334_REG_THSZERO, 0x00cf }, + { IMX334_REG_THSTRAIL, 0x006f }, + { IMX334_REG_THSEXIT, 0x00b7 }, + { IMX334_REG_TPLX, 0x005f }, }; static const char * const imx334_test_pattern_menu[] = { @@ -426,18 +415,16 @@ static const int imx334_test_pattern_val[] = { IMX334_TP_COLOR_VBARS, }; -static const struct imx334_reg raw10_framefmt_regs[] = { - {0x3050, 0x00}, - {0x319d, 0x00}, - {0x341c, 0xff}, - {0x341d, 0x01}, +static const struct cci_reg_sequence raw10_framefmt_regs[] = { + { IMX334_REG_ADBIT, 0x00 }, + { IMX334_REG_MDBIT, 0x00 }, + { IMX334_REG_ADBIT1, 0x01ff }, }; -static const struct imx334_reg raw12_framefmt_regs[] = { - {0x3050, 0x01}, - {0x319d, 0x01}, - {0x341c, 0x47}, - {0x341d, 0x00}, +static const struct cci_reg_sequence raw12_framefmt_regs[] = { + { IMX334_REG_ADBIT, 0x01 }, + { IMX334_REG_MDBIT, 0x01 }, + { IMX334_REG_ADBIT1, 0x0047 }, }; static const u32 imx334_mbus_codes[] = { @@ -513,101 +500,6 @@ static inline struct imx334 *to_imx334(struct v4l2_subdev *subdev) return container_of(subdev, struct imx334, sd); } -/** - * imx334_read_reg() - Read registers. - * @imx334: pointer to imx334 device - * @reg: register address - * @len: length of bytes to read. Max supported bytes is 4 - * @val: pointer to register value to be filled. - * - * Big endian register addresses with little endian values. - * - * Return: 0 if successful, error code otherwise. - */ -static int imx334_read_reg(struct imx334 *imx334, u16 reg, u32 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx334->sd); - struct i2c_msg msgs[2] = {0}; - u8 addr_buf[2] = {0}; - u8 data_buf[4] = {0}; - int ret; - - if (WARN_ON(len > 4)) - return -EINVAL; - - put_unaligned_be16(reg, addr_buf); - - /* Write register address */ - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = ARRAY_SIZE(addr_buf); - msgs[0].buf = addr_buf; - - /* Read data from register */ - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = len; - msgs[1].buf = data_buf; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) - return -EIO; - - *val = get_unaligned_le32(data_buf); - - return 0; -} - -/** - * imx334_write_reg() - Write register - * @imx334: pointer to imx334 device - * @reg: register address - * @len: length of bytes. Max supported bytes is 4 - * @val: register value - * - * Big endian register addresses with little endian values. - * - * Return: 0 if successful, error code otherwise. - */ -static int imx334_write_reg(struct imx334 *imx334, u16 reg, u32 len, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx334->sd); - u8 buf[6] = {0}; - - if (WARN_ON(len > 4)) - return -EINVAL; - - put_unaligned_be16(reg, buf); - put_unaligned_le32(val, buf + 2); - if (i2c_master_send(client, buf, len + 2) != len + 2) - return -EIO; - - return 0; -} - -/** - * imx334_write_regs() - Write a list of registers - * @imx334: pointer to imx334 device - * @regs: list of registers to be written - * @len: length of registers array - * - * Return: 0 if successful, error code otherwise. - */ -static int imx334_write_regs(struct imx334 *imx334, - const struct imx334_reg *regs, u32 len) -{ - unsigned int i; - int ret; - - for (i = 0; i < len; i++) { - ret = imx334_write_reg(imx334, regs[i].address, 1, regs[i].val); - if (ret) - return ret; - } - - return 0; -} - /** * imx334_update_controls() - Update control ranges based on streaming mode * @imx334: pointer to imx334 device @@ -653,7 +545,7 @@ static int imx334_update_controls(struct imx334 *imx334, static int imx334_update_exp_gain(struct imx334 *imx334, u32 exposure, u32 gain) { u32 lpfr, shutter; - int ret; + int ret, ret_hold; lpfr = imx334->vblank + imx334->cur_mode->height; shutter = lpfr - exposure; @@ -661,22 +553,14 @@ static int imx334_update_exp_gain(struct imx334 *imx334, u32 exposure, u32 gain) dev_dbg(imx334->dev, "Set long exp %u analog gain %u sh0 %u lpfr %u\n", exposure, gain, shutter, lpfr); - ret = imx334_write_reg(imx334, IMX334_REG_HOLD, 1, 1); - if (ret) - return ret; - - ret = imx334_write_reg(imx334, IMX334_REG_LPFR, 3, lpfr); - if (ret) - goto error_release_group_hold; - - ret = imx334_write_reg(imx334, IMX334_REG_SHUTTER, 3, shutter); - if (ret) - goto error_release_group_hold; - - ret = imx334_write_reg(imx334, IMX334_REG_AGAIN, 1, gain); + cci_write(imx334->cci, IMX334_REG_HOLD, 1, &ret); + cci_write(imx334->cci, IMX334_REG_VMAX, lpfr, &ret); + cci_write(imx334->cci, IMX334_REG_SHUTTER, shutter, &ret); + cci_write(imx334->cci, IMX334_REG_AGAIN, gain, &ret); -error_release_group_hold: - imx334_write_reg(imx334, IMX334_REG_HOLD, 1, 0); + ret_hold = cci_write(imx334->cci, IMX334_REG_HOLD, 0, NULL); + if (ret_hold) + return ret_hold; return ret; } @@ -740,21 +624,21 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_TEST_PATTERN: if (ctrl->val) { - imx334_write_reg(imx334, IMX334_TP_CLK_EN, 1, - IMX334_TP_CLK_EN_VAL); - imx334_write_reg(imx334, IMX334_DIG_CLP_MODE, 1, 0x0); - imx334_write_reg(imx334, IMX334_TPG_COLORW, 1, - IMX334_TPG_COLORW_120P); - imx334_write_reg(imx334, IMX334_REG_TP, 1, - imx334_test_pattern_val[ctrl->val]); - imx334_write_reg(imx334, IMX334_TPG_EN_DOUT, 1, - IMX334_TP_ENABLE); + cci_write(imx334->cci, IMX334_TP_CLK_EN, + IMX334_TP_CLK_EN_VAL, NULL); + cci_write(imx334->cci, IMX334_DIG_CLP_MODE, 0x0, NULL); + cci_write(imx334->cci, IMX334_TPG_COLORW, + IMX334_TPG_COLORW_120P, NULL); + cci_write(imx334->cci, IMX334_REG_TP, + imx334_test_pattern_val[ctrl->val], NULL); + cci_write(imx334->cci, IMX334_TPG_EN_DOUT, + IMX334_TP_ENABLE, NULL); } else { - imx334_write_reg(imx334, IMX334_DIG_CLP_MODE, 1, 0x1); - imx334_write_reg(imx334, IMX334_TP_CLK_EN, 1, - IMX334_TP_CLK_DIS_VAL); - imx334_write_reg(imx334, IMX334_TPG_EN_DOUT, 1, - IMX334_TP_DISABLE); + cci_write(imx334->cci, IMX334_DIG_CLP_MODE, 0x1, NULL); + cci_write(imx334->cci, IMX334_TP_CLK_EN, + IMX334_TP_CLK_DIS_VAL, NULL); + cci_write(imx334->cci, IMX334_TPG_EN_DOUT, + IMX334_TP_DISABLE, NULL); } ret = 0; break; @@ -961,12 +845,13 @@ static int imx334_set_framefmt(struct imx334 *imx334) { switch (imx334->cur_code) { case MEDIA_BUS_FMT_SRGGB10_1X10: - return imx334_write_regs(imx334, raw10_framefmt_regs, - ARRAY_SIZE(raw10_framefmt_regs)); + return cci_multi_reg_write(imx334->cci, raw10_framefmt_regs, + ARRAY_SIZE(raw10_framefmt_regs), NULL); + case MEDIA_BUS_FMT_SRGGB12_1X12: - return imx334_write_regs(imx334, raw12_framefmt_regs, - ARRAY_SIZE(raw12_framefmt_regs)); + return cci_multi_reg_write(imx334->cci, raw12_framefmt_regs, + ARRAY_SIZE(raw12_framefmt_regs), NULL); } return -EINVAL; @@ -983,8 +868,8 @@ static int imx334_start_streaming(struct imx334 *imx334) const struct imx334_reg_list *reg_list; int ret; - ret = imx334_write_regs(imx334, common_mode_regs, - ARRAY_SIZE(common_mode_regs)); + ret = cci_multi_reg_write(imx334->cci, common_mode_regs, + ARRAY_SIZE(common_mode_regs), NULL); if (ret) { dev_err(imx334->dev, "fail to write common registers\n"); return ret; @@ -992,8 +877,8 @@ static int imx334_start_streaming(struct imx334 *imx334) /* Write sensor mode registers */ reg_list = &imx334->cur_mode->reg_list; - ret = imx334_write_regs(imx334, reg_list->regs, - reg_list->num_of_regs); + ret = cci_multi_reg_write(imx334->cci, reg_list->regs, + reg_list->num_of_regs, NULL); if (ret) { dev_err(imx334->dev, "fail to write initial registers\n"); return ret; @@ -1014,8 +899,8 @@ static int imx334_start_streaming(struct imx334 *imx334) } /* Start streaming */ - ret = imx334_write_reg(imx334, IMX334_REG_MODE_SELECT, - 1, IMX334_MODE_STREAMING); + ret = cci_write(imx334->cci, IMX334_REG_MODE_SELECT, + IMX334_MODE_STREAMING, NULL); if (ret) { dev_err(imx334->dev, "fail to start streaming\n"); return ret; @@ -1032,8 +917,8 @@ static int imx334_start_streaming(struct imx334 *imx334) */ static int imx334_stop_streaming(struct imx334 *imx334) { - return imx334_write_reg(imx334, IMX334_REG_MODE_SELECT, - 1, IMX334_MODE_STANDBY); + return cci_write(imx334->cci, IMX334_REG_MODE_SELECT, + IMX334_MODE_STANDBY, NULL); } /** @@ -1084,14 +969,14 @@ error_unlock: static int imx334_detect(struct imx334 *imx334) { int ret; - u32 val; + u64 val; - ret = imx334_read_reg(imx334, IMX334_REG_ID, 2, &val); + ret = cci_read(imx334->cci, IMX334_REG_ID, &val, NULL); if (ret) return ret; if (val != IMX334_ID) { - dev_err(imx334->dev, "chip id mismatch: %x!=%x\n", + dev_err(imx334->dev, "chip id mismatch: %x!=%llx\n", IMX334_ID, val); return -ENXIO; } @@ -1339,6 +1224,11 @@ static int imx334_probe(struct i2c_client *client) return -ENOMEM; imx334->dev = &client->dev; + imx334->cci = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx334->cci)) { + dev_err(imx334->dev, "Unable to initialize I2C\n"); + return -ENODEV; + } /* Initialize subdev */ v4l2_i2c_subdev_init(&imx334->sd, client, &imx334_subdev_ops); -- cgit v1.2.3-59-g8ed1b From 731c8efd5b74f8aa4c57ffd8e8561d93e425b007 Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:25 +0530 Subject: media: i2c: imx334: Remove redundant register entries IMX334_REG_{ADBIT, MDBIT, ADBIT1}: Already written in imx334_set_framefmt function. IMX334_REG_THSPREPARE: Unnecessary repeated writes removed. CCI_REG8(0x3e04): Unnecessary repeated writes removed. Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 0785bf213d91..9d4d15df8dcf 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -226,7 +226,6 @@ static const struct cci_reg_sequence common_mode_regs[] = { { IMX334_REG_OPB_SIZE_V, 0x00 }, { IMX334_REG_HREVERSE, 0x00 }, { IMX334_REG_VREVERSE, 0x00 }, - { IMX334_REG_ADBIT, 0x00 }, { IMX334_REG_UNREAD_PARAM5, 0x0000 }, { IMX334_REG_UNREAD_PARAM6, 0x0008 }, { IMX334_REG_XVS_XHS_OUTSEL, 0x20 }, @@ -239,19 +238,13 @@ static const struct cci_reg_sequence common_mode_regs[] = { { IMX334_REG_INCKSEL4, 0x7e }, { IMX334_REG_SYS_MODE, 0x02 }, { IMX334_REG_HADD_VADD, 0x00 }, - { IMX334_REG_MDBIT, 0x00 }, { IMX334_REG_VALID_EXPAND, 0x03 }, { IMX334_REG_TCYCLE, 0x00 }, - { IMX334_REG_ADBIT1, 0x01ff }, { IMX334_REG_LANEMODE, 0x03 }, { IMX334_REG_TCLKPOST, 0x007f }, { IMX334_REG_TCLKPREPARE, 0x0037 }, { IMX334_REG_TCLKTRAIL, 0x0037 }, { IMX334_REG_TCLKZERO, 0xf7 }, - { IMX334_REG_THSPREPARE, 0x003f }, - { IMX334_REG_THSPREPARE, 0x006f }, - { IMX334_REG_THSPREPARE, 0x003f }, - { IMX334_REG_THSPREPARE, 0x005f }, { IMX334_REG_THSPREPARE, 0x002f }, { CCI_REG8(0x3078), 0x02 }, { CCI_REG8(0x3079), 0x00 }, @@ -339,7 +332,6 @@ static const struct cci_reg_sequence common_mode_regs[] = { { CCI_REG8(0x37b0), 0x37 }, { CCI_REG8(0x3e04), 0x0e }, { IMX334_REG_AGAIN, 0x0050 }, - { CCI_REG8(0x3e04), 0x0e }, { IMX334_REG_MASTER_MODE, 0x00 }, }; -- cgit v1.2.3-59-g8ed1b From 9e089a649a229fb08942ada75b0f1c7f5814e065 Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:26 +0530 Subject: media: i2c: imx334: Configure lane mode dynamically Configure the lane mode dynamically from the streaming function instead of using a hardcoded value. Signed-off-by: Tarang Raval [Sakari Ailus: Fix checkpatch.pl issue, lower Dynamically.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 9d4d15df8dcf..561ed2c87005 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -47,6 +47,8 @@ #define IMX334_EXPOSURE_DEFAULT 0x0648 #define IMX334_REG_LANEMODE CCI_REG8(0x3a01) +#define IMX334_CSI_4_LANE_MODE 3 +#define IMX334_CSI_8_LANE_MODE 7 /* Window cropping Settings */ #define IMX334_REG_AREA3_ST_ADR_1 CCI_REG16_LE(0x3074) @@ -240,7 +242,6 @@ static const struct cci_reg_sequence common_mode_regs[] = { { IMX334_REG_HADD_VADD, 0x00 }, { IMX334_REG_VALID_EXPAND, 0x03 }, { IMX334_REG_TCYCLE, 0x00 }, - { IMX334_REG_LANEMODE, 0x03 }, { IMX334_REG_TCLKPOST, 0x007f }, { IMX334_REG_TCLKPREPARE, 0x0037 }, { IMX334_REG_TCLKTRAIL, 0x0037 }, @@ -876,6 +877,13 @@ static int imx334_start_streaming(struct imx334 *imx334) return ret; } + ret = cci_write(imx334->cci, IMX334_REG_LANEMODE, + IMX334_CSI_4_LANE_MODE, NULL); + if (ret) { + dev_err(imx334->dev, "failed to configure lanes\n"); + return ret; + } + ret = imx334_set_framefmt(imx334); if (ret) { dev_err(imx334->dev, "%s failed to set frame format: %d\n", -- cgit v1.2.3-59-g8ed1b From a6dde677b93795a04f43f90427723bb4c2a50e5e Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:27 +0530 Subject: media: i2c: imx334: Fix power management and control handling Some controls may need the sensor to be powered on to update their values. Currently, only the exposure control does this. To ensure proper handling, the power-up sequence is moved outside the switch-case. Additionally, VBLANK control is now processed earlier so its changes can correctly affect other controls. Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 561ed2c87005..e8422d2fadfd 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -578,8 +578,7 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) u32 exposure; int ret; - switch (ctrl->id) { - case V4L2_CID_VBLANK: + if (ctrl->id == V4L2_CID_VBLANK) { imx334->vblank = imx334->vblank_ctrl->val; dev_dbg(imx334->dev, "Received vblank %u, new lpfr %u\n", @@ -592,13 +591,24 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) imx334->cur_mode->height - IMX334_EXPOSURE_OFFSET, 1, IMX334_EXPOSURE_DEFAULT); + if (ret) + return ret; + } + + /* Set controls only if sensor is in power on state */ + if (!pm_runtime_get_if_in_use(imx334->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_VBLANK: + exposure = imx334->exp_ctrl->val; + analog_gain = imx334->again_ctrl->val; + + ret = imx334_update_exp_gain(imx334, exposure, analog_gain); + break; case V4L2_CID_EXPOSURE: - /* Set controls only if sensor is in power on state */ - if (!pm_runtime_get_if_in_use(imx334->dev)) - return 0; - exposure = ctrl->val; analog_gain = imx334->again_ctrl->val; @@ -607,8 +617,6 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) ret = imx334_update_exp_gain(imx334, exposure, analog_gain); - pm_runtime_put(imx334->dev); - break; case V4L2_CID_PIXEL_RATE: case V4L2_CID_LINK_FREQ: @@ -640,6 +648,8 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) ret = -EINVAL; } + pm_runtime_put(imx334->dev); + return ret; } -- cgit v1.2.3-59-g8ed1b From b493cd3c03641f9bbaa9787e43ca92163cb50051 Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:28 +0530 Subject: media: i2c: imx334: Fix runtime PM handling in remove function pm_runtime_suspended() only checks the current runtime PM status and does not modify it, making it ineffective in this context. This could result in improper power management if the device remains active when removed. This patch fixes the issue by introducing a check with pm_runtime_status_suspended() to determine if the device is already suspended. If it is not, it calls imx334_power_off() to power down the device and then uses pm_runtime_set_suspended() to correctly update the runtime PM status to suspended. Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index e8422d2fadfd..2ede0787be14 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -1328,7 +1328,10 @@ static void imx334_remove(struct i2c_client *client) v4l2_ctrl_handler_free(sd->ctrl_handler); pm_runtime_disable(&client->dev); - pm_runtime_suspended(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) { + imx334_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + } mutex_destroy(&imx334->mutex); } -- cgit v1.2.3-59-g8ed1b From 01dfdf6a80c57151af0589af0db7adbbdd1361c7 Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:29 +0530 Subject: media: i2c: imx334: Enable runtime PM before sub-device registration Runtime PM is fully initialized before calling v4l2_async_register_subdev_sensor(). Moving the runtime PM initialization earlier prevents potential access to an uninitialized or powered-down device. Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 2ede0787be14..412ab469b130 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -1287,6 +1287,9 @@ static int imx334_probe(struct i2c_client *client) goto error_handler_free; } + pm_runtime_set_active(imx334->dev); + pm_runtime_enable(imx334->dev); + ret = v4l2_async_register_subdev_sensor(&imx334->sd); if (ret < 0) { dev_err(imx334->dev, @@ -1294,13 +1297,13 @@ static int imx334_probe(struct i2c_client *client) goto error_media_entity; } - pm_runtime_set_active(imx334->dev); - pm_runtime_enable(imx334->dev); pm_runtime_idle(imx334->dev); return 0; error_media_entity: + pm_runtime_disable(imx334->dev); + pm_runtime_set_suspended(imx334->dev); media_entity_cleanup(&imx334->sd.entity); error_handler_free: v4l2_ctrl_handler_free(imx334->sd.ctrl_handler); -- cgit v1.2.3-59-g8ed1b From 9d382f6a9978916317b3fb4ef07b5fec684adde0 Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:30 +0530 Subject: media: i2c: imx334: Use subdev state lock for synchronization Replace the custom mutex in the imx334 driver with the V4L2 subdev state lock for control synchronization. Initialize the subdev with v4l2_subdev_init_finalize in imx334_probe, adding proper cleanup in error paths and imx334_remove. This aligns the driver with V4L2 standards. Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 52 +++++++++++++++------------------------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 412ab469b130..fa11619bb368 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -184,7 +184,6 @@ struct imx334_mode { * @again_ctrl: Pointer to analog gain control * @vblank: Vertical blanking in lines * @cur_mode: Pointer to current selected sensor mode - * @mutex: Mutex for serializing sensor controls * @link_freq_bitmap: Menu bitmap for link_freq_ctrl * @cur_code: current selected format code */ @@ -207,7 +206,6 @@ struct imx334 { }; u32 vblank; const struct imx334_mode *cur_mode; - struct mutex mutex; unsigned long link_freq_bitmap; u32 cur_code; }; @@ -755,8 +753,6 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd, { struct imx334 *imx334 = to_imx334(sd); - mutex_lock(&imx334->mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; @@ -767,8 +763,6 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd, imx334_fill_pad_format(imx334, imx334->cur_mode, fmt); } - mutex_unlock(&imx334->mutex); - return 0; } @@ -788,8 +782,6 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, const struct imx334_mode *mode; int ret = 0; - mutex_lock(&imx334->mutex); - mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), width, height, @@ -810,8 +802,6 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, imx334->cur_mode = mode; } - mutex_unlock(&imx334->mutex); - return ret; } @@ -830,8 +820,6 @@ static int imx334_init_state(struct v4l2_subdev *sd, fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - mutex_lock(&imx334->mutex); - imx334_fill_pad_format(imx334, imx334->cur_mode, &fmt); __v4l2_ctrl_modify_range(imx334->link_freq_ctrl, 0, @@ -839,8 +827,6 @@ static int imx334_init_state(struct v4l2_subdev *sd, ~(imx334->link_freq_bitmap), __ffs(imx334->link_freq_bitmap)); - mutex_unlock(&imx334->mutex); - return imx334_set_pad_format(sd, sd_state, &fmt); } @@ -943,12 +929,10 @@ static int imx334_set_stream(struct v4l2_subdev *sd, int enable) struct imx334 *imx334 = to_imx334(sd); int ret; - mutex_lock(&imx334->mutex); - if (enable) { ret = pm_runtime_resume_and_get(imx334->dev); if (ret < 0) - goto error_unlock; + return ret; ret = imx334_start_streaming(imx334); if (ret) @@ -958,15 +942,10 @@ static int imx334_set_stream(struct v4l2_subdev *sd, int enable) pm_runtime_put(imx334->dev); } - mutex_unlock(&imx334->mutex); - return 0; error_power_off: pm_runtime_put(imx334->dev); -error_unlock: - mutex_unlock(&imx334->mutex); - return ret; } @@ -1145,9 +1124,6 @@ static int imx334_init_controls(struct imx334 *imx334) if (ret) return ret; - /* Serialize controls with sensor device */ - ctrl_hdlr->lock = &imx334->mutex; - /* Initialize exposure and gain */ lpfr = mode->vblank + mode->height; imx334->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, @@ -1249,12 +1225,10 @@ static int imx334_probe(struct i2c_client *client) return dev_err_probe(imx334->dev, ret, "HW configuration is not supported\n"); - mutex_init(&imx334->mutex); - ret = imx334_power_on(imx334->dev); if (ret) { dev_err_probe(imx334->dev, ret, "failed to power-on the sensor\n"); - goto error_mutex_destroy; + return ret; } /* Check module identity */ @@ -1287,6 +1261,13 @@ static int imx334_probe(struct i2c_client *client) goto error_handler_free; } + imx334->sd.state_lock = imx334->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx334->sd); + if (ret < 0) { + dev_err(imx334->dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + pm_runtime_set_active(imx334->dev); pm_runtime_enable(imx334->dev); @@ -1294,23 +1275,26 @@ static int imx334_probe(struct i2c_client *client) if (ret < 0) { dev_err(imx334->dev, "failed to register async subdev: %d\n", ret); - goto error_media_entity; + goto error_subdev_cleanup; } pm_runtime_idle(imx334->dev); return 0; -error_media_entity: +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx334->sd); pm_runtime_disable(imx334->dev); pm_runtime_set_suspended(imx334->dev); + +error_media_entity: media_entity_cleanup(&imx334->sd.entity); + error_handler_free: v4l2_ctrl_handler_free(imx334->sd.ctrl_handler); + error_power_off: imx334_power_off(imx334->dev); -error_mutex_destroy: - mutex_destroy(&imx334->mutex); return ret; } @@ -1324,9 +1308,9 @@ error_mutex_destroy: static void imx334_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx334 *imx334 = to_imx334(sd); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); @@ -1335,8 +1319,6 @@ static void imx334_remove(struct i2c_client *client) imx334_power_off(&client->dev); pm_runtime_set_suspended(&client->dev); } - - mutex_destroy(&imx334->mutex); } static const struct dev_pm_ops imx334_pm_ops = { -- cgit v1.2.3-59-g8ed1b From 6f1b74c1a686c93b404bd57d73577a6b5b19c5c3 Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Sat, 29 Mar 2025 11:13:31 +0530 Subject: media: i2c: imx334: switch to {enable,disable}_streams Switch from s_stream to enable_streams and disable_streams callbacks. Signed-off-by: Tarang Raval Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 78 ++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index fa11619bb368..fc875072f859 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -847,21 +847,31 @@ static int imx334_set_framefmt(struct imx334 *imx334) } /** - * imx334_start_streaming() - Start sensor stream - * @imx334: pointer to imx334 device + * 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_start_streaming(struct imx334 *imx334) +static int imx334_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { + struct imx334 *imx334 = to_imx334(sd); const struct imx334_reg_list *reg_list; int ret; + ret = pm_runtime_resume_and_get(imx334->dev); + if (ret < 0) + return ret; + ret = cci_multi_reg_write(imx334->cci, common_mode_regs, ARRAY_SIZE(common_mode_regs), NULL); if (ret) { dev_err(imx334->dev, "fail to write common registers\n"); - return ret; + goto err_rpm_put; } /* Write sensor mode registers */ @@ -870,28 +880,28 @@ static int imx334_start_streaming(struct imx334 *imx334) reg_list->num_of_regs, NULL); if (ret) { dev_err(imx334->dev, "fail to write initial registers\n"); - return ret; + goto err_rpm_put; } ret = cci_write(imx334->cci, IMX334_REG_LANEMODE, IMX334_CSI_4_LANE_MODE, NULL); if (ret) { dev_err(imx334->dev, "failed to configure lanes\n"); - return ret; + goto err_rpm_put; } ret = imx334_set_framefmt(imx334); if (ret) { dev_err(imx334->dev, "%s failed to set frame format: %d\n", __func__, ret); - return ret; + goto err_rpm_put; } /* Setup handler will write actual exposure and gain */ ret = __v4l2_ctrl_handler_setup(imx334->sd.ctrl_handler); if (ret) { dev_err(imx334->dev, "fail to setup handler\n"); - return ret; + goto err_rpm_put; } /* Start streaming */ @@ -899,53 +909,39 @@ static int imx334_start_streaming(struct imx334 *imx334) IMX334_MODE_STREAMING, NULL); if (ret) { dev_err(imx334->dev, "fail to start streaming\n"); - return ret; + goto err_rpm_put; } return 0; -} -/** - * imx334_stop_streaming() - Stop sensor stream - * @imx334: pointer to imx334 device - * - * Return: 0 if successful, error code otherwise. - */ -static int imx334_stop_streaming(struct imx334 *imx334) -{ - return cci_write(imx334->cci, IMX334_REG_MODE_SELECT, - IMX334_MODE_STANDBY, NULL); +err_rpm_put: + pm_runtime_put(imx334->dev); + return ret; } /** - * imx334_set_stream() - Enable sensor streaming - * @sd: pointer to imx334 subdevice - * @enable: set to enable sensor streaming + * 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_set_stream(struct v4l2_subdev *sd, int enable) +static int imx334_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct imx334 *imx334 = to_imx334(sd); int ret; - if (enable) { - ret = pm_runtime_resume_and_get(imx334->dev); - if (ret < 0) - return ret; - - ret = imx334_start_streaming(imx334); - if (ret) - goto error_power_off; - } else { - imx334_stop_streaming(imx334); - pm_runtime_put(imx334->dev); - } - - return 0; + ret = cci_write(imx334->cci, IMX334_REG_MODE_SELECT, + IMX334_MODE_STANDBY, NULL); + if (ret) + dev_err(imx334->dev, "%s failed to stop stream\n", __func__); -error_power_off: pm_runtime_put(imx334->dev); + return ret; } @@ -1040,7 +1036,7 @@ done_endpoint_free: /* V4l2 subdevice ops */ static const struct v4l2_subdev_video_ops imx334_video_ops = { - .s_stream = imx334_set_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_pad_ops imx334_pad_ops = { @@ -1048,6 +1044,8 @@ static const struct v4l2_subdev_pad_ops imx334_pad_ops = { .enum_frame_size = imx334_enum_frame_size, .get_fmt = imx334_get_pad_format, .set_fmt = imx334_set_pad_format, + .enable_streams = imx334_enable_streams, + .disable_streams = imx334_disable_streams, }; static const struct v4l2_subdev_ops imx334_subdev_ops = { -- cgit v1.2.3-59-g8ed1b From 29d69273fefd0bc2a66b5e22f3a4a4c7a53bfa02 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Mon, 3 Mar 2025 11:30:22 +0100 Subject: media: remove STA2x11 media pci driver With commit dcbb01fbb7ae ("x86/pci: Remove old STA2x11 support"), the STA2X11 Video Input Port driver is not needed and cannot be built anymore. Remove the driver and its reference in media documentation. Signed-off-by: Lukas Bulwahn Signed-off-by: Hans Verkuil --- Documentation/admin-guide/media/pci-cardlist.rst | 1 - drivers/media/pci/Kconfig | 1 - drivers/media/pci/Makefile | 2 - drivers/media/pci/sta2x11/Kconfig | 16 - drivers/media/pci/sta2x11/Makefile | 2 - drivers/media/pci/sta2x11/sta2x11_vip.c | 1270 ---------------------- drivers/media/pci/sta2x11/sta2x11_vip.h | 29 - 7 files changed, 1321 deletions(-) delete mode 100644 drivers/media/pci/sta2x11/Kconfig delete mode 100644 drivers/media/pci/sta2x11/Makefile delete mode 100644 drivers/media/pci/sta2x11/sta2x11_vip.c delete mode 100644 drivers/media/pci/sta2x11/sta2x11_vip.h diff --git a/Documentation/admin-guide/media/pci-cardlist.rst b/Documentation/admin-guide/media/pci-cardlist.rst index 7d8e3c8987db..239879634ea5 100644 --- a/Documentation/admin-guide/media/pci-cardlist.rst +++ b/Documentation/admin-guide/media/pci-cardlist.rst @@ -86,7 +86,6 @@ saa7134 Philips SAA7134 saa7164 NXP SAA7164 smipcie SMI PCIe DVBSky cards solo6x10 Bluecherry / Softlogic 6x10 capture cards (MPEG-4/H.264) -sta2x11_vip STA2X11 VIP Video For Linux tw5864 Techwell TW5864 video/audio grabber and encoder tw686x Intersil/Techwell TW686x tw68 Techwell tw68x Video For Linux diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 7f65aa609388..eebb16c58f3d 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -15,7 +15,6 @@ if MEDIA_CAMERA_SUPPORT source "drivers/media/pci/mgb4/Kconfig" source "drivers/media/pci/solo6x10/Kconfig" -source "drivers/media/pci/sta2x11/Kconfig" source "drivers/media/pci/tw5864/Kconfig" source "drivers/media/pci/tw68/Kconfig" source "drivers/media/pci/tw686x/Kconfig" diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index f18c7e15abe3..02763ad88511 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -22,8 +22,6 @@ obj-y += ttpci/ \ # Please keep it alphabetically sorted by Kconfig name # (e. g. LC_ALL=C sort Makefile) -obj-$(CONFIG_STA2X11_VIP) += sta2x11/ - obj-$(CONFIG_VIDEO_BT848) += bt8xx/ obj-$(CONFIG_VIDEO_COBALT) += cobalt/ obj-$(CONFIG_VIDEO_CX18) += cx18/ diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig deleted file mode 100644 index 118b922c08c3..000000000000 --- a/drivers/media/pci/sta2x11/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config STA2X11_VIP - tristate "STA2X11 VIP Video For Linux" - depends on PCI && VIDEO_DEV && I2C - depends on STA2X11 || COMPILE_TEST - select GPIOLIB if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT - select VIDEOBUF2_DMA_CONTIG - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - Say Y for support for STA2X11 VIP (Video Input Port) capture - device. - - To compile this driver as a module, choose M here: the - module will be called sta2x11_vip. diff --git a/drivers/media/pci/sta2x11/Makefile b/drivers/media/pci/sta2x11/Makefile deleted file mode 100644 index bb684a7b6270..000000000000 --- a/drivers/media/pci/sta2x11/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_STA2X11_VIP) += sta2x11_vip.o diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c deleted file mode 100644 index 3049bad20f14..000000000000 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ /dev/null @@ -1,1270 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * This is the driver for the STA2x11 Video Input Port. - * - * Copyright (C) 2012 ST Microelectronics - * author: Federico Vaga - * Copyright (C) 2010 WindRiver Systems, Inc. - * authors: Andreas Kies - * Vlad Lungu - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sta2x11_vip.h" - -#define DRV_VERSION "1.3" - -#ifndef PCI_DEVICE_ID_STMICRO_VIP -#define PCI_DEVICE_ID_STMICRO_VIP 0xCC0D -#endif - -#define MAX_FRAMES 4 - -/*Register offsets*/ -#define DVP_CTL 0x00 -#define DVP_TFO 0x04 -#define DVP_TFS 0x08 -#define DVP_BFO 0x0C -#define DVP_BFS 0x10 -#define DVP_VTP 0x14 -#define DVP_VBP 0x18 -#define DVP_VMP 0x1C -#define DVP_ITM 0x98 -#define DVP_ITS 0x9C -#define DVP_STA 0xA0 -#define DVP_HLFLN 0xA8 -#define DVP_RGB 0xC0 -#define DVP_PKZ 0xF0 - -/*Register fields*/ -#define DVP_CTL_ENA 0x00000001 -#define DVP_CTL_RST 0x80000000 -#define DVP_CTL_DIS (~0x00040001) - -#define DVP_IT_VSB 0x00000008 -#define DVP_IT_VST 0x00000010 -#define DVP_IT_FIFO 0x00000020 - -#define DVP_HLFLN_SD 0x00000001 - -#define SAVE_COUNT 8 -#define AUX_COUNT 3 -#define IRQ_COUNT 1 - - -struct vip_buffer { - struct vb2_v4l2_buffer vb; - struct list_head list; - dma_addr_t dma; -}; -static inline struct vip_buffer *to_vip_buffer(struct vb2_v4l2_buffer *vb2) -{ - return container_of(vb2, struct vip_buffer, vb); -} - -/** - * struct sta2x11_vip - All internal data for one instance of device - * @v4l2_dev: device registered in v4l layer - * @video_dev: properties of our device - * @pdev: PCI device - * @adapter: contains I2C adapter information - * @register_save_area: All relevant register are saved here during suspend - * @decoder: contains information about video DAC - * @ctrl_hdl: handler for control framework - * @format: pixel format, fixed UYVY - * @std: video standard (e.g. PAL/NTSC) - * @input: input line for video signal ( 0 or 1 ) - * @disabled: Device is in power down state - * @slock: for excluse access of registers - * @vb_vidq: queue maintained by videobuf2 layer - * @buffer_list: list of buffer in use - * @sequence: sequence number of acquired buffer - * @active: current active buffer - * @lock: used in videobuf2 callback - * @v4l_lock: serialize its video4linux ioctls - * @tcount: Number of top frames - * @bcount: Number of bottom frames - * @overflow: Number of FIFO overflows - * @iomem: hardware base address - * @config: I2C and gpio config from platform - * - * All non-local data is accessed via this structure. - */ -struct sta2x11_vip { - struct v4l2_device v4l2_dev; - struct video_device video_dev; - struct pci_dev *pdev; - struct i2c_adapter *adapter; - unsigned int register_save_area[IRQ_COUNT + SAVE_COUNT + AUX_COUNT]; - struct v4l2_subdev *decoder; - struct v4l2_ctrl_handler ctrl_hdl; - - - struct v4l2_pix_format format; - v4l2_std_id std; - unsigned int input; - int disabled; - spinlock_t slock; - - struct vb2_queue vb_vidq; - struct list_head buffer_list; - unsigned int sequence; - struct vip_buffer *active; /* current active buffer */ - spinlock_t lock; /* Used in videobuf2 callback */ - struct mutex v4l_lock; - - /* Interrupt counters */ - int tcount, bcount; - int overflow; - - void __iomem *iomem; /* I/O Memory */ - struct vip_config *config; -}; - -static const unsigned int registers_to_save[AUX_COUNT] = { - DVP_HLFLN, DVP_RGB, DVP_PKZ -}; - -static struct v4l2_pix_format formats_50[] = { - { /*PAL interlaced */ - .width = 720, - .height = 576, - .pixelformat = V4L2_PIX_FMT_UYVY, - .field = V4L2_FIELD_INTERLACED, - .bytesperline = 720 * 2, - .sizeimage = 720 * 2 * 576, - .colorspace = V4L2_COLORSPACE_SMPTE170M}, - { /*PAL top */ - .width = 720, - .height = 288, - .pixelformat = V4L2_PIX_FMT_UYVY, - .field = V4L2_FIELD_TOP, - .bytesperline = 720 * 2, - .sizeimage = 720 * 2 * 288, - .colorspace = V4L2_COLORSPACE_SMPTE170M}, - { /*PAL bottom */ - .width = 720, - .height = 288, - .pixelformat = V4L2_PIX_FMT_UYVY, - .field = V4L2_FIELD_BOTTOM, - .bytesperline = 720 * 2, - .sizeimage = 720 * 2 * 288, - .colorspace = V4L2_COLORSPACE_SMPTE170M}, - -}; - -static struct v4l2_pix_format formats_60[] = { - { /*NTSC interlaced */ - .width = 720, - .height = 480, - .pixelformat = V4L2_PIX_FMT_UYVY, - .field = V4L2_FIELD_INTERLACED, - .bytesperline = 720 * 2, - .sizeimage = 720 * 2 * 480, - .colorspace = V4L2_COLORSPACE_SMPTE170M}, - { /*NTSC top */ - .width = 720, - .height = 240, - .pixelformat = V4L2_PIX_FMT_UYVY, - .field = V4L2_FIELD_TOP, - .bytesperline = 720 * 2, - .sizeimage = 720 * 2 * 240, - .colorspace = V4L2_COLORSPACE_SMPTE170M}, - { /*NTSC bottom */ - .width = 720, - .height = 240, - .pixelformat = V4L2_PIX_FMT_UYVY, - .field = V4L2_FIELD_BOTTOM, - .bytesperline = 720 * 2, - .sizeimage = 720 * 2 * 240, - .colorspace = V4L2_COLORSPACE_SMPTE170M}, -}; - -/* Write VIP register */ -static inline void reg_write(struct sta2x11_vip *vip, unsigned int reg, u32 val) -{ - iowrite32((val), (vip->iomem)+(reg)); -} -/* Read VIP register */ -static inline u32 reg_read(struct sta2x11_vip *vip, unsigned int reg) -{ - return ioread32((vip->iomem)+(reg)); -} -/* Start DMA acquisition */ -static void start_dma(struct sta2x11_vip *vip, struct vip_buffer *vip_buf) -{ - unsigned long offset = 0; - - if (vip->format.field == V4L2_FIELD_INTERLACED) - offset = vip->format.width * 2; - - spin_lock_irq(&vip->slock); - /* Enable acquisition */ - reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) | DVP_CTL_ENA); - /* Set Top and Bottom Field memory address */ - reg_write(vip, DVP_VTP, (u32)vip_buf->dma); - reg_write(vip, DVP_VBP, (u32)vip_buf->dma + offset); - spin_unlock_irq(&vip->slock); -} - -/* Fetch the next buffer to activate */ -static void vip_active_buf_next(struct sta2x11_vip *vip) -{ - /* Get the next buffer */ - spin_lock(&vip->lock); - if (list_empty(&vip->buffer_list)) {/* No available buffer */ - spin_unlock(&vip->lock); - return; - } - vip->active = list_first_entry(&vip->buffer_list, - struct vip_buffer, - list); - /* Reset Top and Bottom counter */ - vip->tcount = 0; - vip->bcount = 0; - spin_unlock(&vip->lock); - if (vb2_is_streaming(&vip->vb_vidq)) { /* streaming is on */ - start_dma(vip, vip->active); /* start dma capture */ - } -} - - -/* Videobuf2 Operations */ -static int queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct sta2x11_vip *vip = vb2_get_drv_priv(vq); - - if (!(*nbuffers) || *nbuffers < MAX_FRAMES) - *nbuffers = MAX_FRAMES; - - *nplanes = 1; - sizes[0] = vip->format.sizeimage; - - vip->sequence = 0; - vip->active = NULL; - vip->tcount = 0; - vip->bcount = 0; - - return 0; -}; -static int buffer_init(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct vip_buffer *vip_buf = to_vip_buffer(vbuf); - - vip_buf->dma = vb2_dma_contig_plane_dma_addr(vb, 0); - INIT_LIST_HEAD(&vip_buf->list); - return 0; -} - -static int buffer_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); - struct vip_buffer *vip_buf = to_vip_buffer(vbuf); - unsigned long size; - - size = vip->format.sizeimage; - if (vb2_plane_size(vb, 0) < size) { - v4l2_err(&vip->v4l2_dev, "buffer too small (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -EINVAL; - } - - vb2_set_plane_payload(&vip_buf->vb.vb2_buf, 0, size); - - return 0; -} -static void buffer_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); - struct vip_buffer *vip_buf = to_vip_buffer(vbuf); - - spin_lock(&vip->lock); - list_add_tail(&vip_buf->list, &vip->buffer_list); - if (!vip->active) { /* No active buffer, active the first one */ - vip->active = list_first_entry(&vip->buffer_list, - struct vip_buffer, - list); - if (vb2_is_streaming(&vip->vb_vidq)) /* streaming is on */ - start_dma(vip, vip_buf); /* start dma capture */ - } - spin_unlock(&vip->lock); -} -static void buffer_finish(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); - struct vip_buffer *vip_buf = to_vip_buffer(vbuf); - - /* Buffer handled, remove it from the list */ - spin_lock(&vip->lock); - list_del_init(&vip_buf->list); - spin_unlock(&vip->lock); - - if (vb2_is_streaming(vb->vb2_queue)) - vip_active_buf_next(vip); -} - -static int start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct sta2x11_vip *vip = vb2_get_drv_priv(vq); - - spin_lock_irq(&vip->slock); - /* Enable interrupt VSYNC Top and Bottom*/ - reg_write(vip, DVP_ITM, DVP_IT_VSB | DVP_IT_VST); - spin_unlock_irq(&vip->slock); - - if (count) - start_dma(vip, vip->active); - - return 0; -} - -/* abort streaming and wait for last buffer */ -static void stop_streaming(struct vb2_queue *vq) -{ - struct sta2x11_vip *vip = vb2_get_drv_priv(vq); - struct vip_buffer *vip_buf, *node; - - /* Disable acquisition */ - reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) & ~DVP_CTL_ENA); - /* Disable all interrupts */ - reg_write(vip, DVP_ITM, 0); - - /* Release all active buffers */ - spin_lock(&vip->lock); - list_for_each_entry_safe(vip_buf, node, &vip->buffer_list, list) { - vb2_buffer_done(&vip_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - list_del(&vip_buf->list); - } - spin_unlock(&vip->lock); -} - -static const struct vb2_ops vip_video_qops = { - .queue_setup = queue_setup, - .buf_init = buffer_init, - .buf_prepare = buffer_prepare, - .buf_finish = buffer_finish, - .buf_queue = buffer_queue, - .start_streaming = start_streaming, - .stop_streaming = stop_streaming, -}; - - -/* File Operations */ -static const struct v4l2_file_operations vip_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .unlocked_ioctl = video_ioctl2, - .read = vb2_fop_read, - .mmap = vb2_fop_mmap, - .poll = vb2_fop_poll -}; - - -/** - * vidioc_querycap - return capabilities of device - * @file: descriptor of device - * @cap: contains return values - * @priv: unused - * - * the capabilities of the device are returned - * - * return value: 0, no error. - */ -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); - strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card)); - return 0; -} - -/** - * vidioc_s_std - set video standard - * @file: descriptor of device - * @std: contains standard to be set - * @priv: unused - * - * the video standard is set - * - * return value: 0, no error. - * - * -EIO, no input signal detected - * - * other, returned from video DAC. - */ -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std) -{ - struct sta2x11_vip *vip = video_drvdata(file); - - /* - * This is here for backwards compatibility only. - * The use of V4L2_STD_ALL to trigger a querystd is non-standard. - */ - if (std == V4L2_STD_ALL) { - v4l2_subdev_call(vip->decoder, video, querystd, &std); - if (std == V4L2_STD_UNKNOWN) - return -EIO; - } - - if (vip->std != std) { - vip->std = std; - if (V4L2_STD_525_60 & std) - vip->format = formats_60[0]; - else - vip->format = formats_50[0]; - } - - return v4l2_subdev_call(vip->decoder, video, s_std, std); -} - -/** - * vidioc_g_std - get video standard - * @file: descriptor of device - * @priv: unused - * @std: contains return values - * - * the current video standard is returned - * - * return value: 0, no error. - */ -static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) -{ - struct sta2x11_vip *vip = video_drvdata(file); - - *std = vip->std; - return 0; -} - -/** - * vidioc_querystd - get possible video standards - * @file: descriptor of device - * @priv: unused - * @std: contains return values - * - * all possible video standards are returned - * - * return value: delivered by video DAC routine. - */ -static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std) -{ - struct sta2x11_vip *vip = video_drvdata(file); - - return v4l2_subdev_call(vip->decoder, video, querystd, std); -} - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - if (inp->index > 1) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = V4L2_STD_ALL; - sprintf(inp->name, "Camera %u", inp->index); - - return 0; -} - -/** - * vidioc_s_input - set input line - * @file: descriptor of device - * @priv: unused - * @i: new input line number - * - * the current active input line is set - * - * return value: 0, no error. - * - * -EINVAL, line number out of range - */ -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - struct sta2x11_vip *vip = video_drvdata(file); - int ret; - - if (i > 1) - return -EINVAL; - ret = v4l2_subdev_call(vip->decoder, video, s_routing, i, 0, 0); - - if (!ret) - vip->input = i; - - return 0; -} - -/** - * vidioc_g_input - return input line - * @file: descriptor of device - * @priv: unused - * @i: returned input line number - * - * the current active input line is returned - * - * return value: always 0. - */ -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct sta2x11_vip *vip = video_drvdata(file); - - *i = vip->input; - return 0; -} - -/** - * vidioc_enum_fmt_vid_cap - return video capture format - * @file: descriptor of device - * @priv: unused - * @f: returned format information - * - * returns name and format of video capture - * Only UYVY is supported by hardware. - * - * return value: always 0. - */ -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - - if (f->index != 0) - return -EINVAL; - - f->pixelformat = V4L2_PIX_FMT_UYVY; - return 0; -} - -/** - * vidioc_try_fmt_vid_cap - set video capture format - * @file: descriptor of device - * @priv: unused - * @f: new format - * - * new video format is set which includes width and - * field type. width is fixed to 720, no scaling. - * Only UYVY is supported by this hardware. - * the minimum height is 200, the maximum is 576 (PAL) - * - * return value: 0, no error - * - * -EINVAL, pixel or field format not supported - * - */ -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct sta2x11_vip *vip = video_drvdata(file); - int interlace_lim; - - if (V4L2_PIX_FMT_UYVY != f->fmt.pix.pixelformat) { - v4l2_warn(&vip->v4l2_dev, "Invalid format, only UYVY supported\n"); - return -EINVAL; - } - - if (V4L2_STD_525_60 & vip->std) - interlace_lim = 240; - else - interlace_lim = 288; - - switch (f->fmt.pix.field) { - default: - case V4L2_FIELD_ANY: - if (interlace_lim < f->fmt.pix.height) - f->fmt.pix.field = V4L2_FIELD_INTERLACED; - else - f->fmt.pix.field = V4L2_FIELD_BOTTOM; - break; - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - if (interlace_lim < f->fmt.pix.height) - f->fmt.pix.height = interlace_lim; - break; - case V4L2_FIELD_INTERLACED: - break; - } - - /* It is the only supported format */ - f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; - f->fmt.pix.height &= ~1; - if (2 * interlace_lim < f->fmt.pix.height) - f->fmt.pix.height = 2 * interlace_lim; - if (200 > f->fmt.pix.height) - f->fmt.pix.height = 200; - f->fmt.pix.width = 720; - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.width * 2 * f->fmt.pix.height; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - return 0; -} - -/** - * vidioc_s_fmt_vid_cap - set current video format parameters - * @file: descriptor of device - * @priv: unused - * @f: returned format information - * - * set new capture format - * return value: 0, no error - * - * other, delivered by video DAC routine. - */ -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct sta2x11_vip *vip = video_drvdata(file); - unsigned int t_stop, b_stop, pitch; - int ret; - - ret = vidioc_try_fmt_vid_cap(file, priv, f); - if (ret) - return ret; - - if (vb2_is_busy(&vip->vb_vidq)) { - /* Can't change format during acquisition */ - v4l2_err(&vip->v4l2_dev, "device busy\n"); - return -EBUSY; - } - vip->format = f->fmt.pix; - switch (vip->format.field) { - case V4L2_FIELD_INTERLACED: - t_stop = ((vip->format.height / 2 - 1) << 16) | - (2 * vip->format.width - 1); - b_stop = t_stop; - pitch = 4 * vip->format.width; - break; - case V4L2_FIELD_TOP: - t_stop = ((vip->format.height - 1) << 16) | - (2 * vip->format.width - 1); - b_stop = (0 << 16) | (2 * vip->format.width - 1); - pitch = 2 * vip->format.width; - break; - case V4L2_FIELD_BOTTOM: - t_stop = (0 << 16) | (2 * vip->format.width - 1); - b_stop = (vip->format.height << 16) | - (2 * vip->format.width - 1); - pitch = 2 * vip->format.width; - break; - default: - v4l2_err(&vip->v4l2_dev, "unknown field format\n"); - return -EINVAL; - } - - spin_lock_irq(&vip->slock); - /* Y-X Top Field Offset */ - reg_write(vip, DVP_TFO, 0); - /* Y-X Bottom Field Offset */ - reg_write(vip, DVP_BFO, 0); - /* Y-X Top Field Stop*/ - reg_write(vip, DVP_TFS, t_stop); - /* Y-X Bottom Field Stop */ - reg_write(vip, DVP_BFS, b_stop); - /* Video Memory Pitch */ - reg_write(vip, DVP_VMP, pitch); - spin_unlock_irq(&vip->slock); - - return 0; -} - -/** - * vidioc_g_fmt_vid_cap - get current video format parameters - * @file: descriptor of device - * @priv: unused - * @f: contains format information - * - * returns current video format parameters - * - * return value: 0, always successful - */ -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct sta2x11_vip *vip = video_drvdata(file); - - f->fmt.pix = vip->format; - - return 0; -} - -static const struct v4l2_ioctl_ops vip_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - /* FMT handling */ - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - /* Buffer handlers */ - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - /* Stream on/off */ - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - /* Standard handling */ - .vidioc_g_std = vidioc_g_std, - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, - /* Input handling */ - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - /* Log status ioctl */ - .vidioc_log_status = v4l2_ctrl_log_status, - /* Event handling */ - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct video_device video_dev_template = { - .name = KBUILD_MODNAME, - .release = video_device_release_empty, - .fops = &vip_fops, - .ioctl_ops = &vip_ioctl_ops, - .tvnorms = V4L2_STD_ALL, - .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING, -}; - -/** - * vip_irq - interrupt routine - * @irq: Number of interrupt ( not used, correct number is assumed ) - * @data: local data structure containing all information - * - * check for both frame interrupts set ( top and bottom ). - * check FIFO overflow, but limit number of log messages after open. - * signal a complete buffer if done - * - * return value: IRQ_NONE, interrupt was not generated by VIP - * - * IRQ_HANDLED, interrupt done. - */ -static irqreturn_t vip_irq(int irq, void *data) -{ - struct sta2x11_vip *vip = data; - unsigned int status; - - status = reg_read(vip, DVP_ITS); - - if (!status) /* No interrupt to handle */ - return IRQ_NONE; - - if (status & DVP_IT_FIFO) - if (vip->overflow++ > 5) - pr_info("VIP: fifo overflow\n"); - - if ((status & DVP_IT_VST) && (status & DVP_IT_VSB)) { - /* this is bad, we are too slow, hope the condition is gone - * on the next frame */ - return IRQ_HANDLED; - } - - if (status & DVP_IT_VST) - if ((++vip->tcount) < 2) - return IRQ_HANDLED; - if (status & DVP_IT_VSB) { - vip->bcount++; - return IRQ_HANDLED; - } - - if (vip->active) { /* Acquisition is over on this buffer */ - /* Disable acquisition */ - reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) & ~DVP_CTL_ENA); - /* Remove the active buffer from the list */ - vip->active->vb.vb2_buf.timestamp = ktime_get_ns(); - vip->active->vb.sequence = vip->sequence++; - vb2_buffer_done(&vip->active->vb.vb2_buf, VB2_BUF_STATE_DONE); - } - - return IRQ_HANDLED; -} - -static void sta2x11_vip_init_register(struct sta2x11_vip *vip) -{ - /* Register initialization */ - spin_lock_irq(&vip->slock); - /* Clean interrupt */ - reg_read(vip, DVP_ITS); - /* Enable Half Line per vertical */ - reg_write(vip, DVP_HLFLN, DVP_HLFLN_SD); - /* Reset VIP control */ - reg_write(vip, DVP_CTL, DVP_CTL_RST); - /* Clear VIP control */ - reg_write(vip, DVP_CTL, 0); - spin_unlock_irq(&vip->slock); -} -static void sta2x11_vip_clear_register(struct sta2x11_vip *vip) -{ - spin_lock_irq(&vip->slock); - /* Disable interrupt */ - reg_write(vip, DVP_ITM, 0); - /* Reset VIP Control */ - reg_write(vip, DVP_CTL, DVP_CTL_RST); - /* Clear VIP Control */ - reg_write(vip, DVP_CTL, 0); - /* Clean VIP Interrupt */ - reg_read(vip, DVP_ITS); - spin_unlock_irq(&vip->slock); -} -static int sta2x11_vip_init_buffer(struct sta2x11_vip *vip) -{ - int err; - - err = dma_set_coherent_mask(&vip->pdev->dev, DMA_BIT_MASK(29)); - if (err) { - v4l2_err(&vip->v4l2_dev, "Cannot configure coherent mask"); - return err; - } - memset(&vip->vb_vidq, 0, sizeof(struct vb2_queue)); - vip->vb_vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - vip->vb_vidq.io_modes = VB2_MMAP | VB2_READ; - vip->vb_vidq.drv_priv = vip; - vip->vb_vidq.buf_struct_size = sizeof(struct vip_buffer); - vip->vb_vidq.ops = &vip_video_qops; - vip->vb_vidq.mem_ops = &vb2_dma_contig_memops; - vip->vb_vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vip->vb_vidq.dev = &vip->pdev->dev; - vip->vb_vidq.lock = &vip->v4l_lock; - err = vb2_queue_init(&vip->vb_vidq); - if (err) - return err; - INIT_LIST_HEAD(&vip->buffer_list); - spin_lock_init(&vip->lock); - return 0; -} - -static int sta2x11_vip_init_controls(struct sta2x11_vip *vip) -{ - /* - * Inititialize an empty control so VIP can inerithing controls - * from ADV7180 - */ - v4l2_ctrl_handler_init(&vip->ctrl_hdl, 0); - - vip->v4l2_dev.ctrl_handler = &vip->ctrl_hdl; - if (vip->ctrl_hdl.error) { - int err = vip->ctrl_hdl.error; - - v4l2_ctrl_handler_free(&vip->ctrl_hdl); - return err; - } - - return 0; -} - -/** - * vip_gpio_reserve - reserve gpio pin - * @dev: device - * @pin: GPIO pin number - * @dir: direction, input or output - * @name: GPIO pin name - * - */ -static int vip_gpio_reserve(struct device *dev, int pin, int dir, - const char *name) -{ - struct gpio_desc *desc = gpio_to_desc(pin); - int ret = -ENODEV; - - if (!gpio_is_valid(pin)) - return ret; - - ret = gpio_request(pin, name); - if (ret) { - dev_err(dev, "Failed to allocate pin %d (%s)\n", pin, name); - return ret; - } - - ret = gpiod_direction_output(desc, dir); - if (ret) { - dev_err(dev, "Failed to set direction for pin %d (%s)\n", - pin, name); - gpio_free(pin); - return ret; - } - - ret = gpiod_export(desc, false); - if (ret) { - dev_err(dev, "Failed to export pin %d (%s)\n", pin, name); - gpio_free(pin); - return ret; - } - - return 0; -} - -/** - * vip_gpio_release - release gpio pin - * @dev: device - * @pin: GPIO pin number - * @name: GPIO pin name - * - */ -static void vip_gpio_release(struct device *dev, int pin, const char *name) -{ - if (gpio_is_valid(pin)) { - struct gpio_desc *desc = gpio_to_desc(pin); - - dev_dbg(dev, "releasing pin %d (%s)\n", pin, name); - gpiod_unexport(desc); - gpio_free(pin); - } -} - -/** - * sta2x11_vip_init_one - init one instance of video device - * @pdev: PCI device - * @ent: (not used) - * - * allocate reset pins for DAC. - * Reset video DAC, this is done via reset line. - * allocate memory for managing device - * request interrupt - * map IO region - * register device - * find and initialize video DAC - * - * return value: 0, no error - * - * -ENOMEM, no memory - * - * -ENODEV, device could not be detected or registered - */ -static int sta2x11_vip_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int ret; - struct sta2x11_vip *vip; - struct vip_config *config; - - /* Check if hardware support 26-bit DMA */ - if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(26))) { - dev_err(&pdev->dev, "26-bit DMA addressing not available\n"); - return -EINVAL; - } - /* Enable PCI */ - ret = pci_enable_device(pdev); - if (ret) - return ret; - - /* Get VIP platform data */ - config = dev_get_platdata(&pdev->dev); - if (!config) { - dev_info(&pdev->dev, "VIP slot disabled\n"); - ret = -EINVAL; - goto disable; - } - - /* Power configuration */ - ret = vip_gpio_reserve(&pdev->dev, config->pwr_pin, 0, - config->pwr_name); - if (ret) - goto disable; - - ret = vip_gpio_reserve(&pdev->dev, config->reset_pin, 0, - config->reset_name); - if (ret) { - vip_gpio_release(&pdev->dev, config->pwr_pin, - config->pwr_name); - goto disable; - } - - if (gpio_is_valid(config->pwr_pin)) { - /* Datasheet says 5ms between PWR and RST */ - usleep_range(5000, 25000); - gpio_direction_output(config->pwr_pin, 1); - } - - if (gpio_is_valid(config->reset_pin)) { - /* Datasheet says 5ms between PWR and RST */ - usleep_range(5000, 25000); - gpio_direction_output(config->reset_pin, 1); - } - usleep_range(5000, 25000); - - /* Allocate a new VIP instance */ - vip = kzalloc(sizeof(struct sta2x11_vip), GFP_KERNEL); - if (!vip) { - ret = -ENOMEM; - goto release_gpios; - } - vip->pdev = pdev; - vip->std = V4L2_STD_PAL; - vip->format = formats_50[0]; - vip->config = config; - mutex_init(&vip->v4l_lock); - - ret = sta2x11_vip_init_controls(vip); - if (ret) - goto free_mem; - ret = v4l2_device_register(&pdev->dev, &vip->v4l2_dev); - if (ret) - goto free_mem; - - dev_dbg(&pdev->dev, "BAR #0 at 0x%lx 0x%lx irq %d\n", - (unsigned long)pci_resource_start(pdev, 0), - (unsigned long)pci_resource_len(pdev, 0), pdev->irq); - - pci_set_master(pdev); - - ret = pci_request_regions(pdev, KBUILD_MODNAME); - if (ret) - goto unreg; - - vip->iomem = pci_iomap(pdev, 0, 0x100); - if (!vip->iomem) { - ret = -ENOMEM; - goto release; - } - - pci_enable_msi(pdev); - - /* Initialize buffer */ - ret = sta2x11_vip_init_buffer(vip); - if (ret) - goto unmap; - - spin_lock_init(&vip->slock); - - ret = request_irq(pdev->irq, vip_irq, IRQF_SHARED, KBUILD_MODNAME, vip); - if (ret) { - dev_err(&pdev->dev, "request_irq failed\n"); - ret = -ENODEV; - goto release_buf; - } - - /* Initialize and register video device */ - vip->video_dev = video_dev_template; - vip->video_dev.v4l2_dev = &vip->v4l2_dev; - vip->video_dev.queue = &vip->vb_vidq; - vip->video_dev.lock = &vip->v4l_lock; - video_set_drvdata(&vip->video_dev, vip); - - ret = video_register_device(&vip->video_dev, VFL_TYPE_VIDEO, -1); - if (ret) - goto vrelease; - - /* Get ADV7180 subdevice */ - vip->adapter = i2c_get_adapter(vip->config->i2c_id); - if (!vip->adapter) { - ret = -ENODEV; - dev_err(&pdev->dev, "no I2C adapter found\n"); - goto vunreg; - } - - vip->decoder = v4l2_i2c_new_subdev(&vip->v4l2_dev, vip->adapter, - "adv7180", vip->config->i2c_addr, - NULL); - if (!vip->decoder) { - ret = -ENODEV; - dev_err(&pdev->dev, "no decoder found\n"); - goto vunreg; - } - - i2c_put_adapter(vip->adapter); - v4l2_subdev_call(vip->decoder, core, init, 0); - - sta2x11_vip_init_register(vip); - - dev_info(&pdev->dev, "STA2X11 Video Input Port (VIP) loaded\n"); - return 0; - -vunreg: - video_set_drvdata(&vip->video_dev, NULL); -vrelease: - vb2_video_unregister_device(&vip->video_dev); - free_irq(pdev->irq, vip); -release_buf: - pci_disable_msi(pdev); -unmap: - pci_iounmap(pdev, vip->iomem); -release: - pci_release_regions(pdev); -unreg: - v4l2_device_unregister(&vip->v4l2_dev); -free_mem: - kfree(vip); -release_gpios: - vip_gpio_release(&pdev->dev, config->reset_pin, config->reset_name); - vip_gpio_release(&pdev->dev, config->pwr_pin, config->pwr_name); -disable: - /* - * do not call pci_disable_device on sta2x11 because it break all - * other Bus masters on this EP - */ - return ret; -} - -/** - * sta2x11_vip_remove_one - release device - * @pdev: PCI device - * - * Undo everything done in .._init_one - * - * unregister video device - * free interrupt - * unmap ioadresses - * free memory - * free GPIO pins - */ -static void sta2x11_vip_remove_one(struct pci_dev *pdev) -{ - struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); - struct sta2x11_vip *vip = - container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); - - sta2x11_vip_clear_register(vip); - - video_set_drvdata(&vip->video_dev, NULL); - vb2_video_unregister_device(&vip->video_dev); - free_irq(pdev->irq, vip); - pci_disable_msi(pdev); - pci_iounmap(pdev, vip->iomem); - pci_release_regions(pdev); - - v4l2_device_unregister(&vip->v4l2_dev); - - vip_gpio_release(&pdev->dev, vip->config->pwr_pin, - vip->config->pwr_name); - vip_gpio_release(&pdev->dev, vip->config->reset_pin, - vip->config->reset_name); - - kfree(vip); - /* - * do not call pci_disable_device on sta2x11 because it break all - * other Bus masters on this EP - */ -} - -/** - * sta2x11_vip_suspend - set device into power save mode - * @dev_d: PCI device - * - * all relevant registers are saved and an attempt to set a new state is made. - * - * return value: 0 always indicate success, - * even if device could not be disabled. (workaround for hardware problem) - */ -static int __maybe_unused sta2x11_vip_suspend(struct device *dev_d) -{ - struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); - struct sta2x11_vip *vip = - container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); - unsigned long flags; - int i; - - spin_lock_irqsave(&vip->slock, flags); - vip->register_save_area[0] = reg_read(vip, DVP_CTL); - reg_write(vip, DVP_CTL, vip->register_save_area[0] & DVP_CTL_DIS); - vip->register_save_area[SAVE_COUNT] = reg_read(vip, DVP_ITM); - reg_write(vip, DVP_ITM, 0); - for (i = 1; i < SAVE_COUNT; i++) - vip->register_save_area[i] = reg_read(vip, 4 * i); - for (i = 0; i < AUX_COUNT; i++) - vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] = - reg_read(vip, registers_to_save[i]); - spin_unlock_irqrestore(&vip->slock, flags); - - vip->disabled = 1; - - pr_info("VIP: suspend\n"); - return 0; -} - -/** - * sta2x11_vip_resume - resume device operation - * @dev_d : PCI device - * - * return value: 0, no error. - * - * other, could not set device to power on state. - */ -static int __maybe_unused sta2x11_vip_resume(struct device *dev_d) -{ - struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); - struct sta2x11_vip *vip = - container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); - unsigned long flags; - int i; - - pr_info("VIP: resume\n"); - - vip->disabled = 0; - - spin_lock_irqsave(&vip->slock, flags); - for (i = 1; i < SAVE_COUNT; i++) - reg_write(vip, 4 * i, vip->register_save_area[i]); - for (i = 0; i < AUX_COUNT; i++) - reg_write(vip, registers_to_save[i], - vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i]); - reg_write(vip, DVP_CTL, vip->register_save_area[0]); - reg_write(vip, DVP_ITM, vip->register_save_area[SAVE_COUNT]); - spin_unlock_irqrestore(&vip->slock, flags); - return 0; -} - -static const struct pci_device_id sta2x11_vip_pci_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)}, - {0,} -}; - -static SIMPLE_DEV_PM_OPS(sta2x11_vip_pm_ops, - sta2x11_vip_suspend, - sta2x11_vip_resume); - -static struct pci_driver sta2x11_vip_driver = { - .name = KBUILD_MODNAME, - .probe = sta2x11_vip_init_one, - .remove = sta2x11_vip_remove_one, - .id_table = sta2x11_vip_pci_tbl, - .driver.pm = &sta2x11_vip_pm_ops, -}; - -static int __init sta2x11_vip_init_module(void) -{ - return pci_register_driver(&sta2x11_vip_driver); -} - -static void __exit sta2x11_vip_exit_module(void) -{ - pci_unregister_driver(&sta2x11_vip_driver); -} - -#ifdef MODULE -module_init(sta2x11_vip_init_module); -module_exit(sta2x11_vip_exit_module); -#else -late_initcall_sync(sta2x11_vip_init_module); -#endif - -MODULE_DESCRIPTION("STA2X11 Video Input Port driver"); -MODULE_AUTHOR("Wind River"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(DRV_VERSION); -MODULE_DEVICE_TABLE(pci, sta2x11_vip_pci_tbl); diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.h b/drivers/media/pci/sta2x11/sta2x11_vip.h deleted file mode 100644 index de6000e7943e..000000000000 --- a/drivers/media/pci/sta2x11/sta2x11_vip.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2011 Wind River Systems, Inc. - * - * Author: Anders Wallin - */ - -#ifndef __STA2X11_VIP_H -#define __STA2X11_VIP_H - -/** - * struct vip_config - video input configuration data - * @pwr_name: ADV powerdown name - * @pwr_pin: ADV powerdown pin - * @reset_name: ADV reset name - * @reset_pin: ADV reset pin - * @i2c_id: ADV i2c adapter ID - * @i2c_addr: ADV i2c address - */ -struct vip_config { - const char *pwr_name; - int pwr_pin; - const char *reset_name; - int reset_pin; - int i2c_id; - int i2c_addr; -}; - -#endif /* __STA2X11_VIP_H */ -- cgit v1.2.3-59-g8ed1b From df8375bbe2d5bb6d7f86701b15426674f2529a18 Mon Sep 17 00:00:00 2001 From: Matthew Majewski Date: Tue, 4 Mar 2025 14:16:59 -0500 Subject: media: v4l2-common: Add RGBR format info Add missing RGBR entry in the v4l2_format_info[] table. RGBR has identical format information to RGBP, as it is a big endian variant of RGB-5-6-5 pixel encoding according to the description in videodev2.h. Signed-off-by: Matthew Majewski 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 aa86b8c6aa75..4ee4aa19efe6 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -250,6 +250,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .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 }, -- cgit v1.2.3-59-g8ed1b From bbd0df9bfe1c085bf9b0a315dd5fb58e491dfa5b Mon Sep 17 00:00:00 2001 From: Matthew Majewski Date: Tue, 4 Mar 2025 14:17:00 -0500 Subject: media: vim2m: Simplify try_fmt Clean up vidioc_try_fmt with the following changes: 1. remove unsused vim2m_fmt parameter 2. use clamp() macro to restrain width/height bounds 3. use ALIGN() macro to align width/height 4. use v4l2_fill_pixfmt to set bytesperline/sizeimage Signed-off-by: Matthew Majewski Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vim2m.c | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/drivers/media/test-drivers/vim2m.c b/drivers/media/test-drivers/vim2m.c index 0fe97e208c02..ba3419820ff1 100644 --- a/drivers/media/test-drivers/vim2m.c +++ b/drivers/media/test-drivers/vim2m.c @@ -26,6 +26,7 @@ #include #include #include +#include MODULE_DESCRIPTION("Virtual device for mem2mem framework testing"); MODULE_AUTHOR("Pawel Osciak, "); @@ -755,31 +756,21 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, return vidioc_g_fmt(file2ctx(file), f); } -static int vidioc_try_fmt(struct v4l2_format *f, struct vim2m_fmt *fmt) +static int vidioc_try_fmt(struct v4l2_format *f) { - int walign, halign; - /* - * V4L2 specification specifies the driver corrects the - * format struct if any of the dimensions is unsupported - */ - if (f->fmt.pix.height < MIN_H) - f->fmt.pix.height = MIN_H; - else if (f->fmt.pix.height > MAX_H) - f->fmt.pix.height = MAX_H; + int width, height, walign, halign; - if (f->fmt.pix.width < MIN_W) - f->fmt.pix.width = MIN_W; - else if (f->fmt.pix.width > MAX_W) - f->fmt.pix.width = MAX_W; + width = clamp(f->fmt.pix.width, MIN_W, MAX_W); + height = clamp(f->fmt.pix.width, MIN_H, MAX_H); get_alignment(f->fmt.pix.pixelformat, &walign, &halign); - f->fmt.pix.width &= ~(walign - 1); - f->fmt.pix.height &= ~(halign - 1); - f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + width = ALIGN(width, walign); + height = ALIGN(height, halign); + f->fmt.pix.field = V4L2_FIELD_NONE; - return 0; + return v4l2_fill_pixfmt(&f->fmt.pix, f->fmt.pix.pixelformat, + width, height); } static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, @@ -804,7 +795,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; f->fmt.pix.quantization = ctx->quant; - return vidioc_try_fmt(f, fmt); + return vidioc_try_fmt(f); } static int vidioc_try_fmt_vid_out(struct file *file, void *priv, @@ -827,7 +818,7 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv, if (!f->fmt.pix.colorspace) f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; - return vidioc_try_fmt(f, fmt); + return vidioc_try_fmt(f); } static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) -- cgit v1.2.3-59-g8ed1b From c09acbbfff2037fe594396388e0c28eeeca79012 Mon Sep 17 00:00:00 2001 From: Matthew Majewski Date: Tue, 4 Mar 2025 14:17:01 -0500 Subject: media: vim2m: Add parametized support for multiplanar API Add support for the mulitiplaner API. The device can now act as either a multi-planar or a single-planar device depending on a module parameter, similar to the way vivid behaves. Multiplanar support was added by implementing the appropate try/get/set mplane functions, and by modifying the queue_setup() and buf_prepare() functions to handle multiple planes. Implementation was inspired by vivid. Signed-off-by: Matthew Majewski Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vim2m.c | 306 ++++++++++++++++++++++++++++++++----- 1 file changed, 267 insertions(+), 39 deletions(-) diff --git a/drivers/media/test-drivers/vim2m.c b/drivers/media/test-drivers/vim2m.c index ba3419820ff1..1d1a9e768505 100644 --- a/drivers/media/test-drivers/vim2m.c +++ b/drivers/media/test-drivers/vim2m.c @@ -43,6 +43,10 @@ static unsigned int default_transtime = 40; /* Max 25 fps */ module_param(default_transtime, uint, 0644); MODULE_PARM_DESC(default_transtime, "default transaction time in ms"); +static unsigned int multiplanar = 1; +module_param(multiplanar, uint, 0644); +MODULE_PARM_DESC(multiplanar, "1 (default) creates a single planar device, 2 creates multiplanar device."); + #define MIN_W 32 #define MIN_H 32 #define MAX_W 640 @@ -135,7 +139,8 @@ static struct vim2m_fmt formats[] = { struct vim2m_q_data { unsigned int width; unsigned int height; - unsigned int sizeimage; + unsigned int num_mem_planes; + unsigned int sizeimage[VIDEO_MAX_PLANES]; unsigned int sequence; struct vim2m_fmt *fmt; }; @@ -194,6 +199,7 @@ struct vim2m_dev { struct mutex dev_mutex; struct v4l2_m2m_dev *m2m_dev; + bool multiplanar; }; struct vim2m_ctx { @@ -238,8 +244,10 @@ static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx, { switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: return &ctx->q_data[V4L2_M2M_SRC]; case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: return &ctx->q_data[V4L2_M2M_DST]; default: return NULL; @@ -250,8 +258,10 @@ static const char *type_name(enum v4l2_buf_type type) { switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: return "Output"; case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: return "Capture"; default: return "Invalid"; @@ -721,6 +731,7 @@ static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) { struct vb2_queue *vq; struct vim2m_q_data *q_data; + int ret; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (!vq) @@ -730,12 +741,12 @@ static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) if (!q_data) return -EINVAL; - f->fmt.pix.width = q_data->width; - f->fmt.pix.height = q_data->height; + ret = v4l2_fill_pixfmt(&f->fmt.pix, q_data->fmt->fourcc, + q_data->width, q_data->height); + if (ret) + return ret; + f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.pixelformat = q_data->fmt->fourcc; - f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3; - f->fmt.pix.sizeimage = q_data->sizeimage; f->fmt.pix.colorspace = ctx->colorspace; f->fmt.pix.xfer_func = ctx->xfer_func; f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; @@ -744,33 +755,102 @@ static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) return 0; } +static int vidioc_g_fmt_mplane(struct vim2m_ctx *ctx, struct v4l2_format *f) +{ + struct vb2_queue *vq; + struct vim2m_q_data *q_data; + int ret; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + ret = v4l2_fill_pixfmt_mp(&f->fmt.pix_mp, q_data->fmt->fourcc, + q_data->width, q_data->height); + if (ret) + return ret; + + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.colorspace = ctx->colorspace; + f->fmt.pix_mp.xfer_func = ctx->xfer_func; + f->fmt.pix_mp.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix_mp.quantization = ctx->quant; + + return 0; +} + static int vidioc_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; + return vidioc_g_fmt(file2ctx(file), f); } static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; + return vidioc_g_fmt(file2ctx(file), f); } -static int vidioc_try_fmt(struct v4l2_format *f) +static int vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) { - int width, height, walign, halign; + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + + return vidioc_g_fmt_mplane(file2ctx(file), f); +} + +static int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; - width = clamp(f->fmt.pix.width, MIN_W, MAX_W); - height = clamp(f->fmt.pix.width, MIN_H, MAX_H); + return vidioc_g_fmt_mplane(file2ctx(file), f); +} + +static int vidioc_try_fmt(struct v4l2_format *f, bool is_mplane) +{ + int walign, halign, ret; + int width = (is_mplane) ? f->fmt.pix_mp.width : f->fmt.pix.width; + int height = (is_mplane) ? f->fmt.pix_mp.height : f->fmt.pix.height; + u32 pixfmt = (is_mplane) ? f->fmt.pix_mp.pixelformat : + f->fmt.pix.pixelformat; + + width = clamp(width, MIN_W, MAX_W); + height = clamp(height, MIN_H, MAX_H); - get_alignment(f->fmt.pix.pixelformat, &walign, &halign); + get_alignment(pixfmt, &walign, &halign); width = ALIGN(width, walign); height = ALIGN(height, halign); f->fmt.pix.field = V4L2_FIELD_NONE; - return v4l2_fill_pixfmt(&f->fmt.pix, f->fmt.pix.pixelformat, - width, height); + if (is_mplane) { + ret = v4l2_fill_pixfmt_mp(&f->fmt.pix_mp, pixfmt, width, + height); + } else { + ret = v4l2_fill_pixfmt(&f->fmt.pix, pixfmt, width, height); + } + return ret; } static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, @@ -778,6 +858,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { struct vim2m_fmt *fmt; struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; fmt = find_format(f->fmt.pix.pixelformat); if (!fmt) { @@ -795,7 +879,36 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; f->fmt.pix.quantization = ctx->quant; - return vidioc_try_fmt(f); + return vidioc_try_fmt(f, false); +} + +static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vim2m_fmt *fmt; + struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + + fmt = find_format(f->fmt.pix_mp.pixelformat); + if (!fmt) { + f->fmt.pix_mp.pixelformat = formats[0].fourcc; + fmt = find_format(f->fmt.pix_mp.pixelformat); + } + if (!(fmt->types & MEM2MEM_CAPTURE)) { + v4l2_err(&ctx->dev->v4l2_dev, + "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + f->fmt.pix_mp.colorspace = ctx->colorspace; + f->fmt.pix_mp.xfer_func = ctx->xfer_func; + f->fmt.pix_mp.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix_mp.quantization = ctx->quant; + + return vidioc_try_fmt(f, true); } static int vidioc_try_fmt_vid_out(struct file *file, void *priv, @@ -803,6 +916,10 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv, { struct vim2m_fmt *fmt; struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; fmt = find_format(f->fmt.pix.pixelformat); if (!fmt) { @@ -818,13 +935,45 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv, if (!f->fmt.pix.colorspace) f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; - return vidioc_try_fmt(f); + return vidioc_try_fmt(f, false); +} + +static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vim2m_fmt *fmt; + struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + + fmt = find_format(f->fmt.pix_mp.pixelformat); + if (!fmt) { + f->fmt.pix_mp.pixelformat = formats[0].fourcc; + fmt = find_format(f->fmt.pix_mp.pixelformat); + } + if (!(fmt->types & MEM2MEM_OUTPUT)) { + v4l2_err(&ctx->dev->v4l2_dev, + "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix_mp.pixelformat); + return -EINVAL; + } + if (!f->fmt.pix_mp.colorspace) + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; + + return vidioc_try_fmt(f, true); } static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) { struct vim2m_q_data *q_data; struct vb2_queue *vq; + unsigned int i; + bool is_mplane = ctx->dev->multiplanar; + u32 pixfmt = (is_mplane) ? f->fmt.pix_mp.pixelformat : f->fmt.pix.pixelformat; + u32 width = (is_mplane) ? f->fmt.pix_mp.width : f->fmt.pix.width; + u32 height = (is_mplane) ? f->fmt.pix_mp.height : f->fmt.pix.height; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (!vq) @@ -839,11 +988,17 @@ static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f) return -EBUSY; } - q_data->fmt = find_format(f->fmt.pix.pixelformat); - q_data->width = f->fmt.pix.width; - q_data->height = f->fmt.pix.height; - q_data->sizeimage = q_data->width * q_data->height - * q_data->fmt->depth >> 3; + q_data->fmt = find_format(pixfmt); + q_data->width = width; + q_data->height = height; + if (is_mplane) { + q_data->num_mem_planes = f->fmt.pix_mp.num_planes; + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) + q_data->sizeimage[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } else { + q_data->sizeimage[0] = f->fmt.pix.sizeimage; + q_data->num_mem_planes = 1; + } dprintk(ctx->dev, 1, "Format for type %s: %dx%d (%d bpp), fmt: %c%c%c%c\n", @@ -861,6 +1016,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { int ret; + struct vim2m_dev *dev = video_drvdata(file); + + if (dev->multiplanar) + return -ENOTTY; ret = vidioc_try_fmt_vid_cap(file, priv, f); if (ret) @@ -869,12 +1028,32 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, return vidioc_s_fmt(file2ctx(file), f); } +static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + struct vim2m_dev *dev = video_drvdata(file); + + if (!dev->multiplanar) + return -ENOTTY; + + ret = vidioc_try_fmt_vid_cap_mplane(file, priv, f); + if (ret) + return ret; + + return vidioc_s_fmt(file2ctx(file), f); +} + static int vidioc_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); int ret; + if (dev->multiplanar) + return -ENOTTY; + ret = vidioc_try_fmt_vid_out(file, priv, f); if (ret) return ret; @@ -889,6 +1068,30 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv, return ret; } +static int vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vim2m_ctx *ctx = file2ctx(file); + struct vim2m_dev *dev = video_drvdata(file); + int ret; + + if (!dev->multiplanar) + return -ENOTTY; + + ret = vidioc_try_fmt_vid_out_mplane(file, priv, f); + if (ret) + return ret; + + ret = vidioc_s_fmt(file2ctx(file), f); + if (!ret) { + ctx->colorspace = f->fmt.pix_mp.colorspace; + ctx->xfer_func = f->fmt.pix_mp.xfer_func; + ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + ctx->quant = f->fmt.pix_mp.quantization; + } + return ret; +} + static int vim2m_s_ctrl(struct v4l2_ctrl *ctrl) { struct vim2m_ctx *ctx = @@ -939,11 +1142,17 @@ static const struct v4l2_ioctl_ops vim2m_ioctl_ops = { .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane, + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, + .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane, .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out_mplane, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane, + .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out_mplane, .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, @@ -972,23 +1181,32 @@ static int vim2m_queue_setup(struct vb2_queue *vq, { struct vim2m_ctx *ctx = vb2_get_drv_priv(vq); struct vim2m_q_data *q_data; - unsigned int size, count = *nbuffers; + unsigned int size, p, count = *nbuffers; q_data = get_q_data(ctx, vq->type); if (!q_data) return -EINVAL; - size = q_data->width * q_data->height * q_data->fmt->depth >> 3; + size = 0; + for (p = 0; p < q_data->num_mem_planes; p++) + size += q_data->sizeimage[p]; while (size * count > MEM2MEM_VID_MEM_LIMIT) (count)--; *nbuffers = count; - if (*nplanes) - return sizes[0] < size ? -EINVAL : 0; - - *nplanes = 1; - sizes[0] = size; + if (*nplanes) { + if (*nplanes != q_data->num_mem_planes) + return -EINVAL; + for (p = 0; p < q_data->num_mem_planes; p++) { + if (sizes[p] < q_data->sizeimage[p]) + return -EINVAL; + } + } else { + *nplanes = q_data->num_mem_planes; + for (p = 0; p < q_data->num_mem_planes; p++) + sizes[p] = q_data->sizeimage[p]; + } dprintk(ctx->dev, 1, "%s: get %d buffer(s) of size %d each.\n", type_name(vq->type), count, size); @@ -1015,21 +1233,24 @@ static int vim2m_buf_prepare(struct vb2_buffer *vb) { struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct vim2m_q_data *q_data; + unsigned int p; dprintk(ctx->dev, 2, "type: %s\n", type_name(vb->vb2_queue->type)); q_data = get_q_data(ctx, vb->vb2_queue->type); if (!q_data) return -EINVAL; - if (vb2_plane_size(vb, 0) < q_data->sizeimage) { - dprintk(ctx->dev, 1, - "%s data will not fit into plane (%lu < %lu)\n", - __func__, vb2_plane_size(vb, 0), - (long)q_data->sizeimage); - return -EINVAL; - } - vb2_set_plane_payload(vb, 0, q_data->sizeimage); + for (p = 0; p < q_data->num_mem_planes; p++) { + if (vb2_plane_size(vb, p) < q_data->sizeimage[p]) { + dprintk(ctx->dev, 1, + "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, p), + (long)q_data->sizeimage[p]); + return -EINVAL; + } + vb2_set_plane_payload(vb, p, q_data->sizeimage[p]); + } return 0; } @@ -1100,7 +1321,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vim2m_ctx *ctx = priv; int ret; - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->type = (ctx->dev->multiplanar) ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : + V4L2_BUF_TYPE_VIDEO_OUTPUT; src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); @@ -1114,7 +1336,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, if (ret) return ret; - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->type = (ctx->dev->multiplanar) ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : + V4L2_BUF_TYPE_VIDEO_CAPTURE; dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); @@ -1188,10 +1411,11 @@ static int vim2m_open(struct file *file) ctx->q_data[V4L2_M2M_SRC].fmt = &formats[0]; ctx->q_data[V4L2_M2M_SRC].width = 640; ctx->q_data[V4L2_M2M_SRC].height = 480; - ctx->q_data[V4L2_M2M_SRC].sizeimage = + ctx->q_data[V4L2_M2M_SRC].sizeimage[0] = ctx->q_data[V4L2_M2M_SRC].width * ctx->q_data[V4L2_M2M_SRC].height * (ctx->q_data[V4L2_M2M_SRC].fmt->depth >> 3); + ctx->q_data[V4L2_M2M_SRC].num_mem_planes = 1; ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; ctx->colorspace = V4L2_COLORSPACE_REC709; @@ -1268,7 +1492,7 @@ static const struct video_device vim2m_videodev = { .ioctl_ops = &vim2m_ioctl_ops, .minor = -1, .release = vim2m_device_release, - .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, + .device_caps = V4L2_CAP_STREAMING, }; static const struct v4l2_m2m_ops m2m_ops = { @@ -1299,10 +1523,14 @@ static int vim2m_probe(struct platform_device *pdev) atomic_set(&dev->num_inst, 0); mutex_init(&dev->dev_mutex); + dev->multiplanar = (multiplanar == 2); + dev->vfd = vim2m_videodev; vfd = &dev->vfd; vfd->lock = &dev->dev_mutex; vfd->v4l2_dev = &dev->v4l2_dev; + vfd->device_caps |= (dev->multiplanar) ? V4L2_CAP_VIDEO_M2M_MPLANE : + V4L2_CAP_VIDEO_M2M; video_set_drvdata(vfd, dev); platform_set_drvdata(pdev, dev); -- cgit v1.2.3-59-g8ed1b From 728c0d50994764a3783ce4993b54e4e2855aab3b Mon Sep 17 00:00:00 2001 From: Aakarsh Jain Date: Wed, 5 Mar 2025 11:23:08 +0530 Subject: media: s5p-mfc: Support for handling RET_ENC_BUFFER_FULL interrupt When output encoded buffer size provided by userspace is insufficient with current encoding parameters, it leads to RET_ENC_BUFFER_FULL interrupt which was not handled in IRQ handler. On handling of RET_ENC_BUFFER_FULL interrupt leads to NAL_ABORT command from host to risc which in turn leads to RET_NAL_ABORT interrupt. On receiving RET_NAL_ABORT driver clears workbit and VB2 queues for cleaner closing of MFC instance. When user encounters "Call on DQBUF after unrecoverable error", userspace should close fd and restart with larger output encoder buffer size. Signed-off-by: Aakarsh Jain Acked-by: Marek Szyprowski Signed-off-by: Hans Verkuil --- drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h | 1 + drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c | 14 ++++++++++++++ drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h | 1 + drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c | 5 +++++ 4 files changed, 21 insertions(+) diff --git a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h index fa49fe580e1a..075a58b50b8c 100644 --- a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h +++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h @@ -45,6 +45,7 @@ #define S5P_FIMV_H2R_CMD_WAKEUP_V6 8 #define S5P_FIMV_CH_LAST_FRAME_V6 9 #define S5P_FIMV_H2R_CMD_FLUSH_V6 10 +#define S5P_FIMV_H2R_CMD_NAL_ABORT_V6 11 /* RMVME: REALLOC used? */ #define S5P_FIMV_CH_FRAME_START_REALLOC_V6 5 diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c index c8e0ee383af3..9f89bd2620c7 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c @@ -739,6 +739,20 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) ctx->state = MFCINST_RUNNING; goto irq_cleanup_hw; + case S5P_MFC_R2H_CMD_ENC_BUFFER_FUL_RET: + ctx->state = MFCINST_NAL_ABORT; + s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); + set_work_bit(ctx); + WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); + s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + break; + + case S5P_MFC_R2H_CMD_NAL_ABORT_RET: + ctx->state = MFCINST_ERROR; + s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst); + s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src); + goto irq_cleanup_hw; + default: mfc_debug(2, "Unknown int reason\n"); s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h index 3cc2a4f5c40a..86c316c1ff8f 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h @@ -141,6 +141,7 @@ enum s5p_mfc_inst_state { MFCINST_RES_CHANGE_INIT, MFCINST_RES_CHANGE_FLUSH, MFCINST_RES_CHANGE_END, + MFCINST_NAL_ABORT, }; /* diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c index 0c636090d723..98f8292b3173 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c @@ -2229,6 +2229,11 @@ static void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev) case MFCINST_HEAD_PRODUCED: ret = s5p_mfc_run_init_enc_buffers(ctx); break; + case MFCINST_NAL_ABORT: + mfc_write(dev, ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); + s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, + dev, S5P_FIMV_H2R_CMD_NAL_ABORT_V6, NULL); + break; default: ret = -EAGAIN; } -- cgit v1.2.3-59-g8ed1b From c1c01458af573a0c33058117fdaf62146031c0fa Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sun, 9 Mar 2025 00:14:31 +0000 Subject: media: pvrusb2: Remove unused pvr2_std_create_enum pvr2_std_create_enum() has been unused since 2012's commit c0bb609fdc0b ("[media] pvrusb2: Get rid of obsolete code for video standard enumeration") Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Hans Verkuil --- drivers/media/usb/pvrusb2/pvrusb2-std.c | 167 -------------------------------- drivers/media/usb/pvrusb2/pvrusb2-std.h | 6 -- 2 files changed, 173 deletions(-) diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.c b/drivers/media/usb/pvrusb2/pvrusb2-std.c index e7ab41401577..81c994e62241 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-std.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-std.c @@ -212,173 +212,6 @@ unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, } -// Template data for possible enumerated video standards. Here we group -// standards which share common frame rates and resolution. -static struct v4l2_standard generic_standards[] = { - { - .id = (TSTD_B|TSTD_B1| - TSTD_D|TSTD_D1| - TSTD_G| - TSTD_H| - TSTD_I| - TSTD_K|TSTD_K1| - TSTD_L| - V4L2_STD_SECAM_LC | - TSTD_N|TSTD_Nc), - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, { - .id = (TSTD_M| - V4L2_STD_NTSC_M_JP| - V4L2_STD_NTSC_M_KR), - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - }, { // This is a total wild guess - .id = (TSTD_60), - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - }, { // This is total wild guess - .id = V4L2_STD_NTSC_443, - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - } -}; - -static struct v4l2_standard *match_std(v4l2_std_id id) -{ - unsigned int idx; - for (idx = 0; idx < ARRAY_SIZE(generic_standards); idx++) { - if (generic_standards[idx].id & id) { - return generic_standards + idx; - } - } - return NULL; -} - -static int pvr2_std_fill(struct v4l2_standard *std,v4l2_std_id id) -{ - struct v4l2_standard *template; - int idx; - unsigned int bcnt; - template = match_std(id); - if (!template) return 0; - idx = std->index; - memcpy(std,template,sizeof(*template)); - std->index = idx; - std->id = id; - bcnt = pvr2_std_id_to_str(std->name,sizeof(std->name)-1,id); - std->name[bcnt] = 0; - pvr2_trace(PVR2_TRACE_STD,"Set up standard idx=%u name=%s", - std->index,std->name); - return !0; -} - -/* These are special cases of combined standards that we should enumerate - separately if the component pieces are present. */ -static v4l2_std_id std_mixes[] = { - V4L2_STD_PAL_B | V4L2_STD_PAL_G, - V4L2_STD_PAL_D | V4L2_STD_PAL_K, - V4L2_STD_SECAM_B | V4L2_STD_SECAM_G, - V4L2_STD_SECAM_D | V4L2_STD_SECAM_K, -}; - -struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, - v4l2_std_id id) -{ - unsigned int std_cnt = 0; - unsigned int idx,bcnt,idx2; - v4l2_std_id idmsk,cmsk,fmsk; - struct v4l2_standard *stddefs; - - if (pvrusb2_debug & PVR2_TRACE_STD) { - char buf[100]; - bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id); - pvr2_trace( - PVR2_TRACE_STD,"Mapping standards mask=0x%x (%.*s)", - (int)id,bcnt,buf); - } - - *countptr = 0; - std_cnt = 0; - fmsk = 0; - for (idmsk = 1, cmsk = id; cmsk; idmsk <<= 1) { - if (!(idmsk & cmsk)) continue; - cmsk &= ~idmsk; - if (match_std(idmsk)) { - std_cnt++; - continue; - } - fmsk |= idmsk; - } - - for (idx2 = 0; idx2 < ARRAY_SIZE(std_mixes); idx2++) { - if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++; - } - - /* Don't complain about ATSC standard values */ - fmsk &= ~CSTD_ATSC; - - if (fmsk) { - char buf[100]; - bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk); - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "***WARNING*** Failed to classify the following standard(s): %.*s", - bcnt,buf); - } - - pvr2_trace(PVR2_TRACE_STD,"Setting up %u unique standard(s)", - std_cnt); - if (!std_cnt) return NULL; // paranoia - - stddefs = kcalloc(std_cnt, sizeof(struct v4l2_standard), - GFP_KERNEL); - if (!stddefs) - return NULL; - - for (idx = 0; idx < std_cnt; idx++) - stddefs[idx].index = idx; - - idx = 0; - - /* Enumerate potential special cases */ - for (idx2 = 0; (idx2 < ARRAY_SIZE(std_mixes)) && (idx < std_cnt); - idx2++) { - if (!(id & std_mixes[idx2])) continue; - if (pvr2_std_fill(stddefs+idx,std_mixes[idx2])) idx++; - } - /* Now enumerate individual pieces */ - for (idmsk = 1, cmsk = id; cmsk && (idx < std_cnt); idmsk <<= 1) { - if (!(idmsk & cmsk)) continue; - cmsk &= ~idmsk; - if (!pvr2_std_fill(stddefs+idx,idmsk)) continue; - idx++; - } - - *countptr = std_cnt; - return stddefs; -} - v4l2_std_id pvr2_std_get_usable(void) { return CSTD_ALL; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.h b/drivers/media/usb/pvrusb2/pvrusb2-std.h index d8b4c6dc72fe..74b05ecb9708 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-std.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-std.h @@ -23,12 +23,6 @@ int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, v4l2_std_id id); -// Create an array of suitable v4l2_standard structures given a bit mask of -// video standards to support. The array is allocated from the heap, and -// the number of elements is returned in the first argument. -struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, - v4l2_std_id id); - // Return mask of which video standard bits are valid v4l2_std_id pvr2_std_get_usable(void); -- cgit v1.2.3-59-g8ed1b From 1d5f88f053480326873115092bc116b7d14916ba Mon Sep 17 00:00:00 2001 From: Edward Adam Davis Date: Tue, 11 Mar 2025 15:20:14 +0800 Subject: media: vidtv: Terminating the subsequent process of initialization failure syzbot reported a slab-use-after-free Read in vidtv_mux_init. [1] After PSI initialization fails, the si member is accessed again, resulting in this uaf. After si initialization fails, the subsequent process needs to be exited. [1] BUG: KASAN: slab-use-after-free in vidtv_mux_pid_ctx_init drivers/media/test-drivers/vidtv/vidtv_mux.c:78 [inline] BUG: KASAN: slab-use-after-free in vidtv_mux_init+0xac2/0xbe0 drivers/media/test-drivers/vidtv/vidtv_mux.c:524 Read of size 8 at addr ffff88802fa42acc by task syz.2.37/6059 CPU: 0 UID: 0 PID: 6059 Comm: syz.2.37 Not tainted 6.14.0-rc5-syzkaller #0 Hardware name: Google Compute Engine, BIOS Google 02/12/2025 Call Trace: __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:408 [inline] print_report+0xc3/0x670 mm/kasan/report.c:521 kasan_report+0xd9/0x110 mm/kasan/report.c:634 vidtv_mux_pid_ctx_init drivers/media/test-drivers/vidtv/vidtv_mux.c:78 vidtv_mux_init+0xac2/0xbe0 drivers/media/test-drivers/vidtv/vidtv_mux.c:524 vidtv_start_streaming drivers/media/test-drivers/vidtv/vidtv_bridge.c:194 vidtv_start_feed drivers/media/test-drivers/vidtv/vidtv_bridge.c:239 dmx_section_feed_start_filtering drivers/media/dvb-core/dvb_demux.c:973 dvb_dmxdev_feed_start drivers/media/dvb-core/dmxdev.c:508 [inline] dvb_dmxdev_feed_restart.isra.0 drivers/media/dvb-core/dmxdev.c:537 dvb_dmxdev_filter_stop+0x2b4/0x3a0 drivers/media/dvb-core/dmxdev.c:564 dvb_dmxdev_filter_free drivers/media/dvb-core/dmxdev.c:840 [inline] dvb_demux_release+0x92/0x550 drivers/media/dvb-core/dmxdev.c:1246 __fput+0x3ff/0xb70 fs/file_table.c:464 task_work_run+0x14e/0x250 kernel/task_work.c:227 exit_task_work include/linux/task_work.h:40 [inline] do_exit+0xad8/0x2d70 kernel/exit.c:938 do_group_exit+0xd3/0x2a0 kernel/exit.c:1087 __do_sys_exit_group kernel/exit.c:1098 [inline] __se_sys_exit_group kernel/exit.c:1096 [inline] __x64_sys_exit_group+0x3e/0x50 kernel/exit.c:1096 x64_sys_call+0x151f/0x1720 arch/x86/include/generated/asm/syscalls_64.h:232 do_syscall_x64 arch/x86/entry/common.c:52 [inline] do_syscall_64+0xcd/0x250 arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f871d58d169 Code: Unable to access opcode bytes at 0x7f871d58d13f. RSP: 002b:00007fff4b19a788 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7 RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f871d58d169 RDX: 0000000000000064 RSI: 0000000000000000 RDI: 0000000000000000 RBP: 00007fff4b19a7ec R08: 0000000b4b19a87f R09: 00000000000927c0 R10: 0000000000000001 R11: 0000000000000246 R12: 0000000000000003 R13: 00000000000927c0 R14: 000000000001d553 R15: 00007fff4b19a840 Allocated by task 6059: kasan_save_stack+0x33/0x60 mm/kasan/common.c:47 kasan_save_track+0x14/0x30 mm/kasan/common.c:68 poison_kmalloc_redzone mm/kasan/common.c:377 [inline] __kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:394 kmalloc_noprof include/linux/slab.h:901 [inline] kzalloc_noprof include/linux/slab.h:1037 [inline] vidtv_psi_pat_table_init drivers/media/test-drivers/vidtv/vidtv_psi.c:970 vidtv_channel_si_init drivers/media/test-drivers/vidtv/vidtv_channel.c:423 vidtv_mux_init drivers/media/test-drivers/vidtv/vidtv_mux.c:519 vidtv_start_streaming drivers/media/test-drivers/vidtv/vidtv_bridge.c:194 vidtv_start_feed drivers/media/test-drivers/vidtv/vidtv_bridge.c:239 dmx_section_feed_start_filtering drivers/media/dvb-core/dvb_demux.c:973 dvb_dmxdev_feed_start drivers/media/dvb-core/dmxdev.c:508 [inline] dvb_dmxdev_feed_restart.isra.0 drivers/media/dvb-core/dmxdev.c:537 dvb_dmxdev_filter_stop+0x2b4/0x3a0 drivers/media/dvb-core/dmxdev.c:564 dvb_dmxdev_filter_free drivers/media/dvb-core/dmxdev.c:840 [inline] dvb_demux_release+0x92/0x550 drivers/media/dvb-core/dmxdev.c:1246 __fput+0x3ff/0xb70 fs/file_table.c:464 task_work_run+0x14e/0x250 kernel/task_work.c:227 exit_task_work include/linux/task_work.h:40 [inline] do_exit+0xad8/0x2d70 kernel/exit.c:938 do_group_exit+0xd3/0x2a0 kernel/exit.c:1087 __do_sys_exit_group kernel/exit.c:1098 [inline] __se_sys_exit_group kernel/exit.c:1096 [inline] __x64_sys_exit_group+0x3e/0x50 kernel/exit.c:1096 x64_sys_call arch/x86/include/generated/asm/syscalls_64.h:232 do_syscall_x64 arch/x86/entry/common.c:52 [inline] do_syscall_64+0xcd/0x250 arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 6059: kasan_save_stack+0x33/0x60 mm/kasan/common.c:47 kasan_save_track+0x14/0x30 mm/kasan/common.c:68 kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:576 poison_slab_object mm/kasan/common.c:247 [inline] __kasan_slab_free+0x51/0x70 mm/kasan/common.c:264 kasan_slab_free include/linux/kasan.h:233 [inline] slab_free_hook mm/slub.c:2353 [inline] slab_free mm/slub.c:4609 [inline] kfree+0x2c4/0x4d0 mm/slub.c:4757 vidtv_channel_si_init drivers/media/test-drivers/vidtv/vidtv_channel.c:499 vidtv_mux_init drivers/media/test-drivers/vidtv/vidtv_mux.c:519 vidtv_start_streaming drivers/media/test-drivers/vidtv/vidtv_bridge.c:194 vidtv_start_feed drivers/media/test-drivers/vidtv/vidtv_bridge.c:239 dmx_section_feed_start_filtering drivers/media/dvb-core/dvb_demux.c:973 dvb_dmxdev_feed_start drivers/media/dvb-core/dmxdev.c:508 [inline] dvb_dmxdev_feed_restart.isra.0 drivers/media/dvb-core/dmxdev.c:537 dvb_dmxdev_filter_stop+0x2b4/0x3a0 drivers/media/dvb-core/dmxdev.c:564 dvb_dmxdev_filter_free drivers/media/dvb-core/dmxdev.c:840 [inline] dvb_demux_release+0x92/0x550 drivers/media/dvb-core/dmxdev.c:1246 __fput+0x3ff/0xb70 fs/file_table.c:464 task_work_run+0x14e/0x250 kernel/task_work.c:227 exit_task_work include/linux/task_work.h:40 [inline] do_exit+0xad8/0x2d70 kernel/exit.c:938 do_group_exit+0xd3/0x2a0 kernel/exit.c:1087 __do_sys_exit_group kernel/exit.c:1098 [inline] __se_sys_exit_group kernel/exit.c:1096 [inline] __x64_sys_exit_group+0x3e/0x50 kernel/exit.c:1096 x64_sys_call arch/x86/include/generated/asm/syscalls_64.h:232 do_syscall_x64 arch/x86/entry/common.c:52 [inline] do_syscall_64+0xcd/0x250 arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: 3be8037960bc ("media: vidtv: add error checks") Cc: stable@vger.kernel.org Reported-by: syzbot+0d33ab192bd50b6c91e6@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=0d33ab192bd50b6c91e6 Signed-off-by: Edward Adam Davis Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vidtv/vidtv_channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/test-drivers/vidtv/vidtv_channel.c b/drivers/media/test-drivers/vidtv/vidtv_channel.c index 7838e6272712..f3023e91b3eb 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_channel.c +++ b/drivers/media/test-drivers/vidtv/vidtv_channel.c @@ -497,7 +497,7 @@ free_sdt: vidtv_psi_sdt_table_destroy(m->si.sdt); free_pat: vidtv_psi_pat_table_destroy(m->si.pat); - return 0; + return -EINVAL; } void vidtv_channel_si_destroy(struct vidtv_mux *m) -- cgit v1.2.3-59-g8ed1b From 1a27fce0fa79ef32c37f7e1b2d49357da7f2e2b2 Mon Sep 17 00:00:00 2001 From: Martin Tůma Date: Tue, 11 Mar 2025 18:31:41 +0100 Subject: docs: media: mgb4: Improve mgb4 driver documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add some basic info about the HW/driver + contact info. Signed-off-by: Martin Tůma Signed-off-by: Hans Verkuil --- Documentation/admin-guide/media/mgb4.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/admin-guide/media/mgb4.rst b/Documentation/admin-guide/media/mgb4.rst index f69d331e3cb1..5ac69b833a7a 100644 --- a/Documentation/admin-guide/media/mgb4.rst +++ b/Documentation/admin-guide/media/mgb4.rst @@ -1,8 +1,17 @@ .. SPDX-License-Identifier: GPL-2.0 +.. include:: + The mgb4 driver =============== +Copyright |copy| 2023 - 2025 Digiteq Automotive + author: Martin Tůma + +This is a v4l2 device driver for the Digiteq Automotive FrameGrabber 4, a PCIe +card capable of capturing and generating FPD-Link III and GMSL2/3 video streams +as used in the automotive industry. + sysfs interface --------------- -- cgit v1.2.3-59-g8ed1b From ca7af8040ed1a2a034d6fecd8a8c0ddbf819a1bf Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 14 Mar 2025 12:39:40 +0000 Subject: media: vivid: Fix requirement about webcam_intervals Since commit f0b4a2c037c0 ("media: vivid: Extend FPS rates offered by simulated webcam") we do not require twice as many intervals as sizes. In fact, now we have 13 intervals and 6 sizes. Fix the comment. Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vivid/vivid-vid-cap.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index b166d90177c6..623ba1e5e547 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -33,8 +33,7 @@ static const struct v4l2_frmsize_discrete webcam_sizes[] = { }; /* - * Intervals must be in increasing order and there must be twice as many - * elements in this array as there are in webcam_sizes. + * Intervals must be in increasing order. */ static const struct v4l2_fract webcam_intervals[] = { { 1, 1 }, -- cgit v1.2.3-59-g8ed1b From 7ca9a4d9bdc30e148d3096db23e689ffe4f548c1 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 14 Mar 2025 12:39:41 +0000 Subject: media: vivid: Add more webcam resolutions Add 3 more common resolution for webcams. This is required to increase the test coverage of unit tests based on vivid. Co-developed-by: Hidenori Kobayashi Signed-off-by: Hidenori Kobayashi Signed-off-by: Ricardo Ribalda Tested-by: Hidenori Kobayashi # v4l2-compliance, Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vivid/vivid-vid-cap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index 623ba1e5e547..df726961222b 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -25,9 +25,12 @@ /* Sizes must be in increasing order */ static const struct v4l2_frmsize_discrete webcam_sizes[] = { { 320, 180 }, + { 320, 240 }, { 640, 360 }, { 640, 480 }, { 1280, 720 }, + { 1280, 960 }, + { 1600, 1200 }, { 1920, 1080 }, { 3840, 2160 }, }; -- cgit v1.2.3-59-g8ed1b From 3d622ba277bdffd38c8179b5ebcea99206f39151 Mon Sep 17 00:00:00 2001 From: MichaÅ‚ MirosÅ‚aw Date: Fri, 28 Mar 2025 23:05:27 +0100 Subject: media: videobuf2: check constants during build time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is nothing a driver author can do fix in the driver to make the global constants match. Since the assertion can be verified at build time, don't return EINVAL at runtime for it. Signed-off-by: MichaÅ‚ MirosÅ‚aw Acked-by: Marek Szyprowski Signed-off-by: Hans Verkuil --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 9201d854dbcc..1cd26faee503 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -903,6 +903,11 @@ EXPORT_SYMBOL_GPL(vb2_expbuf); int vb2_queue_init_name(struct vb2_queue *q, const char *name) { + /* vb2_memory should match with v4l2_memory */ + BUILD_BUG_ON(VB2_MEMORY_MMAP != (int)V4L2_MEMORY_MMAP); + BUILD_BUG_ON(VB2_MEMORY_USERPTR != (int)V4L2_MEMORY_USERPTR); + BUILD_BUG_ON(VB2_MEMORY_DMABUF != (int)V4L2_MEMORY_DMABUF); + /* * Sanity check */ @@ -916,12 +921,6 @@ int vb2_queue_init_name(struct vb2_queue *q, const char *name) WARN_ON((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN); - /* Warn that vb2_memory should match with v4l2_memory */ - if (WARN_ON(VB2_MEMORY_MMAP != (int)V4L2_MEMORY_MMAP) - || WARN_ON(VB2_MEMORY_USERPTR != (int)V4L2_MEMORY_USERPTR) - || WARN_ON(VB2_MEMORY_DMABUF != (int)V4L2_MEMORY_DMABUF)) - return -EINVAL; - if (q->buf_struct_size == 0) q->buf_struct_size = sizeof(struct vb2_v4l2_buffer); -- cgit v1.2.3-59-g8ed1b From a898d2ea7e78614fdbfcc4c7b5ca60509deb0370 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Tue, 1 Apr 2025 14:17:54 +0000 Subject: media: atomisp: Fix Wformat-truncation warning Gcc8 is convinced that we do not have enough space in dot_id_input_bin. Extend the variable 17 bytes which is just used for debugging. drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c:1336:9: warning: '(pipe' directive output may be truncated writing 5 bytes into a region of size between 1 and 74 [-Wformat-truncation=] Signed-off-by: Ricardo Ribalda Reviewed-by: Andy Shevchenko Signed-off-by: Hans Verkuil --- drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 9818771a35e5..84220359c957 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 @@ -121,7 +121,8 @@ static const char *const pipe_id_to_str[] = { /* [IA_CSS_PIPE_ID_YUVPP] =*/ "yuvpp", }; -static char dot_id_input_bin[SH_CSS_MAX_BINARY_NAME + 10]; +/* 27 is combined length of _stage%d(pipe%d)\0. */ +static char dot_id_input_bin[SH_CSS_MAX_BINARY_NAME + 27]; static char ring_buffer[200]; void ia_css_debug_dtrace(unsigned int level, const char *fmt, ...) -- cgit v1.2.3-59-g8ed1b From 5edc9b560f60c40e658af0b8e98ae2dfadc438d8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 Apr 2025 14:17:55 +0000 Subject: media: cec: extron-da-hd-4k-plus: Fix Wformat-truncation Fix gcc8 warning: drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c:1014:44: warning: 'DCEC' directive output may be truncated writing 4 bytes into a region of size between 0 and 53 [-Wformat-truncation=] Resizing the 'buf' and 'cmd' arrays fixed the warning. Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c index cfbfc4c1b2e6..41d019b01ec0 100644 --- a/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c +++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c @@ -1002,8 +1002,8 @@ static int extron_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, u32 signal_free_time, struct cec_msg *msg) { struct extron_port *port = cec_get_drvdata(adap); - char buf[CEC_MAX_MSG_SIZE * 3 + 1]; - char cmd[CEC_MAX_MSG_SIZE * 3 + 13]; + char buf[(CEC_MAX_MSG_SIZE - 1) * 3 + 1]; + char cmd[sizeof(buf) + 14]; unsigned int i; if (port->disconnected) -- cgit v1.2.3-59-g8ed1b From 32b38fbf6401e87d82f0173a772218f121257cb2 Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Fri, 4 Apr 2025 15:53:42 +0200 Subject: media: pt3: Replace deprecated PCI functions pcim_iomap_table() and pcim_iomap_regions() have been deprecated. Replace them with pcim_iomap_region(). Signed-off-by: Philipp Stanner Tested-by: Akihiro Tsukada Signed-off-by: Hans Verkuil --- drivers/media/pci/pt3/pt3.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/media/pci/pt3/pt3.c b/drivers/media/pci/pt3/pt3.c index 246f73b8a9e7..c55aa782b72c 100644 --- a/drivers/media/pci/pt3/pt3.c +++ b/drivers/media/pci/pt3/pt3.c @@ -692,6 +692,7 @@ static int pt3_probe(struct pci_dev *pdev, const struct pci_device_id *ent) u8 rev; u32 ver; int i, ret; + void __iomem *iomem; struct pt3_board *pt3; struct i2c_adapter *i2c; @@ -703,10 +704,6 @@ static int pt3_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return -ENODEV; pci_set_master(pdev); - ret = pcim_iomap_regions(pdev, BIT(0) | BIT(2), DRV_NAME); - if (ret < 0) - return ret; - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); if (ret) { dev_err(&pdev->dev, "Failed to set DMA mask\n"); @@ -719,8 +716,16 @@ static int pt3_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_drvdata(pdev, pt3); pt3->pdev = pdev; mutex_init(&pt3->lock); - pt3->regs[0] = pcim_iomap_table(pdev)[0]; - pt3->regs[1] = pcim_iomap_table(pdev)[2]; + + iomem = pcim_iomap_region(pdev, 0, DRV_NAME); + if (IS_ERR(iomem)) + return PTR_ERR(iomem); + pt3->regs[0] = iomem; + + iomem = pcim_iomap_region(pdev, 2, DRV_NAME); + if (IS_ERR(iomem)) + return PTR_ERR(iomem); + pt3->regs[1] = iomem; ver = ioread32(pt3->regs[0] + REG_VERSION); if ((ver >> 16) != 0x0301) { -- cgit v1.2.3-59-g8ed1b From bd2ad1a08a4c65cbb4dbf30c553a57be8439df5d Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Fri, 4 Apr 2025 15:53:44 +0200 Subject: media: solo6x10: Replace deprecated PCI functions pcim_iomap_table() and pcim_iomap_regions() have been deprecated. Replace them with pcim_iomap_region(). Signed-off-by: Philipp Stanner Signed-off-by: Hans Verkuil --- drivers/media/pci/solo6x10/solo6x10-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index 6ec1480a6d18..febb2c156cf6 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -477,10 +477,10 @@ static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_write_config_byte(pdev, 0x40, 0x00); pci_write_config_byte(pdev, 0x41, 0x00); - ret = pcim_iomap_regions(pdev, BIT(0), SOLO6X10_NAME); + solo_dev->reg_base = pcim_iomap_region(pdev, 0, SOLO6X10_NAME); + ret = PTR_ERR_OR_ZERO(solo_dev->reg_base); if (ret) goto fail_probe; - solo_dev->reg_base = pcim_iomap_table(pdev)[0]; chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & SOLO_CHIP_ID_MASK; -- cgit v1.2.3-59-g8ed1b From f8a1082ecb5d6e6a8c4ee2b0d465b16c1f8e735c Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Fri, 4 Apr 2025 15:53:45 +0200 Subject: media: tw5864: Replace deprecated PCI functions pcim_iomap_table() and pcim_iomap_regions() have been deprecated. pcim_iomap_regions(), furthermore, has so far wrongly been passed the device's name instead of the driver's name, which makes that function's debug prints useless. Replace the deprecated function with pcim_iomap_region(). Define the driver name globally and use it where appropriate. Signed-off-by: Philipp Stanner Signed-off-by: Hans Verkuil --- drivers/media/pci/tw5864/tw5864-core.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/media/pci/tw5864/tw5864-core.c b/drivers/media/pci/tw5864/tw5864-core.c index 4d33caf83307..832788603f88 100644 --- a/drivers/media/pci/tw5864/tw5864-core.c +++ b/drivers/media/pci/tw5864/tw5864-core.c @@ -24,6 +24,8 @@ #include "tw5864.h" #include "tw5864-reg.h" +#define DRIVER_NAME "tw5864" + MODULE_DESCRIPTION("V4L2 driver module for tw5864-based multimedia capture & encoding devices"); MODULE_AUTHOR("Bluecherry Maintainers "); MODULE_AUTHOR("Andrey Utkin "); @@ -246,7 +248,8 @@ static int tw5864_initdev(struct pci_dev *pci_dev, if (!dev) return -ENOMEM; - snprintf(dev->name, sizeof(dev->name), "tw5864:%s", pci_name(pci_dev)); + snprintf(dev->name, sizeof(dev->name), "%s:%s", DRIVER_NAME, + pci_name(pci_dev)); err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); if (err) @@ -269,12 +272,12 @@ static int tw5864_initdev(struct pci_dev *pci_dev, } /* get mmio */ - err = pcim_iomap_regions(pci_dev, BIT(0), dev->name); + dev->mmio = pcim_iomap_region(pci_dev, 0, DRIVER_NAME); + err = PTR_ERR_OR_ZERO(dev->mmio); if (err) { dev_err(&dev->pci->dev, "Cannot request regions for MMIO\n"); goto unreg_v4l2; } - dev->mmio = pcim_iomap_table(pci_dev)[0]; spin_lock_init(&dev->slock); @@ -290,7 +293,7 @@ static int tw5864_initdev(struct pci_dev *pci_dev, /* get irq */ err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw5864_isr, - IRQF_SHARED, "tw5864", dev); + IRQF_SHARED, DRIVER_NAME, dev); if (err < 0) { dev_err(&dev->pci->dev, "can't get IRQ %d\n", pci_dev->irq); goto fini_video; @@ -324,7 +327,7 @@ static void tw5864_finidev(struct pci_dev *pci_dev) } static struct pci_driver tw5864_pci_driver = { - .name = "tw5864", + .name = DRIVER_NAME, .id_table = tw5864_pci_tbl, .probe = tw5864_initdev, .remove = tw5864_finidev, -- cgit v1.2.3-59-g8ed1b From 73fb3b92da84637e3817580fa205d48065924e15 Mon Sep 17 00:00:00 2001 From: Edward Adam Davis Date: Sat, 5 Apr 2025 19:56:41 +0800 Subject: media: cxusb: no longer judge rbuf when the write fails syzbot reported a uninit-value in cxusb_i2c_xfer. [1] Only when the write operation of usb_bulk_msg() in dvb_usb_generic_rw() succeeds and rlen is greater than 0, the read operation of usb_bulk_msg() will be executed to read rlen bytes of data from the dvb device into the rbuf. In this case, although rlen is 1, the write operation failed which resulted in the dvb read operation not being executed, and ultimately variable i was not initialized. [1] BUG: KMSAN: uninit-value in cxusb_gpio_tuner drivers/media/usb/dvb-usb/cxusb.c:124 [inline] BUG: KMSAN: uninit-value in cxusb_i2c_xfer+0x153a/0x1a60 drivers/media/usb/dvb-usb/cxusb.c:196 cxusb_gpio_tuner drivers/media/usb/dvb-usb/cxusb.c:124 [inline] cxusb_i2c_xfer+0x153a/0x1a60 drivers/media/usb/dvb-usb/cxusb.c:196 __i2c_transfer+0xe25/0x3150 drivers/i2c/i2c-core-base.c:-1 i2c_transfer+0x317/0x4a0 drivers/i2c/i2c-core-base.c:2315 i2c_transfer_buffer_flags+0x125/0x1e0 drivers/i2c/i2c-core-base.c:2343 i2c_master_send include/linux/i2c.h:109 [inline] i2cdev_write+0x210/0x280 drivers/i2c/i2c-dev.c:183 do_loop_readv_writev fs/read_write.c:848 [inline] vfs_writev+0x963/0x14e0 fs/read_write.c:1057 do_writev+0x247/0x5c0 fs/read_write.c:1101 __do_sys_writev fs/read_write.c:1169 [inline] __se_sys_writev fs/read_write.c:1166 [inline] __x64_sys_writev+0x98/0xe0 fs/read_write.c:1166 x64_sys_call+0x2229/0x3c80 arch/x86/include/generated/asm/syscalls_64.h:21 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xcd/0x1e0 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Reported-by: syzbot+526bd95c0ec629993bf3@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=526bd95c0ec629993bf3 Tested-by: syzbot+526bd95c0ec629993bf3@syzkaller.appspotmail.com Fixes: 22c6d93a7310 ("[PATCH] dvb: usb: support Medion hybrid USB2.0 DVB-T/analogue box") Cc: stable@vger.kernel.org Signed-off-by: Edward Adam Davis Signed-off-by: Hans Verkuil --- drivers/media/usb/dvb-usb/cxusb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index f44529b40989..d0501c1e81d6 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -119,9 +119,8 @@ static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff) o[0] = GPIO_TUNER; o[1] = onoff; - cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1); - if (i != 0x01) + if (!cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1) && i != 0x01) dev_info(&d->udev->dev, "gpio_write failed.\n"); st->gpio_write_state[GPIO_TUNER] = onoff; -- cgit v1.2.3-59-g8ed1b From e6fd3d81dba13c7c8a244e15db0f1e37f4ed21ab Mon Sep 17 00:00:00 2001 From: Martin Tůma Date: Mon, 7 Apr 2025 17:50:35 +0200 Subject: media: mgb4: Fix resolution change events triggering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always generate the resolution change event when the HW reports it and only discard the streaming termination in case the new resolution is the same as the old one. The old logic prevented events on "no signal" -> "valid resolution" transitions as VIDIOC_QUERY_DV_TIMINGS never updates the timings when there is no signal present. Signed-off-by: Martin Tůma Signed-off-by: Hans Verkuil --- drivers/media/pci/mgb4/mgb4_vin.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/pci/mgb4/mgb4_vin.c b/drivers/media/pci/mgb4/mgb4_vin.c index 434eaf0440e2..6e806e075837 100644 --- a/drivers/media/pci/mgb4/mgb4_vin.c +++ b/drivers/media/pci/mgb4/mgb4_vin.c @@ -749,14 +749,14 @@ static void signal_change(struct work_struct *work) u32 width = resolution >> 16; u32 height = resolution & 0xFFFF; - if (timings->width != width || timings->height != height) { - static const struct v4l2_event ev = { - .type = V4L2_EVENT_SOURCE_CHANGE, - .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, - }; + static const struct v4l2_event ev = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; - v4l2_event_queue(&vindev->vdev, &ev); + v4l2_event_queue(&vindev->vdev, &ev); + if (timings->width != width || timings->height != height) { if (vb2_is_streaming(&vindev->queue)) vb2_queue_error(&vindev->queue); } -- cgit v1.2.3-59-g8ed1b From a9076609e14988a0a99bb4992998e688652604b4 Mon Sep 17 00:00:00 2001 From: Kells Ping Date: Tue, 8 Apr 2025 10:53:09 +0800 Subject: media: platform: cros-ec: Add Dirks to the match table The Google Dirks 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 12b73ea0f31d..a8d31c3126f8 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -329,6 +329,8 @@ static const struct cec_dmi_match cec_dmi_match_table[] = { { "Google", "Dexi", "0000:00:02.0", port_db_conns }, /* Google Dita */ { "Google", "Dita", "0000:00:02.0", port_db_conns }, + /* Google Dirks */ + { "Google", "Dirks", "0000:00:02.0", port_db_conns }, }; static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, -- cgit v1.2.3-59-g8ed1b From 497f1fb94759fa0c638f15c12b1ab3e586bccfcb Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Tue, 8 Apr 2025 13:48:39 +0800 Subject: media: nuvoton: npcm-video: Fix stuck due to no video signal error Fix the issue when start_frame and detect_resolution functions are executed at the same time, which may cause driver stops capturing due to status of no video signal error. Signed-off-by: Michael Chang Signed-off-by: Hans Verkuil --- drivers/media/platform/nuvoton/npcm-video.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c index 7a9d8928ae40..3022fdcf66ec 100644 --- a/drivers/media/platform/nuvoton/npcm-video.c +++ b/drivers/media/platform/nuvoton/npcm-video.c @@ -863,7 +863,6 @@ static void npcm_video_detect_resolution(struct npcm_video *video) struct regmap *gfxi = video->gfx_regmap; unsigned int dispst; - video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; det->width = npcm_video_hres(video); det->height = npcm_video_vres(video); @@ -892,12 +891,16 @@ static void npcm_video_detect_resolution(struct npcm_video *video) clear_bit(VIDEO_RES_CHANGING, &video->flags); } - if (det->width && det->height) + if (det->width && det->height) { video->v4l2_input_status = 0; - - dev_dbg(video->dev, "Got resolution[%dx%d] -> [%dx%d], status %d\n", - act->width, act->height, det->width, det->height, - video->v4l2_input_status); + dev_dbg(video->dev, "Got resolution[%dx%d] -> [%dx%d], status %d\n", + act->width, act->height, det->width, det->height, + video->v4l2_input_status); + } else { + video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; + dev_err(video->dev, "Got invalid resolution[%dx%d]\n", det->width, + det->height); + } } static int npcm_video_set_resolution(struct npcm_video *video, -- cgit v1.2.3-59-g8ed1b From e2ff3200065283b795f9800b9e03a93dd0d9f7e3 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Tue, 8 Apr 2025 13:54:54 +0800 Subject: media: nuvoton: npcm-video: Prevent returning unsupported resolutions To restrict the returned resolution due to Nuvoton SoC hardware limitations. Signed-off-by: Michael Chang Signed-off-by: Hans Verkuil --- drivers/media/platform/nuvoton/npcm-video.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c index 3022fdcf66ec..44e904e61801 100644 --- a/drivers/media/platform/nuvoton/npcm-video.c +++ b/drivers/media/platform/nuvoton/npcm-video.c @@ -578,7 +578,7 @@ static unsigned int npcm_video_hres(struct npcm_video *video) regmap_read(gfxi, HVCNTL, &hvcntl); apb_hor_res = (((hvcnth & HVCNTH_MASK) << 8) + (hvcntl & HVCNTL_MASK) + 1); - return apb_hor_res; + return (apb_hor_res > MAX_WIDTH) ? MAX_WIDTH : apb_hor_res; } static unsigned int npcm_video_vres(struct npcm_video *video) @@ -591,7 +591,7 @@ static unsigned int npcm_video_vres(struct npcm_video *video) apb_ver_res = (((vvcnth & VVCNTH_MASK) << 8) + (vvcntl & VVCNTL_MASK)); - return apb_ver_res; + return (apb_ver_res > MAX_HEIGHT) ? MAX_HEIGHT : apb_ver_res; } static int npcm_video_capres(struct npcm_video *video, unsigned int hor_res, -- cgit v1.2.3-59-g8ed1b From 2226b2dd42c59dff5f7de35afe5c0cc1a674b085 Mon Sep 17 00:00:00 2001 From: Martin Tůma Date: Tue, 8 Apr 2025 15:21:55 +0200 Subject: media: mgb4: Enumerate only the available timings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enumerate only the available (as given by the input source) timings, not all theoretically possible. Signed-off-by: Martin Tůma Signed-off-by: Hans Verkuil --- drivers/media/pci/mgb4/mgb4_vin.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/mgb4/mgb4_vin.c b/drivers/media/pci/mgb4/mgb4_vin.c index 6e806e075837..989e93f67f75 100644 --- a/drivers/media/pci/mgb4/mgb4_vin.c +++ b/drivers/media/pci/mgb4/mgb4_vin.c @@ -641,7 +641,14 @@ static int vidioc_query_dv_timings(struct file *file, void *fh, static int vidioc_enum_dv_timings(struct file *file, void *fh, struct v4l2_enum_dv_timings *timings) { - return v4l2_enum_dv_timings_cap(timings, &video_timings_cap, NULL, NULL); + struct mgb4_vin_dev *vindev = video_drvdata(file); + + if (timings->index != 0) + return -EINVAL; + if (get_timings(vindev, &timings->timings) < 0) + return -ENODATA; + + return 0; } static int vidioc_dv_timings_cap(struct file *file, void *fh, -- cgit v1.2.3-59-g8ed1b From 8fc0ef066b8baa43572520725f18afffd83ce59b Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Wed, 9 Apr 2025 14:17:37 +0530 Subject: media: atmel-isi: use devm_kmemdup_array() Convert to use devm_kmemdup_array() and while at it, make the size robust against type changes. Signed-off-by: Raag Jadav Signed-off-by: Hans Verkuil --- drivers/media/platform/atmel/atmel-isi.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index 0d1c39347529..a05a744cbb75 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -1072,16 +1072,12 @@ static int isi_formats_init(struct atmel_isi *isi) return -ENXIO; isi->num_user_formats = num_fmts; - isi->user_formats = devm_kcalloc(isi->dev, - num_fmts, sizeof(struct isi_format *), - GFP_KERNEL); + isi->user_formats = devm_kmemdup_array(isi->dev, isi_fmts, num_fmts, + sizeof(*isi_fmts), GFP_KERNEL); if (!isi->user_formats) return -ENOMEM; - memcpy(isi->user_formats, isi_fmts, - num_fmts * sizeof(struct isi_format *)); isi->current_fmt = isi->user_formats[0]; - return 0; } -- cgit v1.2.3-59-g8ed1b From 8b807366d85092862c0233914d62a45ea32702f1 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Wed, 9 Apr 2025 14:17:38 +0530 Subject: media: stm32-dcmi: use devm_kmemdup_array() Convert to use devm_kmemdup_array() and while at it, make the size robust against type changes. Signed-off-by: Raag Jadav Signed-off-by: Hans Verkuil --- drivers/media/platform/st/stm32/stm32-dcmi.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index 9b699ee2b1e0..ba77062357eb 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -1682,18 +1682,14 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi) return -ENXIO; dcmi->num_of_sd_formats = num_fmts; - dcmi->sd_formats = devm_kcalloc(dcmi->dev, - num_fmts, sizeof(struct dcmi_format *), - GFP_KERNEL); + dcmi->sd_formats = devm_kmemdup_array(dcmi->dev, sd_fmts, num_fmts, + sizeof(*sd_fmts), GFP_KERNEL); if (!dcmi->sd_formats) { dev_err(dcmi->dev, "Could not allocate memory\n"); return -ENOMEM; } - memcpy(dcmi->sd_formats, sd_fmts, - num_fmts * sizeof(struct dcmi_format *)); dcmi->sd_format = dcmi->sd_formats[0]; - return 0; } -- cgit v1.2.3-59-g8ed1b From 29c71dc4c83208b32dfa55778f3a99165691cdcb Mon Sep 17 00:00:00 2001 From: Ken Lin Date: Thu, 10 Apr 2025 11:03:59 +0800 Subject: media: platform: cros-ec: Add Moxie to the match table The Google Moxie device uses the same approach as the 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 | 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 a8d31c3126f8..106860d11cef 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -331,6 +331,8 @@ static const struct cec_dmi_match cec_dmi_match_table[] = { { "Google", "Dita", "0000:00:02.0", port_db_conns }, /* Google Dirks */ { "Google", "Dirks", "0000:00:02.0", port_db_conns }, + /* Google Moxie */ + { "Google", "Moxie", "0000:00:02.0", port_b_conns }, }; static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, -- cgit v1.2.3-59-g8ed1b From 051e634ee4ceed611c7162ef22563c6acef591f5 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 10 Apr 2025 06:52:21 +0100 Subject: media: platform: exynos4-is: Use of_get_available_child_by_name() Simplify fimc_md_is_isp_available() by using of_get_available_child_by_name(). Reviewed-by: Krzysztof Kozlowski Signed-off-by: Biju Das Signed-off-by: Hans Verkuil --- drivers/media/platform/samsung/exynos4-is/media-dev.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.h b/drivers/media/platform/samsung/exynos4-is/media-dev.h index a50e58ab7ef7..ea496670d4b5 100644 --- a/drivers/media/platform/samsung/exynos4-is/media-dev.h +++ b/drivers/media/platform/samsung/exynos4-is/media-dev.h @@ -179,8 +179,8 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on); static inline bool fimc_md_is_isp_available(struct device_node *node) { struct device_node *child __free(device_node) = - of_get_child_by_name(node, FIMC_IS_OF_NODE_NAME); - return child ? of_device_is_available(child) : false; + of_get_available_child_by_name(node, FIMC_IS_OF_NODE_NAME); + return child; } #else #define fimc_md_is_isp_available(node) (false) -- cgit v1.2.3-59-g8ed1b From 5bc68bd3826e573f9a83c82c5107536cf616fea8 Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Sun, 13 Apr 2025 14:35:32 -0500 Subject: media: dt-bindings: Document Tegra186 and Tegra194 cec These are already used in device trees, so describe them here. As the driver only declares up through Tegra210, these must use a fallback compatible of tegra210-cec. Acked-by: Krzysztof Kozlowski Signed-off-by: Aaron Kling Signed-off-by: Hans Verkuil --- .../devicetree/bindings/media/cec/nvidia,tegra114-cec.yaml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/media/cec/nvidia,tegra114-cec.yaml b/Documentation/devicetree/bindings/media/cec/nvidia,tegra114-cec.yaml index a6b73498bc21..4b46aa755ccd 100644 --- a/Documentation/devicetree/bindings/media/cec/nvidia,tegra114-cec.yaml +++ b/Documentation/devicetree/bindings/media/cec/nvidia,tegra114-cec.yaml @@ -14,10 +14,16 @@ allOf: properties: compatible: - enum: - - nvidia,tegra114-cec - - nvidia,tegra124-cec - - nvidia,tegra210-cec + oneOf: + - enum: + - nvidia,tegra114-cec + - nvidia,tegra124-cec + - nvidia,tegra210-cec + - items: + - enum: + - nvidia,tegra186-cec + - nvidia,tegra194-cec + - const: nvidia,tegra210-cec clocks: maxItems: 1 -- cgit v1.2.3-59-g8ed1b From f83ac8d30c43fd902af7c84c480f216157b60ef0 Mon Sep 17 00:00:00 2001 From: Denis Arefev Date: Tue, 15 Apr 2025 11:27:21 +0300 Subject: media: vivid: Change the siize of the composing syzkaller found a bug: BUG: KASAN: vmalloc-out-of-bounds in tpg_fill_plane_pattern drivers/media/common/v4l2-tpg/v4l2-tpg-core.c:2608 [inline] BUG: KASAN: vmalloc-out-of-bounds in tpg_fill_plane_buffer+0x1a9c/0x5af0 drivers/media/common/v4l2-tpg/v4l2-tpg-core.c:2705 Write of size 1440 at addr ffffc9000d0ffda0 by task vivid-000-vid-c/5304 CPU: 0 UID: 0 PID: 5304 Comm: vivid-000-vid-c Not tainted 6.14.0-rc2-syzkaller-00039-g09fbf3d50205 #0 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2~bpo12+1 04/01/2014 Call Trace: __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x241/0x360 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0x169/0x550 mm/kasan/report.c:489 kasan_report+0x143/0x180 mm/kasan/report.c:602 kasan_check_range+0x282/0x290 mm/kasan/generic.c:189 __asan_memcpy+0x40/0x70 mm/kasan/shadow.c:106 tpg_fill_plane_pattern drivers/media/common/v4l2-tpg/v4l2-tpg-core.c:2608 [inline] tpg_fill_plane_buffer+0x1a9c/0x5af0 drivers/media/common/v4l2-tpg/v4l2-tpg-core.c:2705 vivid_fillbuff drivers/media/test-drivers/vivid/vivid-kthread-cap.c:470 [inline] vivid_thread_vid_cap_tick+0xf8e/0x60d0 drivers/media/test-drivers/vivid/vivid-kthread-cap.c:629 vivid_thread_vid_cap+0x8aa/0xf30 drivers/media/test-drivers/vivid/vivid-kthread-cap.c:767 kthread+0x7a9/0x920 kernel/kthread.c:464 ret_from_fork+0x4b/0x80 arch/x86/kernel/process.c:148 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:244 The composition size cannot be larger than the size of fmt_cap_rect. So execute v4l2_rect_map_inside() even if has_compose_cap == 0. Fixes: 94a7ad928346 ("media: vivid: fix compose size exceed boundary") Cc: stable@vger.kernel.org Reported-by: syzbot+365005005522b70a36f2@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?id=8ed8e8cc30cbe0d86c9a25bd1d6a5775129b8ea3 Signed-off-by: Denis Arefev Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vivid/vivid-vid-cap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index df726961222b..84e9155b5815 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -948,8 +948,8 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection if (dev->has_compose_cap) { v4l2_rect_set_min_size(compose, &min_rect); v4l2_rect_set_max_size(compose, &max_rect); - v4l2_rect_map_inside(compose, &fmt); } + v4l2_rect_map_inside(compose, &fmt); dev->fmt_cap_rect = fmt; tpg_s_buf_height(&dev->tpg, fmt.height); } else if (dev->has_compose_cap) { -- cgit v1.2.3-59-g8ed1b From 974a8ab3bf2f2760375fa78561f85d8726740012 Mon Sep 17 00:00:00 2001 From: Andrew Kreimer Date: Tue, 15 Apr 2025 15:38:55 +0300 Subject: media: dvb: Fix typos bloc -> block There are some typos in comments / dprintk messages: bloc -> block. Fix them via codespell. Signed-off-by: Andrew Kreimer Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/dib7000p.c | 4 ++-- drivers/media/dvb-frontends/dib8000.c | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index c5582d4fa5be..b40daf242046 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -2630,7 +2630,7 @@ static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode) dib7090_configMpegMux(state, 3, 1, 1); dib7090_setHostBusMux(state, MPEG_ON_HOSTBUS); } else {/* Use Smooth block */ - dprintk("setting output mode TS_SERIAL using Smooth bloc\n"); + dprintk("setting output mode TS_SERIAL using Smooth block\n"); dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS); outreg |= (2<<6) | (0 << 1); } @@ -2654,7 +2654,7 @@ static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode) outreg |= (1<<6); break; - case OUTMODE_MPEG2_FIFO: /* Using Smooth block because not supported by new Mpeg Mux bloc */ + case OUTMODE_MPEG2_FIFO: /* Using Smooth block because not supported by new Mpeg Mux block */ dprintk("setting output mode TS_FIFO using Smooth block\n"); dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS); outreg |= (5<<6); diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index cfe59c3255f7..d90f1b0b2051 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -1584,7 +1584,7 @@ static int dib8096p_set_output_mode(struct dvb_frontend *fe, int mode) dib8096p_configMpegMux(state, 3, 1, 1); dib8096p_setHostBusMux(state, MPEG_ON_HOSTBUS); } else {/* Use Smooth block */ - dprintk("dib8096P setting output mode TS_SERIAL using Smooth bloc\n"); + dprintk("dib8096P setting output mode TS_SERIAL using Smooth block\n"); dib8096p_setHostBusMux(state, DEMOUT_ON_HOSTBUS); outreg |= (2 << 6) | (0 << 1); @@ -1612,7 +1612,8 @@ static int dib8096p_set_output_mode(struct dvb_frontend *fe, int mode) case OUTMODE_MPEG2_FIFO: /* Using Smooth block because not supported - by new Mpeg Mux bloc */ + * by new Mpeg Mux block + */ dprintk("dib8096P setting output mode TS_FIFO using Smooth block\n"); dib8096p_setHostBusMux(state, DEMOUT_ON_HOSTBUS); outreg |= (5 << 6); -- cgit v1.2.3-59-g8ed1b From 024bf40edf1155e7a587f0ec46294049777d9b02 Mon Sep 17 00:00:00 2001 From: Dmitry Nikiforov Date: Wed, 16 Apr 2025 23:51:19 +0300 Subject: media: davinci: vpif: Fix memory leak in probe error path If an error occurs during the initialization of `pdev_display`, the allocated platform device `pdev_capture` is not released properly, leading to a memory leak. Adjust error path handling to fix the leak. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 43acb728bbc4 ("media: davinci: vpif: fix use-after-free on driver unbind") Cc: stable@vger.kernel.org Signed-off-by: Dmitry Nikiforov Reviewed-by: Johan Hovold Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/davinci/vpif.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/ti/davinci/vpif.c b/drivers/media/platform/ti/davinci/vpif.c index a81719702a22..969d623fc842 100644 --- a/drivers/media/platform/ti/davinci/vpif.c +++ b/drivers/media/platform/ti/davinci/vpif.c @@ -504,7 +504,7 @@ static int vpif_probe(struct platform_device *pdev) pdev_display = kzalloc(sizeof(*pdev_display), GFP_KERNEL); if (!pdev_display) { ret = -ENOMEM; - goto err_put_pdev_capture; + goto err_del_pdev_capture; } pdev_display->name = "vpif_display"; @@ -527,6 +527,8 @@ static int vpif_probe(struct platform_device *pdev) err_put_pdev_display: platform_device_put(pdev_display); +err_del_pdev_capture: + platform_device_del(pdev_capture); err_put_pdev_capture: platform_device_put(pdev_capture); err_put_rpm: -- cgit v1.2.3-59-g8ed1b From a93f42c77100c3ef03529cdc558ec93bc8f49871 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 17 Apr 2025 01:46:01 +0300 Subject: media: adv7511-v4l2: use constants for BT.2020 colorimetry Replace numeric values with constants from hdmi.h. Signed-off-by: Dmitry Baryshkov Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil --- drivers/media/i2c/adv7511-v4l2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index f95a99d85360..853c7806de92 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -1370,9 +1370,9 @@ static int adv7511_set_fmt(struct v4l2_subdev *sd, case V4L2_COLORSPACE_BT2020: c = HDMI_COLORIMETRY_EXTENDED; if (y && format->format.ycbcr_enc == V4L2_YCBCR_ENC_BT2020_CONST_LUM) - ec = 5; /* Not yet available in hdmi.h */ + ec = HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM; else - ec = 6; /* Not yet available in hdmi.h */ + ec = HDMI_EXTENDED_COLORIMETRY_BT2020; break; default: break; -- cgit v1.2.3-59-g8ed1b From bd9f6ce7d512fa21249415c16af801a4ed5d97b6 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 22 Apr 2025 10:13:45 +0800 Subject: media: platform: exynos4-is: Add hardware sync wait to fimc_is_hw_change_mode() In fimc_is_hw_change_mode(), the function changes camera modes without waiting for hardware completion, risking corrupted data or system hangs if subsequent operations proceed before the hardware is ready. Add fimc_is_hw_wait_intmsr0_intmsd0() after mode configuration, ensuring hardware state synchronization and stable interrupt handling. Signed-off-by: Wentao Liang Signed-off-by: Hans Verkuil --- drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c index 366e6393817d..5f9c44e825a5 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c @@ -164,6 +164,7 @@ int fimc_is_hw_change_mode(struct fimc_is *is) if (WARN_ON(is->config_index >= ARRAY_SIZE(cmd))) return -EINVAL; + fimc_is_hw_wait_intmsr0_intmsd0(is); mcuctl_write(cmd[is->config_index], is, MCUCTL_REG_ISSR(0)); mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); mcuctl_write(is->setfile.sub_index, is, MCUCTL_REG_ISSR(2)); -- cgit v1.2.3-59-g8ed1b From 398a1b33f1479af35ca915c5efc9b00d6204f8fa Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 22 Apr 2025 11:07:39 +0800 Subject: media: gspca: Add error handling for stv06xx_read_sensor() In hdcs_init(), the return value of stv06xx_read_sensor() needs to be checked. A proper implementation can be found in vv6410_dump(). Add a check in loop condition and propergate error code to fix this issue. Fixes: 4c98834addfe ("V4L/DVB (10048): gspca - stv06xx: New subdriver.") Cc: stable@vger.kernel.org # v2.6+ Signed-off-by: Wentao Liang Signed-off-by: Hans Verkuil --- drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c index 5a47dcbf1c8e..303b055fefea 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c @@ -520,12 +520,13 @@ static int hdcs_init(struct sd *sd) static int hdcs_dump(struct sd *sd) { u16 reg, val; + int err = 0; pr_info("Dumping sensor registers:\n"); - for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) { - stv06xx_read_sensor(sd, reg, &val); + for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH && !err; reg++) { + err = stv06xx_read_sensor(sd, reg, &val); pr_info("reg 0x%02x = 0x%02x\n", reg, val); } - return 0; + return (err < 0) ? err : 0; } -- cgit v1.2.3-59-g8ed1b From 6e40cc9b4b462bf13ca69621f417a8658b8e1783 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 28 Jan 2025 16:08:18 +0100 Subject: media: omap3isp: drop wait_prepare/finish callbacks Since commit 88785982a19d ("media: vb2: use lock if wait_prepare/finish are NULL") it is no longer needed to set the wait_prepare/finish vb2_ops callbacks as long as the lock field in vb2_queue is set. Set the queue lock to &video->queue_lock, which makes it possible to drop the wait_prepare/finish callbacks. This simplifies the code and this is a step towards the goal of deleting these callbacks. Note that the lock field of struct video_device is never set in this driver, so the core v4l2_ioctl.c function v4l2_ioctl_get_lock() will never take a lock. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti/omap3isp/ispvideo.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/drivers/media/platform/ti/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c index 5c9aa80023fd..78e30298c7ad 100644 --- a/drivers/media/platform/ti/omap3isp/ispvideo.c +++ b/drivers/media/platform/ti/omap3isp/ispvideo.c @@ -480,29 +480,11 @@ static int isp_video_start_streaming(struct vb2_queue *queue, return 0; } -static void omap3isp_wait_prepare(struct vb2_queue *vq) -{ - struct isp_video_fh *vfh = vb2_get_drv_priv(vq); - struct isp_video *video = vfh->video; - - mutex_unlock(&video->queue_lock); -} - -static void omap3isp_wait_finish(struct vb2_queue *vq) -{ - struct isp_video_fh *vfh = vb2_get_drv_priv(vq); - struct isp_video *video = vfh->video; - - mutex_lock(&video->queue_lock); -} - static const struct vb2_ops isp_video_queue_ops = { .queue_setup = isp_video_queue_setup, .buf_prepare = isp_video_buffer_prepare, .buf_queue = isp_video_buffer_queue, .start_streaming = isp_video_start_streaming, - .wait_prepare = omap3isp_wait_prepare, - .wait_finish = omap3isp_wait_finish, }; /* @@ -1338,6 +1320,7 @@ static int isp_video_open(struct file *file) queue->buf_struct_size = sizeof(struct isp_buffer); queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; queue->dev = video->isp->dev; + queue->lock = &video->queue_lock; ret = vb2_queue_init(&handle->queue); if (ret < 0) { -- cgit v1.2.3-59-g8ed1b From 992f05c53567e6c20416adf63f6b74678f4763fa Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 4 Mar 2025 12:08:09 +0100 Subject: media: staging: atomisp/starfive: use (t,l)/wxh format for rectangle Standardize reporting of rectangles to (t,l)/wxh. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_cmd.c | 4 ++-- drivers/staging/media/starfive/camss/stf-isp.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index 02ccf80e6559..8feb627ddcca 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -3742,8 +3742,8 @@ static int atomisp_set_sensor_crop_and_fmt(struct atomisp_device *isp, ret = v4l2_subdev_call(input->camera, pad, set_selection, sd_state, &sel); if (ret) - dev_err(isp->dev, "Error setting crop to %ux%u @%ux%u: %d\n", - sel.r.width, sel.r.height, sel.r.left, sel.r.top, ret); + dev_err(isp->dev, "Error setting crop to (%d,%d)/%ux%u: %d\n", + sel.r.left, sel.r.top, sel.r.width, sel.r.height, ret); set_fmt: if (ret == 0) diff --git a/drivers/staging/media/starfive/camss/stf-isp.c b/drivers/staging/media/starfive/camss/stf-isp.c index 4e6e26736852..df7a903fbb1b 100644 --- a/drivers/staging/media/starfive/camss/stf-isp.c +++ b/drivers/staging/media/starfive/camss/stf-isp.c @@ -278,7 +278,7 @@ static int isp_set_selection(struct v4l2_subdev *sd, isp_set_format(sd, state, &fmt); } - dev_dbg(isp_dev->stfcamss->dev, "pad: %d sel(%d,%d)/%dx%d\n", + dev_dbg(isp_dev->stfcamss->dev, "pad: %d sel(%d,%d)/%ux%u\n", sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); return 0; -- cgit v1.2.3-59-g8ed1b From d1bbab01cc6dec2096d6438463620e6f1b51e8b6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 4 Mar 2025 12:08:10 +0100 Subject: media: usb: em28xx: use (t,l)/wxh format for rectangle Standardize reporting of rectangles to (t,l)/wxh. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 66c09bc6d59e..2dfa3242a7ab 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -264,7 +264,7 @@ static void em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, u8 overflow = (height >> 9 & 0x02) | (width >> 10 & 0x01); /* NOTE: size limit: 2047x1023 = 2MPix */ - em28xx_videodbg("capture area set to (%d,%d): %dx%d\n", + em28xx_videodbg("capture area set to (%u,%u)/%ux%u\n", hstart, vstart, ((overflow & 2) << 9 | cwidth << 2), ((overflow & 1) << 10 | cheight << 2)); -- cgit v1.2.3-59-g8ed1b From ddd91a1ec3655775665ed748a8c89f4e8a28a4c1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 4 Mar 2025 12:08:11 +0100 Subject: media: vivid: use (t,l)/wxh format for rectangle Standardize reporting of rectangles to (t,l)/wxh. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vivid/vivid-kthread-cap.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-kthread-cap.c b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c index 273e8ed8c2a9..d845e1644649 100644 --- a/drivers/media/test-drivers/vivid/vivid-kthread-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c @@ -165,13 +165,13 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev, struct vivid_dev *ou v4l2_rect_scale(&dev->loop_vid_cap, &dev->crop_cap, &dev->compose_cap); dprintk(dev, 1, - "loop_vid_copy: %dx%d@%dx%d loop_vid_out: %dx%d@%dx%d loop_vid_cap: %dx%d@%dx%d\n", - dev->loop_vid_copy.width, dev->loop_vid_copy.height, + "loop_vid_copy: (%d,%d)/%ux%u loop_vid_out: (%d,%d)/%ux%u loop_vid_cap: (%d,%d)/%ux%u\n", dev->loop_vid_copy.left, dev->loop_vid_copy.top, - dev->loop_vid_out.width, dev->loop_vid_out.height, + dev->loop_vid_copy.width, dev->loop_vid_copy.height, dev->loop_vid_out.left, dev->loop_vid_out.top, - dev->loop_vid_cap.width, dev->loop_vid_cap.height, - dev->loop_vid_cap.left, dev->loop_vid_cap.top); + dev->loop_vid_out.width, dev->loop_vid_out.height, + dev->loop_vid_cap.left, dev->loop_vid_cap.top, + dev->loop_vid_cap.width, dev->loop_vid_cap.height); v4l2_rect_intersect(&r_overlay, &r_fb, &r_overlay); @@ -190,13 +190,13 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev, struct vivid_dev *ou v4l2_rect_scale(&dev->loop_vid_overlay_cap, &dev->crop_cap, &dev->compose_cap); dprintk(dev, 1, - "loop_fb_copy: %dx%d@%dx%d loop_vid_overlay: %dx%d@%dx%d loop_vid_overlay_cap: %dx%d@%dx%d\n", - dev->loop_fb_copy.width, dev->loop_fb_copy.height, + "loop_fb_copy: (%d,%d)/%ux%u loop_vid_overlay: (%d,%d)/%ux%u loop_vid_overlay_cap: (%d,%d)/%ux%u\n", dev->loop_fb_copy.left, dev->loop_fb_copy.top, - dev->loop_vid_overlay.width, dev->loop_vid_overlay.height, + dev->loop_fb_copy.width, dev->loop_fb_copy.height, dev->loop_vid_overlay.left, dev->loop_vid_overlay.top, - dev->loop_vid_overlay_cap.width, dev->loop_vid_overlay_cap.height, - dev->loop_vid_overlay_cap.left, dev->loop_vid_overlay_cap.top); + dev->loop_vid_overlay.width, dev->loop_vid_overlay.height, + dev->loop_vid_overlay_cap.left, dev->loop_vid_overlay_cap.top, + dev->loop_vid_overlay_cap.width, dev->loop_vid_overlay_cap.height); } static void *plane_vaddr(struct tpg_data *tpg, struct vivid_buffer *buf, -- cgit v1.2.3-59-g8ed1b From ddbb23e9eab2964e708a53e8fb433c6e1af3424d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 4 Mar 2025 12:08:12 +0100 Subject: media: i2c: imx283: use (t,l)/wxh format for rectangle Standardize reporting of rectangles to (t,l)/wxh. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx283.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx283.c b/drivers/media/i2c/imx283.c index beb9169f93ad..da618c8cbadc 100644 --- a/drivers/media/i2c/imx283.c +++ b/drivers/media/i2c/imx283.c @@ -1082,7 +1082,7 @@ static int imx283_start_streaming(struct imx283 *imx283, cci_write(imx283->cci, IMX283_REG_SVR, 0x00, &ret); dev_dbg(imx283->dev, "Mode: Size %d x %d\n", mode->width, mode->height); - dev_dbg(imx283->dev, "Analogue Crop (in the mode) %d,%d %dx%d\n", + dev_dbg(imx283->dev, "Analogue Crop (in the mode) (%d,%d)/%ux%u\n", mode->crop.left, mode->crop.top, mode->crop.width, -- cgit v1.2.3-59-g8ed1b From 84d4e40b257fde40eafa16727534c5d22f50193d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 4 Mar 2025 12:08:13 +0100 Subject: media: pci: zoran: use (t,l)/wxh format for rectangle Standardize reporting of rectangles to (t,l)/wxh. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/zoran/zoran_card.c | 2 +- drivers/media/pci/zoran/zr36016.c | 2 +- drivers/media/pci/zoran/zr36050.c | 2 +- drivers/media/pci/zoran/zr36060.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c index 3975fc1b2ee3..e31f9f19a48a 100644 --- a/drivers/media/pci/zoran/zoran_card.c +++ b/drivers/media/pci/zoran/zoran_card.c @@ -1202,7 +1202,7 @@ static int zoran_debugfs_show(struct seq_file *seq, void *v) seq_printf(seq, "JPG ver_dcm %u\n", zr->jpg_settings.ver_dcm); seq_printf(seq, "JPG tmp_dcm %u\n", zr->jpg_settings.tmp_dcm); seq_printf(seq, "JPG odd_even %u\n", zr->jpg_settings.odd_even); - seq_printf(seq, "JPG crop %dx%d %d %d\n", + seq_printf(seq, "JPG crop (%d,%d)/%dx%d\n", zr->jpg_settings.img_x, zr->jpg_settings.img_y, zr->jpg_settings.img_width, diff --git a/drivers/media/pci/zoran/zr36016.c b/drivers/media/pci/zoran/zr36016.c index 4b328ad6083f..d2e136c48a1b 100644 --- a/drivers/media/pci/zoran/zr36016.c +++ b/drivers/media/pci/zoran/zr36016.c @@ -216,7 +216,7 @@ static int zr36016_set_video(struct videocodec *codec, const struct tvnorm *norm struct zr36016 *ptr = (struct zr36016 *)codec->data; struct zoran *zr = videocodec_to_zoran(codec); - zrdev_dbg(zr, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n", + zrdev_dbg(zr, "%s: set_video %d.%d, (%u,%u)/%ux%u (0x%x) call\n", ptr->name, norm->h_start, norm->v_start, cap->x, cap->y, cap->width, cap->height, cap->decimation); diff --git a/drivers/media/pci/zoran/zr36050.c b/drivers/media/pci/zoran/zr36050.c index b07d7e5c1b4a..c17965073557 100644 --- a/drivers/media/pci/zoran/zr36050.c +++ b/drivers/media/pci/zoran/zr36050.c @@ -547,7 +547,7 @@ static int zr36050_set_video(struct videocodec *codec, const struct tvnorm *norm struct zoran *zr = videocodec_to_zoran(codec); int size; - zrdev_dbg(zr, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) q%d call\n", + zrdev_dbg(zr, "%s: set_video %d.%d, (%u,%u)/%ux%u (0x%x) q%d call\n", ptr->name, norm->h_start, norm->v_start, cap->x, cap->y, cap->width, cap->height, cap->decimation, cap->quality); diff --git a/drivers/media/pci/zoran/zr36060.c b/drivers/media/pci/zoran/zr36060.c index 75fd167603dc..d6c12efc5bb6 100644 --- a/drivers/media/pci/zoran/zr36060.c +++ b/drivers/media/pci/zoran/zr36060.c @@ -488,7 +488,7 @@ static int zr36060_set_video(struct videocodec *codec, const struct tvnorm *norm u32 reg; int size; - zrdev_dbg(zr, "%s: set_video %d/%d-%dx%d (%%%d) call\n", ptr->name, + zrdev_dbg(zr, "%s: set_video (%u,%u)/%ux%u (%%%d) call\n", ptr->name, cap->x, cap->y, cap->width, cap->height, cap->decimation); /* if () return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 80a9da00b999561882d8ec3bda0a131476628a9a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 4 Mar 2025 12:08:14 +0100 Subject: media: platform: use (t,l)/wxh format for rectangle Standardize reporting of rectangles to (t,l)/wxh. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c | 2 +- drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c | 4 ++-- drivers/media/platform/nxp/dw100/dw100.c | 8 ++++---- drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c | 8 ++++---- drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c | 2 +- drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c | 2 +- drivers/media/platform/samsung/exynos4-is/fimc-capture.c | 6 +++--- drivers/media/platform/samsung/exynos4-is/fimc-lite.c | 8 ++++---- drivers/media/platform/samsung/s3c-camif/camif-capture.c | 12 ++++++------ drivers/media/platform/st/sti/bdisp/bdisp-debug.c | 8 ++++---- drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c | 14 +++++++------- drivers/media/platform/st/sti/delta/delta-debug.c | 8 ++++---- drivers/media/platform/st/stm32/stm32-dcmi.c | 10 +++++----- .../media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c | 10 +++++----- drivers/media/platform/ti/am437x/am437x-vpfe.c | 2 +- 15 files changed, 52 insertions(+), 52 deletions(-) diff --git a/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c index 28c998bd3a81..d0fd77dcf8e2 100644 --- a/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c @@ -342,7 +342,7 @@ static int mtk_mdp_try_crop(struct mtk_mdp_ctx *ctx, u32 type, if (r->left & 1) r->left -= 1; - mtk_mdp_dbg(2, "[%d] crop l,t,w,h:%d,%d,%d,%d, max:%dx%d", ctx->id, + mtk_mdp_dbg(2, "[%d] crop (%d,%d)/%ux%u, max:%dx%d", ctx->id, r->left, r->top, r->width, r->height, max_w, max_h); return 0; diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c index 657356f87743..644b223b2877 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c @@ -236,7 +236,7 @@ int mdp_try_crop(struct mdp_m2m_ctx *ctx, struct v4l2_rect *r, u32 framew, frameh, walign, halign; int ret; - dev_dbg(dev, "%d target:%d, set:(%d,%d) %ux%u", ctx->id, + dev_dbg(dev, "%d target:%d, set:(%d,%d)/%ux%u", ctx->id, s->target, s->r.left, s->r.top, s->r.width, s->r.height); left = s->r.left; @@ -275,7 +275,7 @@ int mdp_try_crop(struct mdp_m2m_ctx *ctx, struct v4l2_rect *r, r->width = right - left; r->height = bottom - top; - dev_dbg(dev, "%d crop:(%d,%d) %ux%u", ctx->id, + dev_dbg(dev, "%d crop:(%d,%d)/%ux%u", ctx->id, r->left, r->top, r->width, r->height); return 0; } diff --git a/drivers/media/platform/nxp/dw100/dw100.c b/drivers/media/platform/nxp/dw100/dw100.c index 66582e7f92fc..3d1db1121bf9 100644 --- a/drivers/media/platform/nxp/dw100/dw100.c +++ b/drivers/media/platform/nxp/dw100/dw100.c @@ -961,9 +961,9 @@ static int dw100_s_selection(struct file *file, void *fh, src_q_data = dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); dev_dbg(&ctx->dw_dev->pdev->dev, - ">>> Buffer Type: %u Target: %u Rect: %ux%u@%d.%d\n", + ">>> Buffer Type: %u Target: %u Rect: (%d,%d)/%ux%u\n", sel->type, sel->target, - sel->r.width, sel->r.height, sel->r.left, sel->r.top); + sel->r.left, sel->r.top, sel->r.width, sel->r.height); switch (sel->target) { case V4L2_SEL_TGT_CROP: @@ -1025,9 +1025,9 @@ static int dw100_s_selection(struct file *file, void *fh, } dev_dbg(&ctx->dw_dev->pdev->dev, - "<<< Buffer Type: %u Target: %u Rect: %ux%u@%d.%d\n", + "<<< Buffer Type: %u Target: %u Rect: (%d,%d)/%ux%u\n", sel->type, sel->target, - sel->r.width, sel->r.height, sel->r.left, sel->r.top); + sel->r.left, sel->r.top, sel->r.width, sel->r.height); return 0; } diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c index a5763f1c5784..ade007a9811f 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c @@ -584,8 +584,8 @@ static int rvin_s_selection(struct file *file, void *fh, vin->crop = s->r = r; - vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n", - r.width, r.height, r.left, r.top, + vin_dbg(vin, "Cropped (%d,%d)/%ux%u of %dx%d\n", + r.left, r.top, r.width, r.height, max_rect.width, max_rect.height); break; case V4L2_SEL_TGT_COMPOSE: @@ -609,8 +609,8 @@ static int rvin_s_selection(struct file *file, void *fh, vin->compose = s->r = r; - vin_dbg(vin, "Compose %dx%d@%d:%d in %dx%d\n", - r.width, r.height, r.left, r.top, + vin_dbg(vin, "Compose (%d,%d)/%ux%u in %dx%d\n", + r.left, r.top, r.width, r.height, vin->format.width, vin->format.height); break; default: diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index d94917211828..8c29a1c9309a 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -881,7 +881,7 @@ static int rkisp1_isp_set_selection(struct v4l2_subdev *sd, if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - dev_dbg(isp->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, + dev_dbg(isp->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%ux%u\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index f073e72a0d37..8e6b753d3081 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -600,7 +600,7 @@ static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd, if (sel->target != V4L2_SEL_TGT_CROP || sel->pad == RKISP1_RSZ_PAD_SRC) return -EINVAL; - dev_dbg(rsz->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, + dev_dbg(rsz->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%ux%u\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); rkisp1_rsz_set_sink_crop(rsz, sd_state, &sel->r); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c index c3c2e474a18a..5b412afd7d60 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c @@ -700,7 +700,7 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, r->top = clamp_t(u32, r->top, 0, sink->f_height - r->height); r->left = round_down(r->left, var->hor_offs_align); - dbg("target %#x: (%d,%d)/%dx%d, sink fmt: %dx%d", + dbg("target %#x: (%d,%d)/%ux%u, sink fmt: %dx%d", target, r->left, r->top, r->width, r->height, sink->f_width, sink->f_height); } @@ -1622,7 +1622,7 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, r->height = f->height; } - dbg("target %#x: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d", + dbg("target %#x: (%d,%d)/%ux%u, f_w: %d, f_h: %d", sel->pad, r->left, r->top, r->width, r->height, f->f_width, f->f_height); @@ -1671,7 +1671,7 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, spin_unlock_irqrestore(&fimc->slock, flags); } - dbg("target %#x: (%d,%d)/%dx%d", sel->target, r->left, r->top, + dbg("target %#x: (%d,%d)/%ux%u", sel->target, r->left, r->top, r->width, r->height); mutex_unlock(&fimc->lock); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c index f23e51e3da2f..0ce293b0718b 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c @@ -611,7 +611,7 @@ static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r) r->left = round_down(r->left, fimc->dd->win_hor_offs_align); r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height); - v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d\n", + v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%ux%u, sink fmt: %dx%d\n", r->left, r->top, r->width, r->height, frame->f_width, frame->f_height); } @@ -631,7 +631,7 @@ static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r) r->left = round_down(r->left, fimc->dd->out_hor_offs_align); r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height); - v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d\n", + v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%ux%u, source fmt: %dx%d\n", r->left, r->top, r->width, r->height, frame->f_width, frame->f_height); } @@ -1140,7 +1140,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, } mutex_unlock(&fimc->lock); - v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n", + v4l2_dbg(1, debug, sd, "%s: (%d,%d)/%ux%u, f_w: %d, f_h: %d\n", __func__, f->rect.left, f->rect.top, f->rect.width, f->rect.height, f->f_width, f->f_height); @@ -1174,7 +1174,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, } mutex_unlock(&fimc->lock); - v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n", + v4l2_dbg(1, debug, sd, "%s: (%d,%d)/%ux%u, f_w: %d, f_h: %d\n", __func__, f->rect.left, f->rect.top, f->rect.width, f->rect.height, f->f_width, f->f_height); diff --git a/drivers/media/platform/samsung/s3c-camif/camif-capture.c b/drivers/media/platform/samsung/s3c-camif/camif-capture.c index bd1149e8abc2..3e566b65f417 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-capture.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-capture.c @@ -1030,9 +1030,9 @@ static int s3c_camif_s_selection(struct file *file, void *priv, vp->state |= ST_VP_CONFIG; spin_unlock_irqrestore(&camif->slock, flags); - pr_debug("type: %#x, target: %#x, flags: %#x, (%d,%d)/%dx%d\n", - sel->type, sel->target, sel->flags, - sel->r.left, sel->r.top, sel->r.width, sel->r.height); + pr_debug("type: %#x, target: %#x, flags: %#x, (%d,%d)/%ux%u\n", + sel->type, sel->target, sel->flags, + sel->r.left, sel->r.top, sel->r.width, sel->r.height); return 0; } @@ -1372,7 +1372,7 @@ static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd, mutex_unlock(&camif->lock); - v4l2_dbg(1, debug, sd, "%s: crop: (%d,%d) %dx%d, size: %ux%u\n", + v4l2_dbg(1, debug, sd, "%s: crop: (%d,%d)/%ux%u, size: %ux%u\n", __func__, crop->left, crop->top, crop->width, crop->height, mf->width, mf->height); @@ -1424,7 +1424,7 @@ static void __camif_try_crop(struct camif_dev *camif, struct v4l2_rect *r) } } - v4l2_dbg(1, debug, &camif->v4l2_dev, "crop: (%d,%d)/%dx%d, fmt: %ux%u\n", + v4l2_dbg(1, debug, &camif->v4l2_dev, "crop: (%d,%d)/%ux%u, fmt: %ux%u\n", r->left, r->top, r->width, r->height, mf->width, mf->height); } @@ -1464,7 +1464,7 @@ static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd, } mutex_unlock(&camif->lock); - v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %u, f_h: %u\n", + v4l2_dbg(1, debug, sd, "%s: (%d,%d)/%ux%u, f_w: %u, f_h: %u\n", __func__, crop->left, crop->top, crop->width, crop->height, camif->mbus_fmt.width, camif->mbus_fmt.height); diff --git a/drivers/media/platform/st/sti/bdisp/bdisp-debug.c b/drivers/media/platform/st/sti/bdisp/bdisp-debug.c index a27f638df11c..f9348aeacc11 100644 --- a/drivers/media/platform/st/sti/bdisp/bdisp-debug.c +++ b/drivers/media/platform/st/sti/bdisp/bdisp-debug.c @@ -455,11 +455,11 @@ static int last_request_show(struct seq_file *s, void *data) seq_printf(s, "Format: %s\t\t\t%s\n", bdisp_fmt_to_str(src), bdisp_fmt_to_str(dst)); - seq_printf(s, "Crop area: %dx%d @ %d,%d ==>\t%dx%d @ %d,%d\n", - src.crop.width, src.crop.height, + seq_printf(s, "Crop area: (%d,%d)/%ux%u ==>\t(%d,%d)/%ux%u\n", src.crop.left, src.crop.top, - dst.crop.width, dst.crop.height, - dst.crop.left, dst.crop.top); + src.crop.width, src.crop.height, + dst.crop.left, dst.crop.top, + dst.crop.width, dst.crop.height); seq_printf(s, "Buff size: %dx%d\t\t%dx%d\n\n", src.width, src.height, dst.width, dst.height); diff --git a/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c index 73ad66ed20f2..1eb934490c0b 100644 --- a/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c @@ -953,8 +953,8 @@ static int bdisp_s_selection(struct file *file, void *fh, if ((out.left < 0) || (out.left >= frame->width) || (out.top < 0) || (out.top >= frame->height)) { dev_err(ctx->bdisp_dev->dev, - "Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n", - out.width, out.height, out.left, out.top, + "Invalid crop: (%d,%d)/%ux%u vs frame: %dx%d\n", + out.left, out.top, out.width, out.height, frame->width, frame->height); return -EINVAL; } @@ -966,8 +966,8 @@ static int bdisp_s_selection(struct file *file, void *fh, if (((out.left + out.width) > frame->width) || ((out.top + out.height) > frame->height)) { dev_err(ctx->bdisp_dev->dev, - "Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n", - out.width, out.height, out.left, out.top, + "Invalid crop: (%d,%d)/%ux%u vs frame: %dx%d\n", + out.left, out.top, out.width, out.height, frame->width, frame->height); return -EINVAL; } @@ -982,9 +982,9 @@ static int bdisp_s_selection(struct file *file, void *fh, if ((out.left != in->left) || (out.top != in->top) || (out.width != in->width) || (out.height != in->height)) { dev_dbg(ctx->bdisp_dev->dev, - "%s crop updated: %dx%d@(%d,%d) -> %dx%d@(%d,%d)\n", - __func__, in->width, in->height, in->left, in->top, - out.width, out.height, out.left, out.top); + "%s crop updated: (%d,%d)/%ux%u -> (%d,%d)/%ux%u\n", + __func__, in->left, in->top, in->width, in->height, + out.left, out.top, out.width, out.height); *in = out; } diff --git a/drivers/media/platform/st/sti/delta/delta-debug.c b/drivers/media/platform/st/sti/delta/delta-debug.c index 4b2eb6b63aa2..6acf46913cda 100644 --- a/drivers/media/platform/st/sti/delta/delta-debug.c +++ b/drivers/media/platform/st/sti/delta/delta-debug.c @@ -16,14 +16,14 @@ char *delta_streaminfo_str(struct delta_streaminfo *s, char *str, return NULL; snprintf(str, len, - "%4.4s %dx%d %s %s dpb=%d %s %s %s%dx%d@(%d,%d) %s%d/%d", + "%4.4s %dx%d %s %s dpb=%d %s %s %s(%d,%d)/%ux%u %s%d/%d", (char *)&s->streamformat, s->width, s->height, s->profile, s->level, s->dpb, (s->field == V4L2_FIELD_NONE) ? "progressive" : "interlaced", s->other, s->flags & DELTA_STREAMINFO_FLAG_CROP ? "crop=" : "", - s->crop.width, s->crop.height, s->crop.left, s->crop.top, + s->crop.width, s->crop.height, s->flags & DELTA_STREAMINFO_FLAG_PIXELASPECT ? "par=" : "", s->pixelaspect.numerator, s->pixelaspect.denominator); @@ -38,13 +38,13 @@ char *delta_frameinfo_str(struct delta_frameinfo *f, char *str, return NULL; snprintf(str, len, - "%4.4s %dx%d aligned %dx%d %s %s%dx%d@(%d,%d) %s%d/%d", + "%4.4s %dx%d aligned %dx%d %s %s(%d,%d)/%ux%u %s%d/%d", (char *)&f->pixelformat, f->width, f->height, f->aligned_width, f->aligned_height, (f->field == V4L2_FIELD_NONE) ? "progressive" : "interlaced", f->flags & DELTA_STREAMINFO_FLAG_CROP ? "crop=" : "", - f->crop.width, f->crop.height, f->crop.left, f->crop.top, + f->crop.width, f->crop.height, f->flags & DELTA_STREAMINFO_FLAG_PIXELASPECT ? "par=" : "", f->pixelaspect.numerator, f->pixelaspect.denominator); diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index ba77062357eb..d94c61b8569d 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -388,9 +388,9 @@ static void dcmi_set_crop(struct stm32_dcmi *dcmi) ((dcmi->crop.left << 1)); reg_write(dcmi->regs, DCMI_CWSTRT, start); - dev_dbg(dcmi->dev, "Cropping to %ux%u@%u:%u\n", - dcmi->crop.width, dcmi->crop.height, - dcmi->crop.left, dcmi->crop.top); + dev_dbg(dcmi->dev, "Cropping to (%d,%d)/%ux%u\n", + dcmi->crop.left, dcmi->crop.top, + dcmi->crop.width, dcmi->crop.height); /* Enable crop */ reg_set(dcmi->regs, DCMI_CR, CR_CROP); @@ -1292,8 +1292,8 @@ static int dcmi_s_selection(struct file *file, void *priv, /* Crop if request is different than sensor resolution */ dcmi->do_crop = true; dcmi->crop = r; - dev_dbg(dcmi->dev, "s_selection: crop %ux%u@(%u,%u) from %ux%u\n", - r.width, r.height, r.left, r.top, + dev_dbg(dcmi->dev, "s_selection: crop (%d,%d)/%ux%u from %ux%u\n", + r.left, r.top, r.width, r.height, pix.width, pix.height); } else { /* Disable crop */ diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c index 3c742a546441..db76a02a1848 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c @@ -373,8 +373,8 @@ static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd, mf->width = s->r.width; mf->height = s->r.height; - dev_dbg(byteproc->dev, "s_selection: crop %ux%u@(%u,%u)\n", - crop->width, crop->height, crop->left, crop->top); + dev_dbg(byteproc->dev, "s_selection: crop (%d,%d)/%ux%u\n", + crop->left, crop->top, crop->width, crop->height); break; case V4L2_SEL_TGT_COMPOSE: mf = v4l2_subdev_state_get_format(sd_state, 0); @@ -386,9 +386,9 @@ static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd, mf->width = s->r.width; mf->height = s->r.height; - dev_dbg(byteproc->dev, "s_selection: compose %ux%u@(%u,%u)\n", - compose->width, compose->height, - compose->left, compose->top); + dev_dbg(byteproc->dev, "s_selection: compose (%d,%d)/%ux%u\n", + compose->left, compose->top, + compose->width, compose->height); break; default: return -EINVAL; diff --git a/drivers/media/platform/ti/am437x/am437x-vpfe.c b/drivers/media/platform/ti/am437x/am437x-vpfe.c index 44cdccb89377..1ca559df7e59 100644 --- a/drivers/media/platform/ti/am437x/am437x-vpfe.c +++ b/drivers/media/platform/ti/am437x/am437x-vpfe.c @@ -2030,7 +2030,7 @@ vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) vpfe->fmt.fmt.pix.sizeimage = vpfe->fmt.fmt.pix.bytesperline * vpfe->fmt.fmt.pix.height; - vpfe_dbg(1, vpfe, "cropped (%d,%d)/%dx%d of %dx%d\n", + vpfe_dbg(1, vpfe, "cropped (%d,%d)/%ux%u of %dx%d\n", r.left, r.top, r.width, r.height, cr.width, cr.height); return 0; -- cgit v1.2.3-59-g8ed1b From 6829c5b5d26b1be31880d74ec24cb32d2d75f1ae Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 1 Apr 2025 11:54:17 +0200 Subject: media: tc358743: ignore video while HPD is low If the HPD is low (happens if there is no EDID or the EDID is being updated), then return -ENOLINK in tc358743_get_detected_timings() instead of detecting video. This avoids userspace thinking that it can start streaming when the HPD is low. Signed-off-by: Hans Verkuil Tested-by: Maxime Ripard Link: https://lore.kernel.org/linux-media/20240628-stoic-bettong-of-fortitude-e25611@houat/ Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 2d5f42f11158..dcef93e1a3bc 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -313,6 +313,10 @@ static int tc358743_get_detected_timings(struct v4l2_subdev *sd, memset(timings, 0, sizeof(struct v4l2_dv_timings)); + /* if HPD is low, ignore any video */ + if (!(i2c_rd8(sd, HPD_CTL) & MASK_HPD_OUT0)) + return -ENOLINK; + if (no_signal(sd)) { v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); return -ENOLINK; -- cgit v1.2.3-59-g8ed1b From d12ddda5239826ca6978eaadea1b9280762c830a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 10 Apr 2025 12:00:49 +0200 Subject: media: uapi: cec-funcs.h: use CEC_LOG_ADDR_BROADCAST The cec-funcs.h header sets the destination to 0xf for those messages that can only be broadcast. Instead of writing: msg->msg[0] |= 0xf; /* broadcast */ just write: msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; which is more descriptive and allows us to drop the comment. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/cec-funcs.h | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/include/uapi/linux/cec-funcs.h b/include/uapi/linux/cec-funcs.h index d58fa1cdcb08..189ecf0e13cd 100644 --- a/include/uapi/linux/cec-funcs.h +++ b/include/uapi/linux/cec-funcs.h @@ -14,7 +14,7 @@ static inline void cec_msg_active_source(struct cec_msg *msg, __u16 phys_addr) { msg->len = 4; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_ACTIVE_SOURCE; msg->msg[2] = phys_addr >> 8; msg->msg[3] = phys_addr & 0xff; @@ -59,7 +59,7 @@ static inline void cec_msg_request_active_source(struct cec_msg *msg, int reply) { msg->len = 2; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_REQUEST_ACTIVE_SOURCE; msg->reply = reply ? CEC_MSG_ACTIVE_SOURCE : 0; } @@ -68,7 +68,7 @@ static inline void cec_msg_routing_information(struct cec_msg *msg, __u16 phys_addr) { msg->len = 4; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_ROUTING_INFORMATION; msg->msg[2] = phys_addr >> 8; msg->msg[3] = phys_addr & 0xff; @@ -86,7 +86,7 @@ static inline void cec_msg_routing_change(struct cec_msg *msg, __u16 new_phys_addr) { msg->len = 6; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_ROUTING_CHANGE; msg->msg[2] = orig_phys_addr >> 8; msg->msg[3] = orig_phys_addr & 0xff; @@ -106,7 +106,7 @@ static inline void cec_ops_routing_change(const struct cec_msg *msg, static inline void cec_msg_set_stream_path(struct cec_msg *msg, __u16 phys_addr) { msg->len = 4; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_SET_STREAM_PATH; msg->msg[2] = phys_addr >> 8; msg->msg[3] = phys_addr & 0xff; @@ -791,7 +791,7 @@ static inline void cec_msg_report_physical_addr(struct cec_msg *msg, __u16 phys_addr, __u8 prim_devtype) { msg->len = 5; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_REPORT_PHYSICAL_ADDR; msg->msg[2] = phys_addr >> 8; msg->msg[3] = phys_addr & 0xff; @@ -817,7 +817,7 @@ static inline void cec_msg_set_menu_language(struct cec_msg *msg, const char *language) { msg->len = 5; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_SET_MENU_LANGUAGE; memcpy(msg->msg + 2, language, 3); } @@ -850,7 +850,7 @@ static inline void cec_msg_report_features(struct cec_msg *msg, __u8 rc_profile, __u8 dev_features) { msg->len = 6; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_REPORT_FEATURES; msg->msg[2] = cec_version; msg->msg[3] = all_device_types; @@ -1092,7 +1092,7 @@ static inline void cec_msg_tuner_step_increment(struct cec_msg *msg) static inline void cec_msg_device_vendor_id(struct cec_msg *msg, __u32 vendor_id) { msg->len = 5; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_DEVICE_VENDOR_ID; msg->msg[2] = vendor_id >> 16; msg->msg[3] = (vendor_id >> 8) & 0xff; @@ -1655,7 +1655,7 @@ static inline void cec_msg_report_current_latency(struct cec_msg *msg, __u8 audio_out_delay) { msg->len = 6; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_REPORT_CURRENT_LATENCY; msg->msg[2] = phys_addr >> 8; msg->msg[3] = phys_addr & 0xff; @@ -1687,7 +1687,7 @@ static inline void cec_msg_request_current_latency(struct cec_msg *msg, __u16 phys_addr) { msg->len = 4; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_REQUEST_CURRENT_LATENCY; msg->msg[2] = phys_addr >> 8; msg->msg[3] = phys_addr & 0xff; @@ -1707,7 +1707,7 @@ static inline void cec_msg_cdc_hec_inquire_state(struct cec_msg *msg, __u16 phys_addr2) { msg->len = 9; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_CDC_MESSAGE; /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ msg->msg[4] = CEC_MSG_CDC_HEC_INQUIRE_STATE; @@ -1737,7 +1737,7 @@ static inline void cec_msg_cdc_hec_report_state(struct cec_msg *msg, __u16 hec_field) { msg->len = has_field ? 10 : 8; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_CDC_MESSAGE; /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ msg->msg[4] = CEC_MSG_CDC_HEC_REPORT_STATE; @@ -1782,7 +1782,7 @@ static inline void cec_msg_cdc_hec_set_state(struct cec_msg *msg, __u16 phys_addr5) { msg->len = 10; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_CDC_MESSAGE; /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ msg->msg[4] = CEC_MSG_CDC_HEC_INQUIRE_STATE; @@ -1832,7 +1832,7 @@ static inline void cec_msg_cdc_hec_set_state_adjacent(struct cec_msg *msg, __u8 hec_set_state) { msg->len = 8; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_CDC_MESSAGE; /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ msg->msg[4] = CEC_MSG_CDC_HEC_SET_STATE_ADJACENT; @@ -1857,7 +1857,7 @@ static inline void cec_msg_cdc_hec_request_deactivation(struct cec_msg *msg, __u16 phys_addr3) { msg->len = 11; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_CDC_MESSAGE; /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ msg->msg[4] = CEC_MSG_CDC_HEC_REQUEST_DEACTIVATION; @@ -1884,7 +1884,7 @@ static inline void cec_ops_cdc_hec_request_deactivation(const struct cec_msg *ms static inline void cec_msg_cdc_hec_notify_alive(struct cec_msg *msg) { msg->len = 5; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_CDC_MESSAGE; /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ msg->msg[4] = CEC_MSG_CDC_HEC_NOTIFY_ALIVE; @@ -1899,7 +1899,7 @@ static inline void cec_ops_cdc_hec_notify_alive(const struct cec_msg *msg, static inline void cec_msg_cdc_hec_discover(struct cec_msg *msg) { msg->len = 5; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_CDC_MESSAGE; /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ msg->msg[4] = CEC_MSG_CDC_HEC_DISCOVER; @@ -1916,7 +1916,7 @@ static inline void cec_msg_cdc_hpd_set_state(struct cec_msg *msg, __u8 hpd_state) { msg->len = 6; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_CDC_MESSAGE; /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ msg->msg[4] = CEC_MSG_CDC_HPD_SET_STATE; @@ -1938,7 +1938,7 @@ static inline void cec_msg_cdc_hpd_report_state(struct cec_msg *msg, __u8 hpd_error) { msg->len = 6; - msg->msg[0] |= 0xf; /* broadcast */ + msg->msg[0] |= CEC_LOG_ADDR_BROADCAST; msg->msg[1] = CEC_MSG_CDC_MESSAGE; /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */ msg->msg[4] = CEC_MSG_CDC_HPD_REPORT_STATE; -- cgit v1.2.3-59-g8ed1b From 543f81b86cf4046c7454a05c741c491a4fac44dd Mon Sep 17 00:00:00 2001 From: Petja Patjas Date: Thu, 27 Mar 2025 20:56:04 +0200 Subject: media: rc: add keymap for Hauppauge Credit Card RC Add keymap for the black Hauppauge Credit Card Remote Control. Signed-off-by: Petja Patjas Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/rc-hauppauge.c | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/media/rc/keymaps/rc-hauppauge.c b/drivers/media/rc/keymaps/rc-hauppauge.c index d7156774aa0e..9e64c0b2d18e 100644 --- a/drivers/media/rc/keymaps/rc-hauppauge.c +++ b/drivers/media/rc/keymaps/rc-hauppauge.c @@ -261,6 +261,48 @@ static struct rc_map_table rc5_hauppauge_new[] = { { 0x001e, KEY_RED }, /* Reserved */ { 0x0000, KEY_NUMERIC_0 }, { 0x0026, KEY_SLEEP }, /* Minimize */ + + /* + * Keycodes for the black Credit Card Remote Control shipped with, for + * example, the WinTV-dualHD tuner. + * Keycodes start with address = 0x19 + */ + { 0x190a, KEY_LAST }, /* <- */ + { 0x192f, KEY_MENU }, /* List */ + { 0x1910, KEY_CHANNELUP }, + { 0x192e, KEY_CHANNELDOWN }, + { 0x192c, KEY_OK }, + + { 0x1911, KEY_TV }, + { 0x190c, KEY_POWER }, + + { 0x1900, KEY_NUMERIC_0 }, + { 0x1938, KEY_NUMERIC_1 }, + { 0x1920, KEY_NUMERIC_2 }, + { 0x1901, KEY_NUMERIC_3 }, + { 0x1902, KEY_NUMERIC_4 }, + { 0x1904, KEY_NUMERIC_5 }, + { 0x1905, KEY_NUMERIC_6 }, + { 0x1907, KEY_NUMERIC_7 }, + { 0x1908, KEY_NUMERIC_8 }, + { 0x190f, KEY_NUMERIC_9 }, + + { 0x1921, KEY_VOLUMEUP }, + { 0x1903, KEY_VOLUMEDOWN }, + { 0x1906, KEY_MUTE }, + + { 0x1909, KEY_CAMERA }, /* Snap */ + { 0x1922, KEY_SUBTITLE }, /* CC */ + { 0x192b, KEY_INFO }, + + { 0x1929, KEY_END }, /* Skip to live TV */ + { 0x190d, KEY_PLAYPAUSE }, + { 0x1926, KEY_STOP }, + { 0x192a, KEY_RECORD }, + { 0x193a, KEY_PREVIOUS }, /* |< */ + { 0x193b, KEY_REWIND }, /* << */ + { 0x193c, KEY_FASTFORWARD }, /* >> */ + { 0x193d, KEY_NEXT }, /* >| */ }; static struct rc_map_list rc5_hauppauge_new_map = { -- cgit v1.2.3-59-g8ed1b From 910efa649076be9c2e1326059830327cf4228cf6 Mon Sep 17 00:00:00 2001 From: Laurentiu Palcu Date: Wed, 23 Oct 2024 11:56:43 +0300 Subject: media: nxp: imx8-isi: better handle the m2m usage_count Currently, if streamon/streamoff calls are imbalanced we can either end up with a negative ISI m2m usage_count (if streamoff() is called more times than streamon()) in which case we'll not be able to restart the ISI pipe next time, or the usage_count never gets to 0 and the pipe is never switched off. To avoid that, add a 'streaming' flag to mxc_isi_m2m_ctx_queue_data and use it in the streamon/streamoff to avoid incrementing/decrementing the usage_count uselessly, if called multiple times from the same context. Fixes: cf21f328fcafac ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Suggested-by: Laurent Pinchart Signed-off-by: Laurentiu Palcu Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20241023085643.978729-1-laurentiu.palcu@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) 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 794050a6a919..22e49d3a1287 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c @@ -43,6 +43,7 @@ struct mxc_isi_m2m_ctx_queue_data { struct v4l2_pix_format_mplane format; const struct mxc_isi_format_info *info; u32 sequence; + bool streaming; }; struct mxc_isi_m2m_ctx { @@ -484,15 +485,18 @@ static int mxc_isi_m2m_streamon(struct file *file, void *fh, enum v4l2_buf_type type) { struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh); + struct mxc_isi_m2m_ctx_queue_data *q = mxc_isi_m2m_ctx_qdata(ctx, type); const struct v4l2_pix_format_mplane *out_pix = &ctx->queues.out.format; const struct v4l2_pix_format_mplane *cap_pix = &ctx->queues.cap.format; const struct mxc_isi_format_info *cap_info = ctx->queues.cap.info; const struct mxc_isi_format_info *out_info = ctx->queues.out.info; struct mxc_isi_m2m *m2m = ctx->m2m; bool bypass; - int ret; + if (q->streaming) + return 0; + mutex_lock(&m2m->lock); if (m2m->usage_count == INT_MAX) { @@ -545,6 +549,8 @@ static int mxc_isi_m2m_streamon(struct file *file, void *fh, goto unchain; } + q->streaming = true; + return 0; unchain: @@ -567,10 +573,14 @@ static int mxc_isi_m2m_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) { struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh); + struct mxc_isi_m2m_ctx_queue_data *q = mxc_isi_m2m_ctx_qdata(ctx, type); struct mxc_isi_m2m *m2m = ctx->m2m; v4l2_m2m_ioctl_streamoff(file, fh, type); + if (!q->streaming) + return 0; + mutex_lock(&m2m->lock); /* @@ -596,6 +606,8 @@ static int mxc_isi_m2m_streamoff(struct file *file, void *fh, mutex_unlock(&m2m->lock); + q->streaming = false; + return 0; } -- cgit v1.2.3-59-g8ed1b From 2e79181dfc85e1347a8655ea8d8a314158155c52 Mon Sep 17 00:00:00 2001 From: Stefan Klug Date: Thu, 27 Feb 2025 12:45:01 +0100 Subject: media: rkisp1: Remove unnecessary defines The effect modes are not shifts but numbers which are already defined a few lines above. Remove the misleading defines. Signed-off-by: Stefan Klug Link: https://lore.kernel.org/r/20250227114558.3097101-4-stefan.klug@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h index bf0260600a19..139177db9c6d 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h @@ -327,13 +327,6 @@ #define RKISP1_CIF_IMG_EFF_CTRL_CFG_UPD BIT(4) #define RKISP1_CIF_IMG_EFF_CTRL_YCBCR_FULL BIT(5) -#define RKISP1_CIF_IMG_EFF_CTRL_MODE_BLACKWHITE_SHIFT 0 -#define RKISP1_CIF_IMG_EFF_CTRL_MODE_NEGATIVE_SHIFT 1 -#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SEPIA_SHIFT 2 -#define RKISP1_CIF_IMG_EFF_CTRL_MODE_COLOR_SEL_SHIFT 3 -#define RKISP1_CIF_IMG_EFF_CTRL_MODE_EMBOSS_SHIFT 4 -#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SKETCH_SHIFT 5 -#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SHARPEN_SHIFT 6 #define RKISP1_CIF_IMG_EFF_CTRL_MODE_MASK 0xe /* IMG_EFF_COLOR_SEL */ -- cgit v1.2.3-59-g8ed1b From f0a0a2388f443e949894f68ca49ccc9618512316 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 17 Apr 2025 16:59:01 +0200 Subject: dt-bindings: media: qcom,sm8550-iris: document SM8650 IRIS accelerator Document the IRIS video decoder and encoder accelerator found in the SM8650 platform, it requires 2 more reset lines in addition to the properties required for the SM8550 platform. Reviewed-by: Rob Herring (Arm) Reviewed-by: Vikash Garodia Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # x1e Dell Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- .../bindings/media/qcom,sm8550-iris.yaml | 33 ++++++++++++++++++---- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml b/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml index 6a89e9e38087..f567f84bd60d 100644 --- a/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml @@ -14,9 +14,6 @@ description: The iris video processing unit is a video encode and decode accelerator present on Qualcomm platforms. -allOf: - - $ref: qcom,venus-common.yaml# - properties: compatible: oneOf: @@ -24,7 +21,9 @@ properties: - enum: - qcom,sa8775p-iris - const: qcom,sm8550-iris - - const: qcom,sm8550-iris + - enum: + - qcom,sm8550-iris + - qcom,sm8650-iris power-domains: maxItems: 4 @@ -54,11 +53,15 @@ properties: - const: video-mem resets: - maxItems: 1 + minItems: 1 + maxItems: 3 reset-names: + minItems: 1 items: - const: bus + - const: xo + - const: core iommus: maxItems: 2 @@ -80,6 +83,26 @@ required: - iommus - dma-coherent +allOf: + - $ref: qcom,venus-common.yaml# + - if: + properties: + compatible: + enum: + - qcom,sm8650-iris + then: + properties: + resets: + minItems: 3 + reset-names: + minItems: 3 + else: + properties: + resets: + maxItems: 1 + reset-names: + maxItems: 1 + unevaluatedProperties: false examples: -- cgit v1.2.3-59-g8ed1b From c69df5de4ac3c8dff7f6663c960dd8c5f0dfbaa4 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 17 Apr 2025 16:59:02 +0200 Subject: media: platform: qcom/iris: add power_off_controller to vpu_ops In order to support the SM8650 iris33 hardware, we need to provide a specific constoller power off sequences via the vpu_ops callbacks. Add the callback, and use the current helper for currently supported platforms. Reviewed-by: Bryan O'Donoghue Reviewed-by: Dikshita Agarwal Tested-by: Bryan O'Donoghue # x1e Dell Reviewed-by: Vikash Garodia Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/iris/iris_vpu2.c | 1 + drivers/media/platform/qcom/iris/iris_vpu3.c | 1 + drivers/media/platform/qcom/iris/iris_vpu_common.c | 4 ++-- drivers/media/platform/qcom/iris/iris_vpu_common.h | 2 ++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_vpu2.c b/drivers/media/platform/qcom/iris/iris_vpu2.c index 8f502aed43ce..7cf1bfc352d3 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu2.c +++ b/drivers/media/platform/qcom/iris/iris_vpu2.c @@ -34,5 +34,6 @@ static u64 iris_vpu2_calc_freq(struct iris_inst *inst, size_t data_size) const struct vpu_ops iris_vpu2_ops = { .power_off_hw = iris_vpu_power_off_hw, + .power_off_controller = iris_vpu_power_off_controller, .calc_freq = iris_vpu2_calc_freq, }; diff --git a/drivers/media/platform/qcom/iris/iris_vpu3.c b/drivers/media/platform/qcom/iris/iris_vpu3.c index b484638e6105..13dab61427b8 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu3.c +++ b/drivers/media/platform/qcom/iris/iris_vpu3.c @@ -118,5 +118,6 @@ static u64 iris_vpu3_calculate_frequency(struct iris_inst *inst, size_t data_siz const struct vpu_ops iris_vpu3_ops = { .power_off_hw = iris_vpu3_power_off_hardware, + .power_off_controller = iris_vpu_power_off_controller, .calc_freq = iris_vpu3_calculate_frequency, }; diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index fe9896d66848..268e45acaa7c 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -211,7 +211,7 @@ skip_power_off: return -EAGAIN; } -static int iris_vpu_power_off_controller(struct iris_core *core) +int iris_vpu_power_off_controller(struct iris_core *core) { u32 val = 0; int ret; @@ -264,7 +264,7 @@ void iris_vpu_power_off(struct iris_core *core) { dev_pm_opp_set_rate(core->dev, 0); core->iris_platform_data->vpu_ops->power_off_hw(core); - iris_vpu_power_off_controller(core); + core->iris_platform_data->vpu_ops->power_off_controller(core); iris_unset_icc_bw(core); if (!iris_vpu_watchdog(core, core->intr_status)) diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h b/drivers/media/platform/qcom/iris/iris_vpu_common.h index 63fa1fa5a498..f8965661c602 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.h +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h @@ -13,6 +13,7 @@ extern const struct vpu_ops iris_vpu3_ops; struct vpu_ops { void (*power_off_hw)(struct iris_core *core); + int (*power_off_controller)(struct iris_core *core); u64 (*calc_freq)(struct iris_inst *inst, size_t data_size); }; @@ -22,6 +23,7 @@ void iris_vpu_clear_interrupt(struct iris_core *core); int iris_vpu_watchdog(struct iris_core *core, u32 intr_status); int iris_vpu_prepare_pc(struct iris_core *core); int iris_vpu_power_on(struct iris_core *core); +int iris_vpu_power_off_controller(struct iris_core *core); void iris_vpu_power_off_hw(struct iris_core *core); void iris_vpu_power_off(struct iris_core *core); -- cgit v1.2.3-59-g8ed1b From 322e9061edcbe5be110029a3453d8acc9888a684 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 17 Apr 2025 16:59:03 +0200 Subject: media: platform: qcom/iris: introduce optional controller_rst_tbl Introduce an optional controller_rst_tbl use to store reset lines used to reset part of the controller. This is necessary for the vpu3 support, when the xo reset line must be asserted separately from the other reset line on power off operation. Factor the iris_init_resets() logic to allow requesting multiple reset tables. Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # x1e Dell Reviewed-by: Dikshita Agarwal Signed-off-by: Neil Armstrong Reviewed-by: Vikash Garodia Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/iris/iris_core.h | 2 ++ .../platform/qcom/iris/iris_platform_common.h | 2 ++ drivers/media/platform/qcom/iris/iris_probe.c | 39 +++++++++++++++------- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h index 37fb4919fecc..aeeac32a1f6d 100644 --- a/drivers/media/platform/qcom/iris/iris_core.h +++ b/drivers/media/platform/qcom/iris/iris_core.h @@ -43,6 +43,7 @@ struct icc_info { * @clock_tbl: table of iris clocks * @clk_count: count of iris clocks * @resets: table of iris reset clocks + * @controller_resets: table of controller reset clocks * @iris_platform_data: a structure for platform data * @state: current state of core * @iface_q_table_daddr: device address for interface queue table memory @@ -82,6 +83,7 @@ struct iris_core { struct clk_bulk_data *clock_tbl; u32 clk_count; struct reset_control_bulk_data *resets; + struct reset_control_bulk_data *controller_resets; const struct iris_platform_data *iris_platform_data; enum iris_core_state state; dma_addr_t iface_q_table_daddr; diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index f6b15d2805fb..fdd40fd80178 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -156,6 +156,8 @@ struct iris_platform_data { unsigned int clk_tbl_size; const char * const *clk_rst_tbl; unsigned int clk_rst_tbl_size; + const char * const *controller_rst_tbl; + unsigned int controller_rst_tbl_size; u64 dma_mask; const char *fwname; u32 pas_id; diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index aca442dcc153..4f8bce6e2002 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -91,25 +91,40 @@ static int iris_init_clocks(struct iris_core *core) return 0; } -static int iris_init_resets(struct iris_core *core) +static int iris_init_reset_table(struct iris_core *core, + struct reset_control_bulk_data **resets, + const char * const *rst_tbl, u32 rst_tbl_size) { - const char * const *rst_tbl; - u32 rst_tbl_size; u32 i = 0; - rst_tbl = core->iris_platform_data->clk_rst_tbl; - rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size; - - core->resets = devm_kzalloc(core->dev, - sizeof(*core->resets) * rst_tbl_size, - GFP_KERNEL); - if (!core->resets) + *resets = devm_kzalloc(core->dev, + sizeof(struct reset_control_bulk_data) * rst_tbl_size, + GFP_KERNEL); + if (!*resets) return -ENOMEM; for (i = 0; i < rst_tbl_size; i++) - core->resets[i].id = rst_tbl[i]; + (*resets)[i].id = rst_tbl[i]; + + return devm_reset_control_bulk_get_exclusive(core->dev, rst_tbl_size, *resets); +} + +static int iris_init_resets(struct iris_core *core) +{ + int ret; + + ret = iris_init_reset_table(core, &core->resets, + core->iris_platform_data->clk_rst_tbl, + core->iris_platform_data->clk_rst_tbl_size); + if (ret) + return ret; + + if (!core->iris_platform_data->controller_rst_tbl_size) + return 0; - return devm_reset_control_bulk_get_exclusive(core->dev, rst_tbl_size, core->resets); + return iris_init_reset_table(core, &core->controller_resets, + core->iris_platform_data->controller_rst_tbl, + core->iris_platform_data->controller_rst_tbl_size); } static int iris_init_resources(struct iris_core *core) -- cgit v1.2.3-59-g8ed1b From 9cd2b62c8c29a3316abff91bae23c54d06a63992 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 17 Apr 2025 16:59:04 +0200 Subject: media: platform: qcom/iris: rename iris_vpu3 to iris_vpu3x The vpu33 HW is very close to vpu3, and shares most of the operations, so rename file to vpu3x since we'll handle all vpu3 variants in it. Reviewed-by: Dikshita Agarwal Tested-by: Bryan O'Donoghue # x1e Dell Reviewed-by: Vikash Garodia Signed-off-by: Neil Armstrong Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/iris/Makefile | 2 +- drivers/media/platform/qcom/iris/iris_vpu3.c | 123 -------------------------- drivers/media/platform/qcom/iris/iris_vpu3x.c | 123 ++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 124 deletions(-) delete mode 100644 drivers/media/platform/qcom/iris/iris_vpu3.c create mode 100644 drivers/media/platform/qcom/iris/iris_vpu3x.c diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile index 35390534534e..473aaf655448 100644 --- a/drivers/media/platform/qcom/iris/Makefile +++ b/drivers/media/platform/qcom/iris/Makefile @@ -20,7 +20,7 @@ qcom-iris-objs += \ iris_vb2.o \ iris_vdec.o \ iris_vpu2.o \ - iris_vpu3.o \ + iris_vpu3x.o \ iris_vpu_buffer.o \ iris_vpu_common.o \ diff --git a/drivers/media/platform/qcom/iris/iris_vpu3.c b/drivers/media/platform/qcom/iris/iris_vpu3.c deleted file mode 100644 index 13dab61427b8..000000000000 --- a/drivers/media/platform/qcom/iris/iris_vpu3.c +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. - */ - -#include - -#include "iris_instance.h" -#include "iris_vpu_common.h" -#include "iris_vpu_register_defines.h" - -#define AON_MVP_NOC_RESET 0x0001F000 - -#define WRAPPER_CORE_CLOCK_CONFIG (WRAPPER_BASE_OFFS + 0x88) -#define CORE_CLK_RUN 0x0 - -#define CPU_CS_AHB_BRIDGE_SYNC_RESET (CPU_CS_BASE_OFFS + 0x160) -#define CORE_BRIDGE_SW_RESET BIT(0) -#define CORE_BRIDGE_HW_RESET_DISABLE BIT(1) - -#define AON_WRAPPER_MVP_NOC_RESET_REQ (AON_MVP_NOC_RESET + 0x000) -#define VIDEO_NOC_RESET_REQ (BIT(0) | BIT(1)) - -#define AON_WRAPPER_MVP_NOC_RESET_ACK (AON_MVP_NOC_RESET + 0x004) - -#define VCODEC_SS_IDLE_STATUSN (VCODEC_BASE_OFFS + 0x70) - -static bool iris_vpu3_hw_power_collapsed(struct iris_core *core) -{ - u32 value, pwr_status; - - value = readl(core->reg_base + WRAPPER_CORE_POWER_STATUS); - pwr_status = value & BIT(1); - - return pwr_status ? false : true; -} - -static void iris_vpu3_power_off_hardware(struct iris_core *core) -{ - u32 reg_val = 0, value, i; - int ret; - - if (iris_vpu3_hw_power_collapsed(core)) - goto disable_power; - - dev_err(core->dev, "video hw is power on\n"); - - value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); - if (value) - writel(CORE_CLK_RUN, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); - - for (i = 0; i < core->iris_platform_data->num_vpp_pipe; i++) { - ret = readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN + 4 * i, - reg_val, reg_val & 0x400000, 2000, 20000); - if (ret) - goto disable_power; - } - - writel(VIDEO_NOC_RESET_REQ, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ); - - ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK, - reg_val, reg_val & 0x3, 200, 2000); - if (ret) - goto disable_power; - - writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ); - - ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK, - reg_val, !(reg_val & 0x3), 200, 2000); - if (ret) - goto disable_power; - - writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, - core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); - writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); - writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); - -disable_power: - iris_vpu_power_off_hw(core); -} - -static u64 iris_vpu3_calculate_frequency(struct iris_inst *inst, size_t data_size) -{ - struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps; - struct v4l2_format *inp_f = inst->fmt_src; - u32 height, width, mbs_per_second, mbpf; - u64 fw_cycles, fw_vpp_cycles; - u64 vsp_cycles, vpp_cycles; - u32 fps = DEFAULT_FPS; - - width = max(inp_f->fmt.pix_mp.width, inst->crop.width); - height = max(inp_f->fmt.pix_mp.height, inst->crop.height); - - mbpf = NUM_MBS_PER_FRAME(height, width); - mbs_per_second = mbpf * fps; - - fw_cycles = fps * caps->mb_cycles_fw; - fw_vpp_cycles = fps * caps->mb_cycles_fw_vpp; - - vpp_cycles = mult_frac(mbs_per_second, caps->mb_cycles_vpp, (u32)inst->fw_caps[PIPE].value); - /* 21 / 20 is minimum overhead factor */ - vpp_cycles += max(div_u64(vpp_cycles, 20), fw_vpp_cycles); - - /* 1.059 is multi-pipe overhead */ - if (inst->fw_caps[PIPE].value > 1) - vpp_cycles += div_u64(vpp_cycles * 59, 1000); - - vsp_cycles = fps * data_size * 8; - vsp_cycles = div_u64(vsp_cycles, 2); - /* VSP FW overhead 1.05 */ - vsp_cycles = div_u64(vsp_cycles * 21, 20); - - if (inst->fw_caps[STAGE].value == STAGE_1) - vsp_cycles = vsp_cycles * 3; - - return max3(vpp_cycles, vsp_cycles, fw_cycles); -} - -const struct vpu_ops iris_vpu3_ops = { - .power_off_hw = iris_vpu3_power_off_hardware, - .power_off_controller = iris_vpu_power_off_controller, - .calc_freq = iris_vpu3_calculate_frequency, -}; diff --git a/drivers/media/platform/qcom/iris/iris_vpu3x.c b/drivers/media/platform/qcom/iris/iris_vpu3x.c new file mode 100644 index 000000000000..13dab61427b8 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_vpu3x.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include + +#include "iris_instance.h" +#include "iris_vpu_common.h" +#include "iris_vpu_register_defines.h" + +#define AON_MVP_NOC_RESET 0x0001F000 + +#define WRAPPER_CORE_CLOCK_CONFIG (WRAPPER_BASE_OFFS + 0x88) +#define CORE_CLK_RUN 0x0 + +#define CPU_CS_AHB_BRIDGE_SYNC_RESET (CPU_CS_BASE_OFFS + 0x160) +#define CORE_BRIDGE_SW_RESET BIT(0) +#define CORE_BRIDGE_HW_RESET_DISABLE BIT(1) + +#define AON_WRAPPER_MVP_NOC_RESET_REQ (AON_MVP_NOC_RESET + 0x000) +#define VIDEO_NOC_RESET_REQ (BIT(0) | BIT(1)) + +#define AON_WRAPPER_MVP_NOC_RESET_ACK (AON_MVP_NOC_RESET + 0x004) + +#define VCODEC_SS_IDLE_STATUSN (VCODEC_BASE_OFFS + 0x70) + +static bool iris_vpu3_hw_power_collapsed(struct iris_core *core) +{ + u32 value, pwr_status; + + value = readl(core->reg_base + WRAPPER_CORE_POWER_STATUS); + pwr_status = value & BIT(1); + + return pwr_status ? false : true; +} + +static void iris_vpu3_power_off_hardware(struct iris_core *core) +{ + u32 reg_val = 0, value, i; + int ret; + + if (iris_vpu3_hw_power_collapsed(core)) + goto disable_power; + + dev_err(core->dev, "video hw is power on\n"); + + value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); + if (value) + writel(CORE_CLK_RUN, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); + + for (i = 0; i < core->iris_platform_data->num_vpp_pipe; i++) { + ret = readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN + 4 * i, + reg_val, reg_val & 0x400000, 2000, 20000); + if (ret) + goto disable_power; + } + + writel(VIDEO_NOC_RESET_REQ, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ); + + ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK, + reg_val, reg_val & 0x3, 200, 2000); + if (ret) + goto disable_power; + + writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ); + + ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK, + reg_val, !(reg_val & 0x3), 200, 2000); + if (ret) + goto disable_power; + + writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, + core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); + writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); + writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); + +disable_power: + iris_vpu_power_off_hw(core); +} + +static u64 iris_vpu3_calculate_frequency(struct iris_inst *inst, size_t data_size) +{ + struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps; + struct v4l2_format *inp_f = inst->fmt_src; + u32 height, width, mbs_per_second, mbpf; + u64 fw_cycles, fw_vpp_cycles; + u64 vsp_cycles, vpp_cycles; + u32 fps = DEFAULT_FPS; + + width = max(inp_f->fmt.pix_mp.width, inst->crop.width); + height = max(inp_f->fmt.pix_mp.height, inst->crop.height); + + mbpf = NUM_MBS_PER_FRAME(height, width); + mbs_per_second = mbpf * fps; + + fw_cycles = fps * caps->mb_cycles_fw; + fw_vpp_cycles = fps * caps->mb_cycles_fw_vpp; + + vpp_cycles = mult_frac(mbs_per_second, caps->mb_cycles_vpp, (u32)inst->fw_caps[PIPE].value); + /* 21 / 20 is minimum overhead factor */ + vpp_cycles += max(div_u64(vpp_cycles, 20), fw_vpp_cycles); + + /* 1.059 is multi-pipe overhead */ + if (inst->fw_caps[PIPE].value > 1) + vpp_cycles += div_u64(vpp_cycles * 59, 1000); + + vsp_cycles = fps * data_size * 8; + vsp_cycles = div_u64(vsp_cycles, 2); + /* VSP FW overhead 1.05 */ + vsp_cycles = div_u64(vsp_cycles * 21, 20); + + if (inst->fw_caps[STAGE].value == STAGE_1) + vsp_cycles = vsp_cycles * 3; + + return max3(vpp_cycles, vsp_cycles, fw_cycles); +} + +const struct vpu_ops iris_vpu3_ops = { + .power_off_hw = iris_vpu3_power_off_hardware, + .power_off_controller = iris_vpu_power_off_controller, + .calc_freq = iris_vpu3_calculate_frequency, +}; -- cgit v1.2.3-59-g8ed1b From 02083a1e00ae294001faf81ef43f9edfd3952361 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 17 Apr 2025 16:59:05 +0200 Subject: media: platform: qcom/iris: add support for vpu33 The IRIS acceleration found in the SM8650 platforms uses the vpu33 hardware version, and requires a slighly different reset and power off sequences in order to properly get out of runtime suspend. Tested-by: Bryan O'Donoghue # x1e Dell Signed-off-by: Neil Armstrong Reviewed-by: Vikash Garodia Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/iris/iris_vpu3x.c | 160 ++++++++++++++++++++- drivers/media/platform/qcom/iris/iris_vpu_common.h | 1 + 2 files changed, 157 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_vpu3x.c b/drivers/media/platform/qcom/iris/iris_vpu3x.c index 13dab61427b8..9b7c9a1495ee 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu3x.c +++ b/drivers/media/platform/qcom/iris/iris_vpu3x.c @@ -4,20 +4,39 @@ */ #include +#include #include "iris_instance.h" #include "iris_vpu_common.h" #include "iris_vpu_register_defines.h" +#define WRAPPER_TZ_BASE_OFFS 0x000C0000 +#define AON_BASE_OFFS 0x000E0000 #define AON_MVP_NOC_RESET 0x0001F000 +#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x54) +#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS (WRAPPER_BASE_OFFS + 0x58) +#define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x5C) +#define REQ_POWER_DOWN_PREP BIT(0) +#define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x60) #define WRAPPER_CORE_CLOCK_CONFIG (WRAPPER_BASE_OFFS + 0x88) #define CORE_CLK_RUN 0x0 +#define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS + 0x14) +#define CTL_AXI_CLK_HALT BIT(0) +#define CTL_CLK_HALT BIT(1) + +#define WRAPPER_TZ_QNS4PDXFIFO_RESET (WRAPPER_TZ_BASE_OFFS + 0x18) +#define RESET_HIGH BIT(0) + #define CPU_CS_AHB_BRIDGE_SYNC_RESET (CPU_CS_BASE_OFFS + 0x160) #define CORE_BRIDGE_SW_RESET BIT(0) #define CORE_BRIDGE_HW_RESET_DISABLE BIT(1) +#define CPU_CS_X2RPMH (CPU_CS_BASE_OFFS + 0x168) +#define MSK_SIGNAL_FROM_TENSILICA BIT(0) +#define MSK_CORE_POWER_ON BIT(1) + #define AON_WRAPPER_MVP_NOC_RESET_REQ (AON_MVP_NOC_RESET + 0x000) #define VIDEO_NOC_RESET_REQ (BIT(0) | BIT(1)) @@ -25,7 +44,16 @@ #define VCODEC_SS_IDLE_STATUSN (VCODEC_BASE_OFFS + 0x70) -static bool iris_vpu3_hw_power_collapsed(struct iris_core *core) +#define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS) +#define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4) + +#define AON_WRAPPER_MVP_NOC_CORE_SW_RESET (AON_BASE_OFFS + 0x18) +#define SW_RESET BIT(0) +#define AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL (AON_BASE_OFFS + 0x20) +#define NOC_HALT BIT(0) +#define AON_WRAPPER_SPARE (AON_BASE_OFFS + 0x28) + +static bool iris_vpu3x_hw_power_collapsed(struct iris_core *core) { u32 value, pwr_status; @@ -40,7 +68,7 @@ static void iris_vpu3_power_off_hardware(struct iris_core *core) u32 reg_val = 0, value, i; int ret; - if (iris_vpu3_hw_power_collapsed(core)) + if (iris_vpu3x_hw_power_collapsed(core)) goto disable_power; dev_err(core->dev, "video hw is power on\n"); @@ -79,7 +107,125 @@ disable_power: iris_vpu_power_off_hw(core); } -static u64 iris_vpu3_calculate_frequency(struct iris_inst *inst, size_t data_size) +static void iris_vpu33_power_off_hardware(struct iris_core *core) +{ + u32 reg_val = 0, value, i; + int ret; + + if (iris_vpu3x_hw_power_collapsed(core)) + goto disable_power; + + dev_err(core->dev, "video hw is power on\n"); + + value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); + if (value) + writel(CORE_CLK_RUN, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); + + for (i = 0; i < core->iris_platform_data->num_vpp_pipe; i++) { + ret = readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN + 4 * i, + reg_val, reg_val & 0x400000, 2000, 20000); + if (ret) + goto disable_power; + } + + ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS, + reg_val, reg_val & BIT(0), 200, 2000); + if (ret) + goto disable_power; + + /* set MNoC to low power, set PD_NOC_QREQ (bit 0) */ + writel(BIT(0), core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); + + writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, + core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); + writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); + writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); + +disable_power: + iris_vpu_power_off_hw(core); +} + +static int iris_vpu33_power_off_controller(struct iris_core *core) +{ + u32 xo_rst_tbl_size = core->iris_platform_data->controller_rst_tbl_size; + u32 clk_rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size; + u32 val = 0; + int ret; + + writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH); + + writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL); + + ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS, + val, val & BIT(0), 200, 2000); + if (ret) + goto disable_power; + + writel(0x0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL); + + ret = readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS, + val, val == 0, 200, 2000); + if (ret) + goto disable_power; + + writel(CTL_AXI_CLK_HALT | CTL_CLK_HALT, + core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG); + writel(RESET_HIGH, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET); + writel(0x0, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET); + writel(0x0, core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG); + + reset_control_bulk_reset(clk_rst_tbl_size, core->resets); + + /* Disable MVP NoC clock */ + val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL); + val |= NOC_HALT; + writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL); + + /* enable MVP NoC reset */ + val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET); + val |= SW_RESET; + writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET); + + /* poll AON spare register bit0 to become zero with 50ms timeout */ + ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_SPARE, + val, (val & BIT(0)) == 0, 1000, 50000); + if (ret) + goto disable_power; + + /* enable bit(1) to avoid cvp noc xo reset */ + val = readl(core->reg_base + AON_WRAPPER_SPARE); + val |= BIT(1); + writel(val, core->reg_base + AON_WRAPPER_SPARE); + + reset_control_bulk_assert(xo_rst_tbl_size, core->controller_resets); + + /* De-assert MVP NoC reset */ + val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET); + val &= ~SW_RESET; + writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET); + + usleep_range(80, 100); + + reset_control_bulk_deassert(xo_rst_tbl_size, core->controller_resets); + + /* reset AON spare register */ + writel(0, core->reg_base + AON_WRAPPER_SPARE); + + /* Enable MVP NoC clock */ + val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL); + val &= ~NOC_HALT; + writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL); + + iris_disable_unprepare_clock(core, IRIS_CTRL_CLK); + +disable_power: + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); + iris_disable_unprepare_clock(core, IRIS_AXI_CLK); + + return 0; +} + +static u64 iris_vpu3x_calculate_frequency(struct iris_inst *inst, size_t data_size) { struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps; struct v4l2_format *inp_f = inst->fmt_src; @@ -119,5 +265,11 @@ static u64 iris_vpu3_calculate_frequency(struct iris_inst *inst, size_t data_siz const struct vpu_ops iris_vpu3_ops = { .power_off_hw = iris_vpu3_power_off_hardware, .power_off_controller = iris_vpu_power_off_controller, - .calc_freq = iris_vpu3_calculate_frequency, + .calc_freq = iris_vpu3x_calculate_frequency, +}; + +const struct vpu_ops iris_vpu33_ops = { + .power_off_hw = iris_vpu33_power_off_hardware, + .power_off_controller = iris_vpu33_power_off_controller, + .calc_freq = iris_vpu3x_calculate_frequency, }; diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h b/drivers/media/platform/qcom/iris/iris_vpu_common.h index f8965661c602..93b7fa27be3b 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.h +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h @@ -10,6 +10,7 @@ struct iris_core; extern const struct vpu_ops iris_vpu2_ops; extern const struct vpu_ops iris_vpu3_ops; +extern const struct vpu_ops iris_vpu33_ops; struct vpu_ops { void (*power_off_hw)(struct iris_core *core); -- cgit v1.2.3-59-g8ed1b From dc40021c13d412f48e11a5c4aeb9e0a997ff2b95 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 17 Apr 2025 16:59:06 +0200 Subject: media: platform: qcom/iris: rename platform_sm8550 to platform_gen2 In order to prepare for supporting the SM8650 SoC, move the iris_platform_sm8550.c file into iris_platform_gen2.c that will contain all the common HFI GEN2x structures. Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # x1e Dell Signed-off-by: Neil Armstrong Reviewed-by: Vikash Garodia Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/iris/Makefile | 2 +- .../media/platform/qcom/iris/iris_platform_gen2.c | 266 +++++++++++++++++++++ .../platform/qcom/iris/iris_platform_sm8550.c | 266 --------------------- 3 files changed, 267 insertions(+), 267 deletions(-) create mode 100644 drivers/media/platform/qcom/iris/iris_platform_gen2.c delete mode 100644 drivers/media/platform/qcom/iris/iris_platform_sm8550.c diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile index 473aaf655448..e86d00ee6f15 100644 --- a/drivers/media/platform/qcom/iris/Makefile +++ b/drivers/media/platform/qcom/iris/Makefile @@ -10,7 +10,7 @@ qcom-iris-objs += \ iris_hfi_gen2_packet.o \ iris_hfi_gen2_response.o \ iris_hfi_queue.o \ - iris_platform_sm8550.o \ + iris_platform_gen2.o \ iris_power.o \ iris_probe.o \ iris_resources.o \ diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c new file mode 100644 index 000000000000..35d278996c43 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#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_common.h" + +#define VIDEO_ARCH_LX 1 + +static struct platform_inst_fw_cap inst_fw_cap_sm8550[] = { + { + .cap_id = PROFILE, + .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH), + .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + .hfi_id = HFI_PROP_PROFILE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = LEVEL, + .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .max = V4L2_MPEG_VIDEO_H264_LEVEL_6_2, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_2), + .value = V4L2_MPEG_VIDEO_H264_LEVEL_6_1, + .hfi_id = HFI_PROP_LEVEL, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = INPUT_BUF_HOST_MAX_COUNT, + .min = DEFAULT_MAX_HOST_BUF_COUNT, + .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT, + .step_or_mask = 1, + .value = DEFAULT_MAX_HOST_BUF_COUNT, + .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT, + .flags = CAP_FLAG_INPUT_PORT, + .set = iris_set_u32, + }, + { + .cap_id = STAGE, + .min = STAGE_1, + .max = STAGE_2, + .step_or_mask = 1, + .value = STAGE_2, + .hfi_id = HFI_PROP_STAGE, + .set = iris_set_stage, + }, + { + .cap_id = PIPE, + .min = PIPE_1, + .max = PIPE_4, + .step_or_mask = 1, + .value = PIPE_4, + .hfi_id = HFI_PROP_PIPE, + .set = iris_set_pipe, + }, + { + .cap_id = POC, + .min = 0, + .max = 2, + .step_or_mask = 1, + .value = 1, + .hfi_id = HFI_PROP_PIC_ORDER_CNT_TYPE, + }, + { + .cap_id = CODED_FRAMES, + .min = CODED_FRAMES_PROGRESSIVE, + .max = CODED_FRAMES_PROGRESSIVE, + .step_or_mask = 0, + .value = CODED_FRAMES_PROGRESSIVE, + .hfi_id = HFI_PROP_CODED_FRAMES, + }, + { + .cap_id = BIT_DEPTH, + .min = BIT_DEPTH_8, + .max = BIT_DEPTH_8, + .step_or_mask = 1, + .value = BIT_DEPTH_8, + .hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH, + }, + { + .cap_id = RAP_FRAME, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 1, + .hfi_id = HFI_PROP_DEC_START_FROM_RAP_FRAME, + .flags = CAP_FLAG_INPUT_PORT, + .set = iris_set_u32, + }, +}; + +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, +}; + +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 }, +}; + +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 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, + .bank_swz3_level = 1, + .bank_spreading = 1, +}; + +static 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[] = { + HFI_PROP_BITSTREAM_RESOLUTION, + HFI_PROP_CROP_OFFSETS, + HFI_PROP_CODED_FRAMES, + HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT, + HFI_PROP_PIC_ORDER_CNT_TYPE, + HFI_PROP_PROFILE, + HFI_PROP_LEVEL, + HFI_PROP_SIGNAL_COLOR_INFO, +}; + +static const u32 sm8550_vdec_output_config_params[] = { + HFI_PROP_COLOR_FORMAT, + HFI_PROP_LINEAR_STRIDE_SCANLINE, +}; + +static const u32 sm8550_vdec_subscribe_input_properties[] = { + HFI_PROP_NO_OUTPUT, +}; + +static const u32 sm8550_vdec_subscribe_output_properties[] = { + HFI_PROP_PICTURE_TYPE, + HFI_PROP_CABAC_SESSION, +}; + +static const u32 sm8550_dec_ip_int_buf_tbl[] = { + BUF_BIN, + BUF_COMV, + BUF_NON_COMV, + BUF_LINE, +}; + +static const u32 sm8550_dec_op_int_buf_tbl[] = { + BUF_DPB, +}; + +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, + .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, + .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), + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu30_p4.mbn", + .pas_id = IRIS_PAS_ID, + .inst_caps = &platform_inst_cap_sm8550, + .inst_fw_caps = inst_fw_cap_sm8550, + .inst_fw_caps_size = ARRAY_SIZE(inst_fw_cap_sm8550), + .tz_cp_config_data = &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 = ((8192 * 4352) / 256) * 2, + .input_config_params = + sm8550_vdec_input_config_params, + .input_config_params_size = + ARRAY_SIZE(sm8550_vdec_input_config_params), + .output_config_params = + sm8550_vdec_output_config_params, + .output_config_params_size = + ARRAY_SIZE(sm8550_vdec_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 = sm8550_vdec_subscribe_output_properties, + .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties), + + .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), +}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8550.c b/drivers/media/platform/qcom/iris/iris_platform_sm8550.c deleted file mode 100644 index 35d278996c43..000000000000 --- a/drivers/media/platform/qcom/iris/iris_platform_sm8550.c +++ /dev/null @@ -1,266 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. - */ - -#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_common.h" - -#define VIDEO_ARCH_LX 1 - -static struct platform_inst_fw_cap inst_fw_cap_sm8550[] = { - { - .cap_id = PROFILE, - .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, - .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH), - .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, - .hfi_id = HFI_PROP_PROFILE, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - .set = iris_set_u32_enum, - }, - { - .cap_id = LEVEL, - .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, - .max = V4L2_MPEG_VIDEO_H264_LEVEL_6_2, - .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_2), - .value = V4L2_MPEG_VIDEO_H264_LEVEL_6_1, - .hfi_id = HFI_PROP_LEVEL, - .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, - .set = iris_set_u32_enum, - }, - { - .cap_id = INPUT_BUF_HOST_MAX_COUNT, - .min = DEFAULT_MAX_HOST_BUF_COUNT, - .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT, - .step_or_mask = 1, - .value = DEFAULT_MAX_HOST_BUF_COUNT, - .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT, - .flags = CAP_FLAG_INPUT_PORT, - .set = iris_set_u32, - }, - { - .cap_id = STAGE, - .min = STAGE_1, - .max = STAGE_2, - .step_or_mask = 1, - .value = STAGE_2, - .hfi_id = HFI_PROP_STAGE, - .set = iris_set_stage, - }, - { - .cap_id = PIPE, - .min = PIPE_1, - .max = PIPE_4, - .step_or_mask = 1, - .value = PIPE_4, - .hfi_id = HFI_PROP_PIPE, - .set = iris_set_pipe, - }, - { - .cap_id = POC, - .min = 0, - .max = 2, - .step_or_mask = 1, - .value = 1, - .hfi_id = HFI_PROP_PIC_ORDER_CNT_TYPE, - }, - { - .cap_id = CODED_FRAMES, - .min = CODED_FRAMES_PROGRESSIVE, - .max = CODED_FRAMES_PROGRESSIVE, - .step_or_mask = 0, - .value = CODED_FRAMES_PROGRESSIVE, - .hfi_id = HFI_PROP_CODED_FRAMES, - }, - { - .cap_id = BIT_DEPTH, - .min = BIT_DEPTH_8, - .max = BIT_DEPTH_8, - .step_or_mask = 1, - .value = BIT_DEPTH_8, - .hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH, - }, - { - .cap_id = RAP_FRAME, - .min = 0, - .max = 1, - .step_or_mask = 1, - .value = 1, - .hfi_id = HFI_PROP_DEC_START_FROM_RAP_FRAME, - .flags = CAP_FLAG_INPUT_PORT, - .set = iris_set_u32, - }, -}; - -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, -}; - -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 }, -}; - -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 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, - .bank_swz3_level = 1, - .bank_spreading = 1, -}; - -static 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[] = { - HFI_PROP_BITSTREAM_RESOLUTION, - HFI_PROP_CROP_OFFSETS, - HFI_PROP_CODED_FRAMES, - HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT, - HFI_PROP_PIC_ORDER_CNT_TYPE, - HFI_PROP_PROFILE, - HFI_PROP_LEVEL, - HFI_PROP_SIGNAL_COLOR_INFO, -}; - -static const u32 sm8550_vdec_output_config_params[] = { - HFI_PROP_COLOR_FORMAT, - HFI_PROP_LINEAR_STRIDE_SCANLINE, -}; - -static const u32 sm8550_vdec_subscribe_input_properties[] = { - HFI_PROP_NO_OUTPUT, -}; - -static const u32 sm8550_vdec_subscribe_output_properties[] = { - HFI_PROP_PICTURE_TYPE, - HFI_PROP_CABAC_SESSION, -}; - -static const u32 sm8550_dec_ip_int_buf_tbl[] = { - BUF_BIN, - BUF_COMV, - BUF_NON_COMV, - BUF_LINE, -}; - -static const u32 sm8550_dec_op_int_buf_tbl[] = { - BUF_DPB, -}; - -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, - .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, - .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), - /* Upper bound of DMA address range */ - .dma_mask = 0xe0000000 - 1, - .fwname = "qcom/vpu/vpu30_p4.mbn", - .pas_id = IRIS_PAS_ID, - .inst_caps = &platform_inst_cap_sm8550, - .inst_fw_caps = inst_fw_cap_sm8550, - .inst_fw_caps_size = ARRAY_SIZE(inst_fw_cap_sm8550), - .tz_cp_config_data = &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 = ((8192 * 4352) / 256) * 2, - .input_config_params = - sm8550_vdec_input_config_params, - .input_config_params_size = - ARRAY_SIZE(sm8550_vdec_input_config_params), - .output_config_params = - sm8550_vdec_output_config_params, - .output_config_params_size = - ARRAY_SIZE(sm8550_vdec_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 = sm8550_vdec_subscribe_output_properties, - .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties), - - .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), -}; -- cgit v1.2.3-59-g8ed1b From cae67e94e46a057e3b3d092db04557acce48b82d Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 17 Apr 2025 16:59:07 +0200 Subject: media: platform: qcom/iris: add sm8650 support Add support for the SM8650 platform by re-using the SM8550 definitions and using the vpu33 ops. Move the Sm8650 reset tables that differs in a per-SoC platform header, that will contain mode SoC specific data when more codecs are introduced. The SM8650/vpu33 requires more reset lines, but the H.264 decoder capabilities are identical. Tested-by: Bryan O'Donoghue # x1e Dell Reviewed-by: Bryan O'Donoghue Signed-off-by: Neil Armstrong Reviewed-by: Vikash Garodia Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- .../platform/qcom/iris/iris_platform_common.h | 1 + .../media/platform/qcom/iris/iris_platform_gen2.c | 62 ++++++++++++++++++++++ .../platform/qcom/iris/iris_platform_sm8650.h | 13 +++++ drivers/media/platform/qcom/iris/iris_probe.c | 4 ++ 4 files changed, 80 insertions(+) create mode 100644 drivers/media/platform/qcom/iris/iris_platform_sm8650.h diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index fdd40fd80178..6bc3a7975b04 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -35,6 +35,7 @@ enum pipe_type { extern struct iris_platform_data sm8250_data; extern struct iris_platform_data sm8550_data; +extern struct iris_platform_data sm8650_data; enum platform_clk_type { IRIS_AXI_CLK, diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 35d278996c43..5ff82296ee8e 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -10,6 +10,8 @@ #include "iris_platform_common.h" #include "iris_vpu_common.h" +#include "iris_platform_sm8650.h" + #define VIDEO_ARCH_LX 1 static struct platform_inst_fw_cap inst_fw_cap_sm8550[] = { @@ -264,3 +266,63 @@ struct iris_platform_data sm8550_data = { .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), }; + +/* + * 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" + */ +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, + .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, + .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), + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu33_p4.mbn", + .pas_id = IRIS_PAS_ID, + .inst_caps = &platform_inst_cap_sm8550, + .inst_fw_caps = inst_fw_cap_sm8550, + .inst_fw_caps_size = ARRAY_SIZE(inst_fw_cap_sm8550), + .tz_cp_config_data = &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 = ((8192 * 4352) / 256) * 2, + .input_config_params = + sm8550_vdec_input_config_params, + .input_config_params_size = + ARRAY_SIZE(sm8550_vdec_input_config_params), + .output_config_params = + sm8550_vdec_output_config_params, + .output_config_params_size = + ARRAY_SIZE(sm8550_vdec_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 = sm8550_vdec_subscribe_output_properties, + .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties), + + .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), +}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8650.h b/drivers/media/platform/qcom/iris/iris_platform_sm8650.h new file mode 100644 index 000000000000..75e9d572e788 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_sm8650.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __IRIS_PLATFORM_SM8650_H__ +#define __IRIS_PLATFORM_SM8650_H__ + +static const char * const sm8650_clk_reset_table[] = { "bus", "core" }; + +static const char * const sm8650_controller_reset_table[] = { "xo" }; + +#endif diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index 4f8bce6e2002..7cd8650fbe9c 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -345,6 +345,10 @@ static const struct of_device_id iris_dt_match[] = { .data = &sm8250_data, }, #endif + { + .compatible = "qcom,sm8650-iris", + .data = &sm8650_data, + }, { }, }; MODULE_DEVICE_TABLE(of, iris_dt_match); -- cgit v1.2.3-59-g8ed1b From 6aa5f5887df3b2eaca45ad263acb301540e52802 Mon Sep 17 00:00:00 2001 From: Vikash Garodia Date: Thu, 24 Apr 2025 14:20:45 +0530 Subject: dt-bindings: media: qcom,sm8550-iris: document QCS8300 IRIS accelerator Document the IRIS video decoder/encoder accelerator found in the QCS8300 platform. It belongs to same iris v3 family as that of SM8550 but is a downscaled version of SM8550. It has 2 frame processing hardware blocks while SM8550 has 4. Thereby QCS8300 have fewer capabilities than those of SM8550. Reviewed-by: Bryan O'Donoghue Signed-off-by: Vikash Garodia Acked-by: Krzysztof Kozlowski Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml b/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml index f567f84bd60d..c79bf2101812 100644 --- a/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml @@ -22,6 +22,7 @@ properties: - qcom,sa8775p-iris - const: qcom,sm8550-iris - enum: + - qcom,qcs8300-iris - qcom,sm8550-iris - qcom,sm8650-iris -- cgit v1.2.3-59-g8ed1b From 6490cf1653768a1d4cd9a717a8009785e50495e0 Mon Sep 17 00:00:00 2001 From: Vikash Garodia Date: Thu, 24 Apr 2025 14:20:46 +0530 Subject: media: iris: fix the order of compat strings Fix the order of compatible strings to make it in alpha numeric order. Reviewed-by: Bryan O'Donoghue Signed-off-by: Vikash Garodia Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/iris/iris_probe.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index 7cd8650fbe9c..fa3b9c9b1493 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -335,16 +335,16 @@ static const struct dev_pm_ops iris_pm_ops = { }; static const struct of_device_id iris_dt_match[] = { +#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_VENUS)) + { + .compatible = "qcom,sm8250-venus", + .data = &sm8250_data, + }, +#endif { .compatible = "qcom,sm8550-iris", .data = &sm8550_data, }, -#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_VENUS)) - { - .compatible = "qcom,sm8250-venus", - .data = &sm8250_data, - }, -#endif { .compatible = "qcom,sm8650-iris", .data = &sm8650_data, -- cgit v1.2.3-59-g8ed1b From 146eadf3d1ef3b13d31e814ab213909c98c0427e Mon Sep 17 00:00:00 2001 From: Vikash Garodia Date: Thu, 24 Apr 2025 14:20:47 +0530 Subject: media: iris: add qcs8300 platform data Add platform data for QCS8300, which has different capabilities compared to SM8550. Introduce a QCS8300 header that defines these capabilities. Reviewed-by: Bryan O'Donoghue Signed-off-by: Vikash Garodia Reviewed-by: Dikshita Agarwal Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- .../platform/qcom/iris/iris_platform_common.h | 1 + .../media/platform/qcom/iris/iris_platform_gen2.c | 57 ++++++++++ .../platform/qcom/iris/iris_platform_qcs8300.h | 124 +++++++++++++++++++++ drivers/media/platform/qcom/iris/iris_probe.c | 4 + 4 files changed, 186 insertions(+) create mode 100644 drivers/media/platform/qcom/iris/iris_platform_qcs8300.h diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 6bc3a7975b04..ac76d9e1ef9c 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -33,6 +33,7 @@ enum pipe_type { PIPE_4 = 4, }; +extern struct iris_platform_data qcs8300_data; extern struct iris_platform_data sm8250_data; extern struct iris_platform_data sm8550_data; extern struct iris_platform_data sm8650_data; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 5ff82296ee8e..1e69ba15db0f 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -10,6 +10,7 @@ #include "iris_platform_common.h" #include "iris_vpu_common.h" +#include "iris_platform_qcs8300.h" #include "iris_platform_sm8650.h" #define VIDEO_ARCH_LX 1 @@ -326,3 +327,59 @@ struct iris_platform_data sm8650_data = { .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), }; + +/* + * Shares most of SM8550 data except: + * - inst_caps to platform_inst_cap_qcs8300 + * - inst_fw_caps to inst_fw_cap_qcs8300 + */ +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, + .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, + .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), + /* Upper bound of DMA address range */ + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu/vpu30_p4_s6.mbn", + .pas_id = IRIS_PAS_ID, + .inst_caps = &platform_inst_cap_qcs8300, + .inst_fw_caps = inst_fw_cap_qcs8300, + .inst_fw_caps_size = ARRAY_SIZE(inst_fw_cap_qcs8300), + .tz_cp_config_data = &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, + .input_config_params = + sm8550_vdec_input_config_params, + .input_config_params_size = + ARRAY_SIZE(sm8550_vdec_input_config_params), + .output_config_params = + sm8550_vdec_output_config_params, + .output_config_params_size = + ARRAY_SIZE(sm8550_vdec_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 = sm8550_vdec_subscribe_output_properties, + .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties), + + .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), +}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h new file mode 100644 index 000000000000..f82355d72fcf --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +static struct platform_inst_fw_cap inst_fw_cap_qcs8300[] = { + { + .cap_id = PROFILE, + .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH), + .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + .hfi_id = HFI_PROP_PROFILE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = LEVEL, + .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .max = V4L2_MPEG_VIDEO_H264_LEVEL_6_2, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_2), + .value = V4L2_MPEG_VIDEO_H264_LEVEL_6_1, + .hfi_id = HFI_PROP_LEVEL, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = INPUT_BUF_HOST_MAX_COUNT, + .min = DEFAULT_MAX_HOST_BUF_COUNT, + .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT, + .step_or_mask = 1, + .value = DEFAULT_MAX_HOST_BUF_COUNT, + .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT, + .flags = CAP_FLAG_INPUT_PORT, + .set = iris_set_u32, + }, + { + .cap_id = STAGE, + .min = STAGE_1, + .max = STAGE_2, + .step_or_mask = 1, + .value = STAGE_2, + .hfi_id = HFI_PROP_STAGE, + .set = iris_set_stage, + }, + { + .cap_id = PIPE, + .min = PIPE_1, + .max = PIPE_2, + .step_or_mask = 1, + .value = PIPE_2, + .hfi_id = HFI_PROP_PIPE, + .set = iris_set_pipe, + }, + { + .cap_id = POC, + .min = 0, + .max = 2, + .step_or_mask = 1, + .value = 1, + .hfi_id = HFI_PROP_PIC_ORDER_CNT_TYPE, + }, + { + .cap_id = CODED_FRAMES, + .min = CODED_FRAMES_PROGRESSIVE, + .max = CODED_FRAMES_PROGRESSIVE, + .step_or_mask = 0, + .value = CODED_FRAMES_PROGRESSIVE, + .hfi_id = HFI_PROP_CODED_FRAMES, + }, + { + .cap_id = BIT_DEPTH, + .min = BIT_DEPTH_8, + .max = BIT_DEPTH_8, + .step_or_mask = 1, + .value = BIT_DEPTH_8, + .hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH, + }, + { + .cap_id = RAP_FRAME, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 1, + .hfi_id = HFI_PROP_DEC_START_FROM_RAP_FRAME, + .flags = CAP_FLAG_INPUT_PORT, + .set = iris_set_u32, + }, +}; + +static struct platform_inst_caps platform_inst_cap_qcs8300 = { + .min_frame_width = 96, + .max_frame_width = 4096, + .min_frame_height = 96, + .max_frame_height = 4096, + .max_mbpf = (4096 * 2176) / 256, + .mb_cycles_vpp = 200, + .mb_cycles_fw = 326389, + .mb_cycles_fw_vpp = 44156, + .num_comv = 0, +}; diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index fa3b9c9b1493..9a7ce142f700 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -335,6 +335,10 @@ static const struct dev_pm_ops iris_pm_ops = { }; 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,sm8250-venus", -- cgit v1.2.3-59-g8ed1b From d6a0866750bb14e3c0c16677fc45162513f1be1f Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 24 Apr 2025 10:05:33 +0300 Subject: media: rcar-vin: Add RCAR_GEN4 model value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently Gen4 VINs are marked as RCAN_GEN3 models. Add a new enum value, RCAR_GEN4, and use it for Gen4 VINs. No functional changes in this patch. Signed-off-by: Tomi Valkeinen Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Link: https://lore.kernel.org/r/20250424-rcar-fix-raw-v2-1-f6afca378124@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rcar-vin/rcar-core.c | 2 +- drivers/media/platform/renesas/rcar-vin/rcar-dma.c | 8 ++++---- drivers/media/platform/renesas/rcar-vin/rcar-vin.h | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c index cfbc9ec27706..846ae7989b1d 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c @@ -1273,7 +1273,7 @@ static const struct rvin_info rcar_info_r8a77995 = { }; static const struct rvin_info rcar_info_gen4 = { - .model = RCAR_GEN3, + .model = RCAR_GEN4, .use_mc = true, .use_isp = true, .nv12 = true, diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index f7b80e61a987..374396bcf8b9 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -783,7 +783,7 @@ static int rvin_setup(struct rvin_dev *vin) } /* Make sure input interface and input format is valid. */ - if (vin->info->model == RCAR_GEN3) { + if (vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4) { switch (vnmc & VNMC_INF_MASK) { case VNMC_INF_YUV8_BT656: case VNMC_INF_YUV10_BT656: @@ -806,7 +806,7 @@ static int rvin_setup(struct rvin_dev *vin) } /* Enable VSYNC Field Toggle mode after one VSYNC input */ - if (vin->info->model == RCAR_GEN3) + if (vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4) dmr2 = VNDMR2_FTEV; else dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1); @@ -906,7 +906,7 @@ static int rvin_setup(struct rvin_dev *vin) if (input_is_yuv == output_is_yuv) vnmc |= VNMC_BPS; - if (vin->info->model == RCAR_GEN3) { + if (vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4) { /* Select between CSI-2 and parallel input */ if (vin->is_csi) vnmc &= ~VNMC_DPINE; @@ -1287,7 +1287,7 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, if (rvin_scaler_needed(vin)) { /* Gen3 can't scale NV12 */ - if (vin->info->model == RCAR_GEN3 && + if ((vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4) && vin->format.pixelformat == V4L2_PIX_FMT_NV12) return -EPIPE; diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h index 934474d2334a..83d1b2734c41 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h @@ -39,6 +39,7 @@ enum model_id { RCAR_M1, RCAR_GEN2, RCAR_GEN3, + RCAR_GEN4, }; enum rvin_csi_id { -- cgit v1.2.3-59-g8ed1b From 21cb8227e35ec93c637a2330fc3cb2e63170e7d4 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 24 Apr 2025 10:05:34 +0300 Subject: media: rcar-vin: Remove unnecessary checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unnecessary checks wrt. formats and interfaces in rvin_setup(). The validity of the formats has already been checked earlier. Signed-off-by: Tomi Valkeinen Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Link: https://lore.kernel.org/r/20250424-rcar-fix-raw-v2-2-f6afca378124@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rcar-vin/rcar-dma.c | 23 ---------------------- 1 file changed, 23 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index 374396bcf8b9..e0464eb49b59 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -782,29 +782,6 @@ static int rvin_setup(struct rvin_dev *vin) break; } - /* Make sure input interface and input format is valid. */ - if (vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4) { - switch (vnmc & VNMC_INF_MASK) { - case VNMC_INF_YUV8_BT656: - case VNMC_INF_YUV10_BT656: - case VNMC_INF_YUV16: - case VNMC_INF_RGB666: - if (vin->is_csi) { - vin_err(vin, "Invalid setting in MIPI CSI2\n"); - return -EINVAL; - } - break; - case VNMC_INF_RAW8: - if (!vin->is_csi) { - vin_err(vin, "Invalid setting in Digital Pins\n"); - return -EINVAL; - } - break; - default: - break; - } - } - /* Enable VSYNC Field Toggle mode after one VSYNC input */ if (vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4) dmr2 = VNDMR2_FTEV; -- cgit v1.2.3-59-g8ed1b From 17b5496c30709d83a0ca2d11731720a4dc0ef540 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 24 Apr 2025 10:05:35 +0300 Subject: media: rcar-vin: Fix RAW8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Gen4 we need to set VNMC's EXINF to a different value (1) than in Gen3 (0). Add a define for this, and set the bit for Gen4. Signed-off-by: Tomi Valkeinen Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Link: https://lore.kernel.org/r/20250424-rcar-fix-raw-v2-3-f6afca378124@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rcar-vin/rcar-dma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index e0464eb49b59..65e41c68e627 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -94,6 +94,7 @@ #define VNMC_INF_YUV16 (5 << 16) #define VNMC_INF_RGB888 (6 << 16) #define VNMC_INF_RGB666 (7 << 16) +#define VNMC_EXINF_RAW8 (1 << 12) /* Gen4 specific */ #define VNMC_VUP (1 << 10) #define VNMC_IM_ODD (0 << 3) #define VNMC_IM_ODD_EVEN (1 << 3) @@ -771,6 +772,8 @@ static int rvin_setup(struct rvin_dev *vin) case MEDIA_BUS_FMT_SRGGB8_1X8: case MEDIA_BUS_FMT_Y8_1X8: vnmc |= VNMC_INF_RAW8; + if (vin->info->model == RCAR_GEN4) + vnmc |= VNMC_EXINF_RAW8; break; case MEDIA_BUS_FMT_SBGGR10_1X10: case MEDIA_BUS_FMT_SGBRG10_1X10: -- cgit v1.2.3-59-g8ed1b From 94bf847ae5a61e0ab0b971ed186a443688eb793f Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 24 Apr 2025 10:05:36 +0300 Subject: media: rcar-vin: Fix RAW10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following to get RAW10 formats working: In rvin_formats, the bpp is set to 4 for RAW10. As VIN unpacks RAW10 to 16-bit containers, the bpp should be 2. Don't set VNDMR_YC_THR to the VNDMR register. The YC_THR is "YC Data Through Mode", used for YUV formats and should not be set for RAW10. Fixes: 1b7e7240eaf3 ("media: rcar-vin: Add support for RAW10") Cc: stable@vger.kernel.org Signed-off-by: Tomi Valkeinen Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Link: https://lore.kernel.org/r/20250424-rcar-fix-raw-v2-4-f6afca378124@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rcar-vin/rcar-dma.c | 2 +- drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index 65e41c68e627..5c08ee2c9807 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -870,7 +870,7 @@ static int rvin_setup(struct rvin_dev *vin) case V4L2_PIX_FMT_SGBRG10: case V4L2_PIX_FMT_SGRBG10: case V4L2_PIX_FMT_SRGGB10: - dmr = VNDMR_RMODE_RAW10 | VNDMR_YC_THR; + dmr = VNDMR_RMODE_RAW10; break; default: vin_err(vin, "Invalid pixelformat (0x%x)\n", diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c index ade007a9811f..db091af57c19 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c @@ -88,19 +88,19 @@ static const struct rvin_video_format rvin_formats[] = { }, { .fourcc = V4L2_PIX_FMT_SBGGR10, - .bpp = 4, + .bpp = 2, }, { .fourcc = V4L2_PIX_FMT_SGBRG10, - .bpp = 4, + .bpp = 2, }, { .fourcc = V4L2_PIX_FMT_SGRBG10, - .bpp = 4, + .bpp = 2, }, { .fourcc = V4L2_PIX_FMT_SRGGB10, - .bpp = 4, + .bpp = 2, }, }; -- cgit v1.2.3-59-g8ed1b From 63fd40f67283ce7b3ef7414d73ab1cf78fd9c89c Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Wed, 23 Apr 2025 18:31:07 +0200 Subject: dt-bindings: media: renesas,isp: Add ISP core function block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some R-Car ISP instances have in addition to the channel selector (CS) an ISP core (CORE) to perform operations on an image stream. The core function is mapped to a different memory region and has a separate interrupt than CS, extend the bindings to allow describing this. On the same SoC different instances of the ISP IP may have, or not have, the CORE functionality. The CS function on all instances on the SoC are the same and the documentation describes the full ISP (CS + CORE) as a single IP block. Where instances not having the CORE function simply lack the functionality to modify the image data. There are dependencies on the CS functionality while operating the CORE functionality. In order for the ISP core to function in memory-to-memory mode it needs to be feed input data from a Streaming Bridge interface. This interface is provided thru the VSP-X device. Add an optional new property "renesas,vspx" to provide a phandle to describe this relationship. While adding mandatory reg-names and interrupt-names breaks existing bindings the driver itself remains backward compatible and provides CS functionality if a single unnamed reg and interrupt property is present. Furthermore all existing users of the bindings are updated in following work to add these new mandatory properties. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Reviewed-by: Geert Uytterhoeven Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250423163113.2961049-2-niklas.soderlund+renesas@ragnatech.se Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../devicetree/bindings/media/renesas,isp.yaml | 63 +++++++++++++++++++--- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/media/renesas,isp.yaml b/Documentation/devicetree/bindings/media/renesas,isp.yaml index c4de4555b753..d25e020f5e5e 100644 --- a/Documentation/devicetree/bindings/media/renesas,isp.yaml +++ b/Documentation/devicetree/bindings/media/renesas,isp.yaml @@ -25,19 +25,55 @@ properties: - renesas,r8a779h0-isp # V4M - const: renesas,rcar-gen4-isp # Generic R-Car Gen4 reg: - maxItems: 1 + minItems: 1 + maxItems: 2 + + reg-names: + minItems: 1 + items: + - const: cs + - const: core interrupts: - maxItems: 1 + minItems: 1 + maxItems: 2 + + interrupt-names: + minItems: 1 + items: + - const: cs + - const: core clocks: - maxItems: 1 + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + items: + - const: cs + - const: core power-domains: maxItems: 1 resets: - maxItems: 1 + minItems: 1 + maxItems: 2 + + reset-names: + minItems: 1 + items: + - const: cs + - const: core + + renesas,vspx: + $ref: /schemas/types.yaml#/definitions/phandle + description: + A phandle to the companion VSPX responsible for the Streaming Bridge + functionality. The Streaming Bridge is responsible for feeding image + and configuration data to the ISP when operating in memory-to-memory + mode. ports: $ref: /schemas/graph.yaml#/properties/ports @@ -103,10 +139,14 @@ properties: required: - compatible - reg + - reg-names - interrupts + - interrupt-names - clocks + - clock-names - power-domains - resets + - reset-names - ports additionalProperties: false @@ -119,11 +159,18 @@ examples: isp1: isp@fed20000 { compatible = "renesas,r8a779a0-isp", "renesas,rcar-gen4-isp"; - reg = <0xfed20000 0x10000>; - interrupts = ; - clocks = <&cpg CPG_MOD 613>; + reg = <0xfed20000 0x10000>, <0xfee00000 0x100000>; + reg-names = "cs", "core"; + interrupts = , + ; + interrupt-names = "cs", "core"; + clocks = <&cpg CPG_MOD 613>, <&cpg CPG_MOD 17>; + clock-names = "cs", "core"; power-domains = <&sysc R8A779A0_PD_A3ISP01>; - resets = <&cpg 613>; + resets = <&cpg 613>, <&cpg 17>; + reset-names = "cs", "core"; + + renesas,vspx = <&vspx1>; ports { #address-cells = <1>; -- cgit v1.2.3-59-g8ed1b From 9103d33f22b12f9633157d2e853594aabc377eb7 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Wed, 23 Apr 2025 18:31:11 +0200 Subject: media: rcar-isp: Move driver to own directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before extending the driver with functions from the R-Car ISP core that will span multiple files move the existing driver to a separate directory. While at it rename the single source file to allow future files to be grouped by functions. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20250423163113.2961049-6-niklas.soderlund+renesas@ragnatech.se Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- MAINTAINERS | 2 +- drivers/media/platform/renesas/Kconfig | 18 +- drivers/media/platform/renesas/Makefile | 2 +- drivers/media/platform/renesas/rcar-isp.c | 582 ----------------------- drivers/media/platform/renesas/rcar-isp/Kconfig | 18 + drivers/media/platform/renesas/rcar-isp/Makefile | 4 + drivers/media/platform/renesas/rcar-isp/csisp.c | 582 +++++++++++++++++++++++ 7 files changed, 607 insertions(+), 601 deletions(-) delete mode 100644 drivers/media/platform/renesas/rcar-isp.c create mode 100644 drivers/media/platform/renesas/rcar-isp/Kconfig create mode 100644 drivers/media/platform/renesas/rcar-isp/Makefile create mode 100644 drivers/media/platform/renesas/rcar-isp/csisp.c diff --git a/MAINTAINERS b/MAINTAINERS index 1b5e8ec57851..5dee8459a614 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14897,7 +14897,7 @@ F: Documentation/devicetree/bindings/media/renesas,csi2.yaml F: Documentation/devicetree/bindings/media/renesas,isp.yaml F: Documentation/devicetree/bindings/media/renesas,vin.yaml F: drivers/media/platform/renesas/rcar-csi2.c -F: drivers/media/platform/renesas/rcar-isp.c +F: drivers/media/platform/renesas/rcar-isp/ F: drivers/media/platform/renesas/rcar-vin/ MEDIA DRIVERS FOR RENESAS - VSP1 diff --git a/drivers/media/platform/renesas/Kconfig b/drivers/media/platform/renesas/Kconfig index c7fc718a30a5..27a54fa79083 100644 --- a/drivers/media/platform/renesas/Kconfig +++ b/drivers/media/platform/renesas/Kconfig @@ -30,23 +30,6 @@ config VIDEO_RCAR_CSI2 To compile this driver as a module, choose M here: the module will be called rcar-csi2. -config VIDEO_RCAR_ISP - tristate "R-Car Image Signal Processor (ISP)" - depends on V4L_PLATFORM_DRIVERS - depends on VIDEO_DEV && OF - depends on ARCH_RENESAS || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select RESET_CONTROLLER - select V4L2_FWNODE - help - Support for Renesas R-Car Image Signal Processor (ISP). - Enable this to support the Renesas R-Car Image Signal - Processor (ISP). - - To compile this driver as a module, choose M here: the - module will be called rcar-isp. - config VIDEO_SH_VOU tristate "SuperH VOU video output driver" depends on V4L_PLATFORM_DRIVERS @@ -56,6 +39,7 @@ config VIDEO_SH_VOU help Support for the Video Output Unit (VOU) on SuperH SoCs. +source "drivers/media/platform/renesas/rcar-isp/Kconfig" source "drivers/media/platform/renesas/rcar-vin/Kconfig" source "drivers/media/platform/renesas/rzg2l-cru/Kconfig" diff --git a/drivers/media/platform/renesas/Makefile b/drivers/media/platform/renesas/Makefile index 50774a20330c..1127259c09d6 100644 --- a/drivers/media/platform/renesas/Makefile +++ b/drivers/media/platform/renesas/Makefile @@ -3,13 +3,13 @@ # Makefile for the Renesas capture/playback device drivers. # +obj-y += rcar-isp/ obj-y += rcar-vin/ obj-y += rzg2l-cru/ obj-y += vsp1/ obj-$(CONFIG_VIDEO_RCAR_CSI2) += rcar-csi2.o obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.o -obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o obj-$(CONFIG_VIDEO_RENESAS_CEU) += renesas-ceu.o obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o obj-$(CONFIG_VIDEO_RENESAS_FDP1) += rcar_fdp1.o diff --git a/drivers/media/platform/renesas/rcar-isp.c b/drivers/media/platform/renesas/rcar-isp.c deleted file mode 100644 index 4bc89d4757fa..000000000000 --- a/drivers/media/platform/renesas/rcar-isp.c +++ /dev/null @@ -1,582 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2021 Renesas Electronics Corp. - * - * Driver for Renesas R-Car ISP Channel Selector - * - * The ISP hardware is capable of more than just channel selection, features - * such as demosaicing, white balance control and color space conversion are - * also possible. These more advanced features are not supported by the driver - * due to lack of documentation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#define ISPINPUTSEL0_REG 0x0008 -#define ISPINPUTSEL0_SEL_CSI0 BIT(31) - -#define ISPSTART_REG 0x0014 -#define ISPSTART_START 0xffff -#define ISPSTART_STOP 0x0000 - -#define ISPPROCMODE_DT_REG(n) (0x1100 + (0x4 * (n))) -#define ISPPROCMODE_DT_PROC_MODE_VC3(pm) (((pm) & 0x3f) << 24) -#define ISPPROCMODE_DT_PROC_MODE_VC2(pm) (((pm) & 0x3f) << 16) -#define ISPPROCMODE_DT_PROC_MODE_VC1(pm) (((pm) & 0x3f) << 8) -#define ISPPROCMODE_DT_PROC_MODE_VC0(pm) ((pm) & 0x3f) - -#define ISPCS_FILTER_ID_CH_REG(n) (0x3000 + (0x0100 * (n))) - -#define ISPCS_DT_CODE03_CH_REG(n) (0x3008 + (0x100 * (n))) -#define ISPCS_DT_CODE03_EN3 BIT(31) -#define ISPCS_DT_CODE03_DT3(dt) (((dt) & 0x3f) << 24) -#define ISPCS_DT_CODE03_EN2 BIT(23) -#define ISPCS_DT_CODE03_DT2(dt) (((dt) & 0x3f) << 16) -#define ISPCS_DT_CODE03_EN1 BIT(15) -#define ISPCS_DT_CODE03_DT1(dt) (((dt) & 0x3f) << 8) -#define ISPCS_DT_CODE03_EN0 BIT(7) -#define ISPCS_DT_CODE03_DT0(dt) ((dt) & 0x3f) - -struct rcar_isp_format { - u32 code; - unsigned int datatype; - unsigned int procmode; -}; - -static const struct rcar_isp_format rcar_isp_formats[] = { - { - .code = MEDIA_BUS_FMT_RGB888_1X24, - .datatype = MIPI_CSI2_DT_RGB888, - .procmode = 0x15 - }, { - .code = MEDIA_BUS_FMT_Y10_1X10, - .datatype = MIPI_CSI2_DT_RAW10, - .procmode = 0x10, - }, { - .code = MEDIA_BUS_FMT_UYVY8_1X16, - .datatype = MIPI_CSI2_DT_YUV422_8B, - .procmode = 0x0c, - }, { - .code = MEDIA_BUS_FMT_YUYV8_1X16, - .datatype = MIPI_CSI2_DT_YUV422_8B, - .procmode = 0x0c, - }, { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .datatype = MIPI_CSI2_DT_YUV422_8B, - .procmode = 0x0c, - }, { - .code = MEDIA_BUS_FMT_YUYV10_2X10, - .datatype = MIPI_CSI2_DT_YUV422_8B, - .procmode = 0x0c, - }, { - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .datatype = MIPI_CSI2_DT_RAW8, - .procmode = 0x00, - }, { - .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .datatype = MIPI_CSI2_DT_RAW8, - .procmode = 0x00, - }, { - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .datatype = MIPI_CSI2_DT_RAW8, - .procmode = 0x00, - }, { - .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .datatype = MIPI_CSI2_DT_RAW8, - .procmode = 0x00, - }, { - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .datatype = MIPI_CSI2_DT_RAW10, - .procmode = 0x01, - }, { - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .datatype = MIPI_CSI2_DT_RAW10, - .procmode = 0x01, - }, { - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .datatype = MIPI_CSI2_DT_RAW10, - .procmode = 0x01, - }, { - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .datatype = MIPI_CSI2_DT_RAW10, - .procmode = 0x01, - }, { - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .datatype = MIPI_CSI2_DT_RAW12, - .procmode = 0x02, - }, { - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .datatype = MIPI_CSI2_DT_RAW12, - .procmode = 0x02, - }, { - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .datatype = MIPI_CSI2_DT_RAW12, - .procmode = 0x02, - }, { - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .datatype = MIPI_CSI2_DT_RAW12, - .procmode = 0x02, - }, -}; - -static const struct rcar_isp_format *risp_code_to_fmt(unsigned int code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(rcar_isp_formats); i++) { - if (rcar_isp_formats[i].code == code) - return &rcar_isp_formats[i]; - } - - return NULL; -} - -enum rcar_isp_input { - RISP_CSI_INPUT0, - RISP_CSI_INPUT1, -}; - -enum rcar_isp_pads { - RCAR_ISP_SINK, - RCAR_ISP_PORT0, - RCAR_ISP_PORT1, - RCAR_ISP_PORT2, - RCAR_ISP_PORT3, - RCAR_ISP_PORT4, - RCAR_ISP_PORT5, - RCAR_ISP_PORT6, - RCAR_ISP_PORT7, - RCAR_ISP_NUM_PADS, -}; - -struct rcar_isp { - struct device *dev; - void __iomem *base; - struct reset_control *rstc; - - enum rcar_isp_input csi_input; - - struct v4l2_subdev subdev; - struct media_pad pads[RCAR_ISP_NUM_PADS]; - - struct v4l2_async_notifier notifier; - struct v4l2_subdev *remote; - unsigned int remote_pad; - - int stream_count; -}; - -static inline struct rcar_isp *sd_to_isp(struct v4l2_subdev *sd) -{ - return container_of(sd, struct rcar_isp, subdev); -} - -static inline struct rcar_isp *notifier_to_isp(struct v4l2_async_notifier *n) -{ - return container_of(n, struct rcar_isp, notifier); -} - -static void risp_write(struct rcar_isp *isp, u32 offset, u32 value) -{ - iowrite32(value, isp->base + offset); -} - -static u32 risp_read(struct rcar_isp *isp, u32 offset) -{ - return ioread32(isp->base + offset); -} - -static int risp_power_on(struct rcar_isp *isp) -{ - int ret; - - ret = pm_runtime_resume_and_get(isp->dev); - if (ret < 0) - return ret; - - ret = reset_control_deassert(isp->rstc); - if (ret < 0) { - pm_runtime_put(isp->dev); - return ret; - } - - return 0; -} - -static void risp_power_off(struct rcar_isp *isp) -{ - reset_control_assert(isp->rstc); - pm_runtime_put(isp->dev); -} - -static int risp_start(struct rcar_isp *isp, struct v4l2_subdev_state *state) -{ - const struct v4l2_mbus_framefmt *fmt; - const struct rcar_isp_format *format; - unsigned int vc; - u32 sel_csi = 0; - int ret; - - fmt = v4l2_subdev_state_get_format(state, RCAR_ISP_SINK); - if (!fmt) - return -EINVAL; - - format = risp_code_to_fmt(fmt->code); - if (!format) { - dev_err(isp->dev, "Unsupported bus format\n"); - return -EINVAL; - } - - ret = risp_power_on(isp); - if (ret) { - dev_err(isp->dev, "Failed to power on ISP\n"); - return ret; - } - - /* Select CSI-2 input source. */ - if (isp->csi_input == RISP_CSI_INPUT1) - sel_csi = ISPINPUTSEL0_SEL_CSI0; - - risp_write(isp, ISPINPUTSEL0_REG, - risp_read(isp, ISPINPUTSEL0_REG) | sel_csi); - - /* Configure Channel Selector. */ - for (vc = 0; vc < 4; vc++) { - u8 ch = vc + 4; - u8 dt = format->datatype; - - risp_write(isp, ISPCS_FILTER_ID_CH_REG(ch), BIT(vc)); - risp_write(isp, ISPCS_DT_CODE03_CH_REG(ch), - ISPCS_DT_CODE03_EN3 | ISPCS_DT_CODE03_DT3(dt) | - ISPCS_DT_CODE03_EN2 | ISPCS_DT_CODE03_DT2(dt) | - ISPCS_DT_CODE03_EN1 | ISPCS_DT_CODE03_DT1(dt) | - ISPCS_DT_CODE03_EN0 | ISPCS_DT_CODE03_DT0(dt)); - } - - /* Setup processing method. */ - risp_write(isp, ISPPROCMODE_DT_REG(format->datatype), - ISPPROCMODE_DT_PROC_MODE_VC3(format->procmode) | - ISPPROCMODE_DT_PROC_MODE_VC2(format->procmode) | - ISPPROCMODE_DT_PROC_MODE_VC1(format->procmode) | - ISPPROCMODE_DT_PROC_MODE_VC0(format->procmode)); - - /* Start ISP. */ - risp_write(isp, ISPSTART_REG, ISPSTART_START); - - ret = v4l2_subdev_enable_streams(isp->remote, isp->remote_pad, - BIT_ULL(0)); - if (ret) - risp_power_off(isp); - - return ret; -} - -static void risp_stop(struct rcar_isp *isp) -{ - v4l2_subdev_disable_streams(isp->remote, isp->remote_pad, BIT_ULL(0)); - - /* Stop ISP. */ - risp_write(isp, ISPSTART_REG, ISPSTART_STOP); - - risp_power_off(isp); -} - -static int risp_enable_streams(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, u32 source_pad, - u64 source_streams_mask) -{ - struct rcar_isp *isp = sd_to_isp(sd); - int ret = 0; - - if (source_streams_mask != 1) - return -EINVAL; - - if (!isp->remote) - return -ENODEV; - - if (isp->stream_count == 0) { - ret = risp_start(isp, state); - if (ret) - return ret; - } - - isp->stream_count += 1; - - return ret; -} - -static int risp_disable_streams(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, u32 source_pad, - u64 source_streams_mask) -{ - struct rcar_isp *isp = sd_to_isp(sd); - - if (source_streams_mask != 1) - return -EINVAL; - - if (!isp->remote) - return -ENODEV; - - if (isp->stream_count == 1) - risp_stop(isp); - - isp->stream_count -= 1; - - return 0; -} - -static int risp_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *framefmt; - - if (format->pad > RCAR_ISP_SINK) - return v4l2_subdev_get_fmt(sd, state, format); - - if (!risp_code_to_fmt(format->format.code)) - format->format.code = rcar_isp_formats[0].code; - - for (unsigned int i = 0; i < RCAR_ISP_NUM_PADS; i++) { - framefmt = v4l2_subdev_state_get_format(state, i); - *framefmt = format->format; - } - - return 0; -} - -static const struct v4l2_subdev_pad_ops risp_pad_ops = { - .enable_streams = risp_enable_streams, - .disable_streams = risp_disable_streams, - .set_fmt = risp_set_pad_format, - .get_fmt = v4l2_subdev_get_fmt, - .link_validate = v4l2_subdev_link_validate_default, -}; - -static const struct v4l2_subdev_ops rcar_isp_subdev_ops = { - .pad = &risp_pad_ops, -}; - -/* ----------------------------------------------------------------------------- - * Async handling and registration of subdevices and links - */ - -static int risp_notify_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_connection *asd) -{ - struct rcar_isp *isp = notifier_to_isp(notifier); - int pad; - - pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode, - MEDIA_PAD_FL_SOURCE); - if (pad < 0) { - dev_err(isp->dev, "Failed to find pad for %s\n", subdev->name); - return pad; - } - - isp->remote = subdev; - isp->remote_pad = pad; - - dev_dbg(isp->dev, "Bound %s pad: %d\n", subdev->name, pad); - - return media_create_pad_link(&subdev->entity, pad, - &isp->subdev.entity, 0, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); -} - -static void risp_notify_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_connection *asd) -{ - struct rcar_isp *isp = notifier_to_isp(notifier); - - isp->remote = NULL; - - dev_dbg(isp->dev, "Unbind %s\n", subdev->name); -} - -static const struct v4l2_async_notifier_operations risp_notify_ops = { - .bound = risp_notify_bound, - .unbind = risp_notify_unbind, -}; - -static int risp_parse_dt(struct rcar_isp *isp) -{ - struct v4l2_async_connection *asd; - struct fwnode_handle *fwnode; - struct fwnode_handle *ep; - unsigned int id; - int ret; - - for (id = 0; id < 2; id++) { - ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(isp->dev), - 0, id, 0); - if (ep) - break; - } - - if (!ep) { - dev_err(isp->dev, "Not connected to subdevice\n"); - return -EINVAL; - } - - if (id == 1) - isp->csi_input = RISP_CSI_INPUT1; - - fwnode = fwnode_graph_get_remote_endpoint(ep); - fwnode_handle_put(ep); - - dev_dbg(isp->dev, "Found '%pOF'\n", to_of_node(fwnode)); - - v4l2_async_subdev_nf_init(&isp->notifier, &isp->subdev); - isp->notifier.ops = &risp_notify_ops; - - asd = v4l2_async_nf_add_fwnode(&isp->notifier, fwnode, - struct v4l2_async_connection); - fwnode_handle_put(fwnode); - if (IS_ERR(asd)) - return PTR_ERR(asd); - - ret = v4l2_async_nf_register(&isp->notifier); - if (ret) - v4l2_async_nf_cleanup(&isp->notifier); - - return ret; -} - -/* ----------------------------------------------------------------------------- - * Platform Device Driver - */ - -static const struct media_entity_operations risp_entity_ops = { - .link_validate = v4l2_subdev_link_validate, -}; - -static int risp_probe_resources(struct rcar_isp *isp, - struct platform_device *pdev) -{ - isp->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); - if (IS_ERR(isp->base)) - return PTR_ERR(isp->base); - - isp->rstc = devm_reset_control_get(&pdev->dev, NULL); - - return PTR_ERR_OR_ZERO(isp->rstc); -} - -static const struct of_device_id risp_of_id_table[] = { - { .compatible = "renesas,r8a779a0-isp" }, - { .compatible = "renesas,r8a779g0-isp" }, - /* Keep above for compatibility with old DTB files. */ - { .compatible = "renesas,rcar-gen4-isp" }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, risp_of_id_table); - -static int risp_probe(struct platform_device *pdev) -{ - struct rcar_isp *isp; - unsigned int i; - int ret; - - isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL); - if (!isp) - return -ENOMEM; - - isp->dev = &pdev->dev; - - ret = risp_probe_resources(isp, pdev); - if (ret) { - dev_err(isp->dev, "Failed to get resources\n"); - return ret; - } - - platform_set_drvdata(pdev, isp); - - pm_runtime_enable(&pdev->dev); - - ret = risp_parse_dt(isp); - if (ret) - goto error_pm; - - isp->subdev.owner = THIS_MODULE; - isp->subdev.dev = &pdev->dev; - v4l2_subdev_init(&isp->subdev, &rcar_isp_subdev_ops); - v4l2_set_subdevdata(&isp->subdev, &pdev->dev); - snprintf(isp->subdev.name, sizeof(isp->subdev.name), "%s %s", - KBUILD_MODNAME, dev_name(&pdev->dev)); - isp->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; - - isp->subdev.entity.function = MEDIA_ENT_F_VID_MUX; - isp->subdev.entity.ops = &risp_entity_ops; - - isp->pads[RCAR_ISP_SINK].flags = MEDIA_PAD_FL_SINK; - for (i = RCAR_ISP_PORT0; i < RCAR_ISP_NUM_PADS; i++) - isp->pads[i].flags = MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(&isp->subdev.entity, RCAR_ISP_NUM_PADS, - isp->pads); - if (ret) - goto error_notifier; - - ret = v4l2_subdev_init_finalize(&isp->subdev); - if (ret) - goto error_notifier; - - ret = v4l2_async_register_subdev(&isp->subdev); - if (ret < 0) - goto error_subdev; - - dev_info(isp->dev, "Using CSI-2 input: %u\n", isp->csi_input); - - return 0; - -error_subdev: - v4l2_subdev_cleanup(&isp->subdev); -error_notifier: - v4l2_async_nf_unregister(&isp->notifier); - v4l2_async_nf_cleanup(&isp->notifier); -error_pm: - pm_runtime_disable(&pdev->dev); - - return ret; -} - -static void risp_remove(struct platform_device *pdev) -{ - struct rcar_isp *isp = platform_get_drvdata(pdev); - - v4l2_async_nf_unregister(&isp->notifier); - v4l2_async_nf_cleanup(&isp->notifier); - - v4l2_async_unregister_subdev(&isp->subdev); - v4l2_subdev_cleanup(&isp->subdev); - - pm_runtime_disable(&pdev->dev); -} - -static struct platform_driver rcar_isp_driver = { - .driver = { - .name = "rcar-isp", - .suppress_bind_attrs = true, - .of_match_table = risp_of_id_table, - }, - .probe = risp_probe, - .remove = risp_remove, -}; - -module_platform_driver(rcar_isp_driver); - -MODULE_AUTHOR("Niklas Söderlund "); -MODULE_DESCRIPTION("Renesas R-Car ISP Channel Selector driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/renesas/rcar-isp/Kconfig b/drivers/media/platform/renesas/rcar-isp/Kconfig new file mode 100644 index 000000000000..242f6a23851f --- /dev/null +++ b/drivers/media/platform/renesas/rcar-isp/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0 + +config VIDEO_RCAR_ISP + tristate "R-Car Image Signal Processor (ISP)" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + depends on ARCH_RENESAS || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select RESET_CONTROLLER + select V4L2_FWNODE + help + Support for Renesas R-Car Image Signal Processor (ISP). + Enable this to support the Renesas R-Car Image Signal + Processor (ISP). + + To compile this driver as a module, choose M here: the + module will be called rcar-isp. diff --git a/drivers/media/platform/renesas/rcar-isp/Makefile b/drivers/media/platform/renesas/rcar-isp/Makefile new file mode 100644 index 000000000000..b542118c831e --- /dev/null +++ b/drivers/media/platform/renesas/rcar-isp/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +rcar-isp-objs = csisp.o + +obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o diff --git a/drivers/media/platform/renesas/rcar-isp/csisp.c b/drivers/media/platform/renesas/rcar-isp/csisp.c new file mode 100644 index 000000000000..4bc89d4757fa --- /dev/null +++ b/drivers/media/platform/renesas/rcar-isp/csisp.c @@ -0,0 +1,582 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Renesas Electronics Corp. + * + * Driver for Renesas R-Car ISP Channel Selector + * + * The ISP hardware is capable of more than just channel selection, features + * such as demosaicing, white balance control and color space conversion are + * also possible. These more advanced features are not supported by the driver + * due to lack of documentation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define ISPINPUTSEL0_REG 0x0008 +#define ISPINPUTSEL0_SEL_CSI0 BIT(31) + +#define ISPSTART_REG 0x0014 +#define ISPSTART_START 0xffff +#define ISPSTART_STOP 0x0000 + +#define ISPPROCMODE_DT_REG(n) (0x1100 + (0x4 * (n))) +#define ISPPROCMODE_DT_PROC_MODE_VC3(pm) (((pm) & 0x3f) << 24) +#define ISPPROCMODE_DT_PROC_MODE_VC2(pm) (((pm) & 0x3f) << 16) +#define ISPPROCMODE_DT_PROC_MODE_VC1(pm) (((pm) & 0x3f) << 8) +#define ISPPROCMODE_DT_PROC_MODE_VC0(pm) ((pm) & 0x3f) + +#define ISPCS_FILTER_ID_CH_REG(n) (0x3000 + (0x0100 * (n))) + +#define ISPCS_DT_CODE03_CH_REG(n) (0x3008 + (0x100 * (n))) +#define ISPCS_DT_CODE03_EN3 BIT(31) +#define ISPCS_DT_CODE03_DT3(dt) (((dt) & 0x3f) << 24) +#define ISPCS_DT_CODE03_EN2 BIT(23) +#define ISPCS_DT_CODE03_DT2(dt) (((dt) & 0x3f) << 16) +#define ISPCS_DT_CODE03_EN1 BIT(15) +#define ISPCS_DT_CODE03_DT1(dt) (((dt) & 0x3f) << 8) +#define ISPCS_DT_CODE03_EN0 BIT(7) +#define ISPCS_DT_CODE03_DT0(dt) ((dt) & 0x3f) + +struct rcar_isp_format { + u32 code; + unsigned int datatype; + unsigned int procmode; +}; + +static const struct rcar_isp_format rcar_isp_formats[] = { + { + .code = MEDIA_BUS_FMT_RGB888_1X24, + .datatype = MIPI_CSI2_DT_RGB888, + .procmode = 0x15 + }, { + .code = MEDIA_BUS_FMT_Y10_1X10, + .datatype = MIPI_CSI2_DT_RAW10, + .procmode = 0x10, + }, { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .procmode = 0x0c, + }, { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .procmode = 0x0c, + }, { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .procmode = 0x0c, + }, { + .code = MEDIA_BUS_FMT_YUYV10_2X10, + .datatype = MIPI_CSI2_DT_YUV422_8B, + .procmode = 0x0c, + }, { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .datatype = MIPI_CSI2_DT_RAW8, + .procmode = 0x00, + }, { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .datatype = MIPI_CSI2_DT_RAW8, + .procmode = 0x00, + }, { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .datatype = MIPI_CSI2_DT_RAW8, + .procmode = 0x00, + }, { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .datatype = MIPI_CSI2_DT_RAW8, + .procmode = 0x00, + }, { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .datatype = MIPI_CSI2_DT_RAW10, + .procmode = 0x01, + }, { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .datatype = MIPI_CSI2_DT_RAW10, + .procmode = 0x01, + }, { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .datatype = MIPI_CSI2_DT_RAW10, + .procmode = 0x01, + }, { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .datatype = MIPI_CSI2_DT_RAW10, + .procmode = 0x01, + }, { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .datatype = MIPI_CSI2_DT_RAW12, + .procmode = 0x02, + }, { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .datatype = MIPI_CSI2_DT_RAW12, + .procmode = 0x02, + }, { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .datatype = MIPI_CSI2_DT_RAW12, + .procmode = 0x02, + }, { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .datatype = MIPI_CSI2_DT_RAW12, + .procmode = 0x02, + }, +}; + +static const struct rcar_isp_format *risp_code_to_fmt(unsigned int code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rcar_isp_formats); i++) { + if (rcar_isp_formats[i].code == code) + return &rcar_isp_formats[i]; + } + + return NULL; +} + +enum rcar_isp_input { + RISP_CSI_INPUT0, + RISP_CSI_INPUT1, +}; + +enum rcar_isp_pads { + RCAR_ISP_SINK, + RCAR_ISP_PORT0, + RCAR_ISP_PORT1, + RCAR_ISP_PORT2, + RCAR_ISP_PORT3, + RCAR_ISP_PORT4, + RCAR_ISP_PORT5, + RCAR_ISP_PORT6, + RCAR_ISP_PORT7, + RCAR_ISP_NUM_PADS, +}; + +struct rcar_isp { + struct device *dev; + void __iomem *base; + struct reset_control *rstc; + + enum rcar_isp_input csi_input; + + struct v4l2_subdev subdev; + struct media_pad pads[RCAR_ISP_NUM_PADS]; + + struct v4l2_async_notifier notifier; + struct v4l2_subdev *remote; + unsigned int remote_pad; + + int stream_count; +}; + +static inline struct rcar_isp *sd_to_isp(struct v4l2_subdev *sd) +{ + return container_of(sd, struct rcar_isp, subdev); +} + +static inline struct rcar_isp *notifier_to_isp(struct v4l2_async_notifier *n) +{ + return container_of(n, struct rcar_isp, notifier); +} + +static void risp_write(struct rcar_isp *isp, u32 offset, u32 value) +{ + iowrite32(value, isp->base + offset); +} + +static u32 risp_read(struct rcar_isp *isp, u32 offset) +{ + return ioread32(isp->base + offset); +} + +static int risp_power_on(struct rcar_isp *isp) +{ + int ret; + + ret = pm_runtime_resume_and_get(isp->dev); + if (ret < 0) + return ret; + + ret = reset_control_deassert(isp->rstc); + if (ret < 0) { + pm_runtime_put(isp->dev); + return ret; + } + + return 0; +} + +static void risp_power_off(struct rcar_isp *isp) +{ + reset_control_assert(isp->rstc); + pm_runtime_put(isp->dev); +} + +static int risp_start(struct rcar_isp *isp, struct v4l2_subdev_state *state) +{ + const struct v4l2_mbus_framefmt *fmt; + const struct rcar_isp_format *format; + unsigned int vc; + u32 sel_csi = 0; + int ret; + + fmt = v4l2_subdev_state_get_format(state, RCAR_ISP_SINK); + if (!fmt) + return -EINVAL; + + format = risp_code_to_fmt(fmt->code); + if (!format) { + dev_err(isp->dev, "Unsupported bus format\n"); + return -EINVAL; + } + + ret = risp_power_on(isp); + if (ret) { + dev_err(isp->dev, "Failed to power on ISP\n"); + return ret; + } + + /* Select CSI-2 input source. */ + if (isp->csi_input == RISP_CSI_INPUT1) + sel_csi = ISPINPUTSEL0_SEL_CSI0; + + risp_write(isp, ISPINPUTSEL0_REG, + risp_read(isp, ISPINPUTSEL0_REG) | sel_csi); + + /* Configure Channel Selector. */ + for (vc = 0; vc < 4; vc++) { + u8 ch = vc + 4; + u8 dt = format->datatype; + + risp_write(isp, ISPCS_FILTER_ID_CH_REG(ch), BIT(vc)); + risp_write(isp, ISPCS_DT_CODE03_CH_REG(ch), + ISPCS_DT_CODE03_EN3 | ISPCS_DT_CODE03_DT3(dt) | + ISPCS_DT_CODE03_EN2 | ISPCS_DT_CODE03_DT2(dt) | + ISPCS_DT_CODE03_EN1 | ISPCS_DT_CODE03_DT1(dt) | + ISPCS_DT_CODE03_EN0 | ISPCS_DT_CODE03_DT0(dt)); + } + + /* Setup processing method. */ + risp_write(isp, ISPPROCMODE_DT_REG(format->datatype), + ISPPROCMODE_DT_PROC_MODE_VC3(format->procmode) | + ISPPROCMODE_DT_PROC_MODE_VC2(format->procmode) | + ISPPROCMODE_DT_PROC_MODE_VC1(format->procmode) | + ISPPROCMODE_DT_PROC_MODE_VC0(format->procmode)); + + /* Start ISP. */ + risp_write(isp, ISPSTART_REG, ISPSTART_START); + + ret = v4l2_subdev_enable_streams(isp->remote, isp->remote_pad, + BIT_ULL(0)); + if (ret) + risp_power_off(isp); + + return ret; +} + +static void risp_stop(struct rcar_isp *isp) +{ + v4l2_subdev_disable_streams(isp->remote, isp->remote_pad, BIT_ULL(0)); + + /* Stop ISP. */ + risp_write(isp, ISPSTART_REG, ISPSTART_STOP); + + risp_power_off(isp); +} + +static int risp_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 source_pad, + u64 source_streams_mask) +{ + struct rcar_isp *isp = sd_to_isp(sd); + int ret = 0; + + if (source_streams_mask != 1) + return -EINVAL; + + if (!isp->remote) + return -ENODEV; + + if (isp->stream_count == 0) { + ret = risp_start(isp, state); + if (ret) + return ret; + } + + isp->stream_count += 1; + + return ret; +} + +static int risp_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 source_pad, + u64 source_streams_mask) +{ + struct rcar_isp *isp = sd_to_isp(sd); + + if (source_streams_mask != 1) + return -EINVAL; + + if (!isp->remote) + return -ENODEV; + + if (isp->stream_count == 1) + risp_stop(isp); + + isp->stream_count -= 1; + + return 0; +} + +static int risp_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *framefmt; + + if (format->pad > RCAR_ISP_SINK) + return v4l2_subdev_get_fmt(sd, state, format); + + if (!risp_code_to_fmt(format->format.code)) + format->format.code = rcar_isp_formats[0].code; + + for (unsigned int i = 0; i < RCAR_ISP_NUM_PADS; i++) { + framefmt = v4l2_subdev_state_get_format(state, i); + *framefmt = format->format; + } + + return 0; +} + +static const struct v4l2_subdev_pad_ops risp_pad_ops = { + .enable_streams = risp_enable_streams, + .disable_streams = risp_disable_streams, + .set_fmt = risp_set_pad_format, + .get_fmt = v4l2_subdev_get_fmt, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct v4l2_subdev_ops rcar_isp_subdev_ops = { + .pad = &risp_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Async handling and registration of subdevices and links + */ + +static int risp_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct rcar_isp *isp = notifier_to_isp(notifier); + int pad; + + pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode, + MEDIA_PAD_FL_SOURCE); + if (pad < 0) { + dev_err(isp->dev, "Failed to find pad for %s\n", subdev->name); + return pad; + } + + isp->remote = subdev; + isp->remote_pad = pad; + + dev_dbg(isp->dev, "Bound %s pad: %d\n", subdev->name, pad); + + return media_create_pad_link(&subdev->entity, pad, + &isp->subdev.entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} + +static void risp_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct rcar_isp *isp = notifier_to_isp(notifier); + + isp->remote = NULL; + + dev_dbg(isp->dev, "Unbind %s\n", subdev->name); +} + +static const struct v4l2_async_notifier_operations risp_notify_ops = { + .bound = risp_notify_bound, + .unbind = risp_notify_unbind, +}; + +static int risp_parse_dt(struct rcar_isp *isp) +{ + struct v4l2_async_connection *asd; + struct fwnode_handle *fwnode; + struct fwnode_handle *ep; + unsigned int id; + int ret; + + for (id = 0; id < 2; id++) { + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(isp->dev), + 0, id, 0); + if (ep) + break; + } + + if (!ep) { + dev_err(isp->dev, "Not connected to subdevice\n"); + return -EINVAL; + } + + if (id == 1) + isp->csi_input = RISP_CSI_INPUT1; + + fwnode = fwnode_graph_get_remote_endpoint(ep); + fwnode_handle_put(ep); + + dev_dbg(isp->dev, "Found '%pOF'\n", to_of_node(fwnode)); + + v4l2_async_subdev_nf_init(&isp->notifier, &isp->subdev); + isp->notifier.ops = &risp_notify_ops; + + asd = v4l2_async_nf_add_fwnode(&isp->notifier, fwnode, + struct v4l2_async_connection); + fwnode_handle_put(fwnode); + if (IS_ERR(asd)) + return PTR_ERR(asd); + + ret = v4l2_async_nf_register(&isp->notifier); + if (ret) + v4l2_async_nf_cleanup(&isp->notifier); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static const struct media_entity_operations risp_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int risp_probe_resources(struct rcar_isp *isp, + struct platform_device *pdev) +{ + isp->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(isp->base)) + return PTR_ERR(isp->base); + + isp->rstc = devm_reset_control_get(&pdev->dev, NULL); + + return PTR_ERR_OR_ZERO(isp->rstc); +} + +static const struct of_device_id risp_of_id_table[] = { + { .compatible = "renesas,r8a779a0-isp" }, + { .compatible = "renesas,r8a779g0-isp" }, + /* Keep above for compatibility with old DTB files. */ + { .compatible = "renesas,rcar-gen4-isp" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, risp_of_id_table); + +static int risp_probe(struct platform_device *pdev) +{ + struct rcar_isp *isp; + unsigned int i; + int ret; + + isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL); + if (!isp) + return -ENOMEM; + + isp->dev = &pdev->dev; + + ret = risp_probe_resources(isp, pdev); + if (ret) { + dev_err(isp->dev, "Failed to get resources\n"); + return ret; + } + + platform_set_drvdata(pdev, isp); + + pm_runtime_enable(&pdev->dev); + + ret = risp_parse_dt(isp); + if (ret) + goto error_pm; + + isp->subdev.owner = THIS_MODULE; + isp->subdev.dev = &pdev->dev; + v4l2_subdev_init(&isp->subdev, &rcar_isp_subdev_ops); + v4l2_set_subdevdata(&isp->subdev, &pdev->dev); + snprintf(isp->subdev.name, sizeof(isp->subdev.name), "%s %s", + KBUILD_MODNAME, dev_name(&pdev->dev)); + isp->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + + isp->subdev.entity.function = MEDIA_ENT_F_VID_MUX; + isp->subdev.entity.ops = &risp_entity_ops; + + isp->pads[RCAR_ISP_SINK].flags = MEDIA_PAD_FL_SINK; + for (i = RCAR_ISP_PORT0; i < RCAR_ISP_NUM_PADS; i++) + isp->pads[i].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&isp->subdev.entity, RCAR_ISP_NUM_PADS, + isp->pads); + if (ret) + goto error_notifier; + + ret = v4l2_subdev_init_finalize(&isp->subdev); + if (ret) + goto error_notifier; + + ret = v4l2_async_register_subdev(&isp->subdev); + if (ret < 0) + goto error_subdev; + + dev_info(isp->dev, "Using CSI-2 input: %u\n", isp->csi_input); + + return 0; + +error_subdev: + v4l2_subdev_cleanup(&isp->subdev); +error_notifier: + v4l2_async_nf_unregister(&isp->notifier); + v4l2_async_nf_cleanup(&isp->notifier); +error_pm: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static void risp_remove(struct platform_device *pdev) +{ + struct rcar_isp *isp = platform_get_drvdata(pdev); + + v4l2_async_nf_unregister(&isp->notifier); + v4l2_async_nf_cleanup(&isp->notifier); + + v4l2_async_unregister_subdev(&isp->subdev); + v4l2_subdev_cleanup(&isp->subdev); + + pm_runtime_disable(&pdev->dev); +} + +static struct platform_driver rcar_isp_driver = { + .driver = { + .name = "rcar-isp", + .suppress_bind_attrs = true, + .of_match_table = risp_of_id_table, + }, + .probe = risp_probe, + .remove = risp_remove, +}; + +module_platform_driver(rcar_isp_driver); + +MODULE_AUTHOR("Niklas Söderlund "); +MODULE_DESCRIPTION("Renesas R-Car ISP Channel Selector driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 7281a7bd5b7b57fb6841f27e95115934ab25f4a4 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Wed, 23 Apr 2025 18:31:12 +0200 Subject: media: rcar-isp: Rename base register variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for extending the driver to in addition to supporting the channel selector (CS) also support the core ISP. The two different functions have different base addresses so the driver needs to distinguish between them. Prepare for this by marking existing base address variable and read/write functions to make it clear they operate on the CS portion of the driver. There is no functional change. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20250423163113.2961049-7-niklas.soderlund+renesas@ragnatech.se Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rcar-isp/csisp.c | 46 ++++++++++++------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-isp/csisp.c b/drivers/media/platform/renesas/rcar-isp/csisp.c index 4bc89d4757fa..f36d43c2e0a2 100644 --- a/drivers/media/platform/renesas/rcar-isp/csisp.c +++ b/drivers/media/platform/renesas/rcar-isp/csisp.c @@ -159,7 +159,7 @@ enum rcar_isp_pads { struct rcar_isp { struct device *dev; - void __iomem *base; + void __iomem *csbase; struct reset_control *rstc; enum rcar_isp_input csi_input; @@ -184,14 +184,14 @@ static inline struct rcar_isp *notifier_to_isp(struct v4l2_async_notifier *n) return container_of(n, struct rcar_isp, notifier); } -static void risp_write(struct rcar_isp *isp, u32 offset, u32 value) +static void risp_write_cs(struct rcar_isp *isp, u32 offset, u32 value) { - iowrite32(value, isp->base + offset); + iowrite32(value, isp->csbase + offset); } -static u32 risp_read(struct rcar_isp *isp, u32 offset) +static u32 risp_read_cs(struct rcar_isp *isp, u32 offset) { - return ioread32(isp->base + offset); + return ioread32(isp->csbase + offset); } static int risp_power_on(struct rcar_isp *isp) @@ -245,31 +245,31 @@ static int risp_start(struct rcar_isp *isp, struct v4l2_subdev_state *state) if (isp->csi_input == RISP_CSI_INPUT1) sel_csi = ISPINPUTSEL0_SEL_CSI0; - risp_write(isp, ISPINPUTSEL0_REG, - risp_read(isp, ISPINPUTSEL0_REG) | sel_csi); + risp_write_cs(isp, ISPINPUTSEL0_REG, + risp_read_cs(isp, ISPINPUTSEL0_REG) | sel_csi); /* Configure Channel Selector. */ for (vc = 0; vc < 4; vc++) { u8 ch = vc + 4; u8 dt = format->datatype; - risp_write(isp, ISPCS_FILTER_ID_CH_REG(ch), BIT(vc)); - risp_write(isp, ISPCS_DT_CODE03_CH_REG(ch), - ISPCS_DT_CODE03_EN3 | ISPCS_DT_CODE03_DT3(dt) | - ISPCS_DT_CODE03_EN2 | ISPCS_DT_CODE03_DT2(dt) | - ISPCS_DT_CODE03_EN1 | ISPCS_DT_CODE03_DT1(dt) | - ISPCS_DT_CODE03_EN0 | ISPCS_DT_CODE03_DT0(dt)); + risp_write_cs(isp, ISPCS_FILTER_ID_CH_REG(ch), BIT(vc)); + risp_write_cs(isp, ISPCS_DT_CODE03_CH_REG(ch), + ISPCS_DT_CODE03_EN3 | ISPCS_DT_CODE03_DT3(dt) | + ISPCS_DT_CODE03_EN2 | ISPCS_DT_CODE03_DT2(dt) | + ISPCS_DT_CODE03_EN1 | ISPCS_DT_CODE03_DT1(dt) | + ISPCS_DT_CODE03_EN0 | ISPCS_DT_CODE03_DT0(dt)); } /* Setup processing method. */ - risp_write(isp, ISPPROCMODE_DT_REG(format->datatype), - ISPPROCMODE_DT_PROC_MODE_VC3(format->procmode) | - ISPPROCMODE_DT_PROC_MODE_VC2(format->procmode) | - ISPPROCMODE_DT_PROC_MODE_VC1(format->procmode) | - ISPPROCMODE_DT_PROC_MODE_VC0(format->procmode)); + risp_write_cs(isp, ISPPROCMODE_DT_REG(format->datatype), + ISPPROCMODE_DT_PROC_MODE_VC3(format->procmode) | + ISPPROCMODE_DT_PROC_MODE_VC2(format->procmode) | + ISPPROCMODE_DT_PROC_MODE_VC1(format->procmode) | + ISPPROCMODE_DT_PROC_MODE_VC0(format->procmode)); /* Start ISP. */ - risp_write(isp, ISPSTART_REG, ISPSTART_START); + risp_write_cs(isp, ISPSTART_REG, ISPSTART_START); ret = v4l2_subdev_enable_streams(isp->remote, isp->remote_pad, BIT_ULL(0)); @@ -284,7 +284,7 @@ static void risp_stop(struct rcar_isp *isp) v4l2_subdev_disable_streams(isp->remote, isp->remote_pad, BIT_ULL(0)); /* Stop ISP. */ - risp_write(isp, ISPSTART_REG, ISPSTART_STOP); + risp_write_cs(isp, ISPSTART_REG, ISPSTART_STOP); risp_power_off(isp); } @@ -465,9 +465,9 @@ static const struct media_entity_operations risp_entity_ops = { static int risp_probe_resources(struct rcar_isp *isp, struct platform_device *pdev) { - isp->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); - if (IS_ERR(isp->base)) - return PTR_ERR(isp->base); + isp->csbase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(isp->csbase)) + return PTR_ERR(isp->csbase); isp->rstc = devm_reset_control_get(&pdev->dev, NULL); -- cgit v1.2.3-59-g8ed1b From 09d76b4e83e4911238f74a2ba48d92db6e04f10a Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Wed, 23 Apr 2025 18:31:13 +0200 Subject: media: rcar-isp: Parse named cs memory region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the device tree parsing to optionally parse the cs memory region by name. The change is backward compatible with the device tree model where a single unnamed region describes only the ISP channel select function. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20250423163113.2961049-8-niklas.soderlund+renesas@ragnatech.se Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rcar-isp/csisp.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/renesas/rcar-isp/csisp.c b/drivers/media/platform/renesas/rcar-isp/csisp.c index f36d43c2e0a2..1eb29a0b774a 100644 --- a/drivers/media/platform/renesas/rcar-isp/csisp.c +++ b/drivers/media/platform/renesas/rcar-isp/csisp.c @@ -465,7 +465,18 @@ static const struct media_entity_operations risp_entity_ops = { static int risp_probe_resources(struct rcar_isp *isp, struct platform_device *pdev) { - isp->csbase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + struct resource *res; + + /* + * For backward compatibility allow cs base to be the only reg if no + * reg-names are set in DT. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs"); + if (!res) + isp->csbase = devm_platform_ioremap_resource(pdev, 0); + else + isp->csbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(isp->csbase)) return PTR_ERR(isp->csbase); -- cgit v1.2.3-59-g8ed1b From 7305ee12b2c9a8fb615319dc96d9aed847c80117 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 1 Apr 2025 16:22:01 +0200 Subject: media: renesas: vsp1: Add support IIF ISP Interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The IIF (ISP InterFace) is a VSP2 function that transfers data to the ISP by reading from external memory through two RPF instances. Add support for it in the vsp1 driver by introducing a new entity type. The sole required operation is to enable the IIF function during configure_stream(). Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Link: https://lore.kernel.org/r/20250401-v4h-iif-v7-1-cc547c0bddd5@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/Makefile | 2 +- drivers/media/platform/renesas/vsp1/vsp1.h | 3 + drivers/media/platform/renesas/vsp1/vsp1_drv.c | 11 ++ drivers/media/platform/renesas/vsp1/vsp1_entity.c | 8 ++ drivers/media/platform/renesas/vsp1/vsp1_entity.h | 1 + drivers/media/platform/renesas/vsp1/vsp1_iif.c | 121 ++++++++++++++++++++++ drivers/media/platform/renesas/vsp1/vsp1_iif.h | 26 +++++ drivers/media/platform/renesas/vsp1/vsp1_pipe.c | 1 + drivers/media/platform/renesas/vsp1/vsp1_pipe.h | 1 + drivers/media/platform/renesas/vsp1/vsp1_regs.h | 8 ++ 10 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/renesas/vsp1/vsp1_iif.c create mode 100644 drivers/media/platform/renesas/vsp1/vsp1_iif.h diff --git a/drivers/media/platform/renesas/vsp1/Makefile b/drivers/media/platform/renesas/vsp1/Makefile index 4bb4dcbef7b5..de8c802e1d1a 100644 --- a/drivers/media/platform/renesas/vsp1/Makefile +++ b/drivers/media/platform/renesas/vsp1/Makefile @@ -5,6 +5,6 @@ vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o vsp1-y += vsp1_clu.o vsp1_hsit.o vsp1_lut.o vsp1-y += vsp1_brx.o vsp1_sru.o vsp1_uds.o vsp1-y += vsp1_hgo.o vsp1_hgt.o vsp1_histo.o -vsp1-y += vsp1_lif.o vsp1_uif.o +vsp1-y += vsp1_iif.o vsp1_lif.o vsp1_uif.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o diff --git a/drivers/media/platform/renesas/vsp1/vsp1.h b/drivers/media/platform/renesas/vsp1/vsp1.h index 2f6f0c6ae555..263024639dd2 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1.h +++ b/drivers/media/platform/renesas/vsp1/vsp1.h @@ -32,6 +32,7 @@ struct vsp1_clu; struct vsp1_hgo; struct vsp1_hgt; struct vsp1_hsit; +struct vsp1_iif; struct vsp1_lif; struct vsp1_lut; struct vsp1_rwpf; @@ -56,6 +57,7 @@ struct vsp1_uif; #define VSP1_HAS_BRS BIT(9) #define VSP1_HAS_EXT_DL BIT(10) #define VSP1_HAS_NON_ZERO_LBA BIT(11) +#define VSP1_HAS_IIF BIT(12) struct vsp1_device_info { u32 version; @@ -91,6 +93,7 @@ struct vsp1_device { struct vsp1_hgt *hgt; struct vsp1_hsit *hsi; struct vsp1_hsit *hst; + struct vsp1_iif *iif; struct vsp1_lif *lif[VSP1_MAX_LIF]; struct vsp1_lut *lut; struct vsp1_rwpf *rpf[VSP1_MAX_RPF]; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index 9fc6bf624a52..d13e9b31aa7c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -29,6 +29,7 @@ #include "vsp1_hgo.h" #include "vsp1_hgt.h" #include "vsp1_hsit.h" +#include "vsp1_iif.h" #include "vsp1_lif.h" #include "vsp1_lut.h" #include "vsp1_pipe.h" @@ -340,6 +341,16 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) &vsp1->entities); } + if (vsp1_feature(vsp1, VSP1_HAS_IIF)) { + vsp1->iif = vsp1_iif_create(vsp1); + if (IS_ERR(vsp1->iif)) { + ret = PTR_ERR(vsp1->iif); + goto done; + } + + list_add_tail(&vsp1->iif->entity.list_dev, &vsp1->entities); + } + /* * The LIFs are only supported when used in conjunction with the DU, in * which case the userspace API is disabled. If the userspace API is diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 8b8945bd8f10..2096a09a1278 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -63,9 +63,14 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, /* * The ILV and BRS share the same data path route. The extra BRSSEL bit * selects between the ILV and BRS. + * + * The BRU and IIF share the same data path route. The extra IIFSEL bit + * selects between the IIF and BRU. */ if (source->type == VSP1_ENTITY_BRS) route |= VI6_DPR_ROUTE_BRSSEL; + else if (source->type == VSP1_ENTITY_IIF) + route |= VI6_DPR_ROUTE_IIFSEL; vsp1_dl_body_write(dlb, source->route->reg, route); } @@ -528,6 +533,9 @@ struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad) { VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) } static const struct vsp1_route vsp1_routes[] = { + { VSP1_ENTITY_IIF, 0, VI6_DPR_BRU_ROUTE, + { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1), + VI6_DPR_NODE_BRU_IN(3) }, VI6_DPR_NODE_WPF(0) }, { VSP1_ENTITY_BRS, 0, VI6_DPR_ILV_BRS_ROUTE, { VI6_DPR_NODE_BRS_IN(0), VI6_DPR_NODE_BRS_IN(1) }, 0 }, { VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index 1bcc9e27dfdc..bdcb780a79da 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -28,6 +28,7 @@ enum vsp1_entity_type { VSP1_ENTITY_HGT, VSP1_ENTITY_HSI, VSP1_ENTITY_HST, + VSP1_ENTITY_IIF, VSP1_ENTITY_LIF, VSP1_ENTITY_LUT, VSP1_ENTITY_RPF, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_iif.c b/drivers/media/platform/renesas/vsp1/vsp1_iif.c new file mode 100644 index 000000000000..5dd62bebbe8c --- /dev/null +++ b/drivers/media/platform/renesas/vsp1/vsp1_iif.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * vsp1_iif.c -- R-Car VSP1 IIF (ISP Interface) + * + * Copyright (C) 2025 Ideas On Board Oy + * Copyright (C) 2025 Renesas Corporation + */ + +#include "vsp1.h" +#include "vsp1_dl.h" +#include "vsp1_iif.h" + +#define IIF_MIN_WIDTH 128U +#define IIF_MIN_HEIGHT 32U +#define IIF_MAX_WIDTH 5120U +#define IIF_MAX_HEIGHT 4096U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline void vsp1_iif_write(struct vsp1_dl_body *dlb, u32 reg, u32 data) +{ + vsp1_dl_body_write(dlb, reg, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static const unsigned int iif_codes[] = { + MEDIA_BUS_FMT_Y8_1X8, + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_Y12_1X12, + MEDIA_BUS_FMT_Y16_1X16, + MEDIA_BUS_FMT_METADATA_FIXED +}; + +static int iif_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, iif_codes, + ARRAY_SIZE(iif_codes)); +} + +static int iif_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, + IIF_MIN_WIDTH, IIF_MIN_HEIGHT, + IIF_MAX_WIDTH, IIF_MAX_HEIGHT); +} + +static int iif_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, iif_codes, + ARRAY_SIZE(iif_codes), + IIF_MIN_WIDTH, IIF_MIN_HEIGHT, + IIF_MAX_WIDTH, IIF_MAX_HEIGHT); +} + +static const struct v4l2_subdev_pad_ops iif_pad_ops = { + .enum_mbus_code = iif_enum_mbus_code, + .enum_frame_size = iif_enum_frame_size, + .get_fmt = vsp1_subdev_get_pad_format, + .set_fmt = iif_set_format, +}; + +static const struct v4l2_subdev_ops iif_ops = { + .pad = &iif_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * VSP1 Entity Operations + */ + +static void iif_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + vsp1_iif_write(dlb, VI6_IIF_CTRL, VI6_IIF_CTRL_CTRL); +} + +static const struct vsp1_entity_operations iif_entity_ops = { + .configure_stream = iif_configure_stream, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_iif *vsp1_iif_create(struct vsp1_device *vsp1) +{ + struct vsp1_iif *iif; + int ret; + + iif = devm_kzalloc(vsp1->dev, sizeof(*iif), GFP_KERNEL); + if (!iif) + return ERR_PTR(-ENOMEM); + + iif->entity.ops = &iif_entity_ops; + iif->entity.type = VSP1_ENTITY_IIF; + + /* + * The IIF is never exposed to userspace, but media entity registration + * requires a function to be set. Use PROC_VIDEO_PIXEL_FORMATTER just to + * avoid triggering a WARN_ON(), the value won't be seen anywhere. + */ + ret = vsp1_entity_init(vsp1, &iif->entity, "iif", 3, &iif_ops, + MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER); + if (ret < 0) + return ERR_PTR(ret); + + return iif; +} diff --git a/drivers/media/platform/renesas/vsp1/vsp1_iif.h b/drivers/media/platform/renesas/vsp1/vsp1_iif.h new file mode 100644 index 000000000000..165996a822c1 --- /dev/null +++ b/drivers/media/platform/renesas/vsp1/vsp1_iif.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * vsp1_iif.h -- R-Car VSP1 IIF (ISP Interface) + * + * Copyright (C) 2025 Ideas On Board Oy + * Copyright (C) 2025 Renesas Corporation + */ +#ifndef __VSP1_IIF_H__ +#define __VSP1_IIF_H__ + +#include + +#include "vsp1_entity.h" + +struct vsp1_iif { + struct vsp1_entity entity; +}; + +static inline struct vsp1_iif *to_iif(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_iif, entity.subdev); +} + +struct vsp1_iif *vsp1_iif_create(struct vsp1_device *vsp1); + +#endif /* __VSP1_IIF_H__ */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index bb0739f684f3..8e9be3ec1b4d 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -286,6 +286,7 @@ void vsp1_pipeline_reset(struct vsp1_pipeline *pipe) pipe->brx = NULL; pipe->hgo = NULL; pipe->hgt = NULL; + pipe->iif = NULL; pipe->lif = NULL; pipe->uds = NULL; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index 1ba7bdbad5a8..1655a820da10 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h @@ -119,6 +119,7 @@ struct vsp1_pipeline { struct vsp1_entity *brx; struct vsp1_entity *hgo; struct vsp1_entity *hgt; + struct vsp1_entity *iif; struct vsp1_entity *lif; struct vsp1_entity *uds; struct vsp1_entity *uds_input; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_regs.h b/drivers/media/platform/renesas/vsp1/vsp1_regs.h index 7eca82e0ba7e..86e47c2d991f 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_regs.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_regs.h @@ -252,6 +252,13 @@ #define VI6_RPF_BRDITH_CTRL_ODE BIT(8) #define VI6_RPF_BRDITH_CTRL_CBRM BIT(0) +/* ----------------------------------------------------------------------------- + * IIF Control Registers + */ + +#define VI6_IIF_CTRL 0x0608 +#define VI6_IIF_CTRL_CTRL 0x13 + /* ----------------------------------------------------------------------------- * WPF Control Registers */ @@ -388,6 +395,7 @@ #define VI6_DPR_HST_ROUTE 0x2044 #define VI6_DPR_HSI_ROUTE 0x2048 #define VI6_DPR_BRU_ROUTE 0x204c +#define VI6_DPR_ROUTE_IIFSEL BIT(28) #define VI6_DPR_ILV_BRS_ROUTE 0x2050 #define VI6_DPR_ROUTE_BRSSEL BIT(28) #define VI6_DPR_ROUTE_FXA_MASK (0xff << 16) -- cgit v1.2.3-59-g8ed1b From 15b3c76da9e19b24a69c603ec51a965c5be91e26 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 1 Apr 2025 16:22:02 +0200 Subject: media: renesas: vsp1: dl: Use singleshot DL for VSPX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vsp1_dl library allows to program a display list and feed it continuously to the VSP2. As an alternative operation mode, the library allows to program the VSP2 in 'single shot' mode, where a display list is submitted to the VSP on request only. Currently the 'single shot' mode is only available when the VSP2 is controlled by userspace, while it works in continuous mode when driven by DRM, as frames have to be submitted to the display continuously. For the VSPX use case, where there is no uapi support, we should however work in single-shot mode as the ISP driver programs the VSPX on request. Initialize the display lists in single shot mode in case the VSP1 is controlled by userspace or in case the pipeline features an IIF entity, as in the VSPX case. Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Link: https://lore.kernel.org/r/20250401-v4h-iif-v7-2-cc547c0bddd5@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_dl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c index ad3fa1c9cc73..bb8228b19824 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c @@ -1099,7 +1099,12 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, return NULL; dlm->index = index; - dlm->singleshot = vsp1->info->uapi; + /* + * uapi = single shot mode; + * DRM = continuous mode; + * VSPX = single shot mode; + */ + dlm->singleshot = vsp1->info->uapi || vsp1->iif; dlm->vsp1 = vsp1; spin_lock_init(&dlm->lock); -- cgit v1.2.3-59-g8ed1b From f4fea51415e0e82af58e930585ba8c8a4772c945 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 1 Apr 2025 16:22:03 +0200 Subject: media: renesas: vsp1: wpf: Propagate vsp1_rwpf_init_ctrls() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vsp1_wpf.c calls vsp1_rwpf_init_ctrls() to initialize controls that are common between RPF and WPF. However, the vsp1_wpf.c implementation does not check for the function call return value. Fix this by propagating to the caller the return value. While at it, drop a duplicated error message in wpf_init_controls() as the caller already report it. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Link: https://lore.kernel.org/r/20250401-v4h-iif-v7-3-cc547c0bddd5@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_wpf.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index f176750ccd98..da651a882bbb 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -133,6 +133,7 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf) { struct vsp1_device *vsp1 = wpf->entity.vsp1; unsigned int num_flip_ctrls; + int ret; spin_lock_init(&wpf->flip.lock); @@ -156,7 +157,9 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf) num_flip_ctrls = 0; } - vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls); + ret = vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls); + if (ret < 0) + return ret; if (num_flip_ctrls >= 1) { wpf->flip.ctrls.vflip = @@ -174,11 +177,8 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf) v4l2_ctrl_cluster(3, &wpf->flip.ctrls.vflip); } - if (wpf->ctrls.error) { - dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", - wpf->entity.index); + if (wpf->ctrls.error) return wpf->ctrls.error; - } return 0; } -- cgit v1.2.3-59-g8ed1b From 8ebd5f70c2600a499b656fc5eb4d8f0888b66a4f Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 1 Apr 2025 16:22:04 +0200 Subject: media: renesas: vsp1: rwpf: Support operations with IIF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the RPF/WPF units are used for ISP interfacing through the IIF, the set of accessible registers is limited compared to the regular VSPD operations. Support ISP interfacing in the rpf and wpf entities by checking if the pipe features an IIF instance and writing only the relevant registers. Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Link: https://lore.kernel.org/r/20250401-v4h-iif-v7-4-cc547c0bddd5@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_iif.h | 3 +++ drivers/media/platform/renesas/vsp1/vsp1_rpf.c | 9 ++++++++- drivers/media/platform/renesas/vsp1/vsp1_wpf.c | 14 ++++++++++---- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_iif.h b/drivers/media/platform/renesas/vsp1/vsp1_iif.h index 165996a822c1..46f327851c35 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_iif.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_iif.h @@ -12,6 +12,9 @@ #include "vsp1_entity.h" +#define VSPX_IIF_SINK_PAD_IMG 0 +#define VSPX_IIF_SINK_PAD_CONFIG 2 + struct vsp1_iif { struct vsp1_entity entity; }; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index 5c8b3ba1bd3c..5e84536f0cdd 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -84,7 +84,7 @@ static void rpf_configure_stream(struct vsp1_entity *entity, sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); - infmt = VI6_RPF_INFMT_CIPM + infmt = (pipe->iif ? 0 : VI6_RPF_INFMT_CIPM) | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); if (fmtinfo->swap_yc) @@ -98,6 +98,13 @@ static void rpf_configure_stream(struct vsp1_entity *entity, vsp1_rpf_write(rpf, dlb, VI6_RPF_INFMT, infmt); vsp1_rpf_write(rpf, dlb, VI6_RPF_DSWAP, fmtinfo->swap); + /* No further configuration for VSPX. */ + if (pipe->iif) { + /* VSPX wants alpha_sel to be set to 0. */ + vsp1_rpf_write(rpf, dlb, VI6_RPF_ALPH_SEL, 0); + return; + } + if (entity->vsp1->info->gen == 4) { u32 ext_infmt0; u32 ext_infmt1; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index da651a882bbb..f3ea3b17e4cb 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -247,8 +247,11 @@ static void wpf_configure_stream(struct vsp1_entity *entity, sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); - /* Format */ - if (!pipe->lif || wpf->writeback) { + /* + * Format configuration. Skip for IIF (VSPX) or if the pipe doesn't + * write to memory. + */ + if (!pipe->iif && (!pipe->lif || wpf->writeback)) { const struct v4l2_pix_format_mplane *format = &wpf->format; const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; @@ -291,7 +294,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity, * Sources. If the pipeline has a single input and BRx is not used, * configure it as the master layer. Otherwise configure all * inputs as sub-layers and select the virtual RPF as the master - * layer. + * layer. For VSPX configure the enabled sources as masters. */ for (i = 0; i < vsp1->info->rpf_count; ++i) { struct vsp1_rwpf *input = pipe->inputs[i]; @@ -299,7 +302,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity, if (!input) continue; - srcrpf |= (!pipe->brx && pipe->num_inputs == 1) + srcrpf |= (pipe->iif || (!pipe->brx && pipe->num_inputs == 1)) ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index) : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index); } @@ -316,6 +319,9 @@ static void wpf_configure_stream(struct vsp1_entity *entity, vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(index), VI6_WPF_IRQ_ENB_DFEE); + if (pipe->iif) + return; + /* * Configure writeback for display pipelines (the wpf writeback flag is * never set for memory-to-memory pipelines). Start by adding a chained -- cgit v1.2.3-59-g8ed1b From 5dc8bd50c7d8630ae520cb35f21bd1088509142c Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 30 Mar 2025 10:17:35 +0100 Subject: media: renesas: vsp1: Use %p4cc printk modifier to print FourCC codes Replace '%08x->%p4cc' printk modifier to print FourCC codes for pixel formats. Signed-off-by: Biju Das Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham Link: https://lore.kernel.org/r/20250330091738.27052-1-biju.das.jz@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_drm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index b5d1f238f7be..f8a575f6188a 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -593,8 +593,8 @@ static int vsp1_du_pipeline_set_rwpf_format(struct vsp1_device *vsp1, fmtinfo = vsp1_get_format_info(vsp1, pixelformat); if (!fmtinfo) { - dev_dbg(vsp1->dev, "Unsupported pixel format %08x\n", - pixelformat); + dev_dbg(vsp1->dev, "Unsupported pixel format %p4cc\n", + &pixelformat); return -EINVAL; } @@ -849,11 +849,11 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, } dev_dbg(vsp1->dev, - "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad, %pad } zpos %u\n", + "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%p4cc), pitch %u dma { %pad, %pad, %pad } zpos %u\n", __func__, rpf_index, cfg->src.left, cfg->src.top, cfg->src.width, cfg->src.height, cfg->dst.left, cfg->dst.top, cfg->dst.width, cfg->dst.height, - cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1], + &cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1], &cfg->mem[2], cfg->zpos); /* -- cgit v1.2.3-59-g8ed1b From 57024cd2790a25049c13528a5c3e81eb0fe7382e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 30 Apr 2025 02:28:56 +0300 Subject: media: renesas: vsp1: Implement pixel format enumeration The VSP1 driver is missing the ability to enumerate pixel formats on its video nodes, which is supposed to be supported according to the V4L2 API. Implement the enumeration to fix this issue. As the device is media controller-centric, also implement the ability to filter pixel formats by media bus code, and report the missing V4L2_CAP_IO_MC capability. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Link: https://lore.kernel.org/r/20250429232904.26413-2-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_pipe.c | 101 ++++++++++++++++++----- drivers/media/platform/renesas/vsp1/vsp1_pipe.h | 3 + drivers/media/platform/renesas/vsp1/vsp1_video.c | 24 +++++- 3 files changed, 104 insertions(+), 24 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 8e9be3ec1b4d..15ff39c02cbe 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -138,14 +138,6 @@ static const struct vsp1_format_info vsp1_video_formats[] = { VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, 1, { 32, 0, 0 }, false, false, 1, 1, false }, - { V4L2_PIX_FMT_HSV24, MEDIA_BUS_FMT_AHSV8888_1X32, - VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 24, 0, 0 }, false, false, 1, 1, false }, - { V4L2_PIX_FMT_HSV32, MEDIA_BUS_FMT_AHSV8888_1X32, - VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 32, 0, 0 }, false, false, 1, 1, false }, { V4L2_PIX_FMT_RGBX1010102, MEDIA_BUS_FMT_ARGB8888_1X32, VI6_FMT_RGB10_RGB10A2_A2RGB10, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS, @@ -162,10 +154,6 @@ static const struct vsp1_format_info vsp1_video_formats[] = { VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, 1, { 16, 0, 0 }, false, false, 2, 1, false }, - { V4L2_PIX_FMT_VYUY, MEDIA_BUS_FMT_AYUV8_1X32, - VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, false, true, 2, 1, false }, { V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_AYUV8_1X32, VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, @@ -222,6 +210,21 @@ static const struct vsp1_format_info vsp1_video_formats[] = { 1, { 32, 0, 0 }, false, false, 2, 1, false }, }; +static const struct vsp1_format_info vsp1_video_gen2_formats[] = { + { V4L2_PIX_FMT_VYUY, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 16, 0, 0 }, false, true, 2, 1, false }, + { V4L2_PIX_FMT_HSV24, MEDIA_BUS_FMT_AHSV8888_1X32, + VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 24, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_HSV32, MEDIA_BUS_FMT_AHSV8888_1X32, + VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 32, 0, 0 }, false, false, 1, 1, false }, +}; + /** * vsp1_get_format_info - Retrieve format information for a 4CC * @vsp1: the VSP1 device @@ -235,21 +238,77 @@ const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, { unsigned int i; - /* Special case, the VYUY and HSV formats are supported on Gen2 only. */ - if (vsp1->info->gen != 2) { - switch (fourcc) { - case V4L2_PIX_FMT_VYUY: - case V4L2_PIX_FMT_HSV24: - case V4L2_PIX_FMT_HSV32: - return NULL; + for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { + const struct vsp1_format_info *info = &vsp1_video_formats[i]; + + 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]; + + if (info->fourcc == fourcc) + return info; + } + } + + return NULL; +} + +/** + * vsp1_get_format_info_by_index - Enumerate format information + * @vsp1: the VSP1 device + * @index: the format index + * @code: media bus code to limit enumeration + * + * Return a pointer to the format information structure corresponding to the + * given index, or NULL if the index exceeds the supported formats list. If the + * @code parameter is not zero, only formats compatible with the media bus code + * will be enumerated. + */ +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]; + + if (vsp1->info->gen == 2) { + index -= ARRAY_SIZE(vsp1_video_formats); + if (index < ARRAY_SIZE(vsp1_video_gen2_formats)) + return &vsp1_video_gen2_formats[index]; } + + return NULL; } for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { const struct vsp1_format_info *info = &vsp1_video_formats[i]; - if (info->fourcc == fourcc) - return info; + if (info->mbus == code) { + if (!index) + return info; + 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]; + + if (info->mbus == code) { + if (!index) + return info; + index--; + } + } } return NULL; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index 1655a820da10..383951c5bd90 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h @@ -180,5 +180,8 @@ void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, u32 fourcc); +const struct vsp1_format_info * +vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, + u32 code); #endif /* __VSP1_PIPE_H__ */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 03f4efd6b82b..da578993f472 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -888,7 +888,7 @@ vsp1_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) struct vsp1_video *video = to_vsp1_video(vfh->vdev); cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING - | V4L2_CAP_VIDEO_CAPTURE_MPLANE + | V4L2_CAP_IO_MC | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; @@ -898,6 +898,22 @@ vsp1_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) return 0; } +static int vsp1_video_enum_format(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct v4l2_fh *vfh = file->private_data; + struct vsp1_video *video = to_vsp1_video(vfh->vdev); + const struct vsp1_format_info *info; + + info = vsp1_get_format_info_by_index(video->vsp1, f->index, f->mbus_code); + if (!info) + return -EINVAL; + + f->pixelformat = info->fourcc; + + return 0; +} + static int vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format) { @@ -1013,6 +1029,8 @@ err_pipe: static const struct v4l2_ioctl_ops vsp1_video_ioctl_ops = { .vidioc_querycap = vsp1_video_querycap, + .vidioc_enum_fmt_vid_cap = vsp1_video_enum_format, + .vidioc_enum_fmt_vid_out = vsp1_video_enum_format, .vidioc_g_fmt_vid_cap_mplane = vsp1_video_get_format, .vidioc_s_fmt_vid_cap_mplane = vsp1_video_set_format, .vidioc_try_fmt_vid_cap_mplane = vsp1_video_try_format, @@ -1207,14 +1225,14 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, video->pad.flags = MEDIA_PAD_FL_SOURCE; video->video.vfl_dir = VFL_DIR_TX; video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE | - V4L2_CAP_STREAMING; + V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; } else { direction = "output"; video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; video->pad.flags = MEDIA_PAD_FL_SINK; video->video.vfl_dir = VFL_DIR_RX; video->video.device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | - V4L2_CAP_STREAMING; + V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; } mutex_init(&video->lock); -- cgit v1.2.3-59-g8ed1b From a4b25ae7b24b2cdf59fa6896f6a82e030606ca51 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 30 Apr 2025 02:28:57 +0300 Subject: media: renesas: vsp1: Make HSI and HST modules optional Not all VSP instance incorporate the HSI and HST modules. Add a VSP1_HAS_HSIT feature flag, and create the modules only on VSP instances that implement them. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Link: https://lore.kernel.org/r/20250429232904.26413-3-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1.h | 1 + drivers/media/platform/renesas/vsp1/vsp1_drv.c | 59 ++++++++++++++------------ 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1.h b/drivers/media/platform/renesas/vsp1/vsp1.h index 263024639dd2..f97a1a31bfab 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1.h +++ b/drivers/media/platform/renesas/vsp1/vsp1.h @@ -58,6 +58,7 @@ struct vsp1_uif; #define VSP1_HAS_EXT_DL BIT(10) #define VSP1_HAS_NON_ZERO_LBA BIT(11) #define VSP1_HAS_IIF BIT(12) +#define VSP1_HAS_HSIT BIT(13) struct vsp1_device_info { u32 version; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index d13e9b31aa7c..8270a9d207cb 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -303,22 +303,6 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->clu->entity.list_dev, &vsp1->entities); } - vsp1->hsi = vsp1_hsit_create(vsp1, true); - if (IS_ERR(vsp1->hsi)) { - ret = PTR_ERR(vsp1->hsi); - goto done; - } - - list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities); - - vsp1->hst = vsp1_hsit_create(vsp1, false); - if (IS_ERR(vsp1->hst)) { - ret = PTR_ERR(vsp1->hst); - goto done; - } - - list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities); - if (vsp1_feature(vsp1, VSP1_HAS_HGO) && vsp1->info->uapi) { vsp1->hgo = vsp1_hgo_create(vsp1); if (IS_ERR(vsp1->hgo)) { @@ -351,6 +335,24 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->iif->entity.list_dev, &vsp1->entities); } + if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) { + vsp1->hsi = vsp1_hsit_create(vsp1, true); + if (IS_ERR(vsp1->hsi)) { + ret = PTR_ERR(vsp1->hsi); + goto done; + } + + list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities); + + vsp1->hst = vsp1_hsit_create(vsp1, false); + if (IS_ERR(vsp1->hst)) { + ret = PTR_ERR(vsp1->hst); + goto done; + } + + list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities); + } + /* * The LIFs are only supported when used in conjunction with the DU, in * which case the userspace API is disabled. If the userspace API is @@ -694,8 +696,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .model = "VSP1-S", .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HGO - | VSP1_HAS_HGT | VSP1_HAS_LUT | VSP1_HAS_SRU - | VSP1_HAS_WPF_VFLIP, + | VSP1_HAS_HGT | VSP1_HAS_HSIT | VSP1_HAS_LUT + | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .uds_count = 3, .wpf_count = 4, @@ -705,7 +707,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPR_H2, .model = "VSP1-R", .gen = 2, - .features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, + .features = VSP1_HAS_BRU | VSP1_HAS_HSIT | VSP1_HAS_SRU + | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .uds_count = 3, .wpf_count = 4, @@ -715,7 +718,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPD_GEN2, .model = "VSP1-D", .gen = 2, - .features = VSP1_HAS_BRU | VSP1_HAS_HGO | VSP1_HAS_LUT, + .features = VSP1_HAS_BRU | VSP1_HAS_HGO | VSP1_HAS_HSIT + | VSP1_HAS_LUT, .lif_count = 1, .rpf_count = 4, .uds_count = 1, @@ -727,8 +731,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .model = "VSP1-S", .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HGO - | VSP1_HAS_HGT | VSP1_HAS_LUT | VSP1_HAS_SRU - | VSP1_HAS_WPF_VFLIP, + | VSP1_HAS_HGT | VSP1_HAS_HSIT | VSP1_HAS_LUT + | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .uds_count = 1, .wpf_count = 4, @@ -738,8 +742,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPS_V2H, .model = "VSP1V-S", .gen = 2, - .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT - | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HSIT + | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 4, .uds_count = 1, .wpf_count = 4, @@ -749,7 +753,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPD_V2H, .model = "VSP1V-D", .gen = 2, - .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HSIT + | VSP1_HAS_LUT, .lif_count = 1, .rpf_count = 4, .uds_count = 1, @@ -761,8 +766,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .model = "VSP2-I", .gen = 3, .features = VSP1_HAS_CLU | VSP1_HAS_HGO | VSP1_HAS_HGT - | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_HFLIP - | VSP1_HAS_WPF_VFLIP, + | VSP1_HAS_HSIT | VSP1_HAS_LUT | VSP1_HAS_SRU + | VSP1_HAS_WPF_HFLIP | VSP1_HAS_WPF_VFLIP, .rpf_count = 1, .uds_count = 1, .wpf_count = 1, -- cgit v1.2.3-59-g8ed1b From 687dae9eedb0fb0153ef69b35b40f738f64793d0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 30 Apr 2025 02:28:58 +0300 Subject: media: renesas: vsp1: Fix HSV format enumeration The HSV formats are not restricted to Gen2 platforms, but to VSP instances that implement the HSI and HST modules. Make it conditional to the VSP1_HAS_HSIT feature flag. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Link: https://lore.kernel.org/r/20250429232904.26413-4-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_pipe.c | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 15ff39c02cbe..c8ec5bfa7944 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -215,6 +215,9 @@ static const struct vsp1_format_info vsp1_video_gen2_formats[] = { VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, 1, { 16, 0, 0 }, false, true, 2, 1, false }, +}; + +static const struct vsp1_format_info vsp1_video_hsit_formats[] = { { V4L2_PIX_FMT_HSV24, MEDIA_BUS_FMT_AHSV8888_1X32, VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, @@ -255,6 +258,16 @@ const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, } } + 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]; + + if (info->fourcc == fourcc) + return info; + } + } + return NULL; } @@ -285,6 +298,12 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, return &vsp1_video_gen2_formats[index]; } + if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) { + index -= ARRAY_SIZE(vsp1_video_gen2_formats); + if (index < ARRAY_SIZE(vsp1_video_hsit_formats)) + return &vsp1_video_hsit_formats[index]; + } + return NULL; } @@ -311,6 +330,19 @@ 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]; + + if (info->mbus == code) { + if (!index) + return info; + index--; + } + } + } + return NULL; } -- cgit v1.2.3-59-g8ed1b From b6e57605eff6224df4debf188eb7a02dedb7686f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 30 Apr 2025 02:28:59 +0300 Subject: media: renesas: vsp1: Fix media bus code setup on RWPF source pad The RWPF source pad media bus code can only be different from the sink pad code when enabling color space conversion, which can only convert between RGB and YUV. If the sink pad code is HSV, no conversion is possible. Fix the pad set format handler to reflect this hardware limitation. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Link: https://lore.kernel.org/r/20250429232904.26413-5-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_rwpf.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index 9d38203e73d0..1b4bac7b7cfa 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -76,11 +76,20 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == RWPF_PAD_SOURCE) { + const struct v4l2_mbus_framefmt *sink_format = + v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + /* * The RWPF performs format conversion but can't scale, only the - * format code can be changed on the source pad. + * format code can be changed on the source pad when converting + * between RGB and YUV. */ - format->code = fmt->format.code; + if (sink_format->code != MEDIA_BUS_FMT_AHSV8888_1X32 && + fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32) + format->code = fmt->format.code; + else + format->code = sink_format->code; + fmt->format = *format; goto done; } -- cgit v1.2.3-59-g8ed1b From d5e3bc24d5ce4c256d5bbe7f2d6b9c4a3c71a401 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 30 Apr 2025 02:29:00 +0300 Subject: media: renesas: vsp1: Report colour space information to userspace The vsp1 driver implements very partial colour space support: it hardcodes the colorspace field on all video devices and subdevices to V4L2_COLORSPACE_SRGB, regardless of the configured format. The xfer_func, ycbcr_enc and quantization fields are not set (except for hsv_enc for HSV formats on video devices). This doesn't match the hardware configuration, which handles YUV data as encoding in BT.601 with limited range. As a first step towards colour space configuration, keep the colour space fields hardcoded, but set them based on the selected format type (RGB, YUV or HSV). While at it, remove an extra blank line. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Link: https://lore.kernel.org/r/20250429232904.26413-6-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_brx.c | 9 ++++- drivers/media/platform/renesas/vsp1/vsp1_entity.c | 22 +++++++++++- drivers/media/platform/renesas/vsp1/vsp1_entity.h | 2 ++ drivers/media/platform/renesas/vsp1/vsp1_hsit.c | 11 +++++- drivers/media/platform/renesas/vsp1/vsp1_pipe.c | 44 +++++++++++++++++++++++ drivers/media/platform/renesas/vsp1/vsp1_pipe.h | 2 ++ drivers/media/platform/renesas/vsp1/vsp1_rwpf.c | 11 +++++- drivers/media/platform/renesas/vsp1/vsp1_sru.c | 9 ++++- drivers/media/platform/renesas/vsp1/vsp1_uds.c | 9 ++++- drivers/media/platform/renesas/vsp1/vsp1_video.c | 7 ++-- 10 files changed, 115 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index 5dee0490c593..5fc2e5a3bb30 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -15,6 +15,7 @@ #include "vsp1.h" #include "vsp1_brx.h" #include "vsp1_dl.h" +#include "vsp1_entity.h" #include "vsp1_pipe.h" #include "vsp1_rwpf.h" #include "vsp1_video.h" @@ -108,6 +109,8 @@ static void brx_try_format(struct vsp1_brx *brx, if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 && fmt->code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->code = MEDIA_BUS_FMT_AYUV8_1X32; + + vsp1_entity_adjust_color_space(fmt); break; default: @@ -115,13 +118,17 @@ static void brx_try_format(struct vsp1_brx *brx, format = v4l2_subdev_state_get_format(sd_state, BRX_PAD_SINK(0)); fmt->code = format->code; + + fmt->colorspace = format->colorspace; + fmt->xfer_func = format->xfer_func; + fmt->ycbcr_enc = format->ycbcr_enc; + fmt->quantization = format->quantization; break; } fmt->width = clamp(fmt->width, BRX_MIN_SIZE, BRX_MAX_SIZE); fmt->height = clamp(fmt->height, BRX_MIN_SIZE, BRX_MAX_SIZE); fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_SRGB; } static int brx_set_format(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 2096a09a1278..a6680d531872 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -104,6 +104,20 @@ void vsp1_entity_configure_partition(struct vsp1_entity *entity, dl, dlb); } +void vsp1_entity_adjust_color_space(struct v4l2_mbus_framefmt *format) +{ + u8 xfer_func = format->xfer_func; + u8 ycbcr_enc = format->ycbcr_enc; + u8 quantization = format->quantization; + + vsp1_adjust_color_space(format->code, &format->colorspace, &xfer_func, + &ycbcr_enc, &quantization); + + format->xfer_func = xfer_func; + format->ycbcr_enc = ycbcr_enc; + format->quantization = quantization; +} + /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations */ @@ -334,7 +348,13 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, format->height = clamp_t(unsigned int, fmt->format.height, min_height, max_height); format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; + + format->colorspace = fmt->format.colorspace; + format->xfer_func = fmt->format.xfer_func; + format->ycbcr_enc = fmt->format.ycbcr_enc; + format->quantization = fmt->format.quantization; + + vsp1_entity_adjust_color_space(format); fmt->format = *format; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index bdcb780a79da..b7c72d0b7f8e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -171,6 +171,8 @@ void vsp1_entity_configure_partition(struct vsp1_entity *entity, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); +void vsp1_entity_adjust_color_space(struct v4l2_mbus_framefmt *format); + struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad); int vsp1_subdev_get_pad_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 8ba2a7c7305c..1fcd1967d3b2 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -14,6 +14,7 @@ #include "vsp1.h" #include "vsp1_dl.h" +#include "vsp1_entity.h" #include "vsp1_hsit.h" #define HSIT_MIN_SIZE 4U @@ -96,7 +97,13 @@ static int hsit_set_format(struct v4l2_subdev *subdev, format->height = clamp_t(unsigned int, fmt->format.height, HSIT_MIN_SIZE, HSIT_MAX_SIZE); format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; + + format->colorspace = fmt->format.colorspace; + format->xfer_func = fmt->format.xfer_func; + format->ycbcr_enc = fmt->format.ycbcr_enc; + format->quantization = fmt->format.quantization; + + vsp1_entity_adjust_color_space(format); fmt->format = *format; @@ -106,6 +113,8 @@ static int hsit_set_format(struct v4l2_subdev *subdev, format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 : MEDIA_BUS_FMT_AHSV8888_1X32; + vsp1_entity_adjust_color_space(format); + done: mutex_unlock(&hsit->entity.lock); return ret; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index c8ec5bfa7944..94875c499df4 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -346,6 +346,50 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, return NULL; } +/** + * vsp1_adjust_color_space - Adjust color space fields in a format + * @code: the media bus code + * @colorspace: the colorspace + * @xfer_func: the transfer function + * @encoding: the encoding + * @quantization: the quantization + * + * This function adjusts all color space fields of a video device of subdev + * format structure, taking into account the requested format, requested color + * space and limitations of the VSP1. It should be used in the video device and + * subdev set format handlers. + * + * For now, simply hardcode the color space fields to the VSP1 defaults based + * on the media bus code. + */ +void vsp1_adjust_color_space(u32 code, u32 *colorspace, u8 *xfer_func, + u8 *encoding, u8 *quantization) +{ + switch (code) { + case MEDIA_BUS_FMT_ARGB8888_1X32: + default: + *colorspace = V4L2_COLORSPACE_SRGB; + *xfer_func = V4L2_XFER_FUNC_SRGB; + *encoding = V4L2_YCBCR_ENC_601; + *quantization = V4L2_QUANTIZATION_FULL_RANGE; + break; + + case MEDIA_BUS_FMT_AHSV8888_1X32: + *colorspace = V4L2_COLORSPACE_SRGB; + *xfer_func = V4L2_XFER_FUNC_SRGB; + *encoding = V4L2_HSV_ENC_256; + *quantization = V4L2_QUANTIZATION_FULL_RANGE; + break; + + case MEDIA_BUS_FMT_AYUV8_1X32: + *colorspace = V4L2_COLORSPACE_SMPTE170M; + *xfer_func = V4L2_XFER_FUNC_709; + *encoding = V4L2_YCBCR_ENC_601; + *quantization = V4L2_QUANTIZATION_LIM_RANGE; + break; + } +} + /* ----------------------------------------------------------------------------- * Pipeline Management */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index 383951c5bd90..7f623b8cbe5c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h @@ -183,5 +183,7 @@ const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, const struct vsp1_format_info * vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, u32 code); +void vsp1_adjust_color_space(u32 code, u32 *colorspace, u8 *xfer_func, + u8 *encoding, u8 *quantization); #endif /* __VSP1_PIPE_H__ */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index 1b4bac7b7cfa..4e8bcf6a59ad 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -10,6 +10,7 @@ #include #include "vsp1.h" +#include "vsp1_entity.h" #include "vsp1_rwpf.h" #include "vsp1_video.h" @@ -90,6 +91,8 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, else format->code = sink_format->code; + vsp1_entity_adjust_color_space(format); + fmt->format = *format; goto done; } @@ -100,7 +103,13 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, format->height = clamp_t(unsigned int, fmt->format.height, RWPF_MIN_HEIGHT, rwpf->max_height); format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; + + format->colorspace = fmt->format.colorspace; + format->xfer_func = fmt->format.xfer_func; + format->ycbcr_enc = fmt->format.ycbcr_enc; + format->quantization = fmt->format.quantization; + + vsp1_entity_adjust_color_space(format); fmt->format = *format; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index 1759ce642e6e..bba2872afaf2 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -14,6 +14,7 @@ #include "vsp1.h" #include "vsp1_dl.h" +#include "vsp1_entity.h" #include "vsp1_pipe.h" #include "vsp1_sru.h" @@ -178,6 +179,8 @@ static void sru_try_format(struct vsp1_sru *sru, fmt->code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->code = MEDIA_BUS_FMT_AYUV8_1X32; + vsp1_entity_adjust_color_space(fmt); + fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE); fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE); break; @@ -187,6 +190,11 @@ static void sru_try_format(struct vsp1_sru *sru, format = v4l2_subdev_state_get_format(sd_state, SRU_PAD_SINK); fmt->code = format->code; + fmt->colorspace = format->colorspace; + fmt->xfer_func = format->xfer_func; + fmt->ycbcr_enc = format->ycbcr_enc; + fmt->quantization = format->quantization; + /* * We can upscale by 2 in both direction, but not independently. * Compare the input and output rectangles areas (avoiding @@ -211,7 +219,6 @@ static void sru_try_format(struct vsp1_sru *sru, } fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_SRGB; } static int sru_set_format(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index c5a38478cf8c..2db473b6f83c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -14,6 +14,7 @@ #include "vsp1.h" #include "vsp1_dl.h" +#include "vsp1_entity.h" #include "vsp1_pipe.h" #include "vsp1_uds.h" @@ -177,6 +178,8 @@ static void uds_try_format(struct vsp1_uds *uds, fmt->code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->code = MEDIA_BUS_FMT_AYUV8_1X32; + vsp1_entity_adjust_color_space(fmt); + fmt->width = clamp(fmt->width, UDS_MIN_SIZE, UDS_MAX_SIZE); fmt->height = clamp(fmt->height, UDS_MIN_SIZE, UDS_MAX_SIZE); break; @@ -186,6 +189,11 @@ static void uds_try_format(struct vsp1_uds *uds, format = v4l2_subdev_state_get_format(sd_state, UDS_PAD_SINK); fmt->code = format->code; + fmt->colorspace = format->colorspace; + fmt->xfer_func = format->xfer_func; + fmt->ycbcr_enc = format->ycbcr_enc; + fmt->quantization = format->quantization; + uds_output_limits(format->width, &minimum, &maximum); fmt->width = clamp(fmt->width, minimum, maximum); uds_output_limits(format->height, &minimum, &maximum); @@ -194,7 +202,6 @@ static void uds_try_format(struct vsp1_uds *uds, } fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_SRGB; } static int uds_set_format(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index da578993f472..68d495c20a84 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -127,12 +127,10 @@ static int __vsp1_video_try_format(struct vsp1_video *video, info = vsp1_get_format_info(video->vsp1, VSP1_VIDEO_DEF_FORMAT); pix->pixelformat = info->fourcc; - pix->colorspace = V4L2_COLORSPACE_SRGB; pix->field = V4L2_FIELD_NONE; - if (info->fourcc == V4L2_PIX_FMT_HSV24 || - info->fourcc == V4L2_PIX_FMT_HSV32) - pix->hsv_enc = V4L2_HSV_ENC_256; + vsp1_adjust_color_space(info->mbus, &pix->colorspace, &pix->xfer_func, + &pix->ycbcr_enc, &pix->quantization); memset(pix->reserved, 0, sizeof(pix->reserved)); @@ -891,7 +889,6 @@ vsp1_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) | V4L2_CAP_IO_MC | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; - strscpy(cap->driver, "vsp1", sizeof(cap->driver)); strscpy(cap->card, video->video.name, sizeof(cap->card)); -- cgit v1.2.3-59-g8ed1b From e6c9597e5f320c6bc9751183450e19386be476c8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 30 Apr 2025 02:29:01 +0300 Subject: media: renesas: vsp1: Allow setting encoding and quantization The RPF and WPF support different encodings and quantizations when converting between RGB and YUV formats. Allow setting the corresponding format parameters from userspace, and configure the hardware accordingly. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Link: https://lore.kernel.org/r/20250429232904.26413-7-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_pipe.c | 29 ++++++++++++++-------- drivers/media/platform/renesas/vsp1/vsp1_rpf.c | 29 ++++++++++++++++++++-- drivers/media/platform/renesas/vsp1/vsp1_rwpf.c | 31 ++++++++++++++++++++++-- drivers/media/platform/renesas/vsp1/vsp1_video.c | 19 +++++++++++++++ drivers/media/platform/renesas/vsp1/vsp1_wpf.c | 29 ++++++++++++++++++++-- 5 files changed, 121 insertions(+), 16 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 94875c499df4..3cbb768cf6ad 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -359,33 +359,42 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, * space and limitations of the VSP1. It should be used in the video device and * subdev set format handlers. * - * For now, simply hardcode the color space fields to the VSP1 defaults based - * on the media bus code. + * The colorspace and xfer_func fields are freely configurable, as they are out + * of scope for VSP processing. The encoding and quantization is hardcoded for + * non-YUV formats, and can be configured for YUV formats. */ void vsp1_adjust_color_space(u32 code, u32 *colorspace, u8 *xfer_func, u8 *encoding, u8 *quantization) { + if (*colorspace == V4L2_COLORSPACE_DEFAULT || + *colorspace >= V4L2_COLORSPACE_LAST) + *colorspace = code == MEDIA_BUS_FMT_AYUV8_1X32 + ? V4L2_COLORSPACE_SMPTE170M + : V4L2_COLORSPACE_SRGB; + + if (*xfer_func == V4L2_XFER_FUNC_DEFAULT || + *xfer_func >= V4L2_XFER_FUNC_LAST) + *xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(*colorspace); + switch (code) { case MEDIA_BUS_FMT_ARGB8888_1X32: default: - *colorspace = V4L2_COLORSPACE_SRGB; - *xfer_func = V4L2_XFER_FUNC_SRGB; *encoding = V4L2_YCBCR_ENC_601; *quantization = V4L2_QUANTIZATION_FULL_RANGE; break; case MEDIA_BUS_FMT_AHSV8888_1X32: - *colorspace = V4L2_COLORSPACE_SRGB; - *xfer_func = V4L2_XFER_FUNC_SRGB; *encoding = V4L2_HSV_ENC_256; *quantization = V4L2_QUANTIZATION_FULL_RANGE; break; case MEDIA_BUS_FMT_AYUV8_1X32: - *colorspace = V4L2_COLORSPACE_SMPTE170M; - *xfer_func = V4L2_XFER_FUNC_709; - *encoding = V4L2_YCBCR_ENC_601; - *quantization = V4L2_QUANTIZATION_LIM_RANGE; + if (*encoding != V4L2_YCBCR_ENC_601 && + *encoding != V4L2_YCBCR_ENC_709) + *encoding = V4L2_YCBCR_ENC_601; + if (*quantization != V4L2_QUANTIZATION_FULL_RANGE && + *quantization != V4L2_QUANTIZATION_LIM_RANGE) + *quantization = V4L2_QUANTIZATION_LIM_RANGE; break; } } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index 5e84536f0cdd..811f2b7c5cc5 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -92,8 +92,33 @@ static void rpf_configure_stream(struct vsp1_entity *entity, if (fmtinfo->swap_uv) infmt |= VI6_RPF_INFMT_SPUVS; - if (sink_format->code != source_format->code) - infmt |= VI6_RPF_INFMT_CSC; + if (sink_format->code != source_format->code) { + u16 ycbcr_enc; + u16 quantization; + u32 rdtm; + + if (sink_format->code == MEDIA_BUS_FMT_AYUV8_1X32) { + ycbcr_enc = sink_format->ycbcr_enc; + quantization = sink_format->quantization; + } else { + ycbcr_enc = source_format->ycbcr_enc; + quantization = source_format->quantization; + } + + if (ycbcr_enc == V4L2_YCBCR_ENC_601 && + quantization == V4L2_QUANTIZATION_LIM_RANGE) + rdtm = VI6_RPF_INFMT_RDTM_BT601; + else if (ycbcr_enc == V4L2_YCBCR_ENC_601 && + quantization == V4L2_QUANTIZATION_FULL_RANGE) + rdtm = VI6_RPF_INFMT_RDTM_BT601_EXT; + else if (ycbcr_enc == V4L2_YCBCR_ENC_709 && + quantization == V4L2_QUANTIZATION_LIM_RANGE) + rdtm = VI6_RPF_INFMT_RDTM_BT709; + else + rdtm = VI6_RPF_INFMT_RDTM_BT709_EXT; + + infmt |= VI6_RPF_INFMT_CSC | rdtm; + } vsp1_rpf_write(rpf, dlb, VI6_RPF_INFMT, infmt); vsp1_rpf_write(rpf, dlb, VI6_RPF_DSWAP, fmtinfo->swap); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index 4e8bcf6a59ad..9c8085d5d306 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -36,6 +36,11 @@ static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, code->code = codes[code->index]; + if (code->pad == RWPF_PAD_SOURCE && + code->code == MEDIA_BUS_FMT_AYUV8_1X32) + code->flags = V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC + | V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION; + return 0; } @@ -79,11 +84,13 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, if (fmt->pad == RWPF_PAD_SOURCE) { const struct v4l2_mbus_framefmt *sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + u16 flags = fmt->format.flags & V4L2_MBUS_FRAMEFMT_SET_CSC; + bool csc; /* * The RWPF performs format conversion but can't scale, only the - * format code can be changed on the source pad when converting - * between RGB and YUV. + * format code, encoding and quantization can be changed on the + * source pad when converting between RGB and YUV. */ if (sink_format->code != MEDIA_BUS_FMT_AHSV8888_1X32 && fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32) @@ -91,9 +98,29 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, else format->code = sink_format->code; + /* + * Encoding and quantization can only be configured when YCbCr + * <-> RGB is enabled. The V4L2 API requires userspace to set + * the V4L2_MBUS_FRAMEFMT_SET_CSC flag. If either of these + * conditions is not met, use the encoding and quantization + * values from the sink pad. + */ + csc = (format->code == MEDIA_BUS_FMT_AYUV8_1X32) != + (sink_format->code == MEDIA_BUS_FMT_AYUV8_1X32); + + if (csc && (flags & V4L2_MBUS_FRAMEFMT_SET_CSC)) { + format->ycbcr_enc = fmt->format.ycbcr_enc; + format->quantization = fmt->format.quantization; + } else { + format->ycbcr_enc = sink_format->ycbcr_enc; + format->quantization = sink_format->quantization; + } + vsp1_entity_adjust_color_space(format); fmt->format = *format; + fmt->format.flags = flags; + goto done; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 68d495c20a84..bc66fbdde3cc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -129,6 +129,20 @@ static int __vsp1_video_try_format(struct vsp1_video *video, pix->pixelformat = info->fourcc; pix->field = V4L2_FIELD_NONE; + /* + * Adjust the colour space fields. On capture devices, userspace needs + * to set the V4L2_PIX_FMT_FLAG_SET_CSC to override the defaults. Reset + * all fields to *_DEFAULT if the flag isn't set, to then handle + * capture and output devices in the same way. + */ + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + !(pix->flags & V4L2_PIX_FMT_FLAG_SET_CSC)) { + pix->colorspace = V4L2_COLORSPACE_DEFAULT; + pix->xfer_func = V4L2_XFER_FUNC_DEFAULT; + pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix->quantization = V4L2_QUANTIZATION_DEFAULT; + } + vsp1_adjust_color_space(info->mbus, &pix->colorspace, &pix->xfer_func, &pix->ycbcr_enc, &pix->quantization); @@ -908,6 +922,11 @@ static int vsp1_video_enum_format(struct file *file, void *fh, f->pixelformat = info->fourcc; + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + info->mbus == MEDIA_BUS_FMT_AYUV8_1X32) + f->flags = V4L2_FMT_FLAG_CSC_YCBCR_ENC + | V4L2_FMT_FLAG_CSC_QUANTIZATION; + return 0; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index f3ea3b17e4cb..30662cfdf837 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -282,8 +282,33 @@ static void wpf_configure_stream(struct vsp1_entity *entity, (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT)); } - if (sink_format->code != source_format->code) - outfmt |= VI6_WPF_OUTFMT_CSC; + if (sink_format->code != source_format->code) { + u16 ycbcr_enc; + u16 quantization; + u32 wrtm; + + if (sink_format->code == MEDIA_BUS_FMT_AYUV8_1X32) { + ycbcr_enc = sink_format->ycbcr_enc; + quantization = sink_format->quantization; + } else { + ycbcr_enc = source_format->ycbcr_enc; + quantization = source_format->quantization; + } + + if (ycbcr_enc == V4L2_YCBCR_ENC_601 && + quantization == V4L2_QUANTIZATION_LIM_RANGE) + wrtm = VI6_WPF_OUTFMT_WRTM_BT601; + else if (ycbcr_enc == V4L2_YCBCR_ENC_601 && + quantization == V4L2_QUANTIZATION_FULL_RANGE) + wrtm = VI6_WPF_OUTFMT_WRTM_BT601_EXT; + else if (ycbcr_enc == V4L2_YCBCR_ENC_709 && + quantization == V4L2_QUANTIZATION_LIM_RANGE) + wrtm = VI6_WPF_OUTFMT_WRTM_BT709; + else + wrtm = VI6_WPF_OUTFMT_WRTM_BT709_EXT; + + outfmt |= VI6_WPF_OUTFMT_CSC | wrtm; + } wpf->outfmt = outfmt; -- cgit v1.2.3-59-g8ed1b From 67cbb2be3ae7f21f85642d81b451bee20f687d06 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 30 Apr 2025 02:29:02 +0300 Subject: media: renesas: vsp1: Name nested structure in vsp1_drm The vsp1_drm structure defines an anonymous nested structure to store per-input data. In preparation for extending that structure, give it a name and is it through the driver. This improves code readability. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Link: https://lore.kernel.org/r/20250429232904.26413-8-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_drm.c | 18 +++++++++--------- drivers/media/platform/renesas/vsp1/vsp1_drm.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index f8a575f6188a..e5339fda5941 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -118,24 +118,22 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, struct vsp1_entity *uif, unsigned int brx_input) { + const struct vsp1_drm_input *input = &vsp1->drm->inputs[rpf->entity.index]; struct v4l2_subdev_selection sel = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; - const struct v4l2_rect *crop; int ret; /* * Configure the format on the RPF sink pad and propagate it up to the * BRx sink pad. */ - crop = &vsp1->drm->inputs[rpf->entity.index].crop; - format.pad = RWPF_PAD_SINK; - format.format.width = crop->width + crop->left; - format.format.height = crop->height + crop->top; + format.format.width = input->crop.width + input->crop.left; + format.format.height = input->crop.height + input->crop.top; format.format.code = rpf->fmtinfo->mbus; format.format.field = V4L2_FIELD_NONE; @@ -151,7 +149,7 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, sel.pad = RWPF_PAD_SINK; sel.target = V4L2_SEL_TGT_CROP; - sel.r = *crop; + sel.r = input->crop; ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL, &sel); @@ -826,12 +824,14 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, { struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index]; + struct vsp1_drm_input *input; struct vsp1_rwpf *rpf; int ret; if (rpf_index >= vsp1->info->rpf_count) return -EINVAL; + input = &vsp1->drm->inputs[rpf_index]; rpf = vsp1->rpf[rpf_index]; if (!cfg) { @@ -873,9 +873,9 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, rpf->format.flags = cfg->premult ? V4L2_PIX_FMT_FLAG_PREMUL_ALPHA : 0; - vsp1->drm->inputs[rpf_index].crop = cfg->src; - vsp1->drm->inputs[rpf_index].compose = cfg->dst; - vsp1->drm->inputs[rpf_index].zpos = cfg->zpos; + input->crop = cfg->src; + input->compose = cfg->dst; + input->zpos = cfg->zpos; drm_pipe->pipe.inputs[rpf_index] = rpf; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.h b/drivers/media/platform/renesas/vsp1/vsp1_drm.h index 3fd95b53f27e..7234737cc464 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.h @@ -59,7 +59,7 @@ struct vsp1_drm { struct vsp1_drm_pipeline pipe[VSP1_MAX_LIF]; struct mutex lock; - struct { + struct vsp1_drm_input { struct v4l2_rect crop; struct v4l2_rect compose; unsigned int zpos; -- cgit v1.2.3-59-g8ed1b From b64b134942c8cf4801ea288b3fd38b509aedec21 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 30 Apr 2025 02:29:03 +0300 Subject: media: renesas: vsp1: Expose color space through the DRM API Now that the VSP1 driver supports color spaces, expose them through the API used by the DU driver. This allows configuring the YCbCr encoding and quantization used by each plane, ensuring correct color rendering. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Link: https://lore.kernel.org/r/20250429232904.26413-9-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_drm.c | 4 ++++ drivers/media/platform/renesas/vsp1/vsp1_drm.h | 6 ++++-- include/media/vsp1.h | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index e5339fda5941..fe55e8747b05 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -136,6 +136,8 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, format.format.height = input->crop.height + input->crop.top; format.format.code = rpf->fmtinfo->mbus; format.format.field = V4L2_FIELD_NONE; + format.format.ycbcr_enc = input->ycbcr_enc; + format.format.quantization = input->quantization; ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, &format); @@ -876,6 +878,8 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, input->crop = cfg->src; input->compose = cfg->dst; input->zpos = cfg->zpos; + input->ycbcr_enc = cfg->color_encoding; + input->quantization = cfg->color_range; drm_pipe->pipe.inputs[rpf_index] = rpf; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.h b/drivers/media/platform/renesas/vsp1/vsp1_drm.h index 7234737cc464..07a5d0adbd08 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.h @@ -52,8 +52,8 @@ struct vsp1_drm_pipeline { * struct vsp1_drm - State for the API exposed to the DRM driver * @pipe: the VSP1 DRM pipeline used for display * @lock: protects the BRU and BRS allocation - * @inputs: source crop rectangle, destination compose rectangle and z-order - * position for every input (indexed by RPF index) + * @inputs: source crop rectangle, destination compose rectangle, z-order + * position and colorspace for every input (indexed by RPF index) */ struct vsp1_drm { struct vsp1_drm_pipeline pipe[VSP1_MAX_LIF]; @@ -63,6 +63,8 @@ struct vsp1_drm { struct v4l2_rect crop; struct v4l2_rect compose; unsigned int zpos; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; } inputs[VSP1_MAX_RPF]; }; diff --git a/include/media/vsp1.h b/include/media/vsp1.h index 48f4a5023d81..4ea6352fd63f 100644 --- a/include/media/vsp1.h +++ b/include/media/vsp1.h @@ -52,6 +52,8 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, * @alpha: alpha value (0: fully transparent, 255: fully opaque) * @zpos: Z position of the plane (from 0 to number of planes minus 1) * @premult: true for premultiplied alpha + * @color_encoding: color encoding (valid for YUV formats only) + * @color_range: color range (valid for YUV formats only) */ struct vsp1_du_atomic_config { u32 pixelformat; @@ -62,6 +64,8 @@ struct vsp1_du_atomic_config { unsigned int alpha; unsigned int zpos; bool premult; + enum v4l2_ycbcr_encoding color_encoding; + enum v4l2_quantization color_range; }; /** -- cgit v1.2.3-59-g8ed1b From fa88d420cb477993d6ddd23d34d6bc61f6805e41 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 4 Nov 2024 13:38:32 +0100 Subject: media: atomisp: Remove gmin_platform Asus T100TA quirks The Asus T100TA quirks set the same values as the defaults, so they are not necessary. Remove them. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20241104123832.5539-1-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/atomisp/pci/atomisp_gmin_platform.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c index e176483df301..8ec990293b48 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c @@ -225,16 +225,6 @@ static struct gmin_cfg_var ffrd8_vars[] = { {}, }; -/* Cribbed from MCG defaults in the mt9m114 driver, not actually verified - * vs. T100 hardware - */ -static struct gmin_cfg_var t100_vars[] = { - { "INT33F0:00_CsiPort", "0" }, - { "INT33F0:00_CsiLanes", "1" }, - { "INT33F0:00_CamClk", "1" }, - {}, -}; - static struct gmin_cfg_var mrd7_vars[] = { {"INT33F8:00_CamType", "1"}, {"INT33F8:00_CsiPort", "1"}, @@ -309,13 +299,6 @@ static const struct dmi_system_id gmin_vars[] = { }, .driver_data = ffrd8_vars, }, - { - .ident = "T100TA", - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "T100TA"), - }, - .driver_data = t100_vars, - }, { .ident = "MRD7", .matches = { -- cgit v1.2.3-59-g8ed1b From 48ba117fadaf09b076c81e500a74f79646381fa8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 7 Nov 2024 23:11:34 +0100 Subject: media: atomisp: gmin: Remove GPIO driven regulator support The GMIN code has support for sensors using external regulators enabled by GPIOS, rather then using regulators build into the PMIC. With the exception of the Trekstor ST70408-4 (1) tablet there are no known devices which actually use external regulators for the sensors and the code for this is using deprecated old style GPIO numbers support for which is going away. Remove the GPIO driven regulator support so that the gmin code no longer depends on deprecated GPIO APIs. 1) The GMIN support itself is also deprecated and all sensor drivers still using it are being moved over to use ACPI + runtime-pm and the ST70408-4 shipped with Android as factory OS and thus will have broken ACPI tables for the sensors, so like other Android factory OS tablets it will need a bespoke solution anyways. Reported-by: Bartosz Golaszewski Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Reviewed-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20241107221134.596149-1-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/pci/atomisp_gmin_platform.c | 69 ---------------------- 1 file changed, 69 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c index 8ec990293b48..e7923dc38f2e 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c @@ -85,9 +85,6 @@ struct gmin_subdev { bool v2p8_on; bool v1p2_on; - int v1p8_gpio; - int v2p8_gpio; - u8 pwm_i2c_addr; /* For PMIC AXP */ @@ -242,22 +239,6 @@ static struct gmin_cfg_var mrd7_vars[] = { {}, }; -static struct gmin_cfg_var ecs7_vars[] = { - {"INT33BE:00_CsiPort", "1"}, - {"INT33BE:00_CsiLanes", "2"}, - {"INT33BE:00_CsiFmt", "13"}, - {"INT33BE:00_CsiBayer", "2"}, - {"INT33BE:00_CamClk", "0"}, - - {"INT33F0:00_CsiPort", "0"}, - {"INT33F0:00_CsiLanes", "1"}, - {"INT33F0:00_CsiFmt", "13"}, - {"INT33F0:00_CsiBayer", "0"}, - {"INT33F0:00_CamClk", "1"}, - {"gmin_V2P8GPIO", "402"}, - {}, -}; - static struct gmin_cfg_var i8880_vars[] = { {"XXOV2680:00_CsiPort", "1"}, {"XXOV2680:00_CsiLanes", "1"}, @@ -307,13 +288,6 @@ static const struct dmi_system_id gmin_vars[] = { }, .driver_data = mrd7_vars, }, - { - .ident = "ST70408", - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "ST70408"), - }, - .driver_data = ecs7_vars, - }, { .ident = "VTA0803", .matches = { @@ -531,23 +505,6 @@ static int gmin_subdev_add(struct gmin_subdev *gs) else dev_info(dev, "will handle gpio1 via ACPI\n"); - /* - * Those are used only when there is an external regulator apart - * from the PMIC that would be providing power supply, like on the - * two cases below: - * - * The ECS E7 board drives camera 2.8v from an external regulator - * instead of the PMIC. There's a gmin_CamV2P8 config variable - * that specifies the GPIO to handle this particular case, - * but this needs a broader architecture for handling camera power. - * - * The CHT RVP board drives camera 1.8v from an* external regulator - * instead of the PMIC just like ECS E7 board. - */ - - gs->v1p8_gpio = gmin_get_var_int(dev, true, "V1P8GPIO", -1); - gs->v2p8_gpio = gmin_get_var_int(dev, true, "V2P8GPIO", -1); - /* * FIXME: * @@ -820,16 +777,6 @@ static int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on) if (!gs || gs->v1p8_on == on) return 0; - if (gs->v1p8_gpio >= 0) { - pr_info("atomisp_gmin_platform: 1.8v power on GPIO %d\n", - gs->v1p8_gpio); - ret = gpio_request(gs->v1p8_gpio, "camera_v1p8_en"); - if (!ret) - ret = gpio_direction_output(gs->v1p8_gpio, 0); - if (ret) - pr_err("V1P8 GPIO initialization failed\n"); - } - gs->v1p8_on = on; ret = 0; @@ -844,9 +791,6 @@ static int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on) goto out; /* Still needed */ } - if (gs->v1p8_gpio >= 0) - gpio_set_value(gs->v1p8_gpio, on); - if (gs->v1p8_reg) { regulator_set_voltage(gs->v1p8_reg, 1800000, 1800000); if (on) @@ -901,16 +845,6 @@ static int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on) if (WARN_ON(!gs)) return -ENODEV; - if (gs->v2p8_gpio >= 0) { - pr_info("atomisp_gmin_platform: 2.8v power on GPIO %d\n", - gs->v2p8_gpio); - ret = gpio_request(gs->v2p8_gpio, "camera_v2p8"); - if (!ret) - ret = gpio_direction_output(gs->v2p8_gpio, 0); - if (ret) - pr_err("V2P8 GPIO initialization failed\n"); - } - if (gs->v2p8_on == on) return 0; gs->v2p8_on = on; @@ -927,9 +861,6 @@ static int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on) goto out; /* Still needed */ } - if (gs->v2p8_gpio >= 0) - gpio_set_value(gs->v2p8_gpio, on); - if (gs->v2p8_reg) { regulator_set_voltage(gs->v2p8_reg, 2900000, 2900000); if (on) -- cgit v1.2.3-59-g8ed1b From 1d2e5bef1315fe5d92cb087beaba5d7b2c8d2cff Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 11 Dec 2024 18:35:16 +0100 Subject: media: atomisp: Avoid picking too big sensor resolution atomisp_try_fmt() is limiting the width of the requested resolution to 1920 before calling the sensor's try_fmt() method. But it is not limiting the height. In case of the old mode-list based t4ka3 driver which has a mode list of: 736x496 896x736 1936x1096 3280x2464 This results in 3280x2464 being selected when try_fmt is called with a requested resolution of 3280x2464, which is not supported because its width > 1920 . Fix this by also limiting the height when in preview mode. Reviewed-by: Andy Shevchenko Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20241211173516.350779-1-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_cmd.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index 8feb627ddcca..ea2099d2897f 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -3784,9 +3784,14 @@ int atomisp_try_fmt(struct atomisp_device *isp, struct v4l2_pix_format *f, return -EINVAL; } - /* The preview pipeline does not support width > 1920 */ - if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) - f->width = min_t(u32, f->width, 1920); + /* + * The preview pipeline does not support width > 1920. Also limit height + * to avoid sensor drivers still picking a too wide resolution. + */ + if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) { + f->width = min(f->width, 1920U); + f->height = min(f->height, 1440U); + } /* * atomisp_set_fmt() will set the sensor resolution to the requested -- cgit v1.2.3-59-g8ed1b From 5e663d592b33bf24d2d7dea4b08cabf317b41f46 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 2 Dec 2024 15:47:16 +0000 Subject: media: atomisp: Use the actual value of the enum instead of the enum hrt_isp_css_irq_sw_pin_0 has a different enum type than irq_sw_channel_id_t. Replace it with the actual value of hrt_isp_css_irq_sw_pin_0 to avoid arithmetic operations between different enum types (and make the compiler happy). It is required to build with llvm 19 without these warnings: .../sh_css_hrt.c:68:19: warning: arithmetic between different enumeration types ('irq_sw_channel_id_t' and 'enum hrt_isp_css_irq') [-Wenum-enum-conversion] .../sh_css.c:1233:40: warning: arithmetic between different enumeration types ('irq_sw_channel_id_t' and 'enum hrt_isp_css_irq') [-Wenum-enum-conversion] .../sh_css.c:1237:40: warning: arithmetic between different enumeration types ('irq_sw_channel_id_t' and 'enum hrt_isp_css_irq') [-Wenum-enum-conversion] Signed-off-by: Ricardo Ribalda Link: https://lore.kernel.org/r/20241202-fix-llvm9-v1-2-2a50f5acfd0b@chromium.org Reviewed-by: Andy Shevchenko Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/hive_isp_css_common/irq_global.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/irq_global.h b/drivers/staging/media/atomisp/pci/hive_isp_css_common/irq_global.h index 2c47e7820bd7..69e68ecd6bc3 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/irq_global.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/irq_global.h @@ -18,7 +18,7 @@ #endif /* The IRQ is not mapped uniformly on its related interfaces */ -#define IRQ_SW_CHANNEL_OFFSET hrt_isp_css_irq_sw_pin_0 +#define IRQ_SW_CHANNEL_OFFSET HIVE_GP_DEV_IRQ_SW_PIN_0_BIT_ID typedef enum { IRQ_SW_CHANNEL0_ID = hrt_isp_css_irq_sw_pin_0 - IRQ_SW_CHANNEL_OFFSET, -- cgit v1.2.3-59-g8ed1b From 2f9fc1b53c7bfc563a9362adf52955fe869b0387 Mon Sep 17 00:00:00 2001 From: Liu Jing Date: Mon, 9 Dec 2024 14:40:22 +0800 Subject: media: atomisp: Fix spelling error in ia_css_sdis2_types.h Fix the coefficients spelling error in ia_css_sdis2_types.h. Signed-off-by: Liu Jing Link: https://lore.kernel.org/r/20241209064022.4342-1-liujing@cmss.chinamobile.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2_types.h index f37802878528..2bed08435755 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2_types.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2_types.h @@ -19,7 +19,7 @@ #endif /* DVS 2.0 Coefficient types. This structure contains 4 pointers to - * arrays that contain the coeffients for each type. + * arrays that contain the coefficients for each type. */ struct ia_css_dvs2_coef_types { s16 *odd_real; /** real part of the odd coefficients*/ -- cgit v1.2.3-59-g8ed1b From e079805764f98de10adaa44b01a4ef309752c547 Mon Sep 17 00:00:00 2001 From: Gabriel Shahrouzi Date: Mon, 7 Apr 2025 08:36:08 -0400 Subject: media: atomisp: Fix indentation to use TAB instead of spaces Replace spaces with TAB to comply with kernel coding style. Signed-off-by: Gabriel Shahrouzi Link: https://lore.kernel.org/r/20250407123608.366190-1-gshahrouzi@gmail.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ece5e3da34ee..127f12ba2214 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 @@ -114,7 +114,7 @@ configure_dma( } int ia_css_vf_configure(const struct ia_css_binary *binary, - const struct ia_css_frame_info *out_info, + const struct ia_css_frame_info *out_info, struct ia_css_frame_info *vf_info, unsigned int *downscale_log2) { -- cgit v1.2.3-59-g8ed1b From 330995bb78375bb6309cf007585f429e9d6bc731 Mon Sep 17 00:00:00 2001 From: Abraham Samuel Adekunle Date: Thu, 3 Apr 2025 14:26:41 +0100 Subject: media: atomisp: gmin: Remove duplicate NULL test When a value has been tested for NULL in an expression, a second NULL test on the same value in another expression is unnecessary when the value has not been assigned NULL. Remove unnecessary duplicate NULL tests on the same value that has previously been NULL tested. Found by Coccinelle. Signed-off-by: Abraham Samuel Adekunle Link: https://lore.kernel.org/r/26990d4a9d4419f9d4155a40595bc213acb671a0.1743685415.git.abrahamadekunle50@gmail.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c | 2 +- 1 file changed, 1 insertion(+), 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 e7923dc38f2e..b4f2c785e6a6 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c @@ -1206,7 +1206,7 @@ static int gmin_get_config_dsm_var(struct device *dev, * if it founds something different than string, letting it * to fall back to the old code. */ - if (cur && cur->type != ACPI_TYPE_STRING) { + if (cur->type != ACPI_TYPE_STRING) { dev_info(dev, "found non-string _DSM entry for '%s'\n", var); ACPI_FREE(obj); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From fecae1a423962aff68ad171ff12baba5d67eccbb Mon Sep 17 00:00:00 2001 From: Thomas Andreatta Date: Sat, 26 Apr 2025 21:30:33 +0200 Subject: media: atomisp: gmin: Fix indentation to use TAB instead of spaces Fix indentation to use TAB instead of spaces. Signed-off-by: Thomas Andreatta Link: https://lore.kernel.org/r/20250426193033.483124-1-thomas.andreatta2000@gmail.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c index b4f2c785e6a6..5f59519ac8e2 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c +++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c @@ -473,9 +473,9 @@ static int gmin_subdev_add(struct gmin_subdev *gs) dev_info(dev, "%s: ACPI path is %pfw\n", __func__, dev_fwnode(dev)); - /*WA:CHT requires XTAL clock as PLL is not stable.*/ + /* WA:CHT requires XTAL clock as PLL is not stable. */ gs->clock_src = gmin_get_var_int(dev, false, "ClkSrc", - VLV2_CLK_PLL_19P2MHZ); + VLV2_CLK_PLL_19P2MHZ); /* * Get ACPI _PR0 derived clock here already because it is used -- cgit v1.2.3-59-g8ed1b From eb5e3cdcfe87a5f23043a1e1ba41a9159545904e Mon Sep 17 00:00:00 2001 From: Hardevsinh Palaniya Date: Mon, 21 Apr 2025 10:57:58 +0530 Subject: media: atomisp: Remove compat ioctl32 header file The atomisp_compat_ioctl32() function was deleted in commit b4c650f1af68 ("media: atomisp: remove compat_ioctl32 code"), so this header file is no longer needed. Additionally, the definition of atomisp_compat_ioctl32() in atomisp_ioctl.h is unused as well. Delete the declaration from the header. Signed-off-by: Hardevsinh Palaniya Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250421052804.11721-1-hardevsinh.palaniya@siliconsignals.io Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/pci/atomisp_compat_ioctl32.h | 244 --------------------- drivers/staging/media/atomisp/pci/atomisp_ioctl.h | 4 - 2 files changed, 248 deletions(-) delete mode 100644 drivers/staging/media/atomisp/pci/atomisp_compat_ioctl32.h diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_ioctl32.h b/drivers/staging/media/atomisp/pci/atomisp_compat_ioctl32.h deleted file mode 100644 index 23d798f3085c..000000000000 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_ioctl32.h +++ /dev/null @@ -1,244 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Support for Intel Camera Imaging ISP subsystem. - * - * Copyright (c) 2013 Intel Corporation. All Rights Reserved. - */ -#ifndef __ATOMISP_COMPAT_IOCTL32_H__ -#define __ATOMISP_COMPAT_IOCTL32_H__ - -#include -#include - -#include "atomisp_compat.h" - -struct atomisp_histogram32 { - unsigned int num_elements; - compat_uptr_t data; -}; - -struct atomisp_dvs2_stat_types32 { - compat_uptr_t odd_real; /** real part of the odd statistics*/ - compat_uptr_t odd_imag; /** imaginary part of the odd statistics*/ - compat_uptr_t even_real;/** real part of the even statistics*/ - compat_uptr_t even_imag;/** imaginary part of the even statistics*/ -}; - -struct atomisp_dvs2_coef_types32 { - compat_uptr_t odd_real; /** real part of the odd coefficients*/ - compat_uptr_t odd_imag; /** imaginary part of the odd coefficients*/ - compat_uptr_t even_real;/** real part of the even coefficients*/ - compat_uptr_t even_imag;/** imaginary part of the even coefficients*/ -}; - -struct atomisp_dvs2_statistics32 { - struct atomisp_dvs_grid_info grid_info; - struct atomisp_dvs2_stat_types32 hor_prod; - struct atomisp_dvs2_stat_types32 ver_prod; -}; - -struct atomisp_dis_statistics32 { - struct atomisp_dvs2_statistics32 dvs2_stat; - u32 exp_id; -}; - -struct atomisp_dis_coefficients32 { - struct atomisp_dvs_grid_info grid_info; - struct atomisp_dvs2_coef_types32 hor_coefs; - struct atomisp_dvs2_coef_types32 ver_coefs; -}; - -struct atomisp_3a_statistics32 { - struct atomisp_grid_info grid_info; - compat_uptr_t data; - compat_uptr_t rgby_data; - u32 exp_id; - u32 isp_config_id; -}; - -struct atomisp_morph_table32 { - unsigned int enabled; - unsigned int height; - unsigned int width; /* number of valid elements per line */ - compat_uptr_t coordinates_x[ATOMISP_MORPH_TABLE_NUM_PLANES]; - compat_uptr_t coordinates_y[ATOMISP_MORPH_TABLE_NUM_PLANES]; -}; - -struct v4l2_framebuffer32 { - __u32 capability; - __u32 flags; - compat_uptr_t base; - struct v4l2_pix_format fmt; -}; - -struct atomisp_overlay32 { - /* 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. - */ - compat_uptr_t frame; - /* Y value of overlay background */ - unsigned char bg_y; - /* U value of overlay background */ - char bg_u; - /* V value of overlay background */ - char bg_v; - /* the blending percent of input data for Y subpixels */ - unsigned char blend_input_perc_y; - /* the blending percent of input data for U subpixels */ - unsigned char blend_input_perc_u; - /* the blending percent of input data for V subpixels */ - unsigned char blend_input_perc_v; - /* the blending percent of overlay data for Y subpixels */ - unsigned char blend_overlay_perc_y; - /* the blending percent of overlay data for U subpixels */ - unsigned char blend_overlay_perc_u; - /* the blending percent 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. */ - unsigned int overlay_start_x; - /* the overlay start y pixel position on output frame It should be the - multiples of 2. */ - unsigned int overlay_start_y; -}; - -struct atomisp_shading_table32 { - __u32 enable; - __u32 sensor_width; - __u32 sensor_height; - __u32 width; - __u32 height; - __u32 fraction_bits; - - compat_uptr_t data[ATOMISP_NUM_SC_COLORS]; -}; - -struct atomisp_parameters32 { - compat_uptr_t wb_config; /* White Balance config */ - compat_uptr_t cc_config; /* Color Correction config */ - compat_uptr_t tnr_config; /* Temporal Noise Reduction */ - compat_uptr_t ecd_config; /* Eigen Color Demosaicing */ - compat_uptr_t ynr_config; /* Y(Luma) Noise Reduction */ - compat_uptr_t fc_config; /* Fringe Control */ - compat_uptr_t formats_config; /* Formats Control */ - compat_uptr_t cnr_config; /* Chroma Noise Reduction */ - compat_uptr_t macc_config; /* MACC */ - compat_uptr_t ctc_config; /* Chroma Tone Control */ - compat_uptr_t aa_config; /* Anti-Aliasing */ - compat_uptr_t baa_config; /* Anti-Aliasing */ - compat_uptr_t ce_config; - compat_uptr_t dvs_6axis_config; - compat_uptr_t ob_config; /* Objective Black config */ - compat_uptr_t dp_config; /* Dead Pixel config */ - compat_uptr_t nr_config; /* Noise Reduction config */ - compat_uptr_t ee_config; /* Edge Enhancement config */ - compat_uptr_t de_config; /* Demosaic config */ - compat_uptr_t gc_config; /* Gamma Correction config */ - compat_uptr_t anr_config; /* Advanced Noise Reduction */ - compat_uptr_t a3a_config; /* 3A Statistics config */ - compat_uptr_t xnr_config; /* eXtra Noise Reduction */ - compat_uptr_t dz_config; /* Digital Zoom */ - compat_uptr_t yuv2rgb_cc_config; /* Color - Correction config */ - compat_uptr_t rgb2yuv_cc_config; /* Color - Correction config */ - compat_uptr_t macc_table; - compat_uptr_t gamma_table; - compat_uptr_t ctc_table; - compat_uptr_t xnr_table; - compat_uptr_t r_gamma_table; - compat_uptr_t g_gamma_table; - compat_uptr_t b_gamma_table; - compat_uptr_t motion_vector; /* For 2-axis DVS */ - compat_uptr_t shading_table; - compat_uptr_t morph_table; - compat_uptr_t dvs_coefs; /* DVS 1.0 coefficients */ - compat_uptr_t dvs2_coefs; /* DVS 2.0 coefficients */ - compat_uptr_t capture_config; - compat_uptr_t anr_thres; - - compat_uptr_t lin_2500_config; /* Skylake: Linearization config */ - compat_uptr_t obgrid_2500_config; /* Skylake: OBGRID config */ - compat_uptr_t bnr_2500_config; /* Skylake: bayer denoise config */ - compat_uptr_t shd_2500_config; /* Skylake: shading config */ - compat_uptr_t dm_2500_config; /* Skylake: demosaic config */ - compat_uptr_t rgbpp_2500_config; /* Skylake: RGBPP config */ - compat_uptr_t dvs_stat_2500_config; /* Skylake: DVS STAT config */ - compat_uptr_t lace_stat_2500_config; /* Skylake: LACE STAT config */ - compat_uptr_t yuvp1_2500_config; /* Skylake: yuvp1 config */ - compat_uptr_t yuvp2_2500_config; /* Skylake: yuvp2 config */ - compat_uptr_t tnr_2500_config; /* Skylake: TNR config */ - compat_uptr_t dpc_2500_config; /* Skylake: DPC config */ - compat_uptr_t awb_2500_config; /* Skylake: auto white balance config */ - compat_uptr_t - awb_fr_2500_config; /* Skylake: auto white balance filter response config */ - compat_uptr_t anr_2500_config; /* Skylake: ANR config */ - compat_uptr_t af_2500_config; /* Skylake: auto focus config */ - compat_uptr_t ae_2500_config; /* Skylake: auto exposure config */ - compat_uptr_t bds_2500_config; /* Skylake: bayer downscaler config */ - compat_uptr_t - dvs_2500_config; /* Skylake: digital video stabilization config */ - compat_uptr_t 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. - */ - compat_uptr_t output_frame; - /* - * Unique ID to track which config was actually applied to a particular - * frame, driver will send this id back with output frame together. - */ - u32 isp_config_id; - u32 per_frame_setting; -}; - -struct atomisp_dvs_6axis_config32 { - u32 exp_id; - u32 width_y; - u32 height_y; - u32 width_uv; - u32 height_uv; - compat_uptr_t xcoords_y; - compat_uptr_t ycoords_y; - compat_uptr_t xcoords_uv; - compat_uptr_t ycoords_uv; -}; - -#define ATOMISP_IOC_G_HISTOGRAM32 \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 3, struct atomisp_histogram32) -#define ATOMISP_IOC_S_HISTOGRAM32 \ - _IOW('v', BASE_VIDIOC_PRIVATE + 3, struct atomisp_histogram32) - -#define ATOMISP_IOC_G_DIS_STAT32 \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 6, struct atomisp_dis_statistics32) -#define ATOMISP_IOC_S_DIS_COEFS32 \ - _IOW('v', BASE_VIDIOC_PRIVATE + 6, struct atomisp_dis_coefficients32) - -#define ATOMISP_IOC_S_DIS_VECTOR32 \ - _IOW('v', BASE_VIDIOC_PRIVATE + 6, struct atomisp_dvs_6axis_config32) - -#define ATOMISP_IOC_G_3A_STAT32 \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 7, struct atomisp_3a_statistics32) - -#define ATOMISP_IOC_G_ISP_GDC_TAB32 \ - _IOR('v', BASE_VIDIOC_PRIVATE + 10, struct atomisp_morph_table32) -#define ATOMISP_IOC_S_ISP_GDC_TAB32 \ - _IOW('v', BASE_VIDIOC_PRIVATE + 10, struct atomisp_morph_table32) - -#define ATOMISP_IOC_S_ISP_FPN_TABLE32 \ - _IOW('v', BASE_VIDIOC_PRIVATE + 17, struct v4l2_framebuffer32) - -#define ATOMISP_IOC_G_ISP_OVERLAY32 \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 18, struct atomisp_overlay32) -#define ATOMISP_IOC_S_ISP_OVERLAY32 \ - _IOW('v', BASE_VIDIOC_PRIVATE + 18, struct atomisp_overlay32) - -#define ATOMISP_IOC_S_ISP_SHD_TAB32 \ - _IOWR('v', BASE_VIDIOC_PRIVATE + 27, struct atomisp_shading_table32) - -#define ATOMISP_IOC_S_PARAMETERS32 \ - _IOW('v', BASE_VIDIOC_PRIVATE + 32, struct atomisp_parameters32) - -#endif /* __ATOMISP_COMPAT_IOCTL32_H__ */ diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.h b/drivers/staging/media/atomisp/pci/atomisp_ioctl.h index 4feaa0338cb4..57f608f9db56 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.h +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.h @@ -33,8 +33,4 @@ void atomisp_stop_streaming(struct vb2_queue *vq); extern const struct v4l2_ioctl_ops atomisp_ioctl_ops; -/* compat_ioctl for 32bit userland app and 64bit kernel */ -long atomisp_compat_ioctl32(struct file *file, - unsigned int cmd, unsigned long arg); - #endif /* __ATOMISP_IOCTL_H__ */ -- cgit v1.2.3-59-g8ed1b From 72ebfff219459b062394fd2ad886b8e59e0195ca Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 5 Jan 2025 19:25:06 +0100 Subject: media: atomisp: Rename camera to sensor The camera v4l2_subdev pointer in struct atomisp_input_subdev points to an image sensor, rename camera to sensor to make this more clear. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_cmd.c | 28 ++++++++++---------- .../media/atomisp/pci/atomisp_compat_css20.c | 2 +- drivers/staging/media/atomisp/pci/atomisp_csi2.c | 2 +- .../staging/media/atomisp/pci/atomisp_internal.h | 4 +-- drivers/staging/media/atomisp/pci/atomisp_ioctl.c | 30 +++++++++++----------- drivers/staging/media/atomisp/pci/atomisp_v4l2.c | 25 +++++++++--------- 6 files changed, 46 insertions(+), 45 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index ea2099d2897f..697561769eb0 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -87,7 +87,7 @@ static unsigned short atomisp_get_sensor_fps(struct atomisp_sub_device *asd) unsigned short fps = 0; int ret; - ret = v4l2_subdev_call_state_active(isp->inputs[asd->input_curr].camera, + ret = v4l2_subdev_call_state_active(isp->inputs[asd->input_curr].sensor, pad, get_frame_interval, &fi); if (!ret && fi.interval.numerator) @@ -881,7 +881,7 @@ void atomisp_assert_recovery_work(struct work_struct *work) spin_unlock_irqrestore(&isp->lock, flags); /* stream off sensor */ - ret = v4l2_subdev_call(isp->inputs[isp->asd.input_curr].camera, video, s_stream, 0); + ret = v4l2_subdev_call(isp->inputs[isp->asd.input_curr].sensor, video, s_stream, 0); if (ret) dev_warn(isp->dev, "Stopping sensor stream failed: %d\n", ret); @@ -936,7 +936,7 @@ void atomisp_assert_recovery_work(struct work_struct *work) /* Requeue unprocessed per-frame parameters. */ atomisp_recover_params_queue(&isp->asd.video_out); - ret = v4l2_subdev_call(isp->inputs[isp->asd.input_curr].camera, video, s_stream, 1); + ret = v4l2_subdev_call(isp->inputs[isp->asd.input_curr].sensor, video, s_stream, 1); if (ret) dev_err(isp->dev, "Starting sensor stream failed: %d\n", ret); @@ -3124,7 +3124,7 @@ int atomisp_color_effect(struct atomisp_sub_device *asd, int flag, control.id = V4L2_CID_COLORFX; control.value = *effect; ret = - v4l2_s_ctrl(NULL, isp->inputs[asd->input_curr].camera->ctrl_handler, + v4l2_s_ctrl(NULL, isp->inputs[asd->input_curr].sensor->ctrl_handler, &control); /* * if set color effect to sensor successfully, return @@ -3620,16 +3620,16 @@ int atomisp_s_sensor_power(struct atomisp_device *isp, unsigned int input, bool { int ret; - if (isp->inputs[input].camera_on == on) + if (isp->inputs[input].sensor_on == on) return 0; - ret = v4l2_subdev_call(isp->inputs[input].camera, core, s_power, on); + ret = v4l2_subdev_call(isp->inputs[input].sensor, core, s_power, on); if (ret && ret != -ENOIOCTLCMD) { dev_err(isp->dev, "Error setting sensor power %d: %d\n", on, ret); return ret; } - isp->inputs[input].camera_on = on; + isp->inputs[input].sensor_on = on; return 0; } @@ -3677,7 +3677,7 @@ void atomisp_setup_input_links(struct atomisp_device *isp) * will end up calling atomisp_link_setup() which calls this * function again leading to endless recursion. */ - if (isp->sensor_subdevs[i] == isp->inputs[isp->asd.input_curr].camera) + if (isp->sensor_subdevs[i] == isp->inputs[isp->asd.input_curr].sensor) link->flags |= MEDIA_LNK_FL_ENABLED; else link->flags &= ~MEDIA_LNK_FL_ENABLED; @@ -3704,7 +3704,7 @@ static int atomisp_set_sensor_crop_and_fmt(struct atomisp_device *isp, struct v4l2_subdev_state *sd_state; int ret = 0; - if (!input->camera) + if (!input->sensor) return -EINVAL; /* @@ -3719,7 +3719,7 @@ static int atomisp_set_sensor_crop_and_fmt(struct atomisp_device *isp, } sd_state = (which == V4L2_SUBDEV_FORMAT_TRY) ? input->try_sd_state : - input->camera->active_state; + input->sensor->active_state; if (sd_state) v4l2_subdev_lock_state(sd_state); @@ -3740,14 +3740,14 @@ static int atomisp_set_sensor_crop_and_fmt(struct atomisp_device *isp, sel.r.left = ((input->native_rect.width - sel.r.width) / 2) & ~1; sel.r.top = ((input->native_rect.height - sel.r.height) / 2) & ~1; - ret = v4l2_subdev_call(input->camera, pad, set_selection, sd_state, &sel); + ret = v4l2_subdev_call(input->sensor, pad, set_selection, sd_state, &sel); if (ret) dev_err(isp->dev, "Error setting crop to (%d,%d)/%ux%u: %d\n", sel.r.left, sel.r.top, sel.r.width, sel.r.height, ret); set_fmt: if (ret == 0) - ret = v4l2_subdev_call(input->camera, pad, set_fmt, sd_state, &format); + ret = v4l2_subdev_call(input->sensor, pad, set_fmt, sd_state, &format); if (sd_state) v4l2_subdev_unlock_state(sd_state); @@ -3880,7 +3880,7 @@ static inline int atomisp_set_sensor_mipi_to_isp( u32 mipi_port, metadata_width = 0, metadata_height = 0; ctrl.id = V4L2_CID_LINK_FREQ; - if (v4l2_g_ctrl(input->camera->ctrl_handler, &ctrl) == 0) + if (v4l2_g_ctrl(input->sensor->ctrl_handler, &ctrl) == 0) mipi_freq = ctrl.value; if (asd->stream_env[stream_id].isys_configs == 1) { @@ -4043,7 +4043,7 @@ static int atomisp_set_fmt_to_isp(struct video_device *vdev, if (!format) return -EINVAL; - mipi_info = atomisp_to_sensor_mipi_info(input->camera); + mipi_info = atomisp_to_sensor_mipi_info(input->sensor); if (atomisp_set_sensor_mipi_to_isp(asd, ATOMISP_INPUT_STREAM_GENERAL, mipi_info)) diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c index 2eb44bccff0e..bc97fa2c374c 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -1845,7 +1845,7 @@ static enum ia_css_pipe_mode __pipe_id_to_pipe_mode( { struct atomisp_device *isp = asd->isp; struct camera_mipi_info *mipi_info = atomisp_to_sensor_mipi_info( - isp->inputs[asd->input_curr].camera); + isp->inputs[asd->input_curr].sensor); switch (pipe_id) { case IA_CSS_PIPE_ID_COPY: diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.c b/drivers/staging/media/atomisp/pci/atomisp_csi2.c index 28afc0bfc43b..95b9113d75e9 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_csi2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.c @@ -298,7 +298,7 @@ static void atomisp_csi2_configure_isp2401(struct atomisp_sub_device *asd) ctrl.id = V4L2_CID_LINK_FREQ; if (v4l2_g_ctrl - (isp->inputs[asd->input_curr].camera->ctrl_handler, &ctrl) == 0) + (isp->inputs[asd->input_curr].sensor->ctrl_handler, &ctrl) == 0) mipi_freq = ctrl.value; clk_termen = atomisp_csi2_configure_calc(coeff_clk_termen, mipi_freq, diff --git a/drivers/staging/media/atomisp/pci/atomisp_internal.h b/drivers/staging/media/atomisp/pci/atomisp_internal.h index 775506757471..268cfafbfe48 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_internal.h +++ b/drivers/staging/media/atomisp/pci/atomisp_internal.h @@ -114,8 +114,8 @@ struct atomisp_input_subdev { u32 code; /* MEDIA_BUS_FMT_* */ bool binning_support; bool crop_support; - bool camera_on; - struct v4l2_subdev *camera; + bool sensor_on; + struct v4l2_subdev *sensor; struct v4l2_subdev *csi_port; /* Sensor rects for sensors which support crop */ struct v4l2_rect native_rect; diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c index 1fb2ba819de3..269c2d5efa8c 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c @@ -386,11 +386,11 @@ static int atomisp_enum_input(struct file *file, void *fh, if (index >= isp->input_cnt) return -EINVAL; - if (!isp->inputs[index].camera) + if (!isp->inputs[index].sensor) return -EINVAL; memset(input, 0, sizeof(struct v4l2_input)); - strscpy(input->name, isp->inputs[index].camera->name, + strscpy(input->name, isp->inputs[index].sensor->name, sizeof(input->name)); input->type = V4L2_INPUT_TYPE_CAMERA; @@ -433,7 +433,7 @@ static int atomisp_s_input(struct file *file, void *fh, unsigned int input) if (input >= isp->input_cnt) return -EINVAL; - if (!isp->inputs[input].camera) + if (!isp->inputs[input].sensor) return -EINVAL; ret = atomisp_pipe_check(pipe, true); @@ -539,14 +539,14 @@ static int atomisp_enum_framesizes(struct file *file, void *priv, struct v4l2_subdev_state *act_sd_state; int ret; - if (!input->camera) + if (!input->sensor) return -EINVAL; if (input->crop_support) return atomisp_enum_framesizes_crop(isp, fsize); - act_sd_state = v4l2_subdev_lock_and_get_active_state(input->camera); - ret = v4l2_subdev_call(input->camera, pad, enum_frame_size, + act_sd_state = v4l2_subdev_lock_and_get_active_state(input->sensor); + ret = v4l2_subdev_call(input->sensor, pad, enum_frame_size, act_sd_state, &fse); if (act_sd_state) v4l2_subdev_unlock_state(act_sd_state); @@ -577,11 +577,11 @@ static int atomisp_enum_frameintervals(struct file *file, void *priv, struct v4l2_subdev_state *act_sd_state; int ret; - if (!input->camera) + if (!input->sensor) return -EINVAL; - act_sd_state = v4l2_subdev_lock_and_get_active_state(input->camera); - ret = v4l2_subdev_call(input->camera, pad, enum_frame_interval, + act_sd_state = v4l2_subdev_lock_and_get_active_state(input->sensor); + ret = v4l2_subdev_call(input->sensor, pad, enum_frame_interval, act_sd_state, &fie); if (act_sd_state) v4l2_subdev_unlock_state(act_sd_state); @@ -609,11 +609,11 @@ static int atomisp_enum_fmt_cap(struct file *file, void *fh, unsigned int i, fi = 0; int ret; - if (!input->camera) + if (!input->sensor) return -EINVAL; - act_sd_state = v4l2_subdev_lock_and_get_active_state(input->camera); - ret = v4l2_subdev_call(input->camera, pad, enum_mbus_code, + act_sd_state = v4l2_subdev_lock_and_get_active_state(input->sensor); + ret = v4l2_subdev_call(input->sensor, pad, enum_mbus_code, act_sd_state, &code); if (act_sd_state) v4l2_subdev_unlock_state(act_sd_state); @@ -945,7 +945,7 @@ int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count) } /* stream on the sensor */ - ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, + ret = v4l2_subdev_call(isp->inputs[asd->input_curr].sensor, video, s_stream, 1); if (ret) { dev_err(isp->dev, "Starting sensor stream failed: %d\n", ret); @@ -1002,7 +1002,7 @@ void atomisp_stop_streaming(struct vb2_queue *vq) atomisp_subdev_cleanup_pending_events(asd); - ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, + ret = v4l2_subdev_call(isp->inputs[asd->input_curr].sensor, video, s_stream, 0); if (ret) dev_warn(isp->dev, "Stopping sensor stream failed: %d\n", ret); @@ -1332,7 +1332,7 @@ static int atomisp_s_parm(struct file *file, void *fh, fi.interval = parm->parm.capture.timeperframe; - rval = v4l2_subdev_call_state_active(isp->inputs[asd->input_curr].camera, + rval = v4l2_subdev_call_state_active(isp->inputs[asd->input_curr].sensor, pad, set_frame_interval, &fi); if (!rval) parm->parm.capture.timeperframe = fi.interval; diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index 2841824cd0ca..03c535bcb229 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -895,24 +895,25 @@ static void atomisp_init_sensor(struct atomisp_input_subdev *input) * it emulates a normal v4l2 device there, passing through try_fmt / * set_fmt to the sensor. */ - try_sd_state = __v4l2_subdev_state_alloc(input->camera, - "atomisp:try_sd_state->lock", &try_sd_state_key); + try_sd_state = __v4l2_subdev_state_alloc(input->sensor, + "atomisp:try_sd_state->lock", + &try_sd_state_key); if (IS_ERR(try_sd_state)) return; input->try_sd_state = try_sd_state; - act_sd_state = v4l2_subdev_lock_and_get_active_state(input->camera); + act_sd_state = v4l2_subdev_lock_and_get_active_state(input->sensor); mbus_code_enum.which = V4L2_SUBDEV_FORMAT_ACTIVE; - err = v4l2_subdev_call(input->camera, pad, enum_mbus_code, + err = v4l2_subdev_call(input->sensor, pad, enum_mbus_code, act_sd_state, &mbus_code_enum); if (!err) input->code = mbus_code_enum.code; sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; sel.target = V4L2_SEL_TGT_NATIVE_SIZE; - err = v4l2_subdev_call(input->camera, pad, get_selection, + err = v4l2_subdev_call(input->sensor, pad, get_selection, act_sd_state, &sel); if (err) goto unlock_act_sd_state; @@ -921,7 +922,7 @@ static void atomisp_init_sensor(struct atomisp_input_subdev *input) sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; sel.target = V4L2_SEL_TGT_CROP_DEFAULT; - err = v4l2_subdev_call(input->camera, pad, get_selection, + err = v4l2_subdev_call(input->sensor, pad, get_selection, act_sd_state, &sel); if (err) goto unlock_act_sd_state; @@ -939,7 +940,7 @@ static void atomisp_init_sensor(struct atomisp_input_subdev *input) fse.code = input->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - err = v4l2_subdev_call(input->camera, pad, enum_frame_size, + err = v4l2_subdev_call(input->sensor, pad, enum_frame_size, act_sd_state, &fse); if (err) break; @@ -963,7 +964,7 @@ static void atomisp_init_sensor(struct atomisp_input_subdev *input) sel.target = V4L2_SEL_TGT_CROP; sel.r = input->native_rect; v4l2_subdev_lock_state(input->try_sd_state); - err = v4l2_subdev_call(input->camera, pad, set_selection, + err = v4l2_subdev_call(input->sensor, pad, set_selection, input->try_sd_state, &sel); v4l2_subdev_unlock_state(input->try_sd_state); if (err) @@ -972,12 +973,12 @@ static void atomisp_init_sensor(struct atomisp_input_subdev *input) sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; sel.target = V4L2_SEL_TGT_CROP; sel.r = input->native_rect; - err = v4l2_subdev_call(input->camera, pad, set_selection, + err = v4l2_subdev_call(input->sensor, pad, set_selection, act_sd_state, &sel); if (err) goto unlock_act_sd_state; - dev_info(input->camera->dev, "Supports crop native %dx%d active %dx%d binning %d\n", + dev_info(input->sensor->dev, "Supports crop native %dx%d active %dx%d binning %d\n", input->native_rect.width, input->native_rect.height, input->active_rect.width, input->active_rect.height, input->binning_support); @@ -1007,12 +1008,12 @@ int atomisp_register_device_nodes(struct atomisp_device *isp) input = &isp->inputs[isp->input_cnt]; input->port = i; - input->camera = isp->sensor_subdevs[i]; + input->sensor = isp->sensor_subdevs[i]; input->csi_port = &isp->csi2_port[i].subdev; atomisp_init_sensor(input); - err = media_create_pad_link(&input->camera->entity, 0, + err = media_create_pad_link(&input->sensor->entity, 0, &isp->csi2_port[i].subdev.entity, CSI2_PAD_SINK, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); -- cgit v1.2.3-59-g8ed1b From bceff719ef46078ddbc3f20298b7b79ad0f67889 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 1 May 2025 11:44:44 +0200 Subject: media: atomisp: Avoid deadlock with sensor subdevs with state_lock set When a (sensor) v4l2_subdev has its state_lock member set to non NULL, then all v4l2_subdev_state-s for the sensor share the same lock. atomisp_init_sensor() calls v4l2_subdev_lock_and_get_active_state() and then later on also tries to lock a separate v4l2_subdev_state used for try calls (rather then changing the active state), while still holding the active state lock. Since this try v4l2_subdev_state shares a lock with the active state this results in a deadlock. Skip locking try_sd_state when sensor->state_lock is set to avoid this. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_v4l2.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index 03c535bcb229..ac8fefca7922 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -963,10 +963,17 @@ static void atomisp_init_sensor(struct atomisp_input_subdev *input) sel.which = V4L2_SUBDEV_FORMAT_TRY; sel.target = V4L2_SEL_TGT_CROP; sel.r = input->native_rect; - v4l2_subdev_lock_state(input->try_sd_state); + + /* Don't lock try_sd_state if the lock is shared with the active state */ + if (!input->sensor->state_lock) + v4l2_subdev_lock_state(input->try_sd_state); + err = v4l2_subdev_call(input->sensor, pad, set_selection, input->try_sd_state, &sel); - v4l2_subdev_unlock_state(input->try_sd_state); + + if (!input->sensor->state_lock) + v4l2_subdev_unlock_state(input->try_sd_state); + if (err) goto unlock_act_sd_state; -- cgit v1.2.3-59-g8ed1b From 1e8c2aa905e50a331f2d52bf1e72594f27113c6e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 5 Jan 2025 22:17:27 +0100 Subject: media: atomisp: Add support for sensors with a separate ISP v4l2_subdev Some sensors have an ISP inside the sensor which the sensor driver models as a separate v4l2-subdev, like the mt9m114 sensor-driver does. Since the atomisp driver emulates a standard /dev/video# v4l2-device without requiring the application to be aware of media-controller centric /dev/video# devices this requires some special handling in the driver. Add support for this setup to the atomisp driver. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_cmd.c | 36 +++++++++++++++++++--- .../staging/media/atomisp/pci/atomisp_internal.h | 6 ++++ drivers/staging/media/atomisp/pci/atomisp_ioctl.c | 6 ++-- drivers/staging/media/atomisp/pci/atomisp_v4l2.c | 28 +++++++++++++++-- 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index 697561769eb0..3a4eb4f6d3be 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -881,7 +881,8 @@ void atomisp_assert_recovery_work(struct work_struct *work) spin_unlock_irqrestore(&isp->lock, flags); /* stream off sensor */ - ret = v4l2_subdev_call(isp->inputs[isp->asd.input_curr].sensor, video, s_stream, 0); + ret = v4l2_subdev_call(isp->inputs[isp->asd.input_curr].csi_remote_source, + video, s_stream, 0); if (ret) dev_warn(isp->dev, "Stopping sensor stream failed: %d\n", ret); @@ -936,7 +937,8 @@ void atomisp_assert_recovery_work(struct work_struct *work) /* Requeue unprocessed per-frame parameters. */ atomisp_recover_params_queue(&isp->asd.video_out); - ret = v4l2_subdev_call(isp->inputs[isp->asd.input_curr].sensor, video, s_stream, 1); + ret = v4l2_subdev_call(isp->inputs[isp->asd.input_curr].csi_remote_source, + video, s_stream, 1); if (ret) dev_err(isp->dev, "Starting sensor stream failed: %d\n", ret); @@ -3677,7 +3679,7 @@ void atomisp_setup_input_links(struct atomisp_device *isp) * will end up calling atomisp_link_setup() which calls this * function again leading to endless recursion. */ - if (isp->sensor_subdevs[i] == isp->inputs[isp->asd.input_curr].sensor) + if (isp->sensor_subdevs[i] == isp->inputs[isp->asd.input_curr].csi_remote_source) link->flags |= MEDIA_LNK_FL_ENABLED; else link->flags &= ~MEDIA_LNK_FL_ENABLED; @@ -3746,14 +3748,38 @@ static int atomisp_set_sensor_crop_and_fmt(struct atomisp_device *isp, sel.r.left, sel.r.top, sel.r.width, sel.r.height, ret); set_fmt: - if (ret == 0) + if (ret == 0) { ret = v4l2_subdev_call(input->sensor, pad, set_fmt, sd_state, &format); + dev_dbg(isp->dev, "Set sensor format ret: %d size %dx%d\n", + ret, format.format.width, format.format.height); + } if (sd_state) v4l2_subdev_unlock_state(sd_state); + /* Propagate new fmt to sensor ISP */ + if (ret == 0 && which == V4L2_SUBDEV_FORMAT_ACTIVE && input->sensor_isp) { + sd_state = v4l2_subdev_lock_and_get_active_state(input->sensor_isp); + + format.pad = SENSOR_ISP_PAD_SINK; + ret = v4l2_subdev_call(input->sensor_isp, pad, set_fmt, sd_state, &format); + dev_dbg(isp->dev, "Set sensor ISP sink format ret: %d size %dx%d\n", + ret, format.format.width, format.format.height); + + if (ret == 0) { + format.pad = SENSOR_ISP_PAD_SOURCE; + ret = v4l2_subdev_call(input->sensor_isp, pad, set_fmt, sd_state, &format); + dev_dbg(isp->dev, "Set sensor ISP source format ret: %d size %dx%d\n", + ret, format.format.width, format.format.height); + } + + if (sd_state) + v4l2_subdev_unlock_state(sd_state); + } + /* Propagate new fmt to CSI port */ - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (ret == 0 && which == V4L2_SUBDEV_FORMAT_ACTIVE) { + format.pad = CSI2_PAD_SINK; ret = v4l2_subdev_call(input->csi_port, pad, set_fmt, NULL, &format); if (ret) return ret; diff --git a/drivers/staging/media/atomisp/pci/atomisp_internal.h b/drivers/staging/media/atomisp/pci/atomisp_internal.h index 268cfafbfe48..5a69580b8251 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_internal.h +++ b/drivers/staging/media/atomisp/pci/atomisp_internal.h @@ -109,6 +109,10 @@ #define DIV_NEAREST_STEP(n, d, step) \ round_down((2 * (n) + (d) * (step)) / (2 * (d)), (step)) +#define SENSOR_ISP_PAD_SINK 0 +#define SENSOR_ISP_PAD_SOURCE 1 +#define SENSOR_ISP_PADS_NUM 2 + struct atomisp_input_subdev { enum atomisp_camera_port port; u32 code; /* MEDIA_BUS_FMT_* */ @@ -116,7 +120,9 @@ struct atomisp_input_subdev { bool crop_support; bool sensor_on; struct v4l2_subdev *sensor; + struct v4l2_subdev *sensor_isp; struct v4l2_subdev *csi_port; + struct v4l2_subdev *csi_remote_source; /* Sensor rects for sensors which support crop */ struct v4l2_rect native_rect; struct v4l2_rect active_rect; diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c index 269c2d5efa8c..97d99bed1560 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c @@ -945,7 +945,7 @@ int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count) } /* stream on the sensor */ - ret = v4l2_subdev_call(isp->inputs[asd->input_curr].sensor, + ret = v4l2_subdev_call(isp->inputs[asd->input_curr].csi_remote_source, video, s_stream, 1); if (ret) { dev_err(isp->dev, "Starting sensor stream failed: %d\n", ret); @@ -1002,7 +1002,7 @@ void atomisp_stop_streaming(struct vb2_queue *vq) atomisp_subdev_cleanup_pending_events(asd); - ret = v4l2_subdev_call(isp->inputs[asd->input_curr].sensor, + ret = v4l2_subdev_call(isp->inputs[asd->input_curr].csi_remote_source, video, s_stream, 0); if (ret) dev_warn(isp->dev, "Stopping sensor stream failed: %d\n", ret); @@ -1332,7 +1332,7 @@ static int atomisp_s_parm(struct file *file, void *fh, fi.interval = parm->parm.capture.timeperframe; - rval = v4l2_subdev_call_state_active(isp->inputs[asd->input_curr].sensor, + rval = v4l2_subdev_call_state_active(isp->inputs[asd->input_curr].csi_remote_source, pad, set_frame_interval, &fi); if (!rval) parm->parm.capture.timeperframe = fi.interval; diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index ac8fefca7922..0bd0bfded4af 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -999,8 +999,9 @@ unlock_act_sd_state: int atomisp_register_device_nodes(struct atomisp_device *isp) { + struct media_pad *sensor_isp_sink, *sensor_src; struct atomisp_input_subdev *input; - int i, err; + int i, err, source_pad; for (i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++) { err = media_create_pad_link(&isp->csi2_port[i].subdev.entity, @@ -1015,12 +1016,33 @@ int atomisp_register_device_nodes(struct atomisp_device *isp) input = &isp->inputs[isp->input_cnt]; input->port = i; - input->sensor = isp->sensor_subdevs[i]; input->csi_port = &isp->csi2_port[i].subdev; + input->csi_remote_source = isp->sensor_subdevs[i]; + + /* + * Special case for sensors with a ISP in the sensor modelled + * as a separate v4l2-subdev, like the mt9m114. + */ + if (isp->sensor_subdevs[i]->entity.function == MEDIA_ENT_F_PROC_VIDEO_ISP) { + input->sensor_isp = isp->sensor_subdevs[i]; + source_pad = SENSOR_ISP_PAD_SOURCE; + + sensor_isp_sink = &input->sensor_isp->entity.pads[SENSOR_ISP_PAD_SINK]; + sensor_src = media_pad_remote_pad_first(sensor_isp_sink); + if (!sensor_src) { + dev_err(isp->dev, "Error could not find remote pad for sensor ISP sink\n"); + return -ENOENT; + } + + input->sensor = media_entity_to_v4l2_subdev(sensor_src->entity); + } else { + input->sensor = isp->sensor_subdevs[i]; + source_pad = 0; + } atomisp_init_sensor(input); - err = media_create_pad_link(&input->sensor->entity, 0, + err = media_create_pad_link(&isp->sensor_subdevs[i]->entity, source_pad, &isp->csi2_port[i].subdev.entity, CSI2_PAD_SINK, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); -- cgit v1.2.3-59-g8ed1b From 6e769fd53d25df5353150628ac0b149ae65d9cbd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 1 May 2025 11:00:16 +0200 Subject: media: atomisp: Remove atomisp-mt9m114 driver The "media: atomisp: Add support for sensors with a separate ISP v4l2_subdev" combined with some pending drivers/media/i2c/mt9m114.c changes makes the atomisp work nicely with the standard V4L2 mt9m114.c driver, avoiding the need for the atomisp specific atomisp-mt9m114 driver. The normal driver actually works better since the atomisp-mt9m114 driver does not have working exposure control, leading to a much too dark image. Remove the no longer necessary atomisp-mt9m114 driver. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/Kconfig | 12 - drivers/staging/media/atomisp/i2c/Makefile | 1 - .../staging/media/atomisp/i2c/atomisp-mt9m114.c | 1612 ------------------ drivers/staging/media/atomisp/i2c/mt9m114.h | 1768 -------------------- 4 files changed, 3393 deletions(-) delete mode 100644 drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c delete mode 100644 drivers/staging/media/atomisp/i2c/mt9m114.h diff --git a/drivers/staging/media/atomisp/i2c/Kconfig b/drivers/staging/media/atomisp/i2c/Kconfig index f5ab23592f29..4f46182da58b 100644 --- a/drivers/staging/media/atomisp/i2c/Kconfig +++ b/drivers/staging/media/atomisp/i2c/Kconfig @@ -27,18 +27,6 @@ config VIDEO_ATOMISP_GC2235 It currently only works with the atomisp driver. -config VIDEO_ATOMISP_MT9M114 - tristate "Aptina mt9m114 sensor support" - depends on ACPI - depends on I2C && VIDEO_DEV - help - This is a Video4Linux2 sensor-level driver for the Micron - mt9m114 1.3 Mpixel camera. - - mt9m114 is video camera sensor. - - It currently only works with the atomisp driver. - config VIDEO_ATOMISP_GC0310 tristate "GC0310 sensor support" depends on ACPI diff --git a/drivers/staging/media/atomisp/i2c/Makefile b/drivers/staging/media/atomisp/i2c/Makefile index 021a7ea0a075..e1637417e5c5 100644 --- a/drivers/staging/media/atomisp/i2c/Makefile +++ b/drivers/staging/media/atomisp/i2c/Makefile @@ -3,7 +3,6 @@ # Makefile for sensor drivers # -obj-$(CONFIG_VIDEO_ATOMISP_MT9M114) += atomisp-mt9m114.o obj-$(CONFIG_VIDEO_ATOMISP_GC2235) += atomisp-gc2235.o obj-$(CONFIG_VIDEO_ATOMISP_OV2722) += atomisp-ov2722.o obj-$(CONFIG_VIDEO_ATOMISP_GC0310) += atomisp-gc0310.o diff --git a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c deleted file mode 100644 index 4658aeeb88fd..000000000000 --- a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c +++ /dev/null @@ -1,1612 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Support for mt9m114 Camera Sensor. - * - * Copyright (c) 2010 Intel Corporation. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../include/linux/atomisp_gmin_platform.h" -#include - -#include "mt9m114.h" - -#define to_mt9m114_sensor(s) container_of(s, struct mt9m114_device, sd) - -/* - * TODO: use debug parameter to actually define when debug messages should - * be printed. - */ -static int debug; -static int aaalock; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -static int mt9m114_t_vflip(struct v4l2_subdev *sd, int value); -static int mt9m114_t_hflip(struct v4l2_subdev *sd, int value); -static int mt9m114_wait_state(struct i2c_client *client, int timeout); - -static int -mt9m114_read_reg(struct i2c_client *client, u16 data_length, u32 reg, u32 *val) -{ - int err; - struct i2c_msg msg[2]; - unsigned char data[4]; - - if (!client->adapter) { - v4l2_err(client, "%s error, no client->adapter\n", __func__); - return -ENODEV; - } - - if (data_length != MISENSOR_8BIT && data_length != MISENSOR_16BIT - && data_length != MISENSOR_32BIT) { - v4l2_err(client, "%s error, invalid data length\n", __func__); - return -EINVAL; - } - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = MSG_LEN_OFFSET; - msg[0].buf = data; - - /* high byte goes out first */ - data[0] = (u16)(reg >> 8); - data[1] = (u16)(reg & 0xff); - - msg[1].addr = client->addr; - msg[1].len = data_length; - msg[1].flags = I2C_M_RD; - msg[1].buf = data; - - err = i2c_transfer(client->adapter, msg, 2); - - if (err >= 0) { - *val = 0; - /* high byte comes first */ - if (data_length == MISENSOR_8BIT) - *val = data[0]; - else if (data_length == MISENSOR_16BIT) - *val = data[1] + (data[0] << 8); - else - *val = data[3] + (data[2] << 8) + - (data[1] << 16) + (data[0] << 24); - - return 0; - } - - dev_err(&client->dev, "read from offset 0x%x error %d", reg, err); - return err; -} - -static int -mt9m114_write_reg(struct i2c_client *client, u16 data_length, u16 reg, u32 val) -{ - int num_msg; - struct i2c_msg msg; - unsigned char data[6] = {0}; - __be16 *wreg; - int retry = 0; - - if (!client->adapter) { - v4l2_err(client, "%s error, no client->adapter\n", __func__); - return -ENODEV; - } - - if (data_length != MISENSOR_8BIT && data_length != MISENSOR_16BIT - && data_length != MISENSOR_32BIT) { - v4l2_err(client, "%s error, invalid data_length\n", __func__); - return -EINVAL; - } - - memset(&msg, 0, sizeof(msg)); - -again: - msg.addr = client->addr; - msg.flags = 0; - msg.len = 2 + data_length; - msg.buf = data; - - /* high byte goes out first */ - wreg = (void *)data; - *wreg = cpu_to_be16(reg); - - if (data_length == MISENSOR_8BIT) { - data[2] = (u8)(val); - } else if (data_length == MISENSOR_16BIT) { - u16 *wdata = (void *)&data[2]; - - *wdata = be16_to_cpu(*(__be16 *)&data[2]); - } else { - /* MISENSOR_32BIT */ - u32 *wdata = (void *)&data[2]; - - *wdata = be32_to_cpu(*(__be32 *)&data[2]); - } - - num_msg = i2c_transfer(client->adapter, &msg, 1); - - /* - * HACK: Need some delay here for Rev 2 sensors otherwise some - * registers do not seem to load correctly. - */ - mdelay(1); - - if (num_msg >= 0) - return 0; - - dev_err(&client->dev, "write error: wrote 0x%x to offset 0x%x error %d", - val, reg, num_msg); - if (retry <= I2C_RETRY_COUNT) { - dev_dbg(&client->dev, "retrying... %d", retry); - retry++; - msleep(20); - goto again; - } - - return num_msg; -} - -/** - * misensor_rmw_reg - Read/Modify/Write a value to a register in the sensor - * device - * @client: i2c driver client structure - * @data_length: 8/16/32-bits length - * @reg: register address - * @mask: masked out bits - * @set: bits set - * - * Read/modify/write a value to a register in the sensor device. - * Returns zero if successful, or non-zero otherwise. - */ -static int -misensor_rmw_reg(struct i2c_client *client, u16 data_length, u16 reg, - u32 mask, u32 set) -{ - int err; - u32 val; - - /* Exit when no mask */ - if (mask == 0) - return 0; - - /* @mask must not exceed data length */ - switch (data_length) { - case MISENSOR_8BIT: - if (mask & ~0xff) - return -EINVAL; - break; - case MISENSOR_16BIT: - if (mask & ~0xffff) - return -EINVAL; - break; - case MISENSOR_32BIT: - break; - default: - /* Wrong @data_length */ - return -EINVAL; - } - - err = mt9m114_read_reg(client, data_length, reg, &val); - if (err) { - v4l2_err(client, "%s error exit, read failed\n", __func__); - return -EINVAL; - } - - val &= ~mask; - - /* - * Perform the OR function if the @set exists. - * Shift @set value to target bit location. @set should set only - * bits included in @mask. - * - * REVISIT: This function expects @set to be non-shifted. Its shift - * value is then defined to be equal to mask's LSB position. - * How about to inform values in their right offset position and avoid - * this unneeded shift operation? - */ - set <<= ffs(mask) - 1; - val |= set & mask; - - err = mt9m114_write_reg(client, data_length, reg, val); - if (err) { - v4l2_err(client, "%s error exit, write failed\n", __func__); - return -EINVAL; - } - - return 0; -} - -static int __mt9m114_flush_reg_array(struct i2c_client *client, - struct mt9m114_write_ctrl *ctrl) -{ - struct i2c_msg msg; - const int num_msg = 1; - int ret; - int retry = 0; - __be16 *data16 = (void *)&ctrl->buffer.addr; - - if (ctrl->index == 0) - return 0; - -again: - msg.addr = client->addr; - msg.flags = 0; - msg.len = 2 + ctrl->index; - *data16 = cpu_to_be16(ctrl->buffer.addr); - msg.buf = (u8 *)&ctrl->buffer; - - ret = i2c_transfer(client->adapter, &msg, num_msg); - if (ret != num_msg) { - if (++retry <= I2C_RETRY_COUNT) { - dev_dbg(&client->dev, "retrying... %d\n", retry); - msleep(20); - goto again; - } - dev_err(&client->dev, "%s: i2c transfer error\n", __func__); - return -EIO; - } - - ctrl->index = 0; - - /* - * REVISIT: Previously we had a delay after writing data to sensor. - * But it was removed as our tests have shown it is not necessary - * anymore. - */ - - return 0; -} - -static int __mt9m114_buf_reg_array(struct i2c_client *client, - struct mt9m114_write_ctrl *ctrl, - const struct misensor_reg *next) -{ - __be16 *data16; - __be32 *data32; - int err; - - /* Insufficient buffer? Let's flush and get more free space. */ - if (ctrl->index + next->length >= MT9M114_MAX_WRITE_BUF_SIZE) { - err = __mt9m114_flush_reg_array(client, ctrl); - if (err) - return err; - } - - switch (next->length) { - case MISENSOR_8BIT: - ctrl->buffer.data[ctrl->index] = (u8)next->val; - break; - case MISENSOR_16BIT: - data16 = (__be16 *)&ctrl->buffer.data[ctrl->index]; - *data16 = cpu_to_be16((u16)next->val); - break; - case MISENSOR_32BIT: - data32 = (__be32 *)&ctrl->buffer.data[ctrl->index]; - *data32 = cpu_to_be32(next->val); - break; - default: - return -EINVAL; - } - - /* When first item is added, we need to store its starting address */ - if (ctrl->index == 0) - ctrl->buffer.addr = next->reg; - - ctrl->index += next->length; - - return 0; -} - -static int -__mt9m114_write_reg_is_consecutive(struct i2c_client *client, - struct mt9m114_write_ctrl *ctrl, - const struct misensor_reg *next) -{ - if (ctrl->index == 0) - return 1; - - return ctrl->buffer.addr + ctrl->index == next->reg; -} - -/* - * mt9m114_write_reg_array - Initializes a list of mt9m114 registers - * @client: i2c driver client structure - * @reglist: list of registers to be written - * @poll: completion polling requirement - * This function initializes a list of registers. When consecutive addresses - * are found in a row on the list, this function creates a buffer and sends - * consecutive data in a single i2c_transfer(). - * - * __mt9m114_flush_reg_array, __mt9m114_buf_reg_array() and - * __mt9m114_write_reg_is_consecutive() are internal functions to - * mt9m114_write_reg_array() and should be not used anywhere else. - * - */ -static int mt9m114_write_reg_array(struct i2c_client *client, - const struct misensor_reg *reglist, - int poll) -{ - const struct misensor_reg *next = reglist; - struct mt9m114_write_ctrl ctrl; - int err; - - if (poll == PRE_POLLING) { - err = mt9m114_wait_state(client, MT9M114_WAIT_STAT_TIMEOUT); - if (err) - return err; - } - - ctrl.index = 0; - for (; next->length != MISENSOR_TOK_TERM; next++) { - switch (next->length & MISENSOR_TOK_MASK) { - case MISENSOR_TOK_DELAY: - err = __mt9m114_flush_reg_array(client, &ctrl); - if (err) - return err; - msleep(next->val); - break; - case MISENSOR_TOK_RMW: - err = __mt9m114_flush_reg_array(client, &ctrl); - err |= misensor_rmw_reg(client, - next->length & - ~MISENSOR_TOK_RMW, - next->reg, next->val, - next->val2); - if (err) { - dev_err(&client->dev, "%s read err. aborted\n", - __func__); - return -EINVAL; - } - break; - default: - /* - * If next address is not consecutive, data needs to be - * flushed before proceed. - */ - if (!__mt9m114_write_reg_is_consecutive(client, &ctrl, - next)) { - err = __mt9m114_flush_reg_array(client, &ctrl); - if (err) - return err; - } - err = __mt9m114_buf_reg_array(client, &ctrl, next); - if (err) { - v4l2_err(client, "%s: write error, aborted\n", - __func__); - return err; - } - break; - } - } - - err = __mt9m114_flush_reg_array(client, &ctrl); - if (err) - return err; - - if (poll == POST_POLLING) - return mt9m114_wait_state(client, MT9M114_WAIT_STAT_TIMEOUT); - - return 0; -} - -static int mt9m114_wait_state(struct i2c_client *client, int timeout) -{ - int ret; - unsigned int val; - - while (timeout-- > 0) { - ret = mt9m114_read_reg(client, MISENSOR_16BIT, 0x0080, &val); - if (ret) - return ret; - if ((val & 0x2) == 0) - return 0; - msleep(20); - } - - return -EINVAL; -} - -static int mt9m114_set_suspend(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return mt9m114_write_reg_array(client, - mt9m114_standby_reg, POST_POLLING); -} - -static int mt9m114_init_common(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return mt9m114_write_reg_array(client, mt9m114_common, PRE_POLLING); -} - -static int power_ctrl(struct v4l2_subdev *sd, bool flag) -{ - int ret; - struct mt9m114_device *dev = to_mt9m114_sensor(sd); - - if (!dev || !dev->platform_data) - return -ENODEV; - - if (flag) { - ret = dev->platform_data->v2p8_ctrl(sd, 1); - if (ret == 0) { - ret = dev->platform_data->v1p8_ctrl(sd, 1); - if (ret) - ret = dev->platform_data->v2p8_ctrl(sd, 0); - } - } else { - ret = dev->platform_data->v2p8_ctrl(sd, 0); - ret = dev->platform_data->v1p8_ctrl(sd, 0); - } - return ret; -} - -static int gpio_ctrl(struct v4l2_subdev *sd, bool flag) -{ - int ret; - struct mt9m114_device *dev = to_mt9m114_sensor(sd); - - if (!dev || !dev->platform_data) - return -ENODEV; - - /* - * Note: current modules wire only one GPIO signal (RESET#), - * but the schematic wires up two to the connector. BIOS - * versions have been unfortunately inconsistent with which - * ACPI index RESET# is on, so hit both - */ - - if (flag) { - ret = dev->platform_data->gpio0_ctrl(sd, 0); - ret = dev->platform_data->gpio1_ctrl(sd, 0); - msleep(60); - ret |= dev->platform_data->gpio0_ctrl(sd, 1); - ret |= dev->platform_data->gpio1_ctrl(sd, 1); - } else { - ret = dev->platform_data->gpio0_ctrl(sd, 0); - ret = dev->platform_data->gpio1_ctrl(sd, 0); - } - return ret; -} - -static int power_up(struct v4l2_subdev *sd) -{ - struct mt9m114_device *dev = to_mt9m114_sensor(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - if (!dev->platform_data) { - dev_err(&client->dev, "no camera_sensor_platform_data"); - return -ENODEV; - } - - /* power control */ - ret = power_ctrl(sd, 1); - if (ret) - goto fail_power; - - /* flis clock control */ - ret = dev->platform_data->flisclk_ctrl(sd, 1); - if (ret) - goto fail_clk; - - /* gpio ctrl */ - ret = gpio_ctrl(sd, 1); - if (ret) - dev_err(&client->dev, "gpio failed 1\n"); - /* - * according to DS, 44ms is needed between power up and first i2c - * commend - */ - msleep(50); - - return 0; - -fail_clk: - dev->platform_data->flisclk_ctrl(sd, 0); -fail_power: - power_ctrl(sd, 0); - dev_err(&client->dev, "sensor power-up failed\n"); - - return ret; -} - -static int power_down(struct v4l2_subdev *sd) -{ - struct mt9m114_device *dev = to_mt9m114_sensor(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - if (!dev->platform_data) { - dev_err(&client->dev, "no camera_sensor_platform_data"); - return -ENODEV; - } - - ret = dev->platform_data->flisclk_ctrl(sd, 0); - if (ret) - dev_err(&client->dev, "flisclk failed\n"); - - /* gpio ctrl */ - ret = gpio_ctrl(sd, 0); - if (ret) - dev_err(&client->dev, "gpio failed 1\n"); - - /* power control */ - ret = power_ctrl(sd, 0); - if (ret) - dev_err(&client->dev, "vprog failed.\n"); - - /* according to DS, 20ms is needed after power down */ - msleep(20); - - return ret; -} - -static int mt9m114_s_power(struct v4l2_subdev *sd, int power) -{ - if (power == 0) - return power_down(sd); - - if (power_up(sd)) - return -EINVAL; - - return mt9m114_init_common(sd); -} - -static int mt9m114_res2size(struct v4l2_subdev *sd, int *h_size, int *v_size) -{ - struct mt9m114_device *dev = to_mt9m114_sensor(sd); - unsigned short hsize; - unsigned short vsize; - - switch (dev->res) { - case MT9M114_RES_736P: - hsize = MT9M114_RES_736P_SIZE_H; - vsize = MT9M114_RES_736P_SIZE_V; - break; - case MT9M114_RES_864P: - hsize = MT9M114_RES_864P_SIZE_H; - vsize = MT9M114_RES_864P_SIZE_V; - break; - case MT9M114_RES_960P: - hsize = MT9M114_RES_960P_SIZE_H; - vsize = MT9M114_RES_960P_SIZE_V; - break; - default: - v4l2_err(sd, "%s: Resolution 0x%08x unknown\n", __func__, - dev->res); - return -EINVAL; - } - - if (h_size) - *h_size = hsize; - if (v_size) - *v_size = vsize; - - return 0; -} - -static int mt9m114_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *fmt = &format->format; - int width, height; - int ret; - - if (format->pad) - return -EINVAL; - fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; - - ret = mt9m114_res2size(sd, &width, &height); - if (ret) - return ret; - fmt->width = width; - fmt->height = height; - - return 0; -} - -static int mt9m114_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *fmt = &format->format; - struct i2c_client *c = v4l2_get_subdevdata(sd); - struct mt9m114_device *dev = to_mt9m114_sensor(sd); - struct mt9m114_res_struct *res; - u32 width = fmt->width; - u32 height = fmt->height; - struct camera_mipi_info *mt9m114_info = NULL; - - int ret; - - if (format->pad) - return -EINVAL; - dev->streamon = 0; - dev->first_exp = MT9M114_DEFAULT_FIRST_EXP; - - mt9m114_info = v4l2_get_subdev_hostdata(sd); - if (!mt9m114_info) - return -EINVAL; - - res = v4l2_find_nearest_size(mt9m114_res, - ARRAY_SIZE(mt9m114_res), width, - height, fmt->width, fmt->height); - if (!res) - res = &mt9m114_res[N_RES - 1]; - - fmt->width = res->width; - fmt->height = res->height; - fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_state_get_format(sd_state, 0) = *fmt; - return 0; - } - - switch (res->res) { - case MT9M114_RES_736P: - ret = mt9m114_write_reg_array(c, mt9m114_736P_init, NO_POLLING); - ret += misensor_rmw_reg(c, MISENSOR_16BIT, MISENSOR_READ_MODE, - MISENSOR_R_MODE_MASK, MISENSOR_NORMAL_SET); - break; - case MT9M114_RES_864P: - ret = mt9m114_write_reg_array(c, mt9m114_864P_init, NO_POLLING); - ret += misensor_rmw_reg(c, MISENSOR_16BIT, MISENSOR_READ_MODE, - MISENSOR_R_MODE_MASK, MISENSOR_NORMAL_SET); - break; - case MT9M114_RES_960P: - ret = mt9m114_write_reg_array(c, mt9m114_976P_init, NO_POLLING); - /* set sensor read_mode to Normal */ - ret += misensor_rmw_reg(c, MISENSOR_16BIT, MISENSOR_READ_MODE, - MISENSOR_R_MODE_MASK, MISENSOR_NORMAL_SET); - break; - default: - v4l2_err(sd, "set resolution: %d failed!\n", res->res); - return -EINVAL; - } - - if (ret) - return -EINVAL; - - ret = mt9m114_write_reg_array(c, mt9m114_chgstat_reg, POST_POLLING); - if (ret < 0) - return ret; - - if (mt9m114_set_suspend(sd)) - return -EINVAL; - - if (dev->res != res->res) { - int index; - - /* Switch to different size */ - if (width <= 640) { - dev->nctx = 0x00; /* Set for context A */ - } else { - /* - * Context B is used for resolutions larger than 640x480 - * Using YUV for Context B. - */ - dev->nctx = 0x01; /* set for context B */ - } - - /* - * Marked current sensor res as being "used" - * - * REVISIT: We don't need to use an "used" field on each mode - * list entry to know which mode is selected. If this - * information is really necessary, how about to use a single - * variable on sensor dev struct? - */ - for (index = 0; index < N_RES; index++) { - if ((width == mt9m114_res[index].width) && - (height == mt9m114_res[index].height)) { - mt9m114_res[index].used = true; - continue; - } - mt9m114_res[index].used = false; - } - } - /* - * mt9m114 - we don't poll for context switch - * because it does not happen with streaming disabled. - */ - dev->res = res->res; - - fmt->width = width; - fmt->height = height; - fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; - return 0; -} - -/* Horizontal flip the image. */ -static int mt9m114_g_hflip(struct v4l2_subdev *sd, s32 *val) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - int ret; - u32 data; - - ret = mt9m114_read_reg(c, MISENSOR_16BIT, - (u32)MISENSOR_READ_MODE, &data); - if (ret) - return ret; - *val = !!(data & MISENSOR_HFLIP_MASK); - - return 0; -} - -static int mt9m114_g_vflip(struct v4l2_subdev *sd, s32 *val) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - int ret; - u32 data; - - ret = mt9m114_read_reg(c, MISENSOR_16BIT, - (u32)MISENSOR_READ_MODE, &data); - if (ret) - return ret; - *val = !!(data & MISENSOR_VFLIP_MASK); - - return 0; -} - -static long mt9m114_s_exposure(struct v4l2_subdev *sd, - struct atomisp_exposure *exposure) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9m114_device *dev = to_mt9m114_sensor(sd); - int ret = 0; - unsigned int coarse_integration = 0; - unsigned int f_lines = 0; - unsigned int frame_len_lines = 0; /* ExposureTime.FrameLengthLines; */ - unsigned int analog_gain, digital_gain; - u32 analog_gain_to_write = 0; - - dev_dbg(&client->dev, "%s(0x%X 0x%X 0x%X)\n", __func__, - exposure->integration_time[0], exposure->gain[0], - exposure->gain[1]); - - coarse_integration = exposure->integration_time[0]; - /* - * fine_integration = ExposureTime.FineIntegrationTime; - * frame_len_lines = ExposureTime.FrameLengthLines; - */ - f_lines = mt9m114_res[dev->res].lines_per_frame; - analog_gain = exposure->gain[0]; - digital_gain = exposure->gain[1]; - if (!dev->streamon) { - /*Save the first exposure values while stream is off*/ - dev->first_exp = coarse_integration; - dev->first_gain = analog_gain; - dev->first_diggain = digital_gain; - } - /* digital_gain = 0x400 * (((u16) digital_gain) >> 8) + */ - /* ((unsigned int)(0x400 * (((u16) digital_gain) & 0xFF)) >>8); */ - - /* set frame length */ - if (f_lines < coarse_integration + 6) - f_lines = coarse_integration + 6; - if (f_lines < frame_len_lines) - f_lines = frame_len_lines; - ret = mt9m114_write_reg(client, MISENSOR_16BIT, 0x300A, f_lines); - if (ret) { - v4l2_err(client, "%s: fail to set f_lines\n", __func__); - return -EINVAL; - } - - /* set coarse integration */ - /* - * 3A provide real exposure time. - * should not translate to any value here. - */ - ret = mt9m114_write_reg(client, MISENSOR_16BIT, - REG_EXPO_COARSE, (u16)(coarse_integration)); - if (ret) { - v4l2_err(client, "%s: fail to set exposure time\n", __func__); - return -EINVAL; - } - - /* - * set analog/digital gain - switch(analog_gain) - { - case 0: - analog_gain_to_write = 0x0; - break; - case 1: - analog_gain_to_write = 0x20; - break; - case 2: - analog_gain_to_write = 0x60; - break; - case 4: - analog_gain_to_write = 0xA0; - break; - case 8: - analog_gain_to_write = 0xE0; - break; - default: - analog_gain_to_write = 0x20; - break; - } - */ - if (digital_gain >= 16 || digital_gain <= 1) - digital_gain = 1; - /* - * analog_gain_to_write = (u16)((digital_gain << 12) - * | analog_gain_to_write); - */ - analog_gain_to_write = (u16)((digital_gain << 12) | (u16)analog_gain); - ret = mt9m114_write_reg(client, MISENSOR_16BIT, - REG_GAIN, analog_gain_to_write); - if (ret) { - v4l2_err(client, "%s: fail to set analog_gain_to_write\n", - __func__); - return -EINVAL; - } - - return ret; -} - -static long mt9m114_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) -{ - switch (cmd) { - case ATOMISP_IOC_S_EXPOSURE: - return mt9m114_s_exposure(sd, arg); - default: - return -EINVAL; - } - - return 0; -} - -/* - * This returns the exposure time being used. This should only be used - * for filling in EXIF data, not for actual image processing. - */ -static int mt9m114_g_exposure(struct v4l2_subdev *sd, s32 *value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u32 coarse; - int ret; - - /* the fine integration time is currently not calculated */ - ret = mt9m114_read_reg(client, MISENSOR_16BIT, - REG_EXPO_COARSE, &coarse); - if (ret) - return ret; - - *value = coarse; - return 0; -} - -/* - * This function will return the sensor supported max exposure zone number. - * the sensor which supports max exposure zone number is 1. - */ -static int mt9m114_g_exposure_zone_num(struct v4l2_subdev *sd, s32 *val) -{ - *val = 1; - - return 0; -} - -/* - * set exposure metering, average/center_weighted/spot/matrix. - */ -static int mt9m114_s_exposure_metering(struct v4l2_subdev *sd, s32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - switch (val) { - case V4L2_EXPOSURE_METERING_SPOT: - ret = mt9m114_write_reg_array(client, mt9m114_exp_average, - NO_POLLING); - if (ret) { - dev_err(&client->dev, "write exp_average reg err.\n"); - return ret; - } - break; - case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: - default: - ret = mt9m114_write_reg_array(client, mt9m114_exp_center, - NO_POLLING); - if (ret) { - dev_err(&client->dev, "write exp_default reg err"); - return ret; - } - } - - return 0; -} - -/* - * This function is for touch exposure feature. - */ -static int mt9m114_s_exposure_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct misensor_reg exp_reg; - int width, height; - int grid_width, grid_height; - int grid_left, grid_top, grid_right, grid_bottom; - int win_left, win_top, win_right, win_bottom; - int i, j; - int ret; - - if (sel->which != V4L2_SUBDEV_FORMAT_TRY && - sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - grid_left = sel->r.left; - grid_top = sel->r.top; - grid_right = sel->r.left + sel->r.width - 1; - grid_bottom = sel->r.top + sel->r.height - 1; - - ret = mt9m114_res2size(sd, &width, &height); - if (ret) - return ret; - - grid_width = width / 5; - grid_height = height / 5; - - if (grid_width && grid_height) { - win_left = grid_left / grid_width; - win_top = grid_top / grid_height; - win_right = grid_right / grid_width; - win_bottom = grid_bottom / grid_height; - } else { - dev_err(&client->dev, "Incorrect exp grid.\n"); - return -EINVAL; - } - - win_left = clamp_t(int, win_left, 0, 4); - win_top = clamp_t(int, win_top, 0, 4); - win_right = clamp_t(int, win_right, 0, 4); - win_bottom = clamp_t(int, win_bottom, 0, 4); - - ret = mt9m114_write_reg_array(client, mt9m114_exp_average, NO_POLLING); - if (ret) { - dev_err(&client->dev, "write exp_average reg err.\n"); - return ret; - } - - for (i = win_top; i <= win_bottom; i++) { - for (j = win_left; j <= win_right; j++) { - exp_reg = mt9m114_exp_win[i][j]; - - ret = mt9m114_write_reg(client, exp_reg.length, - exp_reg.reg, exp_reg.val); - if (ret) { - dev_err(&client->dev, "write exp_reg err.\n"); - return ret; - } - } - } - - return 0; -} - -static int mt9m114_s_ev(struct v4l2_subdev *sd, s32 val) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - s32 luma = 0x37; - int err; - - /* - * EV value only support -2 to 2 - * 0: 0x37, 1:0x47, 2:0x57, -1:0x27, -2:0x17 - */ - if (val < -2 || val > 2) - return -EINVAL; - luma += 0x10 * val; - dev_dbg(&c->dev, "%s val:%d luma:0x%x\n", __func__, val, luma); - err = mt9m114_write_reg(c, MISENSOR_16BIT, 0x098E, 0xC87A); - if (err) { - dev_err(&c->dev, "%s logic addr access error\n", __func__); - return err; - } - err = mt9m114_write_reg(c, MISENSOR_8BIT, 0xC87A, (u32)luma); - if (err) { - dev_err(&c->dev, "%s write target_average_luma failed\n", - __func__); - return err; - } - udelay(10); - - return 0; -} - -static int mt9m114_g_ev(struct v4l2_subdev *sd, s32 *val) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - int err; - u32 luma; - - err = mt9m114_write_reg(c, MISENSOR_16BIT, 0x098E, 0xC87A); - if (err) { - dev_err(&c->dev, "%s logic addr access error\n", __func__); - return err; - } - err = mt9m114_read_reg(c, MISENSOR_8BIT, 0xC87A, &luma); - if (err) { - dev_err(&c->dev, "%s read target_average_luma failed\n", - __func__); - return err; - } - luma -= 0x17; - luma /= 0x10; - *val = (s32)luma - 2; - dev_dbg(&c->dev, "%s val:%d\n", __func__, *val); - - return 0; -} - -/* - * Fake interface - * mt9m114 now can not support 3a_lock - */ -static int mt9m114_s_3a_lock(struct v4l2_subdev *sd, s32 val) -{ - aaalock = val; - return 0; -} - -static int mt9m114_g_3a_lock(struct v4l2_subdev *sd, s32 *val) -{ - if (aaalock) - return V4L2_LOCK_EXPOSURE | V4L2_LOCK_WHITE_BALANCE - | V4L2_LOCK_FOCUS; - return 0; -} - -static int mt9m114_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9m114_device *dev = - container_of(ctrl->handler, struct mt9m114_device, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - dev_dbg(&client->dev, "%s: CID_VFLIP:%d.\n", - __func__, ctrl->val); - ret = mt9m114_t_vflip(&dev->sd, ctrl->val); - break; - case V4L2_CID_HFLIP: - dev_dbg(&client->dev, "%s: CID_HFLIP:%d.\n", - __func__, ctrl->val); - ret = mt9m114_t_hflip(&dev->sd, ctrl->val); - break; - case V4L2_CID_EXPOSURE_METERING: - ret = mt9m114_s_exposure_metering(&dev->sd, ctrl->val); - break; - case V4L2_CID_EXPOSURE: - ret = mt9m114_s_ev(&dev->sd, ctrl->val); - break; - case V4L2_CID_3A_LOCK: - ret = mt9m114_s_3a_lock(&dev->sd, ctrl->val); - break; - default: - ret = -EINVAL; - } - return ret; -} - -static int mt9m114_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9m114_device *dev = - container_of(ctrl->handler, struct mt9m114_device, ctrl_handler); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - ret = mt9m114_g_vflip(&dev->sd, &ctrl->val); - break; - case V4L2_CID_HFLIP: - ret = mt9m114_g_hflip(&dev->sd, &ctrl->val); - break; - case V4L2_CID_EXPOSURE_ABSOLUTE: - ret = mt9m114_g_exposure(&dev->sd, &ctrl->val); - break; - case V4L2_CID_EXPOSURE_ZONE_NUM: - ret = mt9m114_g_exposure_zone_num(&dev->sd, &ctrl->val); - break; - case V4L2_CID_EXPOSURE: - ret = mt9m114_g_ev(&dev->sd, &ctrl->val); - break; - case V4L2_CID_3A_LOCK: - ret = mt9m114_g_3a_lock(&dev->sd, &ctrl->val); - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static const struct v4l2_ctrl_ops ctrl_ops = { - .s_ctrl = mt9m114_s_ctrl, - .g_volatile_ctrl = mt9m114_g_volatile_ctrl -}; - -static struct v4l2_ctrl_config mt9m114_controls[] = { - { - .ops = &ctrl_ops, - .id = V4L2_CID_VFLIP, - .name = "Image v-Flip", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = 0, - .max = 1, - .step = 1, - .def = 0, - }, - { - .ops = &ctrl_ops, - .id = V4L2_CID_HFLIP, - .name = "Image h-Flip", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = 0, - .max = 1, - .step = 1, - .def = 0, - }, - { - .ops = &ctrl_ops, - .id = V4L2_CID_EXPOSURE_ABSOLUTE, - .name = "exposure", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = 0, - .max = 0xffff, - .step = 1, - .def = 0, - .flags = 0, - }, - { - .ops = &ctrl_ops, - .id = V4L2_CID_EXPOSURE_ZONE_NUM, - .name = "one-time exposure zone number", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = 0, - .max = 0xffff, - .step = 1, - .def = 0, - .flags = 0, - }, - { - .ops = &ctrl_ops, - .id = V4L2_CID_EXPOSURE_METERING, - .name = "metering", - .type = V4L2_CTRL_TYPE_MENU, - .min = 0, - .max = 3, - .step = 0, - .def = 1, - .flags = 0, - }, - { - .ops = &ctrl_ops, - .id = V4L2_CID_EXPOSURE, - .name = "exposure biasx", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = -2, - .max = 2, - .step = 1, - .def = 0, - .flags = 0, - }, -#if 0 /* Causes v4l2_ctrl_new_custom() to fail with -ERANGE, disable for now */ - { - .ops = &ctrl_ops, - .id = V4L2_CID_3A_LOCK, - .name = "3a lock", - .type = V4L2_CTRL_TYPE_BITMASK, - .min = 0, - .max = V4L2_LOCK_EXPOSURE | V4L2_LOCK_WHITE_BALANCE | V4L2_LOCK_FOCUS, - .step = 1, - .def = 0, - .flags = 0, - }, -#endif -}; - -static int mt9m114_detect(struct mt9m114_device *dev, struct i2c_client *client) -{ - struct i2c_adapter *adapter = client->adapter; - u32 model; - int ret; - - if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { - dev_err(&client->dev, "%s: i2c error", __func__); - return -ENODEV; - } - ret = mt9m114_read_reg(client, MISENSOR_16BIT, MT9M114_PID, &model); - if (ret) - return ret; - dev->real_model_id = model; - - if (model != MT9M114_MOD_ID) { - dev_err(&client->dev, "%s: failed: client->addr = %x\n", - __func__, client->addr); - return -ENODEV; - } - - return 0; -} - -static int -mt9m114_s_config(struct v4l2_subdev *sd, int irq, void *platform_data) -{ - struct mt9m114_device *dev = to_mt9m114_sensor(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - if (!platform_data) - return -ENODEV; - - dev->platform_data = - (struct camera_sensor_platform_data *)platform_data; - - ret = power_up(sd); - if (ret) { - v4l2_err(client, "mt9m114 power-up err"); - return ret; - } - - /* config & detect sensor */ - ret = mt9m114_detect(dev, client); - if (ret) { - v4l2_err(client, "mt9m114_detect err s_config.\n"); - goto fail_detect; - } - - ret = dev->platform_data->csi_cfg(sd, 1); - if (ret) - goto fail_csi_cfg; - - ret = mt9m114_set_suspend(sd); - if (ret) { - v4l2_err(client, "mt9m114 suspend err"); - return ret; - } - - ret = power_down(sd); - if (ret) { - v4l2_err(client, "mt9m114 power down err"); - return ret; - } - - return ret; - -fail_csi_cfg: - dev->platform_data->csi_cfg(sd, 0); -fail_detect: - power_down(sd); - dev_err(&client->dev, "sensor power-gating failed\n"); - return ret; -} - -/* Horizontal flip the image. */ -static int mt9m114_t_hflip(struct v4l2_subdev *sd, int value) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - struct mt9m114_device *dev = to_mt9m114_sensor(sd); - int err; - /* set for direct mode */ - err = mt9m114_write_reg(c, MISENSOR_16BIT, 0x098E, 0xC850); - if (value) { - /* enable H flip ctx A */ - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC850, 0x01, 0x01); - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC851, 0x01, 0x01); - /* ctx B */ - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC888, 0x01, 0x01); - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC889, 0x01, 0x01); - - err += misensor_rmw_reg(c, MISENSOR_16BIT, MISENSOR_READ_MODE, - MISENSOR_HFLIP_MASK, MISENSOR_FLIP_EN); - - dev->bpat = MT9M114_BPAT_GRGRBGBG; - } else { - /* disable H flip ctx A */ - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC850, 0x01, 0x00); - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC851, 0x01, 0x00); - /* ctx B */ - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC888, 0x01, 0x00); - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC889, 0x01, 0x00); - - err += misensor_rmw_reg(c, MISENSOR_16BIT, MISENSOR_READ_MODE, - MISENSOR_HFLIP_MASK, MISENSOR_FLIP_DIS); - - dev->bpat = MT9M114_BPAT_BGBGGRGR; - } - - err += mt9m114_write_reg(c, MISENSOR_8BIT, 0x8404, 0x06); - udelay(10); - - return !!err; -} - -/* Vertically flip the image */ -static int mt9m114_t_vflip(struct v4l2_subdev *sd, int value) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - int err; - /* set for direct mode */ - err = mt9m114_write_reg(c, MISENSOR_16BIT, 0x098E, 0xC850); - if (value >= 1) { - /* enable H flip - ctx A */ - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC850, 0x02, 0x01); - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC851, 0x02, 0x01); - /* ctx B */ - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC888, 0x02, 0x01); - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC889, 0x02, 0x01); - - err += misensor_rmw_reg(c, MISENSOR_16BIT, MISENSOR_READ_MODE, - MISENSOR_VFLIP_MASK, MISENSOR_FLIP_EN); - } else { - /* disable H flip - ctx A */ - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC850, 0x02, 0x00); - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC851, 0x02, 0x00); - /* ctx B */ - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC888, 0x02, 0x00); - err += misensor_rmw_reg(c, MISENSOR_8BIT, 0xC889, 0x02, 0x00); - - err += misensor_rmw_reg(c, MISENSOR_16BIT, MISENSOR_READ_MODE, - MISENSOR_VFLIP_MASK, MISENSOR_FLIP_DIS); - } - - err += mt9m114_write_reg(c, MISENSOR_8BIT, 0x8404, 0x06); - udelay(10); - - return !!err; -} - -static int mt9m114_get_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_interval *interval) -{ - struct mt9m114_device *dev = to_mt9m114_sensor(sd); - - /* - * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 - * subdev active state API. - */ - if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - interval->interval.numerator = 1; - interval->interval.denominator = mt9m114_res[dev->res].fps; - - return 0; -} - -static int mt9m114_s_stream(struct v4l2_subdev *sd, int enable) -{ - int ret; - struct i2c_client *c = v4l2_get_subdevdata(sd); - struct mt9m114_device *dev = to_mt9m114_sensor(sd); - struct atomisp_exposure exposure; - - if (enable) { - ret = mt9m114_write_reg_array(c, mt9m114_chgstat_reg, - POST_POLLING); - if (ret < 0) - return ret; - - if (dev->first_exp > MT9M114_MAX_FIRST_EXP) { - exposure.integration_time[0] = dev->first_exp; - exposure.gain[0] = dev->first_gain; - exposure.gain[1] = dev->first_diggain; - mt9m114_s_exposure(sd, &exposure); - } - dev->streamon = 1; - - } else { - dev->streamon = 0; - ret = mt9m114_set_suspend(sd); - } - - return ret; -} - -static int mt9m114_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index) - return -EINVAL; - code->code = MEDIA_BUS_FMT_SGRBG10_1X10; - - return 0; -} - -static int mt9m114_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - unsigned int index = fse->index; - - if (index >= N_RES) - return -EINVAL; - - fse->min_width = mt9m114_res[index].width; - fse->min_height = mt9m114_res[index].height; - fse->max_width = mt9m114_res[index].width; - fse->max_height = mt9m114_res[index].height; - - return 0; -} - -static int mt9m114_g_skip_frames(struct v4l2_subdev *sd, u32 *frames) -{ - int index; - struct mt9m114_device *snr = to_mt9m114_sensor(sd); - - if (!frames) - return -EINVAL; - - for (index = 0; index < N_RES; index++) { - if (mt9m114_res[index].res == snr->res) - break; - } - - if (index >= N_RES) - return -EINVAL; - - *frames = mt9m114_res[index].skip_frames; - - return 0; -} - -static const struct v4l2_subdev_video_ops mt9m114_video_ops = { - .s_stream = mt9m114_s_stream, -}; - -static const struct v4l2_subdev_sensor_ops mt9m114_sensor_ops = { - .g_skip_frames = mt9m114_g_skip_frames, -}; - -static const struct v4l2_subdev_core_ops mt9m114_core_ops = { - .s_power = mt9m114_s_power, - .ioctl = mt9m114_ioctl, -}; - -/* REVISIT: Do we need pad operations? */ -static const struct v4l2_subdev_pad_ops mt9m114_pad_ops = { - .enum_mbus_code = mt9m114_enum_mbus_code, - .enum_frame_size = mt9m114_enum_frame_size, - .get_fmt = mt9m114_get_fmt, - .set_fmt = mt9m114_set_fmt, - .set_selection = mt9m114_s_exposure_selection, - .get_frame_interval = mt9m114_get_frame_interval, -}; - -static const struct v4l2_subdev_ops mt9m114_ops = { - .core = &mt9m114_core_ops, - .video = &mt9m114_video_ops, - .pad = &mt9m114_pad_ops, - .sensor = &mt9m114_sensor_ops, -}; - -static void mt9m114_remove(struct i2c_client *client) -{ - struct mt9m114_device *dev; - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - dev = container_of(sd, struct mt9m114_device, sd); - dev->platform_data->csi_cfg(sd, 0); - v4l2_device_unregister_subdev(sd); - media_entity_cleanup(&dev->sd.entity); - v4l2_ctrl_handler_free(&dev->ctrl_handler); -} - -static int mt9m114_probe(struct i2c_client *client) -{ - struct mt9m114_device *dev; - int ret = 0; - unsigned int i; - void *pdata; - - /* Setup sensor configuration structure */ - dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - ret = devm_mutex_init(&client->dev, &dev->input_lock); - if (ret) - return ret; - - v4l2_i2c_subdev_init(&dev->sd, client, &mt9m114_ops); - pdata = gmin_camera_platform_data(&dev->sd, - ATOMISP_INPUT_FORMAT_RAW_10, - atomisp_bayer_order_grbg); - if (pdata) - ret = mt9m114_s_config(&dev->sd, client->irq, pdata); - if (!pdata || ret) { - v4l2_device_unregister_subdev(&dev->sd); - return ret; - } - - ret = atomisp_register_i2c_module(&dev->sd, pdata); - if (ret) { - v4l2_device_unregister_subdev(&dev->sd); - /* Coverity CID 298095 - return on error */ - return ret; - } - - /* TODO add format code here */ - dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - dev->pad.flags = MEDIA_PAD_FL_SOURCE; - dev->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; - dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - - ret = - v4l2_ctrl_handler_init(&dev->ctrl_handler, - ARRAY_SIZE(mt9m114_controls)); - if (ret) { - mt9m114_remove(client); - return ret; - } - - for (i = 0; i < ARRAY_SIZE(mt9m114_controls); i++) - v4l2_ctrl_new_custom(&dev->ctrl_handler, &mt9m114_controls[i], - NULL); - - if (dev->ctrl_handler.error) { - mt9m114_remove(client); - return dev->ctrl_handler.error; - } - - /* Use same lock for controls as for everything else. */ - dev->ctrl_handler.lock = &dev->input_lock; - dev->sd.ctrl_handler = &dev->ctrl_handler; - - /* REVISIT: Do we need media controller? */ - ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); - if (ret) { - mt9m114_remove(client); - return ret; - } - return 0; -} - -static const struct acpi_device_id mt9m114_acpi_match[] = { - { "INT33F0" }, - { "CRMT1040" }, - {}, -}; -MODULE_DEVICE_TABLE(acpi, mt9m114_acpi_match); - -static struct i2c_driver mt9m114_driver = { - .driver = { - .name = "mt9m114", - .acpi_match_table = mt9m114_acpi_match, - }, - .probe = mt9m114_probe, - .remove = mt9m114_remove, -}; -module_i2c_driver(mt9m114_driver); - -MODULE_AUTHOR("Shuguang Gong "); -MODULE_DESCRIPTION("Aptina mt9m114 sensor support module"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/atomisp/i2c/mt9m114.h b/drivers/staging/media/atomisp/i2c/mt9m114.h deleted file mode 100644 index 97820db90827..000000000000 --- a/drivers/staging/media/atomisp/i2c/mt9m114.h +++ /dev/null @@ -1,1768 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Support for mt9m114 Camera Sensor. - * - * Copyright (c) 2010 Intel Corporation. All Rights Reserved. - */ - -#ifndef __A1040_H__ -#define __A1040_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../include/linux/atomisp_platform.h" -#include "../include/linux/atomisp.h" - -#define V4L2_IDENT_MT9M114 8245 - -#define MT9P111_REV3 -#define FULLINISUPPORT - -/* #defines for register writes and register array processing */ -#define MISENSOR_8BIT 1 -#define MISENSOR_16BIT 2 -#define MISENSOR_32BIT 4 - -#define MISENSOR_FWBURST0 0x80 -#define MISENSOR_FWBURST1 0x81 -#define MISENSOR_FWBURST4 0x84 -#define MISENSOR_FWBURST 0x88 - -#define MISENSOR_TOK_TERM 0xf000 /* terminating token for reg list */ -#define MISENSOR_TOK_DELAY 0xfe00 /* delay token for reg list */ -#define MISENSOR_TOK_FWLOAD 0xfd00 /* token indicating load FW */ -#define MISENSOR_TOK_POLL 0xfc00 /* token indicating poll instruction */ -#define MISENSOR_TOK_RMW 0x0010 /* RMW operation */ -#define MISENSOR_TOK_MASK 0xfff0 -#define MISENSOR_AWB_STEADY BIT(0) /* awb steady */ -#define MISENSOR_AE_READY BIT(3) /* ae status ready */ - -/* mask to set sensor read_mode via misensor_rmw_reg */ -#define MISENSOR_R_MODE_MASK 0x0330 -/* mask to set sensor vert_flip and horz_mirror */ -#define MISENSOR_VFLIP_MASK 0x0002 -#define MISENSOR_HFLIP_MASK 0x0001 -#define MISENSOR_FLIP_EN 1 -#define MISENSOR_FLIP_DIS 0 - -/* bits set to set sensor read_mode via misensor_rmw_reg */ -#define MISENSOR_SKIPPING_SET 0x0011 -#define MISENSOR_SUMMING_SET 0x0033 -#define MISENSOR_NORMAL_SET 0x0000 - -/* sensor register that control sensor read-mode and mirror */ -#define MISENSOR_READ_MODE 0xC834 -/* sensor ae-track status register */ -#define MISENSOR_AE_TRACK_STATUS 0xA800 -/* sensor awb status register */ -#define MISENSOR_AWB_STATUS 0xAC00 -/* sensor coarse integration time register */ -#define MISENSOR_COARSE_INTEGRATION_TIME 0xC83C - -/* registers */ -#define REG_SW_RESET 0x301A -#define REG_SW_STREAM 0xDC00 -#define REG_SCCB_CTRL 0x3100 -#define REG_SC_CMMN_CHIP_ID 0x0000 -#define REG_V_START 0xc800 /* 16bits */ -#define REG_H_START 0xc802 /* 16bits */ -#define REG_V_END 0xc804 /* 16bits */ -#define REG_H_END 0xc806 /* 16bits */ -#define REG_PIXEL_CLK 0xc808 /* 32bits */ -#define REG_TIMING_VTS 0xc812 /* 16bits */ -#define REG_TIMING_HTS 0xc814 /* 16bits */ -#define REG_WIDTH 0xC868 /* 16bits */ -#define REG_HEIGHT 0xC86A /* 16bits */ -#define REG_EXPO_COARSE 0x3012 /* 16bits */ -#define REG_EXPO_FINE 0x3014 /* 16bits */ -#define REG_GAIN 0x305E -#define REG_ANALOGGAIN 0x305F -#define REG_ADDR_ACESSS 0x098E /* logical_address_access */ -#define REG_COMM_Register 0x0080 /* command_register */ - -#define SENSOR_DETECTED 1 -#define SENSOR_NOT_DETECTED 0 - -#define I2C_RETRY_COUNT 5 -#define MSG_LEN_OFFSET 2 - -#ifndef MIPI_CONTROL -#define MIPI_CONTROL 0x3400 /* MIPI_Control */ -#endif - -/* GPIO pin on Moorestown */ -#define GPIO_SCLK_25 44 -#define GPIO_STB_PIN 47 - -#define GPIO_STDBY_PIN 49 /* ab:new */ -#define GPIO_RESET_PIN 50 - -/* System control register for Aptina A-1040SOC*/ -#define MT9M114_PID 0x0 - -/* MT9P111_DEVICE_ID */ -#define MT9M114_MOD_ID 0x2481 - -#define MT9M114_FINE_INTG_TIME_MIN 0 -#define MT9M114_FINE_INTG_TIME_MAX_MARGIN 0 -#define MT9M114_COARSE_INTG_TIME_MIN 1 -#define MT9M114_COARSE_INTG_TIME_MAX_MARGIN 6 - -/* ulBPat; */ - -#define MT9M114_BPAT_RGRGGBGB BIT(0) -#define MT9M114_BPAT_GRGRBGBG BIT(1) -#define MT9M114_BPAT_GBGBRGRG BIT(2) -#define MT9M114_BPAT_BGBGGRGR BIT(3) - -#define MT9M114_FOCAL_LENGTH_NUM 208 /*2.08mm*/ -#define MT9M114_WAIT_STAT_TIMEOUT 100 -#define MT9M114_FLICKER_MODE_50HZ 1 -#define MT9M114_FLICKER_MODE_60HZ 2 -/* - * focal length bits definition: - * bits 31-16: numerator, bits 15-0: denominator - */ -#define MT9M114_FOCAL_LENGTH_DEFAULT 0xD00064 - -/* - * current f-number bits definition: - * bits 31-16: numerator, bits 15-0: denominator - */ -#define MT9M114_F_NUMBER_DEFAULT 0x18000a - -/* - * f-number range bits definition: - * bits 31-24: max f-number numerator - * bits 23-16: max f-number denominator - * bits 15-8: min f-number numerator - * bits 7-0: min f-number denominator - */ -#define MT9M114_F_NUMBER_RANGE 0x180a180a - -/* Supported resolutions */ -enum { - MT9M114_RES_736P, - MT9M114_RES_864P, - MT9M114_RES_960P, -}; - -#define MT9M114_RES_960P_SIZE_H 1296 -#define MT9M114_RES_960P_SIZE_V 976 -#define MT9M114_RES_720P_SIZE_H 1280 -#define MT9M114_RES_720P_SIZE_V 720 -#define MT9M114_RES_576P_SIZE_H 1024 -#define MT9M114_RES_576P_SIZE_V 576 -#define MT9M114_RES_480P_SIZE_H 768 -#define MT9M114_RES_480P_SIZE_V 480 -#define MT9M114_RES_VGA_SIZE_H 640 -#define MT9M114_RES_VGA_SIZE_V 480 -#define MT9M114_RES_QVGA_SIZE_H 320 -#define MT9M114_RES_QVGA_SIZE_V 240 -#define MT9M114_RES_QCIF_SIZE_H 176 -#define MT9M114_RES_QCIF_SIZE_V 144 - -#define MT9M114_RES_720_480p_768_SIZE_H 736 -#define MT9M114_RES_720_480p_768_SIZE_V 496 -#define MT9M114_RES_736P_SIZE_H 1296 -#define MT9M114_RES_736P_SIZE_V 736 -#define MT9M114_RES_864P_SIZE_H 1296 -#define MT9M114_RES_864P_SIZE_V 864 -#define MT9M114_RES_976P_SIZE_H 1296 -#define MT9M114_RES_976P_SIZE_V 976 - -#define MT9M114_BIN_FACTOR_MAX 3 - -#define MT9M114_DEFAULT_FIRST_EXP 0x10 -#define MT9M114_MAX_FIRST_EXP 0x302 - -/* completion status polling requirements, usage based on Aptina .INI Rev2 */ -enum poll_reg { - NO_POLLING, - PRE_POLLING, - POST_POLLING, -}; - -/* - * struct misensor_reg - MI sensor register format - * @length: length of the register - * @reg: 16-bit offset to register - * @val: 8/16/32-bit register value - * Define a structure for sensor register initialization values - */ -struct misensor_reg { - u32 length; - u32 reg; - u32 val; /* value or for read/mod/write, AND mask */ - u32 val2; /* optional; for rmw, OR mask */ -}; - -/* - * struct misensor_fwreg - Firmware burst command - * @type: FW burst or 8/16 bit register - * @addr: 16-bit offset to register or other values depending on type - * @valx: data value for burst (or other commands) - * - * Define a structure for sensor register initialization values - */ -struct misensor_fwreg { - u32 type; /* type of value, register or FW burst string */ - u32 addr; /* target address */ - u32 val0; - u32 val1; - u32 val2; - u32 val3; - u32 val4; - u32 val5; - u32 val6; - u32 val7; -}; - -struct regval_list { - u16 reg_num; - u8 value; -}; - -struct mt9m114_device { - struct v4l2_subdev sd; - struct media_pad pad; - struct v4l2_mbus_framefmt format; - - struct camera_sensor_platform_data *platform_data; - struct mutex input_lock; /* serialize sensor's ioctl */ - struct v4l2_ctrl_handler ctrl_handler; - int real_model_id; - int nctx; - int power; - - unsigned int bus_width; - unsigned int mode; - unsigned int field_inv; - unsigned int field_sel; - unsigned int ycseq; - unsigned int conv422; - unsigned int bpat; - unsigned int hpol; - unsigned int vpol; - unsigned int edge; - unsigned int bls; - unsigned int gamma; - unsigned int cconv; - unsigned int res; - unsigned int dwn_sz; - unsigned int blc; - unsigned int agc; - unsigned int awb; - unsigned int aec; - /* extension SENSOR version 2 */ - unsigned int cie_profile; - - /* extension SENSOR version 3 */ - unsigned int flicker_freq; - - /* extension SENSOR version 4 */ - unsigned int smia_mode; - unsigned int mipi_mode; - - /* Add name here to load shared library */ - unsigned int type; - - /*Number of MIPI lanes*/ - unsigned int mipi_lanes; - /*WA for low light AE*/ - unsigned int first_exp; - unsigned int first_gain; - unsigned int first_diggain; - char name[32]; - - u8 lightfreq; - u8 streamon; -}; - -struct mt9m114_format_struct { - u8 *desc; - u32 pixelformat; - struct regval_list *regs; -}; - -struct mt9m114_res_struct { - u8 *desc; - int res; - int width; - int height; - int fps; - int skip_frames; - bool used; - struct regval_list *regs; - u16 pixels_per_line; - u16 lines_per_frame; -}; - -/* 2 bytes used for address: 256 bytes total */ -#define MT9M114_MAX_WRITE_BUF_SIZE 254 -struct mt9m114_write_buffer { - u16 addr; - u8 data[MT9M114_MAX_WRITE_BUF_SIZE]; -}; - -struct mt9m114_write_ctrl { - int index; - struct mt9m114_write_buffer buffer; -}; - -/* - * Modes supported by the mt9m114 driver. - * Please, keep them in ascending order. - */ -static struct mt9m114_res_struct mt9m114_res[] = { - { - .desc = "720P", - .res = MT9M114_RES_736P, - .width = 1296, - .height = 736, - .fps = 30, - .used = false, - .regs = NULL, - .skip_frames = 1, - - .pixels_per_line = 0x0640, - .lines_per_frame = 0x0307, - }, - { - .desc = "848P", - .res = MT9M114_RES_864P, - .width = 1296, - .height = 864, - .fps = 30, - .used = false, - .regs = NULL, - .skip_frames = 1, - - .pixels_per_line = 0x0640, - .lines_per_frame = 0x03E8, - }, - { - .desc = "960P", - .res = MT9M114_RES_960P, - .width = 1296, - .height = 976, - .fps = 30, - .used = false, - .regs = NULL, - .skip_frames = 1, - - .pixels_per_line = 0x0644, /* consistent with regs arrays */ - .lines_per_frame = 0x03E5, /* consistent with regs arrays */ - }, -}; - -#define N_RES (ARRAY_SIZE(mt9m114_res)) - -#if 0 /* Currently unused */ -static struct misensor_reg const mt9m114_exitstandby[] = { - {MISENSOR_16BIT, 0x098E, 0xDC00}, - /* exit-standby */ - {MISENSOR_8BIT, 0xDC00, 0x54}, - {MISENSOR_16BIT, 0x0080, 0x8002}, - {MISENSOR_TOK_TERM, 0, 0} -}; -#endif - -static struct misensor_reg const mt9m114_exp_win[5][5] = { - { - {MISENSOR_8BIT, 0xA407, 0x64}, - {MISENSOR_8BIT, 0xA408, 0x64}, - {MISENSOR_8BIT, 0xA409, 0x64}, - {MISENSOR_8BIT, 0xA40A, 0x64}, - {MISENSOR_8BIT, 0xA40B, 0x64}, - }, - { - {MISENSOR_8BIT, 0xA40C, 0x64}, - {MISENSOR_8BIT, 0xA40D, 0x64}, - {MISENSOR_8BIT, 0xA40E, 0x64}, - {MISENSOR_8BIT, 0xA40F, 0x64}, - {MISENSOR_8BIT, 0xA410, 0x64}, - }, - { - {MISENSOR_8BIT, 0xA411, 0x64}, - {MISENSOR_8BIT, 0xA412, 0x64}, - {MISENSOR_8BIT, 0xA413, 0x64}, - {MISENSOR_8BIT, 0xA414, 0x64}, - {MISENSOR_8BIT, 0xA415, 0x64}, - }, - { - {MISENSOR_8BIT, 0xA416, 0x64}, - {MISENSOR_8BIT, 0xA417, 0x64}, - {MISENSOR_8BIT, 0xA418, 0x64}, - {MISENSOR_8BIT, 0xA419, 0x64}, - {MISENSOR_8BIT, 0xA41A, 0x64}, - }, - { - {MISENSOR_8BIT, 0xA41B, 0x64}, - {MISENSOR_8BIT, 0xA41C, 0x64}, - {MISENSOR_8BIT, 0xA41D, 0x64}, - {MISENSOR_8BIT, 0xA41E, 0x64}, - {MISENSOR_8BIT, 0xA41F, 0x64}, - }, -}; - -static struct misensor_reg const mt9m114_exp_average[] = { - {MISENSOR_8BIT, 0xA407, 0x00}, - {MISENSOR_8BIT, 0xA408, 0x00}, - {MISENSOR_8BIT, 0xA409, 0x00}, - {MISENSOR_8BIT, 0xA40A, 0x00}, - {MISENSOR_8BIT, 0xA40B, 0x00}, - {MISENSOR_8BIT, 0xA40C, 0x00}, - {MISENSOR_8BIT, 0xA40D, 0x00}, - {MISENSOR_8BIT, 0xA40E, 0x00}, - {MISENSOR_8BIT, 0xA40F, 0x00}, - {MISENSOR_8BIT, 0xA410, 0x00}, - {MISENSOR_8BIT, 0xA411, 0x00}, - {MISENSOR_8BIT, 0xA412, 0x00}, - {MISENSOR_8BIT, 0xA413, 0x00}, - {MISENSOR_8BIT, 0xA414, 0x00}, - {MISENSOR_8BIT, 0xA415, 0x00}, - {MISENSOR_8BIT, 0xA416, 0x00}, - {MISENSOR_8BIT, 0xA417, 0x00}, - {MISENSOR_8BIT, 0xA418, 0x00}, - {MISENSOR_8BIT, 0xA419, 0x00}, - {MISENSOR_8BIT, 0xA41A, 0x00}, - {MISENSOR_8BIT, 0xA41B, 0x00}, - {MISENSOR_8BIT, 0xA41C, 0x00}, - {MISENSOR_8BIT, 0xA41D, 0x00}, - {MISENSOR_8BIT, 0xA41E, 0x00}, - {MISENSOR_8BIT, 0xA41F, 0x00}, - {MISENSOR_TOK_TERM, 0, 0} -}; - -static struct misensor_reg const mt9m114_exp_center[] = { - {MISENSOR_8BIT, 0xA407, 0x19}, - {MISENSOR_8BIT, 0xA408, 0x19}, - {MISENSOR_8BIT, 0xA409, 0x19}, - {MISENSOR_8BIT, 0xA40A, 0x19}, - {MISENSOR_8BIT, 0xA40B, 0x19}, - {MISENSOR_8BIT, 0xA40C, 0x19}, - {MISENSOR_8BIT, 0xA40D, 0x4B}, - {MISENSOR_8BIT, 0xA40E, 0x4B}, - {MISENSOR_8BIT, 0xA40F, 0x4B}, - {MISENSOR_8BIT, 0xA410, 0x19}, - {MISENSOR_8BIT, 0xA411, 0x19}, - {MISENSOR_8BIT, 0xA412, 0x4B}, - {MISENSOR_8BIT, 0xA413, 0x64}, - {MISENSOR_8BIT, 0xA414, 0x4B}, - {MISENSOR_8BIT, 0xA415, 0x19}, - {MISENSOR_8BIT, 0xA416, 0x19}, - {MISENSOR_8BIT, 0xA417, 0x4B}, - {MISENSOR_8BIT, 0xA418, 0x4B}, - {MISENSOR_8BIT, 0xA419, 0x4B}, - {MISENSOR_8BIT, 0xA41A, 0x19}, - {MISENSOR_8BIT, 0xA41B, 0x19}, - {MISENSOR_8BIT, 0xA41C, 0x19}, - {MISENSOR_8BIT, 0xA41D, 0x19}, - {MISENSOR_8BIT, 0xA41E, 0x19}, - {MISENSOR_8BIT, 0xA41F, 0x19}, - {MISENSOR_TOK_TERM, 0, 0} -}; - -#if 0 /* Currently unused */ -static struct misensor_reg const mt9m114_suspend[] = { - {MISENSOR_16BIT, 0x098E, 0xDC00}, - {MISENSOR_8BIT, 0xDC00, 0x40}, - {MISENSOR_16BIT, 0x0080, 0x8002}, - {MISENSOR_TOK_TERM, 0, 0} -}; - -static struct misensor_reg const mt9m114_streaming[] = { - {MISENSOR_16BIT, 0x098E, 0xDC00}, - {MISENSOR_8BIT, 0xDC00, 0x34}, - {MISENSOR_16BIT, 0x0080, 0x8002}, - {MISENSOR_TOK_TERM, 0, 0} -}; -#endif - -static struct misensor_reg const mt9m114_standby_reg[] = { - {MISENSOR_16BIT, 0x098E, 0xDC00}, - {MISENSOR_8BIT, 0xDC00, 0x50}, - {MISENSOR_16BIT, 0x0080, 0x8002}, - {MISENSOR_TOK_TERM, 0, 0} -}; - -#if 0 /* Currently unused */ -static struct misensor_reg const mt9m114_wakeup_reg[] = { - {MISENSOR_16BIT, 0x098E, 0xDC00}, - {MISENSOR_8BIT, 0xDC00, 0x54}, - {MISENSOR_16BIT, 0x0080, 0x8002}, - {MISENSOR_TOK_TERM, 0, 0} -}; -#endif - -static struct misensor_reg const mt9m114_chgstat_reg[] = { - {MISENSOR_16BIT, 0x098E, 0xDC00}, - {MISENSOR_8BIT, 0xDC00, 0x28}, - {MISENSOR_16BIT, 0x0080, 0x8002}, - {MISENSOR_TOK_TERM, 0, 0} -}; - -/* [1296x976_30fps] - Intel */ -#if 0 -static struct misensor_reg const mt9m114_960P_init[] = { - {MISENSOR_16BIT, 0x098E, 0x1000}, - {MISENSOR_8BIT, 0xC97E, 0x01}, /* cam_sysctl_pll_enable = 1 */ - {MISENSOR_16BIT, 0xC980, 0x0128}, /* cam_sysctl_pll_divider_m_n = 276 */ - {MISENSOR_16BIT, 0xC982, 0x0700}, /* cam_sysctl_pll_divider_p = 1792 */ - {MISENSOR_16BIT, 0xC800, 0x0000}, /* cam_sensor_cfg_y_addr_start = 0 */ - {MISENSOR_16BIT, 0xC802, 0x0000}, /* cam_sensor_cfg_x_addr_start = 0 */ - {MISENSOR_16BIT, 0xC804, 0x03CF}, /* cam_sensor_cfg_y_addr_end = 971 */ - {MISENSOR_16BIT, 0xC806, 0x050F}, /* cam_sensor_cfg_x_addr_end = 1291 */ - {MISENSOR_16BIT, 0xC808, 0x02DC}, /* cam_sensor_cfg_pixclk = 48000000 */ - {MISENSOR_16BIT, 0xC80A, 0x6C00}, - {MISENSOR_16BIT, 0xC80C, 0x0001}, /* cam_sensor_cfg_row_speed = 1 */ - /* cam_sensor_cfg_fine_integ_time_min = 219 */ - {MISENSOR_16BIT, 0xC80E, 0x00DB}, - /* cam_sensor_cfg_fine_integ_time_max = 1459 */ - {MISENSOR_16BIT, 0xC810, 0x05B3}, - /* cam_sensor_cfg_frame_length_lines = 1006 */ - {MISENSOR_16BIT, 0xC812, 0x03F6}, - /* cam_sensor_cfg_line_length_pck = 1590 */ - {MISENSOR_16BIT, 0xC814, 0x063E}, - /* cam_sensor_cfg_fine_correction = 96 */ - {MISENSOR_16BIT, 0xC816, 0x0060}, - /* cam_sensor_cfg_cpipe_last_row = 963 */ - {MISENSOR_16BIT, 0xC818, 0x03C3}, - {MISENSOR_16BIT, 0xC826, 0x0020}, /* cam_sensor_cfg_reg_0_data = 32 */ - {MISENSOR_16BIT, 0xC834, 0x0000}, /* cam_sensor_control_read_mode = 0 */ - {MISENSOR_16BIT, 0xC854, 0x0000}, /* cam_crop_window_xoffset = 0 */ - {MISENSOR_16BIT, 0xC856, 0x0000}, /* cam_crop_window_yoffset = 0 */ - {MISENSOR_16BIT, 0xC858, 0x0508}, /* cam_crop_window_width = 1280 */ - {MISENSOR_16BIT, 0xC85A, 0x03C8}, /* cam_crop_window_height = 960 */ - {MISENSOR_8BIT, 0xC85C, 0x03}, /* cam_crop_cropmode = 3 */ - {MISENSOR_16BIT, 0xC868, 0x0508}, /* cam_output_width = 1280 */ - {MISENSOR_16BIT, 0xC86A, 0x03C8}, /* cam_output_height = 960 */ - {MISENSOR_TOK_TERM, 0, 0}, -}; -#endif - -/* [1296x976_30fps_768Mbps] */ -static struct misensor_reg const mt9m114_976P_init[] = { - {MISENSOR_16BIT, 0x98E, 0x1000}, - {MISENSOR_8BIT, 0xC97E, 0x01}, /* cam_sysctl_pll_enable = 1 */ - {MISENSOR_16BIT, 0xC980, 0x0128}, /* cam_sysctl_pll_divider_m_n = 276 */ - {MISENSOR_16BIT, 0xC982, 0x0700}, /* cam_sysctl_pll_divider_p = 1792 */ - {MISENSOR_16BIT, 0xC800, 0x0000}, /* cam_sensor_cfg_y_addr_start = 0 */ - {MISENSOR_16BIT, 0xC802, 0x0000}, /* cam_sensor_cfg_x_addr_start = 0 */ - {MISENSOR_16BIT, 0xC804, 0x03CF}, /* cam_sensor_cfg_y_addr_end = 975 */ - {MISENSOR_16BIT, 0xC806, 0x050F}, /* cam_sensor_cfg_x_addr_end = 1295 */ - {MISENSOR_32BIT, 0xC808, 0x2DC6C00},/* cam_sensor_cfg_pixclk = 480000*/ - {MISENSOR_16BIT, 0xC80C, 0x0001}, /* cam_sensor_cfg_row_speed = 1 */ - /* cam_sensor_cfg_fine_integ_time_min = 219 */ - {MISENSOR_16BIT, 0xC80E, 0x00DB}, - /* 0x062E //cam_sensor_cfg_fine_integ_time_max = 1459 */ - {MISENSOR_16BIT, 0xC810, 0x05B3}, - /* 0x074C //cam_sensor_cfg_frame_length_lines = 1006 */ - {MISENSOR_16BIT, 0xC812, 0x03E5}, - /* 0x06B1 /cam_sensor_cfg_line_length_pck = 1590 */ - {MISENSOR_16BIT, 0xC814, 0x0644}, - /* cam_sensor_cfg_fine_correction = 96 */ - {MISENSOR_16BIT, 0xC816, 0x0060}, - /* cam_sensor_cfg_cpipe_last_row = 963 */ - {MISENSOR_16BIT, 0xC818, 0x03C3}, - {MISENSOR_16BIT, 0xC826, 0x0020}, /* cam_sensor_cfg_reg_0_data = 32 */ - {MISENSOR_16BIT, 0xC834, 0x0000}, /* cam_sensor_control_read_mode = 0 */ - {MISENSOR_16BIT, 0xC854, 0x0000}, /* cam_crop_window_xoffset = 0 */ - {MISENSOR_16BIT, 0xC856, 0x0000}, /* cam_crop_window_yoffset = 0 */ - {MISENSOR_16BIT, 0xC858, 0x0508}, /* cam_crop_window_width = 1288 */ - {MISENSOR_16BIT, 0xC85A, 0x03C8}, /* cam_crop_window_height = 968 */ - {MISENSOR_8BIT, 0xC85C, 0x03}, /* cam_crop_cropmode = 3 */ - {MISENSOR_16BIT, 0xC868, 0x0508}, /* cam_output_width = 1288 */ - {MISENSOR_16BIT, 0xC86A, 0x03C8}, /* cam_output_height = 968 */ - {MISENSOR_8BIT, 0xC878, 0x00}, /* 0x0E //cam_aet_aemode = 0 */ - {MISENSOR_TOK_TERM, 0, 0} -}; - -/* [1296x864_30fps] */ -static struct misensor_reg const mt9m114_864P_init[] = { - {MISENSOR_16BIT, 0x98E, 0x1000}, - {MISENSOR_8BIT, 0xC97E, 0x01}, /* cam_sysctl_pll_enable = 1 */ - {MISENSOR_16BIT, 0xC980, 0x0128}, /* cam_sysctl_pll_divider_m_n = 276 */ - {MISENSOR_16BIT, 0xC982, 0x0700}, /* cam_sysctl_pll_divider_p = 1792 */ - {MISENSOR_16BIT, 0xC800, 0x0038}, /* cam_sensor_cfg_y_addr_start = 56 */ - {MISENSOR_16BIT, 0xC802, 0x0000}, /* cam_sensor_cfg_x_addr_start = 0 */ - {MISENSOR_16BIT, 0xC804, 0x0397}, /* cam_sensor_cfg_y_addr_end = 919 */ - {MISENSOR_16BIT, 0xC806, 0x050F}, /* cam_sensor_cfg_x_addr_end = 1295 */ - /* cam_sensor_cfg_pixclk = 48000000 */ - {MISENSOR_32BIT, 0xC808, 0x2DC6C00}, - {MISENSOR_16BIT, 0xC80C, 0x0001}, /* cam_sensor_cfg_row_speed = 1 */ - /* cam_sensor_cfg_fine_integ_time_min = 219 */ - {MISENSOR_16BIT, 0xC80E, 0x00DB}, - /* cam_sensor_cfg_fine_integ_time_max = 1469 */ - {MISENSOR_16BIT, 0xC810, 0x05BD}, - /* cam_sensor_cfg_frame_length_lines = 1000 */ - {MISENSOR_16BIT, 0xC812, 0x03E8}, - /* cam_sensor_cfg_line_length_pck = 1600 */ - {MISENSOR_16BIT, 0xC814, 0x0640}, - /* cam_sensor_cfg_fine_correction = 96 */ - {MISENSOR_16BIT, 0xC816, 0x0060}, - /* cam_sensor_cfg_cpipe_last_row = 859 */ - {MISENSOR_16BIT, 0xC818, 0x035B}, - {MISENSOR_16BIT, 0xC826, 0x0020}, /* cam_sensor_cfg_reg_0_data = 32 */ - {MISENSOR_16BIT, 0xC834, 0x0000}, /* cam_sensor_control_read_mode = 0 */ - {MISENSOR_16BIT, 0xC854, 0x0000}, /* cam_crop_window_xoffset = 0 */ - {MISENSOR_16BIT, 0xC856, 0x0000}, /* cam_crop_window_yoffset = 0 */ - {MISENSOR_16BIT, 0xC858, 0x0508}, /* cam_crop_window_width = 1288 */ - {MISENSOR_16BIT, 0xC85A, 0x0358}, /* cam_crop_window_height = 856 */ - {MISENSOR_8BIT, 0xC85C, 0x03}, /* cam_crop_cropmode = 3 */ - {MISENSOR_16BIT, 0xC868, 0x0508}, /* cam_output_width = 1288 */ - {MISENSOR_16BIT, 0xC86A, 0x0358}, /* cam_output_height = 856 */ - {MISENSOR_8BIT, 0xC878, 0x00}, /* 0x0E //cam_aet_aemode = 0 */ - {MISENSOR_TOK_TERM, 0, 0} -}; - -/* [1296x736_30fps] */ -static struct misensor_reg const mt9m114_736P_init[] = { - {MISENSOR_16BIT, 0x98E, 0x1000}, - {MISENSOR_8BIT, 0xC97E, 0x01}, /* cam_sysctl_pll_enable = 1 */ - {MISENSOR_16BIT, 0xC980, 0x011F}, /* cam_sysctl_pll_divider_m_n = 287 */ - {MISENSOR_16BIT, 0xC982, 0x0700}, /* cam_sysctl_pll_divider_p = 1792 */ - {MISENSOR_16BIT, 0xC800, 0x0078}, /* cam_sensor_cfg_y_addr_start = 120*/ - {MISENSOR_16BIT, 0xC802, 0x0000}, /* cam_sensor_cfg_x_addr_start = 0 */ - {MISENSOR_16BIT, 0xC804, 0x0357}, /* cam_sensor_cfg_y_addr_end = 855 */ - {MISENSOR_16BIT, 0xC806, 0x050F}, /* cam_sensor_cfg_x_addr_end = 1295 */ - {MISENSOR_32BIT, 0xC808, 0x237A07F}, /* cam_sensor_cfg_pixclk=37199999*/ - {MISENSOR_16BIT, 0xC80C, 0x0001}, /* cam_sensor_cfg_row_speed = 1 */ - /* cam_sensor_cfg_fine_integ_time_min = 219 */ - {MISENSOR_16BIT, 0xC80E, 0x00DB}, - /* 0x062E //cam_sensor_cfg_fine_integ_time_max = 1469 */ - {MISENSOR_16BIT, 0xC810, 0x05BD}, - /* 0x074C //cam_sensor_cfg_frame_length_lines = 775 */ - {MISENSOR_16BIT, 0xC812, 0x0307}, - /* 0x06B1 /cam_sensor_cfg_line_length_pck = 1600 */ - {MISENSOR_16BIT, 0xC814, 0x0640}, - /* cam_sensor_cfg_fine_correction = 96 */ - {MISENSOR_16BIT, 0xC816, 0x0060}, - /* cam_sensor_cfg_cpipe_last_row = 731 */ - {MISENSOR_16BIT, 0xC818, 0x02DB}, - {MISENSOR_16BIT, 0xC826, 0x0020}, /* cam_sensor_cfg_reg_0_data = 32 */ - {MISENSOR_16BIT, 0xC834, 0x0000}, /* cam_sensor_control_read_mode = 0 */ - {MISENSOR_16BIT, 0xC854, 0x0000}, /* cam_crop_window_xoffset = 0 */ - {MISENSOR_16BIT, 0xC856, 0x0000}, /* cam_crop_window_yoffset = 0 */ - {MISENSOR_16BIT, 0xC858, 0x0508}, /* cam_crop_window_width = 1288 */ - {MISENSOR_16BIT, 0xC85A, 0x02D8}, /* cam_crop_window_height = 728 */ - {MISENSOR_8BIT, 0xC85C, 0x03}, /* cam_crop_cropmode = 3 */ - {MISENSOR_16BIT, 0xC868, 0x0508}, /* cam_output_width = 1288 */ - {MISENSOR_16BIT, 0xC86A, 0x02D8}, /* cam_output_height = 728 */ - {MISENSOR_8BIT, 0xC878, 0x00}, /* 0x0E //cam_aet_aemode = 0 */ - {MISENSOR_TOK_TERM, 0, 0} -}; - -/* [736x496_30fps_768Mbps] */ -#if 0 /* Currently unused */ -static struct misensor_reg const mt9m114_720_480P_init[] = { - {MISENSOR_16BIT, 0x98E, 0x1000}, - {MISENSOR_8BIT, 0xC97E, 0x01}, /* cam_sysctl_pll_enable = 1 */ - {MISENSOR_16BIT, 0xC980, 0x0128}, /* cam_sysctl_pll_divider_m_n = 276 */ - {MISENSOR_16BIT, 0xC982, 0x0700}, /* cam_sysctl_pll_divider_p = 1792 */ - {MISENSOR_16BIT, 0xC800, 0x00F0}, /* cam_sensor_cfg_y_addr_start = 240*/ - {MISENSOR_16BIT, 0xC802, 0x0118}, /* cam_sensor_cfg_x_addr_start = 280*/ - {MISENSOR_16BIT, 0xC804, 0x02DF}, /* cam_sensor_cfg_y_addr_end = 735 */ - {MISENSOR_16BIT, 0xC806, 0x03F7}, /* cam_sensor_cfg_x_addr_end = 1015 */ - /* cam_sensor_cfg_pixclk = 48000000 */ - {MISENSOR_32BIT, 0xC808, 0x2DC6C00}, - {MISENSOR_16BIT, 0xC80C, 0x0001}, /* cam_sensor_cfg_row_speed = 1 */ - /* cam_sensor_cfg_fine_integ_time_min = 219 */ - {MISENSOR_16BIT, 0xC80E, 0x00DB}, - /* 0x062E //cam_sensor_cfg_fine_integ_time_max = 1459 */ - {MISENSOR_16BIT, 0xC810, 0x05B3}, - /* 0x074C //cam_sensor_cfg_frame_length_lines = 997 */ - {MISENSOR_16BIT, 0xC812, 0x03E5}, - /* 0x06B1 /cam_sensor_cfg_line_length_pck = 1604 */ - {MISENSOR_16BIT, 0xC814, 0x0644}, - /* cam_sensor_cfg_fine_correction = 96 */ - {MISENSOR_16BIT, 0xC816, 0x0060}, - {MISENSOR_16BIT, 0xC818, 0x03C3}, /* cam_sensor_cfg_cpipe_last_row=963*/ - {MISENSOR_16BIT, 0xC826, 0x0020}, /* cam_sensor_cfg_reg_0_data = 32 */ - {MISENSOR_16BIT, 0xC834, 0x0000}, /* cam_sensor_control_read_mode = 0*/ - {MISENSOR_16BIT, 0xC854, 0x0000}, /* cam_crop_window_xoffset = 0 */ - {MISENSOR_16BIT, 0xC856, 0x0000}, /* cam_crop_window_yoffset = 0 */ - {MISENSOR_16BIT, 0xC858, 0x02D8}, /* cam_crop_window_width = 728 */ - {MISENSOR_16BIT, 0xC85A, 0x01E8}, /* cam_crop_window_height = 488 */ - {MISENSOR_8BIT, 0xC85C, 0x03}, /* cam_crop_cropmode = 3 */ - {MISENSOR_16BIT, 0xC868, 0x02D8}, /* cam_output_width = 728 */ - {MISENSOR_16BIT, 0xC86A, 0x01E8}, /* cam_output_height = 488 */ - {MISENSOR_8BIT, 0xC878, 0x00}, /* 0x0E //cam_aet_aemode = 0 */ - {MISENSOR_TOK_TERM, 0, 0} -}; -#endif - -static struct misensor_reg const mt9m114_common[] = { - /* reset */ - {MISENSOR_16BIT, 0x301A, 0x0234}, - /* LOAD = Step2-PLL_Timing //PLL and Timing */ - {MISENSOR_16BIT, 0x098E, 0x1000}, /* LOGICAL_ADDRESS_ACCESS */ - {MISENSOR_8BIT, 0xC97E, 0x01}, /* cam_sysctl_pll_enable = 1 */ - {MISENSOR_16BIT, 0xC980, 0x0128}, /* cam_sysctl_pll_divider_m_n = 276 */ - {MISENSOR_16BIT, 0xC982, 0x0700}, /* cam_sysctl_pll_divider_p = 1792 */ - {MISENSOR_16BIT, 0xC800, 0x0000}, /* cam_sensor_cfg_y_addr_start = 216*/ - {MISENSOR_16BIT, 0xC802, 0x0000}, /* cam_sensor_cfg_x_addr_start = 168*/ - {MISENSOR_16BIT, 0xC804, 0x03CD}, /* cam_sensor_cfg_y_addr_end = 761 */ - {MISENSOR_16BIT, 0xC806, 0x050D}, /* cam_sensor_cfg_x_addr_end = 1127 */ - {MISENSOR_16BIT, 0xC808, 0x02DC}, /* cam_sensor_cfg_pixclk = 24000000 */ - {MISENSOR_16BIT, 0xC80A, 0x6C00}, - {MISENSOR_16BIT, 0xC80C, 0x0001}, /* cam_sensor_cfg_row_speed = 1 */ - /* cam_sensor_cfg_fine_integ_time_min = 219 */ - {MISENSOR_16BIT, 0xC80E, 0x01C3}, - /* cam_sensor_cfg_fine_integ_time_max = 1149 */ - {MISENSOR_16BIT, 0xC810, 0x03F7}, - /* cam_sensor_cfg_frame_length_lines = 625 */ - {MISENSOR_16BIT, 0xC812, 0x0500}, - /* cam_sensor_cfg_line_length_pck = 1280 */ - {MISENSOR_16BIT, 0xC814, 0x04E2}, - /* cam_sensor_cfg_fine_correction = 96 */ - {MISENSOR_16BIT, 0xC816, 0x00E0}, - /* cam_sensor_cfg_cpipe_last_row = 541 */ - {MISENSOR_16BIT, 0xC818, 0x01E3}, - {MISENSOR_16BIT, 0xC826, 0x0020}, /* cam_sensor_cfg_reg_0_data = 32 */ - {MISENSOR_16BIT, 0xC834, 0x0330}, /* cam_sensor_control_read_mode = 0 */ - {MISENSOR_16BIT, 0xC854, 0x0000}, /* cam_crop_window_xoffset = 0 */ - {MISENSOR_16BIT, 0xC856, 0x0000}, /* cam_crop_window_yoffset = 0 */ - {MISENSOR_16BIT, 0xC858, 0x0280}, /* cam_crop_window_width = 952 */ - {MISENSOR_16BIT, 0xC85A, 0x01E0}, /* cam_crop_window_height = 538 */ - {MISENSOR_8BIT, 0xC85C, 0x03}, /* cam_crop_cropmode = 3 */ - {MISENSOR_16BIT, 0xC868, 0x0280}, /* cam_output_width = 952 */ - {MISENSOR_16BIT, 0xC86A, 0x01E0}, /* cam_output_height = 538 */ - /* - * LOAD = Step3-Recommended - * Patch, Errata and Sensor optimization Setting - */ - {MISENSOR_16BIT, 0x316A, 0x8270}, /* DAC_TXLO_ROW */ - {MISENSOR_16BIT, 0x316C, 0x8270}, /* DAC_TXLO */ - {MISENSOR_16BIT, 0x3ED0, 0x2305}, /* DAC_LD_4_5 */ - {MISENSOR_16BIT, 0x3ED2, 0x77CF}, /* DAC_LD_6_7 */ - {MISENSOR_16BIT, 0x316E, 0x8202}, /* DAC_ECL */ - {MISENSOR_16BIT, 0x3180, 0x87FF}, /* DELTA_DK_CONTROL */ - {MISENSOR_16BIT, 0x30D4, 0x6080}, /* COLUMN_CORRECTION */ - {MISENSOR_16BIT, 0xA802, 0x0008}, /* AE_TRACK_MODE */ - {MISENSOR_16BIT, 0x3E14, 0xFF39}, /* SAMP_COL_PUP2 */ - {MISENSOR_16BIT, 0x31E0, 0x0003}, /* PIX_DEF_ID */ - /* LOAD = Step8-Features //Ports, special features, etc. */ - {MISENSOR_16BIT, 0x098E, 0x0000}, /* LOGICAL_ADDRESS_ACCESS */ - {MISENSOR_16BIT, 0x001E, 0x0777}, /* PAD_SLEW */ - {MISENSOR_16BIT, 0x098E, 0x0000}, /* LOGICAL_ADDRESS_ACCESS */ - {MISENSOR_16BIT, 0xC984, 0x8001}, /* CAM_PORT_OUTPUT_CONTROL */ - {MISENSOR_16BIT, 0xC988, 0x0F00}, /* CAM_PORT_MIPI_TIMING_T_HS_ZERO */ - /* CAM_PORT_MIPI_TIMING_T_HS_EXIT_HS_TRAIL */ - {MISENSOR_16BIT, 0xC98A, 0x0B07}, - /* CAM_PORT_MIPI_TIMING_T_CLK_POST_CLK_PRE */ - {MISENSOR_16BIT, 0xC98C, 0x0D01}, - /* CAM_PORT_MIPI_TIMING_T_CLK_TRAIL_CLK_ZERO */ - {MISENSOR_16BIT, 0xC98E, 0x071D}, - {MISENSOR_16BIT, 0xC990, 0x0006}, /* CAM_PORT_MIPI_TIMING_T_LPX */ - {MISENSOR_16BIT, 0xC992, 0x0A0C}, /* CAM_PORT_MIPI_TIMING_INIT_TIMING */ - {MISENSOR_16BIT, 0x3C5A, 0x0009}, /* MIPI_DELAY_TRIM */ - {MISENSOR_16BIT, 0xC86C, 0x0210}, /* CAM_OUTPUT_FORMAT */ - {MISENSOR_16BIT, 0xA804, 0x0000}, /* AE_TRACK_ALGO */ - /* default exposure */ - {MISENSOR_16BIT, 0x3012, 0x0110}, /* COMMAND_REGISTER */ - {MISENSOR_TOK_TERM, 0, 0}, - -}; - -#if 0 /* Currently unused */ -static struct misensor_reg const mt9m114_antiflicker_50hz[] = { - {MISENSOR_16BIT, 0x098E, 0xC88B}, - {MISENSOR_8BIT, 0xC88B, 0x32}, - {MISENSOR_8BIT, 0xDC00, 0x28}, - {MISENSOR_16BIT, 0x0080, 0x8002}, - {MISENSOR_TOK_TERM, 0, 0} -}; - -static struct misensor_reg const mt9m114_antiflicker_60hz[] = { - {MISENSOR_16BIT, 0x098E, 0xC88B}, - {MISENSOR_8BIT, 0xC88B, 0x3C}, - {MISENSOR_8BIT, 0xDC00, 0x28}, - {MISENSOR_16BIT, 0x0080, 0x8002}, - {MISENSOR_TOK_TERM, 0, 0} -}; - -static struct misensor_reg const mt9m114_iq[] = { - /* [Step3-Recommended] [Sensor optimization] */ - {MISENSOR_16BIT, 0x316A, 0x8270}, - {MISENSOR_16BIT, 0x316C, 0x8270}, - {MISENSOR_16BIT, 0x3ED0, 0x2305}, - {MISENSOR_16BIT, 0x3ED2, 0x77CF}, - {MISENSOR_16BIT, 0x316E, 0x8202}, - {MISENSOR_16BIT, 0x3180, 0x87FF}, - {MISENSOR_16BIT, 0x30D4, 0x6080}, - {MISENSOR_16BIT, 0xA802, 0x0008}, - - /* This register is from vender to avoid low light color noise */ - {MISENSOR_16BIT, 0x31E0, 0x0001}, - - /* LOAD=Errata item 1 */ - {MISENSOR_16BIT, 0x3E14, 0xFF39}, - - /* LOAD=Errata item 2 */ - {MISENSOR_16BIT, 0x301A, 0x8234}, - - /* - * LOAD=Errata item 3 - * LOAD=Patch 0202; - * Feature Recommended; Black level correction fix - */ - {MISENSOR_16BIT, 0x0982, 0x0001}, - {MISENSOR_16BIT, 0x098A, 0x5000}, - {MISENSOR_16BIT, 0xD000, 0x70CF}, - {MISENSOR_16BIT, 0xD002, 0xFFFF}, - {MISENSOR_16BIT, 0xD004, 0xC5D4}, - {MISENSOR_16BIT, 0xD006, 0x903A}, - {MISENSOR_16BIT, 0xD008, 0x2144}, - {MISENSOR_16BIT, 0xD00A, 0x0C00}, - {MISENSOR_16BIT, 0xD00C, 0x2186}, - {MISENSOR_16BIT, 0xD00E, 0x0FF3}, - {MISENSOR_16BIT, 0xD010, 0xB844}, - {MISENSOR_16BIT, 0xD012, 0xB948}, - {MISENSOR_16BIT, 0xD014, 0xE082}, - {MISENSOR_16BIT, 0xD016, 0x20CC}, - {MISENSOR_16BIT, 0xD018, 0x80E2}, - {MISENSOR_16BIT, 0xD01A, 0x21CC}, - {MISENSOR_16BIT, 0xD01C, 0x80A2}, - {MISENSOR_16BIT, 0xD01E, 0x21CC}, - {MISENSOR_16BIT, 0xD020, 0x80E2}, - {MISENSOR_16BIT, 0xD022, 0xF404}, - {MISENSOR_16BIT, 0xD024, 0xD801}, - {MISENSOR_16BIT, 0xD026, 0xF003}, - {MISENSOR_16BIT, 0xD028, 0xD800}, - {MISENSOR_16BIT, 0xD02A, 0x7EE0}, - {MISENSOR_16BIT, 0xD02C, 0xC0F1}, - {MISENSOR_16BIT, 0xD02E, 0x08BA}, - - {MISENSOR_16BIT, 0xD030, 0x0600}, - {MISENSOR_16BIT, 0xD032, 0xC1A1}, - {MISENSOR_16BIT, 0xD034, 0x76CF}, - {MISENSOR_16BIT, 0xD036, 0xFFFF}, - {MISENSOR_16BIT, 0xD038, 0xC130}, - {MISENSOR_16BIT, 0xD03A, 0x6E04}, - {MISENSOR_16BIT, 0xD03C, 0xC040}, - {MISENSOR_16BIT, 0xD03E, 0x71CF}, - {MISENSOR_16BIT, 0xD040, 0xFFFF}, - {MISENSOR_16BIT, 0xD042, 0xC790}, - {MISENSOR_16BIT, 0xD044, 0x8103}, - {MISENSOR_16BIT, 0xD046, 0x77CF}, - {MISENSOR_16BIT, 0xD048, 0xFFFF}, - {MISENSOR_16BIT, 0xD04A, 0xC7C0}, - {MISENSOR_16BIT, 0xD04C, 0xE001}, - {MISENSOR_16BIT, 0xD04E, 0xA103}, - {MISENSOR_16BIT, 0xD050, 0xD800}, - {MISENSOR_16BIT, 0xD052, 0x0C6A}, - {MISENSOR_16BIT, 0xD054, 0x04E0}, - {MISENSOR_16BIT, 0xD056, 0xB89E}, - {MISENSOR_16BIT, 0xD058, 0x7508}, - {MISENSOR_16BIT, 0xD05A, 0x8E1C}, - {MISENSOR_16BIT, 0xD05C, 0x0809}, - {MISENSOR_16BIT, 0xD05E, 0x0191}, - - {MISENSOR_16BIT, 0xD060, 0xD801}, - {MISENSOR_16BIT, 0xD062, 0xAE1D}, - {MISENSOR_16BIT, 0xD064, 0xE580}, - {MISENSOR_16BIT, 0xD066, 0x20CA}, - {MISENSOR_16BIT, 0xD068, 0x0022}, - {MISENSOR_16BIT, 0xD06A, 0x20CF}, - {MISENSOR_16BIT, 0xD06C, 0x0522}, - {MISENSOR_16BIT, 0xD06E, 0x0C5C}, - {MISENSOR_16BIT, 0xD070, 0x04E2}, - {MISENSOR_16BIT, 0xD072, 0x21CA}, - {MISENSOR_16BIT, 0xD074, 0x0062}, - {MISENSOR_16BIT, 0xD076, 0xE580}, - {MISENSOR_16BIT, 0xD078, 0xD901}, - {MISENSOR_16BIT, 0xD07A, 0x79C0}, - {MISENSOR_16BIT, 0xD07C, 0xD800}, - {MISENSOR_16BIT, 0xD07E, 0x0BE6}, - {MISENSOR_16BIT, 0xD080, 0x04E0}, - {MISENSOR_16BIT, 0xD082, 0xB89E}, - {MISENSOR_16BIT, 0xD084, 0x70CF}, - {MISENSOR_16BIT, 0xD086, 0xFFFF}, - {MISENSOR_16BIT, 0xD088, 0xC8D4}, - {MISENSOR_16BIT, 0xD08A, 0x9002}, - {MISENSOR_16BIT, 0xD08C, 0x0857}, - {MISENSOR_16BIT, 0xD08E, 0x025E}, - - {MISENSOR_16BIT, 0xD090, 0xFFDC}, - {MISENSOR_16BIT, 0xD092, 0xE080}, - {MISENSOR_16BIT, 0xD094, 0x25CC}, - {MISENSOR_16BIT, 0xD096, 0x9022}, - {MISENSOR_16BIT, 0xD098, 0xF225}, - {MISENSOR_16BIT, 0xD09A, 0x1700}, - {MISENSOR_16BIT, 0xD09C, 0x108A}, - {MISENSOR_16BIT, 0xD09E, 0x73CF}, - {MISENSOR_16BIT, 0xD0A0, 0xFF00}, - {MISENSOR_16BIT, 0xD0A2, 0x3174}, - {MISENSOR_16BIT, 0xD0A4, 0x9307}, - {MISENSOR_16BIT, 0xD0A6, 0x2A04}, - {MISENSOR_16BIT, 0xD0A8, 0x103E}, - {MISENSOR_16BIT, 0xD0AA, 0x9328}, - {MISENSOR_16BIT, 0xD0AC, 0x2942}, - {MISENSOR_16BIT, 0xD0AE, 0x7140}, - {MISENSOR_16BIT, 0xD0B0, 0x2A04}, - {MISENSOR_16BIT, 0xD0B2, 0x107E}, - {MISENSOR_16BIT, 0xD0B4, 0x9349}, - {MISENSOR_16BIT, 0xD0B6, 0x2942}, - {MISENSOR_16BIT, 0xD0B8, 0x7141}, - {MISENSOR_16BIT, 0xD0BA, 0x2A04}, - {MISENSOR_16BIT, 0xD0BC, 0x10BE}, - {MISENSOR_16BIT, 0xD0BE, 0x934A}, - - {MISENSOR_16BIT, 0xD0C0, 0x2942}, - {MISENSOR_16BIT, 0xD0C2, 0x714B}, - {MISENSOR_16BIT, 0xD0C4, 0x2A04}, - {MISENSOR_16BIT, 0xD0C6, 0x10BE}, - {MISENSOR_16BIT, 0xD0C8, 0x130C}, - {MISENSOR_16BIT, 0xD0CA, 0x010A}, - {MISENSOR_16BIT, 0xD0CC, 0x2942}, - {MISENSOR_16BIT, 0xD0CE, 0x7142}, - {MISENSOR_16BIT, 0xD0D0, 0x2250}, - {MISENSOR_16BIT, 0xD0D2, 0x13CA}, - {MISENSOR_16BIT, 0xD0D4, 0x1B0C}, - {MISENSOR_16BIT, 0xD0D6, 0x0284}, - {MISENSOR_16BIT, 0xD0D8, 0xB307}, - {MISENSOR_16BIT, 0xD0DA, 0xB328}, - {MISENSOR_16BIT, 0xD0DC, 0x1B12}, - {MISENSOR_16BIT, 0xD0DE, 0x02C4}, - {MISENSOR_16BIT, 0xD0E0, 0xB34A}, - {MISENSOR_16BIT, 0xD0E2, 0xED88}, - {MISENSOR_16BIT, 0xD0E4, 0x71CF}, - {MISENSOR_16BIT, 0xD0E6, 0xFF00}, - {MISENSOR_16BIT, 0xD0E8, 0x3174}, - {MISENSOR_16BIT, 0xD0EA, 0x9106}, - {MISENSOR_16BIT, 0xD0EC, 0xB88F}, - {MISENSOR_16BIT, 0xD0EE, 0xB106}, - - {MISENSOR_16BIT, 0xD0F0, 0x210A}, - {MISENSOR_16BIT, 0xD0F2, 0x8340}, - {MISENSOR_16BIT, 0xD0F4, 0xC000}, - {MISENSOR_16BIT, 0xD0F6, 0x21CA}, - {MISENSOR_16BIT, 0xD0F8, 0x0062}, - {MISENSOR_16BIT, 0xD0FA, 0x20F0}, - {MISENSOR_16BIT, 0xD0FC, 0x0040}, - {MISENSOR_16BIT, 0xD0FE, 0x0B02}, - {MISENSOR_16BIT, 0xD100, 0x0320}, - {MISENSOR_16BIT, 0xD102, 0xD901}, - {MISENSOR_16BIT, 0xD104, 0x07F1}, - {MISENSOR_16BIT, 0xD106, 0x05E0}, - {MISENSOR_16BIT, 0xD108, 0xC0A1}, - {MISENSOR_16BIT, 0xD10A, 0x78E0}, - {MISENSOR_16BIT, 0xD10C, 0xC0F1}, - {MISENSOR_16BIT, 0xD10E, 0x71CF}, - {MISENSOR_16BIT, 0xD110, 0xFFFF}, - {MISENSOR_16BIT, 0xD112, 0xC7C0}, - {MISENSOR_16BIT, 0xD114, 0xD840}, - {MISENSOR_16BIT, 0xD116, 0xA900}, - {MISENSOR_16BIT, 0xD118, 0x71CF}, - {MISENSOR_16BIT, 0xD11A, 0xFFFF}, - {MISENSOR_16BIT, 0xD11C, 0xD02C}, - {MISENSOR_16BIT, 0xD11E, 0xD81E}, - - {MISENSOR_16BIT, 0xD120, 0x0A5A}, - {MISENSOR_16BIT, 0xD122, 0x04E0}, - {MISENSOR_16BIT, 0xD124, 0xDA00}, - {MISENSOR_16BIT, 0xD126, 0xD800}, - {MISENSOR_16BIT, 0xD128, 0xC0D1}, - {MISENSOR_16BIT, 0xD12A, 0x7EE0}, - - {MISENSOR_16BIT, 0x098E, 0x0000}, - {MISENSOR_16BIT, 0xE000, 0x010C}, - {MISENSOR_16BIT, 0xE002, 0x0202}, - {MISENSOR_16BIT, 0xE004, 0x4103}, - {MISENSOR_16BIT, 0xE006, 0x0202}, - {MISENSOR_16BIT, 0x0080, 0xFFF0}, - {MISENSOR_16BIT, 0x0080, 0xFFF1}, - - /* LOAD=Patch 0302; Feature Recommended; Adaptive Sensitivity */ - {MISENSOR_16BIT, 0x0982, 0x0001}, - {MISENSOR_16BIT, 0x098A, 0x512C}, - {MISENSOR_16BIT, 0xD12C, 0x70CF}, - {MISENSOR_16BIT, 0xD12E, 0xFFFF}, - {MISENSOR_16BIT, 0xD130, 0xC5D4}, - {MISENSOR_16BIT, 0xD132, 0x903A}, - {MISENSOR_16BIT, 0xD134, 0x2144}, - {MISENSOR_16BIT, 0xD136, 0x0C00}, - {MISENSOR_16BIT, 0xD138, 0x2186}, - {MISENSOR_16BIT, 0xD13A, 0x0FF3}, - {MISENSOR_16BIT, 0xD13C, 0xB844}, - {MISENSOR_16BIT, 0xD13E, 0x262F}, - {MISENSOR_16BIT, 0xD140, 0xF008}, - {MISENSOR_16BIT, 0xD142, 0xB948}, - {MISENSOR_16BIT, 0xD144, 0x21CC}, - {MISENSOR_16BIT, 0xD146, 0x8021}, - {MISENSOR_16BIT, 0xD148, 0xD801}, - {MISENSOR_16BIT, 0xD14A, 0xF203}, - {MISENSOR_16BIT, 0xD14C, 0xD800}, - {MISENSOR_16BIT, 0xD14E, 0x7EE0}, - {MISENSOR_16BIT, 0xD150, 0xC0F1}, - {MISENSOR_16BIT, 0xD152, 0x71CF}, - {MISENSOR_16BIT, 0xD154, 0xFFFF}, - {MISENSOR_16BIT, 0xD156, 0xC610}, - {MISENSOR_16BIT, 0xD158, 0x910E}, - {MISENSOR_16BIT, 0xD15A, 0x208C}, - {MISENSOR_16BIT, 0xD15C, 0x8014}, - {MISENSOR_16BIT, 0xD15E, 0xF418}, - {MISENSOR_16BIT, 0xD160, 0x910F}, - {MISENSOR_16BIT, 0xD162, 0x208C}, - {MISENSOR_16BIT, 0xD164, 0x800F}, - {MISENSOR_16BIT, 0xD166, 0xF414}, - {MISENSOR_16BIT, 0xD168, 0x9116}, - {MISENSOR_16BIT, 0xD16A, 0x208C}, - {MISENSOR_16BIT, 0xD16C, 0x800A}, - {MISENSOR_16BIT, 0xD16E, 0xF410}, - {MISENSOR_16BIT, 0xD170, 0x9117}, - {MISENSOR_16BIT, 0xD172, 0x208C}, - {MISENSOR_16BIT, 0xD174, 0x8807}, - {MISENSOR_16BIT, 0xD176, 0xF40C}, - {MISENSOR_16BIT, 0xD178, 0x9118}, - {MISENSOR_16BIT, 0xD17A, 0x2086}, - {MISENSOR_16BIT, 0xD17C, 0x0FF3}, - {MISENSOR_16BIT, 0xD17E, 0xB848}, - {MISENSOR_16BIT, 0xD180, 0x080D}, - {MISENSOR_16BIT, 0xD182, 0x0090}, - {MISENSOR_16BIT, 0xD184, 0xFFEA}, - {MISENSOR_16BIT, 0xD186, 0xE081}, - {MISENSOR_16BIT, 0xD188, 0xD801}, - {MISENSOR_16BIT, 0xD18A, 0xF203}, - {MISENSOR_16BIT, 0xD18C, 0xD800}, - {MISENSOR_16BIT, 0xD18E, 0xC0D1}, - {MISENSOR_16BIT, 0xD190, 0x7EE0}, - {MISENSOR_16BIT, 0xD192, 0x78E0}, - {MISENSOR_16BIT, 0xD194, 0xC0F1}, - {MISENSOR_16BIT, 0xD196, 0x71CF}, - {MISENSOR_16BIT, 0xD198, 0xFFFF}, - {MISENSOR_16BIT, 0xD19A, 0xC610}, - {MISENSOR_16BIT, 0xD19C, 0x910E}, - {MISENSOR_16BIT, 0xD19E, 0x208C}, - {MISENSOR_16BIT, 0xD1A0, 0x800A}, - {MISENSOR_16BIT, 0xD1A2, 0xF418}, - {MISENSOR_16BIT, 0xD1A4, 0x910F}, - {MISENSOR_16BIT, 0xD1A6, 0x208C}, - {MISENSOR_16BIT, 0xD1A8, 0x8807}, - {MISENSOR_16BIT, 0xD1AA, 0xF414}, - {MISENSOR_16BIT, 0xD1AC, 0x9116}, - {MISENSOR_16BIT, 0xD1AE, 0x208C}, - {MISENSOR_16BIT, 0xD1B0, 0x800A}, - {MISENSOR_16BIT, 0xD1B2, 0xF410}, - {MISENSOR_16BIT, 0xD1B4, 0x9117}, - {MISENSOR_16BIT, 0xD1B6, 0x208C}, - {MISENSOR_16BIT, 0xD1B8, 0x8807}, - {MISENSOR_16BIT, 0xD1BA, 0xF40C}, - {MISENSOR_16BIT, 0xD1BC, 0x9118}, - {MISENSOR_16BIT, 0xD1BE, 0x2086}, - {MISENSOR_16BIT, 0xD1C0, 0x0FF3}, - {MISENSOR_16BIT, 0xD1C2, 0xB848}, - {MISENSOR_16BIT, 0xD1C4, 0x080D}, - {MISENSOR_16BIT, 0xD1C6, 0x0090}, - {MISENSOR_16BIT, 0xD1C8, 0xFFD9}, - {MISENSOR_16BIT, 0xD1CA, 0xE080}, - {MISENSOR_16BIT, 0xD1CC, 0xD801}, - {MISENSOR_16BIT, 0xD1CE, 0xF203}, - {MISENSOR_16BIT, 0xD1D0, 0xD800}, - {MISENSOR_16BIT, 0xD1D2, 0xF1DF}, - {MISENSOR_16BIT, 0xD1D4, 0x9040}, - {MISENSOR_16BIT, 0xD1D6, 0x71CF}, - {MISENSOR_16BIT, 0xD1D8, 0xFFFF}, - {MISENSOR_16BIT, 0xD1DA, 0xC5D4}, - {MISENSOR_16BIT, 0xD1DC, 0xB15A}, - {MISENSOR_16BIT, 0xD1DE, 0x9041}, - {MISENSOR_16BIT, 0xD1E0, 0x73CF}, - {MISENSOR_16BIT, 0xD1E2, 0xFFFF}, - {MISENSOR_16BIT, 0xD1E4, 0xC7D0}, - {MISENSOR_16BIT, 0xD1E6, 0xB140}, - {MISENSOR_16BIT, 0xD1E8, 0x9042}, - {MISENSOR_16BIT, 0xD1EA, 0xB141}, - {MISENSOR_16BIT, 0xD1EC, 0x9043}, - {MISENSOR_16BIT, 0xD1EE, 0xB142}, - {MISENSOR_16BIT, 0xD1F0, 0x9044}, - {MISENSOR_16BIT, 0xD1F2, 0xB143}, - {MISENSOR_16BIT, 0xD1F4, 0x9045}, - {MISENSOR_16BIT, 0xD1F6, 0xB147}, - {MISENSOR_16BIT, 0xD1F8, 0x9046}, - {MISENSOR_16BIT, 0xD1FA, 0xB148}, - {MISENSOR_16BIT, 0xD1FC, 0x9047}, - {MISENSOR_16BIT, 0xD1FE, 0xB14B}, - {MISENSOR_16BIT, 0xD200, 0x9048}, - {MISENSOR_16BIT, 0xD202, 0xB14C}, - {MISENSOR_16BIT, 0xD204, 0x9049}, - {MISENSOR_16BIT, 0xD206, 0x1958}, - {MISENSOR_16BIT, 0xD208, 0x0084}, - {MISENSOR_16BIT, 0xD20A, 0x904A}, - {MISENSOR_16BIT, 0xD20C, 0x195A}, - {MISENSOR_16BIT, 0xD20E, 0x0084}, - {MISENSOR_16BIT, 0xD210, 0x8856}, - {MISENSOR_16BIT, 0xD212, 0x1B36}, - {MISENSOR_16BIT, 0xD214, 0x8082}, - {MISENSOR_16BIT, 0xD216, 0x8857}, - {MISENSOR_16BIT, 0xD218, 0x1B37}, - {MISENSOR_16BIT, 0xD21A, 0x8082}, - {MISENSOR_16BIT, 0xD21C, 0x904C}, - {MISENSOR_16BIT, 0xD21E, 0x19A7}, - {MISENSOR_16BIT, 0xD220, 0x009C}, - {MISENSOR_16BIT, 0xD222, 0x881A}, - {MISENSOR_16BIT, 0xD224, 0x7FE0}, - {MISENSOR_16BIT, 0xD226, 0x1B54}, - {MISENSOR_16BIT, 0xD228, 0x8002}, - {MISENSOR_16BIT, 0xD22A, 0x78E0}, - {MISENSOR_16BIT, 0xD22C, 0x71CF}, - {MISENSOR_16BIT, 0xD22E, 0xFFFF}, - {MISENSOR_16BIT, 0xD230, 0xC350}, - {MISENSOR_16BIT, 0xD232, 0xD828}, - {MISENSOR_16BIT, 0xD234, 0xA90B}, - {MISENSOR_16BIT, 0xD236, 0x8100}, - {MISENSOR_16BIT, 0xD238, 0x01C5}, - {MISENSOR_16BIT, 0xD23A, 0x0320}, - {MISENSOR_16BIT, 0xD23C, 0xD900}, - {MISENSOR_16BIT, 0xD23E, 0x78E0}, - {MISENSOR_16BIT, 0xD240, 0x220A}, - {MISENSOR_16BIT, 0xD242, 0x1F80}, - {MISENSOR_16BIT, 0xD244, 0xFFFF}, - {MISENSOR_16BIT, 0xD246, 0xD4E0}, - {MISENSOR_16BIT, 0xD248, 0xC0F1}, - {MISENSOR_16BIT, 0xD24A, 0x0811}, - {MISENSOR_16BIT, 0xD24C, 0x0051}, - {MISENSOR_16BIT, 0xD24E, 0x2240}, - {MISENSOR_16BIT, 0xD250, 0x1200}, - {MISENSOR_16BIT, 0xD252, 0xFFE1}, - {MISENSOR_16BIT, 0xD254, 0xD801}, - {MISENSOR_16BIT, 0xD256, 0xF006}, - {MISENSOR_16BIT, 0xD258, 0x2240}, - {MISENSOR_16BIT, 0xD25A, 0x1900}, - {MISENSOR_16BIT, 0xD25C, 0xFFDE}, - {MISENSOR_16BIT, 0xD25E, 0xD802}, - {MISENSOR_16BIT, 0xD260, 0x1A05}, - {MISENSOR_16BIT, 0xD262, 0x1002}, - {MISENSOR_16BIT, 0xD264, 0xFFF2}, - {MISENSOR_16BIT, 0xD266, 0xF195}, - {MISENSOR_16BIT, 0xD268, 0xC0F1}, - {MISENSOR_16BIT, 0xD26A, 0x0E7E}, - {MISENSOR_16BIT, 0xD26C, 0x05C0}, - {MISENSOR_16BIT, 0xD26E, 0x75CF}, - {MISENSOR_16BIT, 0xD270, 0xFFFF}, - {MISENSOR_16BIT, 0xD272, 0xC84C}, - {MISENSOR_16BIT, 0xD274, 0x9502}, - {MISENSOR_16BIT, 0xD276, 0x77CF}, - {MISENSOR_16BIT, 0xD278, 0xFFFF}, - {MISENSOR_16BIT, 0xD27A, 0xC344}, - {MISENSOR_16BIT, 0xD27C, 0x2044}, - {MISENSOR_16BIT, 0xD27E, 0x008E}, - {MISENSOR_16BIT, 0xD280, 0xB8A1}, - {MISENSOR_16BIT, 0xD282, 0x0926}, - {MISENSOR_16BIT, 0xD284, 0x03E0}, - {MISENSOR_16BIT, 0xD286, 0xB502}, - {MISENSOR_16BIT, 0xD288, 0x9502}, - {MISENSOR_16BIT, 0xD28A, 0x952E}, - {MISENSOR_16BIT, 0xD28C, 0x7E05}, - {MISENSOR_16BIT, 0xD28E, 0xB5C2}, - {MISENSOR_16BIT, 0xD290, 0x70CF}, - {MISENSOR_16BIT, 0xD292, 0xFFFF}, - {MISENSOR_16BIT, 0xD294, 0xC610}, - {MISENSOR_16BIT, 0xD296, 0x099A}, - {MISENSOR_16BIT, 0xD298, 0x04A0}, - {MISENSOR_16BIT, 0xD29A, 0xB026}, - {MISENSOR_16BIT, 0xD29C, 0x0E02}, - {MISENSOR_16BIT, 0xD29E, 0x0560}, - {MISENSOR_16BIT, 0xD2A0, 0xDE00}, - {MISENSOR_16BIT, 0xD2A2, 0x0A12}, - {MISENSOR_16BIT, 0xD2A4, 0x0320}, - {MISENSOR_16BIT, 0xD2A6, 0xB7C4}, - {MISENSOR_16BIT, 0xD2A8, 0x0B36}, - {MISENSOR_16BIT, 0xD2AA, 0x03A0}, - {MISENSOR_16BIT, 0xD2AC, 0x70C9}, - {MISENSOR_16BIT, 0xD2AE, 0x9502}, - {MISENSOR_16BIT, 0xD2B0, 0x7608}, - {MISENSOR_16BIT, 0xD2B2, 0xB8A8}, - {MISENSOR_16BIT, 0xD2B4, 0xB502}, - {MISENSOR_16BIT, 0xD2B6, 0x70CF}, - {MISENSOR_16BIT, 0xD2B8, 0x0000}, - {MISENSOR_16BIT, 0xD2BA, 0x5536}, - {MISENSOR_16BIT, 0xD2BC, 0x7860}, - {MISENSOR_16BIT, 0xD2BE, 0x2686}, - {MISENSOR_16BIT, 0xD2C0, 0x1FFB}, - {MISENSOR_16BIT, 0xD2C2, 0x9502}, - {MISENSOR_16BIT, 0xD2C4, 0x78C5}, - {MISENSOR_16BIT, 0xD2C6, 0x0631}, - {MISENSOR_16BIT, 0xD2C8, 0x05E0}, - {MISENSOR_16BIT, 0xD2CA, 0xB502}, - {MISENSOR_16BIT, 0xD2CC, 0x72CF}, - {MISENSOR_16BIT, 0xD2CE, 0xFFFF}, - {MISENSOR_16BIT, 0xD2D0, 0xC5D4}, - {MISENSOR_16BIT, 0xD2D2, 0x923A}, - {MISENSOR_16BIT, 0xD2D4, 0x73CF}, - {MISENSOR_16BIT, 0xD2D6, 0xFFFF}, - {MISENSOR_16BIT, 0xD2D8, 0xC7D0}, - {MISENSOR_16BIT, 0xD2DA, 0xB020}, - {MISENSOR_16BIT, 0xD2DC, 0x9220}, - {MISENSOR_16BIT, 0xD2DE, 0xB021}, - {MISENSOR_16BIT, 0xD2E0, 0x9221}, - {MISENSOR_16BIT, 0xD2E2, 0xB022}, - {MISENSOR_16BIT, 0xD2E4, 0x9222}, - {MISENSOR_16BIT, 0xD2E6, 0xB023}, - {MISENSOR_16BIT, 0xD2E8, 0x9223}, - {MISENSOR_16BIT, 0xD2EA, 0xB024}, - {MISENSOR_16BIT, 0xD2EC, 0x9227}, - {MISENSOR_16BIT, 0xD2EE, 0xB025}, - {MISENSOR_16BIT, 0xD2F0, 0x9228}, - {MISENSOR_16BIT, 0xD2F2, 0xB026}, - {MISENSOR_16BIT, 0xD2F4, 0x922B}, - {MISENSOR_16BIT, 0xD2F6, 0xB027}, - {MISENSOR_16BIT, 0xD2F8, 0x922C}, - {MISENSOR_16BIT, 0xD2FA, 0xB028}, - {MISENSOR_16BIT, 0xD2FC, 0x1258}, - {MISENSOR_16BIT, 0xD2FE, 0x0101}, - {MISENSOR_16BIT, 0xD300, 0xB029}, - {MISENSOR_16BIT, 0xD302, 0x125A}, - {MISENSOR_16BIT, 0xD304, 0x0101}, - {MISENSOR_16BIT, 0xD306, 0xB02A}, - {MISENSOR_16BIT, 0xD308, 0x1336}, - {MISENSOR_16BIT, 0xD30A, 0x8081}, - {MISENSOR_16BIT, 0xD30C, 0xA836}, - {MISENSOR_16BIT, 0xD30E, 0x1337}, - {MISENSOR_16BIT, 0xD310, 0x8081}, - {MISENSOR_16BIT, 0xD312, 0xA837}, - {MISENSOR_16BIT, 0xD314, 0x12A7}, - {MISENSOR_16BIT, 0xD316, 0x0701}, - {MISENSOR_16BIT, 0xD318, 0xB02C}, - {MISENSOR_16BIT, 0xD31A, 0x1354}, - {MISENSOR_16BIT, 0xD31C, 0x8081}, - {MISENSOR_16BIT, 0xD31E, 0x7FE0}, - {MISENSOR_16BIT, 0xD320, 0xA83A}, - {MISENSOR_16BIT, 0xD322, 0x78E0}, - {MISENSOR_16BIT, 0xD324, 0xC0F1}, - {MISENSOR_16BIT, 0xD326, 0x0DC2}, - {MISENSOR_16BIT, 0xD328, 0x05C0}, - {MISENSOR_16BIT, 0xD32A, 0x7608}, - {MISENSOR_16BIT, 0xD32C, 0x09BB}, - {MISENSOR_16BIT, 0xD32E, 0x0010}, - {MISENSOR_16BIT, 0xD330, 0x75CF}, - {MISENSOR_16BIT, 0xD332, 0xFFFF}, - {MISENSOR_16BIT, 0xD334, 0xD4E0}, - {MISENSOR_16BIT, 0xD336, 0x8D21}, - {MISENSOR_16BIT, 0xD338, 0x8D00}, - {MISENSOR_16BIT, 0xD33A, 0x2153}, - {MISENSOR_16BIT, 0xD33C, 0x0003}, - {MISENSOR_16BIT, 0xD33E, 0xB8C0}, - {MISENSOR_16BIT, 0xD340, 0x8D45}, - {MISENSOR_16BIT, 0xD342, 0x0B23}, - {MISENSOR_16BIT, 0xD344, 0x0000}, - {MISENSOR_16BIT, 0xD346, 0xEA8F}, - {MISENSOR_16BIT, 0xD348, 0x0915}, - {MISENSOR_16BIT, 0xD34A, 0x001E}, - {MISENSOR_16BIT, 0xD34C, 0xFF81}, - {MISENSOR_16BIT, 0xD34E, 0xE808}, - {MISENSOR_16BIT, 0xD350, 0x2540}, - {MISENSOR_16BIT, 0xD352, 0x1900}, - {MISENSOR_16BIT, 0xD354, 0xFFDE}, - {MISENSOR_16BIT, 0xD356, 0x8D00}, - {MISENSOR_16BIT, 0xD358, 0xB880}, - {MISENSOR_16BIT, 0xD35A, 0xF004}, - {MISENSOR_16BIT, 0xD35C, 0x8D00}, - {MISENSOR_16BIT, 0xD35E, 0xB8A0}, - {MISENSOR_16BIT, 0xD360, 0xAD00}, - {MISENSOR_16BIT, 0xD362, 0x8D05}, - {MISENSOR_16BIT, 0xD364, 0xE081}, - {MISENSOR_16BIT, 0xD366, 0x20CC}, - {MISENSOR_16BIT, 0xD368, 0x80A2}, - {MISENSOR_16BIT, 0xD36A, 0xDF00}, - {MISENSOR_16BIT, 0xD36C, 0xF40A}, - {MISENSOR_16BIT, 0xD36E, 0x71CF}, - {MISENSOR_16BIT, 0xD370, 0xFFFF}, - {MISENSOR_16BIT, 0xD372, 0xC84C}, - {MISENSOR_16BIT, 0xD374, 0x9102}, - {MISENSOR_16BIT, 0xD376, 0x7708}, - {MISENSOR_16BIT, 0xD378, 0xB8A6}, - {MISENSOR_16BIT, 0xD37A, 0x2786}, - {MISENSOR_16BIT, 0xD37C, 0x1FFE}, - {MISENSOR_16BIT, 0xD37E, 0xB102}, - {MISENSOR_16BIT, 0xD380, 0x0B42}, - {MISENSOR_16BIT, 0xD382, 0x0180}, - {MISENSOR_16BIT, 0xD384, 0x0E3E}, - {MISENSOR_16BIT, 0xD386, 0x0180}, - {MISENSOR_16BIT, 0xD388, 0x0F4A}, - {MISENSOR_16BIT, 0xD38A, 0x0160}, - {MISENSOR_16BIT, 0xD38C, 0x70C9}, - {MISENSOR_16BIT, 0xD38E, 0x8D05}, - {MISENSOR_16BIT, 0xD390, 0xE081}, - {MISENSOR_16BIT, 0xD392, 0x20CC}, - {MISENSOR_16BIT, 0xD394, 0x80A2}, - {MISENSOR_16BIT, 0xD396, 0xF429}, - {MISENSOR_16BIT, 0xD398, 0x76CF}, - {MISENSOR_16BIT, 0xD39A, 0xFFFF}, - {MISENSOR_16BIT, 0xD39C, 0xC84C}, - {MISENSOR_16BIT, 0xD39E, 0x082D}, - {MISENSOR_16BIT, 0xD3A0, 0x0051}, - {MISENSOR_16BIT, 0xD3A2, 0x70CF}, - {MISENSOR_16BIT, 0xD3A4, 0xFFFF}, - {MISENSOR_16BIT, 0xD3A6, 0xC90C}, - {MISENSOR_16BIT, 0xD3A8, 0x8805}, - {MISENSOR_16BIT, 0xD3AA, 0x09B6}, - {MISENSOR_16BIT, 0xD3AC, 0x0360}, - {MISENSOR_16BIT, 0xD3AE, 0xD908}, - {MISENSOR_16BIT, 0xD3B0, 0x2099}, - {MISENSOR_16BIT, 0xD3B2, 0x0802}, - {MISENSOR_16BIT, 0xD3B4, 0x9634}, - {MISENSOR_16BIT, 0xD3B6, 0xB503}, - {MISENSOR_16BIT, 0xD3B8, 0x7902}, - {MISENSOR_16BIT, 0xD3BA, 0x1523}, - {MISENSOR_16BIT, 0xD3BC, 0x1080}, - {MISENSOR_16BIT, 0xD3BE, 0xB634}, - {MISENSOR_16BIT, 0xD3C0, 0xE001}, - {MISENSOR_16BIT, 0xD3C2, 0x1D23}, - {MISENSOR_16BIT, 0xD3C4, 0x1002}, - {MISENSOR_16BIT, 0xD3C6, 0xF00B}, - {MISENSOR_16BIT, 0xD3C8, 0x9634}, - {MISENSOR_16BIT, 0xD3CA, 0x9503}, - {MISENSOR_16BIT, 0xD3CC, 0x6038}, - {MISENSOR_16BIT, 0xD3CE, 0xB614}, - {MISENSOR_16BIT, 0xD3D0, 0x153F}, - {MISENSOR_16BIT, 0xD3D2, 0x1080}, - {MISENSOR_16BIT, 0xD3D4, 0xE001}, - {MISENSOR_16BIT, 0xD3D6, 0x1D3F}, - {MISENSOR_16BIT, 0xD3D8, 0x1002}, - {MISENSOR_16BIT, 0xD3DA, 0xFFA4}, - {MISENSOR_16BIT, 0xD3DC, 0x9602}, - {MISENSOR_16BIT, 0xD3DE, 0x7F05}, - {MISENSOR_16BIT, 0xD3E0, 0xD800}, - {MISENSOR_16BIT, 0xD3E2, 0xB6E2}, - {MISENSOR_16BIT, 0xD3E4, 0xAD05}, - {MISENSOR_16BIT, 0xD3E6, 0x0511}, - {MISENSOR_16BIT, 0xD3E8, 0x05E0}, - {MISENSOR_16BIT, 0xD3EA, 0xD800}, - {MISENSOR_16BIT, 0xD3EC, 0xC0F1}, - {MISENSOR_16BIT, 0xD3EE, 0x0CFE}, - {MISENSOR_16BIT, 0xD3F0, 0x05C0}, - {MISENSOR_16BIT, 0xD3F2, 0x0A96}, - {MISENSOR_16BIT, 0xD3F4, 0x05A0}, - {MISENSOR_16BIT, 0xD3F6, 0x7608}, - {MISENSOR_16BIT, 0xD3F8, 0x0C22}, - {MISENSOR_16BIT, 0xD3FA, 0x0240}, - {MISENSOR_16BIT, 0xD3FC, 0xE080}, - {MISENSOR_16BIT, 0xD3FE, 0x20CA}, - {MISENSOR_16BIT, 0xD400, 0x0F82}, - {MISENSOR_16BIT, 0xD402, 0x0000}, - {MISENSOR_16BIT, 0xD404, 0x190B}, - {MISENSOR_16BIT, 0xD406, 0x0C60}, - {MISENSOR_16BIT, 0xD408, 0x05A2}, - {MISENSOR_16BIT, 0xD40A, 0x21CA}, - {MISENSOR_16BIT, 0xD40C, 0x0022}, - {MISENSOR_16BIT, 0xD40E, 0x0C56}, - {MISENSOR_16BIT, 0xD410, 0x0240}, - {MISENSOR_16BIT, 0xD412, 0xE806}, - {MISENSOR_16BIT, 0xD414, 0x0E0E}, - {MISENSOR_16BIT, 0xD416, 0x0220}, - {MISENSOR_16BIT, 0xD418, 0x70C9}, - {MISENSOR_16BIT, 0xD41A, 0xF048}, - {MISENSOR_16BIT, 0xD41C, 0x0896}, - {MISENSOR_16BIT, 0xD41E, 0x0440}, - {MISENSOR_16BIT, 0xD420, 0x0E96}, - {MISENSOR_16BIT, 0xD422, 0x0400}, - {MISENSOR_16BIT, 0xD424, 0x0966}, - {MISENSOR_16BIT, 0xD426, 0x0380}, - {MISENSOR_16BIT, 0xD428, 0x75CF}, - {MISENSOR_16BIT, 0xD42A, 0xFFFF}, - {MISENSOR_16BIT, 0xD42C, 0xD4E0}, - {MISENSOR_16BIT, 0xD42E, 0x8D00}, - {MISENSOR_16BIT, 0xD430, 0x084D}, - {MISENSOR_16BIT, 0xD432, 0x001E}, - {MISENSOR_16BIT, 0xD434, 0xFF47}, - {MISENSOR_16BIT, 0xD436, 0x080D}, - {MISENSOR_16BIT, 0xD438, 0x0050}, - {MISENSOR_16BIT, 0xD43A, 0xFF57}, - {MISENSOR_16BIT, 0xD43C, 0x0841}, - {MISENSOR_16BIT, 0xD43E, 0x0051}, - {MISENSOR_16BIT, 0xD440, 0x8D04}, - {MISENSOR_16BIT, 0xD442, 0x9521}, - {MISENSOR_16BIT, 0xD444, 0xE064}, - {MISENSOR_16BIT, 0xD446, 0x790C}, - {MISENSOR_16BIT, 0xD448, 0x702F}, - {MISENSOR_16BIT, 0xD44A, 0x0CE2}, - {MISENSOR_16BIT, 0xD44C, 0x05E0}, - {MISENSOR_16BIT, 0xD44E, 0xD964}, - {MISENSOR_16BIT, 0xD450, 0x72CF}, - {MISENSOR_16BIT, 0xD452, 0xFFFF}, - {MISENSOR_16BIT, 0xD454, 0xC700}, - {MISENSOR_16BIT, 0xD456, 0x9235}, - {MISENSOR_16BIT, 0xD458, 0x0811}, - {MISENSOR_16BIT, 0xD45A, 0x0043}, - {MISENSOR_16BIT, 0xD45C, 0xFF3D}, - {MISENSOR_16BIT, 0xD45E, 0x080D}, - {MISENSOR_16BIT, 0xD460, 0x0051}, - {MISENSOR_16BIT, 0xD462, 0xD801}, - {MISENSOR_16BIT, 0xD464, 0xFF77}, - {MISENSOR_16BIT, 0xD466, 0xF025}, - {MISENSOR_16BIT, 0xD468, 0x9501}, - {MISENSOR_16BIT, 0xD46A, 0x9235}, - {MISENSOR_16BIT, 0xD46C, 0x0911}, - {MISENSOR_16BIT, 0xD46E, 0x0003}, - {MISENSOR_16BIT, 0xD470, 0xFF49}, - {MISENSOR_16BIT, 0xD472, 0x080D}, - {MISENSOR_16BIT, 0xD474, 0x0051}, - {MISENSOR_16BIT, 0xD476, 0xD800}, - {MISENSOR_16BIT, 0xD478, 0xFF72}, - {MISENSOR_16BIT, 0xD47A, 0xF01B}, - {MISENSOR_16BIT, 0xD47C, 0x0886}, - {MISENSOR_16BIT, 0xD47E, 0x03E0}, - {MISENSOR_16BIT, 0xD480, 0xD801}, - {MISENSOR_16BIT, 0xD482, 0x0EF6}, - {MISENSOR_16BIT, 0xD484, 0x03C0}, - {MISENSOR_16BIT, 0xD486, 0x0F52}, - {MISENSOR_16BIT, 0xD488, 0x0340}, - {MISENSOR_16BIT, 0xD48A, 0x0DBA}, - {MISENSOR_16BIT, 0xD48C, 0x0200}, - {MISENSOR_16BIT, 0xD48E, 0x0AF6}, - {MISENSOR_16BIT, 0xD490, 0x0440}, - {MISENSOR_16BIT, 0xD492, 0x0C22}, - {MISENSOR_16BIT, 0xD494, 0x0400}, - {MISENSOR_16BIT, 0xD496, 0x0D72}, - {MISENSOR_16BIT, 0xD498, 0x0440}, - {MISENSOR_16BIT, 0xD49A, 0x0DC2}, - {MISENSOR_16BIT, 0xD49C, 0x0200}, - {MISENSOR_16BIT, 0xD49E, 0x0972}, - {MISENSOR_16BIT, 0xD4A0, 0x0440}, - {MISENSOR_16BIT, 0xD4A2, 0x0D3A}, - {MISENSOR_16BIT, 0xD4A4, 0x0220}, - {MISENSOR_16BIT, 0xD4A6, 0xD820}, - {MISENSOR_16BIT, 0xD4A8, 0x0BFA}, - {MISENSOR_16BIT, 0xD4AA, 0x0260}, - {MISENSOR_16BIT, 0xD4AC, 0x70C9}, - {MISENSOR_16BIT, 0xD4AE, 0x0451}, - {MISENSOR_16BIT, 0xD4B0, 0x05C0}, - {MISENSOR_16BIT, 0xD4B2, 0x78E0}, - {MISENSOR_16BIT, 0xD4B4, 0xD900}, - {MISENSOR_16BIT, 0xD4B6, 0xF00A}, - {MISENSOR_16BIT, 0xD4B8, 0x70CF}, - {MISENSOR_16BIT, 0xD4BA, 0xFFFF}, - {MISENSOR_16BIT, 0xD4BC, 0xD520}, - {MISENSOR_16BIT, 0xD4BE, 0x7835}, - {MISENSOR_16BIT, 0xD4C0, 0x8041}, - {MISENSOR_16BIT, 0xD4C2, 0x8000}, - {MISENSOR_16BIT, 0xD4C4, 0xE102}, - {MISENSOR_16BIT, 0xD4C6, 0xA040}, - {MISENSOR_16BIT, 0xD4C8, 0x09F1}, - {MISENSOR_16BIT, 0xD4CA, 0x8114}, - {MISENSOR_16BIT, 0xD4CC, 0x71CF}, - {MISENSOR_16BIT, 0xD4CE, 0xFFFF}, - {MISENSOR_16BIT, 0xD4D0, 0xD4E0}, - {MISENSOR_16BIT, 0xD4D2, 0x70CF}, - {MISENSOR_16BIT, 0xD4D4, 0xFFFF}, - {MISENSOR_16BIT, 0xD4D6, 0xC594}, - {MISENSOR_16BIT, 0xD4D8, 0xB03A}, - {MISENSOR_16BIT, 0xD4DA, 0x7FE0}, - {MISENSOR_16BIT, 0xD4DC, 0xD800}, - {MISENSOR_16BIT, 0xD4DE, 0x0000}, - {MISENSOR_16BIT, 0xD4E0, 0x0000}, - {MISENSOR_16BIT, 0xD4E2, 0x0500}, - {MISENSOR_16BIT, 0xD4E4, 0x0500}, - {MISENSOR_16BIT, 0xD4E6, 0x0200}, - {MISENSOR_16BIT, 0xD4E8, 0x0330}, - {MISENSOR_16BIT, 0xD4EA, 0x0000}, - {MISENSOR_16BIT, 0xD4EC, 0x0000}, - {MISENSOR_16BIT, 0xD4EE, 0x03CD}, - {MISENSOR_16BIT, 0xD4F0, 0x050D}, - {MISENSOR_16BIT, 0xD4F2, 0x01C5}, - {MISENSOR_16BIT, 0xD4F4, 0x03B3}, - {MISENSOR_16BIT, 0xD4F6, 0x00E0}, - {MISENSOR_16BIT, 0xD4F8, 0x01E3}, - {MISENSOR_16BIT, 0xD4FA, 0x0280}, - {MISENSOR_16BIT, 0xD4FC, 0x01E0}, - {MISENSOR_16BIT, 0xD4FE, 0x0109}, - {MISENSOR_16BIT, 0xD500, 0x0080}, - {MISENSOR_16BIT, 0xD502, 0x0500}, - {MISENSOR_16BIT, 0xD504, 0x0000}, - {MISENSOR_16BIT, 0xD506, 0x0000}, - {MISENSOR_16BIT, 0xD508, 0x0000}, - {MISENSOR_16BIT, 0xD50A, 0x0000}, - {MISENSOR_16BIT, 0xD50C, 0x0000}, - {MISENSOR_16BIT, 0xD50E, 0x0000}, - {MISENSOR_16BIT, 0xD510, 0x0000}, - {MISENSOR_16BIT, 0xD512, 0x0000}, - {MISENSOR_16BIT, 0xD514, 0x0000}, - {MISENSOR_16BIT, 0xD516, 0x0000}, - {MISENSOR_16BIT, 0xD518, 0x0000}, - {MISENSOR_16BIT, 0xD51A, 0x0000}, - {MISENSOR_16BIT, 0xD51C, 0x0000}, - {MISENSOR_16BIT, 0xD51E, 0x0000}, - {MISENSOR_16BIT, 0xD520, 0xFFFF}, - {MISENSOR_16BIT, 0xD522, 0xC9B4}, - {MISENSOR_16BIT, 0xD524, 0xFFFF}, - {MISENSOR_16BIT, 0xD526, 0xD324}, - {MISENSOR_16BIT, 0xD528, 0xFFFF}, - {MISENSOR_16BIT, 0xD52A, 0xCA34}, - {MISENSOR_16BIT, 0xD52C, 0xFFFF}, - {MISENSOR_16BIT, 0xD52E, 0xD3EC}, - {MISENSOR_16BIT, 0x098E, 0x0000}, - {MISENSOR_16BIT, 0xE000, 0x04B4}, - {MISENSOR_16BIT, 0xE002, 0x0302}, - {MISENSOR_16BIT, 0xE004, 0x4103}, - {MISENSOR_16BIT, 0xE006, 0x0202}, - {MISENSOR_16BIT, 0x0080, 0xFFF0}, - {MISENSOR_16BIT, 0x0080, 0xFFF1}, - - /* PGA parameter and APGA - * [Step4-APGA] [TP101_MT9M114_APGA] - */ - {MISENSOR_16BIT, 0x098E, 0x495E}, - {MISENSOR_16BIT, 0xC95E, 0x0000}, - {MISENSOR_16BIT, 0x3640, 0x02B0}, - {MISENSOR_16BIT, 0x3642, 0x8063}, - {MISENSOR_16BIT, 0x3644, 0x78D0}, - {MISENSOR_16BIT, 0x3646, 0x50CC}, - {MISENSOR_16BIT, 0x3648, 0x3511}, - {MISENSOR_16BIT, 0x364A, 0x0110}, - {MISENSOR_16BIT, 0x364C, 0xBD8A}, - {MISENSOR_16BIT, 0x364E, 0x0CD1}, - {MISENSOR_16BIT, 0x3650, 0x24ED}, - {MISENSOR_16BIT, 0x3652, 0x7C11}, - {MISENSOR_16BIT, 0x3654, 0x0150}, - {MISENSOR_16BIT, 0x3656, 0x124C}, - {MISENSOR_16BIT, 0x3658, 0x3130}, - {MISENSOR_16BIT, 0x365A, 0x508C}, - {MISENSOR_16BIT, 0x365C, 0x21F1}, - {MISENSOR_16BIT, 0x365E, 0x0090}, - {MISENSOR_16BIT, 0x3660, 0xBFCA}, - {MISENSOR_16BIT, 0x3662, 0x0A11}, - {MISENSOR_16BIT, 0x3664, 0x4F4B}, - {MISENSOR_16BIT, 0x3666, 0x28B1}, - {MISENSOR_16BIT, 0x3680, 0x50A9}, - {MISENSOR_16BIT, 0x3682, 0xA04B}, - {MISENSOR_16BIT, 0x3684, 0x0E2D}, - {MISENSOR_16BIT, 0x3686, 0x73EC}, - {MISENSOR_16BIT, 0x3688, 0x164F}, - {MISENSOR_16BIT, 0x368A, 0xF829}, - {MISENSOR_16BIT, 0x368C, 0xC1A8}, - {MISENSOR_16BIT, 0x368E, 0xB0EC}, - {MISENSOR_16BIT, 0x3690, 0xE76A}, - {MISENSOR_16BIT, 0x3692, 0x69AF}, - {MISENSOR_16BIT, 0x3694, 0x378C}, - {MISENSOR_16BIT, 0x3696, 0xA70D}, - {MISENSOR_16BIT, 0x3698, 0x884F}, - {MISENSOR_16BIT, 0x369A, 0xEE8B}, - {MISENSOR_16BIT, 0x369C, 0x5DEF}, - {MISENSOR_16BIT, 0x369E, 0x27CC}, - {MISENSOR_16BIT, 0x36A0, 0xCAAC}, - {MISENSOR_16BIT, 0x36A2, 0x840E}, - {MISENSOR_16BIT, 0x36A4, 0xDAA9}, - {MISENSOR_16BIT, 0x36A6, 0xF00C}, - {MISENSOR_16BIT, 0x36C0, 0x1371}, - {MISENSOR_16BIT, 0x36C2, 0x272F}, - {MISENSOR_16BIT, 0x36C4, 0x2293}, - {MISENSOR_16BIT, 0x36C6, 0xE6D0}, - {MISENSOR_16BIT, 0x36C8, 0xEC32}, - {MISENSOR_16BIT, 0x36CA, 0x11B1}, - {MISENSOR_16BIT, 0x36CC, 0x7BAF}, - {MISENSOR_16BIT, 0x36CE, 0x5813}, - {MISENSOR_16BIT, 0x36D0, 0xB871}, - {MISENSOR_16BIT, 0x36D2, 0x8913}, - {MISENSOR_16BIT, 0x36D4, 0x4610}, - {MISENSOR_16BIT, 0x36D6, 0x7EEE}, - {MISENSOR_16BIT, 0x36D8, 0x0DF3}, - {MISENSOR_16BIT, 0x36DA, 0xB84F}, - {MISENSOR_16BIT, 0x36DC, 0xB532}, - {MISENSOR_16BIT, 0x36DE, 0x1171}, - {MISENSOR_16BIT, 0x36E0, 0x13CF}, - {MISENSOR_16BIT, 0x36E2, 0x22F3}, - {MISENSOR_16BIT, 0x36E4, 0xE090}, - {MISENSOR_16BIT, 0x36E6, 0x8133}, - {MISENSOR_16BIT, 0x3700, 0x88AE}, - {MISENSOR_16BIT, 0x3702, 0x00EA}, - {MISENSOR_16BIT, 0x3704, 0x344F}, - {MISENSOR_16BIT, 0x3706, 0xEC88}, - {MISENSOR_16BIT, 0x3708, 0x3E91}, - {MISENSOR_16BIT, 0x370A, 0xF12D}, - {MISENSOR_16BIT, 0x370C, 0xB0EF}, - {MISENSOR_16BIT, 0x370E, 0x77CD}, - {MISENSOR_16BIT, 0x3710, 0x7930}, - {MISENSOR_16BIT, 0x3712, 0x5C12}, - {MISENSOR_16BIT, 0x3714, 0x500C}, - {MISENSOR_16BIT, 0x3716, 0x22CE}, - {MISENSOR_16BIT, 0x3718, 0x2370}, - {MISENSOR_16BIT, 0x371A, 0x258F}, - {MISENSOR_16BIT, 0x371C, 0x3D30}, - {MISENSOR_16BIT, 0x371E, 0x370C}, - {MISENSOR_16BIT, 0x3720, 0x03ED}, - {MISENSOR_16BIT, 0x3722, 0x9AD0}, - {MISENSOR_16BIT, 0x3724, 0x7ECF}, - {MISENSOR_16BIT, 0x3726, 0x1093}, - {MISENSOR_16BIT, 0x3740, 0x2391}, - {MISENSOR_16BIT, 0x3742, 0xAAD0}, - {MISENSOR_16BIT, 0x3744, 0x28F2}, - {MISENSOR_16BIT, 0x3746, 0xBA4F}, - {MISENSOR_16BIT, 0x3748, 0xC536}, - {MISENSOR_16BIT, 0x374A, 0x1472}, - {MISENSOR_16BIT, 0x374C, 0xD110}, - {MISENSOR_16BIT, 0x374E, 0x2933}, - {MISENSOR_16BIT, 0x3750, 0xD0D1}, - {MISENSOR_16BIT, 0x3752, 0x9F37}, - {MISENSOR_16BIT, 0x3754, 0x34D1}, - {MISENSOR_16BIT, 0x3756, 0x1C6C}, - {MISENSOR_16BIT, 0x3758, 0x3FD2}, - {MISENSOR_16BIT, 0x375A, 0xCB72}, - {MISENSOR_16BIT, 0x375C, 0xBA96}, - {MISENSOR_16BIT, 0x375E, 0x1551}, - {MISENSOR_16BIT, 0x3760, 0xB74F}, - {MISENSOR_16BIT, 0x3762, 0x1672}, - {MISENSOR_16BIT, 0x3764, 0x84F1}, - {MISENSOR_16BIT, 0x3766, 0xC2D6}, - {MISENSOR_16BIT, 0x3782, 0x01E0}, - {MISENSOR_16BIT, 0x3784, 0x0280}, - {MISENSOR_16BIT, 0x37C0, 0xA6EA}, - {MISENSOR_16BIT, 0x37C2, 0x874B}, - {MISENSOR_16BIT, 0x37C4, 0x85CB}, - {MISENSOR_16BIT, 0x37C6, 0x968A}, - {MISENSOR_16BIT, 0x098E, 0x0000}, - {MISENSOR_16BIT, 0xC960, 0x0AF0}, - {MISENSOR_16BIT, 0xC962, 0x79E2}, - {MISENSOR_16BIT, 0xC964, 0x5EC8}, - {MISENSOR_16BIT, 0xC966, 0x791F}, - {MISENSOR_16BIT, 0xC968, 0x76EE}, - {MISENSOR_16BIT, 0xC96A, 0x0FA0}, - {MISENSOR_16BIT, 0xC96C, 0x7DFA}, - {MISENSOR_16BIT, 0xC96E, 0x7DAF}, - {MISENSOR_16BIT, 0xC970, 0x7E02}, - {MISENSOR_16BIT, 0xC972, 0x7E0A}, - {MISENSOR_16BIT, 0xC974, 0x1964}, - {MISENSOR_16BIT, 0xC976, 0x7CDC}, - {MISENSOR_16BIT, 0xC978, 0x7838}, - {MISENSOR_16BIT, 0xC97A, 0x7C2F}, - {MISENSOR_16BIT, 0xC97C, 0x7792}, - {MISENSOR_16BIT, 0xC95E, 0x0003}, - - /* [Step4-APGA] */ - {MISENSOR_16BIT, 0x098E, 0x0000}, - {MISENSOR_16BIT, 0xC95E, 0x0003}, - - /* [Step5-AWB_CCM]1: LOAD=CCM */ - {MISENSOR_16BIT, 0xC892, 0x0267}, - {MISENSOR_16BIT, 0xC894, 0xFF1A}, - {MISENSOR_16BIT, 0xC896, 0xFFB3}, - {MISENSOR_16BIT, 0xC898, 0xFF80}, - {MISENSOR_16BIT, 0xC89A, 0x0166}, - {MISENSOR_16BIT, 0xC89C, 0x0003}, - {MISENSOR_16BIT, 0xC89E, 0xFF9A}, - {MISENSOR_16BIT, 0xC8A0, 0xFEB4}, - {MISENSOR_16BIT, 0xC8A2, 0x024D}, - {MISENSOR_16BIT, 0xC8A4, 0x01BF}, - {MISENSOR_16BIT, 0xC8A6, 0xFF01}, - {MISENSOR_16BIT, 0xC8A8, 0xFFF3}, - {MISENSOR_16BIT, 0xC8AA, 0xFF75}, - {MISENSOR_16BIT, 0xC8AC, 0x0198}, - {MISENSOR_16BIT, 0xC8AE, 0xFFFD}, - {MISENSOR_16BIT, 0xC8B0, 0xFF9A}, - {MISENSOR_16BIT, 0xC8B2, 0xFEE7}, - {MISENSOR_16BIT, 0xC8B4, 0x02A8}, - {MISENSOR_16BIT, 0xC8B6, 0x01D9}, - {MISENSOR_16BIT, 0xC8B8, 0xFF26}, - {MISENSOR_16BIT, 0xC8BA, 0xFFF3}, - {MISENSOR_16BIT, 0xC8BC, 0xFFB3}, - {MISENSOR_16BIT, 0xC8BE, 0x0132}, - {MISENSOR_16BIT, 0xC8C0, 0xFFE8}, - {MISENSOR_16BIT, 0xC8C2, 0xFFDA}, - {MISENSOR_16BIT, 0xC8C4, 0xFECD}, - {MISENSOR_16BIT, 0xC8C6, 0x02C2}, - {MISENSOR_16BIT, 0xC8C8, 0x0075}, - {MISENSOR_16BIT, 0xC8CA, 0x011C}, - {MISENSOR_16BIT, 0xC8CC, 0x009A}, - {MISENSOR_16BIT, 0xC8CE, 0x0105}, - {MISENSOR_16BIT, 0xC8D0, 0x00A4}, - {MISENSOR_16BIT, 0xC8D2, 0x00AC}, - {MISENSOR_16BIT, 0xC8D4, 0x0A8C}, - {MISENSOR_16BIT, 0xC8D6, 0x0F0A}, - {MISENSOR_16BIT, 0xC8D8, 0x1964}, - - /* LOAD=AWB */ - {MISENSOR_16BIT, 0xC914, 0x0000}, - {MISENSOR_16BIT, 0xC916, 0x0000}, - {MISENSOR_16BIT, 0xC918, 0x04FF}, - {MISENSOR_16BIT, 0xC91A, 0x02CF}, - {MISENSOR_16BIT, 0xC904, 0x0033}, - {MISENSOR_16BIT, 0xC906, 0x0040}, - {MISENSOR_8BIT, 0xC8F2, 0x03}, - {MISENSOR_8BIT, 0xC8F3, 0x02}, - {MISENSOR_16BIT, 0xC906, 0x003C}, - {MISENSOR_16BIT, 0xC8F4, 0x0000}, - {MISENSOR_16BIT, 0xC8F6, 0x0000}, - {MISENSOR_16BIT, 0xC8F8, 0x0000}, - {MISENSOR_16BIT, 0xC8FA, 0xE724}, - {MISENSOR_16BIT, 0xC8FC, 0x1583}, - {MISENSOR_16BIT, 0xC8FE, 0x2045}, - {MISENSOR_16BIT, 0xC900, 0x05DC}, - {MISENSOR_16BIT, 0xC902, 0x007C}, - {MISENSOR_8BIT, 0xC90C, 0x80}, - {MISENSOR_8BIT, 0xC90D, 0x80}, - {MISENSOR_8BIT, 0xC90E, 0x80}, - {MISENSOR_8BIT, 0xC90F, 0x88}, - {MISENSOR_8BIT, 0xC910, 0x80}, - {MISENSOR_8BIT, 0xC911, 0x80}, - - /* LOAD=Step7-CPIPE_Preference */ - {MISENSOR_16BIT, 0xC926, 0x0020}, - {MISENSOR_16BIT, 0xC928, 0x009A}, - {MISENSOR_16BIT, 0xC946, 0x0070}, - {MISENSOR_16BIT, 0xC948, 0x00F3}, - {MISENSOR_16BIT, 0xC952, 0x0020}, - {MISENSOR_16BIT, 0xC954, 0x009A}, - {MISENSOR_8BIT, 0xC92A, 0x80}, - {MISENSOR_8BIT, 0xC92B, 0x4B}, - {MISENSOR_8BIT, 0xC92C, 0x00}, - {MISENSOR_8BIT, 0xC92D, 0xFF}, - {MISENSOR_8BIT, 0xC92E, 0x3C}, - {MISENSOR_8BIT, 0xC92F, 0x02}, - {MISENSOR_8BIT, 0xC930, 0x06}, - {MISENSOR_8BIT, 0xC931, 0x64}, - {MISENSOR_8BIT, 0xC932, 0x01}, - {MISENSOR_8BIT, 0xC933, 0x0C}, - {MISENSOR_8BIT, 0xC934, 0x3C}, - {MISENSOR_8BIT, 0xC935, 0x3C}, - {MISENSOR_8BIT, 0xC936, 0x3C}, - {MISENSOR_8BIT, 0xC937, 0x0F}, - {MISENSOR_8BIT, 0xC938, 0x64}, - {MISENSOR_8BIT, 0xC939, 0x64}, - {MISENSOR_8BIT, 0xC93A, 0x64}, - {MISENSOR_8BIT, 0xC93B, 0x32}, - {MISENSOR_16BIT, 0xC93C, 0x0020}, - {MISENSOR_16BIT, 0xC93E, 0x009A}, - {MISENSOR_16BIT, 0xC940, 0x00DC}, - {MISENSOR_8BIT, 0xC942, 0x38}, - {MISENSOR_8BIT, 0xC943, 0x30}, - {MISENSOR_8BIT, 0xC944, 0x50}, - {MISENSOR_8BIT, 0xC945, 0x19}, - {MISENSOR_16BIT, 0xC94A, 0x0230}, - {MISENSOR_16BIT, 0xC94C, 0x0010}, - {MISENSOR_16BIT, 0xC94E, 0x01CD}, - {MISENSOR_8BIT, 0xC950, 0x05}, - {MISENSOR_8BIT, 0xC951, 0x40}, - {MISENSOR_8BIT, 0xC87B, 0x1B}, - {MISENSOR_8BIT, 0xC878, 0x0E}, - {MISENSOR_16BIT, 0xC890, 0x0080}, - {MISENSOR_16BIT, 0xC886, 0x0100}, - {MISENSOR_16BIT, 0xC87C, 0x005A}, - {MISENSOR_8BIT, 0xB42A, 0x05}, - {MISENSOR_8BIT, 0xA80A, 0x20}, - - /* Speed up AE/AWB */ - {MISENSOR_16BIT, 0x098E, 0x2802}, - {MISENSOR_16BIT, 0xA802, 0x0008}, - {MISENSOR_8BIT, 0xC908, 0x01}, - {MISENSOR_8BIT, 0xC879, 0x01}, - {MISENSOR_8BIT, 0xC909, 0x02}, - {MISENSOR_8BIT, 0xA80A, 0x18}, - {MISENSOR_8BIT, 0xA80B, 0x18}, - {MISENSOR_8BIT, 0xAC16, 0x18}, - {MISENSOR_8BIT, 0xC878, 0x0E}, - - {MISENSOR_TOK_TERM, 0, 0} -}; - -#endif -#endif -- cgit v1.2.3-59-g8ed1b From f287a048bccbf7e5a5d1cc55c974ee8ebda83ddc Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 29 Apr 2025 10:34:34 +0100 Subject: media: dt-bindings: Add OmniVision OV02C10 Extend the ov02e10 bindings yaml to describe the ov02c10 sensor which has the same bindings with a different compat string and different i2c address only. Other differences in sensor capabilities exist but are not expressed in devicetree. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bryan O'Donoghue Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil [hverkuil: fix typos: 0V02C10 -> OV02C10] [hverkuil: fix type: Ominivision -> OmniVision] --- .../bindings/media/i2c/ovti,ov02e10.yaml | 47 ++++++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov02e10.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov02e10.yaml index 4ac4e11a16c8..03d476bcf805 100644 --- a/Documentation/devicetree/bindings/media/i2c/ovti,ov02e10.yaml +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov02e10.yaml @@ -11,12 +11,22 @@ maintainers: - Bryan O'Donoghue description: | - The Omnivision OV02E10 is a 2 megapixel, CMOS image sensor which supports: + The Omnivision OV02E10 and OV02C10 sensors are 2 megapixel, CMOS image sensors which support: - Automatic black level calibration (ABLC) - Programmable controls for frame rate, mirror and flip, binning, cropping and windowing - - Output formats 10-bit 4C RGB RAW, 10-bit Bayer RAW - - 2-lane MIPI D-PHY TX @ 720 Mbps per lane + - OVO2C10 + - 10 bit RAW Bayer 1920x1080 60 fps 2-lane @ 800 Mbps/lane + - 10 bit RAW Bayer 1920x1080 60 fps 1-lane @ 1500 Mbps/lane + - 10 bit RAW Bayer 1280x720 60 fps cropped 1-lane @ 960 Mbps/lane + - 10 bit RGB/BW 640x480 60 fps bin2 or skip2 1-lane @ 800 Mbps/lane + - 10 bit RGB/BW 480x270 60 fps bin4 or skip4 1-lane @ 800 Mbps/lane + - OV02E10 + - 10 bit RAW Bayer 1920x1088 60 fps 2-lane @ 720 Mbps/lane + - 10 bit RAW Bayer 1280x1080 60 fps 2-lane @ 720 Mbps/lane + - 10 bit Quad Bayer 960x540 60 fps 2-lane 360 Mbps/lane + - 8 bit Quad Bayer 480x270 1/3/5/10 fps sub2 288 Mbps/lane + - 8 bit Quad Bayer 232x132 1/3/5/10 fps sub4 144 Mbps/lane - Dynamic defect pixel cancellation - Standard SCCB command interface @@ -25,7 +35,9 @@ allOf: properties: compatible: - const: ovti,ov02e10 + enum: + - ovti,ov02c10 + - ovti,ov02e10 reg: maxItems: 1 @@ -109,5 +121,32 @@ examples: }; }; }; + + ov02c10: camera@36 { + compatible = "ovti,ov02c10"; + reg = <0x36>; + + reset-gpios = <&tlmm 237 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&cam_rgb_defaultt>; + + clocks = <&ov02c10_clk>; + + assigned-clocks = <&ov02c10_clk>; + assigned-clock-parents = <&ov02c10_clk_parent>; + assigned-clock-rates = <19200000>; + + avdd-supply = <&vreg_l7b_2p8>; + dvdd-supply = <&vreg_l7b_1p8>; + dovdd-supply = <&vreg_l3m_1p8>; + + port { + ov02c10_ep: endpoint { + remote-endpoint = <&csiphy4_ep>; + data-lanes = <1 2>; + link-frequencies = /bits/ 64 <400000000>; + }; + }; + }; }; ... -- cgit v1.2.3-59-g8ed1b From 44f89010dae0eff6aabb9c14fb4b1001542498b4 Mon Sep 17 00:00:00 2001 From: Heimir Thor Sverrisson Date: Thu, 27 Mar 2025 17:49:27 +0100 Subject: media: i2c: Add Omnivision OV02C10 sensor driver Add a new driver for the Omnivision OV02C10 camera sensor. This is based on the out of tree driver by Hao Yao from: https://github.com/intel/ipu6-drivers/blob/master/drivers/media/i2c/ov02c10.c This has been tested on a Dell XPS 9440 together with the IPU6 isys CSI driver and the libcamera software ISP code. Tested-by: Ingvar Hagelund # Dell XPS 9340 Tested-by: Heimir Thor Sverrisson # Dell XPS 9440 Signed-off-by: Heimir Thor Sverrisson Co-developed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue Co-developed-by: Hans de Goede Signed-off-by: Hans de Goede Tested-by: Stanislaw Gruszka # Dell XPS 9340 Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- MAINTAINERS | 8 + drivers/media/i2c/Kconfig | 10 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov02c10.c | 1013 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1032 insertions(+) create mode 100644 drivers/media/i2c/ov02c10.c diff --git a/MAINTAINERS b/MAINTAINERS index 5dee8459a614..a273aa905bed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17809,6 +17809,14 @@ T: git git://linuxtv.org/media.git F: Documentation/devicetree/bindings/media/i2c/ovti,ov02a10.yaml F: drivers/media/i2c/ov02a10.c +OMNIVISION OV02C10 SENSOR DRIVER +M: Hans de Goede +R: Bryan O'Donoghue +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media.git +F: drivers/media/i2c/ov02c10.c + OMNIVISION OV02E10 SENSOR DRIVER M: Bryan O'Donoghue M: Hans de Goede diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 205360c3ae52..9b85ae654760 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -367,6 +367,16 @@ config VIDEO_OV02E10 To compile this driver as a module, choose M here: the module will be called ov02e10. +config VIDEO_OV02C10 + tristate "OmniVision OV02C10 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the OmniVision + OV02C10 camera. + + To compile this driver as a module, choose M here: the + module will be called ov02c10. + config VIDEO_OV08D10 tristate "OmniVision OV08D10 sensor support" help diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index ed5e62fd6199..1b577e5ebc73 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -83,6 +83,7 @@ obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o +obj-$(CONFIG_VIDEO_OV02C10) += ov02c10.o obj-$(CONFIG_VIDEO_OV02E10) += ov02e10.o obj-$(CONFIG_VIDEO_OV08D10) += ov08d10.o obj-$(CONFIG_VIDEO_OV08X40) += ov08x40.o diff --git a/drivers/media/i2c/ov02c10.c b/drivers/media/i2c/ov02c10.c new file mode 100644 index 000000000000..9e3d4a4e12ce --- /dev/null +++ b/drivers/media/i2c/ov02c10.c @@ -0,0 +1,1013 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OV02C10_LINK_FREQ_400MHZ 400000000ULL +#define OV02C10_MCLK 19200000 +#define OV02C10_RGB_DEPTH 10 + +#define OV02C10_REG_CHIP_ID CCI_REG16(0x300a) +#define OV02C10_CHIP_ID 0x5602 + +#define OV02C10_REG_STREAM_CONTROL CCI_REG8(0x0100) + +#define OV02C10_REG_HTS CCI_REG16(0x380c) + +/* vertical-timings from sensor */ +#define OV02C10_REG_VTS CCI_REG16(0x380e) +#define OV02C10_VTS_MAX 0xffff + +/* Exposure controls from sensor */ +#define OV02C10_REG_EXPOSURE CCI_REG16(0x3501) +#define OV02C10_EXPOSURE_MIN 4 +#define OV02C10_EXPOSURE_MAX_MARGIN 8 +#define OV02C10_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define OV02C10_REG_ANALOG_GAIN CCI_REG16(0x3508) +#define OV02C10_ANAL_GAIN_MIN 0x10 +#define OV02C10_ANAL_GAIN_MAX 0xf8 +#define OV02C10_ANAL_GAIN_STEP 1 +#define OV02C10_ANAL_GAIN_DEFAULT 0x10 + +/* Digital gain controls from sensor */ +#define OV02C10_REG_DIGITAL_GAIN CCI_REG24(0x350a) +#define OV02C10_DGTL_GAIN_MIN 0x0400 +#define OV02C10_DGTL_GAIN_MAX 0x3fff +#define OV02C10_DGTL_GAIN_STEP 1 +#define OV02C10_DGTL_GAIN_DEFAULT 0x0400 + +/* Rotate */ +#define OV02C10_ROTATE_CONTROL CCI_REG8(0x3820) +#define OV02C10_ISP_X_WIN_CONTROL CCI_REG16(0x3810) +#define OV02C10_ISP_Y_WIN_CONTROL CCI_REG16(0x3812) +#define OV02C10_CONFIG_ROTATE 0x18 + +/* Test Pattern Control */ +#define OV02C10_REG_TEST_PATTERN CCI_REG8(0x4503) +#define OV02C10_TEST_PATTERN_ENABLE BIT(7) + +struct ov02c10_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 hts; + + /* Min vertical timining size */ + u32 vts_min; + + /* Sensor register settings for this resolution */ + const struct reg_sequence *reg_sequence; + const int sequence_length; + /* Sensor register settings for 1 or 2 lane config */ + const struct reg_sequence *lane_settings[2]; + const int lane_settings_length[2]; +}; + +static const struct reg_sequence sensor_1928x1092_30fps_setting[] = { + {0x0301, 0x08}, + {0x0303, 0x06}, + {0x0304, 0x01}, + {0x0305, 0xe0}, + {0x0313, 0x40}, + {0x031c, 0x4f}, + {0x3020, 0x97}, + {0x3022, 0x01}, + {0x3026, 0xb4}, + {0x303b, 0x00}, + {0x303c, 0x4f}, + {0x303d, 0xe6}, + {0x303e, 0x00}, + {0x303f, 0x03}, + {0x3021, 0x23}, + {0x3501, 0x04}, + {0x3502, 0x6c}, + {0x3504, 0x0c}, + {0x3507, 0x00}, + {0x3508, 0x08}, + {0x3509, 0x00}, + {0x350a, 0x01}, + {0x350b, 0x00}, + {0x350c, 0x41}, + {0x3600, 0x84}, + {0x3603, 0x08}, + {0x3610, 0x57}, + {0x3611, 0x1b}, + {0x3613, 0x78}, + {0x3623, 0x00}, + {0x3632, 0xa0}, + {0x3642, 0xe8}, + {0x364c, 0x70}, + {0x365f, 0x0f}, + {0x3708, 0x30}, + {0x3714, 0x24}, + {0x3725, 0x02}, + {0x3737, 0x08}, + {0x3739, 0x28}, + {0x3749, 0x32}, + {0x374a, 0x32}, + {0x374b, 0x32}, + {0x374c, 0x32}, + {0x374d, 0x81}, + {0x374e, 0x81}, + {0x374f, 0x81}, + {0x3752, 0x36}, + {0x3753, 0x36}, + {0x3754, 0x36}, + {0x3761, 0x00}, + {0x376c, 0x81}, + {0x3774, 0x18}, + {0x3776, 0x08}, + {0x377c, 0x81}, + {0x377d, 0x81}, + {0x377e, 0x81}, + {0x37a0, 0x44}, + {0x37a6, 0x44}, + {0x37aa, 0x0d}, + {0x37ae, 0x00}, + {0x37cb, 0x03}, + {0x37cc, 0x01}, + {0x37d8, 0x02}, + {0x37d9, 0x10}, + {0x37e1, 0x10}, + {0x37e2, 0x18}, + {0x37e3, 0x08}, + {0x37e4, 0x08}, + {0x37e5, 0x02}, + {0x37e6, 0x08}, + + /* 1928x1092 */ + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x07}, + {0x3805, 0x8f}, + {0x3806, 0x04}, + {0x3807, 0x47}, + {0x3808, 0x07}, + {0x3809, 0x88}, + {0x380a, 0x04}, + {0x380b, 0x44}, + {0x3810, 0x00}, + {0x3811, 0x02}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x01}, + {0x3815, 0x01}, + {0x3816, 0x01}, + {0x3817, 0x01}, + + {0x3820, 0xb0}, + {0x3821, 0x00}, + {0x3822, 0x80}, + {0x3823, 0x08}, + {0x3824, 0x00}, + {0x3825, 0x20}, + {0x3826, 0x00}, + {0x3827, 0x08}, + {0x382a, 0x00}, + {0x382b, 0x08}, + {0x382d, 0x00}, + {0x382e, 0x00}, + {0x382f, 0x23}, + {0x3834, 0x00}, + {0x3839, 0x00}, + {0x383a, 0xd1}, + {0x383e, 0x03}, + {0x393d, 0x29}, + {0x393f, 0x6e}, + {0x394b, 0x06}, + {0x394c, 0x06}, + {0x394d, 0x08}, + {0x394f, 0x01}, + {0x3950, 0x01}, + {0x3951, 0x01}, + {0x3952, 0x01}, + {0x3953, 0x01}, + {0x3954, 0x01}, + {0x3955, 0x01}, + {0x3956, 0x01}, + {0x3957, 0x0e}, + {0x3958, 0x08}, + {0x3959, 0x08}, + {0x395a, 0x08}, + {0x395b, 0x13}, + {0x395c, 0x09}, + {0x395d, 0x05}, + {0x395e, 0x02}, + {0x395f, 0x00}, + {0x395f, 0x00}, + {0x3960, 0x00}, + {0x3961, 0x00}, + {0x3962, 0x00}, + {0x3963, 0x00}, + {0x3964, 0x00}, + {0x3965, 0x00}, + {0x3966, 0x00}, + {0x3967, 0x00}, + {0x3968, 0x01}, + {0x3969, 0x01}, + {0x396a, 0x01}, + {0x396b, 0x01}, + {0x396c, 0x10}, + {0x396d, 0xf0}, + {0x396e, 0x11}, + {0x396f, 0x00}, + {0x3970, 0x37}, + {0x3971, 0x37}, + {0x3972, 0x37}, + {0x3973, 0x37}, + {0x3974, 0x00}, + {0x3975, 0x3c}, + {0x3976, 0x3c}, + {0x3977, 0x3c}, + {0x3978, 0x3c}, + {0x3c00, 0x0f}, + {0x3c20, 0x01}, + {0x3c21, 0x08}, + {0x3f00, 0x8b}, + {0x3f02, 0x0f}, + {0x4000, 0xc3}, + {0x4001, 0xe0}, + {0x4002, 0x00}, + {0x4003, 0x40}, + {0x4008, 0x04}, + {0x4009, 0x23}, + {0x400a, 0x04}, + {0x400b, 0x01}, + {0x4077, 0x06}, + {0x4078, 0x00}, + {0x4079, 0x1a}, + {0x407a, 0x7f}, + {0x407b, 0x01}, + {0x4080, 0x03}, + {0x4081, 0x84}, + {0x4308, 0x03}, + {0x4309, 0xff}, + {0x430d, 0x00}, + {0x4806, 0x00}, + {0x4813, 0x00}, + {0x4837, 0x10}, + {0x4857, 0x05}, + {0x4500, 0x07}, + {0x4501, 0x00}, + {0x4503, 0x00}, + {0x450a, 0x04}, + {0x450e, 0x00}, + {0x450f, 0x00}, + {0x4900, 0x00}, + {0x4901, 0x00}, + {0x4902, 0x01}, + {0x5001, 0x50}, + {0x5006, 0x00}, + {0x5080, 0x40}, + {0x5181, 0x2b}, + {0x5202, 0xa3}, + {0x5206, 0x01}, + {0x5207, 0x00}, + {0x520a, 0x01}, + {0x520b, 0x00}, + {0x365d, 0x00}, + {0x4815, 0x40}, + {0x4816, 0x12}, + {0x4f00, 0x01}, +}; + +static const struct reg_sequence sensor_1928x1092_30fps_1lane_setting[] = { + {0x301b, 0xd2}, + {0x3027, 0xe1}, + {0x380c, 0x08}, + {0x380d, 0xe8}, + {0x380e, 0x04}, + {0x380f, 0x8c}, + {0x394e, 0x0b}, + {0x4800, 0x24}, + {0x5000, 0xf5}, + /* plls */ + {0x0303, 0x05}, + {0x0305, 0x90}, + {0x0316, 0x90}, + {0x3016, 0x12}, +}; + +static const struct reg_sequence sensor_1928x1092_30fps_2lane_setting[] = { + {0x301b, 0xf0}, + {0x3027, 0xf1}, + {0x380c, 0x04}, + {0x380d, 0x74}, + {0x380e, 0x09}, + {0x380f, 0x18}, + {0x394e, 0x0a}, + {0x4041, 0x20}, + {0x4884, 0x04}, + {0x4800, 0x64}, + {0x4d00, 0x03}, + {0x4d01, 0xd8}, + {0x4d02, 0xba}, + {0x4d03, 0xa0}, + {0x4d04, 0xb7}, + {0x4d05, 0x34}, + {0x4d0d, 0x00}, + {0x5000, 0xfd}, + {0x481f, 0x30}, + /* plls */ + {0x0303, 0x05}, + {0x0305, 0x90}, + {0x0316, 0x90}, + {0x3016, 0x32}, +}; + +static const char * const ov02c10_test_pattern_menu[] = { + "Disabled", + "Color Bar", + "Top-Bottom Darker Color Bar", + "Right-Left Darker Color Bar", + "Color Bar type 4", +}; + +static const s64 link_freq_menu_items[] = { + OV02C10_LINK_FREQ_400MHZ, +}; + +static const struct ov02c10_mode supported_modes[] = { + { + .width = 1928, + .height = 1092, + .hts = 2280, + .vts_min = 1164, + .reg_sequence = sensor_1928x1092_30fps_setting, + .sequence_length = ARRAY_SIZE(sensor_1928x1092_30fps_setting), + .lane_settings = { + sensor_1928x1092_30fps_1lane_setting, + sensor_1928x1092_30fps_2lane_setting + }, + .lane_settings_length = { + ARRAY_SIZE(sensor_1928x1092_30fps_1lane_setting), + ARRAY_SIZE(sensor_1928x1092_30fps_2lane_setting), + }, + }, +}; + +static const char * const ov02c10_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + +struct ov02c10 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct regmap *regmap; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + struct clk *img_clk; + struct gpio_desc *reset; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov02c10_supply_names)]; + + /* MIPI lane info */ + u32 link_freq_index; + u8 mipi_lanes; +}; + +static inline struct ov02c10 *to_ov02c10(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct ov02c10, sd); +} + +static int ov02c10_test_pattern(struct ov02c10 *ov02c10, int pattern) +{ + int ret = 0; + + if (!pattern) + return cci_update_bits(ov02c10->regmap, OV02C10_REG_TEST_PATTERN, + BIT(7), 0, NULL); + + cci_update_bits(ov02c10->regmap, OV02C10_REG_TEST_PATTERN, + 0x03, pattern - 1, &ret); + cci_update_bits(ov02c10->regmap, OV02C10_REG_TEST_PATTERN, + BIT(7), OV02C10_TEST_PATTERN_ENABLE, &ret); + return ret; +} + +static int ov02c10_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov02c10 *ov02c10 = container_of(ctrl->handler, + struct ov02c10, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&ov02c10->sd); + const u32 height = supported_modes[0].height; + s64 exposure_max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = height + ctrl->val - OV02C10_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(ov02c10->exposure, + ov02c10->exposure->minimum, + exposure_max, ov02c10->exposure->step, + exposure_max); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + cci_write(ov02c10->regmap, OV02C10_REG_ANALOG_GAIN, + ctrl->val << 4, &ret); + break; + + case V4L2_CID_DIGITAL_GAIN: + cci_write(ov02c10->regmap, OV02C10_REG_DIGITAL_GAIN, + ctrl->val << 6, &ret); + break; + + case V4L2_CID_EXPOSURE: + cci_write(ov02c10->regmap, OV02C10_REG_EXPOSURE, + ctrl->val, &ret); + break; + + case V4L2_CID_VBLANK: + cci_write(ov02c10->regmap, OV02C10_REG_VTS, height + ctrl->val, + &ret); + break; + + case V4L2_CID_TEST_PATTERN: + ret = ov02c10_test_pattern(ov02c10, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov02c10_ctrl_ops = { + .s_ctrl = ov02c10_set_ctrl, +}; + +static int ov02c10_init_controls(struct ov02c10 *ov02c10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02c10->sd); + struct v4l2_ctrl_handler *ctrl_hdlr = &ov02c10->ctrl_handler; + const struct ov02c10_mode *mode = &supported_modes[0]; + u32 vblank_min, vblank_max, vblank_default, vts_def; + struct v4l2_fwnode_device_properties props; + s64 exposure_max, h_blank, pixel_rate; + int ret; + + v4l2_ctrl_handler_init(ctrl_hdlr, 10); + + ov02c10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &ov02c10_ctrl_ops, + V4L2_CID_LINK_FREQ, + ov02c10->link_freq_index, 0, + link_freq_menu_items); + if (ov02c10->link_freq) + ov02c10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* MIPI lanes are DDR -> use link-freq * 2 */ + pixel_rate = link_freq_menu_items[ov02c10->link_freq_index] * 2 * + ov02c10->mipi_lanes / OV02C10_RGB_DEPTH; + + ov02c10->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + pixel_rate, 1, pixel_rate); + + /* + * For default multiple min by number of lanes to keep the default + * FPS the same indepenedent of the lane count. + */ + vts_def = mode->vts_min * ov02c10->mipi_lanes; + + vblank_min = mode->vts_min - mode->height; + vblank_max = OV02C10_VTS_MAX - mode->height; + vblank_default = vts_def - mode->height; + ov02c10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, + V4L2_CID_VBLANK, vblank_min, + vblank_max, 1, vblank_default); + + h_blank = mode->hts - mode->width; + ov02c10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, + 1, h_blank); + if (ov02c10->hblank) + ov02c10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV02C10_ANAL_GAIN_MIN, OV02C10_ANAL_GAIN_MAX, + OV02C10_ANAL_GAIN_STEP, OV02C10_ANAL_GAIN_DEFAULT); + v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV02C10_DGTL_GAIN_MIN, OV02C10_DGTL_GAIN_MAX, + OV02C10_DGTL_GAIN_STEP, OV02C10_DGTL_GAIN_DEFAULT); + exposure_max = vts_def - OV02C10_EXPOSURE_MAX_MARGIN; + ov02c10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, + V4L2_CID_EXPOSURE, + OV02C10_EXPOSURE_MIN, + exposure_max, + OV02C10_EXPOSURE_STEP, + exposure_max); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov02c10_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov02c10_test_pattern_menu) - 1, + 0, 0, ov02c10_test_pattern_menu); + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + return ret; + + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov02c10_ctrl_ops, &props); + + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ov02c10->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void ov02c10_update_pad_format(const struct ov02c10_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int ov02c10_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + const struct ov02c10_mode *mode = &supported_modes[0]; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov02c10 *ov02c10 = to_ov02c10(sd); + const struct reg_sequence *reg_sequence; + int ret, sequence_length; + + ret = pm_runtime_resume_and_get(&client->dev); + if (ret) + return ret; + + reg_sequence = mode->reg_sequence; + sequence_length = mode->sequence_length; + ret = regmap_multi_reg_write(ov02c10->regmap, + reg_sequence, sequence_length); + if (ret) { + dev_err(&client->dev, "failed to set mode\n"); + goto out; + } + + reg_sequence = mode->lane_settings[ov02c10->mipi_lanes - 1]; + sequence_length = mode->lane_settings_length[ov02c10->mipi_lanes - 1]; + ret = regmap_multi_reg_write(ov02c10->regmap, + reg_sequence, sequence_length); + if (ret) { + dev_err(&client->dev, "failed to write lane settings\n"); + goto out; + } + + ret = __v4l2_ctrl_handler_setup(ov02c10->sd.ctrl_handler); + if (ret) + goto out; + + ret = cci_write(ov02c10->regmap, OV02C10_REG_STREAM_CONTROL, 1, NULL); +out: + if (ret) + pm_runtime_put(&client->dev); + + return ret; +} + +static int ov02c10_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov02c10 *ov02c10 = to_ov02c10(sd); + + cci_write(ov02c10->regmap, OV02C10_REG_STREAM_CONTROL, 0, NULL); + pm_runtime_put(&client->dev); + + return 0; +} + +/* This function tries to get power control resources */ +static int ov02c10_get_pm_resources(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02c10 *ov02c10 = to_ov02c10(sd); + int i; + + ov02c10->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ov02c10->reset)) + return dev_err_probe(dev, PTR_ERR(ov02c10->reset), + "failed to get reset gpio\n"); + + for (i = 0; i < ARRAY_SIZE(ov02c10_supply_names); i++) + ov02c10->supplies[i].supply = ov02c10_supply_names[i]; + + return devm_regulator_bulk_get(dev, ARRAY_SIZE(ov02c10_supply_names), + ov02c10->supplies); +} + +static int ov02c10_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02c10 *ov02c10 = to_ov02c10(sd); + + gpiod_set_value_cansleep(ov02c10->reset, 1); + + regulator_bulk_disable(ARRAY_SIZE(ov02c10_supply_names), + ov02c10->supplies); + + clk_disable_unprepare(ov02c10->img_clk); + + return 0; +} + +static int ov02c10_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov02c10 *ov02c10 = to_ov02c10(sd); + int ret; + + ret = clk_prepare_enable(ov02c10->img_clk); + if (ret < 0) { + dev_err(dev, "failed to enable imaging clock: %d", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(ov02c10_supply_names), + ov02c10->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators: %d", ret); + clk_disable_unprepare(ov02c10->img_clk); + return ret; + } + + if (ov02c10->reset) { + /* Assert reset for at least 2ms on back to back off-on */ + usleep_range(2000, 2200); + gpiod_set_value_cansleep(ov02c10->reset, 0); + usleep_range(5000, 5100); + } + + return 0; +} + +static int ov02c10_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + const struct ov02c10_mode *mode = &supported_modes[0]; + struct ov02c10 *ov02c10 = to_ov02c10(sd); + s32 vblank_def, h_blank; + + ov02c10_update_pad_format(mode, &fmt->format); + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_min * ov02c10->mipi_lanes - mode->height; + __v4l2_ctrl_modify_range(ov02c10->vblank, mode->vts_min - mode->height, + OV02C10_VTS_MAX - mode->height, 1, vblank_def); + __v4l2_ctrl_s_ctrl(ov02c10->vblank, vblank_def); + h_blank = mode->hts - mode->width; + __v4l2_ctrl_modify_range(ov02c10->hblank, h_blank, h_blank, 1, h_blank); + + return 0; +} + +static int ov02c10_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov02c10_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int ov02c10_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + ov02c10_update_pad_format(&supported_modes[0], + v4l2_subdev_state_get_format(sd_state, 0)); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov02c10_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops ov02c10_pad_ops = { + .set_fmt = ov02c10_set_format, + .get_fmt = v4l2_subdev_get_fmt, + .enum_mbus_code = ov02c10_enum_mbus_code, + .enum_frame_size = ov02c10_enum_frame_size, + .enable_streams = ov02c10_enable_streams, + .disable_streams = ov02c10_disable_streams, +}; + +static const struct v4l2_subdev_ops ov02c10_subdev_ops = { + .video = &ov02c10_video_ops, + .pad = &ov02c10_pad_ops, +}; + +static const struct media_entity_operations ov02c10_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ov02c10_internal_ops = { + .init_state = ov02c10_init_state, +}; + +static int ov02c10_identify_module(struct ov02c10 *ov02c10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02c10->sd); + u64 chip_id; + int ret; + + ret = cci_read(ov02c10->regmap, OV02C10_REG_CHIP_ID, &chip_id, NULL); + if (ret) + return ret; + + if (chip_id != OV02C10_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%llx", + OV02C10_CHIP_ID, chip_id); + return -ENXIO; + } + + return 0; +} + +static int ov02c10_check_hwcfg(struct device *dev, struct ov02c10 *ov02c10) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep, *fwnode = dev_fwnode(dev); + unsigned long link_freq_bitmap; + u32 mclk; + int ret; + + /* + * Sometimes the fwnode graph is initialized by the bridge driver, + * wait for this. + */ + ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0); + if (!ep) + return dev_err_probe(dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); + + ov02c10->img_clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(ov02c10->img_clk)) { + fwnode_handle_put(ep); + return dev_err_probe(dev, PTR_ERR(ov02c10->img_clk), + "failed to get imaging clock\n"); + } + + if (ov02c10->img_clk) { + mclk = clk_get_rate(ov02c10->img_clk); + } else { + ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + if (ret) { + fwnode_handle_put(ep); + return dev_err_probe(dev, ret, + "reading clock-frequency property\n"); + } + } + + if (mclk != OV02C10_MCLK) { + fwnode_handle_put(ep); + return dev_err_probe(dev, -EINVAL, + "external clock %u is not supported\n", + mclk); + } + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return dev_err_probe(dev, ret, "parsing endpoint failed\n"); + + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &link_freq_bitmap); + if (ret) + goto check_hwcfg_error; + + /* v4l2_link_freq_to_bitmap() guarantees at least 1 bit is set */ + ov02c10->link_freq_index = ffs(link_freq_bitmap) - 1; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 1 && + bus_cfg.bus.mipi_csi2.num_data_lanes != 2) { + ret = dev_err_probe(dev, -EINVAL, + "number of CSI2 data lanes %u is not supported\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + goto check_hwcfg_error; + } + + ov02c10->mipi_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + +check_hwcfg_error: + v4l2_fwnode_endpoint_free(&bus_cfg); + return ret; +} + +static void ov02c10_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) { + ov02c10_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + } +} + +static int ov02c10_probe(struct i2c_client *client) +{ + struct ov02c10 *ov02c10; + int ret; + + ov02c10 = devm_kzalloc(&client->dev, sizeof(*ov02c10), GFP_KERNEL); + if (!ov02c10) + return -ENOMEM; + + v4l2_i2c_subdev_init(&ov02c10->sd, client, &ov02c10_subdev_ops); + + /* Check HW config */ + ret = ov02c10_check_hwcfg(&client->dev, ov02c10); + if (ret) + return ret; + + ret = ov02c10_get_pm_resources(&client->dev); + if (ret) + return ret; + + ov02c10->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(ov02c10->regmap)) + return PTR_ERR(ov02c10->regmap); + + ret = ov02c10_power_on(&client->dev); + if (ret) { + dev_err_probe(&client->dev, ret, "failed to power on\n"); + return ret; + } + + ret = ov02c10_identify_module(ov02c10); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d", ret); + goto probe_error_power_off; + } + + ret = ov02c10_init_controls(ov02c10); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov02c10->sd.internal_ops = &ov02c10_internal_ops; + ov02c10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov02c10->sd.entity.ops = &ov02c10_subdev_entity_ops; + ov02c10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ov02c10->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov02c10->sd.entity, 1, &ov02c10->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov02c10->sd.state_lock = ov02c10->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&ov02c10->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to init subdev: %d", ret); + goto probe_error_media_entity_cleanup; + } + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + + ret = v4l2_async_register_subdev_sensor(&ov02c10->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_v4l2_subdev_cleanup; + } + + pm_runtime_idle(&client->dev); + return 0; + +probe_error_v4l2_subdev_cleanup: + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + v4l2_subdev_cleanup(&ov02c10->sd); + +probe_error_media_entity_cleanup: + media_entity_cleanup(&ov02c10->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(ov02c10->sd.ctrl_handler); + +probe_error_power_off: + ov02c10_power_off(&client->dev); + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ov02c10_pm_ops, ov02c10_power_off, + ov02c10_power_on, NULL); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id ov02c10_acpi_ids[] = { + { "OVTI02C1" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(acpi, ov02c10_acpi_ids); +#endif + +static const struct of_device_id ov02c10_of_match[] = { + { .compatible = "ovti,ov02c10" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov02c10_of_match); + +static struct i2c_driver ov02c10_i2c_driver = { + .driver = { + .name = "ov02c10", + .pm = pm_sleep_ptr(&ov02c10_pm_ops), + .acpi_match_table = ACPI_PTR(ov02c10_acpi_ids), + .of_match_table = ov02c10_of_match, + }, + .probe = ov02c10_probe, + .remove = ov02c10_remove, +}; + +module_i2c_driver(ov02c10_i2c_driver); + +MODULE_AUTHOR("Hao Yao "); +MODULE_AUTHOR("Heimir Thor Sverrisson "); +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("OmniVision OV02C10 sensor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 5ef6bedb29f244e4ea5e9829f174eaf05f82eb6a Mon Sep 17 00:00:00 2001 From: Sylvain Petinot Date: Fri, 2 May 2025 22:18:48 +0200 Subject: media: dt-bindings: Add ST VD56G3 camera sensor Add devicetree bindings Documentation for ST VD56G3 & ST VD66GY camera sensors. Update MAINTAINERS file. Signed-off-by: Sylvain Petinot Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../devicetree/bindings/media/i2c/st,vd56g3.yaml | 139 +++++++++++++++++++++ MAINTAINERS | 7 ++ 2 files changed, 146 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/st,vd56g3.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/st,vd56g3.yaml b/Documentation/devicetree/bindings/media/i2c/st,vd56g3.yaml new file mode 100644 index 000000000000..c6673b8539db --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/st,vd56g3.yaml @@ -0,0 +1,139 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (c) 2024 STMicroelectronics SA. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/st,vd56g3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics VD56G3 Global Shutter Image Sensor + +maintainers: + - Benjamin Mugnier + - Sylvain Petinot + +description: |- + The STMicroelectronics VD56G3 is a 1.5 M pixel global shutter image sensor + with an active array size of 1124 x 1364 (portrait orientation). It is + programmable through I2C, the address is fixed to 0x10. The sensor output is + available via CSI-2, which is configured as either 1 or 2 data lanes. The + sensor provides 8 GPIOS that can be used for external LED signal + (synchronized with sensor integration periods) + +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + +properties: + compatible: + enum: + - st,vd56g3 + - st,vd66gy + description: + Two variants are availables; VD56G3 is a monochrome sensor while VD66GY + is a colour variant. + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + vcore-supply: + description: Digital core power supply (1.15V) + + vddio-supply: + description: Digital IO power supply (1.8V) + + vana-supply: + description: Analog power supply (2.8V) + + reset-gpios: + description: Sensor reset active low GPIO (XSHUTDOWN) + maxItems: 1 + + st,leds: + description: + List sensor's GPIOs used to control strobe light sources during exposure + time. The numbers identify the sensor pin on which the illumination system + is connected. GPIOs are active-high. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 8 + items: + minimum: 0 + maximum: 7 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 2 + items: + enum: [1, 2] + + link-frequencies: + maxItems: 1 + items: + enum: [402000000, 750000000] + + lane-polarities: + minItems: 1 + maxItems: 3 + description: Any lane can be inverted or not. + + required: + - data-lanes + - link-frequencies + +required: + - compatible + - reg + - clocks + - vcore-supply + - vddio-supply + - vana-supply + - reset-gpios + - port + +unevaluatedProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + camera-sensor@10 { + compatible = "st,vd56g3"; + reg = <0x10>; + + clocks = <&camera_clk_12M>; + + vcore-supply = <&camera_vcore_v1v15>; + vddio-supply = <&camera_vddio_v1v8>; + vana-supply = <&camera_vana_v2v8>; + + reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>; + st,leds = <6>; + + orientation = <2>; + rotation = <0>; + + port { + endpoint { + data-lanes = <1 2>; + link-frequencies = /bits/ 64 <402000000>; + remote-endpoint = <&csiphy0_ep>; + }; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index a273aa905bed..d0eb5bdc9083 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22915,6 +22915,13 @@ S: Maintained F: Documentation/hwmon/stpddc60.rst F: drivers/hwmon/pmbus/stpddc60.c +ST VD56G3 IMAGE SENSOR DRIVER +M: Benjamin Mugnier +M: Sylvain Petinot +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/st,vd56g3.yaml + ST VGXY61 DRIVER M: Benjamin Mugnier M: Sylvain Petinot -- cgit v1.2.3-59-g8ed1b From 87aa97fc315759323475c3742664547830256d83 Mon Sep 17 00:00:00 2001 From: Sylvain Petinot Date: Fri, 2 May 2025 22:18:49 +0200 Subject: media: i2c: Add driver for ST VD56G3 camera sensor Add V4L2 sub-device driver for STMicroelectronics VD56G3 camera sensor. This is a 1.5 M pixel global shutter image sensor with an active array size of 1124 x 1364 (portrait orientation). The driver supports Mono (VD56G3) and Color (VD66GY) variants. Signed-off-by: Sylvain Petinot Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- MAINTAINERS | 2 + drivers/media/i2c/Kconfig | 11 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/vd56g3.c | 1586 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1600 insertions(+) create mode 100644 drivers/media/i2c/vd56g3.c diff --git a/MAINTAINERS b/MAINTAINERS index d0eb5bdc9083..32479b909ff0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22921,6 +22921,7 @@ M: Sylvain Petinot L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/i2c/st,vd56g3.yaml +F: drivers/media/i2c/vd56g3.c ST VGXY61 DRIVER M: Benjamin Mugnier @@ -25317,6 +25318,7 @@ F: drivers/media/i2c/mt* F: drivers/media/i2c/og* F: drivers/media/i2c/ov* F: drivers/media/i2c/s5* +F: drivers/media/i2c/vd56g3.c F: drivers/media/i2c/vgxy61.c VF610 NAND DRIVER diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 9b85ae654760..0621a6eb1bf5 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -712,6 +712,17 @@ config VIDEO_S5K6A3 This is a V4L2 sensor driver for Samsung S5K6A3 raw camera sensor. +config VIDEO_VD56G3 + tristate "ST VD56G3 sensor support" + select V4L2_CCI_I2C + depends on GPIOLIB + help + This is a Video4Linux2 sensor driver for the ST VD56G3 + camera sensor. + + To compile this driver as a module, choose M here: the + module will be called vd56g3. + config VIDEO_VGXY61 tristate "ST VGXY61 sensor support" select V4L2_CCI_I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 1b577e5ebc73..8eb84d4c21ff 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -155,6 +155,7 @@ obj-$(CONFIG_VIDEO_TW9910) += tw9910.o obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o +obj-$(CONFIG_VIDEO_VD56G3) += vd56g3.o obj-$(CONFIG_VIDEO_VGXY61) += vgxy61.o obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o diff --git a/drivers/media/i2c/vd56g3.c b/drivers/media/i2c/vd56g3.c new file mode 100644 index 000000000000..5d951ad0b478 --- /dev/null +++ b/drivers/media/i2c/vd56g3.c @@ -0,0 +1,1586 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for ST VD56G3 (Mono) and VD66GY (RGB) global shutter cameras. + * Copyright (C) 2024, STMicroelectronics SA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Register Map */ +#define VD56G3_REG_MODEL_ID CCI_REG16_LE(0x0000) +#define VD56G3_MODEL_ID 0x5603 +#define VD56G3_REG_REVISION CCI_REG16_LE(0x0002) +#define VD56G3_REVISION_CUT3 0x31 +#define VD56G3_REG_OPTICAL_REVISION CCI_REG8(0x001a) +#define VD56G3_OPTICAL_REVISION_MONO 0 +#define VD56G3_OPTICAL_REVISION_BAYER 1 +#define VD56G3_REG_SYSTEM_FSM CCI_REG8(0x0028) +#define VD56G3_SYSTEM_FSM_READY_TO_BOOT 0x01 +#define VD56G3_SYSTEM_FSM_SW_STBY 0x02 +#define VD56G3_SYSTEM_FSM_STREAMING 0x03 +#define VD56G3_REG_APPLIED_COARSE_EXPOSURE CCI_REG16_LE(0x0064) +#define VD56G3_REG_APPLIED_ANALOG_GAIN CCI_REG8(0x0068) +#define VD56G3_REG_APPLIED_DIGITAL_GAIN CCI_REG16_LE(0x006a) +#define VD56G3_REG_BOOT CCI_REG8(0x0200) +#define VD56G3_CMD_ACK 0 +#define VD56G3_CMD_BOOT 1 +#define VD56G3_REG_STBY CCI_REG8(0x0201) +#define VD56G3_CMD_START_STREAM 1 +#define VD56G3_REG_STREAMING CCI_REG8(0x0202) +#define VD56G3_CMD_STOP_STREAM 1 +#define VD56G3_REG_EXT_CLOCK CCI_REG32_LE(0x0220) +#define VD56G3_REG_CLK_PLL_PREDIV CCI_REG8(0x0224) +#define VD56G3_REG_CLK_SYS_PLL_MULT CCI_REG8(0x0226) +#define VD56G3_REG_ORIENTATION CCI_REG8(0x0302) +#define VD56G3_REG_FORMAT_CTRL CCI_REG8(0x030a) +#define VD56G3_REG_OIF_CTRL CCI_REG16_LE(0x030c) +#define VD56G3_REG_OIF_IMG_CTRL CCI_REG8(0x030f) +#define VD56G3_REG_OIF_CSI_BITRATE CCI_REG16_LE(0x0312) +#define VD56G3_REG_DUSTER_CTRL CCI_REG8(0x0318) +#define VD56G3_DUSTER_DISABLE 0 +#define VD56G3_DUSTER_ENABLE_DEF_MODULES 0x13 +#define VD56G3_REG_ISL_ENABLE CCI_REG8(0x0333) +#define VD56G3_REG_DARKCAL_CTRL CCI_REG8(0x0340) +#define VD56G3_DARKCAL_ENABLE 1 +#define VD56G3_DARKCAL_DISABLE_DARKAVG 2 +#define VD56G3_REG_PATGEN_CTRL CCI_REG16_LE(0x0400) +#define VD56G3_PATGEN_ENABLE 1 +#define VD56G3_PATGEN_TYPE_SHIFT 4 +#define VD56G3_REG_AE_COLDSTART_COARSE_EXPOSURE CCI_REG16_LE(0x042a) +#define VD56G3_REG_AE_COLDSTART_ANALOG_GAIN CCI_REG8(0x042c) +#define VD56G3_REG_AE_COLDSTART_DIGITAL_GAIN CCI_REG16_LE(0x042e) +#define VD56G3_REG_AE_ROI_START_H CCI_REG16_LE(0x0432) +#define VD56G3_REG_AE_ROI_START_V CCI_REG16_LE(0x0434) +#define VD56G3_REG_AE_ROI_END_H CCI_REG16_LE(0x0436) +#define VD56G3_REG_AE_ROI_END_V CCI_REG16_LE(0x0438) +#define VD56G3_REG_AE_COMPENSATION CCI_REG16_LE(0x043a) +#define VD56G3_REG_EXP_MODE CCI_REG8(0x044c) +#define VD56G3_EXP_MODE_AUTO 0 +#define VD56G3_EXP_MODE_FREEZE 1 +#define VD56G3_EXP_MODE_MANUAL 2 +#define VD56G3_REG_MANUAL_ANALOG_GAIN CCI_REG8(0x044d) +#define VD56G3_REG_MANUAL_COARSE_EXPOSURE CCI_REG16_LE(0x044e) +#define VD56G3_REG_MANUAL_DIGITAL_GAIN_CH0 CCI_REG16_LE(0x0450) +#define VD56G3_REG_MANUAL_DIGITAL_GAIN_CH1 CCI_REG16_LE(0x0452) +#define VD56G3_REG_MANUAL_DIGITAL_GAIN_CH2 CCI_REG16_LE(0x0454) +#define VD56G3_REG_MANUAL_DIGITAL_GAIN_CH3 CCI_REG16_LE(0x0456) +#define VD56G3_REG_FRAME_LENGTH CCI_REG16_LE(0x0458) +#define VD56G3_REG_Y_START CCI_REG16_LE(0x045a) +#define VD56G3_REG_Y_END CCI_REG16_LE(0x045c) +#define VD56G3_REG_OUT_ROI_X_START CCI_REG16_LE(0x045e) +#define VD56G3_REG_OUT_ROI_X_END CCI_REG16_LE(0x0460) +#define VD56G3_REG_OUT_ROI_Y_START CCI_REG16_LE(0x0462) +#define VD56G3_REG_OUT_ROI_Y_END CCI_REG16_LE(0x0464) +#define VD56G3_REG_GPIO_0_CTRL CCI_REG8(0x0467) +#define VD56G3_GPIOX_GPIO_IN 0x01 +#define VD56G3_GPIOX_STROBE_MODE 0x02 +#define VD56G3_REG_READOUT_CTRL CCI_REG8(0x047e) +#define READOUT_NORMAL 0x00 +#define READOUT_DIGITAL_BINNING_X2 0x01 + +/* The VD56G3 is a portrait image sensor with native resolution of 1124x1364. */ +#define VD56G3_NATIVE_WIDTH 1124 +#define VD56G3_NATIVE_HEIGHT 1364 +#define VD56G3_DEFAULT_MODE 0 + +/* PLL settings */ +#define VD56G3_TARGET_PLL 804000000UL +#define VD56G3_VT_CLOCK_DIV 5 + +/* External clock must be in [6Mhz-27Mhz] */ +#define VD56G3_XCLK_FREQ_MIN (6 * HZ_PER_MHZ) +#define VD56G3_XCLK_FREQ_MAX (27 * HZ_PER_MHZ) + +/* Line length and Frame length (settings are for standard 10bits ADC mode) */ +#define VD56G3_LINE_LENGTH_MIN 1236 +#define VD56G3_VBLANK_MIN 110 +#define VD56G3_FRAME_LENGTH_DEF_60FPS 2168 +#define VD56G3_FRAME_LENGTH_MAX 0xffff + +/* Exposure settings */ +#define VD56G3_EXPOSURE_MARGIN 75 +#define VD56G3_EXPOSURE_MIN 5 +#define VD56G3_EXPOSURE_DEFAULT 1420 + +/* Output Interface settings */ +#define VD56G3_MAX_CSI_DATA_LANES 2 +#define VD56G3_LINK_FREQ_DEF_1LANE 750000000UL +#define VD56G3_LINK_FREQ_DEF_2LANES 402000000UL + +/* GPIOs */ +#define VD56G3_NB_GPIOS 8 + +/* regulator supplies */ +static const char *const vd56g3_supply_names[] = { + "vcore", + "vddio", + "vana", +}; + +/* ----------------------------------------------------------------------------- + * Models (VD56G3: Mono, VD66GY: Bayer RGB), Modes and formats + */ + +enum vd56g3_models { + VD56G3_MODEL_VD56G3, + VD56G3_MODEL_VD66GY, +}; + +struct vd56g3_mode { + u32 width; + u32 height; +}; + +static const struct vd56g3_mode vd56g3_supported_modes[] = { + { + .width = VD56G3_NATIVE_WIDTH, + .height = VD56G3_NATIVE_HEIGHT, + }, + { + .width = 1120, + .height = 1360, + }, + { + .width = 1024, + .height = 1280, + }, + { + .width = 1024, + .height = 768, + }, + { + .width = 768, + .height = 1024, + }, + { + .width = 720, + .height = 1280, + }, + { + .width = 640, + .height = 480, + }, + { + .width = 480, + .height = 640, + }, + { + .width = 320, + .height = 240, + }, +}; + +/* + * Sensor support 8bits and 10bits output in both variants + * - Monochrome + * - RGB (with all H/V flip variations) + */ +static const unsigned int vd56g3_mbus_codes[2][5] = { + { + MEDIA_BUS_FMT_Y8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + }, +}; + +struct vd56g3 { + struct device *dev; + struct v4l2_subdev sd; + struct media_pad pad; + struct regulator_bulk_data supplies[ARRAY_SIZE(vd56g3_supply_names)]; + struct gpio_desc *reset_gpio; + struct clk *xclk; + struct regmap *regmap; + u32 xclk_freq; + u32 pll_prediv; + u32 pll_mult; + u32 pixel_clock; + u16 oif_ctrl; + u8 nb_of_lane; + u32 gpios[VD56G3_NB_GPIOS]; + unsigned long ext_leds_mask; + bool is_mono; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *hblank_ctrl; + struct v4l2_ctrl *vblank_ctrl; + struct { + struct v4l2_ctrl *hflip_ctrl; + struct v4l2_ctrl *vflip_ctrl; + }; + struct v4l2_ctrl *patgen_ctrl; + struct { + struct v4l2_ctrl *ae_ctrl; + struct v4l2_ctrl *expo_ctrl; + struct v4l2_ctrl *again_ctrl; + struct v4l2_ctrl *dgain_ctrl; + }; + struct v4l2_ctrl *ae_lock_ctrl; + struct v4l2_ctrl *ae_bias_ctrl; + struct v4l2_ctrl *led_ctrl; +}; + +static inline struct vd56g3 *to_vd56g3(struct v4l2_subdev *sd) +{ + return container_of_const(sd, struct vd56g3, sd); +} + +static inline struct vd56g3 *ctrl_to_vd56g3(struct v4l2_ctrl *ctrl) +{ + return container_of_const(ctrl->handler, struct vd56g3, ctrl_handler); +} + +/* ----------------------------------------------------------------------------- + * Additional i2c register helpers + */ + +static int vd56g3_poll_reg(struct vd56g3 *sensor, u32 reg, u8 poll_val, + int *err) +{ + unsigned int val = 0; + int ret; + + if (err && *err) + return *err; + + /* + * Timeout must be higher than longuest frame duration. With current + * blanking constraints, frame duration can take up to 504ms. + */ + ret = regmap_read_poll_timeout(sensor->regmap, CCI_REG_ADDR(reg), val, + (val == poll_val), 2000, + 600 * USEC_PER_MSEC); + + if (ret && err) + *err = ret; + + return ret; +} + +static int vd56g3_wait_state(struct vd56g3 *sensor, int state, int *err) +{ + return vd56g3_poll_reg(sensor, VD56G3_REG_SYSTEM_FSM, state, err); +} + +/* ----------------------------------------------------------------------------- + * Controls: definitions, helpers and handlers + */ + +static const char *const vd56g3_tp_menu[] = { "Disabled", + "Solid Color", + "Vertical Color Bars", + "Horizontal Gray Scale", + "Vertical Gray Scale", + "Diagonal Gray Scale", + "Pseudo Random" }; + +static const s64 vd56g3_ev_bias_qmenu[] = { -4000, -3500, -3000, -2500, -2000, + -1500, -1000, -500, 0, 500, + 1000, 1500, 2000, 2500, 3000, + 3500, 4000 }; + +static const s64 vd56g3_link_freq_1lane[] = { VD56G3_LINK_FREQ_DEF_1LANE }; + +static const s64 vd56g3_link_freq_2lanes[] = { VD56G3_LINK_FREQ_DEF_2LANES }; + +static u8 vd56g3_get_bpp(__u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + default: + return 8; + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + return 10; + } +} + +static u8 vd56g3_get_datatype(__u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + default: + return MIPI_CSI2_DT_RAW8; + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + return MIPI_CSI2_DT_RAW10; + } +} + +static int vd56g3_read_expo_cluster(struct vd56g3 *sensor, bool force_cur_val) +{ + u64 exposure; + u64 again; + u64 dgain; + int ret = 0; + + /* + * When 'force_cur_val' is enabled, save the ctrl value in 'cur.val' + * instead of the normal 'val', this is used during poweroff to cache + * volatile ctrls and enable coldstart. + */ + cci_read(sensor->regmap, VD56G3_REG_APPLIED_COARSE_EXPOSURE, &exposure, + &ret); + cci_read(sensor->regmap, VD56G3_REG_APPLIED_ANALOG_GAIN, &again, &ret); + cci_read(sensor->regmap, VD56G3_REG_APPLIED_DIGITAL_GAIN, &dgain, &ret); + if (ret) + return ret; + + if (force_cur_val) { + sensor->expo_ctrl->cur.val = exposure; + sensor->again_ctrl->cur.val = again; + sensor->dgain_ctrl->cur.val = dgain; + } else { + sensor->expo_ctrl->val = exposure; + sensor->again_ctrl->val = again; + sensor->dgain_ctrl->val = dgain; + } + + return ret; +} + +static int vd56g3_update_patgen(struct vd56g3 *sensor, u32 patgen_index) +{ + u32 pattern = patgen_index <= 2 ? patgen_index : patgen_index + 13; + u16 patgen = pattern << VD56G3_PATGEN_TYPE_SHIFT; + u8 duster = VD56G3_DUSTER_ENABLE_DEF_MODULES; + u8 darkcal = VD56G3_DARKCAL_ENABLE; + int ret = 0; + + if (patgen_index) { + patgen |= VD56G3_PATGEN_ENABLE; + duster = VD56G3_DUSTER_DISABLE; + darkcal = VD56G3_DARKCAL_DISABLE_DARKAVG; + } + + cci_write(sensor->regmap, VD56G3_REG_DUSTER_CTRL, duster, &ret); + cci_write(sensor->regmap, VD56G3_REG_DARKCAL_CTRL, darkcal, &ret); + cci_write(sensor->regmap, VD56G3_REG_PATGEN_CTRL, patgen, &ret); + + return ret; +} + +static int vd56g3_update_expo_cluster(struct vd56g3 *sensor, bool is_auto) +{ + u8 expo_state = is_auto ? VD56G3_EXP_MODE_AUTO : VD56G3_EXP_MODE_MANUAL; + int ret = 0; + + if (sensor->ae_ctrl->is_new) + cci_write(sensor->regmap, VD56G3_REG_EXP_MODE, expo_state, + &ret); + + /* In Auto expo, set coldstart parameters */ + if (is_auto && sensor->ae_ctrl->is_new) { + cci_write(sensor->regmap, + VD56G3_REG_AE_COLDSTART_COARSE_EXPOSURE, + sensor->expo_ctrl->val, &ret); + cci_write(sensor->regmap, VD56G3_REG_AE_COLDSTART_ANALOG_GAIN, + sensor->again_ctrl->val, &ret); + cci_write(sensor->regmap, VD56G3_REG_AE_COLDSTART_DIGITAL_GAIN, + sensor->dgain_ctrl->val, &ret); + } + + /* In Manual expo, set exposure, analog and digital gains */ + if (!is_auto && sensor->expo_ctrl->is_new) + cci_write(sensor->regmap, VD56G3_REG_MANUAL_COARSE_EXPOSURE, + sensor->expo_ctrl->val, &ret); + + if (!is_auto && sensor->again_ctrl->is_new) + cci_write(sensor->regmap, VD56G3_REG_MANUAL_ANALOG_GAIN, + sensor->again_ctrl->val, &ret); + + if (!is_auto && sensor->dgain_ctrl->is_new) { + cci_write(sensor->regmap, VD56G3_REG_MANUAL_DIGITAL_GAIN_CH0, + sensor->dgain_ctrl->val, &ret); + cci_write(sensor->regmap, VD56G3_REG_MANUAL_DIGITAL_GAIN_CH1, + sensor->dgain_ctrl->val, &ret); + cci_write(sensor->regmap, VD56G3_REG_MANUAL_DIGITAL_GAIN_CH2, + sensor->dgain_ctrl->val, &ret); + cci_write(sensor->regmap, VD56G3_REG_MANUAL_DIGITAL_GAIN_CH3, + sensor->dgain_ctrl->val, &ret); + } + + return ret; +} + +static int vd56g3_lock_exposure(struct vd56g3 *sensor, u32 lock_val) +{ + bool ae_lock = lock_val & V4L2_LOCK_EXPOSURE; + u8 expo_state = ae_lock ? VD56G3_EXP_MODE_FREEZE : VD56G3_EXP_MODE_AUTO; + + if (sensor->ae_ctrl->val == V4L2_EXPOSURE_AUTO) + return cci_write(sensor->regmap, VD56G3_REG_EXP_MODE, + expo_state, NULL); + + return 0; +} + +static int vd56g3_write_gpiox(struct vd56g3 *sensor, unsigned long gpio_mask) +{ + unsigned long io; + u32 gpio_val; + int ret = 0; + + for_each_set_bit(io, &gpio_mask, VD56G3_NB_GPIOS) { + gpio_val = sensor->gpios[io]; + + if (gpio_val == VD56G3_GPIOX_STROBE_MODE && + sensor->led_ctrl->val == V4L2_FLASH_LED_MODE_NONE) + gpio_val = VD56G3_GPIOX_GPIO_IN; + + cci_write(sensor->regmap, VD56G3_REG_GPIO_0_CTRL + io, gpio_val, + &ret); + } + + return ret; +} + +static int vd56g3_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vd56g3 *sensor = ctrl_to_vd56g3(ctrl); + int ret = 0; + + /* Interact with HW only when it is powered ON */ + if (!pm_runtime_get_if_in_use(sensor->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE_AUTO: + ret = vd56g3_read_expo_cluster(sensor, false); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_mark_last_busy(sensor->dev); + pm_runtime_put_autosuspend(sensor->dev); + + return ret; +} + +static int vd56g3_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vd56g3 *sensor = ctrl_to_vd56g3(ctrl); + struct v4l2_subdev_state *state; + const struct v4l2_rect *crop; + unsigned int frame_length = 0; + unsigned int expo_max; + unsigned int ae_compensation; + bool is_auto = false; + int ret = 0; + + state = v4l2_subdev_get_locked_active_state(&sensor->sd); + crop = v4l2_subdev_state_get_crop(state, 0); + + if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) + return 0; + + /* Update controls state, range, etc. whatever the state of the HW */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + frame_length = crop->height + ctrl->val; + expo_max = frame_length - VD56G3_EXPOSURE_MARGIN; + ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, + VD56G3_EXPOSURE_MIN, expo_max, 1, + min(VD56G3_EXPOSURE_DEFAULT, + expo_max)); + break; + case V4L2_CID_EXPOSURE_AUTO: + is_auto = (ctrl->val == V4L2_EXPOSURE_AUTO); + __v4l2_ctrl_grab(sensor->ae_lock_ctrl, !is_auto); + __v4l2_ctrl_grab(sensor->ae_bias_ctrl, !is_auto); + break; + default: + break; + } + + if (ret) + return ret; + + /* Interact with HW only when it is powered ON */ + if (!pm_runtime_get_if_in_use(sensor->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + ret = cci_write(sensor->regmap, VD56G3_REG_ORIENTATION, + sensor->hflip_ctrl->val | + (sensor->vflip_ctrl->val << 1), + NULL); + break; + case V4L2_CID_TEST_PATTERN: + ret = vd56g3_update_patgen(sensor, ctrl->val); + break; + case V4L2_CID_EXPOSURE_AUTO: + ret = vd56g3_update_expo_cluster(sensor, is_auto); + break; + case V4L2_CID_3A_LOCK: + ret = vd56g3_lock_exposure(sensor, ctrl->val); + break; + case V4L2_CID_AUTO_EXPOSURE_BIAS: + ae_compensation = + DIV_ROUND_CLOSEST((int)vd56g3_ev_bias_qmenu[ctrl->val] * + 256, 1000); + ret = cci_write(sensor->regmap, VD56G3_REG_AE_COMPENSATION, + ae_compensation, NULL); + break; + case V4L2_CID_VBLANK: + ret = cci_write(sensor->regmap, VD56G3_REG_FRAME_LENGTH, + frame_length, NULL); + break; + case V4L2_CID_FLASH_LED_MODE: + ret = vd56g3_write_gpiox(sensor, sensor->ext_leds_mask); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_mark_last_busy(sensor->dev); + pm_runtime_put_autosuspend(sensor->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops vd56g3_ctrl_ops = { + .g_volatile_ctrl = vd56g3_g_volatile_ctrl, + .s_ctrl = vd56g3_s_ctrl, +}; + +static int vd56g3_update_controls(struct vd56g3 *sensor) +{ + struct v4l2_subdev_state *state; + const struct v4l2_rect *crop; + unsigned int hblank; + unsigned int vblank_min, vblank, vblank_max; + unsigned int frame_length; + unsigned int expo_max; + int ret; + + state = v4l2_subdev_get_locked_active_state(&sensor->sd); + crop = v4l2_subdev_state_get_crop(state, 0); + hblank = VD56G3_LINE_LENGTH_MIN - crop->width; + vblank_min = VD56G3_VBLANK_MIN; + vblank = VD56G3_FRAME_LENGTH_DEF_60FPS - crop->height; + vblank_max = VD56G3_FRAME_LENGTH_MAX - crop->height; + frame_length = crop->height + vblank; + expo_max = frame_length - VD56G3_EXPOSURE_MARGIN; + + /* Update blanking and exposure (ranges + values) */ + ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl, hblank, hblank, 1, + hblank); + if (ret) + return ret; + + ret = __v4l2_ctrl_modify_range(sensor->vblank_ctrl, vblank_min, + vblank_max, 1, vblank); + if (ret) + return ret; + + ret = __v4l2_ctrl_s_ctrl(sensor->vblank_ctrl, vblank); + if (ret) + return ret; + + ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, VD56G3_EXPOSURE_MIN, + expo_max, 1, VD56G3_EXPOSURE_DEFAULT); + if (ret) + return ret; + + return __v4l2_ctrl_s_ctrl(sensor->expo_ctrl, VD56G3_EXPOSURE_DEFAULT); +} + +static int vd56g3_init_controls(struct vd56g3 *sensor) +{ + const struct v4l2_ctrl_ops *ops = &vd56g3_ctrl_ops; + struct v4l2_ctrl_handler *hdl = &sensor->ctrl_handler; + struct v4l2_fwnode_device_properties fwnode_props; + struct v4l2_ctrl *ctrl; + int ret; + + v4l2_ctrl_handler_init(hdl, 25); + + /* Horizontal & vertical flips modify bayer code on RGB variant */ + sensor->hflip_ctrl = + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + if (sensor->hflip_ctrl) + sensor->hflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + sensor->vflip_ctrl = + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + if (sensor->vflip_ctrl) + sensor->vflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + sensor->patgen_ctrl = + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(vd56g3_tp_menu) - 1, 0, + 0, vd56g3_tp_menu); + + ctrl = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, + ARRAY_SIZE(vd56g3_link_freq_1lane) - 1, 0, + (sensor->nb_of_lane == 2) ? + vd56g3_link_freq_2lanes : + vd56g3_link_freq_1lane); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, + sensor->pixel_clock, sensor->pixel_clock, 1, + sensor->pixel_clock); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + sensor->ae_ctrl = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + + sensor->ae_lock_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_3A_LOCK, 0, + GENMASK(2, 0), 0, 0); + + sensor->ae_bias_ctrl = + v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(vd56g3_ev_bias_qmenu) - 1, + ARRAY_SIZE(vd56g3_ev_bias_qmenu) / 2, + vd56g3_ev_bias_qmenu); + + /* + * Analog gain [1, 8] is computed with the following logic : + * 32/(32 - again_reg), with again_reg in the range [0:28] + * Digital gain [1.00, 8.00] is coded as a Fixed Point 5.8 + */ + sensor->again_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, + 0, 28, 1, 0); + sensor->dgain_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN, + 0x100, 0x800, 1, 0x100); + + /* + * Set the exposure, horizontal and vertical blanking ctrls + * to hardcoded values, they will be updated in vd56g3_update_controls. + * Exposure being in an auto-cluster, set a significant value here. + */ + sensor->expo_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, + VD56G3_EXPOSURE_DEFAULT, + VD56G3_EXPOSURE_DEFAULT, 1, + VD56G3_EXPOSURE_DEFAULT); + sensor->hblank_ctrl = + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, 1, 1, 1, 1); + if (sensor->hblank_ctrl) + sensor->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->vblank_ctrl = + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, 1, 1, 1, 1); + + /* Additional control based on device tree properties */ + if (sensor->ext_leds_mask) + sensor->led_ctrl = + v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_FLASH_LED_MODE, + V4L2_FLASH_LED_MODE_FLASH, 0, + V4L2_FLASH_LED_MODE_NONE); + + if (hdl->error) { + ret = hdl->error; + goto free_ctrls; + } + + v4l2_ctrl_cluster(2, &sensor->hflip_ctrl); + v4l2_ctrl_auto_cluster(4, &sensor->ae_ctrl, V4L2_EXPOSURE_MANUAL, true); + + /* Optional controls coming from fwnode (e.g. rotation, orientation). */ + ret = v4l2_fwnode_device_parse(sensor->dev, &fwnode_props); + if (ret) + goto free_ctrls; + + ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &fwnode_props); + if (ret) + goto free_ctrls; + + sensor->sd.ctrl_handler = hdl; + + return 0; + +free_ctrls: + v4l2_ctrl_handler_free(hdl); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Pad ops + */ + +/* Media bus code is dependent of : + * - 8bits or 10bits output + * - variant : Mono or RGB + * - H/V flips parameters in case of RGB + */ +static u32 vd56g3_get_mbus_code(struct vd56g3 *sensor, u32 code) +{ + unsigned int i_bpp; + unsigned int j; + + for (i_bpp = 0; i_bpp < ARRAY_SIZE(vd56g3_mbus_codes); i_bpp++) { + for (j = 0; j < ARRAY_SIZE(vd56g3_mbus_codes[i_bpp]); j++) { + if (vd56g3_mbus_codes[i_bpp][j] == code) + goto endloops; + } + } + +endloops: + if (i_bpp >= ARRAY_SIZE(vd56g3_mbus_codes)) + i_bpp = 0; + + if (sensor->is_mono) + j = 0; + else + j = 1 + (sensor->hflip_ctrl->val ? 1 : 0) + + (sensor->vflip_ctrl->val ? 2 : 0); + + return vd56g3_mbus_codes[i_bpp][j]; +} + +static int vd56g3_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct vd56g3 *sensor = to_vd56g3(sd); + + if (code->index >= ARRAY_SIZE(vd56g3_mbus_codes)) + return -EINVAL; + + code->code = + vd56g3_get_mbus_code(sensor, vd56g3_mbus_codes[code->index][0]); + + return 0; +} + +static int vd56g3_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(vd56g3_supported_modes)) + return -EINVAL; + + fse->min_width = vd56g3_supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = vd56g3_supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static void vd56g3_update_img_pad_format(struct vd56g3 *sensor, + const struct vd56g3_mode *mode, + u32 mbus_code, + struct v4l2_mbus_framefmt *mbus_fmt) +{ + mbus_fmt->width = mode->width; + mbus_fmt->height = mode->height; + mbus_fmt->code = vd56g3_get_mbus_code(sensor, mbus_code); + mbus_fmt->colorspace = V4L2_COLORSPACE_RAW; + mbus_fmt->field = V4L2_FIELD_NONE; + mbus_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mbus_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + mbus_fmt->xfer_func = V4L2_XFER_FUNC_NONE; +} + +static int vd56g3_set_pad_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *sd_fmt) +{ + struct vd56g3 *sensor = to_vd56g3(sd); + const struct vd56g3_mode *new_mode; + struct v4l2_rect pad_crop; + unsigned int binning; + + new_mode = v4l2_find_nearest_size(vd56g3_supported_modes, + ARRAY_SIZE(vd56g3_supported_modes), + width, height, sd_fmt->format.width, + sd_fmt->format.height); + + vd56g3_update_img_pad_format(sensor, new_mode, sd_fmt->format.code, + &sd_fmt->format); + *v4l2_subdev_state_get_format(sd_state, sd_fmt->pad) = sd_fmt->format; + + /* Compute and update crop rectangle (maximized via binning) */ + binning = min(VD56G3_NATIVE_WIDTH / sd_fmt->format.width, + VD56G3_NATIVE_HEIGHT / sd_fmt->format.height); + binning = min(binning, 2U); + pad_crop.width = sd_fmt->format.width * binning; + pad_crop.height = sd_fmt->format.height * binning; + pad_crop.left = (VD56G3_NATIVE_WIDTH - pad_crop.width) / 2; + pad_crop.top = (VD56G3_NATIVE_HEIGHT - pad_crop.height) / 2; + *v4l2_subdev_state_get_crop(sd_state, sd_fmt->pad) = pad_crop; + + /* Update controls in case of active state */ + if (sd_fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return vd56g3_update_controls(sensor); + + return 0; +} + +static int vd56g3_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); + break; + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = VD56G3_NATIVE_WIDTH; + sel->r.height = VD56G3_NATIVE_HEIGHT; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vd56g3_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct v4l2_subdev_state *state; + const struct v4l2_mbus_framefmt *format; + + state = v4l2_subdev_lock_and_get_active_state(sd); + format = v4l2_subdev_state_get_format(state, pad); + v4l2_subdev_unlock_state(state); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + fd->num_entries = 1; + fd->entry[0].pixelcode = format->code; + fd->entry[0].stream = 0; + fd->entry[0].bus.csi2.vc = 0; + fd->entry[0].bus.csi2.dt = vd56g3_get_datatype(format->code); + + return 0; +} + +static int vd56g3_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct vd56g3 *sensor = to_vd56g3(sd); + const struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 0); + const struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0); + unsigned int csi_mbps = ((sensor->nb_of_lane == 2) ? + VD56G3_LINK_FREQ_DEF_2LANES : + VD56G3_LINK_FREQ_DEF_1LANE) * + 2 / MEGA; + unsigned int binning; + int ret; + + ret = pm_runtime_resume_and_get(sensor->dev); + if (ret < 0) + return ret; + + /* configure clocks */ + cci_write(sensor->regmap, VD56G3_REG_EXT_CLOCK, sensor->xclk_freq, + &ret); + cci_write(sensor->regmap, VD56G3_REG_CLK_PLL_PREDIV, sensor->pll_prediv, + &ret); + cci_write(sensor->regmap, VD56G3_REG_CLK_SYS_PLL_MULT, sensor->pll_mult, + &ret); + + /* configure output */ + cci_write(sensor->regmap, VD56G3_REG_FORMAT_CTRL, + vd56g3_get_bpp(format->code), &ret); + cci_write(sensor->regmap, VD56G3_REG_OIF_CTRL, sensor->oif_ctrl, &ret); + cci_write(sensor->regmap, VD56G3_REG_OIF_CSI_BITRATE, csi_mbps, &ret); + cci_write(sensor->regmap, VD56G3_REG_OIF_IMG_CTRL, + vd56g3_get_datatype(format->code), &ret); + cci_write(sensor->regmap, VD56G3_REG_ISL_ENABLE, 0, &ret); + + /* configure binning mode */ + switch (crop->width / format->width) { + case 1: + default: + binning = READOUT_NORMAL; + break; + case 2: + binning = READOUT_DIGITAL_BINNING_X2; + break; + } + cci_write(sensor->regmap, VD56G3_REG_READOUT_CTRL, binning, &ret); + + /* configure ROIs */ + cci_write(sensor->regmap, VD56G3_REG_Y_START, crop->top, &ret); + cci_write(sensor->regmap, VD56G3_REG_Y_END, + crop->top + crop->height - 1, &ret); + cci_write(sensor->regmap, VD56G3_REG_OUT_ROI_X_START, crop->left, &ret); + cci_write(sensor->regmap, VD56G3_REG_OUT_ROI_X_END, + crop->left + crop->width - 1, &ret); + cci_write(sensor->regmap, VD56G3_REG_OUT_ROI_Y_START, 0, &ret); + cci_write(sensor->regmap, VD56G3_REG_OUT_ROI_Y_END, crop->height - 1, + &ret); + cci_write(sensor->regmap, VD56G3_REG_AE_ROI_START_H, crop->left, &ret); + cci_write(sensor->regmap, VD56G3_REG_AE_ROI_END_H, + crop->left + crop->width - 1, &ret); + cci_write(sensor->regmap, VD56G3_REG_AE_ROI_START_V, 0, &ret); + cci_write(sensor->regmap, VD56G3_REG_AE_ROI_END_V, crop->height - 1, + &ret); + if (ret) + goto rpm_put; + + /* Setup default GPIO values; could be overridden by V4L2 ctrl setup */ + ret = vd56g3_write_gpiox(sensor, GENMASK(VD56G3_NB_GPIOS - 1, 0)); + if (ret) + goto rpm_put; + + /* Apply settings from V4L2 ctrls */ + ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler); + if (ret) + goto rpm_put; + + /* start streaming */ + cci_write(sensor->regmap, VD56G3_REG_STBY, VD56G3_CMD_START_STREAM, + &ret); + vd56g3_poll_reg(sensor, VD56G3_REG_STBY, VD56G3_CMD_ACK, &ret); + vd56g3_wait_state(sensor, VD56G3_SYSTEM_FSM_STREAMING, &ret); + if (ret) + goto rpm_put; + + /* some controls are locked during streaming */ + __v4l2_ctrl_grab(sensor->hflip_ctrl, true); + __v4l2_ctrl_grab(sensor->vflip_ctrl, true); + __v4l2_ctrl_grab(sensor->patgen_ctrl, true); + + return ret; + +rpm_put: + dev_err(sensor->dev, "Failed to start streaming\n"); + pm_runtime_put_sync(sensor->dev); + + return ret; +} + +static int vd56g3_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct vd56g3 *sensor = to_vd56g3(sd); + int ret; + + /* Retrieve Expo cluster to enable coldstart of AE */ + ret = vd56g3_read_expo_cluster(sensor, true); + + cci_write(sensor->regmap, VD56G3_REG_STREAMING, VD56G3_CMD_STOP_STREAM, + &ret); + vd56g3_poll_reg(sensor, VD56G3_REG_STREAMING, VD56G3_CMD_ACK, &ret); + vd56g3_wait_state(sensor, VD56G3_SYSTEM_FSM_SW_STBY, &ret); + + /* locked controls must be unlocked */ + __v4l2_ctrl_grab(sensor->hflip_ctrl, false); + __v4l2_ctrl_grab(sensor->vflip_ctrl, false); + __v4l2_ctrl_grab(sensor->patgen_ctrl, false); + + pm_runtime_mark_last_busy(sensor->dev); + pm_runtime_put_autosuspend(sensor->dev); + + return ret; +} + +static int vd56g3_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + unsigned int def_mode = VD56G3_DEFAULT_MODE; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = 0, + .format = { + .code = vd56g3_mbus_codes[0][0], + .width = vd56g3_supported_modes[def_mode].width, + .height = vd56g3_supported_modes[def_mode].height, + }, + }; + + return vd56g3_set_pad_fmt(sd, sd_state, &fmt); +} + +static const struct v4l2_subdev_video_ops vd56g3_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops vd56g3_pad_ops = { + .enum_mbus_code = vd56g3_enum_mbus_code, + .enum_frame_size = vd56g3_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = vd56g3_set_pad_fmt, + .get_selection = vd56g3_get_selection, + .get_frame_desc = vd56g3_get_frame_desc, + .enable_streams = vd56g3_enable_streams, + .disable_streams = vd56g3_disable_streams, +}; + +static const struct v4l2_subdev_ops vd56g3_subdev_ops = { + .video = &vd56g3_video_ops, + .pad = &vd56g3_pad_ops, +}; + +static const struct media_entity_operations vd56g3_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops vd56g3_internal_ops = { + .init_state = vd56g3_init_state, +}; + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int vd56g3_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct vd56g3 *sensor = to_vd56g3(sd); + int ret; + + /* power on */ + ret = regulator_bulk_enable(ARRAY_SIZE(sensor->supplies), + sensor->supplies); + if (ret) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(sensor->xclk); + if (ret) { + dev_err(dev, "Failed to enable clock: %d\n", ret); + goto disable_reg; + } + + gpiod_set_value_cansleep(sensor->reset_gpio, 0); + usleep_range(3500, 4000); + ret = vd56g3_wait_state(sensor, VD56G3_SYSTEM_FSM_READY_TO_BOOT, NULL); + if (ret) { + dev_err(dev, "Sensor reset failed: %d\n", ret); + goto disable_clock; + } + + /* boot sensor */ + cci_write(sensor->regmap, VD56G3_REG_BOOT, VD56G3_CMD_BOOT, &ret); + vd56g3_poll_reg(sensor, VD56G3_REG_BOOT, VD56G3_CMD_ACK, &ret); + vd56g3_wait_state(sensor, VD56G3_SYSTEM_FSM_SW_STBY, &ret); + if (ret) { + dev_err(dev, "Sensor boot failed: %d\n", ret); + goto disable_clock; + } + + return 0; + +disable_clock: + gpiod_set_value_cansleep(sensor->reset_gpio, 1); + clk_disable_unprepare(sensor->xclk); +disable_reg: + regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies); + + return ret; +} + +static int vd56g3_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct vd56g3 *sensor = to_vd56g3(sd); + + gpiod_set_value_cansleep(sensor->reset_gpio, 1); + clk_disable_unprepare(sensor->xclk); + regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies); + + return 0; +} + +static const struct dev_pm_ops vd56g3_pm_ops = { + SET_RUNTIME_PM_OPS(vd56g3_power_off, vd56g3_power_on, NULL) +}; + +/* ----------------------------------------------------------------------------- + * Probe and initialization + */ + +static int vd56g3_check_csi_conf(struct vd56g3 *sensor, + struct fwnode_handle *endpoint) +{ + struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + u32 phy_data_lanes[VD56G3_MAX_CSI_DATA_LANES] = { ~0, ~0 }; + u8 n_lanes; + u64 frequency; + int p, l; + int ret = 0; + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); + if (ret) + return -EINVAL; + + /* Check lanes number */ + n_lanes = ep.bus.mipi_csi2.num_data_lanes; + if (n_lanes != 1 && n_lanes != 2) { + dev_err(sensor->dev, "Invalid data lane number: %d\n", n_lanes); + ret = -EINVAL; + goto done; + } + sensor->nb_of_lane = n_lanes; + + /* Clock lane must be first */ + if (ep.bus.mipi_csi2.clock_lane != 0) { + dev_err(sensor->dev, "Clock lane must be mapped to lane 0\n"); + ret = -EINVAL; + goto done; + } + + /* + * Prepare Output Interface conf based on lane settings + * logical to physical lane conversion (+ pad remaining slots) + */ + for (l = 0; l < n_lanes; l++) + phy_data_lanes[ep.bus.mipi_csi2.data_lanes[l] - 1] = l; + for (p = 0; p < VD56G3_MAX_CSI_DATA_LANES; p++) { + if (phy_data_lanes[p] != ~0) + continue; + phy_data_lanes[p] = l; + l++; + } + sensor->oif_ctrl = n_lanes | + (ep.bus.mipi_csi2.lane_polarities[0] << 3) | + ((phy_data_lanes[0]) << 4) | + (ep.bus.mipi_csi2.lane_polarities[1] << 6) | + ((phy_data_lanes[1]) << 7) | + (ep.bus.mipi_csi2.lane_polarities[2] << 9); + + /* Check link frequency */ + if (!ep.nr_of_link_frequencies) { + dev_err(sensor->dev, "link-frequency not found in DT\n"); + ret = -EINVAL; + goto done; + } + frequency = (n_lanes == 2) ? VD56G3_LINK_FREQ_DEF_2LANES : + VD56G3_LINK_FREQ_DEF_1LANE; + if (ep.nr_of_link_frequencies != 1 || + ep.link_frequencies[0] != frequency) { + dev_err(sensor->dev, "Link frequency not supported: %lld\n", + ep.link_frequencies[0]); + ret = -EINVAL; + goto done; + } + +done: + v4l2_fwnode_endpoint_free(&ep); + + return ret; +} + +static int vd56g3_parse_dt_gpios_array(struct vd56g3 *sensor, char *prop_name, + u32 *array, unsigned int *nb) +{ + struct device *dev = sensor->dev; + unsigned int i; + int ret; + + if (!device_property_present(dev, prop_name)) { + *nb = 0; + return 0; + } + + ret = device_property_count_u32(dev, prop_name); + if (ret < 0) { + dev_err(dev, "Failed to read %s count\n", prop_name); + return ret; + } + + *nb = ret; + ret = device_property_read_u32_array(dev, prop_name, array, *nb); + if (ret) { + dev_err(dev, "Failed to read %s prop\n", prop_name); + return ret; + } + + for (i = 0; i < *nb; i++) { + if (array[i] >= VD56G3_NB_GPIOS) { + dev_err(dev, "Invalid GPIO: %d\n", array[i]); + return -EINVAL; + } + } + + return 0; +} + +static int vd56g3_parse_dt_gpios(struct vd56g3 *sensor) +{ + u32 led_gpios[VD56G3_NB_GPIOS]; + unsigned int nb_gpios_leds; + unsigned int i; + int ret; + + /* Initialize GPIOs to default */ + for (i = 0; i < VD56G3_NB_GPIOS; i++) + sensor->gpios[i] = VD56G3_GPIOX_GPIO_IN; + sensor->ext_leds_mask = 0; + + /* Take into account optional 'st,leds' output for GPIOs */ + ret = vd56g3_parse_dt_gpios_array(sensor, "st,leds", led_gpios, + &nb_gpios_leds); + if (ret) + return ret; + for (i = 0; i < nb_gpios_leds; i++) { + sensor->gpios[led_gpios[i]] = VD56G3_GPIOX_STROBE_MODE; + set_bit(led_gpios[i], &sensor->ext_leds_mask); + } + + return 0; +} + +static int vd56g3_parse_dt(struct vd56g3 *sensor) +{ + struct fwnode_handle *endpoint; + int ret; + + endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(sensor->dev), 0, + 0, 0); + if (!endpoint) { + dev_err(sensor->dev, "Endpoint node not found\n"); + return -EINVAL; + } + + ret = vd56g3_check_csi_conf(sensor, endpoint); + fwnode_handle_put(endpoint); + if (ret) + return ret; + + return vd56g3_parse_dt_gpios(sensor); +} + +static int vd56g3_get_regulators(struct vd56g3 *sensor) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sensor->supplies); i++) + sensor->supplies[i].supply = vd56g3_supply_names[i]; + + return devm_regulator_bulk_get(sensor->dev, + ARRAY_SIZE(sensor->supplies), + sensor->supplies); +} + +static int vd56g3_prepare_clock_tree(struct vd56g3 *sensor) +{ + const unsigned int predivs[] = { 1, 2, 4 }; + u32 pll_out; + int i; + + /* External clock must be in [6Mhz-27Mhz] */ + if (sensor->xclk_freq < VD56G3_XCLK_FREQ_MIN || + sensor->xclk_freq > VD56G3_XCLK_FREQ_MAX) { + dev_err(sensor->dev, + "Only 6Mhz-27Mhz clock range supported. Provided %lu MHz\n", + sensor->xclk_freq / HZ_PER_MHZ); + return -EINVAL; + } + + /* PLL input should be in [6Mhz-12Mhz[ */ + for (i = 0; i < ARRAY_SIZE(predivs); i++) { + sensor->pll_prediv = predivs[i]; + if (sensor->xclk_freq / sensor->pll_prediv < 12 * HZ_PER_MHZ) + break; + } + + /* PLL output clock must be as close as possible to 804Mhz */ + sensor->pll_mult = (VD56G3_TARGET_PLL * sensor->pll_prediv + + sensor->xclk_freq / 2) / + sensor->xclk_freq; + pll_out = sensor->xclk_freq * sensor->pll_mult / sensor->pll_prediv; + + /* Target Pixel Clock for standard 10bit ADC mode : 160.8Mhz */ + sensor->pixel_clock = pll_out / VD56G3_VT_CLOCK_DIV; + + return 0; +} + +static int vd56g3_detect(struct vd56g3 *sensor) +{ + struct device *dev = sensor->dev; + unsigned int model; + u64 model_id; + u64 device_revision; + u64 optical_revision; + int ret = 0; + + model = (uintptr_t)device_get_match_data(dev); + + ret = cci_read(sensor->regmap, VD56G3_REG_MODEL_ID, &model_id, NULL); + if (ret) + return ret; + + if (model_id != VD56G3_MODEL_ID) { + dev_err(dev, "Unsupported sensor id: %x\n", (u16)model_id); + return -ENODEV; + } + + ret = cci_read(sensor->regmap, VD56G3_REG_REVISION, &device_revision, + NULL); + if (ret) + return ret; + + if ((device_revision >> 8) != VD56G3_REVISION_CUT3) { + dev_err(dev, "Unsupported version: %x\n", (u16)device_revision); + return -ENODEV; + } + + ret = cci_read(sensor->regmap, VD56G3_REG_OPTICAL_REVISION, + &optical_revision, NULL); + if (ret) + return ret; + + sensor->is_mono = + ((optical_revision & 1) == VD56G3_OPTICAL_REVISION_MONO); + if ((sensor->is_mono && model == VD56G3_MODEL_VD66GY) || + (!sensor->is_mono && model == VD56G3_MODEL_VD56G3)) { + dev_err(dev, "Found %s sensor, while %s model is defined in DT\n", + (sensor->is_mono) ? "Mono" : "Bayer", + (model == VD56G3_MODEL_VD56G3) ? "vd56g3" : "vd66gy"); + return -ENODEV; + } + + return 0; +} + +static int vd56g3_subdev_init(struct vd56g3 *sensor) +{ + struct v4l2_subdev_state *state; + int ret; + + /* Init remaining sub device ops */ + sensor->sd.internal_ops = &vd56g3_internal_ops; + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->sd.entity.ops = &vd56g3_subdev_entity_ops; + + /* Init source pad */ + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); + if (ret) { + dev_err(sensor->dev, "Failed to init media entity: %d\n", ret); + return ret; + } + + /* Init controls */ + ret = vd56g3_init_controls(sensor); + if (ret) { + dev_err(sensor->dev, "Controls initialization failed: %d\n", + ret); + goto err_media; + } + + /* Init vd56g3 struct : default resolution + raw8 */ + sensor->sd.state_lock = sensor->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&sensor->sd); + if (ret) { + dev_err(sensor->dev, "Subdev init failed: %d\n", ret); + goto err_ctrls; + } + + /* Update controls according to the resolution set */ + state = v4l2_subdev_lock_and_get_active_state(&sensor->sd); + ret = vd56g3_update_controls(sensor); + v4l2_subdev_unlock_state(state); + if (ret) { + dev_err(sensor->dev, "Controls update failed: %d\n", ret); + goto err_ctrls; + } + + return 0; + +err_ctrls: + v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); + +err_media: + media_entity_cleanup(&sensor->sd.entity); + + return ret; +} + +static void vd56g3_subdev_cleanup(struct vd56g3 *sensor) +{ + v4l2_async_unregister_subdev(&sensor->sd); + v4l2_subdev_cleanup(&sensor->sd); + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); +} + +static int vd56g3_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct vd56g3 *sensor; + int ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + v4l2_i2c_subdev_init(&sensor->sd, client, &vd56g3_subdev_ops); + sensor->dev = dev; + + ret = vd56g3_parse_dt(sensor); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse Device Tree\n"); + + /* Get (and check) resources : power regs, ext clock, reset gpio */ + ret = vd56g3_get_regulators(sensor); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + sensor->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(sensor->xclk)) + return dev_err_probe(dev, PTR_ERR(sensor->xclk), + "Failed to get xclk\n"); + sensor->xclk_freq = clk_get_rate(sensor->xclk); + ret = vd56g3_prepare_clock_tree(sensor); + if (ret) + return ret; + + sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio), + "Failed to get reset gpio\n"); + + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) + return dev_err_probe(dev, PTR_ERR(sensor->regmap), + "Failed to init regmap\n"); + + /* Power ON */ + ret = vd56g3_power_on(dev); + if (ret) + return dev_err_probe(dev, ret, "Sensor power on failed\n"); + + /* Enable PM runtime with autosuspend (sensor being ON, set active) */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + + /* Check HW model/version */ + ret = vd56g3_detect(sensor); + if (ret) { + dev_err(dev, "Sensor detect failed: %d\n", ret); + goto err_power_off; + } + + /* Initialize & register subdev (v4l2_i2c subdev already initialized) */ + ret = vd56g3_subdev_init(sensor); + if (ret) { + dev_err(dev, "V4l2 init failed: %d\n", ret); + goto err_power_off; + } + + ret = v4l2_async_register_subdev(&sensor->sd); + if (ret) { + dev_err(dev, "Async subdev register failed: %d\n", ret); + goto err_subdev; + } + + /* Sensor could now be powered off (after the autosuspend delay) */ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + dev_dbg(dev, "Successfully probe %s sensor\n", + (sensor->is_mono) ? "vd56g3" : "vd66gy"); + + return 0; + +err_subdev: + vd56g3_subdev_cleanup(sensor); +err_power_off: + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_dont_use_autosuspend(dev); + vd56g3_power_off(dev); + + return ret; +} + +static void vd56g3_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct vd56g3 *sensor = to_vd56g3(sd); + + vd56g3_subdev_cleanup(sensor); + + pm_runtime_disable(sensor->dev); + if (!pm_runtime_status_suspended(sensor->dev)) + vd56g3_power_off(sensor->dev); + pm_runtime_set_suspended(sensor->dev); + pm_runtime_dont_use_autosuspend(sensor->dev); +} + +static const struct of_device_id vd56g3_dt_ids[] = { + { .compatible = "st,vd56g3", .data = (void *)VD56G3_MODEL_VD56G3 }, + { .compatible = "st,vd66gy", .data = (void *)VD56G3_MODEL_VD66GY }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vd56g3_dt_ids); + +static struct i2c_driver vd56g3_i2c_driver = { + .driver = { + .name = "vd56g3", + .of_match_table = vd56g3_dt_ids, + .pm = &vd56g3_pm_ops, + }, + .probe = vd56g3_probe, + .remove = vd56g3_remove, +}; + +module_i2c_driver(vd56g3_i2c_driver); + +MODULE_AUTHOR("Benjamin Mugnier "); +MODULE_AUTHOR("Mickael Guene "); +MODULE_AUTHOR("Sylvain Petinot "); +MODULE_DESCRIPTION("ST VD56G3 sensor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From fba4aa083104808a1098c2863130fab230f959dc Mon Sep 17 00:00:00 2001 From: Benjamin Mugnier Date: Wed, 30 Apr 2025 10:24:38 +0200 Subject: media: dt-bindings: Add ST VD55G1 camera sensor Also update MAINTAINERS file accordingly. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Benjamin Mugnier Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../devicetree/bindings/media/i2c/st,vd55g1.yaml | 133 +++++++++++++++++++++ MAINTAINERS | 7 ++ 2 files changed, 140 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml new file mode 100644 index 000000000000..3c071e6fbea6 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml @@ -0,0 +1,133 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (c) 2025 STMicroelectronics SA. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/st,vd55g1.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics VD55G1 Global Shutter Image Sensor + +maintainers: + - Benjamin Mugnier + - Sylvain Petinot + +description: |- + The STMicroelectronics VD55G1 is a global shutter image sensor with an active + array size of 804H x 704V. It is programmable through I2C interface. The I2C + address is fixed to 0x10. + + Image data is sent through MIPI CSI-2, which is configured as only 1 data + lane. The sensor provides 4 GPIOS that can be used for external LED signal + (synchronized with sensor integration periods). + +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + +properties: + compatible: + const: st,vd55g1 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + vcore-supply: + description: Digital core power supply (1.15V) + + vddio-supply: + description: Digital IO power supply (1.8V) + + vana-supply: + description: Analog power supply (2.8V) + + reset-gpios: + description: Sensor reset active low GPIO (XSHUTDOWN) + maxItems: 1 + + st,leds: + description: + List sensor's GPIOs used to control strobe light sources during exposure + time. The numbers identify the sensor pin on which the illumination + system is connected. GPIOs are active-high. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 4 + items: + minimum: 0 + maximum: 3 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + items: + - const: 1 + + link-frequencies: + maxItems: 1 + items: + minimum: 125000000 + maximum: 600000000 + + lane-polarities: + minItems: 1 + maxItems: 2 + + required: + - data-lanes + - link-frequencies + +required: + - compatible + - reg + - clocks + - vcore-supply + - vddio-supply + - vana-supply + - reset-gpios + - port + +unevaluatedProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + camera-sensor@10 { + compatible = "st,vd55g1"; + reg = <0x10>; + + clocks = <&camera_clk_12M>; + + vcore-supply = <&camera_vcore_v1v15>; + vddio-supply = <&camera_vddio_v1v8>; + vana-supply = <&camera_vana_v2v8>; + + reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>; + st,leds = <2>; + + orientation = <2>; + rotation = <0>; + + port { + endpoint { + data-lanes = <1>; + link-frequencies = /bits/ 64 <600000000>; + remote-endpoint = <&csiphy0_ep>; + }; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 32479b909ff0..9ca8971f1ce0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22915,6 +22915,13 @@ S: Maintained F: Documentation/hwmon/stpddc60.rst F: drivers/hwmon/pmbus/stpddc60.c +ST VD55G1 DRIVER +M: Benjamin Mugnier +M: Sylvain Petinot +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml + ST VD56G3 IMAGE SENSOR DRIVER M: Benjamin Mugnier M: Sylvain Petinot -- cgit v1.2.3-59-g8ed1b From e56616d7b23c120626a4ea670d893bf6e9bf44cd Mon Sep 17 00:00:00 2001 From: Benjamin Mugnier Date: Wed, 30 Apr 2025 10:24:39 +0200 Subject: media: i2c: Add driver for ST VD55G1 camera sensor The VD55G1 is a monochrome global shutter camera with a 804x704 maximum resolution with RAW8 and RAW10 bytes per pixel. The driver supports : - Auto exposure from the sensor, or manual exposure mode - HDR subtraction mode, allowing edge detection and background removal - Auto exposure cold start, using configuration values from last stream to start the next one - LED GPIOs for illumination - Most standard camera sensor features (hblank, vblank, test patterns, again, dgain, hflip, vflip, auto exposure bias, etc.) Add driver source code to MAINTAINERS file. Signed-off-by: Benjamin Mugnier Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- MAINTAINERS | 2 + drivers/media/i2c/Kconfig | 11 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/vd55g1.c | 1965 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1979 insertions(+) create mode 100644 drivers/media/i2c/vd55g1.c diff --git a/MAINTAINERS b/MAINTAINERS index 9ca8971f1ce0..1d8641f29e8a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22921,6 +22921,7 @@ M: Sylvain Petinot L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml +F: drivers/media/i2c/vd55g1.c ST VD56G3 IMAGE SENSOR DRIVER M: Benjamin Mugnier @@ -25325,6 +25326,7 @@ F: drivers/media/i2c/mt* F: drivers/media/i2c/og* F: drivers/media/i2c/ov* F: drivers/media/i2c/s5* +F: drivers/media/i2c/vd55g1.c F: drivers/media/i2c/vd56g3.c F: drivers/media/i2c/vgxy61.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 0621a6eb1bf5..1ba3bdf67574 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -712,6 +712,17 @@ config VIDEO_S5K6A3 This is a V4L2 sensor driver for Samsung S5K6A3 raw camera sensor. +config VIDEO_VD55G1 + tristate "ST VD55G1 sensor support" + select V4L2_CCI_I2C + depends on GPIOLIB + help + This is a Video4Linux2 sensor driver for the ST VD55G1 + camera sensor. + + To compile this driver as a module, choose M here: the + module will be called vd55g1. + config VIDEO_VD56G3 tristate "ST VD56G3 sensor support" select V4L2_CCI_I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 8eb84d4c21ff..5873d29433ee 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -155,6 +155,7 @@ obj-$(CONFIG_VIDEO_TW9910) += tw9910.o obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o +obj-$(CONFIG_VIDEO_VD55G1) += vd55g1.o obj-$(CONFIG_VIDEO_VD56G3) += vd56g3.o obj-$(CONFIG_VIDEO_VGXY61) += vgxy61.o obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c new file mode 100644 index 000000000000..25e2fc88a036 --- /dev/null +++ b/drivers/media/i2c/vd55g1.c @@ -0,0 +1,1965 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for VD55G1 global shutter sensor family driver + * + * Copyright (C) 2025 STMicroelectronics SA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register Map */ +#define VD55G1_REG_MODEL_ID CCI_REG32_LE(0x0000) +#define VD55G1_MODEL_ID 0x53354731 +#define VD55G1_REG_REVISION CCI_REG16_LE(0x0004) +#define VD55G1_REVISION_CCB 0x2020 +#define VD55G1_REG_FWPATCH_REVISION CCI_REG16_LE(0x0012) +#define VD55G1_REG_FWPATCH_START_ADDR CCI_REG8(0x2000) +#define VD55G1_REG_SYSTEM_FSM CCI_REG8(0x001c) +#define VD55G1_SYSTEM_FSM_READY_TO_BOOT 0x01 +#define VD55G1_SYSTEM_FSM_SW_STBY 0x02 +#define VD55G1_SYSTEM_FSM_STREAMING 0x03 +#define VD55G1_REG_BOOT CCI_REG8(0x0200) +#define VD55G1_BOOT_PATCH_SETUP 2 +#define VD55G1_REG_STBY CCI_REG8(0x0201) +#define VD55G1_STBY_START_STREAM 1 +#define VD55G1_REG_STREAMING CCI_REG8(0x0202) +#define VD55G1_STREAMING_STOP_STREAM 1 +#define VD55G1_REG_EXT_CLOCK CCI_REG32_LE(0x0220) +#define VD55G1_REG_LINE_LENGTH CCI_REG16_LE(0x0300) +#define VD55G1_REG_ORIENTATION CCI_REG8(0x0302) +#define VD55G1_REG_FORMAT_CTRL CCI_REG8(0x030a) +#define VD55G1_REG_OIF_CTRL CCI_REG16_LE(0x030c) +#define VD55G1_REG_ISL_ENABLE CCI_REG16_LE(0x326) +#define VD55G1_REG_OIF_IMG_CTRL CCI_REG8(0x030f) +#define VD55G1_REG_MIPI_DATA_RATE CCI_REG32_LE(0x0224) +#define VD55G1_REG_PATGEN_CTRL CCI_REG16_LE(0x0304) +#define VD55G1_PATGEN_TYPE_SHIFT 4 +#define VD55G1_PATGEN_ENABLE BIT(0) +#define VD55G1_REG_MANUAL_ANALOG_GAIN CCI_REG8(0x0501) +#define VD55G1_REG_MANUAL_COARSE_EXPOSURE CCI_REG16_LE(0x0502) +#define VD55G1_REG_MANUAL_DIGITAL_GAIN CCI_REG16_LE(0x0504) +#define VD55G1_REG_APPLIED_COARSE_EXPOSURE CCI_REG16_LE(0x00e8) +#define VD55G1_REG_APPLIED_ANALOG_GAIN CCI_REG16_LE(0x00ea) +#define VD55G1_REG_APPLIED_DIGITAL_GAIN CCI_REG16_LE(0x00ec) +#define VD55G1_REG_AE_FORCE_COLDSTART CCI_REG8(0x0308) +#define VD55G1_REG_AE_COLDSTART_EXP_TIME CCI_REG32_LE(0x0374) +#define VD55G1_REG_READOUT_CTRL CCI_REG8(0x052e) +#define VD55G1_READOUT_CTRL_BIN_MODE_NORMAL 0 +#define VD55G1_READOUT_CTRL_BIN_MODE_DIGITAL_X2 1 +#define VD55G1_REG_DUSTER_CTRL CCI_REG8(0x03ea) +#define VD55G1_DUSTER_ENABLE BIT(0) +#define VD55G1_DUSTER_DISABLE 0 +#define VD55G1_DUSTER_DYN_ENABLE BIT(1) +#define VD55G1_DUSTER_RING_ENABLE BIT(4) +#define VD55G1_REG_AE_TARGET_PERCENTAGE CCI_REG8(0x0486) +#define VD55G1_REG_NEXT_CTX CCI_REG16_LE(0x03e4) +#define VD55G1_REG_EXPOSURE_USE_CASES CCI_REG8(0x0312) +#define VD55G1_EXPOSURE_USE_CASES_MULTI_CONTEXT BIT(2) +#define VD55G1_REG_EXPOSURE_MAX_COARSE CCI_REG16_LE(0x0372) +#define VD55G1_EXPOSURE_MAX_COARSE_DEF 0x7fff +#define VD55G1_EXPOSURE_MAX_COARSE_SUB 446 +#define VD55G1_REG_CTX_REPEAT_COUNT_CTX0 CCI_REG16_LE(0x03dc) +#define VD55G1_REG_CTX_REPEAT_COUNT_CTX1 CCI_REG16_LE(0x03de) + +#define VD55G1_REG_EXP_MODE(ctx) \ + CCI_REG8(0x0500 + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_REG_FRAME_LENGTH(ctx) \ + CCI_REG32_LE(0x050c + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_REG_X_START(ctx) \ + CCI_REG16_LE(0x0514 + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_REG_X_WIDTH(ctx) \ + CCI_REG16_LE(0x0516 + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_REG_Y_START(ctx) \ + CCI_REG16_LE(0x0510 + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_REG_Y_HEIGHT(ctx) \ + CCI_REG16_LE(0x0512 + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_REG_GPIO_0_CTRL(ctx) \ + CCI_REG8(0x051d + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_GPIO_MODE_FSYNC_OUT 0x00 +#define VD55G1_GPIO_MODE_IN 0x01 +#define VD55G1_GPIO_MODE_STROBE 0x02 +#define VD55G1_REG_VT_MODE(ctx) \ + CCI_REG8(0x0536 + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_VT_MODE_NORMAL 0 +#define VD55G1_VT_MODE_SUBTRACTION 1 +#define VD55G1_REG_MASK_FRAME_CTRL(ctx) \ + CCI_REG8(0x0537 + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G1_MASK_FRAME_CTRL_OUTPUT 0 +#define VD55G1_MASK_FRAME_CTRL_MASK 1 +#define VD55G1_REG_EXPOSURE_INSTANCE(ctx) \ + CCI_REG32_LE(0x52D + VD55G1_CTX_OFFSET * (ctx)) + +#define VD55G1_WIDTH 804 +#define VD55G1_HEIGHT 704 +#define VD55G1_DEFAULT_MODE 0 +#define VD55G1_NB_GPIOS 4 +#define VD55G1_MEDIA_BUS_FMT_DEF MEDIA_BUS_FMT_Y8_1X8 +#define VD55G1_DGAIN_DEF 256 +#define VD55G1_AGAIN_DEF 19 +#define VD55G1_EXPO_MAX_TERM 64 +#define VD55G1_EXPO_DEF 500 +#define VD55G1_LINE_LENGTH_MIN 1128 +#define VD55G1_LINE_LENGTH_SUB_MIN 1344 +#define VD55G1_VBLANK_MIN 86 +#define VD55G1_VBLANK_MAX 0xffff +#define VD55G1_FRAME_LENGTH_DEF 1860 /* 60 fps */ +#define VD55G1_MIPI_MARGIN 900 +#define VD55G1_CTX_OFFSET 0x50 +#define VD55G1_FWPATCH_REVISION_MAJOR 2 +#define VD55G1_FWPATCH_REVISION_MINOR 9 +#define VD55G1_XCLK_FREQ_MIN (6 * HZ_PER_MHZ) +#define VD55G1_XCLK_FREQ_MAX (27 * HZ_PER_MHZ) +#define VD55G1_MIPI_RATE_MIN (250 * HZ_PER_MHZ) +#define VD55G1_MIPI_RATE_MAX (1200 * HZ_PER_MHZ) + +static const u8 patch_array[] = { + 0x44, 0x03, 0x09, 0x02, 0xe6, 0x01, 0x42, 0x00, 0xea, 0x01, 0x42, 0x00, + 0xf0, 0x01, 0x42, 0x00, 0xe6, 0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0xfa, 0x68, 0x40, 0x00, 0xe8, + 0x09, 0xbe, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0x1c, 0x00, 0xc0, 0xe2, + 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa, 0x6b, 0x80, 0x98, 0x7f, + 0xfc, 0xef, 0x11, 0xc1, 0x0f, 0x82, 0x69, 0xbe, 0x0f, 0xac, 0x58, 0x40, + 0x00, 0xe8, 0x0c, 0x0c, 0x00, 0xf2, 0x93, 0xdd, 0x1c, 0x00, 0x40, 0xe3, + 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x84, 0xfa, 0x46, 0x0e, 0xe8, 0xe0, + 0x08, 0xde, 0x4a, 0x40, 0x84, 0xe0, 0xa5, 0x86, 0xa8, 0x7d, 0xfc, 0xef, + 0x6b, 0x80, 0x01, 0xbf, 0x28, 0x77, 0x0c, 0xef, 0x0b, 0x0e, 0x21, 0x78, + 0x06, 0xc0, 0x0b, 0xa5, 0xb5, 0x84, 0x06, 0x42, 0x98, 0xe1, 0x01, 0x81, + 0x01, 0x42, 0x38, 0xe0, 0x0c, 0xc4, 0x0e, 0x84, 0x46, 0x02, 0x84, 0xe0, + 0x0c, 0x84, 0x11, 0x81, 0x21, 0x81, 0x31, 0x81, 0x41, 0x81, 0x51, 0x81, + 0xc1, 0x81, 0x05, 0x83, 0x0c, 0x0c, 0x84, 0xf2, 0x93, 0xdd, 0x06, 0x40, + 0x98, 0xe1, 0xc8, 0x80, 0x58, 0x82, 0x48, 0xc0, 0x38, 0xc2, 0x29, 0x00, + 0x10, 0xe0, 0x19, 0x00, 0x14, 0xe0, 0x09, 0x00, 0x38, 0xe0, 0x5f, 0xb8, + 0x5f, 0xa8, 0x5f, 0xa6, 0x5f, 0xa4, 0x5f, 0xa2, 0x5f, 0xa0, 0x56, 0x41, + 0x98, 0xe1, 0x18, 0x82, 0x28, 0x80, 0x38, 0xc0, 0x5f, 0xa2, 0x19, 0x00, + 0x20, 0xf8, 0x5f, 0xa4, 0x28, 0xc2, 0x5f, 0xa6, 0x39, 0x00, 0x10, 0xe0, + 0x5f, 0xa2, 0x19, 0x00, 0x14, 0xe0, 0x5f, 0xa4, 0x29, 0x00, 0x18, 0xe0, + 0x5f, 0xa6, 0x39, 0x00, 0x40, 0xe0, 0x5f, 0xa2, 0x19, 0x00, 0x44, 0xe0, + 0x5f, 0xa4, 0x29, 0x00, 0x1c, 0xe0, 0x5f, 0xa6, 0x39, 0x00, 0x38, 0xe0, + 0x5f, 0xa2, 0x19, 0x00, 0x20, 0xe0, 0x5f, 0xa4, 0x29, 0x00, 0x24, 0xe0, + 0x5f, 0xa6, 0x39, 0x00, 0x28, 0xe0, 0x5f, 0xa2, 0x19, 0x00, 0x2c, 0xe0, + 0x5f, 0xa4, 0x29, 0x00, 0x30, 0xe0, 0x5f, 0xa6, 0x09, 0x00, 0x34, 0xe0, + 0x5f, 0xa2, 0x5f, 0xa4, 0x5f, 0xa0, 0x4a, 0x0a, 0xfc, 0xfb, 0xe5, 0x82, + 0x08, 0xde, 0x4a, 0x40, 0x88, 0xe0, 0xf6, 0x40, 0x00, 0xe0, 0x01, 0x4e, + 0x99, 0x78, 0x0a, 0xc0, 0x85, 0x80, 0x98, 0x40, 0x00, 0xe8, 0x35, 0x81, + 0xa8, 0x40, 0x00, 0xe8, 0x0b, 0x8c, 0x0c, 0x0c, 0x84, 0xf2, 0xd5, 0xed, + 0x83, 0xc1, 0x13, 0xc5, 0x93, 0xdd, 0xc3, 0xc1, 0x83, 0xc1, 0x13, 0xc3, + 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0xc6, 0x0f, 0x94, 0xe0, + 0x19, 0x0e, 0xc9, 0x65, 0x01, 0xc0, 0x28, 0xde, 0x0a, 0x42, 0x80, 0xe0, + 0x24, 0x02, 0x00, 0xfc, 0x16, 0xde, 0xa5, 0x8a, 0x19, 0x00, 0xb8, 0xe0, + 0x10, 0x02, 0x0c, 0xec, 0x1d, 0xe6, 0x14, 0x02, 0x88, 0x80, 0x4e, 0x04, + 0x01, 0x00, 0x10, 0x80, 0x25, 0x02, 0x08, 0x9c, 0x86, 0x02, 0x00, 0x80, + 0x08, 0x44, 0x00, 0x98, 0x55, 0x81, 0x11, 0x85, 0x45, 0x81, 0x11, 0x89, + 0x25, 0x81, 0x11, 0x83, 0x2b, 0x00, 0x24, 0xe0, 0x64, 0xc2, 0x0b, 0x84, + 0x08, 0x51, 0x00, 0xef, 0x2b, 0x80, 0x01, 0x83, 0x1b, 0x8c, 0x38, 0x7d, + 0x5c, 0xef, 0x18, 0xde, 0x0b, 0xa1, 0x25, 0x82, 0x0b, 0x0e, 0x88, 0xf9, + 0x0a, 0x00, 0x00, 0xe8, 0x10, 0x42, 0x04, 0x9c, 0x11, 0x4e, 0x0c, 0x80, + 0x10, 0x40, 0x04, 0xf0, 0x4e, 0x05, 0x01, 0x60, 0x10, 0xc0, 0x06, 0x88, + 0x10, 0x40, 0xf8, 0xf3, 0x06, 0xde, 0x4c, 0x0c, 0x04, 0xf2, 0x93, 0xdd, + 0x0c, 0x04, 0x1c, 0xfe, 0xf6, 0x0f, 0x94, 0xe0, 0x38, 0x9c, 0x46, 0x51, + 0xfc, 0xe0, 0x46, 0x49, 0x38, 0xe2, 0x30, 0x46, 0xf8, 0xf3, 0x36, 0x9c, + 0xc6, 0x46, 0x0c, 0xe1, 0x34, 0x8c, 0x94, 0xa0, 0x4e, 0xa0, 0x39, 0x06, + 0x80, 0xe0, 0x4a, 0x46, 0x94, 0xe0, 0x05, 0x8c, 0x6a, 0x40, 0x80, 0xe0, + 0x2c, 0x0c, 0x00, 0xe2, 0x0b, 0x8c, 0xb8, 0x7c, 0x5c, 0xef, 0x0b, 0x8c, + 0x9e, 0xa0, 0xf8, 0x40, 0x60, 0xef, 0x0b, 0xa1, 0x5a, 0x40, 0x80, 0xe0, + 0x65, 0x88, 0x28, 0x02, 0x01, 0x40, 0x00, 0x80, 0x2a, 0x42, 0x9c, 0xe1, + 0x28, 0x49, 0x60, 0xef, 0x96, 0x4d, 0x9c, 0xe1, 0x01, 0x81, 0x06, 0x98, + 0xd5, 0x81, 0x09, 0x0e, 0xa1, 0x64, 0x01, 0xc0, 0x4a, 0x40, 0x88, 0xe0, + 0x85, 0x80, 0xb8, 0x77, 0xfc, 0xef, 0x35, 0x81, 0xc8, 0x77, 0xfc, 0xef, + 0x08, 0x98, 0x4a, 0x00, 0xfc, 0xfb, 0x55, 0xfc, 0xe8, 0x4a, 0x60, 0xef, + 0x1a, 0x44, 0x9c, 0xe1, 0x35, 0x81, 0x1a, 0x4e, 0x9c, 0xe9, 0x1c, 0x00, + 0x00, 0xe2, 0x0c, 0x0c, 0x1c, 0xf6, 0x93, 0xdd, 0x0d, 0xc3, 0x1a, 0x41, + 0x08, 0xe4, 0x0a, 0x40, 0x84, 0xe1, 0x0c, 0x00, 0x00, 0xe2, 0x93, 0xdd, + 0x4c, 0x04, 0x1c, 0xfa, 0x86, 0x52, 0xec, 0xe1, 0x08, 0xa6, 0x65, 0x12, + 0x24, 0xf8, 0x0e, 0x02, 0x99, 0x7a, 0x00, 0xc0, 0x00, 0x40, 0xa0, 0xf3, + 0x06, 0xa6, 0x0b, 0x8c, 0x08, 0x49, 0x00, 0xef, 0x85, 0x12, 0x28, 0xf8, + 0x02, 0x02, 0xfc, 0xed, 0xf6, 0x47, 0xfd, 0x6f, 0xe0, 0xff, 0x04, 0xe2, + 0x14, 0x04, 0xc0, 0xe0, 0x0f, 0x86, 0x2f, 0xa0, 0x0b, 0x8c, 0x2e, 0xe2, + 0x08, 0x48, 0x00, 0xef, 0x86, 0x02, 0x84, 0xfe, 0x0e, 0x05, 0x09, 0x7d, + 0x00, 0xc0, 0x05, 0x52, 0x08, 0xf8, 0x18, 0x7d, 0xfc, 0xef, 0x4a, 0x40, + 0x80, 0xe0, 0x09, 0x12, 0x04, 0xc0, 0x65, 0x12, 0x20, 0xf8, 0x00, 0x40, + 0x40, 0xdc, 0x01, 0x52, 0x04, 0xc0, 0x0e, 0x00, 0x41, 0x78, 0xf5, 0xc5, + 0x6d, 0xc0, 0xb5, 0x82, 0x05, 0x10, 0x10, 0xe0, 0x11, 0xf1, 0x0f, 0x82, + 0x05, 0x50, 0x10, 0xe0, 0x05, 0x10, 0x10, 0xe0, 0xfe, 0x02, 0xf0, 0xff, + 0x0f, 0x82, 0x85, 0x83, 0x15, 0x10, 0x10, 0xe0, 0x16, 0x00, 0x91, 0x6e, + 0x69, 0xcd, 0x21, 0xf1, 0x6d, 0xc1, 0x01, 0x83, 0x2f, 0x82, 0x26, 0x00, + 0x00, 0x80, 0x2f, 0xa0, 0x25, 0x50, 0x10, 0xe0, 0x05, 0x10, 0x10, 0xe0, + 0x11, 0xa1, 0xfe, 0x04, 0xf0, 0xff, 0x06, 0x42, 0x00, 0x80, 0x0f, 0x84, + 0x0f, 0xa2, 0x05, 0x50, 0x10, 0xe0, 0x16, 0x00, 0x91, 0x6e, 0x69, 0xcd, + 0x6d, 0xc1, 0x71, 0x8d, 0x16, 0x00, 0x79, 0x61, 0x2d, 0xcb, 0x86, 0x0e, + 0x00, 0x80, 0x6d, 0xc1, 0x56, 0x0e, 0x00, 0xc0, 0x0b, 0x8c, 0x1b, 0x8e, + 0x71, 0x52, 0x0c, 0xf8, 0x08, 0x43, 0x00, 0xef, 0x05, 0x52, 0x14, 0xf8, + 0x15, 0x10, 0x28, 0xe0, 0x70, 0x04, 0x04, 0xec, 0x31, 0xe1, 0x29, 0x9e, + 0x1f, 0x86, 0x1f, 0xa4, 0x15, 0x50, 0x28, 0xe0, 0x86, 0x42, 0x3c, 0xe0, + 0x0e, 0x04, 0x9d, 0x64, 0x9b, 0xc2, 0x05, 0x52, 0x1c, 0xf8, 0x78, 0xa6, + 0x48, 0x77, 0xfc, 0xef, 0x4a, 0x40, 0x80, 0xe0, 0x70, 0x4e, 0x10, 0xdc, + 0x1e, 0x00, 0x81, 0x70, 0xeb, 0xcb, 0x70, 0x4e, 0xec, 0x93, 0x6d, 0xc1, + 0x11, 0x85, 0x36, 0x02, 0x00, 0x80, 0x76, 0xa6, 0x11, 0x52, 0x10, 0xf8, + 0x05, 0x10, 0x40, 0xe0, 0xfe, 0x47, 0x0c, 0xff, 0x14, 0x04, 0xa0, 0xe0, + 0x0f, 0x86, 0x0f, 0xa4, 0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x28, 0xe0, + 0xfe, 0x47, 0xfd, 0x7f, 0xe3, 0xff, 0x14, 0x04, 0xd0, 0xe0, 0x0f, 0x86, + 0x2f, 0xa0, 0x20, 0x00, 0x01, 0x6c, 0x00, 0xd0, 0x05, 0x50, 0x28, 0xe0, + 0x0b, 0x8c, 0xf8, 0x7e, 0xfc, 0xee, 0x0e, 0x03, 0x59, 0x78, 0xf5, 0xc5, + 0x0d, 0xc2, 0x05, 0x52, 0x0c, 0xf8, 0x08, 0xa6, 0x46, 0x42, 0xb4, 0xe0, + 0x18, 0x84, 0x00, 0x40, 0xf4, 0x93, 0x00, 0x40, 0x08, 0xdc, 0x1b, 0xa1, + 0x06, 0xa6, 0x05, 0x10, 0x40, 0x80, 0x04, 0x00, 0x50, 0x9c, 0x65, 0x8a, + 0x05, 0x10, 0x44, 0xe0, 0xf6, 0x43, 0xfd, 0x6f, 0x00, 0xf8, 0x0f, 0x82, + 0x06, 0x02, 0x01, 0x60, 0x1e, 0xc0, 0x0f, 0xa2, 0x05, 0x50, 0x44, 0xe0, + 0x05, 0x10, 0x44, 0xe0, 0x0e, 0x02, 0x00, 0xf8, 0x0f, 0x82, 0x09, 0xf6, + 0x05, 0x50, 0x44, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0x54, 0xfc, + 0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0xcc, 0xfc, + 0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0x4c, 0xfc, + 0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0xd0, 0xfc, + 0x05, 0x50, 0x40, 0xe0, 0x4c, 0x0c, 0x1c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, + 0xc6, 0x40, 0xfc, 0xe0, 0x04, 0x80, 0xc6, 0x44, 0x0c, 0xe1, 0x15, 0x04, + 0x0c, 0xf8, 0x0a, 0x80, 0x06, 0x07, 0x04, 0xe0, 0x03, 0x42, 0x48, 0xe1, + 0x46, 0x02, 0x40, 0xe2, 0x08, 0xc6, 0x44, 0x88, 0x06, 0x46, 0x0e, 0xe0, + 0x86, 0x01, 0x84, 0xe0, 0x33, 0x80, 0x39, 0x06, 0xd8, 0xef, 0x0a, 0x46, + 0x80, 0xe0, 0x31, 0xbf, 0x06, 0x06, 0x00, 0xc0, 0x31, 0x48, 0x60, 0xe0, + 0x34, 0x88, 0x49, 0x06, 0x40, 0xe1, 0x40, 0x48, 0x7c, 0xf3, 0x41, 0x46, + 0x40, 0xe1, 0x24, 0x8a, 0x39, 0x04, 0x10, 0xe0, 0x39, 0xc2, 0x31, 0x44, + 0x10, 0xe0, 0x14, 0xc4, 0x1b, 0xa5, 0x11, 0x83, 0x11, 0x40, 0x25, 0x6a, + 0x01, 0xc0, 0x08, 0x5c, 0x00, 0xda, 0x15, 0x00, 0xcc, 0xe0, 0x25, 0x00, + 0xf8, 0xe0, 0x1b, 0x85, 0x08, 0x5c, 0x00, 0x9a, 0x4e, 0x03, 0x01, 0x60, + 0x10, 0xc0, 0x29, 0x00, 0x1c, 0xe4, 0x18, 0x84, 0x20, 0x44, 0xf8, 0xf3, + 0x2f, 0xa2, 0x21, 0x40, 0x1c, 0xe4, 0x93, 0xdd, 0x0c, 0x00, 0x80, 0xfa, + 0x15, 0x00, 0x3c, 0xe0, 0x21, 0x81, 0x31, 0x85, 0x21, 0x42, 0x60, 0xe0, + 0x15, 0x00, 0x44, 0xe0, 0x31, 0x42, 0x40, 0xe1, 0x15, 0x00, 0x34, 0xe0, + 0x21, 0x42, 0x20, 0xe0, 0x15, 0x00, 0x34, 0xe0, 0xd6, 0x04, 0x10, 0xe0, + 0x23, 0x42, 0x30, 0xe0, 0x15, 0x00, 0x34, 0xe0, 0x86, 0x44, 0x04, 0xe0, + 0x23, 0x42, 0x38, 0xe0, 0x05, 0x00, 0x30, 0xe0, 0xc6, 0x02, 0x08, 0xe0, + 0x13, 0x40, 0x10, 0xe3, 0xe8, 0x56, 0x40, 0xef, 0x06, 0x40, 0x0c, 0xe1, + 0x04, 0x80, 0x06, 0x02, 0x94, 0xe0, 0x2b, 0x02, 0xc4, 0xea, 0x3b, 0x00, + 0x78, 0xe2, 0x20, 0x44, 0xfd, 0x73, 0x07, 0xc0, 0x30, 0x46, 0x01, 0x70, + 0xf8, 0xc0, 0x3f, 0xa4, 0x33, 0x40, 0x78, 0xe2, 0x0a, 0x84, 0x0c, 0x08, + 0x80, 0xf2, 0xf8, 0x3b, 0x3c, 0xff, 0xc3, 0xc1, 0x06, 0x40, 0x0c, 0xe1, + 0x04, 0x80, 0x1b, 0x00, 0x40, 0xe4, 0x19, 0xc2, 0x13, 0x40, 0x40, 0xe4, + 0x1b, 0x00, 0x40, 0xe4, 0x19, 0xc4, 0x13, 0x40, 0x40, 0xe4, 0x93, 0xdd, + 0xc6, 0x43, 0xec, 0xe0, 0x46, 0x41, 0xfc, 0xe0, 0x24, 0x84, 0x04, 0x80, + 0x31, 0x81, 0x4a, 0x44, 0x80, 0xe0, 0x86, 0x44, 0x0c, 0xe1, 0x09, 0x00, + 0x6c, 0xe0, 0xc4, 0x8a, 0x8e, 0x47, 0xfc, 0x9f, 0x01, 0x42, 0x51, 0x78, + 0x0c, 0xc0, 0x31, 0x58, 0x90, 0xe0, 0x34, 0x8a, 0x41, 0xbf, 0x06, 0x08, + 0x00, 0xc0, 0x41, 0x46, 0xa0, 0xe0, 0x34, 0x8a, 0x51, 0x81, 0xf6, 0x0b, + 0x00, 0xc0, 0x51, 0x46, 0xd0, 0xe0, 0x34, 0x8a, 0x01, 0xbf, 0x51, 0x46, + 0xe0, 0xe0, 0x44, 0x84, 0x0a, 0x48, 0x84, 0xe0, 0x75, 0x86, 0x54, 0xca, + 0x49, 0x88, 0x44, 0x06, 0x88, 0xe1, 0x36, 0x94, 0x4a, 0x46, 0x80, 0xe0, + 0x34, 0xca, 0x47, 0xc6, 0x11, 0x8d, 0x41, 0x46, 0xd0, 0xe0, 0x34, 0x88, + 0x76, 0x02, 0x00, 0xc0, 0x06, 0x00, 0x00, 0xc0, 0x16, 0x8c, 0x14, 0x88, + 0x01, 0x42, 0xc0, 0xe1, 0x01, 0x42, 0xe0, 0xe1, 0x01, 0x42, 0xf0, 0xe1, + 0x93, 0xdd, 0x34, 0xca, 0x41, 0x85, 0x46, 0x8c, 0x34, 0xca, 0x06, 0x48, + 0x00, 0xe0, 0x41, 0x46, 0xd0, 0xe0, 0x34, 0x88, 0x41, 0x83, 0x46, 0x8c, + 0x34, 0x88, 0x01, 0x46, 0xc0, 0xe1, 0x01, 0x46, 0xe0, 0xe1, 0x01, 0x46, + 0xf0, 0xe1, 0x09, 0x02, 0x20, 0xe0, 0x14, 0xca, 0x03, 0x42, 0x58, 0xe0, + 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0x46, 0x4e, 0x08, 0xe1, + 0x06, 0x4c, 0x0c, 0xe1, 0x0a, 0x9e, 0x14, 0x98, 0x05, 0x42, 0x44, 0xe0, + 0x10, 0x00, 0xe1, 0x65, 0x03, 0xc0, 0x78, 0x41, 0x00, 0xe8, 0x08, 0x9c, + 0x0b, 0xa1, 0x04, 0x98, 0x06, 0x02, 0x10, 0x80, 0x13, 0x40, 0xf8, 0x86, + 0x65, 0x82, 0x00, 0x00, 0xe1, 0x65, 0x03, 0xc0, 0xa8, 0x40, 0x00, 0xe8, + 0x14, 0x98, 0x04, 0x00, 0xa0, 0xfc, 0x03, 0x42, 0x00, 0xe7, 0x4c, 0x0c, + 0x04, 0xf2, 0x93, 0xdd, 0x0a, 0x80, 0x93, 0xdd, 0x0c, 0x04, 0x00, 0xfa, + 0x06, 0x02, 0xec, 0xe1, 0x64, 0x84, 0x15, 0x0c, 0x2c, 0xe0, 0x14, 0x02, + 0xa0, 0xfc, 0x15, 0x4c, 0x2c, 0xe0, 0xd8, 0x40, 0x00, 0xe8, 0x14, 0xd8, + 0x09, 0x82, 0x14, 0x02, 0x00, 0xfc, 0x1f, 0xa0, 0x1e, 0xd8, 0x01, 0x85, + 0x0c, 0x0c, 0x00, 0xf2, 0xe8, 0x32, 0x2c, 0xff, 0x93, 0xdd, 0xc3, 0xc1, + 0x0c, 0x04, 0x00, 0xfa, 0x6b, 0x80, 0xf6, 0x01, 0x94, 0xe0, 0x08, 0x80, + 0x4a, 0x40, 0x80, 0xe0, 0x45, 0x86, 0x06, 0x40, 0x0c, 0xe1, 0x04, 0x80, + 0xc6, 0x02, 0x40, 0xe2, 0x09, 0x00, 0xd0, 0xe0, 0x14, 0x84, 0x1b, 0xa5, + 0x15, 0x84, 0x07, 0xc5, 0x09, 0x82, 0x18, 0x41, 0x00, 0xe8, 0x46, 0x43, + 0xfc, 0xe0, 0x14, 0x84, 0x19, 0x02, 0xd8, 0xe0, 0x19, 0x82, 0x0b, 0x83, + 0x16, 0x00, 0x00, 0xc0, 0x01, 0x4c, 0x00, 0xc0, 0x0c, 0x0c, 0x00, 0xf2, + 0x93, 0xdd, 0xc3, 0xc1, 0x4a, 0x00, 0x00, 0xe0, 0x0c, 0x00, 0x00, 0xe2, + 0x93, 0xdd, 0xc3, 0xc1, 0x46, 0x40, 0x84, 0xe0, 0x11, 0xaf, 0x13, 0x40, + 0x6c, 0xec, 0x11, 0xb3, 0x13, 0x40, 0x70, 0xec, 0xc6, 0x43, 0xf0, 0xe0, + 0x13, 0x40, 0xdc, 0xec, 0xc6, 0x02, 0x24, 0xe0, 0x1c, 0x80, 0x93, 0xdd, + 0x4c, 0x00, 0x00, 0xfa, 0xc8, 0x60, 0x7c, 0xef, 0xe8, 0x61, 0x7c, 0xef, + 0x28, 0x7e, 0x80, 0xef, 0xc6, 0x40, 0x98, 0xe1, 0x11, 0x83, 0x16, 0x80, + 0x46, 0x01, 0x10, 0xe1, 0x11, 0x81, 0x16, 0x80, 0x4c, 0x08, 0x00, 0xf2, + 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x0c, 0xfa, 0x6b, 0x80, 0x04, 0x98, + 0x7b, 0x82, 0x56, 0x42, 0xb4, 0xe0, 0x88, 0x84, 0x05, 0x00, 0x10, 0xe0, + 0x09, 0x86, 0x0b, 0xa5, 0x46, 0x02, 0x00, 0x80, 0x06, 0x05, 0x00, 0x80, + 0x25, 0x82, 0x0b, 0xa3, 0xa5, 0x80, 0x0b, 0xa1, 0x06, 0x00, 0xf4, 0xef, + 0xd5, 0x84, 0x11, 0x85, 0x21, 0x91, 0x0b, 0x8e, 0x88, 0x74, 0x10, 0xef, + 0x0b, 0xa1, 0xf5, 0x82, 0x0a, 0x9e, 0x1a, 0x9c, 0x24, 0x98, 0x07, 0xe0, + 0x0f, 0xa2, 0x0e, 0xca, 0x0a, 0xde, 0x1a, 0xdc, 0x24, 0x98, 0x03, 0xb0, + 0x07, 0xe0, 0x0f, 0xa2, 0x0e, 0xc8, 0x01, 0x81, 0x0c, 0x0c, 0x0c, 0xf2, + 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x7c, 0xfa, 0x46, 0x42, 0x9c, 0xe0, + 0x0b, 0x02, 0x04, 0xe3, 0xf0, 0x1e, 0x30, 0xec, 0x0b, 0xa3, 0x35, 0x96, + 0x8e, 0x01, 0x01, 0x60, 0x10, 0xc0, 0x0e, 0xfc, 0xc6, 0x05, 0xd0, 0xe1, + 0x0b, 0x82, 0x31, 0x81, 0x10, 0x16, 0x00, 0xe5, 0x20, 0x10, 0x20, 0xe7, + 0x0e, 0xbe, 0xb5, 0x85, 0x94, 0xfc, 0xa4, 0xbe, 0x82, 0x4c, 0x9c, 0xf0, + 0x05, 0x0c, 0x40, 0xe0, 0x11, 0x89, 0x93, 0x8e, 0xa3, 0x8e, 0x58, 0x44, + 0x00, 0xe8, 0x15, 0x0c, 0xc0, 0xf8, 0x04, 0x0c, 0x80, 0xfb, 0x0c, 0xed, + 0x0b, 0x82, 0x1b, 0x8c, 0x48, 0x44, 0x00, 0xe8, 0x15, 0x10, 0x1c, 0xfc, + 0x0e, 0xa8, 0x0b, 0x82, 0x1b, 0x8c, 0xd8, 0x43, 0x00, 0xe8, 0x71, 0x88, + 0x0e, 0xa4, 0x0a, 0x0e, 0x40, 0xe0, 0x35, 0xf8, 0x04, 0xbe, 0x14, 0xbc, + 0x81, 0xa0, 0x03, 0x8e, 0x0e, 0xbe, 0x04, 0xfc, 0x11, 0x82, 0x3b, 0x82, + 0x03, 0x8e, 0x0e, 0xfc, 0x3b, 0xa9, 0x06, 0x0e, 0x00, 0xc0, 0x35, 0x5e, + 0x00, 0xc0, 0xd5, 0xfa, 0xc6, 0x01, 0xd0, 0xe1, 0x7b, 0x80, 0x04, 0x9e, + 0x11, 0x91, 0x98, 0x41, 0x00, 0xe8, 0x24, 0x9c, 0x46, 0x42, 0x9c, 0xe0, + 0x6b, 0x82, 0x03, 0x4c, 0xc4, 0xe0, 0x11, 0x91, 0x0b, 0x84, 0xf8, 0x40, + 0x00, 0xe8, 0x19, 0x0e, 0x20, 0xe5, 0x03, 0x4c, 0xc0, 0xe0, 0x0b, 0x82, + 0x08, 0x72, 0xfc, 0xef, 0x01, 0x4c, 0x24, 0xf9, 0xf1, 0x98, 0x0c, 0x0c, + 0x7c, 0xf2, 0x93, 0xdd, 0x4c, 0x00, 0x00, 0xfa, 0x48, 0x65, 0x2c, 0xef, + 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa, + 0x6b, 0x82, 0x78, 0x6e, 0xfc, 0xee, 0x46, 0x42, 0xec, 0xe0, 0x24, 0x84, + 0x24, 0x02, 0x80, 0xfa, 0x1d, 0xcc, 0x11, 0x83, 0xf5, 0x82, 0x24, 0x02, + 0xa0, 0xe1, 0x14, 0x02, 0x80, 0xfa, 0x1d, 0xcc, 0x11, 0x85, 0x15, 0x82, + 0x27, 0xe1, 0x24, 0x02, 0x80, 0xfa, 0x1d, 0xcc, 0x11, 0x89, 0x86, 0x02, + 0x00, 0x80, 0x0c, 0x0c, 0x00, 0xf2, 0x18, 0x17, 0xfc, 0xfe, 0xc3, 0xc1, + 0x0c, 0x04, 0x00, 0xfa, 0x06, 0x41, 0x8c, 0xe0, 0x1b, 0x00, 0xec, 0xe4, + 0x1b, 0xa3, 0x75, 0x84, 0x11, 0x81, 0x8e, 0x05, 0x01, 0x60, 0x10, 0xc0, + 0x00, 0x06, 0xc0, 0xe5, 0x95, 0x81, 0x44, 0x88, 0x1d, 0xee, 0x75, 0x80, + 0x4e, 0xc1, 0x25, 0x81, 0x4e, 0xcd, 0x21, 0x88, 0x11, 0x82, 0x0a, 0x02, + 0x40, 0xe0, 0xd5, 0xfc, 0x56, 0x00, 0x00, 0xe1, 0x18, 0x80, 0x1b, 0xa1, + 0xc5, 0x84, 0x08, 0x82, 0x4a, 0x00, 0xfc, 0xfb, 0x45, 0x84, 0x86, 0x4d, + 0x84, 0xe1, 0x04, 0x98, 0x05, 0x00, 0x10, 0xe0, 0x4a, 0x40, 0x80, 0xe0, + 0x45, 0x82, 0x11, 0x81, 0x0b, 0x8c, 0x58, 0x76, 0x28, 0xef, 0x0b, 0x8c, + 0x0c, 0x0c, 0x00, 0xf2, 0x88, 0x35, 0x28, 0xff, 0x0c, 0x0c, 0x00, 0xf2, + 0x93, 0xdd, 0xc3, 0xc1, 0x46, 0x41, 0xfc, 0xe0, 0x04, 0x80, 0x09, 0x00, + 0x80, 0xe0, 0x09, 0x9e, 0x0b, 0xa3, 0x75, 0x82, 0x46, 0x41, 0x80, 0xe1, + 0x04, 0x80, 0xc6, 0x42, 0x8c, 0xe0, 0x04, 0xc2, 0x00, 0x40, 0x00, 0xf2, + 0x07, 0xcf, 0x06, 0x84, 0x06, 0x40, 0x84, 0xe0, 0x15, 0x00, 0x28, 0xe5, + 0x1c, 0xc2, 0x93, 0xdd, 0x0b, 0xa1, 0xc6, 0x00, 0xa0, 0xe1, 0x15, 0x00, + 0x04, 0xf8, 0x05, 0x84, 0x21, 0x8b, 0x2c, 0x84, 0x14, 0x80, 0x2c, 0x84, + 0x14, 0x82, 0x2c, 0x84, 0x15, 0x00, 0x10, 0xe0, 0x21, 0xa1, 0x21, 0x42, + 0x10, 0xe0, 0x05, 0x00, 0x14, 0xe0, 0x01, 0x88, 0x75, 0x83, 0x21, 0x85, + 0x2c, 0x84, 0x14, 0x80, 0x06, 0x46, 0x00, 0xe0, 0x2c, 0x84, 0x14, 0x82, + 0x2c, 0x84, 0x14, 0xc0, 0x21, 0xa1, 0x21, 0x42, 0x20, 0xe0, 0x14, 0xc2, + 0x31, 0x42, 0x20, 0xe0, 0x15, 0x00, 0x10, 0xe0, 0x21, 0x42, 0x20, 0xe0, + 0x05, 0x00, 0x14, 0xe0, 0x01, 0x90, 0x06, 0x42, 0x00, 0xe0, 0x16, 0x80, + 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x7c, 0xfa, 0x4a, 0x40, 0x80, 0xe0, + 0xf0, 0x1e, 0x30, 0xec, 0xe5, 0x82, 0xa6, 0x40, 0x00, 0xe1, 0x1a, 0x80, + 0x2a, 0xc0, 0x3a, 0xc2, 0x13, 0x40, 0x10, 0xe0, 0x1a, 0x82, 0x23, 0x40, + 0x18, 0xe0, 0x33, 0x40, 0x1c, 0xe0, 0x13, 0x40, 0x14, 0xe0, 0xf8, 0x61, + 0x68, 0xef, 0xc6, 0x13, 0x00, 0xe1, 0x15, 0x12, 0x28, 0xf8, 0x0b, 0x02, + 0x2c, 0xe0, 0x1b, 0x02, 0x24, 0xe0, 0x8a, 0x00, 0xa5, 0x64, 0x03, 0xc0, + 0x35, 0x82, 0x0a, 0x4e, 0x9c, 0xe1, 0x1a, 0x03, 0x11, 0x6f, 0x02, 0xc0, + 0xe8, 0x13, 0x01, 0x20, 0x00, 0xc0, 0x1f, 0xa0, 0x5a, 0x42, 0x80, 0xe0, + 0x0a, 0x4e, 0x9c, 0xe1, 0x68, 0x13, 0x00, 0xa0, 0x09, 0x12, 0x78, 0xf8, + 0xa1, 0x81, 0xf0, 0x02, 0x10, 0xe4, 0x07, 0xc4, 0x0c, 0xfc, 0xf0, 0x00, + 0x20, 0xe4, 0xa6, 0x91, 0xa8, 0x53, 0x74, 0xef, 0x05, 0x12, 0x30, 0xf8, + 0x25, 0x12, 0x28, 0xf8, 0x61, 0x87, 0x09, 0x00, 0x48, 0xe0, 0x81, 0x85, + 0x09, 0x86, 0x0b, 0xa7, 0x26, 0x0c, 0x00, 0xc0, 0x0b, 0xa1, 0x0b, 0x04, + 0x28, 0xe0, 0x16, 0x0c, 0x00, 0x80, 0x03, 0x52, 0x04, 0xf8, 0x0b, 0x04, + 0x20, 0xe0, 0x0c, 0xa6, 0x1b, 0x04, 0x2c, 0xe0, 0x3b, 0x04, 0x28, 0xe0, + 0x4b, 0x04, 0x20, 0xe0, 0x13, 0x86, 0x3b, 0x04, 0x24, 0xe0, 0x10, 0x0a, + 0x04, 0xec, 0x1a, 0xfc, 0x33, 0x88, 0x30, 0x06, 0x04, 0xec, 0x12, 0x4e, + 0x94, 0xf0, 0x32, 0x48, 0x84, 0xf0, 0x4c, 0xe4, 0x7c, 0xa4, 0xcb, 0x04, + 0x28, 0xe0, 0x14, 0x08, 0x84, 0xe1, 0xcd, 0xc9, 0xc2, 0x58, 0x90, 0x91, + 0x42, 0x4e, 0x94, 0x90, 0xc3, 0x52, 0x04, 0x98, 0x73, 0x52, 0x00, 0x80, + 0x5b, 0x04, 0x20, 0xe0, 0x5d, 0xc9, 0x52, 0x40, 0x90, 0x91, 0x42, 0x48, + 0x8c, 0x90, 0x03, 0x52, 0x04, 0x80, 0x43, 0x52, 0x08, 0x80, 0x3b, 0x04, + 0x2c, 0xe0, 0x49, 0x04, 0xb8, 0xe0, 0x33, 0x52, 0x1c, 0xf8, 0x2b, 0x04, + 0x24, 0xe0, 0x4b, 0xab, 0x23, 0x52, 0x18, 0xf8, 0x65, 0x8a, 0x4b, 0xa9, + 0xe5, 0x90, 0x4b, 0xa7, 0x22, 0x44, 0x84, 0xd0, 0x32, 0x46, 0x84, 0xd0, + 0x33, 0x52, 0x1c, 0xd8, 0x23, 0x52, 0x18, 0xd8, 0x95, 0x96, 0x20, 0x44, + 0xf9, 0x73, 0xff, 0xc0, 0x27, 0xc3, 0x23, 0x82, 0x23, 0x52, 0x18, 0xf8, + 0x24, 0x02, 0x80, 0xfb, 0x04, 0x00, 0x80, 0xfb, 0x2b, 0x8c, 0x58, 0x52, + 0x74, 0xef, 0x1b, 0x12, 0x1c, 0xf8, 0x2a, 0xfc, 0x0c, 0xe4, 0x17, 0xc3, + 0x13, 0x84, 0x13, 0x52, 0x1c, 0xf8, 0x0b, 0x12, 0x04, 0xf8, 0x14, 0x02, + 0x80, 0xfb, 0x2b, 0x8c, 0x68, 0x51, 0x74, 0xef, 0xc5, 0x87, 0x20, 0x44, + 0xe1, 0x73, 0xff, 0xc0, 0x27, 0xc7, 0x23, 0x82, 0x23, 0x52, 0x18, 0xf8, + 0x24, 0x02, 0x80, 0xfb, 0x04, 0x00, 0x80, 0xfb, 0x2b, 0x8c, 0x78, 0x57, + 0x74, 0xef, 0x1b, 0x12, 0x1c, 0xf8, 0x2a, 0xfc, 0x0c, 0xe4, 0x17, 0xc7, + 0x13, 0x84, 0x13, 0x52, 0x1c, 0xf8, 0x0b, 0x12, 0x04, 0xf8, 0x14, 0x02, + 0x80, 0xfb, 0x2b, 0x8c, 0x88, 0x56, 0x74, 0xef, 0xe5, 0x83, 0x20, 0x44, + 0xf1, 0x73, 0xff, 0xc0, 0x27, 0xc5, 0x23, 0x82, 0x23, 0x52, 0x18, 0xf8, + 0x24, 0x02, 0x80, 0xfb, 0x04, 0x00, 0x80, 0xfb, 0x2b, 0x8c, 0x18, 0x52, + 0x74, 0xef, 0x1b, 0x12, 0x1c, 0xf8, 0x2a, 0xfc, 0x0c, 0xe4, 0x17, 0xc5, + 0x13, 0x84, 0x13, 0x52, 0x1c, 0xf8, 0x0b, 0x12, 0x04, 0xf8, 0x14, 0x02, + 0x80, 0xfb, 0x2b, 0x8c, 0x28, 0x51, 0x74, 0xef, 0x7b, 0x80, 0x7c, 0xa4, + 0x08, 0x91, 0xa3, 0x52, 0x1c, 0xe0, 0xa3, 0x52, 0x24, 0xe0, 0x0b, 0xa1, + 0x83, 0x52, 0x1c, 0x80, 0x83, 0x52, 0x24, 0x80, 0x89, 0x12, 0x78, 0xf8, + 0xf6, 0x57, 0xfc, 0xef, 0x6b, 0x12, 0x1c, 0xf8, 0xab, 0x12, 0x18, 0xf8, + 0xd6, 0x57, 0xfc, 0x8f, 0x8b, 0xa3, 0xa0, 0x40, 0x00, 0x9c, 0xa5, 0x86, + 0x64, 0x00, 0x80, 0xfb, 0x1b, 0x90, 0xf8, 0x7d, 0xf8, 0xee, 0x6b, 0x80, + 0xa4, 0x00, 0x80, 0xfb, 0x1b, 0x90, 0x98, 0x7d, 0xf8, 0xee, 0x15, 0x12, + 0x28, 0xf8, 0x19, 0x02, 0xb8, 0xe0, 0x1b, 0xad, 0x95, 0x82, 0x1a, 0xa6, + 0xa0, 0x44, 0xf9, 0x73, 0xff, 0xc0, 0x27, 0xc3, 0x13, 0x94, 0x10, 0x02, + 0x08, 0xec, 0x1c, 0xe4, 0x23, 0x52, 0x18, 0xf8, 0x1b, 0x12, 0x04, 0xf8, + 0x03, 0x96, 0x03, 0x52, 0x28, 0xe0, 0x1c, 0xe6, 0x0a, 0xa6, 0x1a, 0xe4, + 0x63, 0x96, 0x63, 0x52, 0x20, 0xe0, 0x73, 0x52, 0x10, 0xe0, 0x03, 0x52, + 0x14, 0xe0, 0x13, 0x52, 0x18, 0xe0, 0x98, 0x52, 0x74, 0xef, 0x09, 0x12, + 0x8c, 0xe0, 0x0b, 0xa1, 0x01, 0x81, 0x01, 0x52, 0x90, 0xe0, 0x65, 0x82, + 0x05, 0x12, 0x30, 0xf8, 0x09, 0x00, 0xa8, 0xe0, 0x0a, 0x00, 0x0c, 0xf8, + 0x16, 0x00, 0x00, 0xc0, 0x01, 0x52, 0x90, 0xc0, 0x46, 0x41, 0x84, 0xe0, + 0x0a, 0x80, 0x0a, 0x4e, 0x9c, 0xe9, 0x1a, 0x00, 0x08, 0xe0, 0x38, 0x01, + 0x01, 0x20, 0x00, 0xc0, 0x0b, 0x12, 0x1c, 0xe0, 0x1b, 0x12, 0x24, 0xe0, + 0x2b, 0x12, 0x28, 0xe0, 0x03, 0x52, 0x2c, 0xe0, 0x0b, 0x12, 0x20, 0xe0, + 0x13, 0x52, 0x34, 0xe0, 0x23, 0x52, 0x38, 0xe0, 0x03, 0x52, 0x30, 0xe0, + 0x0c, 0x00, 0x00, 0xe2, 0xf1, 0x98, 0x0c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd, + 0x13, 0xa9, 0x00, 0x00, 0xa8, 0xc1, 0x40, 0x00, 0x68, 0x04, 0xa0, 0xe0, + 0x40, 0x6c, 0x40, 0x00, 0xe8, 0x34, 0xc8, 0xe0, 0xfc, 0x91, 0x40, 0x00, + 0x68, 0x1f, 0xb8, 0xe0, 0x30, 0x16, 0x41, 0x00, 0x28, 0x39, 0x74, 0xe0, + 0xb0, 0x7e, 0x40, 0x00, 0xe8, 0x38, 0xc0, 0xe0, 0x30, 0x04, 0x41, 0x00, + 0x48, 0x1b, 0x80, 0xe0, 0x30, 0x2e, 0x40, 0x00, 0x88, 0x0c, 0xec, 0xe0, + 0x10, 0x9f, 0x40, 0x00, 0x88, 0x08, 0xb4, 0xe0, 0x10, 0x01, 0x41, 0x00, + 0x68, 0x01, 0x84, 0xe0, 0x54, 0xd6, 0x40, 0x00, 0xc8, 0x1a, 0x98, 0xe0, + 0xd0, 0xc8, 0x40, 0x00, 0x68, 0x08, 0xa0, 0xe0, 0x80, 0xdb, 0x40, 0x00, + 0xe8, 0x35, 0x94, 0xe0, 0x74, 0xff, 0x40, 0x00, 0xa8, 0x11, 0x80, 0xe0, + 0xf8, 0x89, 0x40, 0x00, 0x88, 0x16, 0xbc, 0xe0, 0x00, 0x90, 0x40, 0x00, + 0x08, 0x35, 0xb8, 0xe0, 0x7c, 0x73, 0x40, 0x00, 0x88, 0x1b, 0xc8, 0xe0, + 0xf4, 0xff, 0x40, 0x00, 0x68, 0x39, 0x80, 0xe0, 0xa4, 0xa4, 0x40, 0x00, + 0xa8, 0x16, 0xb0, 0xe0, 0x50, 0xc9, 0x40, 0x00, 0x28, 0x3a, 0x98, 0xe0, + 0x00, 0xb9, 0x00, 0x00, 0xb6, 0x85, 0x00, 0x00, +}; + +static const char * const vd55g1_tp_menu[] = { + "Disabled", + "Diagonal Grey Scale", + "Pseudo-random Noise", +}; + +static const s64 vd55g1_ev_bias_menu[] = { + -3000, -2500, -2000, -1500, -1000, -500, + 0, + 500, 1000, 1500, 2000, 2500, 3000, +}; + +static const char * const vd55g1_hdr_menu[] = { + "No HDR", + /* + * This mode acquires 2 frames on the sensor, the first one is ditched + * out and only used for auto exposure data, the second one is output to + * the host + */ + "Internal subtraction", +}; + +static const char * const vd55g1_supply_name[] = { + "vcore", + "vddio", + "vana", +}; + +enum vd55g1_hdr_mode { + VD55G1_NO_HDR, + VD55G1_HDR_SUB, +}; + +struct vd55g1_mode { + u32 width; + u32 height; +}; + +struct vd55g1_fmt_desc { + u32 code; + u8 bpp; + u8 data_type; +}; + +static const struct vd55g1_fmt_desc vd55g1_mbus_codes[] = { + { + .code = MEDIA_BUS_FMT_Y8_1X8, + .bpp = 8, + .data_type = MIPI_CSI2_DT_RAW8, + }, + { + .code = MEDIA_BUS_FMT_Y10_1X10, + .bpp = 10, + .data_type = MIPI_CSI2_DT_RAW10, + }, +}; + +static const struct vd55g1_mode vd55g1_supported_modes[] = { + { + .width = VD55G1_WIDTH, + .height = VD55G1_HEIGHT, + }, + { + .width = 800, + .height = VD55G1_HEIGHT, + }, + { + .width = 800, + .height = 600, + }, + { + .width = 640, + .height = 480, + }, + { + .width = 320, + .height = 240, + }, +}; + +enum vd55g1_expo_state { + VD55G1_EXP_AUTO, + VD55G1_EXP_FREEZE, + VD55G1_EXP_MANUAL, + VD55G1_EXP_SINGLE_STEP, + VD55G1_EXP_BYPASS, +}; + +struct vd55g1_vblank_limits { + u16 min; + u16 def; + u16 max; +}; + +struct vd55g1 { + struct device *dev; + struct v4l2_subdev sd; + struct media_pad pad; + struct regulator_bulk_data supplies[ARRAY_SIZE(vd55g1_supply_name)]; + struct gpio_desc *reset_gpio; + struct clk *xclk; + struct regmap *regmap; + u32 xclk_freq; + u16 oif_ctrl; + u8 gpios[VD55G1_NB_GPIOS]; + unsigned long ext_leds_mask; + u32 mipi_rate; + u32 pixel_clock; + u64 link_freq; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *pixel_rate_ctrl; + struct v4l2_ctrl *vblank_ctrl; + struct v4l2_ctrl *hblank_ctrl; + struct { + struct v4l2_ctrl *hflip_ctrl; + struct v4l2_ctrl *vflip_ctrl; + }; + struct v4l2_ctrl *patgen_ctrl; + struct { + struct v4l2_ctrl *ae_ctrl; + struct v4l2_ctrl *expo_ctrl; + struct v4l2_ctrl *again_ctrl; + struct v4l2_ctrl *dgain_ctrl; + }; + struct v4l2_ctrl *ae_lock_ctrl; + struct v4l2_ctrl *ae_bias_ctrl; + struct v4l2_ctrl *led_ctrl; + struct v4l2_ctrl *hdr_ctrl; +}; + +static inline struct vd55g1 *to_vd55g1(struct v4l2_subdev *sd) +{ + return container_of_const(sd, struct vd55g1, sd); +} + +static inline struct vd55g1 *ctrl_to_vd55g1(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = &container_of_const(ctrl->handler, + struct vd55g1, + ctrl_handler)->sd; + + return to_vd55g1(sd); +} + +static const struct vd55g1_fmt_desc *vd55g1_get_fmt_desc(struct vd55g1 *sensor, + u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vd55g1_mbus_codes); i++) { + if (vd55g1_mbus_codes[i].code == code) + return &vd55g1_mbus_codes[i]; + } + + /* Should never happen */ + dev_warn(sensor->dev, "Unsupported code %d. default to 8 bpp\n", code); + + return &vd55g1_mbus_codes[0]; +} + +static s32 vd55g1_get_pixel_rate(struct vd55g1 *sensor, + struct v4l2_mbus_framefmt *format) +{ + return sensor->mipi_rate / + vd55g1_get_fmt_desc(sensor, format->code)->bpp; +} + +static unsigned int vd55g1_get_hblank_min(struct vd55g1 *sensor, + struct v4l2_mbus_framefmt *format, + struct v4l2_rect *crop) +{ + u32 mipi_req_line_time; + u32 mipi_req_line_length; + u32 min_line_length; + + /* MIPI required time */ + mipi_req_line_time = (crop->width * + vd55g1_get_fmt_desc(sensor, format->code)->bpp + + VD55G1_MIPI_MARGIN) / + (sensor->mipi_rate / MEGA); + mipi_req_line_length = mipi_req_line_time * sensor->pixel_clock / + HZ_PER_MHZ; + + /* Absolute time required for ADCs to convert pixels */ + min_line_length = VD55G1_LINE_LENGTH_MIN; + if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB) + min_line_length = VD55G1_LINE_LENGTH_SUB_MIN; + + /* Respect both constraint */ + min_line_length = max(min_line_length, mipi_req_line_length); + + return min_line_length - crop->width; +} + +static void vd55g1_get_vblank_limits(struct vd55g1 *sensor, + struct v4l2_rect *crop, + struct vd55g1_vblank_limits *limits) +{ + limits->min = VD55G1_VBLANK_MIN; + limits->def = VD55G1_FRAME_LENGTH_DEF - crop->height; + limits->max = VD55G1_VBLANK_MAX - crop->height; +} + +#define vd55g1_read(sensor, reg, val, err) \ + cci_read((sensor)->regmap, reg, val, err) + +#define vd55g1_write(sensor, reg, val, err) \ + cci_write((sensor)->regmap, reg, val, err) + +static int vd55g1_write_array(struct vd55g1 *sensor, u32 reg, unsigned int len, + const u8 *array, int *err) +{ + unsigned int chunk_sz = 1024; + unsigned int sz; + int ret = 0; + + if (err && *err) + return *err; + + /* + * This loop isn't necessary but in certains conditions (platforms, cpu + * load, etc.) it has been observed that the bulk write could timeout. + */ + while (len) { + sz = min(len, chunk_sz); + ret = regmap_bulk_write(sensor->regmap, reg, array, sz); + if (ret < 0) + goto out; + len -= sz; + reg += sz; + array += sz; + } + +out: + if (ret && err) + *err = ret; + + return ret; +} + +static int vd55g1_poll_reg(struct vd55g1 *sensor, u32 reg, u8 poll_val, + int *err) +{ + unsigned int val = 0; + int ret; + + if (err && *err) + return *err; + + ret = regmap_read_poll_timeout(sensor->regmap, CCI_REG_ADDR(reg), val, + (val == poll_val), 2000, + 500 * USEC_PER_MSEC); + + if (ret && err) + *err = ret; + + return ret; +} + +static int vd55g1_wait_state(struct vd55g1 *sensor, int state, int *err) +{ + return vd55g1_poll_reg(sensor, VD55G1_REG_SYSTEM_FSM, state, err); +} + +static int vd55g1_prepare_clock_tree(struct vd55g1 *sensor) +{ + u32 sys_clk, mipi_div, pixel_div; + + if (sensor->xclk_freq < VD55G1_XCLK_FREQ_MIN || + sensor->xclk_freq > VD55G1_XCLK_FREQ_MAX) { + dev_err(sensor->dev, + "Only %luMhz-%luMhz clock range supported. Provided %lu MHz\n", + VD55G1_XCLK_FREQ_MIN / HZ_PER_MHZ, + VD55G1_XCLK_FREQ_MAX / HZ_PER_MHZ, + sensor->xclk_freq / HZ_PER_MHZ); + return -EINVAL; + } + + /* MIPI bus is double data rate */ + sensor->mipi_rate = sensor->link_freq * 2; + + if (sensor->mipi_rate < VD55G1_MIPI_RATE_MIN || + sensor->mipi_rate > VD55G1_MIPI_RATE_MAX) { + dev_err(sensor->dev, + "Only %luMbps-%luMbps data rate range supported. Provided %lu Mbps\n", + VD55G1_MIPI_RATE_MIN / MEGA, + VD55G1_MIPI_RATE_MAX / MEGA, + sensor->mipi_rate / MEGA); + return -EINVAL; + } + + if (sensor->mipi_rate <= 300 * MEGA) + mipi_div = 4; + else if (sensor->mipi_rate <= 600 * MEGA) + mipi_div = 2; + else + mipi_div = 1; + + sys_clk = sensor->mipi_rate * mipi_div; + + if (sys_clk <= 780 * HZ_PER_MHZ) + pixel_div = 5; + else if (sys_clk <= 900 * HZ_PER_MHZ) + pixel_div = 6; + else + pixel_div = 8; + + sensor->pixel_clock = sys_clk / pixel_div; + + return 0; +} + +static int vd55g1_update_patgen(struct vd55g1 *sensor, u32 patgen_index) +{ + static const u8 index2val[] = { + 0x0, 0x22, 0x28 + }; + u32 pattern = index2val[patgen_index]; + u32 reg = pattern << VD55G1_PATGEN_TYPE_SHIFT; + u8 duster = VD55G1_DUSTER_RING_ENABLE | VD55G1_DUSTER_DYN_ENABLE | + VD55G1_DUSTER_ENABLE; + int ret = 0; + + BUILD_BUG_ON(ARRAY_SIZE(index2val) != ARRAY_SIZE(vd55g1_tp_menu)); + + if (pattern != 0) { + reg |= VD55G1_PATGEN_ENABLE; + /* Take care of duster to not mess up the test pattern output */ + duster = VD55G1_DUSTER_DISABLE; + } + + vd55g1_write(sensor, VD55G1_REG_DUSTER_CTRL, duster, &ret); + vd55g1_write(sensor, VD55G1_REG_PATGEN_CTRL, reg, &ret); + + return ret; +} + +static int vd55g1_update_expo_cluster(struct vd55g1 *sensor, bool is_auto) +{ + enum vd55g1_expo_state expo_state = is_auto ? VD55G1_EXP_AUTO : + VD55G1_EXP_MANUAL; + int ret = 0; + + if (sensor->ae_ctrl->is_new) + vd55g1_write(sensor, VD55G1_REG_EXP_MODE(0), expo_state, &ret); + + if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB && + sensor->hdr_ctrl->is_new) { + vd55g1_write(sensor, VD55G1_REG_EXP_MODE(1), VD55G1_EXP_BYPASS, + &ret); + if (ret) + return ret; + } + + if (!is_auto && sensor->expo_ctrl->is_new) + vd55g1_write(sensor, VD55G1_REG_MANUAL_COARSE_EXPOSURE, + sensor->expo_ctrl->val, &ret); + + if (!is_auto && sensor->again_ctrl->is_new) + vd55g1_write(sensor, VD55G1_REG_MANUAL_ANALOG_GAIN, + sensor->again_ctrl->val, &ret); + + if (!is_auto && sensor->dgain_ctrl->is_new) + vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN, + sensor->dgain_ctrl->val, &ret); + + return ret; +} + +static int vd55g1_lock_exposure(struct vd55g1 *sensor, u32 lock_val) +{ + bool ae_lock = lock_val & V4L2_LOCK_EXPOSURE; + enum vd55g1_expo_state expo_state = ae_lock ? VD55G1_EXP_FREEZE : + VD55G1_EXP_AUTO; + int ret = 0; + + if (sensor->ae_ctrl->val == V4L2_EXPOSURE_AUTO) + vd55g1_write(sensor, VD55G1_REG_EXP_MODE(0), expo_state, &ret); + + return ret; +} + +static int vd55g1_read_expo_cluster(struct vd55g1 *sensor) +{ + u64 exposure = 0; + u64 again = 0; + u64 dgain = 0; + int ret = 0; + + vd55g1_read(sensor, VD55G1_REG_APPLIED_COARSE_EXPOSURE, &exposure, + &ret); + vd55g1_read(sensor, VD55G1_REG_APPLIED_ANALOG_GAIN, &again, &ret); + vd55g1_read(sensor, VD55G1_REG_APPLIED_DIGITAL_GAIN, &dgain, &ret); + if (ret) + return ret; + + sensor->expo_ctrl->cur.val = exposure; + sensor->again_ctrl->cur.val = again; + sensor->dgain_ctrl->cur.val = dgain; + + return 0; +} + +static int vd55g1_update_frame_length(struct vd55g1 *sensor, + unsigned int frame_length) +{ + int ret = 0; + + if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB) + vd55g1_write(sensor, VD55G1_REG_FRAME_LENGTH(1), frame_length, + &ret); + vd55g1_write(sensor, VD55G1_REG_FRAME_LENGTH(0), frame_length, &ret); + + return ret; +} + +static int vd55g1_update_exposure_target(struct vd55g1 *sensor, int index) +{ + /* + * Find auto exposure target with: default target exposure * 2^EV + * Defaut target exposure being 27 for the sensor. + */ + static const unsigned int index2exposure_target[] = { + 3, 5, 7, 10, 14, 19, 27, 38, 54, 76, 108, 153, 216, + }; + int exposure_target = index2exposure_target[index]; + + return vd55g1_write(sensor, VD55G1_REG_AE_TARGET_PERCENTAGE, + exposure_target, NULL); +} + +static int vd55g1_apply_cold_start(struct vd55g1 *sensor, + struct v4l2_rect *crop) +{ + /* + * Cold start register is a single register expressed as exposure time + * in us. This differ from status registers being a combination of + * exposure, digital gain, and analog gain, requiring the following + * format conversion. + */ + unsigned int line_length = crop->width + sensor->hblank_ctrl->val; + unsigned int line_time_us = DIV_ROUND_UP(line_length * MEGA, + sensor->pixel_clock); + u8 d_gain = DIV_ROUND_CLOSEST(sensor->dgain_ctrl->val, 1 << 8); + u8 a_gain = DIV_ROUND_CLOSEST(32, (32 - sensor->again_ctrl->val)); + unsigned int expo_us = sensor->expo_ctrl->val * d_gain * a_gain * + line_time_us; + int ret = 0; + + vd55g1_write(sensor, VD55G1_REG_AE_FORCE_COLDSTART, 1, &ret); + vd55g1_write(sensor, VD55G1_REG_AE_COLDSTART_EXP_TIME, expo_us, &ret); + + return ret; +} + +static void vd55g1_update_img_pad_format(struct vd55g1 *sensor, + const struct vd55g1_mode *mode, + u32 code, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->code = code; + fmt->width = mode->width; + fmt->height = mode->height; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->field = V4L2_FIELD_NONE; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int vd55g1_update_hdr_mode(struct vd55g1 *sensor) +{ + int ret = 0; + + switch (sensor->hdr_ctrl->val) { + case VD55G1_NO_HDR: + vd55g1_write(sensor, VD55G1_REG_EXPOSURE_MAX_COARSE, + VD55G1_EXPOSURE_MAX_COARSE_DEF, &ret); + vd55g1_write(sensor, VD55G1_REG_EXPOSURE_USE_CASES, 0, &ret); + vd55g1_write(sensor, VD55G1_REG_NEXT_CTX, 0x0, &ret); + + vd55g1_write(sensor, VD55G1_REG_CTX_REPEAT_COUNT_CTX0, 0, &ret); + + vd55g1_write(sensor, VD55G1_REG_VT_MODE(0), + VD55G1_VT_MODE_NORMAL, &ret); + vd55g1_write(sensor, VD55G1_REG_MASK_FRAME_CTRL(0), + VD55G1_MASK_FRAME_CTRL_OUTPUT, &ret); + break; + case VD55G1_HDR_SUB: + vd55g1_write(sensor, VD55G1_REG_EXPOSURE_MAX_COARSE, + VD55G1_EXPOSURE_MAX_COARSE_SUB, &ret); + vd55g1_write(sensor, VD55G1_REG_EXPOSURE_USE_CASES, + VD55G1_EXPOSURE_USE_CASES_MULTI_CONTEXT, &ret); + vd55g1_write(sensor, VD55G1_REG_NEXT_CTX, 0x0001, &ret); + + vd55g1_write(sensor, VD55G1_REG_CTX_REPEAT_COUNT_CTX0, 1, &ret); + vd55g1_write(sensor, VD55G1_REG_CTX_REPEAT_COUNT_CTX1, 1, &ret); + + vd55g1_write(sensor, VD55G1_REG_VT_MODE(0), + VD55G1_VT_MODE_NORMAL, &ret); + vd55g1_write(sensor, VD55G1_REG_MASK_FRAME_CTRL(0), + VD55G1_MASK_FRAME_CTRL_MASK, &ret); + vd55g1_write(sensor, VD55G1_REG_EXPOSURE_INSTANCE(0), 0, &ret); + vd55g1_write(sensor, VD55G1_REG_VT_MODE(1), + VD55G1_VT_MODE_SUBTRACTION, &ret); + vd55g1_write(sensor, VD55G1_REG_MASK_FRAME_CTRL(1), + VD55G1_MASK_FRAME_CTRL_OUTPUT, &ret); + vd55g1_write(sensor, VD55G1_REG_EXPOSURE_INSTANCE(1), 1, &ret); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int vd55g1_set_framefmt(struct vd55g1 *sensor, + struct v4l2_mbus_framefmt *format, + struct v4l2_rect *crop) +{ + u8 binning; + int ret = 0; + + vd55g1_write(sensor, VD55G1_REG_FORMAT_CTRL, + vd55g1_get_fmt_desc(sensor, format->code)->bpp, &ret); + vd55g1_write(sensor, VD55G1_REG_OIF_IMG_CTRL, + vd55g1_get_fmt_desc(sensor, format->code)->data_type, + &ret); + + switch (crop->width / format->width) { + case 1: + default: + binning = VD55G1_READOUT_CTRL_BIN_MODE_NORMAL; + break; + case 2: + binning = VD55G1_READOUT_CTRL_BIN_MODE_DIGITAL_X2; + break; + } + vd55g1_write(sensor, VD55G1_REG_READOUT_CTRL, binning, &ret); + + vd55g1_write(sensor, VD55G1_REG_X_START(0), crop->left, &ret); + vd55g1_write(sensor, VD55G1_REG_X_WIDTH(0), crop->width, &ret); + vd55g1_write(sensor, VD55G1_REG_Y_START(0), crop->top, &ret); + vd55g1_write(sensor, VD55G1_REG_Y_HEIGHT(0), crop->height, &ret); + + vd55g1_write(sensor, VD55G1_REG_X_START(1), crop->left, &ret); + vd55g1_write(sensor, VD55G1_REG_X_WIDTH(1), crop->width, &ret); + vd55g1_write(sensor, VD55G1_REG_Y_START(1), crop->top, &ret); + vd55g1_write(sensor, VD55G1_REG_Y_HEIGHT(1), crop->height, &ret); + + return ret; +} + +static int vd55g1_update_gpios(struct vd55g1 *sensor, unsigned long gpio_mask) +{ + unsigned long io; + u8 gpio_val; + int ret = 0; + + for_each_set_bit(io, &gpio_mask, VD55G1_NB_GPIOS) { + gpio_val = sensor->gpios[io]; + + if (gpio_val == VD55G1_GPIO_MODE_STROBE && + sensor->led_ctrl->val == V4L2_FLASH_LED_MODE_NONE) { + gpio_val = VD55G1_GPIO_MODE_IN; + if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB) { + /* Make its context 1 counterpart strobe too */ + vd55g1_write(sensor, + VD55G1_REG_GPIO_0_CTRL(1) + io, + gpio_val, &ret); + } + } + + ret = vd55g1_write(sensor, VD55G1_REG_GPIO_0_CTRL(0) + io, + gpio_val, &ret); + } + + return ret; +} + +static int vd55g1_ro_ctrls_setup(struct vd55g1 *sensor, struct v4l2_rect *crop) +{ + return vd55g1_write(sensor, VD55G1_REG_LINE_LENGTH, + crop->width + sensor->hblank_ctrl->val, NULL); +} + +static void vd55g1_grab_ctrls(struct vd55g1 *sensor, bool enable) +{ + /* These settings cannot change during stream */ + v4l2_ctrl_grab(sensor->hflip_ctrl, enable); + v4l2_ctrl_grab(sensor->vflip_ctrl, enable); + v4l2_ctrl_grab(sensor->patgen_ctrl, enable); + v4l2_ctrl_grab(sensor->hdr_ctrl, enable); +} + +static int vd55g1_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct vd55g1 *sensor = to_vd55g1(sd); + struct v4l2_rect *crop = + v4l2_subdev_state_get_crop(state, 0); + struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 0); + int ret; + + ret = pm_runtime_resume_and_get(sensor->dev); + if (ret < 0) + return ret; + + vd55g1_write(sensor, VD55G1_REG_EXT_CLOCK, sensor->xclk_freq, &ret); + + /* Configure output */ + vd55g1_write(sensor, VD55G1_REG_MIPI_DATA_RATE, + sensor->mipi_rate, &ret); + vd55g1_write(sensor, VD55G1_REG_OIF_CTRL, sensor->oif_ctrl, &ret); + vd55g1_write(sensor, VD55G1_REG_ISL_ENABLE, 0, &ret); + if (ret) + goto err_rpm_put; + + ret = vd55g1_set_framefmt(sensor, format, crop); + if (ret) + goto err_rpm_put; + + /* Setup default GPIO values; could be overridden by V4L2 ctrl setup */ + ret = vd55g1_update_gpios(sensor, GENMASK(VD55G1_NB_GPIOS - 1, 0)); + if (ret) + goto err_rpm_put; + + ret = vd55g1_apply_cold_start(sensor, crop); + if (ret) + goto err_rpm_put; + + /* Apply settings from V4L2 ctrls */ + ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler); + if (ret) + goto err_rpm_put; + + /* Also apply settings from read-only V4L2 ctrls */ + ret = vd55g1_ro_ctrls_setup(sensor, crop); + if (ret) + goto err_rpm_put; + + /* Start streaming */ + vd55g1_write(sensor, VD55G1_REG_STBY, VD55G1_STBY_START_STREAM, &ret); + vd55g1_poll_reg(sensor, VD55G1_REG_STBY, 0, &ret); + vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_STREAMING, &ret); + if (ret) + goto err_rpm_put; + + vd55g1_grab_ctrls(sensor, true); + + return 0; + +err_rpm_put: + pm_runtime_put(sensor->dev); + return 0; +} + +static int vd55g1_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct vd55g1 *sensor = to_vd55g1(sd); + int ret = 0; + + /* Retrieve Expo cluster to enable coldstart of AE */ + ret = vd55g1_read_expo_cluster(sensor); + + vd55g1_write(sensor, VD55G1_REG_STREAMING, VD55G1_STREAMING_STOP_STREAM, + &ret); + vd55g1_poll_reg(sensor, VD55G1_REG_STREAMING, 0, &ret); + vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_SW_STBY, &ret); + + if (ret) + dev_warn(sensor->dev, "Can't disable stream\n"); + + vd55g1_grab_ctrls(sensor, false); + + pm_runtime_mark_last_busy(sensor->dev); + pm_runtime_put_autosuspend(sensor->dev); + + return ret; +} + +static int vd55g1_patch(struct vd55g1 *sensor) +{ + u64 patch; + int ret = 0; + + vd55g1_write_array(sensor, VD55G1_REG_FWPATCH_START_ADDR, + sizeof(patch_array), patch_array, &ret); + vd55g1_write(sensor, VD55G1_REG_BOOT, VD55G1_BOOT_PATCH_SETUP, &ret); + vd55g1_poll_reg(sensor, VD55G1_REG_BOOT, 0, &ret); + if (ret) { + dev_err(sensor->dev, "Failed to apply patch\n"); + return ret; + } + + vd55g1_read(sensor, VD55G1_REG_FWPATCH_REVISION, &patch, &ret); + if (patch != (VD55G1_FWPATCH_REVISION_MAJOR << 8) + + VD55G1_FWPATCH_REVISION_MINOR) { + dev_err(sensor->dev, "Bad patch version expected %d.%d got %d.%d\n", + VD55G1_FWPATCH_REVISION_MAJOR, + VD55G1_FWPATCH_REVISION_MINOR, + (u8)(patch >> 8), (u8)(patch & 0xff)); + return -ENODEV; + } + dev_dbg(sensor->dev, "patch %d.%d applied\n", + (u8)(patch >> 8), (u8)(patch & 0xff)); + + return 0; +} + +static int vd55g1_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + const struct v4l2_rect *crop = v4l2_subdev_state_get_crop(sd_state, 0); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *crop; + return 0; + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = VD55G1_WIDTH; + sel->r.height = VD55G1_HEIGHT; + return 0; + } + + return -EINVAL; +} + +static int vd55g1_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(vd55g1_mbus_codes)) + return -EINVAL; + + code->code = vd55g1_mbus_codes[code->index].code; + + return 0; +} + +static int vd55g1_new_format_change_controls(struct vd55g1 *sensor, + struct v4l2_mbus_framefmt *format, + struct v4l2_rect *crop) +{ + struct vd55g1_vblank_limits vblank; + unsigned int hblank; + unsigned int frame_length = 0; + unsigned int expo_max; + int ret; + + /* Reset vblank and frame length to default */ + vd55g1_get_vblank_limits(sensor, crop, &vblank); + ret = __v4l2_ctrl_modify_range(sensor->vblank_ctrl, vblank.min, + vblank.max, 1, vblank.def); + if (ret) + return ret; + + /* Max exposure changes with vblank */ + frame_length = crop->height + sensor->vblank_ctrl->val; + expo_max = frame_length - VD55G1_EXPO_MAX_TERM; + ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, expo_max, 1, + VD55G1_EXPO_DEF); + if (ret) + return ret; + + /* Update pixel rate to reflect new bpp */ + ret = __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_ctrl, + vd55g1_get_pixel_rate(sensor, format)); + if (ret) + return ret; + + /* Update hblank according to new width */ + hblank = vd55g1_get_hblank_min(sensor, format, crop); + ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl, hblank, hblank, 1, + hblank); + + return ret; +} + +static int vd55g1_set_pad_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *sd_fmt) +{ + struct vd55g1 *sensor = to_vd55g1(sd); + const struct vd55g1_mode *new_mode; + struct v4l2_mbus_framefmt *format; + struct v4l2_rect pad_crop; + unsigned int binning; + + new_mode = v4l2_find_nearest_size(vd55g1_supported_modes, + ARRAY_SIZE(vd55g1_supported_modes), + width, height, sd_fmt->format.width, + sd_fmt->format.height); + + vd55g1_update_img_pad_format(sensor, new_mode, sd_fmt->format.code, + &sd_fmt->format); + + /* + * Use binning to maximize the crop rectangle size, and centre it in the + * sensor. + */ + binning = min(VD55G1_WIDTH / sd_fmt->format.width, + VD55G1_HEIGHT / sd_fmt->format.height); + binning = min(binning, 2U); + pad_crop.width = sd_fmt->format.width * binning; + pad_crop.height = sd_fmt->format.height * binning; + pad_crop.left = (VD55G1_WIDTH - pad_crop.width) / 2; + pad_crop.top = (VD55G1_HEIGHT - pad_crop.height) / 2; + + format = v4l2_subdev_state_get_format(sd_state, sd_fmt->pad); + + *format = sd_fmt->format; + + *v4l2_subdev_state_get_crop(sd_state, sd_fmt->pad) = pad_crop; + if (sd_fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return vd55g1_new_format_change_controls(sensor, + &sd_fmt->format, + &pad_crop); + + return 0; +} + +static int vd55g1_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + unsigned int def_mode = VD55G1_DEFAULT_MODE; + struct vd55g1 *sensor = to_vd55g1(sd); + struct v4l2_subdev_format fmt = { 0 }; + struct v4l2_subdev_route routes[] = { + { .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE } + }; + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + int ret; + + /* Needed by v4l2_subdev_s_stream_helper(), even with 1 stream only */ + ret = v4l2_subdev_set_routing(sd, sd_state, &routing); + if (ret) + return ret; + + vd55g1_update_img_pad_format(sensor, &vd55g1_supported_modes[def_mode], + VD55G1_MEDIA_BUS_FMT_DEF, &fmt.format); + + return vd55g1_set_pad_fmt(sd, sd_state, &fmt); +} + +static int vd55g1_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(vd55g1_supported_modes)) + return -EINVAL; + + fse->min_width = vd55g1_supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = vd55g1_supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static const struct v4l2_subdev_internal_ops vd55g1_internal_ops = { + .init_state = vd55g1_init_state, +}; + +static const struct v4l2_subdev_pad_ops vd55g1_pad_ops = { + .enum_mbus_code = vd55g1_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = vd55g1_set_pad_fmt, + .get_selection = vd55g1_get_selection, + .enum_frame_size = vd55g1_enum_frame_size, + .enable_streams = vd55g1_enable_streams, + .disable_streams = vd55g1_disable_streams, +}; + +static const struct v4l2_subdev_video_ops vd55g1_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_ops vd55g1_subdev_ops = { + .video = &vd55g1_video_ops, + .pad = &vd55g1_pad_ops, +}; + +static int vd55g1_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vd55g1 *sensor = ctrl_to_vd55g1(ctrl); + int ret = 0; + + /* Interact with HW only when it is powered ON */ + if (!pm_runtime_get_if_in_use(sensor->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE_AUTO: + ret = vd55g1_read_expo_cluster(sensor); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_mark_last_busy(sensor->dev); + pm_runtime_put_autosuspend(sensor->dev); + + return ret; +} + +static int vd55g1_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vd55g1 *sensor = ctrl_to_vd55g1(ctrl); + unsigned int frame_length = 0; + unsigned int expo_max; + struct v4l2_subdev_state *state = + v4l2_subdev_get_locked_active_state(&sensor->sd); + struct v4l2_rect *crop = + v4l2_subdev_state_get_crop(state, 0); + struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 0); + unsigned int hblank = vd55g1_get_hblank_min(sensor, format, crop); + bool is_auto = false; + int ret = 0; + + if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) + return 0; + + /* Update controls state, range, etc. whatever the state of the HW */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + frame_length = crop->height + ctrl->val; + expo_max = frame_length - VD55G1_EXPO_MAX_TERM; + ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, expo_max, + 1, VD55G1_EXPO_DEF); + break; + case V4L2_CID_EXPOSURE_AUTO: + is_auto = (ctrl->val == V4L2_EXPOSURE_AUTO); + __v4l2_ctrl_grab(sensor->ae_lock_ctrl, !is_auto); + __v4l2_ctrl_grab(sensor->ae_bias_ctrl, !is_auto); + break; + case V4L2_CID_HDR_SENSOR_MODE: + /* Discriminate if the userspace changed the control value */ + if (ctrl->val != ctrl->cur.val) { + /* Max horizontal blanking changes with hdr mode */ + ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl, + hblank, hblank, 1, + hblank); + } + break; + default: + break; + } + + /* Don't modify hardware if controls modification failed */ + if (ret) + return ret; + + /* Interact with HW only when it is powered ON */ + if (!pm_runtime_get_if_in_use(sensor->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + ret = vd55g1_write(sensor, VD55G1_REG_ORIENTATION, + sensor->hflip_ctrl->val | + (sensor->vflip_ctrl->val << 1), + NULL); + break; + case V4L2_CID_TEST_PATTERN: + ret = vd55g1_update_patgen(sensor, ctrl->val); + break; + case V4L2_CID_EXPOSURE_AUTO: + ret = vd55g1_update_expo_cluster(sensor, is_auto); + break; + case V4L2_CID_3A_LOCK: + ret = vd55g1_lock_exposure(sensor, ctrl->val); + break; + case V4L2_CID_AUTO_EXPOSURE_BIAS: + /* + * We use auto exposure target percentage register to control + * exposure bias for more precision. + */ + ret = vd55g1_update_exposure_target(sensor, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = vd55g1_update_frame_length(sensor, frame_length); + break; + case V4L2_CID_FLASH_LED_MODE: + ret = vd55g1_update_gpios(sensor, sensor->ext_leds_mask); + break; + case V4L2_CID_HDR_SENSOR_MODE: + ret = vd55g1_update_hdr_mode(sensor); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_mark_last_busy(sensor->dev); + pm_runtime_put_autosuspend(sensor->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops vd55g1_ctrl_ops = { + .g_volatile_ctrl = vd55g1_g_volatile_ctrl, + .s_ctrl = vd55g1_s_ctrl, +}; + +static int vd55g1_init_ctrls(struct vd55g1 *sensor) +{ + const struct v4l2_ctrl_ops *ops = &vd55g1_ctrl_ops; + struct v4l2_ctrl_handler *hdl = &sensor->ctrl_handler; + struct v4l2_ctrl *ctrl; + struct v4l2_fwnode_device_properties fwnode_props; + struct vd55g1_vblank_limits vblank; + unsigned int hblank; + struct v4l2_subdev_state *state = + v4l2_subdev_lock_and_get_active_state(&sensor->sd); + struct v4l2_rect *crop = + v4l2_subdev_state_get_crop(state, 0); + struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 0); + s32 pixel_rate = vd55g1_get_pixel_rate(sensor, format); + int ret; + + v4l2_ctrl_handler_init(hdl, 16); + + /* Flip cluster */ + sensor->hflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + sensor->vflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &sensor->hflip_ctrl); + + /* Exposition cluster */ + sensor->ae_ctrl = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, 1, + ~0x3, V4L2_EXPOSURE_AUTO); + sensor->again_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, + 0, 0x1c, 1, VD55G1_AGAIN_DEF); + sensor->dgain_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN, + 256, 0xffff, 1, + VD55G1_DGAIN_DEF); + sensor->expo_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, 0, + VD55G1_FRAME_LENGTH_DEF - + VD55G1_EXPO_MAX_TERM, + 1, VD55G1_EXPO_DEF); + v4l2_ctrl_auto_cluster(4, &sensor->ae_ctrl, V4L2_EXPOSURE_MANUAL, true); + + sensor->patgen_ctrl = + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(vd55g1_tp_menu) - 1, 0, + 0, vd55g1_tp_menu); + ctrl = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, + 0, 0, &sensor->link_freq); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->pixel_rate_ctrl = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_PIXEL_RATE, 1, + INT_MAX, 1, + pixel_rate); + if (sensor->pixel_rate_ctrl) + sensor->pixel_rate_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->ae_lock_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_3A_LOCK, + 0, 1, 0, 0); + sensor->ae_bias_ctrl = + v4l2_ctrl_new_int_menu(hdl, ops, + V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(vd55g1_ev_bias_menu) - 1, + ARRAY_SIZE(vd55g1_ev_bias_menu) / 2, + vd55g1_ev_bias_menu); + sensor->hdr_ctrl = + v4l2_ctrl_new_std_menu_items(hdl, ops, + V4L2_CID_HDR_SENSOR_MODE, + ARRAY_SIZE(vd55g1_hdr_menu) - 1, 0, + VD55G1_NO_HDR, vd55g1_hdr_menu); + hblank = vd55g1_get_hblank_min(sensor, format, crop); + sensor->hblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, + hblank, hblank, 1, hblank); + if (sensor->hblank_ctrl) + sensor->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + vd55g1_get_vblank_limits(sensor, crop, &vblank); + sensor->vblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, + vblank.min, vblank.max, + 1, vblank.def); + + /* Additional controls based on device tree properties */ + if (sensor->ext_leds_mask) { + sensor->led_ctrl = + v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_FLASH_LED_MODE, + V4L2_FLASH_LED_MODE_FLASH, 0, + V4L2_FLASH_LED_MODE_NONE); + } + + ret = v4l2_fwnode_device_parse(sensor->dev, &fwnode_props); + if (ret) + goto free_ctrls; + + ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &fwnode_props); + if (ret) + goto free_ctrls; + + sensor->sd.ctrl_handler = hdl; + goto unlock_state; + +free_ctrls: + v4l2_ctrl_handler_free(hdl); +unlock_state: + v4l2_subdev_unlock_state(state); + return ret; +} + +static int vd55g1_detect(struct vd55g1 *sensor) +{ + u64 device_rev; + u64 id; + int ret; + + ret = vd55g1_read(sensor, VD55G1_REG_MODEL_ID, &id, NULL); + if (ret) + return ret; + + if (id != VD55G1_MODEL_ID) { + dev_warn(sensor->dev, "Unsupported sensor id %x\n", (u32)id); + return -ENODEV; + } + + ret = vd55g1_read(sensor, VD55G1_REG_REVISION, &device_rev, NULL); + if (ret) + return ret; + + if (device_rev != VD55G1_REVISION_CCB) { + dev_err(sensor->dev, "Unsupported sensor revision (0x%x)\n", + (u16)device_rev); + return -ENODEV; + } + + return 0; +} + +static int vd55g1_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct vd55g1 *sensor = to_vd55g1(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(vd55g1_supply_name), + sensor->supplies); + if (ret) { + dev_err(dev, "Failed to enable regulators %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(sensor->xclk); + if (ret) { + dev_err(dev, "Failed to enable clock %d\n", ret); + goto disable_bulk; + } + + gpiod_set_value_cansleep(sensor->reset_gpio, 0); + usleep_range(5000, 10000); + ret = vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_READY_TO_BOOT, NULL); + if (ret) { + dev_err(dev, "Sensor reset failed %d\n", ret); + goto disable_clock; + } + + ret = vd55g1_detect(sensor); + if (ret) { + dev_err(dev, "Sensor detect failed %d\n", ret); + goto disable_clock; + } + + ret = vd55g1_patch(sensor); + if (ret) { + dev_err(dev, "Sensor patch failed %d\n", ret); + goto disable_clock; + } + + ret = vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_SW_STBY, NULL); + if (ret) { + dev_err(dev, "Sensor waiting after patch failed %d\n", + ret); + goto disable_clock; + } + + return 0; + +disable_clock: + gpiod_set_value_cansleep(sensor->reset_gpio, 1); + clk_disable_unprepare(sensor->xclk); +disable_bulk: + regulator_bulk_disable(ARRAY_SIZE(vd55g1_supply_name), + sensor->supplies); + + return ret; +} + +static int vd55g1_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct vd55g1 *sensor = to_vd55g1(sd); + + gpiod_set_value_cansleep(sensor->reset_gpio, 1); + clk_disable_unprepare(sensor->xclk); + regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies); + + return 0; +} + +static int vd55g1_check_csi_conf(struct vd55g1 *sensor, + struct fwnode_handle *endpoint) +{ + struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + u8 n_lanes; + int ret; + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); + if (ret) + return -EINVAL; + + /* Check lanes number */ + n_lanes = ep.bus.mipi_csi2.num_data_lanes; + if (n_lanes != 1) { + dev_err(sensor->dev, "Sensor only supports 1 lane, found %d\n", + n_lanes); + ret = -EINVAL; + goto done; + } + + /* Clock lane must be first */ + if (ep.bus.mipi_csi2.clock_lane != 0) { + dev_err(sensor->dev, "Clock lane must be mapped to lane 0\n"); + ret = -EINVAL; + goto done; + } + + /* Handle polarities in sensor configuration */ + sensor->oif_ctrl = (ep.bus.mipi_csi2.lane_polarities[0] << 3) | + (ep.bus.mipi_csi2.lane_polarities[1] << 6); + + /* Check the link frequency set in device tree */ + if (!ep.nr_of_link_frequencies) { + dev_err(sensor->dev, "link-frequency property not found in DT\n"); + ret = -EINVAL; + goto done; + } + if (ep.nr_of_link_frequencies != 1) { + dev_err(sensor->dev, "Multiple link frequencies not supported\n"); + ret = -EINVAL; + goto done; + } + sensor->link_freq = ep.link_frequencies[0]; + +done: + v4l2_fwnode_endpoint_free(&ep); + + return ret; +} + +static int vd55g1_parse_dt_gpios_array(struct vd55g1 *sensor, + char *prop_name, u32 *array, int *nb) +{ + unsigned int i; + int ret; + + *nb = device_property_count_u32(sensor->dev, prop_name); + if (*nb == -EINVAL) { + /* Property not found */ + *nb = 0; + return 0; + } + + ret = device_property_read_u32_array(sensor->dev, + prop_name, array, *nb); + if (ret) { + dev_err(sensor->dev, "Failed to read %s prop\n", prop_name); + return ret; + } + for (i = 0; i < *nb; i++) { + if (array[i] >= VD55G1_NB_GPIOS) { + dev_err(sensor->dev, "Invalid GPIO number %d\n", + array[i]); + return -EINVAL; + } + } + + return 0; +} + +static int vd55g1_parse_dt_gpios(struct vd55g1 *sensor) +{ + u32 led_gpios[VD55G1_NB_GPIOS]; + int nb_gpios_leds; + unsigned int i; + int ret; + + /* Initialize GPIOs to default */ + for (i = 0; i < VD55G1_NB_GPIOS; i++) + sensor->gpios[i] = VD55G1_GPIO_MODE_IN; + sensor->ext_leds_mask = 0; + + /* Take into account optional 'st,leds' output for GPIOs */ + ret = vd55g1_parse_dt_gpios_array(sensor, "st,leds", led_gpios, + &nb_gpios_leds); + if (ret) + return ret; + + for (i = 0; i < nb_gpios_leds; i++) { + sensor->gpios[led_gpios[i]] = VD55G1_GPIO_MODE_STROBE; + set_bit(led_gpios[i], &sensor->ext_leds_mask); + } + + return 0; +} + +static int vd55g1_parse_dt(struct vd55g1 *sensor) +{ + struct fwnode_handle *endpoint; + int ret; + + endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(sensor->dev), + 0, 0, 0); + if (!endpoint) { + dev_err(sensor->dev, "Endpoint node not found\n"); + return -EINVAL; + } + + ret = vd55g1_check_csi_conf(sensor, endpoint); + fwnode_handle_put(endpoint); + if (ret) + return ret; + + return vd55g1_parse_dt_gpios(sensor); +} + +static int vd55g1_subdev_init(struct vd55g1 *sensor) +{ + int ret; + + /* Init sub device */ + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->sd.internal_ops = &vd55g1_internal_ops; + + /* Init source pad */ + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); + if (ret) { + dev_err(sensor->dev, "Failed to init media entity: %d\n", ret); + return ret; + } + + sensor->sd.state_lock = sensor->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&sensor->sd); + if (ret) { + dev_err(sensor->dev, "Subdev init error: %d\n", ret); + goto err_ctrls; + } + + /* + * Initialize controls after v4l2_subdev_init_finalize() to make sure + * active state is set + */ + ret = vd55g1_init_ctrls(sensor); + if (ret) { + dev_err(sensor->dev, "Controls initialization failed %d\n", + ret); + goto err_media; + } + + return 0; + +err_ctrls: + v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); + +err_media: + media_entity_cleanup(&sensor->sd.entity); + return ret; +} + +static void vd55g1_subdev_cleanup(struct vd55g1 *sensor) +{ + v4l2_async_unregister_subdev(&sensor->sd); + v4l2_subdev_cleanup(&sensor->sd); + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); +} + +static int vd55g1_get_regulators(struct vd55g1 *sensor) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vd55g1_supply_name); i++) + sensor->supplies[i].supply = vd55g1_supply_name[i]; + + return devm_regulator_bulk_get(sensor->dev, + ARRAY_SIZE(vd55g1_supply_name), + sensor->supplies); +} + +static int vd55g1_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct vd55g1 *sensor; + int ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + sensor->dev = &client->dev; + + v4l2_i2c_subdev_init(&sensor->sd, client, &vd55g1_subdev_ops); + + ret = vd55g1_parse_dt(sensor); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse Device Tree\n"); + + /* Get (and check) resources : power regs, ext clock, reset gpio */ + ret = vd55g1_get_regulators(sensor); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + sensor->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(sensor->xclk)) + return dev_err_probe(dev, PTR_ERR(sensor->xclk), + "Failed to get xclk\n"); + + sensor->xclk_freq = clk_get_rate(sensor->xclk); + ret = vd55g1_prepare_clock_tree(sensor); + if (ret) + return ret; + + sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio), + "Failed to get reset gpio\n"); + + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) + return dev_err_probe(dev, PTR_ERR(sensor->regmap), + "Failed to init regmap\n"); + + /* Detect if sensor is present and if its revision is supported */ + ret = vd55g1_power_on(dev); + if (ret) + return ret; + + /* Enable pm_runtime and power off the sensor */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 4000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + ret = vd55g1_subdev_init(sensor); + if (ret) { + dev_err(dev, "V4l2 init failed: %d\n", ret); + goto err_power_off; + } + + ret = v4l2_async_register_subdev(&sensor->sd); + if (ret) { + dev_err(dev, "async subdev register failed %d\n", ret); + goto err_subdev; + } + + return 0; + +err_subdev: + vd55g1_subdev_cleanup(sensor); +err_power_off: + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_dont_use_autosuspend(dev); + vd55g1_power_off(dev); + + return ret; +} + +static void vd55g1_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct vd55g1 *sensor = to_vd55g1(sd); + + vd55g1_subdev_cleanup(sensor); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + vd55g1_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_dont_use_autosuspend(&client->dev); +} + +static const struct of_device_id vd55g1_dt_ids[] = { + { .compatible = "st,vd55g1" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vd55g1_dt_ids); + +static const struct dev_pm_ops vd55g1_pm_ops = { + SET_RUNTIME_PM_OPS(vd55g1_power_off, vd55g1_power_on, NULL) +}; + +static struct i2c_driver vd55g1_i2c_driver = { + .driver = { + .name = "vd55g1", + .of_match_table = vd55g1_dt_ids, + .pm = &vd55g1_pm_ops, + }, + .probe = vd55g1_probe, + .remove = vd55g1_remove, +}; + +module_i2c_driver(vd55g1_i2c_driver); + +MODULE_AUTHOR("Benjamin Mugnier "); +MODULE_AUTHOR("Sylvain Petinot "); +MODULE_DESCRIPTION("VD55G1 camera subdev driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 1d0358c35818c84a810e9bca40015d259dcaf5ab Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Tue, 22 Apr 2025 13:27:01 +0100 Subject: media: imx335: Add MAINTAINER entry In commit 5b0e91fd477d ("media: imx335: Orphan the driver"), the IMX335 driver was marked as an orphan. I have several of these sensors, tested on Raspberry Pi and NXP systems, and the full datasheet. Add myself as a maintainer for the IMX335. Signed-off-by: Kieran Bingham Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 1d8641f29e8a..af67e04a5c54 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22562,8 +22562,9 @@ F: Documentation/devicetree/bindings/media/i2c/sony,imx334.yaml F: drivers/media/i2c/imx334.c SONY IMX335 SENSOR DRIVER +M: Kieran Bingham L: linux-media@vger.kernel.org -S: Orphan +S: Maintained T: git git://linuxtv.org/media.git F: Documentation/devicetree/bindings/media/i2c/sony,imx335.yaml F: drivers/media/i2c/imx335.c -- cgit v1.2.3-59-g8ed1b From b122c9cfcb39c8ef520d50eddfbe15f3e6551a50 Mon Sep 17 00:00:00 2001 From: Umang Jain Date: Tue, 22 Apr 2025 13:20:52 +0100 Subject: media: imx335: Use correct register width for HNUM CCI_REG_HNUM should be using CCI_REG16_LE() instead of CCI_REG8() as HNUM spans from 0x302e[0:7] to 0x302f[0:3]. Signed-off-by: Umang Jain Signed-off-by: Kieran Bingham Fixes: 8f0926dba799 ("media: imx335: Use V4L2 CCI for accessing sensor registers") Cc: stable@vger.kernel.org Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx335.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index 0beb80b8c458..d400a019f6b3 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -31,7 +31,7 @@ #define IMX335_REG_CPWAIT_TIME CCI_REG8(0x300d) #define IMX335_REG_WINMODE CCI_REG8(0x3018) #define IMX335_REG_HTRIMMING_START CCI_REG16_LE(0x302c) -#define IMX335_REG_HNUM CCI_REG8(0x302e) +#define IMX335_REG_HNUM CCI_REG16_LE(0x302e) /* Lines per frame */ #define IMX335_REG_VMAX CCI_REG24_LE(0x3030) -- cgit v1.2.3-59-g8ed1b From 3e51d14286451388d8b5acd5ac4777e98682422b Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 17 Mar 2025 08:38:56 +0100 Subject: media: intel/ipu6: Remove unused ipu6_isys_csi2_pdata The pointer to ipu6_isys_csi2_pdata is not used. Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h index bc8594c94f99..ce8eed91065c 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h @@ -14,7 +14,6 @@ struct v4l2_mbus_frame_desc_entry; struct ipu6_isys_video; struct ipu6_isys; -struct ipu6_isys_csi2_pdata; struct ipu6_isys_stream; #define NR_OF_CSI2_VC 16 @@ -37,7 +36,6 @@ struct ipu6_isys_stream; struct ipu6_isys_csi2 { struct ipu6_isys_subdev asd; - struct ipu6_isys_csi2_pdata *pdata; struct ipu6_isys *isys; struct ipu6_isys_video av[NR_OF_CSI2_SRC_PADS]; -- cgit v1.2.3-59-g8ed1b From a562c39b672506a572263ec876c92b41946307b1 Mon Sep 17 00:00:00 2001 From: Michael Riesch Date: Thu, 10 Apr 2025 21:41:31 +0200 Subject: media: mailmap: add entry for Michael Riesch After five interesting years, I left WolfVision and started to work for Collabora. Add a corresponding mailmap entry. Signed-off-by: Michael Riesch Reviewed-by: Nicolas Dufresne Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 4f7cd8e23177..59f99aa83185 100644 --- a/.mailmap +++ b/.mailmap @@ -503,6 +503,7 @@ Mayuresh Janorkar Md Sadre Alam Miaoqing Pan Michael Buesch +Michael Riesch Michal Simek Michel Dänzer Michel Lespinasse -- cgit v1.2.3-59-g8ed1b From 6be2439fd7d0dc40a2597641d25e3cc9e1e6a30e Mon Sep 17 00:00:00 2001 From: Michael Riesch Date: Thu, 10 Apr 2025 21:41:32 +0200 Subject: media: dt-bindings: sony,imx415: update maintainer e-mail address I recently left WolfVision but would like to continue to maintain the Sony IMX415 image sensor driver. Update my e-mail address. Signed-off-by: Michael Riesch Acked-by: Rob Herring (Arm) Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml | 2 +- MAINTAINERS | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml index 34962c5c7006..7c11e871dca6 100644 --- a/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Sony IMX415 CMOS Image Sensor maintainers: - - Michael Riesch + - Michael Riesch description: |- The Sony IMX415 is a diagonal 6.4 mm (Type 1/2.8) CMOS active pixel type diff --git a/MAINTAINERS b/MAINTAINERS index af67e04a5c54..09688598af4e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22584,7 +22584,7 @@ F: Documentation/devicetree/bindings/media/i2c/sony,imx412.yaml F: drivers/media/i2c/imx412.c SONY IMX415 SENSOR DRIVER -M: Michael Riesch +M: Michael Riesch L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git -- cgit v1.2.3-59-g8ed1b From 20244cbafbd6c8486347bb82d972f6e2d2d5a201 Mon Sep 17 00:00:00 2001 From: Dongcheng Yan Date: Fri, 25 Apr 2025 18:43:31 +0800 Subject: media: i2c: change lt6911uxe irq_gpio name to "hpd" Lt6911uxe is used in IPU6 / x86 platform, worked with an out-of-tree int3472 patch and upstream intel/ipu6 before. It is only used on ACPI platforms till now and there are no devicetree bindings for this driver. The upstream int3472 driver uses "hpd" instead of "readystat" now. this patch updates the irq_gpio name to "hpd" accordingly, so that mere users can now use the upstream version directly without relying on out-of-tree int3472 pin support. The new name "hpd" (Hotplug Detect) aligns with common naming conventions used in other drivers(like adv7604) and documentation. Fixes: e49563c3be09d4 ("media: i2c: add lt6911uxe hdmi bridge driver") Cc: stable@vger.kernel.org Signed-off-by: Dongcheng Yan Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/lt6911uxe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/lt6911uxe.c b/drivers/media/i2c/lt6911uxe.c index c5b40bb58a37..24857d683fcf 100644 --- a/drivers/media/i2c/lt6911uxe.c +++ b/drivers/media/i2c/lt6911uxe.c @@ -605,10 +605,10 @@ static int lt6911uxe_probe(struct i2c_client *client) return dev_err_probe(dev, PTR_ERR(lt6911uxe->reset_gpio), "failed to get reset gpio\n"); - lt6911uxe->irq_gpio = devm_gpiod_get(dev, "readystat", GPIOD_IN); + lt6911uxe->irq_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN); if (IS_ERR(lt6911uxe->irq_gpio)) return dev_err_probe(dev, PTR_ERR(lt6911uxe->irq_gpio), - "failed to get ready_stat gpio\n"); + "failed to get hpd gpio\n"); ret = lt6911uxe_fwnode_parse(lt6911uxe, dev); if (ret) -- cgit v1.2.3-59-g8ed1b From 8268da3c474a43a79a6540fb06c5d3b730a0d5a5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 25 Apr 2025 14:52:37 +0200 Subject: media: ov5675: suppress probe deferral errors Probe deferral should not be logged as an error: ov5675 24-0010: failed to get HW configuration: -517 Drop the (mostly) redundant dev_err() from sensor probe() to suppress it. Note that errors during clock and regulator lookup are already correctly logged using dev_err_probe(). Fixes: 49d9ad719e89 ("media: ov5675: add device-tree support and support runtime PM") Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov5675.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index c1081deffc2f..e7aec281e9a4 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -1295,11 +1295,8 @@ static int ov5675_probe(struct i2c_client *client) return -ENOMEM; ret = ov5675_get_hwcfg(ov5675, &client->dev); - if (ret) { - dev_err(&client->dev, "failed to get HW configuration: %d", - ret); + if (ret) return ret; - } v4l2_i2c_subdev_init(&ov5675->sd, client, &ov5675_subdev_ops); -- cgit v1.2.3-59-g8ed1b From e3d86847fba58cf71f66e81b6a2515e07039ae17 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 25 Apr 2025 14:52:38 +0200 Subject: media: ov8856: suppress probe deferral errors Probe deferral should not be logged as an error: ov8856 24-0010: failed to get HW configuration: -517 Use dev_err_probe() for the clock lookup and drop the (mostly) redundant dev_err() from sensor probe() to suppress it. Note that errors during regulator lookup is already correctly logged using dev_err_probe(). Fixes: 0c2c7a1e0d69 ("media: ov8856: Add devicetree support") Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov8856.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index e6704d018248..4b6874d2a104 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -2276,8 +2276,8 @@ static int ov8856_get_hwcfg(struct ov8856 *ov8856, struct device *dev) if (!is_acpi_node(fwnode)) { ov8856->xvclk = devm_clk_get(dev, "xvclk"); if (IS_ERR(ov8856->xvclk)) { - dev_err(dev, "could not get xvclk clock (%pe)\n", - ov8856->xvclk); + dev_err_probe(dev, PTR_ERR(ov8856->xvclk), + "could not get xvclk clock\n"); return PTR_ERR(ov8856->xvclk); } @@ -2382,11 +2382,8 @@ static int ov8856_probe(struct i2c_client *client) return -ENOMEM; ret = ov8856_get_hwcfg(ov8856, &client->dev); - if (ret) { - dev_err(&client->dev, "failed to get HW configuration: %d", - ret); + if (ret) return ret; - } v4l2_i2c_subdev_init(&ov8856->sd, client, &ov8856_subdev_ops); -- cgit v1.2.3-59-g8ed1b From ac6fb0d8f98810f628e0539f4671d6e71fbf7053 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 28 Apr 2025 01:13:32 +0300 Subject: media: ccs-pll: Print a debug message when VT tree calculation fails When the VT tree calculation fails to find a valid pre-divider, the ccs_pll_calculate_vt_tree() function returns an error silently, and the caller doesn't print any message either. This makes debugging PLL calculation issues more difficult. Add a debug message to report the issue, and amend the corresponding message for the OP tree to mention "OP". Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs-pll.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index 8f9a695bd9e5..4eb83636e102 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -449,6 +449,7 @@ static int ccs_pll_calculate_vt_tree(struct device *dev, return 0; } + dev_dbg(dev, "unable to compute VT pre_pll divisor\n"); return -EINVAL; } @@ -888,8 +889,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, } if (rval) { - dev_dbg(dev, "unable to compute pre_pll divisor\n"); - + dev_dbg(dev, "unable to compute OP pre_pll divisor\n"); return rval; } -- cgit v1.2.3-59-g8ed1b From f0b7912b73c68415cbb435f3724d02e1aa7aa12b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 31 Mar 2025 10:27:07 +0300 Subject: media: i2c: max9671x: Remove (explicitly) unused header The fwnode.h is not supposed to be used by the drivers as it has the definitions for the core parts for different device property provider implementations. Drop it. Note, that fwnode API for drivers is provided in property.h which is included here. Signed-off-by: Andy Shevchenko Reviewed-by: Julien Massot Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/max96714.c | 2 +- drivers/media/i2c/max96717.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/max96714.c b/drivers/media/i2c/max96714.c index 159753b13777..3cc1b1ae47d1 100644 --- a/drivers/media/i2c/max96714.c +++ b/drivers/media/i2c/max96714.c @@ -7,11 +7,11 @@ #include #include -#include #include #include #include #include +#include #include #include diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c index 9259d58ba734..3746729366ac 100644 --- a/drivers/media/i2c/max96717.c +++ b/drivers/media/i2c/max96717.c @@ -9,10 +9,10 @@ #include #include #include -#include #include #include #include +#include #include #include -- cgit v1.2.3-59-g8ed1b From 0979b76babb7e7404c4e3999925c46231ccf93a7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 31 Mar 2025 10:25:07 +0300 Subject: media: i2c: ds90ub9x3: Remove (explicitly) unused header The fwnode.h is not supposed to be used by the drivers as it has the definitions for the core parts for different device property provider implementations. Drop it. Note, that fwnode API for drivers is provided in property.h which is included here. Signed-off-by: Andy Shevchenko Reviewed-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub913.c | 1 - drivers/media/i2c/ds90ub953.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index 401cc2a10c3c..b1e67e514c6a 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c index c305b4e03e07..89e3132e81c5 100644 --- a/drivers/media/i2c/ds90ub953.c +++ b/drivers/media/i2c/ds90ub953.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3-59-g8ed1b From 8908792cbb7ec0e66720b5184c3a57a0d818e29f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 31 Mar 2025 10:21:36 +0300 Subject: media: raspberrypi: rp1-cfe: Remove (explicitly) unused header The fwnode.h is not supposed to be used by the drivers as it has the definitions for the core parts for different device property provider implementations. Drop it. Note, that fwnode API for drivers is provided in property.h which is included here. Signed-off-by: Andy Shevchenko Reviewed-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/platform/raspberrypi/rp1-cfe/cfe.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c index 69a5f23e7954..fcadb2143c88 100644 --- a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3-59-g8ed1b From 046c793c0e944d7bbf3752f18b241f99fb01ffaa Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 31 Mar 2025 11:35:04 +0300 Subject: media: i2c: rdacm2x: Make use of device properties Convert the module to be property provider agnostic and allow it to be used on non-OF platforms. Reviewed-by: Kieran Bingham Signed-off-by: Andy Shevchenko Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/rdacm20.c | 7 +++---- drivers/media/i2c/rdacm21.c | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c index b8bd8354d100..52e8e2620b4d 100644 --- a/drivers/media/i2c/rdacm20.c +++ b/drivers/media/i2c/rdacm20.c @@ -16,10 +16,10 @@ */ #include -#include #include #include #include +#include #include #include @@ -575,10 +575,9 @@ static int rdacm20_probe(struct i2c_client *client) dev->dev = &client->dev; dev->serializer.client = client; - ret = of_property_read_u32_array(client->dev.of_node, "reg", - dev->addrs, 2); + ret = device_property_read_u32_array(dev->dev, "reg", dev->addrs, 2); if (ret < 0) { - dev_err(dev->dev, "Invalid DT reg property: %d\n", ret); + dev_err(dev->dev, "Invalid FW reg property: %d\n", ret); return -EINVAL; } diff --git a/drivers/media/i2c/rdacm21.c b/drivers/media/i2c/rdacm21.c index 3e22df36354f..bcab462708c7 100644 --- a/drivers/media/i2c/rdacm21.c +++ b/drivers/media/i2c/rdacm21.c @@ -11,10 +11,10 @@ */ #include -#include #include #include #include +#include #include #include @@ -551,10 +551,9 @@ static int rdacm21_probe(struct i2c_client *client) dev->dev = &client->dev; dev->serializer.client = client; - ret = of_property_read_u32_array(client->dev.of_node, "reg", - dev->addrs, 2); + ret = device_property_read_u32_array(dev->dev, "reg", dev->addrs, 2); if (ret < 0) { - dev_err(dev->dev, "Invalid DT reg property: %d\n", ret); + dev_err(dev->dev, "Invalid FW reg property: %d\n", ret); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From d380dcad084b60d2f929c8ab8f74e3e7bf6bad1d Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Sun, 4 May 2025 05:35:02 +0200 Subject: MAINTAINERS: adjust file entry in OMNIVISION OV7670 SENSOR DRIVER MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 59b24c0047a2 ("media: dt-bindings: media: i2c: align filenames format with standard") renames the files in Documentation/devicetree/bindings/media/i2c/, but misses to adjust the file entry in OMNIVISION OV7670 SENSOR DRIVER. Adjust the file entry after this renaming. Signed-off-by: Lukas Bulwahn Reviewed-by: Niklas Söderlund Reviewed-by: David Heidelberg Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 09688598af4e..41471386dacd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17948,7 +17948,7 @@ OMNIVISION OV7670 SENSOR DRIVER L: linux-media@vger.kernel.org S: Orphan T: git git://linuxtv.org/media.git -F: Documentation/devicetree/bindings/media/i2c/ov7670.txt +F: Documentation/devicetree/bindings/media/i2c/ovti,ov7670.txt F: drivers/media/i2c/ov7670.c OMNIVISION OV772x SENSOR DRIVER -- cgit v1.2.3-59-g8ed1b From 56fa9206d32da1bfc5ea3eb3d5f543973d6ea29f Mon Sep 17 00:00:00 2001 From: Hao Yao Date: Wed, 12 Mar 2025 10:06:26 +0800 Subject: media: i2c: ov13b10: Fix h_blank calculation Pixel per line (PPL) is calculated as pixel_rate / (VTS * FPS), which is not decided by MIPI CSI-2 link frequency. PPL can vary while link frequency keeps the same. If PPL is wrong, the h_blank = PPL - width is also wrong then FPS control is inaccurate. This patch fix h_blank by: 1. Move PPL from link_freq_config to ov13b10_mode 2. Add PPL value for different modes 3. Use PPL from mode to calculate h_blank Signed-off-by: Bingbu Cao Signed-off-by: Hao Yao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov13b10.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c index 73c844aa5697..79c0280e42f3 100644 --- a/drivers/media/i2c/ov13b10.c +++ b/drivers/media/i2c/ov13b10.c @@ -34,9 +34,6 @@ #define OV13B10_VTS_120FPS 0x0320 #define OV13B10_VTS_MAX 0x7fff -/* HBLANK control - read only */ -#define OV13B10_PPL_560MHZ 4704 - /* Exposure control */ #define OV13B10_REG_EXPOSURE 0x3500 #define OV13B10_EXPOSURE_MIN 4 @@ -95,7 +92,7 @@ struct ov13b10_reg_list { /* Link frequency config */ struct ov13b10_link_freq_config { - u32 pixels_per_line; + u64 link_freq; /* registers for this link frequency */ struct ov13b10_reg_list reg_list; @@ -114,6 +111,10 @@ struct ov13b10_mode { /* Index of Link frequency config to be used */ u32 link_freq_index; + + /* Pixels per line in current mode */ + u32 ppl; + /* Default register values */ struct ov13b10_reg_list reg_list; }; @@ -549,7 +550,7 @@ static const s64 link_freq_menu_items[] = { static const struct ov13b10_link_freq_config link_freq_configs[] = { { - .pixels_per_line = OV13B10_PPL_560MHZ, + .link_freq = OV13B10_LINK_FREQ_560MHZ, .reg_list = { .num_of_regs = ARRAY_SIZE(mipi_data_rate_1120mbps), .regs = mipi_data_rate_1120mbps, @@ -564,6 +565,7 @@ static const struct ov13b10_mode supported_modes[] = { .height = 3120, .vts_def = OV13B10_VTS_30FPS, .vts_min = OV13B10_VTS_30FPS, + .ppl = 4704, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_4208x3120_regs), .regs = mode_4208x3120_regs, @@ -575,6 +577,7 @@ static const struct ov13b10_mode supported_modes[] = { .height = 3120, .vts_def = OV13B10_VTS_30FPS, .vts_min = OV13B10_VTS_30FPS, + .ppl = 4704, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_4160x3120_regs), .regs = mode_4160x3120_regs, @@ -586,6 +589,7 @@ static const struct ov13b10_mode supported_modes[] = { .height = 2340, .vts_def = OV13B10_VTS_30FPS, .vts_min = OV13B10_VTS_30FPS, + .ppl = 4704, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_4160x2340_regs), .regs = mode_4160x2340_regs, @@ -597,6 +601,7 @@ static const struct ov13b10_mode supported_modes[] = { .height = 1560, .vts_def = OV13B10_VTS_60FPS, .vts_min = OV13B10_VTS_60FPS, + .ppl = 4704, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_2104x1560_regs), .regs = mode_2104x1560_regs, @@ -608,6 +613,7 @@ static const struct ov13b10_mode supported_modes[] = { .height = 1170, .vts_def = OV13B10_VTS_60FPS, .vts_min = OV13B10_VTS_60FPS, + .ppl = 4704, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_2080x1170_regs), .regs = mode_2080x1170_regs, @@ -620,6 +626,7 @@ static const struct ov13b10_mode supported_modes[] = { .vts_def = OV13B10_VTS_120FPS, .vts_min = OV13B10_VTS_120FPS, .link_freq_index = OV13B10_LINK_FREQ_INDEX_0, + .ppl = 4664, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1364x768_120fps_regs), .regs = mode_1364x768_120fps_regs, @@ -1072,9 +1079,7 @@ ov13b10_set_pad_format(struct v4l2_subdev *sd, 1, vblank_def); __v4l2_ctrl_s_ctrl(ov13b->vblank, vblank_def); - h_blank = - link_freq_configs[mode->link_freq_index].pixels_per_line - - ov13b->cur_mode->width; + h_blank = mode->ppl - mode->width; __v4l2_ctrl_modify_range(ov13b->hblank, h_blank, h_blank, 1, h_blank); } @@ -1328,8 +1333,7 @@ static int ov13b10_init_controls(struct ov13b10 *ov13b) OV13B10_VTS_MAX - mode->height, 1, vblank_def); - hblank = link_freq_configs[mode->link_freq_index].pixels_per_line - - mode->width; + hblank = mode->ppl - mode->width; ov13b->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov13b10_ctrl_ops, V4L2_CID_HBLANK, hblank, hblank, 1, hblank); -- cgit v1.2.3-59-g8ed1b From 24c01de7728673d1a30986aa247d33ca1a0ef322 Mon Sep 17 00:00:00 2001 From: Hao Yao Date: Wed, 12 Mar 2025 10:06:27 +0800 Subject: media: i2c: ov13b10: Improve code readability Use mode instead of ov13b->cur_mode in set_pad_format. Signed-off-by: Bingbu Cao Signed-off-by: Hao Yao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil [hverkuil: fix typo: redability -> readability] --- drivers/media/i2c/ov13b10.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c index 79c0280e42f3..2e83fc23f321 100644 --- a/drivers/media/i2c/ov13b10.c +++ b/drivers/media/i2c/ov13b10.c @@ -1069,15 +1069,11 @@ ov13b10_set_pad_format(struct v4l2_subdev *sd, __v4l2_ctrl_s_ctrl_int64(ov13b->pixel_rate, pixel_rate); /* Update limits and set FPS to default */ - vblank_def = ov13b->cur_mode->vts_def - - ov13b->cur_mode->height; - vblank_min = ov13b->cur_mode->vts_min - - ov13b->cur_mode->height; + vblank_def = mode->vts_def - mode->height; + vblank_min = mode->vts_min - mode->height; __v4l2_ctrl_modify_range(ov13b->vblank, vblank_min, - OV13B10_VTS_MAX - - ov13b->cur_mode->height, - 1, - vblank_def); + OV13B10_VTS_MAX - mode->height, + 1, vblank_def); __v4l2_ctrl_s_ctrl(ov13b->vblank, vblank_def); h_blank = mode->ppl - mode->width; __v4l2_ctrl_modify_range(ov13b->hblank, h_blank, -- cgit v1.2.3-59-g8ed1b From 65e52d07f1aada509c85c1bb86ce80dd8a5ce0c8 Mon Sep 17 00:00:00 2001 From: Hao Yao Date: Wed, 12 Mar 2025 10:06:28 +0800 Subject: media: i2c: ov13b10: Support 2 lane mode 1. Fix pixel rate calculation to consider different lane number 2. Add 2104x1560 60fps 2 data lanes register setting 3. Support 2 lane in check_hwcfg 4. Select correct mode considering lane number used Signed-off-by: Bingbu Cao Signed-off-by: Hao Yao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov13b10.c | 140 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 118 insertions(+), 22 deletions(-) diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c index 2e83fc23f321..e85c7d33a670 100644 --- a/drivers/media/i2c/ov13b10.c +++ b/drivers/media/i2c/ov13b10.c @@ -514,6 +514,52 @@ static const struct ov13b10_reg mode_1364x768_120fps_regs[] = { {0x5001, 0x0d}, }; +static const struct ov13b10_reg mode_2lanes_2104x1560_60fps_regs[] = { + {0x3016, 0x32}, + {0x3106, 0x29}, + {0x0305, 0xaf}, + {0x3501, 0x06}, + {0x3662, 0x88}, + {0x3714, 0x28}, + {0x3739, 0x10}, + {0x37c2, 0x14}, + {0x37d9, 0x06}, + {0x37e2, 0x0c}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x08}, + {0x3804, 0x10}, + {0x3805, 0x8f}, + {0x3806, 0x0c}, + {0x3807, 0x47}, + {0x3808, 0x08}, + {0x3809, 0x38}, + {0x380a, 0x06}, + {0x380b, 0x18}, + {0x380c, 0x04}, + {0x380d, 0x98}, + {0x380e, 0x06}, + {0x380f, 0x3e}, + {0x3810, 0x00}, + {0x3811, 0x07}, + {0x3812, 0x00}, + {0x3813, 0x05}, + {0x3814, 0x03}, + {0x3816, 0x03}, + {0x3820, 0x8b}, + {0x3c8c, 0x18}, + {0x4008, 0x00}, + {0x4009, 0x05}, + {0x4050, 0x00}, + {0x4051, 0x05}, + {0x4501, 0x08}, + {0x4505, 0x00}, + {0x4837, 0x0e}, + {0x5000, 0xfd}, + {0x5001, 0x0d}, +}; + static const char * const ov13b10_test_pattern_menu[] = { "Disabled", "Vertical Color Bar Type 1", @@ -527,15 +573,16 @@ static const char * const ov13b10_test_pattern_menu[] = { #define OV13B10_LINK_FREQ_INDEX_0 0 #define OV13B10_EXT_CLK 19200000 -#define OV13B10_DATA_LANES 4 +#define OV13B10_4_DATA_LANES 4 +#define OV13B10_2_DATA_LANES 2 /* - * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample - * data rate => double data rate; number of lanes => 4; bits per pixel => 10 + * pixel_rate = data_rate * nr_of_lanes / bits_per_pixel + * data_rate => link_freq * 2; number of lanes => 4 or 2; bits per pixel => 10 */ -static u64 link_freq_to_pixel_rate(u64 f) +static u64 link_freq_to_pixel_rate(u64 f, u8 lanes) { - f *= 2 * OV13B10_DATA_LANES; + f *= 2 * lanes; do_div(f, 10); return f; @@ -559,7 +606,8 @@ static const struct ov13b10_link_freq_config }; /* Mode configs */ -static const struct ov13b10_mode supported_modes[] = { +static const struct ov13b10_mode supported_4_lanes_modes[] = { + /* 4 data lanes */ { .width = 4208, .height = 3120, @@ -634,6 +682,23 @@ static const struct ov13b10_mode supported_modes[] = { }, }; +static const struct ov13b10_mode supported_2_lanes_modes[] = { + /* 2 data lanes */ + { + .width = 2104, + .height = 1560, + .vts_def = OV13B10_VTS_60FPS, + .vts_min = OV13B10_VTS_60FPS, + .link_freq_index = OV13B10_LINK_FREQ_INDEX_0, + .ppl = 2352, + .reg_list = { + .num_of_regs = + ARRAY_SIZE(mode_2lanes_2104x1560_60fps_regs), + .regs = mode_2lanes_2104x1560_60fps_regs, + }, + }, +}; + struct ov13b10 { struct v4l2_subdev sd; struct media_pad pad; @@ -651,12 +716,20 @@ struct ov13b10 { struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; + /* Supported modes */ + const struct ov13b10_mode *supported_modes; + /* Current mode */ const struct ov13b10_mode *cur_mode; /* Mutex for serialized access */ struct mutex mutex; + u8 supported_modes_num; + + /* Data lanes used */ + u8 data_lanes; + /* True if the device has been identified */ bool identified; }; @@ -760,8 +833,8 @@ static int ov13b10_write_reg_list(struct ov13b10 *ov13b, /* Open sub-device */ static int ov13b10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { - const struct ov13b10_mode *default_mode = &supported_modes[0]; struct ov13b10 *ov13b = to_ov13b10(sd); + const struct ov13b10_mode *default_mode = ov13b->supported_modes; struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(fh->state, 0); @@ -980,7 +1053,10 @@ static int ov13b10_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->index >= ARRAY_SIZE(supported_modes)) + struct ov13b10 *ov13b = to_ov13b10(sd); + const struct ov13b10_mode *supported_modes = ov13b->supported_modes; + + if (fse->index >= ov13b->supported_modes_num) return -EINVAL; if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) @@ -1040,6 +1116,7 @@ ov13b10_set_pad_format(struct v4l2_subdev *sd, { struct ov13b10 *ov13b = to_ov13b10(sd); const struct ov13b10_mode *mode; + const struct ov13b10_mode *supported_modes = ov13b->supported_modes; struct v4l2_mbus_framefmt *framefmt; s32 vblank_def; s32 vblank_min; @@ -1054,7 +1131,7 @@ ov13b10_set_pad_format(struct v4l2_subdev *sd, fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), + ov13b->supported_modes_num, width, height, fmt->format.width, fmt->format.height); ov13b10_update_pad_format(mode, fmt); @@ -1065,7 +1142,8 @@ ov13b10_set_pad_format(struct v4l2_subdev *sd, ov13b->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov13b->link_freq, mode->link_freq_index); link_freq = link_freq_menu_items[mode->link_freq_index]; - pixel_rate = link_freq_to_pixel_rate(link_freq); + pixel_rate = link_freq_to_pixel_rate(link_freq, + ov13b->data_lanes); __v4l2_ctrl_s_ctrl_int64(ov13b->pixel_rate, pixel_rate); /* Update limits and set FPS to default */ @@ -1312,7 +1390,8 @@ static int ov13b10_init_controls(struct ov13b10 *ov13b) if (ov13b->link_freq) ov13b->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]); + pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0], + ov13b->data_lanes); pixel_rate_min = 0; /* By default, PIXEL_RATE is read only */ ov13b->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov13b10_ctrl_ops, @@ -1423,7 +1502,7 @@ static int ov13b10_get_pm_resources(struct device *dev) return 0; } -static int ov13b10_check_hwcfg(struct device *dev) +static int ov13b10_check_hwcfg(struct device *dev, struct ov13b10 *ov13b) { struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY @@ -1433,6 +1512,7 @@ static int ov13b10_check_hwcfg(struct device *dev) unsigned int i, j; int ret; u32 ext_clk; + u8 dlane; if (!fwnode) return -ENXIO; @@ -1459,13 +1539,32 @@ static int ov13b10_check_hwcfg(struct device *dev) if (ret) return ret; - if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV13B10_DATA_LANES) { + dlane = bus_cfg.bus.mipi_csi2.num_data_lanes; + switch (dlane) { + case OV13B10_4_DATA_LANES: + ov13b->supported_modes = supported_4_lanes_modes; + ov13b->supported_modes_num = + ARRAY_SIZE(supported_4_lanes_modes); + break; + + case OV13B10_2_DATA_LANES: + ov13b->supported_modes = supported_2_lanes_modes; + ov13b->supported_modes_num = + ARRAY_SIZE(supported_2_lanes_modes); + break; + + default: dev_err(dev, "number of CSI2 data lanes %d is not supported", - bus_cfg.bus.mipi_csi2.num_data_lanes); + dlane); ret = -EINVAL; goto out_err; } + ov13b->data_lanes = dlane; + ov13b->cur_mode = ov13b->supported_modes; + dev_dbg(dev, "%u lanes with %u modes selected\n", + ov13b->data_lanes, ov13b->supported_modes_num); + if (!bus_cfg.nr_of_link_frequencies) { dev_err(dev, "no link frequencies defined"); ret = -EINVAL; @@ -1499,17 +1598,17 @@ static int ov13b10_probe(struct i2c_client *client) bool full_power; int ret; + ov13b = devm_kzalloc(&client->dev, sizeof(*ov13b), GFP_KERNEL); + if (!ov13b) + return -ENOMEM; + /* Check HW config */ - ret = ov13b10_check_hwcfg(&client->dev); + ret = ov13b10_check_hwcfg(&client->dev, ov13b); if (ret) { dev_err(&client->dev, "failed to check hwcfg: %d", ret); return ret; } - ov13b = devm_kzalloc(&client->dev, sizeof(*ov13b), GFP_KERNEL); - if (!ov13b) - return -ENOMEM; - /* Initialize subdev */ v4l2_i2c_subdev_init(&ov13b->sd, client, &ov13b10_subdev_ops); @@ -1533,9 +1632,6 @@ static int ov13b10_probe(struct i2c_client *client) } } - /* Set default mode to max resolution */ - ov13b->cur_mode = &supported_modes[0]; - ret = ov13b10_init_controls(ov13b); if (ret) goto error_power_off; -- cgit v1.2.3-59-g8ed1b From 454ad0169cf7bb09e622c5bc40275ea725ab3a8a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 5 Mar 2025 11:22:01 +0200 Subject: media: common: Add v4l2_find_nearest_size_conditional() v4l2_find_nearest_size() returns a mode from sensor driver's mode list that is a best match width and height wise for the sensor. Some drivers have different set of available modes depending on the number of lanes. While this could be handled within a driver by providing different lists of modes, provide a helper v4l2_find_nearest_size_conditional() to ignore modes that aren't available. Also use size_t for the array index and remove extra commas while at it. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-common.c | 18 ++++++++--- include/media/v4l2-common.h | 58 +++++++++++++++++++++++++++-------- 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 4ee4aa19efe6..bd160a8c9efe 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -154,13 +154,18 @@ void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, EXPORT_SYMBOL_GPL(v4l_bound_align_image); const void * -__v4l2_find_nearest_size(const void *array, size_t array_size, - size_t entry_size, size_t width_offset, - size_t height_offset, s32 width, s32 height) +__v4l2_find_nearest_size_conditional(const void *array, size_t array_size, + size_t entry_size, size_t width_offset, + size_t height_offset, s32 width, + s32 height, + bool (*func)(const void *array, + size_t index, + const void *context), + const void *context) { u32 error, min_error = U32_MAX; const void *best = NULL; - unsigned int i; + size_t i; if (!array) return NULL; @@ -169,6 +174,9 @@ __v4l2_find_nearest_size(const void *array, size_t array_size, const u32 *entry_width = array + width_offset; const u32 *entry_height = array + height_offset; + if (func && !func(array, i, context)) + continue; + error = abs(*entry_width - width) + abs(*entry_height - height); if (error > min_error) continue; @@ -181,7 +189,7 @@ __v4l2_find_nearest_size(const void *array, size_t array_size, return best; } -EXPORT_SYMBOL_GPL(__v4l2_find_nearest_size); +EXPORT_SYMBOL_GPL(__v4l2_find_nearest_size_conditional); int v4l2_g_parm_cap(struct video_device *vdev, struct v4l2_subdev *sd, struct v4l2_streamparm *a) diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index fda903bb3674..0a43f56578bc 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -390,38 +390,72 @@ void v4l_bound_align_image(unsigned int *width, unsigned int wmin, unsigned int salign); /** - * v4l2_find_nearest_size - Find the nearest size among a discrete - * set of resolutions contained in an array of a driver specific struct. + * v4l2_find_nearest_size_conditional - Find the nearest size among a discrete + * set of resolutions contained in an array of a driver specific struct, + * with conditionally exlusion of certain modes * * @array: a driver specific array of image sizes * @array_size: the length of the driver specific array of image sizes * @width_field: the name of the width field in the driver specific struct * @height_field: the name of the height field in the driver specific struct - * @width: desired width. - * @height: desired height. + * @width: desired width + * @height: desired height + * @func: ignores mode if returns false + * @context: context for the function * * Finds the closest resolution to minimize the width and height differences * between what requested and the supported resolutions. The size of the width * and height fields in the driver specific must equal to that of u32, i.e. four - * bytes. + * bytes. @func is called for each mode considered, a mode is ignored if @func + * returns false for it. * * Returns the best match or NULL if the length of the array is zero. */ -#define v4l2_find_nearest_size(array, array_size, width_field, height_field, \ - width, height) \ +#define v4l2_find_nearest_size_conditional(array, array_size, width_field, \ + height_field, width, height, \ + func, context) \ ({ \ BUILD_BUG_ON(sizeof((array)->width_field) != sizeof(u32) || \ sizeof((array)->height_field) != sizeof(u32)); \ - (typeof(&(array)[0]))__v4l2_find_nearest_size( \ + (typeof(&(array)[0]))__v4l2_find_nearest_size_conditional( \ (array), array_size, sizeof(*(array)), \ offsetof(typeof(*(array)), width_field), \ offsetof(typeof(*(array)), height_field), \ - width, height); \ + width, height, func, context); \ }) const void * -__v4l2_find_nearest_size(const void *array, size_t array_size, - size_t entry_size, size_t width_offset, - size_t height_offset, s32 width, s32 height); +__v4l2_find_nearest_size_conditional(const void *array, size_t array_size, + size_t entry_size, size_t width_offset, + size_t height_offset, s32 width, + s32 height, + bool (*func)(const void *array, + size_t index, + const void *context), + const void *context); + +/** + * v4l2_find_nearest_size - Find the nearest size among a discrete set of + * resolutions contained in an array of a driver specific struct + * + * @array: a driver specific array of image sizes + * @array_size: the length of the driver specific array of image sizes + * @width_field: the name of the width field in the driver specific struct + * @height_field: the name of the height field in the driver specific struct + * @width: desired width + * @height: desired height + * + * Finds the closest resolution to minimize the width and height differences + * between what requested and the supported resolutions. The size of the width + * and height fields in the driver specific must equal to that of u32, i.e. four + * bytes. + * + * Returns the best match or NULL if the length of the array is zero. + */ +#define v4l2_find_nearest_size(array, array_size, width_field, \ + height_field, width, height) \ + v4l2_find_nearest_size_conditional(array, array_size, width_field, \ + height_field, width, height, NULL, \ + NULL) /** * v4l2_g_parm_cap - helper routine for vidioc_g_parm to fill this in by -- cgit v1.2.3-59-g8ed1b From 7dc513cf7db712108885fa50ef7a800fd26667ff Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Thu, 24 Apr 2025 01:56:31 +0800 Subject: media: ov08x40: Separate the lane configuration and PLL settings To prepare upcoming support of multiple sensor modes, this change separates the lane configuration and PLL settings from the original mode-specific settings. Signed-off-by: Jason Chen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov08x40.c | 56 ++++++++++----------------------------------- 1 file changed, 12 insertions(+), 44 deletions(-) diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c index 54575eea3c49..4991fcb02a91 100644 --- a/drivers/media/i2c/ov08x40.c +++ b/drivers/media/i2c/ov08x40.c @@ -168,17 +168,7 @@ static const struct ov08x40_reg mipi_data_rate_800mbps[] = { {0x5a1f, 0x0e}, {0x5a27, 0x0e}, {0x6002, 0x2e}, -}; - -static const struct ov08x40_reg mode_3856x2416_regs[] = { - {0x5000, 0x5d}, - {0x5001, 0x20}, - {0x5008, 0xb0}, - {0x50c1, 0x00}, - {0x53c1, 0x00}, - {0x5f40, 0x00}, - {0x5f41, 0x40}, - {0x0300, 0x3a}, + {0x0300, 0x3a}, /* PLL CTRL */ {0x0301, 0xc8}, {0x0302, 0x31}, {0x0303, 0x03}, @@ -211,6 +201,17 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x032f, 0xa0}, {0x0350, 0x00}, {0x0360, 0x01}, + {0x3012, 0x41}, /* MIPI SC Lanes */ +}; + +static const struct ov08x40_reg mode_3856x2416_regs[] = { + {0x5000, 0x5d}, + {0x5001, 0x20}, + {0x5008, 0xb0}, + {0x50c1, 0x00}, + {0x53c1, 0x00}, + {0x5f40, 0x00}, + {0x5f41, 0x40}, {0x1216, 0x60}, {0x1217, 0x5b}, {0x1218, 0x00}, @@ -690,39 +691,6 @@ static const struct ov08x40_reg mode_1928x1208_regs[] = { {0x53c1, 0x00}, {0x5f40, 0x00}, {0x5f41, 0x40}, - {0x0300, 0x3a}, - {0x0301, 0xc8}, - {0x0302, 0x31}, - {0x0303, 0x03}, - {0x0304, 0x01}, - {0x0305, 0xa1}, - {0x0306, 0x04}, - {0x0307, 0x01}, - {0x0308, 0x03}, - {0x0309, 0x03}, - {0x0310, 0x0a}, - {0x0311, 0x02}, - {0x0312, 0x01}, - {0x0313, 0x08}, - {0x0314, 0x66}, - {0x0315, 0x00}, - {0x0316, 0x34}, - {0x0320, 0x02}, - {0x0321, 0x03}, - {0x0323, 0x05}, - {0x0324, 0x01}, - {0x0325, 0xb8}, - {0x0326, 0x4a}, - {0x0327, 0x04}, - {0x0329, 0x00}, - {0x032a, 0x05}, - {0x032b, 0x00}, - {0x032c, 0x00}, - {0x032d, 0x00}, - {0x032e, 0x02}, - {0x032f, 0xa0}, - {0x0350, 0x00}, - {0x0360, 0x01}, {0x1216, 0x60}, {0x1217, 0x5b}, {0x1218, 0x00}, -- cgit v1.2.3-59-g8ed1b From ad12f6f914a4923befe64fce10c4f890173e6152 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Thu, 24 Apr 2025 01:56:32 +0800 Subject: media: ov08x40: Add support for 2/4 lanes at 1500 Mbps Add register settings for 1500Mbps to support both 2-lane and 4-lane configurations Signed-off-by: Jason Chen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov08x40.c | 441 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 441 insertions(+) diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c index 4991fcb02a91..309da4fbfbd1 100644 --- a/drivers/media/i2c/ov08x40.c +++ b/drivers/media/i2c/ov08x40.c @@ -107,6 +107,7 @@ enum { OV08X40_LINK_FREQ_400MHZ_INDEX, + OV08X40_LINK_FREQ_749MHZ_INDEX, }; struct ov08x40_reg { @@ -204,6 +205,392 @@ static const struct ov08x40_reg mipi_data_rate_800mbps[] = { {0x3012, 0x41}, /* MIPI SC Lanes */ }; +static const struct ov08x40_reg mipi_data_rate_1500mbps[] = { + {0x0103, 0x01}, + {0x1000, 0x00}, + {0x1601, 0xd0}, + {0x1001, 0x04}, + {0x5004, 0x53}, + {0x5110, 0x00}, + {0x5111, 0x14}, + {0x5112, 0x01}, + {0x5113, 0x7b}, + {0x5114, 0x00}, + {0x5152, 0xa3}, + {0x5a52, 0x1f}, + {0x5a1a, 0x0e}, + {0x5a1b, 0x10}, + {0x5a1f, 0x0e}, + {0x5a27, 0x0e}, + {0x6002, 0x2e}, + {0x0300, 0x3a}, /* PLL */ + {0x0301, 0x88}, + {0x0302, 0x31}, + {0x0303, 0x05}, + {0x0304, 0x01}, + {0x0305, 0x38}, + {0x0306, 0x04}, + {0x0307, 0x00}, + {0x0308, 0x03}, + {0x0309, 0x02}, + {0x0310, 0x0a}, + {0x0311, 0x02}, + {0x0312, 0x01}, + {0x0313, 0x08}, + {0x0314, 0x00}, + {0x0315, 0x00}, + {0x0316, 0x2c}, + {0x0320, 0x02}, + {0x0321, 0x03}, + {0x0323, 0x05}, + {0x0324, 0x01}, + {0x0325, 0xb8}, + {0x0326, 0x4a}, + {0x0327, 0x04}, + {0x0329, 0x00}, + {0x032a, 0x05}, + {0x032b, 0x00}, + {0x032c, 0x00}, + {0x032d, 0x00}, + {0x032e, 0x02}, + {0x032f, 0xa0}, + {0x0350, 0x00}, + {0x0360, 0x01}, + {0x3012, 0x21}, /* MIPI SC Lanes */ +}; + +static const struct ov08x40_reg mode_3856x2176_regs_800mbps[] = { + {0x5000, 0x5d}, + {0x5001, 0x20}, + {0x3012, 0x41}, + {0x3400, 0x1c}, + {0x3419, 0x13}, + {0x341a, 0x89}, + {0x3426, 0x00}, + {0x3501, 0x02}, + {0x3502, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {0x3541, 0x01}, + {0x3542, 0x00}, + {0x3548, 0x01}, + {0x3712, 0x51}, + {0x3714, 0x24}, + {0x3761, 0x17}, + {0x376e, 0x03}, + {0x37b0, 0x00}, + {0x37b1, 0xab}, + {0x37b3, 0x82}, + {0x37b4, 0x00}, + {0x37b5, 0xe4}, + {0x37b6, 0x01}, + {0x37b7, 0xee}, + {0x3820, 0x00}, + {0x3821, 0x04}, + {0x3823, 0x04}, + {0x384d, 0x80}, + {0x3894, 0x00}, + {0x400b, 0x08}, + {0x400d, 0x08}, + {0x4016, 0x2d}, + {0x4501, 0x00}, + {0x4542, 0x00}, + {0x4837, 0x14}, + {0x4850, 0x42}, + {0x3a20, 0x00}, + {0x3939, 0x9d}, + {0x3902, 0x0e}, + {0x3903, 0x0e}, + {0x3904, 0x0e}, + {0x3905, 0x0e}, + {0x3906, 0x07}, + {0x3907, 0x0d}, + {0x3908, 0x11}, + {0x3909, 0x12}, + {0x390c, 0x33}, + {0x390d, 0x66}, + {0x390e, 0xaa}, + {0x3915, 0x90}, + {0x3917, 0x90}, + {0x3440, 0xa4}, + {0x3a26, 0x1d}, + {0x3a2c, 0x4a}, + {0x3a32, 0x55}, + {0x392d, 0x02}, + {0x3930, 0x08}, + {0x3933, 0x0c}, + {0x392a, 0x54}, + {0x392b, 0xa8}, + {0x380d, 0x80}, + {0x380e, 0x13}, + {0x380f, 0x88}, + {0x3803, 0x70}, + {0x3807, 0x0f}, + {0x3808, 0x0f}, + {0x3809, 0x10}, + {0x380a, 0x08}, + {0x380b, 0x80}, + {0x3811, 0x08}, + {0x3813, 0x10}, + {0x3501, 0x10}, + {0x3508, 0x0f}, + {0x3509, 0x80}, + {0x3813, 0x0f}, +}; + +/* OV08X 1C 3856x2176_DPHY1500M-2L */ +static const struct ov08x40_reg mode_3856x2176_regs_1500mbps[] = { + {0x5000, 0x5d}, + {0x5001, 0x20}, + {0x3012, 0x21}, + {0x3400, 0x1c}, + {0x3419, 0x12}, + {0x341a, 0x99}, + {0x3426, 0x00}, + {0x3501, 0x02}, + {0x3502, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {0x3541, 0x01}, + {0x3542, 0x00}, + {0x3548, 0x01}, + {0x3712, 0x51}, + {0x3714, 0x24}, + {0x3761, 0x17}, + {0x376e, 0x03}, + {0x37b0, 0x00}, + {0x37b1, 0xab}, + {0x37b3, 0x82}, + {0x37b4, 0x00}, + {0x37b5, 0xe4}, + {0x37b6, 0x01}, + {0x37b7, 0xee}, + {0x3803, 0x70}, + {0x3807, 0x0f}, + {0x3808, 0x0f}, + {0x3809, 0x10}, + {0x380a, 0x08}, + {0x380b, 0x80}, + {0x380d, 0xa0}, + {0x380e, 0x12}, + {0x380f, 0x98}, + {0x3811, 0x08}, + {0x3813, 0x10}, + {0x3820, 0x00}, + {0x3821, 0x04}, + {0x3823, 0x04}, + {0x384d, 0xa0}, + {0x3894, 0x00}, + {0x400b, 0x08}, + {0x400d, 0x08}, + {0x4016, 0x2d}, + {0x4501, 0x00}, + {0x4542, 0x00}, + {0x4837, 0x0a}, + {0x4850, 0x47}, + {0x3a20, 0x00}, + {0x3939, 0x9d}, + {0x3902, 0x0e}, + {0x3903, 0x0e}, + {0x3904, 0x0e}, + {0x3905, 0x0e}, + {0x3906, 0x07}, + {0x3907, 0x0d}, + {0x3908, 0x11}, + {0x3909, 0x12}, + {0x390c, 0x33}, + {0x390d, 0x66}, + {0x390e, 0xaa}, + {0x3915, 0x90}, + {0x3917, 0x90}, + {0x3440, 0xa4}, + {0x3a26, 0x1d}, + {0x3a2c, 0x4a}, + {0x3a32, 0x55}, + {0x392d, 0x02}, + {0x3930, 0x08}, + {0x3933, 0x0c}, + {0x392a, 0x54}, + {0x392b, 0xa8}, + {0x3501, 0x10}, + {0x3508, 0x0f}, + {0x3509, 0x80}, + {0x3813, 0x0f}, +}; + +/* OV08X 4C1stg 1928x1088_DPHY1500M-2L 30fps */ +static const struct ov08x40_reg mode_1928x1088_regs_1500mbps[] = { + {0x5000, 0x55}, + {0x5001, 0x00}, + {0x3012, 0x21}, + {0x3400, 0x30}, + {0x3419, 0x08}, + {0x341a, 0x4f}, + {0x3426, 0x00}, + {0x3501, 0x02}, + {0x3502, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {0x3541, 0x01}, + {0x3542, 0x00}, + {0x3548, 0x01}, + {0x3712, 0x50}, + {0x3714, 0x21}, + {0x3761, 0x28}, + {0x376e, 0x07}, + {0x37b0, 0x01}, + {0x37b1, 0x0f}, + {0x37b3, 0xd6}, + {0x37b4, 0x01}, + {0x37b5, 0x48}, + {0x37b6, 0x02}, + {0x37b7, 0x40}, + {0x3803, 0x78}, + {0x3807, 0x07}, + {0x3808, 0x07}, + {0x3809, 0x88}, + {0x380a, 0x04}, + {0x380b, 0x40}, + {0x380d, 0xf0}, + {0x380e, 0x08}, + {0x380f, 0x4e}, + {0x3811, 0x04}, + {0x3813, 0x03}, + {0x3820, 0x02}, + {0x3821, 0x14}, + {0x3823, 0x84}, + {0x384d, 0xf0}, + {0x3894, 0x03}, + {0x400b, 0x04}, + {0x400d, 0x04}, + {0x4016, 0x27}, + {0x4501, 0x10}, + {0x4542, 0x01}, + {0x4837, 0x0a}, + {0x4850, 0x47}, + {0x4911, 0x00}, + {0x4919, 0x00}, + {0x491a, 0x40}, + {0x4920, 0x04}, + {0x4921, 0x00}, + {0x4922, 0x04}, + {0x4923, 0x00}, + {0x4924, 0x04}, + {0x4925, 0x00}, + {0x4926, 0x04}, + {0x4927, 0x00}, + {0x4930, 0x00}, + {0x4931, 0x00}, + {0x4932, 0x00}, + {0x4933, 0x00}, + {0x4934, 0x00}, + {0x4935, 0x00}, + {0x4936, 0x00}, + {0x4937, 0x00}, + {0x4940, 0x00}, + {0x4941, 0x80}, + {0x4942, 0x00}, + {0x4943, 0x80}, + {0x4944, 0x00}, + {0x4945, 0x80}, + {0x4946, 0x00}, + {0x4947, 0x80}, + {0x4960, 0x00}, + {0x4961, 0x00}, + {0x4962, 0x00}, + {0x4963, 0x00}, + {0x4964, 0x00}, + {0x4965, 0x00}, + {0x4966, 0x00}, + {0x4967, 0x00}, + {0x4968, 0x00}, + {0x4969, 0x00}, + {0x496a, 0x00}, + {0x496b, 0x00}, + {0x496c, 0x00}, + {0x496d, 0x00}, + {0x496e, 0x00}, + {0x496f, 0x00}, + {0x4970, 0x00}, + {0x4971, 0x00}, + {0x4972, 0x00}, + {0x4973, 0x00}, + {0x4974, 0x00}, + {0x4975, 0x00}, + {0x4976, 0x00}, + {0x4977, 0x00}, + {0x4978, 0x00}, + {0x4979, 0x00}, + {0x497a, 0x00}, + {0x497b, 0x00}, + {0x497c, 0x00}, + {0x497d, 0x00}, + {0x497e, 0x00}, + {0x497f, 0x00}, + {0x49e0, 0x00}, + {0x49e1, 0x00}, + {0x49e2, 0x00}, + {0x49e3, 0x00}, + {0x49e4, 0x00}, + {0x49e5, 0x00}, + {0x49e6, 0x00}, + {0x49e7, 0x00}, + {0x49e8, 0x00}, + {0x49e9, 0x80}, + {0x49ea, 0x00}, + {0x49eb, 0x80}, + {0x49ec, 0x00}, + {0x49ed, 0x80}, + {0x49ee, 0x00}, + {0x49ef, 0x80}, + {0x49f0, 0x02}, + {0x49f1, 0x04}, + {0x3a20, 0x05}, + {0x3939, 0x6b}, + {0x3902, 0x10}, + {0x3903, 0x10}, + {0x3904, 0x10}, + {0x3905, 0x10}, + {0x3906, 0x01}, + {0x3907, 0x0b}, + {0x3908, 0x10}, + {0x3909, 0x13}, + {0x390b, 0x11}, + {0x390c, 0x21}, + {0x390d, 0x32}, + {0x390e, 0x76}, + {0x3a1a, 0x1c}, + {0x3a26, 0x17}, + {0x3a2c, 0x50}, + {0x3a32, 0x4f}, + {0x3ace, 0x01}, + {0x3ad2, 0x01}, + {0x3ad6, 0x01}, + {0x3ada, 0x01}, + {0x3ade, 0x01}, + {0x3ae2, 0x01}, + {0x3aee, 0x01}, + {0x3af2, 0x01}, + {0x3af6, 0x01}, + {0x3afa, 0x01}, + {0x3afe, 0x01}, + {0x3b02, 0x01}, + {0x3b06, 0x01}, + {0x392d, 0x01}, + {0x3930, 0x09}, + {0x3933, 0x0d}, + {0x392a, 0x52}, + {0x392b, 0xa3}, + {0x340b, 0x1b}, + {0x3501, 0x01}, + {0x3508, 0x0f}, + {0x3509, 0x00}, + {0x3541, 0x00}, + {0x3542, 0x80}, + {0x3548, 0x0f}, + {0x3813, 0x03}, +}; + static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x5000, 0x5d}, {0x5001, 0x20}, @@ -1185,6 +1572,7 @@ static const char * const ov08x40_test_pattern_menu[] = { /* Configurations for supported link frequencies */ #define OV08X40_LINK_FREQ_400MHZ 400000000ULL +#define OV08X40_LINK_FREQ_749MHZ 749000000ULL #define OV08X40_SCLK_96MHZ 96000000ULL #define OV08X40_XVCLK 19200000 #define OV08X40_DATA_LANES 4 @@ -1204,6 +1592,7 @@ static u64 link_freq_to_pixel_rate(u64 f) /* Menu items for LINK_FREQ V4L2 control */ static const s64 link_freq_menu_items[] = { OV08X40_LINK_FREQ_400MHZ, + OV08X40_LINK_FREQ_749MHZ, }; /* Link frequency configs */ @@ -1214,6 +1603,12 @@ static const struct ov08x40_link_freq_config link_freq_configs[] = { .regs = mipi_data_rate_800mbps, } }, + [OV08X40_LINK_FREQ_749MHZ_INDEX] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_1500mbps), + .regs = mipi_data_rate_1500mbps, + } + }, }; /* Mode configs */ @@ -1233,6 +1628,22 @@ static const struct ov08x40_mode supported_modes[] = { .exposure_shift = 1, .exposure_margin = OV08X40_EXPOSURE_MAX_MARGIN, }, + { + .width = 3856, + .height = 2176, + .vts_def = OV08X40_VTS_30FPS, + .vts_min = OV08X40_VTS_30FPS, + .llp = 0x10aa, /* in normal mode, tline time = 2 * HTS / SCLK */ + .lanes = 4, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3856x2176_regs_800mbps), + .regs = mode_3856x2176_regs_800mbps, + }, + .link_freq_index = OV08X40_LINK_FREQ_400MHZ_INDEX, + .exposure_shift = 1, + .exposure_margin = OV08X40_EXPOSURE_MAX_MARGIN, + }, + { .width = 1928, .height = 1208, @@ -1248,6 +1659,36 @@ static const struct ov08x40_mode supported_modes[] = { .exposure_shift = 0, .exposure_margin = OV08X40_EXPOSURE_BIN_MAX_MARGIN, }, + { + .width = 3856, + .height = 2176, + .vts_def = OV08X40_VTS_30FPS, + .vts_min = OV08X40_VTS_30FPS, + .llp = 0x10aa, /* in normal mode, tline time = 2 * HTS / SCLK */ + .lanes = 2, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3856x2176_regs_1500mbps), + .regs = mode_3856x2176_regs_1500mbps, + }, + .link_freq_index = OV08X40_LINK_FREQ_749MHZ_INDEX, + .exposure_shift = 1, + .exposure_margin = OV08X40_EXPOSURE_MAX_MARGIN, + }, + { + .width = 1928, + .height = 1088, + .vts_def = OV08X40_VTS_BIN_30FPS, + .vts_min = OV08X40_VTS_BIN_30FPS, + .llp = 0x960, + .lanes = 2, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1928x1088_regs_1500mbps), + .regs = mode_1928x1088_regs_1500mbps, + }, + .link_freq_index = OV08X40_LINK_FREQ_749MHZ_INDEX, + .exposure_shift = 0, + .exposure_margin = OV08X40_EXPOSURE_MAX_MARGIN, + }, }; static const char * const ov08x40_supply_names[] = { -- cgit v1.2.3-59-g8ed1b From ff1f5010a96a4496ce3c0098b330381f87ac9d87 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Thu, 24 Apr 2025 01:56:33 +0800 Subject: media: ov08x40: Remove common register settings from resolution-specific table Prepare for refactoring register table by removing register entries that are common across all modes from resolution-specific table These will be moved into a shared common register array in the next commit. Signed-off-by: Jason Chen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov08x40.c | 802 -------------------------------------------- 1 file changed, 802 deletions(-) diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c index 309da4fbfbd1..db1abf831cac 100644 --- a/drivers/media/i2c/ov08x40.c +++ b/drivers/media/i2c/ov08x40.c @@ -594,426 +594,52 @@ static const struct ov08x40_reg mode_1928x1088_regs_1500mbps[] = { static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x5000, 0x5d}, {0x5001, 0x20}, - {0x5008, 0xb0}, - {0x50c1, 0x00}, - {0x53c1, 0x00}, - {0x5f40, 0x00}, - {0x5f41, 0x40}, - {0x1216, 0x60}, - {0x1217, 0x5b}, - {0x1218, 0x00}, - {0x1220, 0x24}, - {0x198a, 0x00}, - {0x198b, 0x01}, - {0x198e, 0x00}, - {0x198f, 0x01}, - {0x3009, 0x04}, {0x3012, 0x41}, - {0x3015, 0x00}, - {0x3016, 0xb0}, - {0x3017, 0xf0}, - {0x3018, 0xf0}, - {0x3019, 0xd2}, - {0x301a, 0xb0}, - {0x301c, 0x81}, - {0x301d, 0x02}, - {0x301e, 0x80}, - {0x3022, 0xf0}, - {0x3025, 0x89}, - {0x3030, 0x03}, - {0x3044, 0xc2}, - {0x3050, 0x35}, - {0x3051, 0x60}, - {0x3052, 0x25}, - {0x3053, 0x00}, - {0x3054, 0x00}, - {0x3055, 0x02}, - {0x3056, 0x80}, - {0x3057, 0x80}, - {0x3058, 0x80}, - {0x3059, 0x00}, - {0x3107, 0x86}, {0x3400, 0x1c}, - {0x3401, 0x80}, - {0x3402, 0x8c}, {0x3419, 0x13}, {0x341a, 0x89}, - {0x341b, 0x30}, - {0x3420, 0x00}, - {0x3421, 0x00}, - {0x3422, 0x00}, - {0x3423, 0x00}, - {0x3424, 0x00}, - {0x3425, 0x00}, {0x3426, 0x00}, - {0x3427, 0x00}, - {0x3428, 0x0f}, - {0x3429, 0x00}, - {0x342a, 0x00}, - {0x342b, 0x00}, - {0x342c, 0x00}, - {0x342d, 0x00}, - {0x342e, 0x00}, - {0x342f, 0x11}, - {0x3430, 0x11}, - {0x3431, 0x10}, - {0x3432, 0x00}, - {0x3433, 0x00}, - {0x3434, 0x00}, - {0x3435, 0x00}, - {0x3436, 0x00}, - {0x3437, 0x00}, - {0x3442, 0x02}, - {0x3443, 0x02}, - {0x3444, 0x07}, - {0x3450, 0x00}, - {0x3451, 0x00}, - {0x3452, 0x18}, - {0x3453, 0x18}, - {0x3454, 0x00}, - {0x3455, 0x80}, - {0x3456, 0x08}, - {0x3500, 0x00}, {0x3501, 0x02}, {0x3502, 0x00}, - {0x3504, 0x4c}, - {0x3506, 0x30}, - {0x3507, 0x00}, {0x3508, 0x01}, {0x3509, 0x00}, - {0x350a, 0x01}, - {0x350b, 0x00}, - {0x350c, 0x00}, - {0x3540, 0x00}, {0x3541, 0x01}, {0x3542, 0x00}, - {0x3544, 0x4c}, - {0x3546, 0x30}, - {0x3547, 0x00}, {0x3548, 0x01}, - {0x3549, 0x00}, - {0x354a, 0x01}, - {0x354b, 0x00}, - {0x354c, 0x00}, - {0x3688, 0x02}, - {0x368a, 0x2e}, - {0x368e, 0x71}, - {0x3696, 0xd1}, - {0x3699, 0x00}, - {0x369a, 0x00}, - {0x36a4, 0x00}, - {0x36a6, 0x00}, - {0x3711, 0x00}, {0x3712, 0x51}, - {0x3713, 0x00}, {0x3714, 0x24}, - {0x3716, 0x00}, - {0x3718, 0x07}, - {0x371a, 0x1c}, - {0x371b, 0x00}, - {0x3720, 0x08}, - {0x3725, 0x32}, - {0x3727, 0x05}, - {0x3760, 0x02}, {0x3761, 0x17}, - {0x3762, 0x02}, - {0x3763, 0x02}, - {0x3764, 0x02}, - {0x3765, 0x2c}, - {0x3766, 0x04}, - {0x3767, 0x2c}, - {0x3768, 0x02}, - {0x3769, 0x00}, - {0x376b, 0x20}, {0x376e, 0x03}, {0x37b0, 0x00}, {0x37b1, 0xab}, - {0x37b2, 0x01}, {0x37b3, 0x82}, {0x37b4, 0x00}, {0x37b5, 0xe4}, {0x37b6, 0x01}, {0x37b7, 0xee}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, {0x3803, 0x00}, - {0x3804, 0x0f}, - {0x3805, 0x1f}, - {0x3806, 0x09}, {0x3807, 0x7f}, {0x3808, 0x0f}, {0x3809, 0x10}, {0x380a, 0x09}, {0x380b, 0x70}, - {0x380c, 0x02}, {0x380d, 0x80}, {0x380e, 0x13}, {0x380f, 0x88}, - {0x3810, 0x00}, {0x3811, 0x08}, - {0x3812, 0x00}, {0x3813, 0x07}, - {0x3814, 0x11}, - {0x3815, 0x11}, {0x3820, 0x00}, {0x3821, 0x04}, - {0x3822, 0x00}, {0x3823, 0x04}, - {0x3828, 0x0f}, - {0x382a, 0x80}, - {0x382e, 0x41}, - {0x3837, 0x08}, - {0x383a, 0x81}, - {0x383b, 0x81}, - {0x383c, 0x11}, - {0x383d, 0x11}, - {0x383e, 0x00}, - {0x383f, 0x38}, - {0x3840, 0x00}, - {0x3847, 0x00}, - {0x384a, 0x00}, - {0x384c, 0x02}, {0x384d, 0x80}, - {0x3856, 0x50}, - {0x3857, 0x30}, - {0x3858, 0x80}, - {0x3859, 0x40}, - {0x3860, 0x00}, - {0x3888, 0x00}, - {0x3889, 0x00}, - {0x388a, 0x00}, - {0x388b, 0x00}, - {0x388c, 0x00}, - {0x388d, 0x00}, - {0x388e, 0x00}, - {0x388f, 0x00}, {0x3894, 0x00}, - {0x3895, 0x00}, - {0x3c84, 0x00}, - {0x3d85, 0x8b}, - {0x3daa, 0x80}, - {0x3dab, 0x14}, - {0x3dac, 0x80}, - {0x3dad, 0xc8}, - {0x3dae, 0x81}, - {0x3daf, 0x7b}, - {0x3f00, 0x10}, - {0x3f01, 0x11}, - {0x3f06, 0x0d}, - {0x3f07, 0x0b}, - {0x3f08, 0x0d}, - {0x3f09, 0x0b}, - {0x3f0a, 0x01}, - {0x3f0b, 0x11}, - {0x3f0c, 0x33}, - {0x4001, 0x07}, - {0x4007, 0x20}, - {0x4008, 0x00}, - {0x4009, 0x05}, - {0x400a, 0x00}, {0x400b, 0x08}, - {0x400c, 0x00}, {0x400d, 0x08}, - {0x400e, 0x14}, - {0x4010, 0xf4}, - {0x4011, 0x03}, - {0x4012, 0x55}, - {0x4015, 0x00}, {0x4016, 0x2d}, - {0x4017, 0x00}, - {0x4018, 0x0f}, - {0x401b, 0x08}, - {0x401c, 0x00}, - {0x401d, 0x10}, - {0x401e, 0x02}, - {0x401f, 0x00}, - {0x4050, 0x06}, - {0x4051, 0xff}, - {0x4052, 0xff}, - {0x4053, 0xff}, - {0x4054, 0xff}, - {0x4055, 0xff}, - {0x4056, 0xff}, - {0x4057, 0x7f}, - {0x4058, 0x00}, - {0x4059, 0x00}, - {0x405a, 0x00}, - {0x405b, 0x00}, - {0x405c, 0x07}, - {0x405d, 0xff}, - {0x405e, 0x07}, - {0x405f, 0xff}, - {0x4080, 0x78}, - {0x4081, 0x78}, - {0x4082, 0x78}, - {0x4083, 0x78}, - {0x4019, 0x00}, - {0x401a, 0x40}, - {0x4020, 0x04}, - {0x4021, 0x00}, - {0x4022, 0x04}, - {0x4023, 0x00}, - {0x4024, 0x04}, - {0x4025, 0x00}, - {0x4026, 0x04}, - {0x4027, 0x00}, - {0x4030, 0x00}, - {0x4031, 0x00}, - {0x4032, 0x00}, - {0x4033, 0x00}, - {0x4034, 0x00}, - {0x4035, 0x00}, - {0x4036, 0x00}, - {0x4037, 0x00}, - {0x4040, 0x00}, - {0x4041, 0x80}, - {0x4042, 0x00}, - {0x4043, 0x80}, - {0x4044, 0x00}, - {0x4045, 0x80}, - {0x4046, 0x00}, - {0x4047, 0x80}, - {0x4060, 0x00}, - {0x4061, 0x00}, - {0x4062, 0x00}, - {0x4063, 0x00}, - {0x4064, 0x00}, - {0x4065, 0x00}, - {0x4066, 0x00}, - {0x4067, 0x00}, - {0x4068, 0x00}, - {0x4069, 0x00}, - {0x406a, 0x00}, - {0x406b, 0x00}, - {0x406c, 0x00}, - {0x406d, 0x00}, - {0x406e, 0x00}, - {0x406f, 0x00}, - {0x4070, 0x00}, - {0x4071, 0x00}, - {0x4072, 0x00}, - {0x4073, 0x00}, - {0x4074, 0x00}, - {0x4075, 0x00}, - {0x4076, 0x00}, - {0x4077, 0x00}, - {0x4078, 0x00}, - {0x4079, 0x00}, - {0x407a, 0x00}, - {0x407b, 0x00}, - {0x407c, 0x00}, - {0x407d, 0x00}, - {0x407e, 0x00}, - {0x407f, 0x00}, - {0x40e0, 0x00}, - {0x40e1, 0x00}, - {0x40e2, 0x00}, - {0x40e3, 0x00}, - {0x40e4, 0x00}, - {0x40e5, 0x00}, - {0x40e6, 0x00}, - {0x40e7, 0x00}, - {0x40e8, 0x00}, - {0x40e9, 0x80}, - {0x40ea, 0x00}, - {0x40eb, 0x80}, - {0x40ec, 0x00}, - {0x40ed, 0x80}, - {0x40ee, 0x00}, - {0x40ef, 0x80}, - {0x40f0, 0x02}, - {0x40f1, 0x04}, - {0x4300, 0x00}, - {0x4301, 0x00}, - {0x4302, 0x00}, - {0x4303, 0x00}, - {0x4304, 0x00}, - {0x4305, 0x00}, - {0x4306, 0x00}, - {0x4307, 0x00}, - {0x4308, 0x00}, - {0x4309, 0x00}, - {0x430a, 0x00}, - {0x430b, 0xff}, - {0x430c, 0xff}, - {0x430d, 0x00}, - {0x430e, 0x00}, - {0x4315, 0x00}, - {0x4316, 0x00}, - {0x4317, 0x00}, - {0x4318, 0x00}, - {0x4319, 0x00}, - {0x431a, 0x00}, - {0x431b, 0x00}, - {0x431c, 0x00}, - {0x4500, 0x07}, {0x4501, 0x00}, - {0x4502, 0x00}, - {0x4503, 0x0f}, - {0x4504, 0x80}, - {0x4506, 0x01}, - {0x4509, 0x05}, - {0x450c, 0x00}, - {0x450d, 0x20}, - {0x450e, 0x00}, - {0x450f, 0x00}, - {0x4510, 0x00}, - {0x4523, 0x00}, - {0x4526, 0x00}, {0x4542, 0x00}, - {0x4543, 0x00}, - {0x4544, 0x00}, - {0x4545, 0x00}, - {0x4546, 0x00}, - {0x4547, 0x10}, - {0x4602, 0x00}, - {0x4603, 0x15}, - {0x460b, 0x07}, - {0x4680, 0x11}, - {0x4686, 0x00}, - {0x4687, 0x00}, - {0x4700, 0x00}, - {0x4800, 0x64}, - {0x4806, 0x40}, - {0x480b, 0x10}, - {0x480c, 0x80}, - {0x480f, 0x32}, - {0x4813, 0xe4}, {0x4837, 0x14}, {0x4850, 0x42}, - {0x4884, 0x04}, - {0x4c00, 0xf8}, - {0x4c01, 0x44}, - {0x4c03, 0x00}, - {0x4d00, 0x00}, - {0x4d01, 0x16}, - {0x4d04, 0x10}, - {0x4d05, 0x00}, - {0x4d06, 0x0c}, - {0x4d07, 0x00}, - {0x3d84, 0x04}, - {0x3680, 0xa4}, - {0x3682, 0x80}, - {0x3601, 0x40}, - {0x3602, 0x90}, - {0x3608, 0x0a}, - {0x3938, 0x09}, - {0x3a74, 0x84}, - {0x3a99, 0x84}, - {0x3ab9, 0xa6}, - {0x3aba, 0xba}, - {0x3b12, 0x84}, - {0x3b14, 0xbb}, - {0x3b15, 0xbf}, - {0x3a29, 0x26}, - {0x3a1f, 0x8a}, - {0x3a22, 0x91}, - {0x3a25, 0x96}, - {0x3a28, 0xb4}, - {0x3a2b, 0xba}, - {0x3a2e, 0xbf}, - {0x3a31, 0xc1}, {0x3a20, 0x00}, {0x3939, 0x9d}, {0x3902, 0x0e}, @@ -1024,48 +650,21 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { {0x3907, 0x0d}, {0x3908, 0x11}, {0x3909, 0x12}, - {0x360f, 0x99}, {0x390c, 0x33}, {0x390d, 0x66}, {0x390e, 0xaa}, - {0x3911, 0x90}, - {0x3913, 0x90}, {0x3915, 0x90}, {0x3917, 0x90}, - {0x3b3f, 0x9d}, - {0x3b45, 0x9d}, - {0x3b1b, 0xc9}, - {0x3b21, 0xc9}, {0x3440, 0xa4}, - {0x3a23, 0x15}, {0x3a26, 0x1d}, {0x3a2c, 0x4a}, - {0x3a2f, 0x18}, {0x3a32, 0x55}, - {0x3b0a, 0x01}, - {0x3b0b, 0x00}, - {0x3b0e, 0x01}, - {0x3b0f, 0x00}, - {0x392c, 0x02}, {0x392d, 0x02}, - {0x392e, 0x04}, - {0x392f, 0x03}, {0x3930, 0x08}, - {0x3931, 0x07}, - {0x3932, 0x10}, {0x3933, 0x0c}, - {0x3609, 0x08}, - {0x3921, 0x0f}, - {0x3928, 0x15}, - {0x3929, 0x2a}, {0x392a, 0x54}, {0x392b, 0xa8}, - {0x3426, 0x10}, - {0x3407, 0x01}, - {0x3404, 0x01}, - {0x3500, 0x00}, {0x3501, 0x10}, - {0x3502, 0x10}, {0x3508, 0x0f}, {0x3509, 0x80}, }; @@ -1073,426 +672,52 @@ static const struct ov08x40_reg mode_3856x2416_regs[] = { static const struct ov08x40_reg mode_1928x1208_regs[] = { {0x5000, 0x55}, {0x5001, 0x00}, - {0x5008, 0xb0}, - {0x50c1, 0x00}, - {0x53c1, 0x00}, - {0x5f40, 0x00}, - {0x5f41, 0x40}, - {0x1216, 0x60}, - {0x1217, 0x5b}, - {0x1218, 0x00}, - {0x1220, 0x24}, - {0x198a, 0x00}, - {0x198b, 0x01}, - {0x198e, 0x00}, - {0x198f, 0x01}, - {0x3009, 0x04}, {0x3012, 0x41}, - {0x3015, 0x00}, - {0x3016, 0xb0}, - {0x3017, 0xf0}, - {0x3018, 0xf0}, - {0x3019, 0xd2}, - {0x301a, 0xb0}, - {0x301c, 0x81}, - {0x301d, 0x02}, - {0x301e, 0x80}, - {0x3022, 0xf0}, - {0x3025, 0x89}, - {0x3030, 0x03}, - {0x3044, 0xc2}, - {0x3050, 0x35}, - {0x3051, 0x60}, - {0x3052, 0x25}, - {0x3053, 0x00}, - {0x3054, 0x00}, - {0x3055, 0x02}, - {0x3056, 0x80}, - {0x3057, 0x80}, - {0x3058, 0x80}, - {0x3059, 0x00}, - {0x3107, 0x86}, {0x3400, 0x1c}, - {0x3401, 0x80}, - {0x3402, 0x8c}, {0x3419, 0x08}, {0x341a, 0xaf}, - {0x341b, 0x30}, - {0x3420, 0x00}, - {0x3421, 0x00}, - {0x3422, 0x00}, - {0x3423, 0x00}, - {0x3424, 0x00}, - {0x3425, 0x00}, {0x3426, 0x00}, - {0x3427, 0x00}, - {0x3428, 0x0f}, - {0x3429, 0x00}, - {0x342a, 0x00}, - {0x342b, 0x00}, - {0x342c, 0x00}, - {0x342d, 0x00}, - {0x342e, 0x00}, - {0x342f, 0x11}, - {0x3430, 0x11}, - {0x3431, 0x10}, - {0x3432, 0x00}, - {0x3433, 0x00}, - {0x3434, 0x00}, - {0x3435, 0x00}, - {0x3436, 0x00}, - {0x3437, 0x00}, - {0x3442, 0x02}, - {0x3443, 0x02}, - {0x3444, 0x07}, - {0x3450, 0x00}, - {0x3451, 0x00}, - {0x3452, 0x18}, - {0x3453, 0x18}, - {0x3454, 0x00}, - {0x3455, 0x80}, - {0x3456, 0x08}, - {0x3500, 0x00}, {0x3501, 0x02}, {0x3502, 0x00}, - {0x3504, 0x4c}, - {0x3506, 0x30}, - {0x3507, 0x00}, {0x3508, 0x01}, {0x3509, 0x00}, - {0x350a, 0x01}, - {0x350b, 0x00}, - {0x350c, 0x00}, - {0x3540, 0x00}, {0x3541, 0x01}, {0x3542, 0x00}, - {0x3544, 0x4c}, - {0x3546, 0x30}, - {0x3547, 0x00}, {0x3548, 0x01}, - {0x3549, 0x00}, - {0x354a, 0x01}, - {0x354b, 0x00}, - {0x354c, 0x00}, - {0x3688, 0x02}, - {0x368a, 0x2e}, - {0x368e, 0x71}, - {0x3696, 0xd1}, - {0x3699, 0x00}, - {0x369a, 0x00}, - {0x36a4, 0x00}, - {0x36a6, 0x00}, - {0x3711, 0x00}, {0x3712, 0x50}, - {0x3713, 0x00}, {0x3714, 0x21}, - {0x3716, 0x00}, - {0x3718, 0x07}, - {0x371a, 0x1c}, - {0x371b, 0x00}, - {0x3720, 0x08}, - {0x3725, 0x32}, - {0x3727, 0x05}, - {0x3760, 0x02}, {0x3761, 0x28}, - {0x3762, 0x02}, - {0x3763, 0x02}, - {0x3764, 0x02}, - {0x3765, 0x2c}, - {0x3766, 0x04}, - {0x3767, 0x2c}, - {0x3768, 0x02}, - {0x3769, 0x00}, - {0x376b, 0x20}, {0x376e, 0x07}, {0x37b0, 0x01}, {0x37b1, 0x0f}, - {0x37b2, 0x01}, {0x37b3, 0xd6}, {0x37b4, 0x01}, {0x37b5, 0x48}, {0x37b6, 0x02}, {0x37b7, 0x40}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, {0x3803, 0x00}, - {0x3804, 0x0f}, - {0x3805, 0x1f}, - {0x3806, 0x09}, {0x3807, 0x7f}, {0x3808, 0x07}, {0x3809, 0x88}, {0x380a, 0x04}, {0x380b, 0xb8}, - {0x380c, 0x02}, {0x380d, 0xd0}, {0x380e, 0x11}, {0x380f, 0x5c}, - {0x3810, 0x00}, {0x3811, 0x04}, - {0x3812, 0x00}, {0x3813, 0x03}, - {0x3814, 0x11}, - {0x3815, 0x11}, {0x3820, 0x02}, {0x3821, 0x14}, - {0x3822, 0x00}, {0x3823, 0x04}, - {0x3828, 0x0f}, - {0x382a, 0x80}, - {0x382e, 0x41}, - {0x3837, 0x08}, - {0x383a, 0x81}, - {0x383b, 0x81}, - {0x383c, 0x11}, - {0x383d, 0x11}, - {0x383e, 0x00}, - {0x383f, 0x38}, - {0x3840, 0x00}, - {0x3847, 0x00}, - {0x384a, 0x00}, - {0x384c, 0x02}, {0x384d, 0xd0}, - {0x3856, 0x50}, - {0x3857, 0x30}, - {0x3858, 0x80}, - {0x3859, 0x40}, - {0x3860, 0x00}, - {0x3888, 0x00}, - {0x3889, 0x00}, - {0x388a, 0x00}, - {0x388b, 0x00}, - {0x388c, 0x00}, - {0x388d, 0x00}, - {0x388e, 0x00}, - {0x388f, 0x00}, {0x3894, 0x00}, - {0x3895, 0x00}, - {0x3c84, 0x00}, - {0x3d85, 0x8b}, - {0x3daa, 0x80}, - {0x3dab, 0x14}, - {0x3dac, 0x80}, - {0x3dad, 0xc8}, - {0x3dae, 0x81}, - {0x3daf, 0x7b}, - {0x3f00, 0x10}, - {0x3f01, 0x11}, - {0x3f06, 0x0d}, - {0x3f07, 0x0b}, - {0x3f08, 0x0d}, - {0x3f09, 0x0b}, - {0x3f0a, 0x01}, - {0x3f0b, 0x11}, - {0x3f0c, 0x33}, - {0x4001, 0x07}, - {0x4007, 0x20}, - {0x4008, 0x00}, - {0x4009, 0x05}, - {0x400a, 0x00}, {0x400b, 0x04}, - {0x400c, 0x00}, {0x400d, 0x04}, - {0x400e, 0x14}, - {0x4010, 0xf4}, - {0x4011, 0x03}, - {0x4012, 0x55}, - {0x4015, 0x00}, {0x4016, 0x27}, - {0x4017, 0x00}, - {0x4018, 0x0f}, - {0x401b, 0x08}, - {0x401c, 0x00}, - {0x401d, 0x10}, - {0x401e, 0x02}, - {0x401f, 0x00}, - {0x4050, 0x06}, - {0x4051, 0xff}, - {0x4052, 0xff}, - {0x4053, 0xff}, - {0x4054, 0xff}, - {0x4055, 0xff}, - {0x4056, 0xff}, - {0x4057, 0x7f}, - {0x4058, 0x00}, - {0x4059, 0x00}, - {0x405a, 0x00}, - {0x405b, 0x00}, - {0x405c, 0x07}, - {0x405d, 0xff}, - {0x405e, 0x07}, - {0x405f, 0xff}, - {0x4080, 0x78}, - {0x4081, 0x78}, - {0x4082, 0x78}, - {0x4083, 0x78}, - {0x4019, 0x00}, - {0x401a, 0x40}, - {0x4020, 0x04}, - {0x4021, 0x00}, - {0x4022, 0x04}, - {0x4023, 0x00}, - {0x4024, 0x04}, - {0x4025, 0x00}, - {0x4026, 0x04}, - {0x4027, 0x00}, - {0x4030, 0x00}, - {0x4031, 0x00}, - {0x4032, 0x00}, - {0x4033, 0x00}, - {0x4034, 0x00}, - {0x4035, 0x00}, - {0x4036, 0x00}, - {0x4037, 0x00}, - {0x4040, 0x00}, - {0x4041, 0x80}, - {0x4042, 0x00}, - {0x4043, 0x80}, - {0x4044, 0x00}, - {0x4045, 0x80}, - {0x4046, 0x00}, - {0x4047, 0x80}, - {0x4060, 0x00}, - {0x4061, 0x00}, - {0x4062, 0x00}, - {0x4063, 0x00}, - {0x4064, 0x00}, - {0x4065, 0x00}, - {0x4066, 0x00}, - {0x4067, 0x00}, - {0x4068, 0x00}, - {0x4069, 0x00}, - {0x406a, 0x00}, - {0x406b, 0x00}, - {0x406c, 0x00}, - {0x406d, 0x00}, - {0x406e, 0x00}, - {0x406f, 0x00}, - {0x4070, 0x00}, - {0x4071, 0x00}, - {0x4072, 0x00}, - {0x4073, 0x00}, - {0x4074, 0x00}, - {0x4075, 0x00}, - {0x4076, 0x00}, - {0x4077, 0x00}, - {0x4078, 0x00}, - {0x4079, 0x00}, - {0x407a, 0x00}, - {0x407b, 0x00}, - {0x407c, 0x00}, - {0x407d, 0x00}, - {0x407e, 0x00}, - {0x407f, 0x00}, - {0x40e0, 0x00}, - {0x40e1, 0x00}, - {0x40e2, 0x00}, - {0x40e3, 0x00}, - {0x40e4, 0x00}, - {0x40e5, 0x00}, - {0x40e6, 0x00}, - {0x40e7, 0x00}, - {0x40e8, 0x00}, - {0x40e9, 0x80}, - {0x40ea, 0x00}, - {0x40eb, 0x80}, - {0x40ec, 0x00}, - {0x40ed, 0x80}, - {0x40ee, 0x00}, - {0x40ef, 0x80}, - {0x40f0, 0x02}, - {0x40f1, 0x04}, - {0x4300, 0x00}, - {0x4301, 0x00}, - {0x4302, 0x00}, - {0x4303, 0x00}, - {0x4304, 0x00}, - {0x4305, 0x00}, - {0x4306, 0x00}, - {0x4307, 0x00}, - {0x4308, 0x00}, - {0x4309, 0x00}, - {0x430a, 0x00}, - {0x430b, 0xff}, - {0x430c, 0xff}, - {0x430d, 0x00}, - {0x430e, 0x00}, - {0x4315, 0x00}, - {0x4316, 0x00}, - {0x4317, 0x00}, - {0x4318, 0x00}, - {0x4319, 0x00}, - {0x431a, 0x00}, - {0x431b, 0x00}, - {0x431c, 0x00}, - {0x4500, 0x07}, {0x4501, 0x10}, - {0x4502, 0x00}, - {0x4503, 0x0f}, - {0x4504, 0x80}, - {0x4506, 0x01}, - {0x4509, 0x05}, - {0x450c, 0x00}, - {0x450d, 0x20}, - {0x450e, 0x00}, - {0x450f, 0x00}, - {0x4510, 0x00}, - {0x4523, 0x00}, - {0x4526, 0x00}, {0x4542, 0x00}, - {0x4543, 0x00}, - {0x4544, 0x00}, - {0x4545, 0x00}, - {0x4546, 0x00}, - {0x4547, 0x10}, - {0x4602, 0x00}, - {0x4603, 0x15}, - {0x460b, 0x07}, - {0x4680, 0x11}, - {0x4686, 0x00}, - {0x4687, 0x00}, - {0x4700, 0x00}, - {0x4800, 0x64}, - {0x4806, 0x40}, - {0x480b, 0x10}, - {0x480c, 0x80}, - {0x480f, 0x32}, - {0x4813, 0xe4}, {0x4837, 0x14}, {0x4850, 0x42}, - {0x4884, 0x04}, - {0x4c00, 0xf8}, - {0x4c01, 0x44}, - {0x4c03, 0x00}, - {0x4d00, 0x00}, - {0x4d01, 0x16}, - {0x4d04, 0x10}, - {0x4d05, 0x00}, - {0x4d06, 0x0c}, - {0x4d07, 0x00}, - {0x3d84, 0x04}, - {0x3680, 0xa4}, - {0x3682, 0x80}, - {0x3601, 0x40}, - {0x3602, 0x90}, - {0x3608, 0x0a}, - {0x3938, 0x09}, - {0x3a74, 0x84}, - {0x3a99, 0x84}, - {0x3ab9, 0xa6}, - {0x3aba, 0xba}, - {0x3b12, 0x84}, - {0x3b14, 0xbb}, - {0x3b15, 0xbf}, - {0x3a29, 0x26}, - {0x3a1f, 0x8a}, - {0x3a22, 0x91}, - {0x3a25, 0x96}, - {0x3a28, 0xb4}, - {0x3a2b, 0xba}, - {0x3a2e, 0xbf}, - {0x3a31, 0xc1}, {0x3a20, 0x05}, {0x3939, 0x6b}, {0x3902, 0x10}, @@ -1503,22 +728,13 @@ static const struct ov08x40_reg mode_1928x1208_regs[] = { {0x3907, 0x0b}, {0x3908, 0x10}, {0x3909, 0x13}, - {0x360f, 0x99}, {0x390b, 0x11}, {0x390c, 0x21}, {0x390d, 0x32}, {0x390e, 0x76}, - {0x3911, 0x90}, - {0x3913, 0x90}, - {0x3b3f, 0x9d}, - {0x3b45, 0x9d}, - {0x3b1b, 0xc9}, - {0x3b21, 0xc9}, {0x3a1a, 0x1c}, - {0x3a23, 0x15}, {0x3a26, 0x17}, {0x3a2c, 0x50}, - {0x3a2f, 0x18}, {0x3a32, 0x4f}, {0x3ace, 0x01}, {0x3ad2, 0x01}, @@ -1533,31 +749,13 @@ static const struct ov08x40_reg mode_1928x1208_regs[] = { {0x3afe, 0x01}, {0x3b02, 0x01}, {0x3b06, 0x01}, - {0x3b0a, 0x01}, - {0x3b0b, 0x00}, - {0x3b0e, 0x01}, - {0x3b0f, 0x00}, - {0x392c, 0x02}, {0x392d, 0x01}, - {0x392e, 0x04}, - {0x392f, 0x03}, {0x3930, 0x09}, - {0x3931, 0x07}, - {0x3932, 0x10}, {0x3933, 0x0d}, - {0x3609, 0x08}, - {0x3921, 0x0f}, - {0x3928, 0x15}, - {0x3929, 0x2a}, {0x392a, 0x52}, {0x392b, 0xa3}, {0x340b, 0x1b}, - {0x3426, 0x10}, - {0x3407, 0x01}, - {0x3404, 0x01}, - {0x3500, 0x00}, {0x3501, 0x08}, - {0x3502, 0x10}, {0x3508, 0x04}, {0x3509, 0x00}, }; -- cgit v1.2.3-59-g8ed1b From 508a5dd28596843e15db4f7f716cf072dc560af4 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Thu, 24 Apr 2025 01:56:34 +0800 Subject: media: ov08x40: Add shared global register list This follows the previous cleanup commit where duplicate entries were removed from each mode's register list. Signed-off-by: Jason Chen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov08x40.c | 416 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 416 insertions(+) diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c index db1abf831cac..3de1af63071d 100644 --- a/drivers/media/i2c/ov08x40.c +++ b/drivers/media/i2c/ov08x40.c @@ -151,6 +151,414 @@ struct ov08x40_mode { u16 exposure_shift; }; +static const struct ov08x40_reg ov08x40_global_regs[] = { + {0x1216, 0x60}, + {0x1217, 0x5b}, + {0x1218, 0x00}, + {0x1220, 0x24}, + {0x198a, 0x00}, + {0x198b, 0x01}, + {0x198e, 0x00}, + {0x198f, 0x01}, + {0x3009, 0x04}, + {0x3015, 0x00}, + {0x3016, 0xb0}, + {0x3017, 0xf0}, + {0x3018, 0xf0}, + {0x3019, 0xd2}, + {0x301a, 0xb0}, + {0x301c, 0x81}, + {0x301d, 0x02}, + {0x301e, 0x80}, + {0x3022, 0xf0}, + {0x3025, 0x89}, + {0x3030, 0x03}, + {0x3044, 0xc2}, + {0x3050, 0x35}, + {0x3051, 0x60}, + {0x3052, 0x25}, + {0x3053, 0x00}, + {0x3054, 0x00}, + {0x3055, 0x02}, + {0x3056, 0x80}, + {0x3057, 0x80}, + {0x3058, 0x80}, + {0x3059, 0x00}, + {0x3107, 0x86}, + {0x3401, 0x80}, + {0x3402, 0x8c}, + {0x3404, 0x01}, + {0x3407, 0x01}, + {0x341b, 0x30}, + {0x3420, 0x00}, + {0x3421, 0x00}, + {0x3422, 0x00}, + {0x3423, 0x00}, + {0x3424, 0x00}, + {0x3425, 0x00}, + {0x3426, 0x10}, + {0x3427, 0x00}, + {0x3428, 0x0f}, + {0x3429, 0x00}, + {0x342a, 0x00}, + {0x342b, 0x00}, + {0x342c, 0x00}, + {0x342d, 0x00}, + {0x342e, 0x00}, + {0x342f, 0x11}, + {0x3430, 0x11}, + {0x3431, 0x10}, + {0x3432, 0x00}, + {0x3433, 0x00}, + {0x3434, 0x00}, + {0x3435, 0x00}, + {0x3436, 0x00}, + {0x3437, 0x00}, + {0x3442, 0x02}, + {0x3443, 0x02}, + {0x3444, 0x07}, + {0x3450, 0x00}, + {0x3451, 0x00}, + {0x3452, 0x18}, + {0x3453, 0x18}, + {0x3454, 0x00}, + {0x3455, 0x80}, + {0x3456, 0x08}, + {0x3500, 0x00}, + {0x3502, 0x10}, + {0x3504, 0x4c}, + {0x3506, 0x30}, + {0x3507, 0x00}, + {0x350a, 0x01}, + {0x350b, 0x00}, + {0x350c, 0x00}, + {0x3540, 0x00}, + {0x3544, 0x4c}, + {0x3546, 0x30}, + {0x3547, 0x00}, + {0x3549, 0x00}, + {0x354a, 0x01}, + {0x354b, 0x00}, + {0x354c, 0x00}, + {0x3601, 0x40}, + {0x3602, 0x90}, + {0x3608, 0x0a}, + {0x3609, 0x08}, + {0x360f, 0x99}, + {0x3680, 0xa4}, + {0x3682, 0x80}, + {0x3688, 0x02}, + {0x368a, 0x2e}, + {0x368e, 0x71}, + {0x3696, 0xd1}, + {0x3699, 0x00}, + {0x369a, 0x00}, + {0x36a4, 0x00}, + {0x36a6, 0x00}, + {0x3711, 0x00}, + {0x3713, 0x00}, + {0x3716, 0x00}, + {0x3718, 0x07}, + {0x371a, 0x1c}, + {0x371b, 0x00}, + {0x3720, 0x08}, + {0x3725, 0x32}, + {0x3727, 0x05}, + {0x3760, 0x02}, + {0x3762, 0x02}, + {0x3763, 0x02}, + {0x3764, 0x02}, + {0x3765, 0x2c}, + {0x3766, 0x04}, + {0x3767, 0x2c}, + {0x3768, 0x02}, + {0x3769, 0x00}, + {0x376b, 0x20}, + {0x37b2, 0x01}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3804, 0x0f}, + {0x3805, 0x1f}, + {0x3806, 0x09}, + {0x380c, 0x02}, + {0x3810, 0x00}, + {0x3812, 0x00}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3822, 0x00}, + {0x3828, 0x0f}, + {0x382a, 0x80}, + {0x382e, 0x41}, + {0x3837, 0x08}, + {0x383a, 0x81}, + {0x383b, 0x81}, + {0x383c, 0x11}, + {0x383d, 0x11}, + {0x383e, 0x00}, + {0x383f, 0x38}, + {0x3840, 0x00}, + {0x3847, 0x00}, + {0x384a, 0x00}, + {0x384c, 0x02}, + {0x3856, 0x50}, + {0x3857, 0x30}, + {0x3858, 0x80}, + {0x3859, 0x40}, + {0x3860, 0x00}, + {0x3888, 0x00}, + {0x3889, 0x00}, + {0x388a, 0x00}, + {0x388b, 0x00}, + {0x388c, 0x00}, + {0x388d, 0x00}, + {0x388e, 0x00}, + {0x388f, 0x00}, + {0x3895, 0x00}, + {0x3911, 0x90}, + {0x3913, 0x90}, + {0x3921, 0x0f}, + {0x3928, 0x15}, + {0x3929, 0x2a}, + {0x392c, 0x02}, + {0x392e, 0x04}, + {0x392f, 0x03}, + {0x3931, 0x07}, + {0x3932, 0x10}, + {0x3938, 0x09}, + {0x3a1f, 0x8a}, + {0x3a22, 0x91}, + {0x3a23, 0x15}, + {0x3a25, 0x96}, + {0x3a28, 0xb4}, + {0x3a29, 0x26}, + {0x3a2b, 0xba}, + {0x3a2e, 0xbf}, + {0x3a2f, 0x18}, + {0x3a31, 0xc1}, + {0x3a74, 0x84}, + {0x3a99, 0x84}, + {0x3ab9, 0xa6}, + {0x3aba, 0xba}, + {0x3b0a, 0x01}, + {0x3b0b, 0x00}, + {0x3b0e, 0x01}, + {0x3b0f, 0x00}, + {0x3b12, 0x84}, + {0x3b14, 0xbb}, + {0x3b15, 0xbf}, + {0x3b1b, 0xc9}, + {0x3b21, 0xc9}, + {0x3b3f, 0x9d}, + {0x3b45, 0x9d}, + {0x3c84, 0x00}, + {0x3d84, 0x04}, + {0x3d85, 0x8b}, + {0x3daa, 0x80}, + {0x3dab, 0x14}, + {0x3dac, 0x80}, + {0x3dad, 0xc8}, + {0x3dae, 0x81}, + {0x3daf, 0x7b}, + {0x3f00, 0x10}, + {0x3f01, 0x11}, + {0x3f06, 0x0d}, + {0x3f07, 0x0b}, + {0x3f08, 0x0d}, + {0x3f09, 0x0b}, + {0x3f0a, 0x01}, + {0x3f0b, 0x11}, + {0x3f0c, 0x33}, + {0x4001, 0x07}, + {0x4007, 0x20}, + {0x4008, 0x00}, + {0x4009, 0x05}, + {0x400a, 0x00}, + {0x400c, 0x00}, + {0x400e, 0x14}, + {0x4010, 0xf4}, + {0x4011, 0x03}, + {0x4012, 0x55}, + {0x4015, 0x00}, + {0x4017, 0x00}, + {0x4018, 0x0f}, + {0x4019, 0x00}, + {0x401a, 0x40}, + {0x401b, 0x08}, + {0x401c, 0x00}, + {0x401d, 0x10}, + {0x401e, 0x02}, + {0x401f, 0x00}, + {0x4020, 0x04}, + {0x4021, 0x00}, + {0x4022, 0x04}, + {0x4023, 0x00}, + {0x4024, 0x04}, + {0x4025, 0x00}, + {0x4026, 0x04}, + {0x4027, 0x00}, + {0x4030, 0x00}, + {0x4031, 0x00}, + {0x4032, 0x00}, + {0x4033, 0x00}, + {0x4034, 0x00}, + {0x4035, 0x00}, + {0x4036, 0x00}, + {0x4037, 0x00}, + {0x4040, 0x00}, + {0x4041, 0x80}, + {0x4042, 0x00}, + {0x4043, 0x80}, + {0x4044, 0x00}, + {0x4045, 0x80}, + {0x4046, 0x00}, + {0x4047, 0x80}, + {0x4050, 0x06}, + {0x4051, 0xff}, + {0x4052, 0xff}, + {0x4053, 0xff}, + {0x4054, 0xff}, + {0x4055, 0xff}, + {0x4056, 0xff}, + {0x4057, 0x7f}, + {0x4058, 0x00}, + {0x4059, 0x00}, + {0x405a, 0x00}, + {0x405b, 0x00}, + {0x405c, 0x07}, + {0x405d, 0xff}, + {0x405e, 0x07}, + {0x405f, 0xff}, + {0x4060, 0x00}, + {0x4061, 0x00}, + {0x4062, 0x00}, + {0x4063, 0x00}, + {0x4064, 0x00}, + {0x4065, 0x00}, + {0x4066, 0x00}, + {0x4067, 0x00}, + {0x4068, 0x00}, + {0x4069, 0x00}, + {0x406a, 0x00}, + {0x406b, 0x00}, + {0x406c, 0x00}, + {0x406d, 0x00}, + {0x406e, 0x00}, + {0x406f, 0x00}, + {0x4070, 0x00}, + {0x4071, 0x00}, + {0x4072, 0x00}, + {0x4073, 0x00}, + {0x4074, 0x00}, + {0x4075, 0x00}, + {0x4076, 0x00}, + {0x4077, 0x00}, + {0x4078, 0x00}, + {0x4079, 0x00}, + {0x407a, 0x00}, + {0x407b, 0x00}, + {0x407c, 0x00}, + {0x407d, 0x00}, + {0x407e, 0x00}, + {0x407f, 0x00}, + {0x4080, 0x78}, + {0x4081, 0x78}, + {0x4082, 0x78}, + {0x4083, 0x78}, + {0x40e0, 0x00}, + {0x40e1, 0x00}, + {0x40e2, 0x00}, + {0x40e3, 0x00}, + {0x40e4, 0x00}, + {0x40e5, 0x00}, + {0x40e6, 0x00}, + {0x40e7, 0x00}, + {0x40e8, 0x00}, + {0x40e9, 0x80}, + {0x40ea, 0x00}, + {0x40eb, 0x80}, + {0x40ec, 0x00}, + {0x40ed, 0x80}, + {0x40ee, 0x00}, + {0x40ef, 0x80}, + {0x40f0, 0x02}, + {0x40f1, 0x04}, + {0x4300, 0x00}, + {0x4301, 0x00}, + {0x4302, 0x00}, + {0x4303, 0x00}, + {0x4304, 0x00}, + {0x4305, 0x00}, + {0x4306, 0x00}, + {0x4307, 0x00}, + {0x4308, 0x00}, + {0x4309, 0x00}, + {0x430a, 0x00}, + {0x430b, 0xff}, + {0x430c, 0xff}, + {0x430d, 0x00}, + {0x430e, 0x00}, + {0x4315, 0x00}, + {0x4316, 0x00}, + {0x4317, 0x00}, + {0x4318, 0x00}, + {0x4319, 0x00}, + {0x431a, 0x00}, + {0x431b, 0x00}, + {0x431c, 0x00}, + {0x4500, 0x07}, + {0x4502, 0x00}, + {0x4503, 0x0f}, + {0x4504, 0x80}, + {0x4506, 0x01}, + {0x4509, 0x05}, + {0x450c, 0x00}, + {0x450d, 0x20}, + {0x450e, 0x00}, + {0x450f, 0x00}, + {0x4510, 0x00}, + {0x4523, 0x00}, + {0x4526, 0x00}, + {0x4543, 0x00}, + {0x4544, 0x00}, + {0x4545, 0x00}, + {0x4546, 0x00}, + {0x4547, 0x10}, + {0x4602, 0x00}, + {0x4603, 0x15}, + {0x460b, 0x07}, + {0x4680, 0x11}, + {0x4686, 0x00}, + {0x4687, 0x00}, + {0x4700, 0x00}, + {0x4800, 0x64}, + {0x4806, 0x40}, + {0x480b, 0x10}, + {0x480c, 0x80}, + {0x480f, 0x32}, + {0x4813, 0xe4}, + {0x4884, 0x04}, + {0x4c00, 0xf8}, + {0x4c01, 0x44}, + {0x4c03, 0x00}, + {0x4d00, 0x00}, + {0x4d01, 0x16}, + {0x4d04, 0x10}, + {0x4d05, 0x00}, + {0x4d06, 0x0c}, + {0x4d07, 0x00}, + {0x5008, 0xb0}, + {0x50c1, 0x00}, + {0x53c1, 0x00}, + {0x5f40, 0x00}, + {0x5f41, 0x40}, +}; + +static const struct ov08x40_reg_list ov08x40_global_setting = { + .num_of_regs = ARRAY_SIZE(ov08x40_global_regs), + .regs = ov08x40_global_regs, +}; + static const struct ov08x40_reg mipi_data_rate_800mbps[] = { {0x0103, 0x01}, {0x1000, 0x00}, @@ -1498,6 +1906,14 @@ static int ov08x40_start_streaming(struct ov08x40 *ov08x) return ret; } + reg_list = &ov08x40_global_setting; + ret = ov08x40_write_reg_list(ov08x, reg_list); + if (ret) { + dev_err(&client->dev, "%s failed to set global setting\n", + __func__); + return ret; + } + /* Apply default values of current mode */ reg_list = &ov08x->cur_mode->reg_list; ret = ov08x40_write_reg_list(ov08x, reg_list); -- cgit v1.2.3-59-g8ed1b From 55eac5a4becc374c3b47376a852a20d77f4eb2d2 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Thu, 24 Apr 2025 01:56:35 +0800 Subject: media: ov08x40: Use v4l2_link_freq_to_bitmap helper Use the v4l2_link_freq_to_bitmap() helper to figure out which driver-supported link frequencies can be used on a given system. Signed-off-by: Jason Chen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil [hverkuil: drop unused variable 'max'] --- drivers/media/i2c/ov08x40.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c index 3de1af63071d..7d483a546594 100644 --- a/drivers/media/i2c/ov08x40.c +++ b/drivers/media/i2c/ov08x40.c @@ -1327,6 +1327,8 @@ struct ov08x40 { /* True if the device has been identified */ bool identified; + + unsigned long link_freq_bitmap; }; #define to_ov08x40(_sd) container_of(_sd, struct ov08x40, sd) @@ -2061,7 +2063,6 @@ static int ov08x40_init_controls(struct ov08x40 *ov08x) s64 pixel_rate_min; s64 pixel_rate_max; const struct ov08x40_mode *mode; - u32 max; int ret; ctrl_hdlr = &ov08x->ctrl_handler; @@ -2071,12 +2072,11 @@ static int ov08x40_init_controls(struct ov08x40 *ov08x) mutex_init(&ov08x->mutex); ctrl_hdlr->lock = &ov08x->mutex; - max = ARRAY_SIZE(link_freq_menu_items) - 1; ov08x->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov08x40_ctrl_ops, V4L2_CID_LINK_FREQ, - max, - 0, + __fls(ov08x->link_freq_bitmap), + __ffs(ov08x->link_freq_bitmap), link_freq_menu_items); if (ov08x->link_freq) ov08x->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; @@ -2172,7 +2172,7 @@ static int ov08x40_check_hwcfg(struct ov08x40 *ov08x, struct device *dev) }; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); - unsigned int i, j; + unsigned int i; int ret; u32 xvclk_rate; @@ -2242,21 +2242,11 @@ static int ov08x40_check_hwcfg(struct ov08x40 *ov08x, struct device *dev) ret = -EINVAL; goto out_err; } - - for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { - for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { - if (link_freq_menu_items[i] == - bus_cfg.link_frequencies[j]) - break; - } - - if (j == bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequency %lld supported\n", - link_freq_menu_items[i]); - ret = -EINVAL; - goto out_err; - } - } + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &ov08x->link_freq_bitmap); out_err: v4l2_fwnode_endpoint_free(&bus_cfg); -- cgit v1.2.3-59-g8ed1b From a21e0a8941eaeb253fd64c516c4ea39bdf4db1c6 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Thu, 24 Apr 2025 01:56:36 +0800 Subject: media: ov08x40: Select mode based on mipi lane count Use the v4l2_find_nearest_size_conditional() helper to figure out which drive-supported lane can be used on a given system. Also avoid exposing duplicate frame sizes to userspace when multiple modes share the same resolution but differ in lane count. Signed-off-by: Jason Chen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov08x40.c | 55 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c index 7d483a546594..e0094305ca2a 100644 --- a/drivers/media/i2c/ov08x40.c +++ b/drivers/media/i2c/ov08x40.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -1325,6 +1326,9 @@ struct ov08x40 { /* Mutex for serialized access */ struct mutex mutex; + /* data lanes */ + u8 mipi_lanes; + /* True if the device has been identified */ bool identified; @@ -1744,6 +1748,15 @@ static int ov08x40_set_ctrl(struct v4l2_ctrl *ctrl) return ret; } +static bool filter_by_mipi_lanes(const void *array, size_t index, + const void *context) +{ + const struct ov08x40_mode *mode = array; + const struct ov08x40 *ov08x = context; + + return mode->lanes == ov08x->mipi_lanes; +} + static const struct v4l2_ctrl_ops ov08x40_ctrl_ops = { .s_ctrl = ov08x40_set_ctrl, }; @@ -1765,18 +1778,28 @@ static int ov08x40_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->index >= ARRAY_SIZE(supported_modes)) - return -EINVAL; + struct ov08x40 *ov08x = to_ov08x40(sd); + size_t i, count = 0; if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) return -EINVAL; - fse->min_width = supported_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; - fse->max_height = fse->min_height; + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + if (!filter_by_mipi_lanes(&supported_modes[i], i, ov08x)) + continue; - return 0; + if (count == fse->index) { + fse->min_width = supported_modes[i].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[i].height; + fse->max_height = fse->min_height; + return 0; + } + + count++; + } + + return -EINVAL; } static void ov08x40_update_pad_format(const struct ov08x40_mode *mode, @@ -1839,10 +1862,13 @@ ov08x40_set_pad_format(struct v4l2_subdev *sd, if (fmt->format.code != MEDIA_BUS_FMT_SGRBG10_1X10) fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; - mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), - width, height, - fmt->format.width, fmt->format.height); + mode = v4l2_find_nearest_size_conditional(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, + fmt->format.height, + filter_by_mipi_lanes, + ov08x); ov08x40_update_pad_format(mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); @@ -2230,7 +2256,12 @@ static int ov08x40_check_hwcfg(struct ov08x40 *ov08x, struct device *dev) goto out_err; } - if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV08X40_DATA_LANES) { + switch (bus_cfg.bus.mipi_csi2.num_data_lanes) { + case 2: + case 4: + ov08x->mipi_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + break; + default: dev_err(dev, "number of CSI2 data lanes %d is not supported\n", bus_cfg.bus.mipi_csi2.num_data_lanes); ret = -EINVAL; -- cgit v1.2.3-59-g8ed1b From 3a9619be0dba738397b888a7674049321184a2f9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 30 Apr 2025 11:27:30 +0300 Subject: media: i2c: imx334: uninitialized variable in imx334_update_exp_gain() The "ret" variable is not initialized on the success path. Set it to zero. Fixes: 7b19b0fc8ac8 ("media: i2c: imx334: Convert to CCI register access helpers") Cc: stable@vger.kernel.org Signed-off-by: Dan Carpenter Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx334.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index fc875072f859..846b9928d4e8 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -536,7 +536,8 @@ static int imx334_update_controls(struct imx334 *imx334, static int imx334_update_exp_gain(struct imx334 *imx334, u32 exposure, u32 gain) { u32 lpfr, shutter; - int ret, ret_hold; + int ret_hold; + int ret = 0; lpfr = imx334->vblank + imx334->cur_mode->height; shutter = lpfr - exposure; -- cgit v1.2.3-59-g8ed1b From d97dfb6c3fb1bc9749baf9a68d90728d33b305f2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 30 Apr 2025 11:27:36 +0300 Subject: media: i2c: ds90ub960: Fix uninitialized variable in ub960_serializer_temp_ramp() The "ret" variable is not initialized on the success path. Fixes: a05744749600 ("media: i2c: ds90ub9xx: Set serializer temperature ramp") Cc: stable@vger.kernel.org Signed-off-by: Dan Carpenter Reviewed-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub960.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 1877eb735cc7..665efd661882 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -2051,7 +2051,7 @@ static int ub960_serializer_temp_ramp(struct ub960_rxport *rxport) u8 temp_dynamic_cfg; u8 nport = rxport->nport; u8 ser_temp_code; - int ret; + int ret = 0; /* Configure temp ramp only on UB953 */ if (!fwnode_device_is_compatible(rxport->ser.fwnode, "ti,ds90ub953-q1")) -- cgit v1.2.3-59-g8ed1b From 00cd2d3a1b368685b1302bd45c4783d4c5b907b9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 30 Apr 2025 11:27:42 +0300 Subject: media: i2c: ds90ub960: Fix uninitialized variable in ub960_rxport_bc_ser_config() The "ret" variable is not initialized on success. Set it to zero. Fixes: e2a3b695bc5f ("media: i2c: ds90ub960: Configure serializer using back-channel") Cc: stable@vger.kernel.org Signed-off-by: Dan Carpenter Reviewed-by: Jai Luthra Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ds90ub960.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 665efd661882..ed9ace1a5476 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -2126,7 +2126,7 @@ static int ub960_rxport_bc_ser_config(struct ub960_rxport *rxport) struct ub960_data *priv = rxport->priv; struct device *dev = &priv->client->dev; u8 nport = rxport->nport; - int ret; + int ret = 0; /* Skip port if serializer's address is not known */ if (rxport->ser.addr < 0) { -- cgit v1.2.3-59-g8ed1b From b240df2913d396638033b86af0f0ff76aa1aafc8 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Wed, 30 Apr 2025 08:36:49 +0100 Subject: media: i2c: imx335: Fix frame size enumeration In commit cfa49ff0558a ("media: i2c: imx335: Support 2592x1940 10-bit mode") the IMX335 driver was extended to support multiple output bitdepth modes. This incorrectly extended the frame size enumeration to check against the supported mbus_codes array instead of the supported mode/frame array. This has the unwanted side effect of reporting the currently supported frame size 2592x1944 three times. Fix the check accordingly to report a frame size for each supported size, which is presently only a single entry. Fixes: cfa49ff0558a ("media: i2c: imx335: Support 2592x1940 10-bit mode") Cc: stable@vger.kernel.org Signed-off-by: Kieran Bingham Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx335.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index d400a019f6b3..9b4db4cd4929 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -660,7 +660,8 @@ static int imx335_enum_frame_size(struct v4l2_subdev *sd, struct imx335 *imx335 = to_imx335(sd); u32 code; - if (fsize->index > ARRAY_SIZE(imx335_mbus_codes)) + /* Only a single supported_mode available. */ + if (fsize->index > 0) return -EINVAL; code = imx335_get_format_code(imx335, fsize->code); -- cgit v1.2.3-59-g8ed1b From c139c1ac06bea6e325bb8f28207d170076fa12ab Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 2 May 2025 17:44:42 +0200 Subject: media: intel/ipu6: Remove pin_ready function pointer We can call ipu6_isys_queue_buf_ready() directly. The only current usage is pin_ready pointer is to check if pin was prepared before IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY interrupt, we can use queue pointer for that purpose. Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys-queue.c | 2 +- drivers/media/pci/intel/ipu6/ipu6-isys-video.c | 3 +-- drivers/media/pci/intel/ipu6/ipu6-isys-video.h | 8 +------- drivers/media/pci/intel/ipu6/ipu6-isys.c | 7 +++---- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c index a9127b1c4d45..aa2cf7287477 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c @@ -738,7 +738,7 @@ static void ipu6_stream_buf_ready(struct ipu6_isys_stream *stream, u8 pin_id, u32 pin_addr, u64 time, bool error_check) { - struct ipu6_isys_queue *aq = stream->output_pins[pin_id].aq; + struct ipu6_isys_queue *aq = stream->output_pins_queue[pin_id]; struct ipu6_isys *isys = stream->isys; struct device *dev = &isys->adev->auxdev.dev; struct ipu6_isys_buffer *ib; diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c index 959869a88556..33ff938caa70 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c @@ -486,8 +486,7 @@ static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av, output_pins = cfg->nof_output_pins++; aq->fw_output = output_pins; - stream->output_pins[output_pins].pin_ready = ipu6_isys_queue_buf_ready; - stream->output_pins[output_pins].aq = aq; + stream->output_pins_queue[output_pins] = aq; output_pin = &cfg->output_pins[output_pins]; output_pin->input_pin_id = input_pins; diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h index 1d945be2b879..1dd36f2a077e 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h @@ -37,12 +37,6 @@ struct sequence_info { u64 timestamp; }; -struct output_pin_data { - void (*pin_ready)(struct ipu6_isys_stream *stream, - struct ipu6_fw_isys_resp_info_abi *info); - struct ipu6_isys_queue *aq; -}; - /* * Align with firmware stream. Each stream represents a CSI virtual channel. * May map to multiple video devices @@ -68,7 +62,7 @@ struct ipu6_isys_stream { struct completion stream_stop_completion; struct ipu6_isys *isys; - struct output_pin_data output_pins[IPU6_ISYS_OUTPUT_PINS]; + struct ipu6_isys_queue *output_pins_queue[IPU6_ISYS_OUTPUT_PINS]; int error; u8 vc; }; diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c index 8df1d83a74b5..7b09782ab679 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c @@ -1294,12 +1294,11 @@ static int isys_isr_one(struct ipu6_bus_device *adev) */ ipu6_put_fw_msg_buf(ipu6_bus_get_drvdata(adev), resp->buf_id); if (resp->pin_id < IPU6_ISYS_OUTPUT_PINS && - stream->output_pins[resp->pin_id].pin_ready) - stream->output_pins[resp->pin_id].pin_ready(stream, - resp); + stream->output_pins_queue[resp->pin_id]) + ipu6_isys_queue_buf_ready(stream, resp); else dev_warn(&adev->auxdev.dev, - "%d:No data pin ready handler for pin id %d\n", + "%d:No queue for pin id %d\n", resp->stream_handle, resp->pin_id); if (csi2) ipu6_isys_csi2_error(csi2); -- cgit v1.2.3-59-g8ed1b From c12bbc2820f6190d79c7e4cd695ce68df610f966 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 2 May 2025 17:44:43 +0200 Subject: media: intel/ipu6: Remove line_align isys->line_align value is only used in one place and we can just use the proper value directly there. Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys-video.c | 2 +- drivers/media/pci/intel/ipu6/ipu6-isys.c | 1 - drivers/media/pci/intel/ipu6/ipu6-isys.h | 4 ---- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c index 33ff938caa70..24a2ef93474c 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c @@ -241,7 +241,7 @@ static void ipu6_isys_try_fmt_cap(struct ipu6_isys_video *av, u32 type, else *bytesperline = DIV_ROUND_UP(*width * pfmt->bpp, BITS_PER_BYTE); - *bytesperline = ALIGN(*bytesperline, av->isys->line_align); + *bytesperline = ALIGN(*bytesperline, 64); /* * (height + 1) * bytesperline due to a hardware issue: the DMA unit diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c index 7b09782ab679..fc0ec0a4b8f5 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c @@ -1089,7 +1089,6 @@ static int isys_probe(struct auxiliary_device *auxdev, INIT_LIST_HEAD(&isys->framebuflist); INIT_LIST_HEAD(&isys->framebuflist_fw); - isys->line_align = IPU6_ISYS_2600_MEM_LINE_ALIGN; isys->icache_prefetch = 0; dev_set_drvdata(&auxdev->dev, isys); diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.h b/drivers/media/pci/intel/ipu6/ipu6-isys.h index 610b60e69152..f488e782c26e 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.h @@ -29,8 +29,6 @@ struct ipu6_bus_device; IPU6_ISYS_UNISPART_IRQ_CSI0 | \ IPU6_ISYS_UNISPART_IRQ_CSI1) -#define IPU6_ISYS_2600_MEM_LINE_ALIGN 64 - /* * Current message queue configuration. These must be big enough * so that they never gets full. Queues are located in system memory @@ -118,7 +116,6 @@ struct sensor_async_sd { * @streams: streams per firmware stream ID * @fwcom: fw communication layer private pointer * or optional external library private pointer - * @line_align: line alignment in memory * @phy_termcal_val: the termination calibration value, only used for DWC PHY * @need_reset: Isys requires d0i0->i3 transition * @ref_count: total number of callers fw open @@ -140,7 +137,6 @@ struct ipu6_isys { struct ipu6_isys_stream streams[IPU6_ISYS_MAX_STREAMS]; int streams_ref_count[IPU6_ISYS_MAX_STREAMS]; void *fwcom; - unsigned int line_align; u32 phy_termcal_val; bool need_reset; bool icache_prefetch; -- cgit v1.2.3-59-g8ed1b From 034237ef15511bca8bc3c85da61bc0b204a3c975 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 2 May 2025 17:44:44 +0200 Subject: media: intel/ipu6: Change deprecated lock comment pre_streamon_queued is no longer used. The lock now is protecting active and incoming lists. Signed-off-by: Stanislaw Gruszka Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys-queue.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h index b865428a0fce..844dfda15ab6 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h @@ -20,10 +20,7 @@ struct ipu6_isys_stream; struct ipu6_isys_queue { struct vb2_queue vbq; struct list_head node; - /* - * @lock: serialise access to queued and pre_streamon_queued - */ - spinlock_t lock; + spinlock_t lock; /* Protects active and incoming lists */ struct list_head active; struct list_head incoming; unsigned int fw_output; -- cgit v1.2.3-59-g8ed1b From 108955cd245e80b80b3daaef7fa3621663bc26b8 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Wed, 30 Apr 2025 14:53:22 +0200 Subject: media: dt-bindings: sony,imx290: Update usage example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 98e0500eadb7 ("media: i2c: imx290: Add configurable link frequency and pixel rate") the driver expects two specific link-frequency settings 2-lane (445500000, 297000000) and 4-lane (222750000, 148500000) operation. The driver fails to probe without these exact settings. Update the example in the bindings to match this to make it easier for users to incorporate this sensor in their device tree descriptions without having to read the driver sources when the driver fails to probe. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml index fa69bd21c8da..990acf89af8f 100644 --- a/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml @@ -136,7 +136,7 @@ examples: port { imx290_ep: endpoint { data-lanes = <1 2 3 4>; - link-frequencies = /bits/ 64 <445500000>; + link-frequencies = /bits/ 64 <222750000 148500000>; remote-endpoint = <&csiphy0_ep>; }; }; -- cgit v1.2.3-59-g8ed1b From 48dbb76cef65fabaa3ac97461eda90495e954ecd Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 16 Apr 2025 11:47:27 -0400 Subject: dt-bindings: media: convert imx.txt to yaml format Convert binding doc imx.txt to yaml format. Create two yaml files: fsl,imx6-mipi-csi2.yaml and fsl,imx-capture-subsystem.yaml. Additional changes: - add example for fsl,imx6-mipi-csi2 - add irq err1 and err2 description - update MAINTAINERS Signed-off-by: Frank Li Reviewed-by: Rob Herring (Arm) Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil [hverkuil: drop empty line at the end of the yaml files] --- .../bindings/media/fsl,imx-capture-subsystem.yaml | 37 ++++++ .../bindings/media/fsl,imx6-mipi-csi2.yaml | 143 +++++++++++++++++++++ Documentation/devicetree/bindings/media/imx.txt | 53 -------- MAINTAINERS | 2 +- 4 files changed, 181 insertions(+), 54 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/fsl,imx-capture-subsystem.yaml create mode 100644 Documentation/devicetree/bindings/media/fsl,imx6-mipi-csi2.yaml delete mode 100644 Documentation/devicetree/bindings/media/imx.txt diff --git a/Documentation/devicetree/bindings/media/fsl,imx-capture-subsystem.yaml b/Documentation/devicetree/bindings/media/fsl,imx-capture-subsystem.yaml new file mode 100644 index 000000000000..25e65a344a0a --- /dev/null +++ b/Documentation/devicetree/bindings/media/fsl,imx-capture-subsystem.yaml @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/fsl,imx-capture-subsystem.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX Media Video Device + +description: + This is the media controller node for video capture support. It is a + virtual device that lists the camera serial interface nodes that the + media device will control + +maintainers: + - Frank Li + +properties: + compatible: + const: fsl,imx-capture-subsystem + + ports: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + Should contain a list of phandles pointing to camera + sensor interface ports of IPU devices. + +required: + - compatible + +additionalProperties: false + +examples: + - | + capture-subsystem { + compatible = "fsl,imx-capture-subsystem"; + ports = <&ipu1_csi0>, <&ipu1_csi1>; + }; diff --git a/Documentation/devicetree/bindings/media/fsl,imx6-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/fsl,imx6-mipi-csi2.yaml new file mode 100644 index 000000000000..65255f576f26 --- /dev/null +++ b/Documentation/devicetree/bindings/media/fsl,imx6-mipi-csi2.yaml @@ -0,0 +1,143 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/fsl,imx6-mipi-csi2.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MIPI CSI-2 Receiver core in the i.MX SoC + +description: + This is the device node for the MIPI CSI-2 Receiver core in the i.MX + SoC. This is a Synopsys Designware MIPI CSI-2 host controller core + combined with a D-PHY core mixed into the same register block. In + addition this device consists of an i.MX-specific "CSI2IPU gasket" + glue logic, also controlled from the same register block. The CSI2IPU + gasket demultiplexes the four virtual channel streams from the host + controller's 32-bit output image bus onto four 16-bit parallel busses + to the i.MX IPU CSIs. + +maintainers: + - Frank Li + +properties: + compatible: + const: fsl,imx6-mipi-csi2 + + reg: + maxItems: 1 + + clocks: + items: + - description: hsi_tx (the D-PHY clock) + - description: video_27m (D-PHY PLL reference clock) + - description: eim_podf; + + clock-names: + items: + - const: dphy + - const: ref + - const: pix + + interrupts: + items: + - description: CSI-2 ERR1 irq + - description: CSI-2 ERR2 irq + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Input port node, single endpoint describing the CSI-2 transmitter. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + clock-lanes: + const: 0 + + data-lanes: + minItems: 1 + items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + required: + - data-lanes + +patternProperties: + '^port@[1-4]$': + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + ports 1 through 4 are output ports connecting with parallel bus sink + endpoint nodes and correspond to the four MIPI CSI-2 virtual channel + outputs. + + properties: + endpoint@0: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + endpoint@1: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + + mipi@21dc000 { + compatible = "fsl,imx6-mipi-csi2"; + reg = <0x021dc000 0x4000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clks IMX6QDL_CLK_HSI_TX>, + <&clks IMX6QDL_CLK_VIDEO_27M>, + <&clks IMX6QDL_CLK_EIM_PODF>; + clock-names = "dphy", "ref", "pix"; + + port@0 { + reg = <0>; + + endpoint { + remote-endpoint = <&ov5640_to_mipi_csi2>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + endpoint@0 { + reg = <0>; + remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc0>; + }; + + endpoint@1 { + reg = <1>; + remote-endpoint = <&ipu1_csi1_mux_from_mipi_vc0>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/imx.txt b/Documentation/devicetree/bindings/media/imx.txt deleted file mode 100644 index 77f4b0a7fd2b..000000000000 --- a/Documentation/devicetree/bindings/media/imx.txt +++ /dev/null @@ -1,53 +0,0 @@ -Freescale i.MX Media Video Device -================================= - -Video Media Controller node ---------------------------- - -This is the media controller node for video capture support. It is a -virtual device that lists the camera serial interface nodes that the -media device will control. - -Required properties: -- compatible : "fsl,imx-capture-subsystem"; -- ports : Should contain a list of phandles pointing to camera - sensor interface ports of IPU devices - -example: - -capture-subsystem { - compatible = "fsl,imx-capture-subsystem"; - ports = <&ipu1_csi0>, <&ipu1_csi1>; -}; - - -mipi_csi2 node --------------- - -This is the device node for the MIPI CSI-2 Receiver core in the i.MX -SoC. This is a Synopsys Designware MIPI CSI-2 host controller core -combined with a D-PHY core mixed into the same register block. In -addition this device consists of an i.MX-specific "CSI2IPU gasket" -glue logic, also controlled from the same register block. The CSI2IPU -gasket demultiplexes the four virtual channel streams from the host -controller's 32-bit output image bus onto four 16-bit parallel busses -to the i.MX IPU CSIs. - -Required properties: -- compatible : "fsl,imx6-mipi-csi2"; -- reg : physical base address and length of the register set; -- clocks : the MIPI CSI-2 receiver requires three clocks: hsi_tx - (the D-PHY clock), video_27m (D-PHY PLL reference - clock), and eim_podf; -- clock-names : must contain "dphy", "ref", "pix"; -- port@* : five port nodes must exist, containing endpoints - connecting to the source and sink devices according to - of_graph bindings. The first port is an input port, - connecting with a MIPI CSI-2 source, and ports 1 - through 4 are output ports connecting with parallel - bus sink endpoint nodes and correspond to the four - MIPI CSI-2 virtual channel outputs. - -Optional properties: -- interrupts : must contain two level-triggered interrupts, - in order: 100 and 101; diff --git a/MAINTAINERS b/MAINTAINERS index 41471386dacd..44800b07f67e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14776,7 +14776,7 @@ L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git F: Documentation/admin-guide/media/imx.rst -F: Documentation/devicetree/bindings/media/imx.txt +F: Documentation/devicetree/bindings/media/fsl,imx6-mipi-csi2.yaml F: drivers/staging/media/imx/ F: include/linux/imx-media.h F: include/media/imx.h -- cgit v1.2.3-59-g8ed1b From 2a934fdb01db6458288fc9386d3d8ceba6dd551a Mon Sep 17 00:00:00 2001 From: Ma Ke Date: Wed, 19 Mar 2025 16:02:48 +0800 Subject: media: v4l2-dev: fix error handling in __video_register_device() Once device_register() failed, we should call put_device() to decrement reference count for cleanup. Or it could cause memory leak. And move callback function v4l2_device_release() and v4l2_device_get() before put_device(). As comment of device_register() says, 'NOTE: _Never_ directly free @dev after calling this function, even if it returned an error! Always use put_device() to give up the reference initialized in this function instead.' Found by code review. Cc: stable@vger.kernel.org Fixes: dc93a70cc7f9 ("V4L/DVB (9973): v4l2-dev: use the release callback from device instead of cdev") Signed-off-by: Ma Ke Reviewed-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-dev.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index b40c08ce909d..c369235113d9 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -1054,25 +1054,25 @@ int __video_register_device(struct video_device *vdev, vdev->dev.class = &video_class; vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); vdev->dev.parent = vdev->dev_parent; + vdev->dev.release = v4l2_device_release; dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num); + + /* Increase v4l2_device refcount */ + v4l2_device_get(vdev->v4l2_dev); + mutex_lock(&videodev_lock); ret = device_register(&vdev->dev); if (ret < 0) { mutex_unlock(&videodev_lock); pr_err("%s: device_register failed\n", __func__); - goto cleanup; + put_device(&vdev->dev); + return ret; } - /* Register the release callback that will be called when the last - reference to the device goes away. */ - vdev->dev.release = v4l2_device_release; if (nr != -1 && nr != vdev->num && warn_if_nr_in_use) pr_warn("%s: requested %s%d, got %s\n", __func__, name_base, nr, video_device_node_name(vdev)); - /* Increase v4l2_device refcount */ - v4l2_device_get(vdev->v4l2_dev); - /* Part 5: Register the entity. */ ret = video_register_media_controller(vdev); -- cgit v1.2.3-59-g8ed1b From fdc33c5f89832066d8086745e8e2fba9c81b8eb9 Mon Sep 17 00:00:00 2001 From: Chris Green Date: Tue, 8 Apr 2025 21:31:25 +0100 Subject: media: v4l: subdev: Fix coverity issue: Logically dead code The conditional (type == V4L2_TUNER_RADIO) always evaluates true due to the earlier check for (type != V4L2_TUNER_RADIO) (line 2826) CID: 1226742 Signed-off-by: Chris Green Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-ioctl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index e97881f74c0d..78f217768ae7 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2835,8 +2835,7 @@ static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops, p->capability = m.capability | V4L2_TUNER_CAP_FREQ_BANDS; p->rangelow = m.rangelow; p->rangehigh = m.rangehigh; - p->modulation = (type == V4L2_TUNER_RADIO) ? - V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB; + p->modulation = V4L2_BAND_MODULATION_FM; return 0; } return -ENOTTY; -- cgit v1.2.3-59-g8ed1b From 0400bee67f49753b878c2576c02c1bc454f091ed Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Thu, 10 Apr 2025 16:43:46 -0400 Subject: media: synopsys: hdmirx: Renamed frame_idx to sequence This variable is used to fill the v4l2_buffer.sequence, let's name it the exact same way to reduce confusion. No functional changes. Fixes: 7b59b132ad439 ("media: platform: synopsys: Add support for HDMI input driver") Signed-off-by: Nicolas Dufresne Reviewed-by: Dmitry Osipenko Signed-off-by: Hans Verkuil --- drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c index 3d2913de9a86..f5b3f5010ede 100644 --- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c @@ -114,7 +114,7 @@ struct hdmirx_stream { spinlock_t vbq_lock; /* to lock video buffer queue */ bool stopping; wait_queue_head_t wq_stopped; - u32 frame_idx; + u32 sequence; u32 line_flag_int_cnt; u32 irq_stat; }; @@ -1540,7 +1540,7 @@ static int hdmirx_start_streaming(struct vb2_queue *queue, unsigned int count) int line_flag; mutex_lock(&hdmirx_dev->stream_lock); - stream->frame_idx = 0; + stream->sequence = 0; stream->line_flag_int_cnt = 0; stream->curr_buf = NULL; stream->next_buf = NULL; @@ -1948,7 +1948,7 @@ static void dma_idle_int_handler(struct snps_hdmirx_dev *hdmirx_dev, if (vb_done) { vb_done->vb2_buf.timestamp = ktime_get_ns(); - vb_done->sequence = stream->frame_idx; + vb_done->sequence = stream->sequence; if (bt->interlaced) vb_done->field = V4L2_FIELD_INTERLACED_TB; @@ -1956,8 +1956,8 @@ static void dma_idle_int_handler(struct snps_hdmirx_dev *hdmirx_dev, vb_done->field = V4L2_FIELD_NONE; hdmirx_vb_done(stream, vb_done); - stream->frame_idx++; - if (stream->frame_idx == 30) + stream->sequence++; + if (stream->sequence == 30) v4l2_dbg(1, debug, v4l2_dev, "rcv frames\n"); } -- cgit v1.2.3-59-g8ed1b From 57c8d79adf05244b171964d1d6c7e6fabbe5f5fd Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Thu, 10 Apr 2025 16:43:47 -0400 Subject: media: synopsys: hdmirx: Count dropped frames The sequence number communicate the lost frames to userspace. For this reason, it must be incremented even when a frame is skipped. This allows userspace such as GStreamer to report the loss. Fixes: 7b59b132ad439 ("media: platform: synopsys: Add support for HDMI input driver") Signed-off-by: Nicolas Dufresne Reviewed-by: Dmitry Osipenko Signed-off-by: Hans Verkuil --- drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c index f5b3f5010ede..7af6765532e3 100644 --- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c @@ -1956,10 +1956,6 @@ static void dma_idle_int_handler(struct snps_hdmirx_dev *hdmirx_dev, vb_done->field = V4L2_FIELD_NONE; hdmirx_vb_done(stream, vb_done); - stream->sequence++; - if (stream->sequence == 30) - v4l2_dbg(1, debug, v4l2_dev, - "rcv frames\n"); } stream->curr_buf = NULL; @@ -1971,6 +1967,10 @@ static void dma_idle_int_handler(struct snps_hdmirx_dev *hdmirx_dev, v4l2_dbg(3, debug, v4l2_dev, "%s: next_buf NULL, skip vb_done\n", __func__); } + + stream->sequence++; + if (stream->sequence == 30) + v4l2_dbg(1, debug, v4l2_dev, "rcv frames\n"); } DMA_IDLE_OUT: -- cgit v1.2.3-59-g8ed1b From a704a3c503ae1cfd9de8a2e2d16a0c9430e98162 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 7 May 2025 18:09:11 +0200 Subject: media: videobuf2: use sgtable-based scatterlist wrappers Use common wrappers operating directly on the struct sg_table objects to fix incorrect use of scatterlists sync calls. dma_sync_sg_for_*() functions have to be called with the number of elements originally passed to dma_map_sg_*() function, not the one returned in sgt->nents. Fixes: d4db5eb57cab ("media: videobuf2: add begin/end cpu_access callbacks to dma-sg") CC: stable@vger.kernel.org Signed-off-by: Marek Szyprowski Reviewed-by: Sergey Senozhatsky Acked-by: Tomasz Figa Signed-off-by: Hans Verkuil --- drivers/media/common/videobuf2/videobuf2-dma-sg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index c6ddf2357c58..b3bf2173c14e 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -469,7 +469,7 @@ vb2_dma_sg_dmabuf_ops_begin_cpu_access(struct dma_buf *dbuf, struct vb2_dma_sg_buf *buf = dbuf->priv; struct sg_table *sgt = buf->dma_sgt; - dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + dma_sync_sgtable_for_cpu(buf->dev, sgt, buf->dma_dir); return 0; } @@ -480,7 +480,7 @@ vb2_dma_sg_dmabuf_ops_end_cpu_access(struct dma_buf *dbuf, struct vb2_dma_sg_buf *buf = dbuf->priv; struct sg_table *sgt = buf->dma_sgt; - dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + dma_sync_sgtable_for_device(buf->dev, sgt, buf->dma_dir); return 0; } -- cgit v1.2.3-59-g8ed1b From 3de572fe2189a4a0bd80295e1f478401e739498e Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 7 May 2025 18:09:13 +0200 Subject: media: omap3isp: use sgtable-based scatterlist wrappers Use common wrappers operating directly on the struct sg_table objects to fix incorrect use of scatterlists sync calls. dma_sync_sg_for_*() functions have to be called with the number of elements originally passed to dma_map_sg_*() function, not the one returned in sgtable's nents. Fixes: d33186d0be18 ("[media] omap3isp: ccdc: Use the DMA API for LSC") Fixes: 0e24e90f2ca7 ("[media] omap3isp: stat: Use the DMA API") CC: stable@vger.kernel.org Signed-off-by: Marek Szyprowski Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/omap3isp/ispccdc.c | 8 ++++---- drivers/media/platform/ti/omap3isp/ispstat.c | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/ti/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c index dd375c4e180d..7d0c723dcd11 100644 --- a/drivers/media/platform/ti/omap3isp/ispccdc.c +++ b/drivers/media/platform/ti/omap3isp/ispccdc.c @@ -446,8 +446,8 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc, if (ret < 0) goto done; - dma_sync_sg_for_cpu(isp->dev, req->table.sgt.sgl, - req->table.sgt.nents, DMA_TO_DEVICE); + dma_sync_sgtable_for_cpu(isp->dev, &req->table.sgt, + DMA_TO_DEVICE); if (copy_from_user(req->table.addr, config->lsc, req->config.size)) { @@ -455,8 +455,8 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc, goto done; } - dma_sync_sg_for_device(isp->dev, req->table.sgt.sgl, - req->table.sgt.nents, DMA_TO_DEVICE); + dma_sync_sgtable_for_device(isp->dev, &req->table.sgt, + DMA_TO_DEVICE); } spin_lock_irqsave(&ccdc->lsc.req_lock, flags); diff --git a/drivers/media/platform/ti/omap3isp/ispstat.c b/drivers/media/platform/ti/omap3isp/ispstat.c index 359a846205b0..d3da68408ecb 100644 --- a/drivers/media/platform/ti/omap3isp/ispstat.c +++ b/drivers/media/platform/ti/omap3isp/ispstat.c @@ -161,8 +161,7 @@ static void isp_stat_buf_sync_for_device(struct ispstat *stat, if (ISP_STAT_USES_DMAENGINE(stat)) return; - dma_sync_sg_for_device(stat->isp->dev, buf->sgt.sgl, - buf->sgt.nents, DMA_FROM_DEVICE); + dma_sync_sgtable_for_device(stat->isp->dev, &buf->sgt, DMA_FROM_DEVICE); } static void isp_stat_buf_sync_for_cpu(struct ispstat *stat, @@ -171,8 +170,7 @@ static void isp_stat_buf_sync_for_cpu(struct ispstat *stat, if (ISP_STAT_USES_DMAENGINE(stat)) return; - dma_sync_sg_for_cpu(stat->isp->dev, buf->sgt.sgl, - buf->sgt.nents, DMA_FROM_DEVICE); + dma_sync_sgtable_for_cpu(stat->isp->dev, &buf->sgt, DMA_FROM_DEVICE); } static void isp_stat_buf_clear(struct ispstat *stat) -- cgit v1.2.3-59-g8ed1b From 5a50a258b9ee1049998def0717b1198291dbf1ca Mon Sep 17 00:00:00 2001 From: Kells Ping Date: Thu, 8 May 2025 16:19:04 +0800 Subject: media: platform: cros-ec: select ports ab for Dirks The Google Dirks device used the wrong ports, fix this. Signed-off-by: Kells Ping Signed-off-by: Hans Verkuil [hverkuil: fixed outdated subject and commit log] --- drivers/media/cec/platform/cros-ec/cros-ec-cec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 106860d11cef..419b9a7abcce 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -298,6 +298,7 @@ struct cec_dmi_match { static const char *const port_b_conns[] = { "Port B", NULL }; static const char *const port_db_conns[] = { "Port D", "Port B", NULL }; static const char *const port_ba_conns[] = { "Port B", "Port A", NULL }; +static const char *const port_ab_conns[] = { "Port A", "Port B", NULL }; static const char *const port_d_conns[] = { "Port D", NULL }; static const struct cec_dmi_match cec_dmi_match_table[] = { @@ -330,7 +331,7 @@ static const struct cec_dmi_match cec_dmi_match_table[] = { /* Google Dita */ { "Google", "Dita", "0000:00:02.0", port_db_conns }, /* Google Dirks */ - { "Google", "Dirks", "0000:00:02.0", port_db_conns }, + { "Google", "Dirks", "0000:00:02.0", port_ab_conns }, /* Google Moxie */ { "Google", "Moxie", "0000:00:02.0", port_b_conns }, }; -- cgit v1.2.3-59-g8ed1b From 9c210a24b1f6627e4fbf441314143f618f325d6e Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 9 May 2025 09:25:11 +0300 Subject: media: ov02c10: Use div_u64 to divide a 64-bit number The ov02c10 driver divides a 64-bit number but reply relies on division operator to do that. Use div_u64() so this will compile everywhere. Signed-off-by: Sakari Ailus Reviewed-by: Stanislaw Gruszka Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov02c10.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov02c10.c b/drivers/media/i2c/ov02c10.c index 9e3d4a4e12ce..089a4fd9627c 100644 --- a/drivers/media/i2c/ov02c10.c +++ b/drivers/media/i2c/ov02c10.c @@ -497,8 +497,8 @@ static int ov02c10_init_controls(struct ov02c10 *ov02c10) ov02c10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* MIPI lanes are DDR -> use link-freq * 2 */ - pixel_rate = link_freq_menu_items[ov02c10->link_freq_index] * 2 * - ov02c10->mipi_lanes / OV02C10_RGB_DEPTH; + pixel_rate = div_u64(link_freq_menu_items[ov02c10->link_freq_index] * + 2 * ov02c10->mipi_lanes, OV02C10_RGB_DEPTH); ov02c10->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops, V4L2_CID_PIXEL_RATE, 0, -- cgit v1.2.3-59-g8ed1b From 8005e2afb7028bf05935af722c38d1ccba88b525 Mon Sep 17 00:00:00 2001 From: Keke Li Date: Sun, 27 Apr 2025 14:27:09 +0800 Subject: media: dt-bindings: Add amlogic,c3-mipi-csi2.yaml c3-mipi-csi2 is used to receive mipi data from image sensor. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Keke Li Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- .../bindings/media/amlogic,c3-mipi-csi2.yaml | 127 +++++++++++++++++++++ MAINTAINERS | 6 + 2 files changed, 133 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/amlogic,c3-mipi-csi2.yaml diff --git a/Documentation/devicetree/bindings/media/amlogic,c3-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/amlogic,c3-mipi-csi2.yaml new file mode 100644 index 000000000000..b0129beab0c3 --- /dev/null +++ b/Documentation/devicetree/bindings/media/amlogic,c3-mipi-csi2.yaml @@ -0,0 +1,127 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/amlogic,c3-mipi-csi2.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic C3 MIPI CSI-2 receiver + +maintainers: + - Keke Li + +description: + MIPI CSI-2 receiver contains CSI-2 RX PHY and host controller. + It receives the MIPI data from the image sensor and sends MIPI data + to MIPI adapter. + +properties: + compatible: + enum: + - amlogic,c3-mipi-csi2 + + reg: + maxItems: 3 + + reg-names: + items: + - const: aphy + - const: dphy + - const: host + + power-domains: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: vapb + - const: phy0 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: input port node, connected to sensor. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 4 + + required: + - data-lanes + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: output port node + + required: + - port@0 + - port@1 + +required: + - compatible + - reg + - reg-names + - power-domains + - clocks + - clock-names + - ports + +additionalProperties: false + +examples: + - | + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + csi: csi@ff018000 { + compatible = "amlogic,c3-mipi-csi2"; + reg = <0x0 0xff018000 0x0 0x400>, + <0x0 0xff019000 0x0 0x300>, + <0x0 0xff01a000 0x0 0x100>; + reg-names = "aphy", "dphy", "host"; + power-domains = <&pwrc PWRC_C3_MIPI_ISP_WRAP_ID>; + clocks = <&clkc_periphs CLKID_VAPB>, + <&clkc_periphs CLKID_CSI_PHY0>; + clock-names = "vapb", "phy0"; + assigned-clocks = <&clkc_periphs CLKID_VAPB>, + <&clkc_periphs CLKID_CSI_PHY0>; + assigned-clock-rates = <0>, <200000000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + c3_mipi_csi_in: endpoint { + remote-endpoint = <&imx290_out>; + data-lanes = <1 2 3 4>; + }; + }; + + port@1 { + reg = <1>; + c3_mipi_csi_out: endpoint { + remote-endpoint = <&c3_adap_in>; + }; + }; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 44800b07f67e..68d1ebf23901 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1254,6 +1254,12 @@ F: Documentation/devicetree/bindings/perf/amlogic,g12-ddr-pmu.yaml F: drivers/perf/amlogic/ F: include/soc/amlogic/ +AMLOGIC MIPI CSI2 DRIVER +M: Keke Li +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/amlogic,c3-mipi-csi2.yaml + AMLOGIC PINCTRL DRIVER M: Xianwei Zhao L: linux-amlogic@lists.infradead.org -- cgit v1.2.3-59-g8ed1b From b63ef604a28da4165dcf2573cd508b4fb18e1c53 Mon Sep 17 00:00:00 2001 From: Keke Li Date: Sun, 27 Apr 2025 14:27:10 +0800 Subject: media: platform: Add C3 MIPI CSI-2 driver Add a driver for the CSI-2 receiver unit found on the Amlogic C3 SoC. Create a drivers/media/platform/amlogic/c3/ directory to host the driver and the forthcoming support for the Amlogic C3 MIPI adapter and C3 ISP. This driver is used to receive the MIPI data from image sensor. Reviewed-by: Daniel Scally Reviewed-by: Jacopo Mondi Signed-off-by: Keke Li Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil [hverkuil: fix typo: uinit -> unit] --- MAINTAINERS | 1 + drivers/media/platform/amlogic/Kconfig | 1 + drivers/media/platform/amlogic/Makefile | 2 + drivers/media/platform/amlogic/c3/Kconfig | 3 + drivers/media/platform/amlogic/c3/Makefile | 3 + .../media/platform/amlogic/c3/mipi-csi2/Kconfig | 16 + .../media/platform/amlogic/c3/mipi-csi2/Makefile | 3 + .../platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c | 827 +++++++++++++++++++++ 8 files changed, 856 insertions(+) create mode 100644 drivers/media/platform/amlogic/c3/Kconfig create mode 100644 drivers/media/platform/amlogic/c3/Makefile create mode 100644 drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig create mode 100644 drivers/media/platform/amlogic/c3/mipi-csi2/Makefile create mode 100644 drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c diff --git a/MAINTAINERS b/MAINTAINERS index 68d1ebf23901..9436cce85a17 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1259,6 +1259,7 @@ M: Keke Li L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/amlogic,c3-mipi-csi2.yaml +F: drivers/media/platform/amlogic/c3/mipi-csi2/ AMLOGIC PINCTRL DRIVER M: Xianwei Zhao diff --git a/drivers/media/platform/amlogic/Kconfig b/drivers/media/platform/amlogic/Kconfig index 5014957404e9..458acf3d5fa8 100644 --- a/drivers/media/platform/amlogic/Kconfig +++ b/drivers/media/platform/amlogic/Kconfig @@ -2,4 +2,5 @@ comment "Amlogic media platform drivers" +source "drivers/media/platform/amlogic/c3/Kconfig" source "drivers/media/platform/amlogic/meson-ge2d/Kconfig" diff --git a/drivers/media/platform/amlogic/Makefile b/drivers/media/platform/amlogic/Makefile index d3cdb8fa4ddb..c744afcd1b9e 100644 --- a/drivers/media/platform/amlogic/Makefile +++ b/drivers/media/platform/amlogic/Makefile @@ -1,2 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only + +obj-y += c3/ obj-y += meson-ge2d/ diff --git a/drivers/media/platform/amlogic/c3/Kconfig b/drivers/media/platform/amlogic/c3/Kconfig new file mode 100644 index 000000000000..098d458747b8 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/Kconfig @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +source "drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig" diff --git a/drivers/media/platform/amlogic/c3/Makefile b/drivers/media/platform/amlogic/c3/Makefile new file mode 100644 index 000000000000..a468fb782f94 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-y += mipi-csi2/ diff --git a/drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig b/drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig new file mode 100644 index 000000000000..0d7b2e203273 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_C3_MIPI_CSI2 + tristate "Amlogic C3 MIPI CSI-2 receiver" + depends on ARCH_MESON || COMPILE_TEST + depends on VIDEO_DEV + depends on OF + select MEDIA_CONTROLLER + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + Video4Linux2 driver for Amlogic C3 MIPI CSI-2 receiver. + C3 MIPI CSI-2 receiver is used to receive MIPI data from + image sensor. + + To compile this driver as a module choose m here. diff --git a/drivers/media/platform/amlogic/c3/mipi-csi2/Makefile b/drivers/media/platform/amlogic/c3/mipi-csi2/Makefile new file mode 100644 index 000000000000..cc08fc722bfd --- /dev/null +++ b/drivers/media/platform/amlogic/c3/mipi-csi2/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_VIDEO_C3_MIPI_CSI2) += c3-mipi-csi2.o diff --git a/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c b/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c new file mode 100644 index 000000000000..f92815ffa4ae --- /dev/null +++ b/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c @@ -0,0 +1,827 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* C3 CSI-2 submodule definition */ +enum { + SUBMD_APHY, + SUBMD_DPHY, + SUBMD_HOST, +}; + +#define CSI2_SUBMD_MASK GENMASK(17, 16) +#define CSI2_SUBMD_SHIFT 16 +#define CSI2_SUBMD(x) (((x) & (CSI2_SUBMD_MASK)) >> (CSI2_SUBMD_SHIFT)) +#define CSI2_REG_ADDR_MASK GENMASK(15, 0) +#define CSI2_REG_ADDR(x) ((x) & (CSI2_REG_ADDR_MASK)) +#define CSI2_REG_A(x) ((SUBMD_APHY << CSI2_SUBMD_SHIFT) | (x)) +#define CSI2_REG_D(x) ((SUBMD_DPHY << CSI2_SUBMD_SHIFT) | (x)) +#define CSI2_REG_H(x) ((SUBMD_HOST << CSI2_SUBMD_SHIFT) | (x)) + +#define MIPI_CSI2_CLOCK_NUM_MAX 3 +#define MIPI_CSI2_SUBDEV_NAME "c3-mipi-csi2" + +/* C3 CSI-2 APHY register */ +#define CSI_PHY_CNTL0 CSI2_REG_A(0x44) +#define CSI_PHY_CNTL0_HS_LP_BIAS_EN BIT(10) +#define CSI_PHY_CNTL0_HS_RX_TRIM_11 (11 << 11) +#define CSI_PHY_CNTL0_LP_LOW_VTH_2 (2 << 16) +#define CSI_PHY_CNTL0_LP_HIGH_VTH_4 (4 << 20) +#define CSI_PHY_CNTL0_DATA_LANE0_HS_DIG_EN BIT(24) +#define CSI_PHY_CNTL0_DATA_LANE1_HS_DIG_EN BIT(25) +#define CSI_PHY_CNTL0_CLK0_LANE_HS_DIG_EN BIT(26) +#define CSI_PHY_CNTL0_DATA_LANE2_HS_DIG_EN BIT(27) +#define CSI_PHY_CNTL0_DATA_LANE3_HS_DIG_EN BIT(28) + +#define CSI_PHY_CNTL1 CSI2_REG_A(0x48) +#define CSI_PHY_CNTL1_HS_EQ_CAP_SMALL (2 << 16) +#define CSI_PHY_CNTL1_HS_EQ_CAP_BIG (3 << 16) +#define CSI_PHY_CNTL1_HS_EQ_RES_MIN (3 << 18) +#define CSI_PHY_CNTL1_HS_EQ_RES_MED (2 << 18) +#define CSI_PHY_CNTL1_HS_EQ_RES_MAX BIT(18) +#define CSI_PHY_CNTL1_CLK_CHN_EQ_MAX_GAIN BIT(20) +#define CSI_PHY_CNTL1_DATA_CHN_EQ_MAX_GAIN BIT(21) +#define CSI_PHY_CNTL1_COM_BG_EN BIT(24) +#define CSI_PHY_CNTL1_HS_SYNC_EN BIT(25) + +/* C3 CSI-2 DPHY register */ +#define MIPI_PHY_CTRL CSI2_REG_D(0x00) +#define MIPI_PHY_CTRL_DATA_LANE0_EN (0 << 0) +#define MIPI_PHY_CTRL_DATA_LANE0_DIS BIT(0) +#define MIPI_PHY_CTRL_DATA_LANE1_EN (0 << 1) +#define MIPI_PHY_CTRL_DATA_LANE1_DIS BIT(1) +#define MIPI_PHY_CTRL_DATA_LANE2_EN (0 << 2) +#define MIPI_PHY_CTRL_DATA_LANE2_DIS BIT(2) +#define MIPI_PHY_CTRL_DATA_LANE3_EN (0 << 3) +#define MIPI_PHY_CTRL_DATA_LANE3_DIS BIT(3) +#define MIPI_PHY_CTRL_CLOCK_LANE_EN (0 << 4) +#define MIPI_PHY_CTRL_CLOCK_LANE_DIS BIT(4) + +#define MIPI_PHY_CLK_LANE_CTRL CSI2_REG_D(0x04) +#define MIPI_PHY_CLK_LANE_CTRL_FORCE_ULPS_ENTER BIT(0) +#define MIPI_PHY_CLK_LANE_CTRL_FORCE_ULPS_EXIT BIT(1) +#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS (0 << 3) +#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_2 BIT(3) +#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_4 (2 << 3) +#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_8 (3 << 3) +#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_16 (4 << 3) +#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_EN BIT(6) +#define MIPI_PHY_CLK_LANE_CTRL_LPEN_DIS BIT(7) +#define MIPI_PHY_CLK_LANE_CTRL_END_EN BIT(8) +#define MIPI_PHY_CLK_LANE_CTRL_HS_RX_EN BIT(9) + +#define MIPI_PHY_DATA_LANE_CTRL1 CSI2_REG_D(0x0c) +#define MIPI_PHY_DATA_LANE_CTRL1_INSERT_ERRESC BIT(0) +#define MIPI_PHY_DATA_LANE_CTRL1_HS_SYNC_CHK_EN BIT(1) +#define MIPI_PHY_DATA_LANE_CTRL1_PIPE_MASK GENMASK(6, 2) +#define MIPI_PHY_DATA_LANE_CTRL1_PIPE_ALL_EN (0x1f << 2) +#define MIPI_PHY_DATA_LANE_CTRL1_PIPE_DELAY_MASK GENMASK(9, 7) +#define MIPI_PHY_DATA_LANE_CTRL1_PIPE_DELAY_3 (3 << 7) + +#define MIPI_PHY_TCLK_MISS CSI2_REG_D(0x10) +#define MIPI_PHY_TCLK_MISS_CYCLES_MASK GENMASK(7, 0) +#define MIPI_PHY_TCLK_MISS_CYCLES_9 (9 << 0) + +#define MIPI_PHY_TCLK_SETTLE CSI2_REG_D(0x14) +#define MIPI_PHY_TCLK_SETTLE_CYCLES_MASK GENMASK(7, 0) +#define MIPI_PHY_TCLK_SETTLE_CYCLES_31 (31 << 0) + +#define MIPI_PHY_THS_EXIT CSI2_REG_D(0x18) +#define MIPI_PHY_THS_EXIT_CYCLES_MASK GENMASK(7, 0) +#define MIPI_PHY_THS_EXIT_CYCLES_8 (8 << 0) + +#define MIPI_PHY_THS_SKIP CSI2_REG_D(0x1c) +#define MIPI_PHY_THS_SKIP_CYCLES_MASK GENMASK(7, 0) +#define MIPI_PHY_THS_SKIP_CYCLES_10 (10 << 0) + +#define MIPI_PHY_THS_SETTLE CSI2_REG_D(0x20) +#define MIPI_PHY_THS_SETTLE_CYCLES_MASK GENMASK(7, 0) + +#define MIPI_PHY_TINIT CSI2_REG_D(0x24) +#define MIPI_PHY_TINIT_CYCLES_MASK GENMASK(31, 0) +#define MIPI_PHY_TINIT_CYCLES_20000 (20000 << 0) + +#define MIPI_PHY_TULPS_C CSI2_REG_D(0x28) +#define MIPI_PHY_TULPS_C_CYCLES_MASK GENMASK(31, 0) +#define MIPI_PHY_TULPS_C_CYCLES_4096 (4096 << 0) + +#define MIPI_PHY_TULPS_S CSI2_REG_D(0x2c) +#define MIPI_PHY_TULPS_S_CYCLES_MASK GENMASK(31, 0) +#define MIPI_PHY_TULPS_S_CYCLES_256 (256 << 0) + +#define MIPI_PHY_TMBIAS CSI2_REG_D(0x30) +#define MIPI_PHY_TMBIAS_CYCLES_MASK GENMASK(31, 0) +#define MIPI_PHY_TMBIAS_CYCLES_256 (256 << 0) + +#define MIPI_PHY_TLP_EN_W CSI2_REG_D(0x34) +#define MIPI_PHY_TLP_EN_W_CYCLES_MASK GENMASK(31, 0) +#define MIPI_PHY_TLP_EN_W_CYCLES_12 (12 << 0) + +#define MIPI_PHY_TLPOK CSI2_REG_D(0x38) +#define MIPI_PHY_TLPOK_CYCLES_MASK GENMASK(31, 0) +#define MIPI_PHY_TLPOK_CYCLES_256 (256 << 0) + +#define MIPI_PHY_TWD_INIT CSI2_REG_D(0x3c) +#define MIPI_PHY_TWD_INIT_DOG_MASK GENMASK(31, 0) +#define MIPI_PHY_TWD_INIT_DOG_0X400000 (0x400000 << 0) + +#define MIPI_PHY_TWD_HS CSI2_REG_D(0x40) +#define MIPI_PHY_TWD_HS_DOG_MASK GENMASK(31, 0) +#define MIPI_PHY_TWD_HS_DOG_0X400000 (0x400000 << 0) + +#define MIPI_PHY_MUX_CTRL0 CSI2_REG_D(0x284) +#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_MASK GENMASK(3, 0) +#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE0 (0 << 0) +#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE1 BIT(0) +#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE2 (2 << 0) +#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE3 (3 << 0) +#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_MASK GENMASK(7, 4) +#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE0 (0 << 4) +#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE1 BIT(4) +#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE2 (2 << 4) +#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE3 (3 << 4) +#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_MASK GENMASK(11, 8) +#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE0 (0 << 8) +#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE1 BIT(8) +#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE2 (2 << 8) +#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE3 (3 << 8) +#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_MASK GENMASK(14, 12) +#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE0 (0 << 12) +#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE1 BIT(12) +#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE2 (2 << 12) +#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE3 (3 << 12) + +#define MIPI_PHY_MUX_CTRL1 CSI2_REG_D(0x288) +#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_MASK GENMASK(3, 0) +#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN0 (0 << 0) +#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN1 BIT(0) +#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN2 (2 << 0) +#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN3 (3 << 0) +#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_MASK GENMASK(7, 4) +#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN0 (0 << 4) +#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN1 BIT(4) +#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN2 (2 << 4) +#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN3 (3 << 4) +#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_MASK GENMASK(11, 8) +#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN0 (0 << 8) +#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN1 BIT(8) +#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN2 (2 << 8) +#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN3 (3 << 8) +#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_MASK GENMASK(14, 12) +#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN0 (0 << 12) +#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN1 BIT(12) +#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN2 (2 << 12) +#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN3 (3 << 12) + +/* C3 CSI-2 HOST register */ +#define CSI2_HOST_N_LANES CSI2_REG_H(0x04) +#define CSI2_HOST_N_LANES_MASK GENMASK(1, 0) +#define CSI2_HOST_N_LANES_1 (0 << 0) +#define CSI2_HOST_N_LANES_2 BIT(0) +#define CSI2_HOST_N_LANES_3 (2 << 0) +#define CSI2_HOST_N_LANES_4 (3 << 0) + +#define CSI2_HOST_CSI2_RESETN CSI2_REG_H(0x10) +#define CSI2_HOST_CSI2_RESETN_MASK BIT(0) +#define CSI2_HOST_CSI2_RESETN_ACTIVE (0 << 0) +#define CSI2_HOST_CSI2_RESETN_EXIT BIT(0) + +#define C3_MIPI_CSI2_MAX_WIDTH 2888 +#define C3_MIPI_CSI2_MIN_WIDTH 160 +#define C3_MIPI_CSI2_MAX_HEIGHT 2240 +#define C3_MIPI_CSI2_MIN_HEIGHT 120 +#define C3_MIPI_CSI2_DEFAULT_WIDTH 1920 +#define C3_MIPI_CSI2_DEFAULT_HEIGHT 1080 +#define C3_MIPI_CSI2_DEFAULT_FMT MEDIA_BUS_FMT_SRGGB10_1X10 + +/* C3 CSI-2 pad list */ +enum { + C3_MIPI_CSI2_PAD_SINK, + C3_MIPI_CSI2_PAD_SRC, + C3_MIPI_CSI2_PAD_MAX +}; + +/* + * struct c3_csi_info - MIPI CSI2 information + * + * @clocks: array of MIPI CSI2 clock names + * @clock_num: actual clock number + */ +struct c3_csi_info { + char *clocks[MIPI_CSI2_CLOCK_NUM_MAX]; + u32 clock_num; +}; + +/* + * struct c3_csi_device - MIPI CSI2 platform device + * + * @dev: pointer to the struct device + * @aphy: MIPI CSI2 aphy register address + * @dphy: MIPI CSI2 dphy register address + * @host: MIPI CSI2 host register address + * @clks: array of MIPI CSI2 clocks + * @sd: MIPI CSI2 sub-device + * @pads: MIPI CSI2 sub-device pads + * @notifier: notifier to register on the v4l2-async API + * @src_pad: source sub-device pad + * @bus: MIPI CSI2 bus information + * @info: version-specific MIPI CSI2 information + */ +struct c3_csi_device { + struct device *dev; + void __iomem *aphy; + void __iomem *dphy; + void __iomem *host; + struct clk_bulk_data clks[MIPI_CSI2_CLOCK_NUM_MAX]; + + struct v4l2_subdev sd; + struct media_pad pads[C3_MIPI_CSI2_PAD_MAX]; + struct v4l2_async_notifier notifier; + struct media_pad *src_pad; + struct v4l2_mbus_config_mipi_csi2 bus; + + const struct c3_csi_info *info; +}; + +static const u32 c3_mipi_csi_formats[] = { + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, +}; + +/* Hardware configuration */ + +static void c3_mipi_csi_write(struct c3_csi_device *csi, u32 reg, u32 val) +{ + void __iomem *addr; + + switch (CSI2_SUBMD(reg)) { + case SUBMD_APHY: + addr = csi->aphy + CSI2_REG_ADDR(reg); + break; + case SUBMD_DPHY: + addr = csi->dphy + CSI2_REG_ADDR(reg); + break; + case SUBMD_HOST: + addr = csi->host + CSI2_REG_ADDR(reg); + break; + default: + dev_err(csi->dev, "Invalid sub-module: %lu\n", CSI2_SUBMD(reg)); + return; + } + + writel(val, addr); +} + +static void c3_mipi_csi_cfg_aphy(struct c3_csi_device *csi) +{ + c3_mipi_csi_write(csi, CSI_PHY_CNTL0, + CSI_PHY_CNTL0_HS_LP_BIAS_EN | + CSI_PHY_CNTL0_HS_RX_TRIM_11 | + CSI_PHY_CNTL0_LP_LOW_VTH_2 | + CSI_PHY_CNTL0_LP_HIGH_VTH_4 | + CSI_PHY_CNTL0_DATA_LANE0_HS_DIG_EN | + CSI_PHY_CNTL0_DATA_LANE1_HS_DIG_EN | + CSI_PHY_CNTL0_CLK0_LANE_HS_DIG_EN | + CSI_PHY_CNTL0_DATA_LANE2_HS_DIG_EN | + CSI_PHY_CNTL0_DATA_LANE3_HS_DIG_EN); + + c3_mipi_csi_write(csi, CSI_PHY_CNTL1, + CSI_PHY_CNTL1_HS_EQ_CAP_SMALL | + CSI_PHY_CNTL1_HS_EQ_RES_MED | + CSI_PHY_CNTL1_CLK_CHN_EQ_MAX_GAIN | + CSI_PHY_CNTL1_DATA_CHN_EQ_MAX_GAIN | + CSI_PHY_CNTL1_COM_BG_EN | + CSI_PHY_CNTL1_HS_SYNC_EN); +} + +static void c3_mipi_csi_cfg_dphy(struct c3_csi_device *csi, s64 rate) +{ + u32 val; + u32 settle; + + /* Calculate the high speed settle */ + val = DIV_ROUND_UP(1000000000, rate); + settle = (16 * val + 230) / 10; + + c3_mipi_csi_write(csi, MIPI_PHY_CLK_LANE_CTRL, + MIPI_PHY_CLK_LANE_CTRL_HS_RX_EN | + MIPI_PHY_CLK_LANE_CTRL_END_EN | + MIPI_PHY_CLK_LANE_CTRL_LPEN_DIS | + MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_EN | + MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_8); + + c3_mipi_csi_write(csi, MIPI_PHY_TCLK_MISS, MIPI_PHY_TCLK_MISS_CYCLES_9); + c3_mipi_csi_write(csi, MIPI_PHY_TCLK_SETTLE, + MIPI_PHY_TCLK_SETTLE_CYCLES_31); + c3_mipi_csi_write(csi, MIPI_PHY_THS_EXIT, MIPI_PHY_THS_EXIT_CYCLES_8); + c3_mipi_csi_write(csi, MIPI_PHY_THS_SKIP, MIPI_PHY_THS_SKIP_CYCLES_10); + c3_mipi_csi_write(csi, MIPI_PHY_THS_SETTLE, settle); + c3_mipi_csi_write(csi, MIPI_PHY_TINIT, MIPI_PHY_TINIT_CYCLES_20000); + c3_mipi_csi_write(csi, MIPI_PHY_TMBIAS, MIPI_PHY_TMBIAS_CYCLES_256); + c3_mipi_csi_write(csi, MIPI_PHY_TULPS_C, MIPI_PHY_TULPS_C_CYCLES_4096); + c3_mipi_csi_write(csi, MIPI_PHY_TULPS_S, MIPI_PHY_TULPS_S_CYCLES_256); + c3_mipi_csi_write(csi, MIPI_PHY_TLP_EN_W, MIPI_PHY_TLP_EN_W_CYCLES_12); + c3_mipi_csi_write(csi, MIPI_PHY_TLPOK, MIPI_PHY_TLPOK_CYCLES_256); + c3_mipi_csi_write(csi, MIPI_PHY_TWD_INIT, + MIPI_PHY_TWD_INIT_DOG_0X400000); + c3_mipi_csi_write(csi, MIPI_PHY_TWD_HS, MIPI_PHY_TWD_HS_DOG_0X400000); + + c3_mipi_csi_write(csi, MIPI_PHY_DATA_LANE_CTRL1, + MIPI_PHY_DATA_LANE_CTRL1_INSERT_ERRESC | + MIPI_PHY_DATA_LANE_CTRL1_HS_SYNC_CHK_EN | + MIPI_PHY_DATA_LANE_CTRL1_PIPE_ALL_EN | + MIPI_PHY_DATA_LANE_CTRL1_PIPE_DELAY_3); + + /* Set the order of lanes */ + c3_mipi_csi_write(csi, MIPI_PHY_MUX_CTRL0, + MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE3 | + MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE2 | + MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE1 | + MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE0); + + c3_mipi_csi_write(csi, MIPI_PHY_MUX_CTRL1, + MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN3 | + MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN2 | + MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN1 | + MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN0); + + /* Enable digital data and clock lanes */ + c3_mipi_csi_write(csi, MIPI_PHY_CTRL, + MIPI_PHY_CTRL_DATA_LANE0_EN | + MIPI_PHY_CTRL_DATA_LANE1_EN | + MIPI_PHY_CTRL_DATA_LANE2_EN | + MIPI_PHY_CTRL_DATA_LANE3_EN | + MIPI_PHY_CTRL_CLOCK_LANE_EN); +} + +static void c3_mipi_csi_cfg_host(struct c3_csi_device *csi) +{ + /* Reset CSI-2 controller output */ + c3_mipi_csi_write(csi, CSI2_HOST_CSI2_RESETN, + CSI2_HOST_CSI2_RESETN_ACTIVE); + c3_mipi_csi_write(csi, CSI2_HOST_CSI2_RESETN, + CSI2_HOST_CSI2_RESETN_EXIT); + + /* Set data lane number */ + c3_mipi_csi_write(csi, CSI2_HOST_N_LANES, csi->bus.num_data_lanes - 1); +} + +static int c3_mipi_csi_start_stream(struct c3_csi_device *csi, + struct v4l2_subdev *src_sd) +{ + s64 link_freq; + s64 lane_rate; + + link_freq = v4l2_get_link_freq(src_sd->ctrl_handler, 0, 0); + if (link_freq < 0) { + dev_err(csi->dev, + "Unable to obtain link frequency: %lld\n", link_freq); + return link_freq; + } + + lane_rate = link_freq * 2; + if (lane_rate > 1500000000) { + dev_err(csi->dev, "Invalid lane rate: %lld\n", lane_rate); + return -EINVAL; + } + + c3_mipi_csi_cfg_aphy(csi); + c3_mipi_csi_cfg_dphy(csi, lane_rate); + c3_mipi_csi_cfg_host(csi); + + return 0; +} + +static int c3_mipi_csi_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct c3_csi_device *csi = v4l2_get_subdevdata(sd); + struct media_pad *sink_pad; + struct v4l2_subdev *src_sd; + int ret; + + sink_pad = &csi->pads[C3_MIPI_CSI2_PAD_SINK]; + csi->src_pad = media_pad_remote_pad_unique(sink_pad); + if (IS_ERR(csi->src_pad)) { + dev_dbg(csi->dev, "Failed to get source pad for MIPI CSI-2\n"); + return -EPIPE; + } + + src_sd = media_entity_to_v4l2_subdev(csi->src_pad->entity); + + pm_runtime_resume_and_get(csi->dev); + + c3_mipi_csi_start_stream(csi, src_sd); + + ret = v4l2_subdev_enable_streams(src_sd, csi->src_pad->index, BIT(0)); + if (ret) { + pm_runtime_put(csi->dev); + return ret; + } + + return 0; +} + +static int c3_mipi_csi_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct c3_csi_device *csi = v4l2_get_subdevdata(sd); + struct v4l2_subdev *src_sd; + + if (csi->src_pad) { + src_sd = media_entity_to_v4l2_subdev(csi->src_pad->entity); + v4l2_subdev_disable_streams(src_sd, csi->src_pad->index, + BIT(0)); + } + csi->src_pad = NULL; + + pm_runtime_put(csi->dev); + + return 0; +} + +static int c3_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct v4l2_mbus_framefmt *fmt; + + switch (code->pad) { + case C3_MIPI_CSI2_PAD_SINK: + if (code->index >= ARRAY_SIZE(c3_mipi_csi_formats)) + return -EINVAL; + + code->code = c3_mipi_csi_formats[code->index]; + break; + case C3_MIPI_CSI2_PAD_SRC: + if (code->index) + return -EINVAL; + + fmt = v4l2_subdev_state_get_format(state, code->pad); + code->code = fmt->code; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int c3_mipi_csi_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt; + unsigned int i; + + if (format->pad != C3_MIPI_CSI2_PAD_SINK) + return v4l2_subdev_get_fmt(sd, state, format); + + fmt = v4l2_subdev_state_get_format(state, format->pad); + + for (i = 0; i < ARRAY_SIZE(c3_mipi_csi_formats); i++) { + if (format->format.code == c3_mipi_csi_formats[i]) { + fmt->code = c3_mipi_csi_formats[i]; + break; + } + } + + if (i == ARRAY_SIZE(c3_mipi_csi_formats)) + fmt->code = c3_mipi_csi_formats[0]; + + fmt->width = clamp_t(u32, format->format.width, + C3_MIPI_CSI2_MIN_WIDTH, C3_MIPI_CSI2_MAX_WIDTH); + fmt->height = clamp_t(u32, format->format.height, + C3_MIPI_CSI2_MIN_HEIGHT, C3_MIPI_CSI2_MAX_HEIGHT); + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + + format->format = *fmt; + + /* Synchronize the format to source pad */ + fmt = v4l2_subdev_state_get_format(state, C3_MIPI_CSI2_PAD_SRC); + *fmt = format->format; + + return 0; +} + +static int c3_mipi_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_mbus_framefmt *src_fmt; + + sink_fmt = v4l2_subdev_state_get_format(state, C3_MIPI_CSI2_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(state, C3_MIPI_CSI2_PAD_SRC); + + sink_fmt->width = C3_MIPI_CSI2_DEFAULT_WIDTH; + sink_fmt->height = C3_MIPI_CSI2_DEFAULT_HEIGHT; + sink_fmt->field = V4L2_FIELD_NONE; + sink_fmt->code = C3_MIPI_CSI2_DEFAULT_FMT; + sink_fmt->colorspace = V4L2_COLORSPACE_RAW; + sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE; + sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + + *src_fmt = *sink_fmt; + + return 0; +} + +static const struct v4l2_subdev_pad_ops c3_mipi_csi_pad_ops = { + .enum_mbus_code = c3_mipi_csi_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = c3_mipi_csi_set_fmt, + .enable_streams = c3_mipi_csi_enable_streams, + .disable_streams = c3_mipi_csi_disable_streams, +}; + +static const struct v4l2_subdev_ops c3_mipi_csi_subdev_ops = { + .pad = &c3_mipi_csi_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops c3_mipi_csi_internal_ops = { + .init_state = c3_mipi_csi_init_state, +}; + +/* Media entity operations */ +static const struct media_entity_operations c3_mipi_csi_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* PM runtime */ + +static int c3_mipi_csi_runtime_suspend(struct device *dev) +{ + struct c3_csi_device *csi = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(csi->info->clock_num, csi->clks); + + return 0; +} + +static int c3_mipi_csi_runtime_resume(struct device *dev) +{ + struct c3_csi_device *csi = dev_get_drvdata(dev); + + return clk_bulk_prepare_enable(csi->info->clock_num, csi->clks); +} + +static const struct dev_pm_ops c3_mipi_csi_pm_ops = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + RUNTIME_PM_OPS(c3_mipi_csi_runtime_suspend, + c3_mipi_csi_runtime_resume, NULL) +}; + +/* Probe/remove & platform driver */ + +static int c3_mipi_csi_subdev_init(struct c3_csi_device *csi) +{ + struct v4l2_subdev *sd = &csi->sd; + int ret; + + v4l2_subdev_init(sd, &c3_mipi_csi_subdev_ops); + sd->owner = THIS_MODULE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->internal_ops = &c3_mipi_csi_internal_ops; + snprintf(sd->name, sizeof(sd->name), "%s", MIPI_CSI2_SUBDEV_NAME); + + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->entity.ops = &c3_mipi_csi_entity_ops; + + sd->dev = csi->dev; + v4l2_set_subdevdata(sd, csi); + + csi->pads[C3_MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + csi->pads[C3_MIPI_CSI2_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, C3_MIPI_CSI2_PAD_MAX, + csi->pads); + if (ret) + return ret; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) { + media_entity_cleanup(&sd->entity); + return ret; + } + + return 0; +} + +static void c3_mipi_csi_subdev_deinit(struct c3_csi_device *csi) +{ + v4l2_subdev_cleanup(&csi->sd); + media_entity_cleanup(&csi->sd.entity); +} + +/* Subdev notifier register */ +static int c3_mipi_csi_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asc) +{ + struct c3_csi_device *csi = v4l2_get_subdevdata(notifier->sd); + struct media_pad *sink = &csi->sd.entity.pads[C3_MIPI_CSI2_PAD_SINK]; + + return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} + +static const struct v4l2_async_notifier_operations c3_mipi_csi_notify_ops = { + .bound = c3_mipi_csi_notify_bound, +}; + +static int c3_mipi_csi_async_register(struct c3_csi_device *csi) +{ + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct v4l2_async_connection *asc; + struct fwnode_handle *ep; + int ret; + + v4l2_async_subdev_nf_init(&csi->notifier, &csi->sd); + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) + return -ENOTCONN; + + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + if (ret) + goto err_put_handle; + + csi->bus = vep.bus.mipi_csi2; + + asc = v4l2_async_nf_add_fwnode_remote(&csi->notifier, ep, + struct v4l2_async_connection); + if (IS_ERR(asc)) { + ret = PTR_ERR(asc); + goto err_put_handle; + } + + csi->notifier.ops = &c3_mipi_csi_notify_ops; + ret = v4l2_async_nf_register(&csi->notifier); + if (ret) + goto err_cleanup_nf; + + ret = v4l2_async_register_subdev(&csi->sd); + if (ret) + goto err_unregister_nf; + + fwnode_handle_put(ep); + + return 0; + +err_unregister_nf: + v4l2_async_nf_unregister(&csi->notifier); +err_cleanup_nf: + v4l2_async_nf_cleanup(&csi->notifier); +err_put_handle: + fwnode_handle_put(ep); + return ret; +} + +static void c3_mipi_csi_async_unregister(struct c3_csi_device *csi) +{ + v4l2_async_unregister_subdev(&csi->sd); + v4l2_async_nf_unregister(&csi->notifier); + v4l2_async_nf_cleanup(&csi->notifier); +} + +static int c3_mipi_csi_ioremap_resource(struct c3_csi_device *csi) +{ + struct device *dev = csi->dev; + struct platform_device *pdev = to_platform_device(dev); + + csi->aphy = devm_platform_ioremap_resource_byname(pdev, "aphy"); + if (IS_ERR(csi->aphy)) + return PTR_ERR(csi->aphy); + + csi->dphy = devm_platform_ioremap_resource_byname(pdev, "dphy"); + if (IS_ERR(csi->dphy)) + return PTR_ERR(csi->dphy); + + csi->host = devm_platform_ioremap_resource_byname(pdev, "host"); + if (IS_ERR(csi->host)) + return PTR_ERR(csi->host); + + return 0; +} + +static int c3_mipi_csi_get_clocks(struct c3_csi_device *csi) +{ + const struct c3_csi_info *info = csi->info; + + for (unsigned int i = 0; i < info->clock_num; i++) + csi->clks[i].id = info->clocks[i]; + + return devm_clk_bulk_get(csi->dev, info->clock_num, csi->clks); +} + +static int c3_mipi_csi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct c3_csi_device *csi; + int ret; + + csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL); + if (!csi) + return -ENOMEM; + + csi->info = of_device_get_match_data(dev); + csi->dev = dev; + + ret = c3_mipi_csi_ioremap_resource(csi); + if (ret) + return dev_err_probe(dev, ret, "Failed to ioremap resource\n"); + + ret = c3_mipi_csi_get_clocks(csi); + if (ret) + return dev_err_probe(dev, ret, "Failed to get clocks\n"); + + platform_set_drvdata(pdev, csi); + + pm_runtime_enable(dev); + + ret = c3_mipi_csi_subdev_init(csi); + if (ret) + goto err_disable_runtime_pm; + + ret = c3_mipi_csi_async_register(csi); + if (ret) + goto err_deinit_subdev; + + return 0; + +err_deinit_subdev: + c3_mipi_csi_subdev_deinit(csi); +err_disable_runtime_pm: + pm_runtime_disable(dev); + return ret; +}; + +static void c3_mipi_csi_remove(struct platform_device *pdev) +{ + struct c3_csi_device *csi = platform_get_drvdata(pdev); + + c3_mipi_csi_async_unregister(csi); + c3_mipi_csi_subdev_deinit(csi); + + pm_runtime_disable(&pdev->dev); +}; + +static const struct c3_csi_info c3_mipi_csi_info = { + .clocks = {"vapb", "phy0"}, + .clock_num = 2 +}; + +static const struct of_device_id c3_mipi_csi_of_match[] = { + { + .compatible = "amlogic,c3-mipi-csi2", + .data = &c3_mipi_csi_info, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, c3_mipi_csi_of_match); + +static struct platform_driver c3_mipi_csi_driver = { + .probe = c3_mipi_csi_probe, + .remove = c3_mipi_csi_remove, + .driver = { + .name = "c3-mipi-csi2", + .of_match_table = c3_mipi_csi_of_match, + .pm = pm_ptr(&c3_mipi_csi_pm_ops), + }, +}; + +module_platform_driver(c3_mipi_csi_driver); + +MODULE_AUTHOR("Keke Li "); +MODULE_DESCRIPTION("Amlogic C3 MIPI CSI-2 receiver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From a789e6fc7686c81e59af5697d344df8cf93b8716 Mon Sep 17 00:00:00 2001 From: Keke Li Date: Sun, 27 Apr 2025 14:27:11 +0800 Subject: media: dt-bindings: Add amlogic,c3-mipi-adapter.yaml c3-mipi-adapter is used to organize mipi data and send raw data to ISP module. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Keke Li Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- .../bindings/media/amlogic,c3-mipi-adapter.yaml | 111 +++++++++++++++++++++ MAINTAINERS | 6 ++ 2 files changed, 117 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/amlogic,c3-mipi-adapter.yaml diff --git a/Documentation/devicetree/bindings/media/amlogic,c3-mipi-adapter.yaml b/Documentation/devicetree/bindings/media/amlogic,c3-mipi-adapter.yaml new file mode 100644 index 000000000000..ba43bc6709a0 --- /dev/null +++ b/Documentation/devicetree/bindings/media/amlogic,c3-mipi-adapter.yaml @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/amlogic,c3-mipi-adapter.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic C3 MIPI adapter receiver + +maintainers: + - Keke Li + +description: + MIPI adapter is used to convert the MIPI CSI-2 data + into an ISP supported data format. + +properties: + compatible: + enum: + - amlogic,c3-mipi-adapter + + reg: + maxItems: 3 + + reg-names: + items: + - const: top + - const: fd + - const: rd + + power-domains: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: vapb + - const: isp0 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: input port node. + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: output port node. + + required: + - port@0 + - port@1 + +required: + - compatible + - reg + - reg-names + - power-domains + - clocks + - clock-names + - ports + +additionalProperties: false + +examples: + - | + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + adap: adap@ff010000 { + compatible = "amlogic,c3-mipi-adapter"; + reg = <0x0 0xff010000 0x0 0x100>, + <0x0 0xff01b000 0x0 0x100>, + <0x0 0xff01d000 0x0 0x200>; + reg-names = "top", "fd", "rd"; + power-domains = <&pwrc PWRC_C3_ISP_TOP_ID>; + clocks = <&clkc_periphs CLKID_VAPB>, + <&clkc_periphs CLKID_ISP0>; + clock-names = "vapb", "isp0"; + assigned-clocks = <&clkc_periphs CLKID_VAPB>, + <&clkc_periphs CLKID_ISP0>; + assigned-clock-rates = <0>, <400000000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + c3_adap_in: endpoint { + remote-endpoint = <&c3_mipi_csi_out>; + }; + }; + + port@1 { + reg = <1>; + c3_adap_out: endpoint { + remote-endpoint = <&c3_isp_in>; + }; + }; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 9436cce85a17..131b99c51422 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1254,6 +1254,12 @@ F: Documentation/devicetree/bindings/perf/amlogic,g12-ddr-pmu.yaml F: drivers/perf/amlogic/ F: include/soc/amlogic/ +AMLOGIC MIPI ADAPTER DRIVER +M: Keke Li +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/amlogic,c3-mipi-adapter.yaml + AMLOGIC MIPI CSI2 DRIVER M: Keke Li L: linux-media@vger.kernel.org -- cgit v1.2.3-59-g8ed1b From f0d2d8062cc8d552bd522595d557a0dec32d3ea2 Mon Sep 17 00:00:00 2001 From: Keke Li Date: Sun, 27 Apr 2025 14:27:12 +0800 Subject: media: platform: Add C3 MIPI adapter driver Add a driver for the MIPI adapter unit found on the Amlogic C3 SoC. This driver is used to align the MIPI data from the MIPI CSI-2 receiver unit and send the aligned data to the ISP unit. Reviewed-by: Daniel Scally Reviewed-by: Jacopo Mondi Signed-off-by: Keke Li Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- MAINTAINERS | 1 + drivers/media/platform/amlogic/c3/Kconfig | 1 + drivers/media/platform/amlogic/c3/Makefile | 1 + .../media/platform/amlogic/c3/mipi-adapter/Kconfig | 16 + .../platform/amlogic/c3/mipi-adapter/Makefile | 3 + .../amlogic/c3/mipi-adapter/c3-mipi-adap.c | 842 +++++++++++++++++++++ 6 files changed, 864 insertions(+) create mode 100644 drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig create mode 100644 drivers/media/platform/amlogic/c3/mipi-adapter/Makefile create mode 100644 drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c diff --git a/MAINTAINERS b/MAINTAINERS index 131b99c51422..e9e5878f0c8c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1259,6 +1259,7 @@ M: Keke Li L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/amlogic,c3-mipi-adapter.yaml +F: drivers/media/platform/amlogic/c3/mipi-adapter/ AMLOGIC MIPI CSI2 DRIVER M: Keke Li diff --git a/drivers/media/platform/amlogic/c3/Kconfig b/drivers/media/platform/amlogic/c3/Kconfig index 098d458747b8..a504a1eb22e6 100644 --- a/drivers/media/platform/amlogic/c3/Kconfig +++ b/drivers/media/platform/amlogic/c3/Kconfig @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only +source "drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig" source "drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig" diff --git a/drivers/media/platform/amlogic/c3/Makefile b/drivers/media/platform/amlogic/c3/Makefile index a468fb782f94..770b2a2903ad 100644 --- a/drivers/media/platform/amlogic/c3/Makefile +++ b/drivers/media/platform/amlogic/c3/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-y += mipi-adapter/ obj-y += mipi-csi2/ diff --git a/drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig b/drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig new file mode 100644 index 000000000000..bf19059b3543 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_C3_MIPI_ADAPTER + tristate "Amlogic C3 MIPI adapter" + depends on ARCH_MESON || COMPILE_TEST + depends on VIDEO_DEV + depends on OF + select MEDIA_CONTROLLER + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + Video4Linux2 driver for Amlogic C3 MIPI adapter. + C3 MIPI adapter mainly responsible for organizing + MIPI data and sending raw data to ISP pipeline. + + To compile this driver as a module choose m here. diff --git a/drivers/media/platform/amlogic/c3/mipi-adapter/Makefile b/drivers/media/platform/amlogic/c3/mipi-adapter/Makefile new file mode 100644 index 000000000000..216fc310c5b4 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/mipi-adapter/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_VIDEO_C3_MIPI_ADAPTER) += c3-mipi-adap.o diff --git a/drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c b/drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c new file mode 100644 index 000000000000..4bd98fb9c7e9 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c @@ -0,0 +1,842 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * Adapter Block Diagram + * --------------------- + * + * +--------------------------------------------+ + * | Adapter | + * |--------------------------------------------| + * +------------+ | | | | | +-----+ + * | MIPI CSI-2 |--->| Frontend -> DDR_RD0 -> PIXEL0 -> ALIGNMENT |--->| ISP | + * +------------+ | | | | | +-----+ + * +--------------------------------------------+ + * + */ + +/* C3 adapter submodule definition */ +enum { + SUBMD_TOP, + SUBMD_FD, + SUBMD_RD, +}; + +#define ADAP_SUBMD_MASK GENMASK(17, 16) +#define ADAP_SUBMD_SHIFT 16 +#define ADAP_SUBMD(x) (((x) & (ADAP_SUBMD_MASK)) >> (ADAP_SUBMD_SHIFT)) +#define ADAP_REG_ADDR_MASK GENMASK(15, 0) +#define ADAP_REG_ADDR(x) ((x) & (ADAP_REG_ADDR_MASK)) +#define ADAP_REG_T(x) ((SUBMD_TOP << ADAP_SUBMD_SHIFT) | (x)) +#define ADAP_REG_F(x) ((SUBMD_FD << ADAP_SUBMD_SHIFT) | (x)) +#define ADAP_REG_R(x) ((SUBMD_RD << ADAP_SUBMD_SHIFT) | (x)) + +#define MIPI_ADAP_CLOCK_NUM_MAX 3 +#define MIPI_ADAP_SUBDEV_NAME "c3-mipi-adapter" + +/* C3 MIPI adapter TOP register */ +#define MIPI_TOP_CTRL0 ADAP_REG_T(0x00) +#define MIPI_TOP_CTRL0_RST_ADAPTER_MASK BIT(1) +#define MIPI_TOP_CTRL0_RST_ADAPTER_APPLY BIT(1) +#define MIPI_TOP_CTRL0_RST_ADAPTER_EXIT (0 << 1) + +#define MIPI_ADAPT_DE_CTRL0 ADAP_REG_T(0x40) +#define MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_MASK BIT(3) +#define MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_EN BIT(3) +#define MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_DIS (0 << 3) +#define MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_MASK BIT(7) +#define MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_EN BIT(7) +#define MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_DIS (0 << 7) + +/* C3 MIPI adapter FRONTEND register */ +#define CSI2_CLK_RESET ADAP_REG_F(0x00) +#define CSI2_CLK_RESET_SW_RESET_MASK BIT(0) +#define CSI2_CLK_RESET_SW_RESET_APPLY BIT(0) +#define CSI2_CLK_RESET_SW_RESET_RELEASE (0 << 0) +#define CSI2_CLK_RESET_CLK_ENABLE_MASK BIT(1) +#define CSI2_CLK_RESET_CLK_ENABLE_EN BIT(1) +#define CSI2_CLK_RESET_CLK_ENABLE_DIS (0 << 1) + +#define CSI2_GEN_CTRL0 ADAP_REG_F(0x04) +#define CSI2_GEN_CTRL0_VC0_MASK BIT(0) +#define CSI2_GEN_CTRL0_VC0_EN BIT(0) +#define CSI2_GEN_CTRL0_VC0_DIS (0 << 0) +#define CSI2_GEN_CTRL0_ENABLE_PACKETS_MASK GENMASK(20, 16) +#define CSI2_GEN_CTRL0_ENABLE_PACKETS_RAW BIT(16) +#define CSI2_GEN_CTRL0_ENABLE_PACKETS_YUV (2 << 16) + +#define CSI2_X_START_END_ISP ADAP_REG_F(0x0c) +#define CSI2_X_START_END_ISP_X_START_MASK GENMASK(15, 0) +#define CSI2_X_START_END_ISP_X_START(x) ((x) << 0) +#define CSI2_X_START_END_ISP_X_END_MASK GENMASK(31, 16) +#define CSI2_X_START_END_ISP_X_END(x) (((x) - 1) << 16) + +#define CSI2_Y_START_END_ISP ADAP_REG_F(0x10) +#define CSI2_Y_START_END_ISP_Y_START_MASK GENMASK(15, 0) +#define CSI2_Y_START_END_ISP_Y_START(x) ((x) << 0) +#define CSI2_Y_START_END_ISP_Y_END_MASK GENMASK(31, 16) +#define CSI2_Y_START_END_ISP_Y_END(x) (((x) - 1) << 16) + +#define CSI2_VC_MODE ADAP_REG_F(0x1c) +#define CSI2_VC_MODE_VS_ISP_SEL_VC_MASK GENMASK(19, 16) +#define CSI2_VC_MODE_VS_ISP_SEL_VC_0 BIT(16) +#define CSI2_VC_MODE_VS_ISP_SEL_VC_1 (2 << 16) +#define CSI2_VC_MODE_VS_ISP_SEL_VC_2 (4 << 16) +#define CSI2_VC_MODE_VS_ISP_SEL_VC_3 (8 << 16) +#define CSI2_VC_MODE_HS_ISP_SEL_VC_MASK GENMASK(23, 20) +#define CSI2_VC_MODE_HS_ISP_SEL_VC_0 BIT(20) +#define CSI2_VC_MODE_HS_ISP_SEL_VC_1 (2 << 20) +#define CSI2_VC_MODE_HS_ISP_SEL_VC_2 (4 << 20) +#define CSI2_VC_MODE_HS_ISP_SEL_VC_3 (8 << 20) + +/* C3 MIPI adapter READER register */ +#define MIPI_ADAPT_DDR_RD0_CNTL0 ADAP_REG_R(0x00) +#define MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN_MASK BIT(0) +#define MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN BIT(0) +#define MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_DIS (0 << 0) + +#define MIPI_ADAPT_DDR_RD0_CNTL1 ADAP_REG_R(0x04) +#define MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_MASK GENMASK(31, 30) +#define MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_DIRECT_MODE (0 << 30) +#define MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_DDR_MODE BIT(30) + +#define MIPI_ADAPT_PIXEL0_CNTL0 ADAP_REG_R(0x80) +#define MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_MASK GENMASK(17, 16) +#define MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_RAW_DDR (0 << 16) +#define MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_RAW_DIRECT BIT(16) +#define MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE_MASK GENMASK(25, 20) +#define MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE(x) ((x) << 20) +#define MIPI_ADAPT_PIXEL0_CNTL0_START_EN_MASK BIT(31) +#define MIPI_ADAPT_PIXEL0_CNTL0_START_EN BIT(31) + +#define MIPI_ADAPT_ALIG_CNTL0 ADAP_REG_R(0x100) +#define MIPI_ADAPT_ALIG_CNTL0_H_NUM_MASK GENMASK(15, 0) +#define MIPI_ADAPT_ALIG_CNTL0_H_NUM(x) ((x) << 0) +#define MIPI_ADAPT_ALIG_CNTL0_V_NUM_MASK GENMASK(31, 16) +#define MIPI_ADAPT_ALIG_CNTL0_V_NUM(x) ((x) << 16) + +#define MIPI_ADAPT_ALIG_CNTL1 ADAP_REG_R(0x104) +#define MIPI_ADAPT_ALIG_CNTL1_HPE_NUM_MASK GENMASK(31, 16) +#define MIPI_ADAPT_ALIG_CNTL1_HPE_NUM(x) ((x) << 16) + +#define MIPI_ADAPT_ALIG_CNTL2 ADAP_REG_R(0x108) +#define MIPI_ADAPT_ALIG_CNTL2_VPE_NUM_MASK GENMASK(31, 16) +#define MIPI_ADAPT_ALIG_CNTL2_VPE_NUM(x) ((x) << 16) + +#define MIPI_ADAPT_ALIG_CNTL6 ADAP_REG_R(0x118) +#define MIPI_ADAPT_ALIG_CNTL6_PATH0_EN_MASK BIT(0) +#define MIPI_ADAPT_ALIG_CNTL6_PATH0_EN BIT(0) +#define MIPI_ADAPT_ALIG_CNTL6_PATH0_DIS (0 << 0) +#define MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_MASK BIT(4) +#define MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_DDR (0 << 4) +#define MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_DIRECT BIT(4) +#define MIPI_ADAPT_ALIG_CNTL6_DATA0_EN_MASK BIT(12) +#define MIPI_ADAPT_ALIG_CNTL6_DATA0_EN BIT(12) +#define MIPI_ADAPT_ALIG_CNTL6_DATA0_DIS (0 << 12) + +#define MIPI_ADAPT_ALIG_CNTL8 ADAP_REG_R(0x120) +#define MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_MASK BIT(5) +#define MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_EN BIT(5) +#define MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_DIS (0 << 5) +#define MIPI_ADAPT_ALIG_CNTL8_EXCEED_DIS_MASK BIT(12) +#define MIPI_ADAPT_ALIG_CNTL8_EXCEED_HOLD (0 << 12) +#define MIPI_ADAPT_ALIG_CNTL8_EXCEED_NOT_HOLD BIT(12) +#define MIPI_ADAPT_ALIG_CNTL8_START_EN_MASK BIT(31) +#define MIPI_ADAPT_ALIG_CNTL8_START_EN BIT(31) + +#define MIPI_ADAP_MAX_WIDTH 2888 +#define MIPI_ADAP_MIN_WIDTH 160 +#define MIPI_ADAP_MAX_HEIGHT 2240 +#define MIPI_ADAP_MIN_HEIGHT 120 +#define MIPI_ADAP_DEFAULT_WIDTH 1920 +#define MIPI_ADAP_DEFAULT_HEIGHT 1080 +#define MIPI_ADAP_DEFAULT_FMT MEDIA_BUS_FMT_SRGGB10_1X10 + +/* C3 MIPI adapter pad list */ +enum { + C3_MIPI_ADAP_PAD_SINK, + C3_MIPI_ADAP_PAD_SRC, + C3_MIPI_ADAP_PAD_MAX +}; + +/* + * struct c3_adap_info - mipi adapter information + * + * @clocks: array of mipi adapter clock names + * @clock_num: actual clock number + */ +struct c3_adap_info { + char *clocks[MIPI_ADAP_CLOCK_NUM_MAX]; + u32 clock_num; +}; + +/* + * struct c3_adap_device - mipi adapter platform device + * + * @dev: pointer to the struct device + * @top: mipi adapter top register address + * @fd: mipi adapter frontend register address + * @rd: mipi adapter reader register address + * @clks: array of MIPI adapter clocks + * @sd: mipi adapter sub-device + * @pads: mipi adapter sub-device pads + * @notifier: notifier to register on the v4l2-async API + * @src_sd: source sub-device pad + * @info: version-specific MIPI adapter information + */ +struct c3_adap_device { + struct device *dev; + void __iomem *top; + void __iomem *fd; + void __iomem *rd; + struct clk_bulk_data clks[MIPI_ADAP_CLOCK_NUM_MAX]; + + struct v4l2_subdev sd; + struct media_pad pads[C3_MIPI_ADAP_PAD_MAX]; + struct v4l2_async_notifier notifier; + struct media_pad *src_pad; + + const struct c3_adap_info *info; +}; + +/* Format helpers */ + +struct c3_adap_pix_format { + u32 code; + u8 type; +}; + +static const struct c3_adap_pix_format c3_mipi_adap_formats[] = { + { MEDIA_BUS_FMT_SBGGR10_1X10, MIPI_CSI2_DT_RAW10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, MIPI_CSI2_DT_RAW10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, MIPI_CSI2_DT_RAW10 }, + { MEDIA_BUS_FMT_SRGGB10_1X10, MIPI_CSI2_DT_RAW10 }, + { MEDIA_BUS_FMT_SBGGR12_1X12, MIPI_CSI2_DT_RAW12 }, + { MEDIA_BUS_FMT_SGBRG12_1X12, MIPI_CSI2_DT_RAW12 }, + { MEDIA_BUS_FMT_SGRBG12_1X12, MIPI_CSI2_DT_RAW12 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, MIPI_CSI2_DT_RAW12 }, +}; + +static const struct c3_adap_pix_format *c3_mipi_adap_find_format(u32 code) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(c3_mipi_adap_formats); i++) + if (code == c3_mipi_adap_formats[i].code) + return &c3_mipi_adap_formats[i]; + + return NULL; +} + +/* Hardware configuration */ + +static void c3_mipi_adap_update_bits(struct c3_adap_device *adap, u32 reg, + u32 mask, u32 val) +{ + void __iomem *addr; + u32 orig, tmp; + + switch (ADAP_SUBMD(reg)) { + case SUBMD_TOP: + addr = adap->top + ADAP_REG_ADDR(reg); + break; + case SUBMD_FD: + addr = adap->fd + ADAP_REG_ADDR(reg); + break; + case SUBMD_RD: + addr = adap->rd + ADAP_REG_ADDR(reg); + break; + default: + dev_err(adap->dev, + "Invalid sub-module: %lu\n", ADAP_SUBMD(reg)); + return; + } + + orig = readl(addr); + tmp = orig & ~mask; + tmp |= val & mask; + + if (tmp != orig) + writel(tmp, addr); +} + +/* Configure adapter top sub module */ +static void c3_mipi_adap_cfg_top(struct c3_adap_device *adap) +{ + /* Reset adapter */ + c3_mipi_adap_update_bits(adap, MIPI_TOP_CTRL0, + MIPI_TOP_CTRL0_RST_ADAPTER_MASK, + MIPI_TOP_CTRL0_RST_ADAPTER_APPLY); + c3_mipi_adap_update_bits(adap, MIPI_TOP_CTRL0, + MIPI_TOP_CTRL0_RST_ADAPTER_MASK, + MIPI_TOP_CTRL0_RST_ADAPTER_EXIT); + + /* Bypass decompress */ + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DE_CTRL0, + MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_MASK, + MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_EN); + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DE_CTRL0, + MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_MASK, + MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_EN); +} + +/* Configure adapter frontend sub module */ +static void c3_mipi_adap_cfg_frontend(struct c3_adap_device *adap, + struct v4l2_mbus_framefmt *fmt) +{ + /* Reset frontend module */ + c3_mipi_adap_update_bits(adap, CSI2_CLK_RESET, + CSI2_CLK_RESET_SW_RESET_MASK, + CSI2_CLK_RESET_SW_RESET_APPLY); + c3_mipi_adap_update_bits(adap, CSI2_CLK_RESET, + CSI2_CLK_RESET_SW_RESET_MASK, + CSI2_CLK_RESET_SW_RESET_RELEASE); + c3_mipi_adap_update_bits(adap, CSI2_CLK_RESET, + CSI2_CLK_RESET_CLK_ENABLE_MASK, + CSI2_CLK_RESET_CLK_ENABLE_EN); + + c3_mipi_adap_update_bits(adap, CSI2_X_START_END_ISP, + CSI2_X_START_END_ISP_X_START_MASK, + CSI2_X_START_END_ISP_X_START(0)); + c3_mipi_adap_update_bits(adap, CSI2_X_START_END_ISP, + CSI2_X_START_END_ISP_X_END_MASK, + CSI2_X_START_END_ISP_X_END(fmt->width)); + + c3_mipi_adap_update_bits(adap, CSI2_Y_START_END_ISP, + CSI2_Y_START_END_ISP_Y_START_MASK, + CSI2_Y_START_END_ISP_Y_START(0)); + c3_mipi_adap_update_bits(adap, CSI2_Y_START_END_ISP, + CSI2_Y_START_END_ISP_Y_END_MASK, + CSI2_Y_START_END_ISP_Y_END(fmt->height)); + + /* Select VS and HS signal for direct path */ + c3_mipi_adap_update_bits(adap, CSI2_VC_MODE, + CSI2_VC_MODE_VS_ISP_SEL_VC_MASK, + CSI2_VC_MODE_VS_ISP_SEL_VC_0); + c3_mipi_adap_update_bits(adap, CSI2_VC_MODE, + CSI2_VC_MODE_HS_ISP_SEL_VC_MASK, + CSI2_VC_MODE_HS_ISP_SEL_VC_0); + + /* Enable to receive RAW packet */ + c3_mipi_adap_update_bits(adap, CSI2_GEN_CTRL0, + CSI2_GEN_CTRL0_ENABLE_PACKETS_MASK, + CSI2_GEN_CTRL0_ENABLE_PACKETS_RAW); + + /* Enable virtual channel 0 */ + c3_mipi_adap_update_bits(adap, CSI2_GEN_CTRL0, + CSI2_GEN_CTRL0_VC0_MASK, + CSI2_GEN_CTRL0_VC0_EN); +} + +static void c3_mipi_adap_cfg_rd0(struct c3_adap_device *adap) +{ + /* Select direct mode for DDR_RD0 mode */ + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DDR_RD0_CNTL1, + MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_MASK, + MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_DIRECT_MODE); + + /* Data can't bypass DDR_RD0 in direct mode, so enable DDR_RD0 here */ + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DDR_RD0_CNTL0, + MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN_MASK, + MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN); +} + +static void c3_mipi_adap_cfg_pixel0(struct c3_adap_device *adap, + struct v4l2_mbus_framefmt *fmt) +{ + const struct c3_adap_pix_format *pix; + + pix = c3_mipi_adap_find_format(fmt->code); + + /* Set work mode and data type for PIXEL0 module */ + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_PIXEL0_CNTL0, + MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_MASK, + MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_RAW_DIRECT); + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_PIXEL0_CNTL0, + MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE_MASK, + MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE(pix->type)); + + /* Start PIXEL0 module */ + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_PIXEL0_CNTL0, + MIPI_ADAPT_PIXEL0_CNTL0_START_EN_MASK, + MIPI_ADAPT_PIXEL0_CNTL0_START_EN); +} + +static void c3_mipi_adap_cfg_alig(struct c3_adap_device *adap, + struct v4l2_mbus_framefmt *fmt) +{ + /* + * ISP hardware requires the number of horizonal blanks greater than + * 64 cycles, so adding 64 here. + */ + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL0, + MIPI_ADAPT_ALIG_CNTL0_H_NUM_MASK, + MIPI_ADAPT_ALIG_CNTL0_H_NUM(fmt->width + 64)); + + /* + * ISP hardware requires the number of vertical blanks greater than + * 40 lines, so adding 40 here. + */ + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL0, + MIPI_ADAPT_ALIG_CNTL0_V_NUM_MASK, + MIPI_ADAPT_ALIG_CNTL0_V_NUM(fmt->height + 40)); + + /* End pixel in a line */ + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL1, + MIPI_ADAPT_ALIG_CNTL1_HPE_NUM_MASK, + MIPI_ADAPT_ALIG_CNTL1_HPE_NUM(fmt->width)); + + /* End line in a frame */ + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL2, + MIPI_ADAPT_ALIG_CNTL2_VPE_NUM_MASK, + MIPI_ADAPT_ALIG_CNTL2_VPE_NUM(fmt->height)); + + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL6, + MIPI_ADAPT_ALIG_CNTL6_PATH0_EN_MASK, + MIPI_ADAPT_ALIG_CNTL6_PATH0_EN); + + /* Select direct mode for ALIG module */ + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL6, + MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_MASK, + MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_DIRECT); + + /* Enable to send raw data */ + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL6, + MIPI_ADAPT_ALIG_CNTL6_DATA0_EN_MASK, + MIPI_ADAPT_ALIG_CNTL6_DATA0_EN); + + /* Set continue mode and disable hold counter */ + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL8, + MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_MASK, + MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_EN); + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL8, + MIPI_ADAPT_ALIG_CNTL8_EXCEED_DIS_MASK, + MIPI_ADAPT_ALIG_CNTL8_EXCEED_NOT_HOLD); + + /* Start ALIG module */ + c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL8, + MIPI_ADAPT_ALIG_CNTL8_START_EN_MASK, + MIPI_ADAPT_ALIG_CNTL8_START_EN); +} + +/* V4L2 subdev operations */ + +static int c3_mipi_adap_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct c3_adap_device *adap = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *fmt; + struct media_pad *sink_pad; + struct v4l2_subdev *src_sd; + int ret; + + sink_pad = &adap->pads[C3_MIPI_ADAP_PAD_SINK]; + adap->src_pad = media_pad_remote_pad_unique(sink_pad); + if (IS_ERR(adap->src_pad)) { + dev_dbg(adap->dev, "Failed to get source pad for MIPI adap\n"); + return -EPIPE; + } + + src_sd = media_entity_to_v4l2_subdev(adap->src_pad->entity); + + pm_runtime_resume_and_get(adap->dev); + + fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SINK); + + c3_mipi_adap_cfg_top(adap); + c3_mipi_adap_cfg_frontend(adap, fmt); + c3_mipi_adap_cfg_rd0(adap); + c3_mipi_adap_cfg_pixel0(adap, fmt); + c3_mipi_adap_cfg_alig(adap, fmt); + + ret = v4l2_subdev_enable_streams(src_sd, adap->src_pad->index, BIT(0)); + if (ret) { + pm_runtime_put(adap->dev); + return ret; + } + + return 0; +} + +static int c3_mipi_adap_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct c3_adap_device *adap = v4l2_get_subdevdata(sd); + struct v4l2_subdev *src_sd; + + if (adap->src_pad) { + src_sd = media_entity_to_v4l2_subdev(adap->src_pad->entity); + v4l2_subdev_disable_streams(src_sd, adap->src_pad->index, + BIT(0)); + } + adap->src_pad = NULL; + + pm_runtime_put(adap->dev); + + return 0; +} + +static int c3_mipi_adap_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct v4l2_mbus_framefmt *fmt; + + switch (code->pad) { + case C3_MIPI_ADAP_PAD_SINK: + if (code->index >= ARRAY_SIZE(c3_mipi_adap_formats)) + return -EINVAL; + + code->code = c3_mipi_adap_formats[code->index].code; + break; + case C3_MIPI_ADAP_PAD_SRC: + if (code->index) + return -EINVAL; + + fmt = v4l2_subdev_state_get_format(state, code->pad); + code->code = fmt->code; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int c3_mipi_adap_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt; + const struct c3_adap_pix_format *pix_format; + + if (format->pad != C3_MIPI_ADAP_PAD_SINK) + return v4l2_subdev_get_fmt(sd, state, format); + + pix_format = c3_mipi_adap_find_format(format->format.code); + if (!pix_format) + pix_format = &c3_mipi_adap_formats[0]; + + fmt = v4l2_subdev_state_get_format(state, format->pad); + fmt->code = pix_format->code; + fmt->width = clamp_t(u32, format->format.width, + MIPI_ADAP_MIN_WIDTH, MIPI_ADAP_MAX_WIDTH); + fmt->height = clamp_t(u32, format->format.height, + MIPI_ADAP_MIN_HEIGHT, MIPI_ADAP_MAX_HEIGHT); + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + + format->format = *fmt; + + /* Synchronize the format to source pad */ + fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SRC); + *fmt = format->format; + + return 0; +} + +static int c3_mipi_adap_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_mbus_framefmt *src_fmt; + + sink_fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SRC); + + sink_fmt->width = MIPI_ADAP_DEFAULT_WIDTH; + sink_fmt->height = MIPI_ADAP_DEFAULT_HEIGHT; + sink_fmt->field = V4L2_FIELD_NONE; + sink_fmt->code = MIPI_ADAP_DEFAULT_FMT; + sink_fmt->colorspace = V4L2_COLORSPACE_RAW; + sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE; + sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + + *src_fmt = *sink_fmt; + + return 0; +} + +static const struct v4l2_subdev_pad_ops c3_mipi_adap_pad_ops = { + .enum_mbus_code = c3_mipi_adap_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = c3_mipi_adap_set_fmt, + .enable_streams = c3_mipi_adap_enable_streams, + .disable_streams = c3_mipi_adap_disable_streams, +}; + +static const struct v4l2_subdev_ops c3_mipi_adap_subdev_ops = { + .pad = &c3_mipi_adap_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops c3_mipi_adap_internal_ops = { + .init_state = c3_mipi_adap_init_state, +}; + +/* Media entity operations */ +static const struct media_entity_operations c3_mipi_adap_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* PM runtime */ + +static int c3_mipi_adap_runtime_suspend(struct device *dev) +{ + struct c3_adap_device *adap = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(adap->info->clock_num, adap->clks); + + return 0; +} + +static int c3_mipi_adap_runtime_resume(struct device *dev) +{ + struct c3_adap_device *adap = dev_get_drvdata(dev); + + return clk_bulk_prepare_enable(adap->info->clock_num, adap->clks); +} + +static const struct dev_pm_ops c3_mipi_adap_pm_ops = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + RUNTIME_PM_OPS(c3_mipi_adap_runtime_suspend, + c3_mipi_adap_runtime_resume, NULL) +}; + +/* Probe/remove & platform driver */ + +static int c3_mipi_adap_subdev_init(struct c3_adap_device *adap) +{ + struct v4l2_subdev *sd = &adap->sd; + int ret; + + v4l2_subdev_init(sd, &c3_mipi_adap_subdev_ops); + sd->owner = THIS_MODULE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->internal_ops = &c3_mipi_adap_internal_ops; + snprintf(sd->name, sizeof(sd->name), "%s", MIPI_ADAP_SUBDEV_NAME); + + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->entity.ops = &c3_mipi_adap_entity_ops; + + sd->dev = adap->dev; + v4l2_set_subdevdata(sd, adap); + + adap->pads[C3_MIPI_ADAP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + adap->pads[C3_MIPI_ADAP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, C3_MIPI_ADAP_PAD_MAX, + adap->pads); + if (ret) + return ret; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) { + media_entity_cleanup(&sd->entity); + return ret; + } + + return 0; +} + +static void c3_mipi_adap_subdev_deinit(struct c3_adap_device *adap) +{ + v4l2_subdev_cleanup(&adap->sd); + media_entity_cleanup(&adap->sd.entity); +} + +/* Subdev notifier register */ +static int c3_mipi_adap_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asc) +{ + struct c3_adap_device *adap = v4l2_get_subdevdata(notifier->sd); + struct media_pad *sink = &adap->sd.entity.pads[C3_MIPI_ADAP_PAD_SINK]; + + return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} + +static const struct v4l2_async_notifier_operations c3_mipi_adap_notify_ops = { + .bound = c3_mipi_adap_notify_bound, +}; + +static int c3_mipi_adap_async_register(struct c3_adap_device *adap) +{ + struct v4l2_async_connection *asc; + struct fwnode_handle *ep; + int ret; + + v4l2_async_subdev_nf_init(&adap->notifier, &adap->sd); + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(adap->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) + return -ENOTCONN; + + asc = v4l2_async_nf_add_fwnode_remote(&adap->notifier, ep, + struct v4l2_async_connection); + if (IS_ERR(asc)) { + ret = PTR_ERR(asc); + goto err_put_handle; + } + + adap->notifier.ops = &c3_mipi_adap_notify_ops; + ret = v4l2_async_nf_register(&adap->notifier); + if (ret) + goto err_cleanup_nf; + + ret = v4l2_async_register_subdev(&adap->sd); + if (ret) + goto err_unregister_nf; + + fwnode_handle_put(ep); + + return 0; + +err_unregister_nf: + v4l2_async_nf_unregister(&adap->notifier); +err_cleanup_nf: + v4l2_async_nf_cleanup(&adap->notifier); +err_put_handle: + fwnode_handle_put(ep); + return ret; +} + +static void c3_mipi_adap_async_unregister(struct c3_adap_device *adap) +{ + v4l2_async_unregister_subdev(&adap->sd); + v4l2_async_nf_unregister(&adap->notifier); + v4l2_async_nf_cleanup(&adap->notifier); +} + +static int c3_mipi_adap_ioremap_resource(struct c3_adap_device *adap) +{ + struct device *dev = adap->dev; + struct platform_device *pdev = to_platform_device(dev); + + adap->top = devm_platform_ioremap_resource_byname(pdev, "top"); + if (IS_ERR(adap->top)) + return PTR_ERR(adap->top); + + adap->fd = devm_platform_ioremap_resource_byname(pdev, "fd"); + if (IS_ERR(adap->fd)) + return PTR_ERR(adap->fd); + + adap->rd = devm_platform_ioremap_resource_byname(pdev, "rd"); + if (IS_ERR(adap->rd)) + return PTR_ERR(adap->rd); + + return 0; +} + +static int c3_mipi_adap_get_clocks(struct c3_adap_device *adap) +{ + const struct c3_adap_info *info = adap->info; + + for (unsigned int i = 0; i < info->clock_num; i++) + adap->clks[i].id = info->clocks[i]; + + return devm_clk_bulk_get(adap->dev, info->clock_num, adap->clks); +} + +static int c3_mipi_adap_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct c3_adap_device *adap; + int ret; + + adap = devm_kzalloc(dev, sizeof(*adap), GFP_KERNEL); + if (!adap) + return -ENOMEM; + + adap->info = of_device_get_match_data(dev); + adap->dev = dev; + + ret = c3_mipi_adap_ioremap_resource(adap); + if (ret) + return dev_err_probe(dev, ret, "Failed to ioremap resource\n"); + + ret = c3_mipi_adap_get_clocks(adap); + if (ret) + return dev_err_probe(dev, ret, "Failed to get clocks\n"); + + platform_set_drvdata(pdev, adap); + + pm_runtime_enable(dev); + + ret = c3_mipi_adap_subdev_init(adap); + if (ret) + goto err_disable_runtime_pm; + + ret = c3_mipi_adap_async_register(adap); + if (ret) + goto err_deinit_subdev; + + return 0; + +err_deinit_subdev: + c3_mipi_adap_subdev_deinit(adap); +err_disable_runtime_pm: + pm_runtime_disable(dev); + return ret; +}; + +static void c3_mipi_adap_remove(struct platform_device *pdev) +{ + struct c3_adap_device *adap = platform_get_drvdata(pdev); + + c3_mipi_adap_async_unregister(adap); + c3_mipi_adap_subdev_deinit(adap); + + pm_runtime_disable(&pdev->dev); +}; + +static const struct c3_adap_info c3_mipi_adap_info = { + .clocks = {"vapb", "isp0"}, + .clock_num = 2 +}; + +static const struct of_device_id c3_mipi_adap_of_match[] = { + { + .compatible = "amlogic,c3-mipi-adapter", + .data = &c3_mipi_adap_info + }, + { }, +}; +MODULE_DEVICE_TABLE(of, c3_mipi_adap_of_match); + +static struct platform_driver c3_mipi_adap_driver = { + .probe = c3_mipi_adap_probe, + .remove = c3_mipi_adap_remove, + .driver = { + .name = "c3-mipi-adapter", + .of_match_table = c3_mipi_adap_of_match, + .pm = pm_ptr(&c3_mipi_adap_pm_ops), + }, +}; + +module_platform_driver(c3_mipi_adap_driver); + +MODULE_AUTHOR("Keke Li "); +MODULE_DESCRIPTION("Amlogic C3 MIPI adapter"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From d0a02f67f032873922dccca70d26340e41d62072 Mon Sep 17 00:00:00 2001 From: Keke Li Date: Sun, 27 Apr 2025 14:27:13 +0800 Subject: media: dt-bindings: Add amlogic,c3-isp.yaml c3-isp is used to process raw image. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Keke Li Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- .../devicetree/bindings/media/amlogic,c3-isp.yaml | 88 ++++++++++++++++++++++ MAINTAINERS | 6 ++ 2 files changed, 94 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/amlogic,c3-isp.yaml diff --git a/Documentation/devicetree/bindings/media/amlogic,c3-isp.yaml b/Documentation/devicetree/bindings/media/amlogic,c3-isp.yaml new file mode 100644 index 000000000000..123bf462f098 --- /dev/null +++ b/Documentation/devicetree/bindings/media/amlogic,c3-isp.yaml @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/amlogic,c3-isp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic C3 Image Signal Processing Unit + +maintainers: + - Keke Li + +description: + Amlogic ISP is the RAW image processing module + and supports three channels image output. + +properties: + compatible: + enum: + - amlogic,c3-isp + + reg: + maxItems: 1 + + reg-names: + items: + - const: isp + + power-domains: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: vapb + - const: isp0 + + interrupts: + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/properties/port + description: input port node. + +required: + - compatible + - reg + - reg-names + - power-domains + - clocks + - clock-names + - interrupts + - port + +additionalProperties: false + +examples: + - | + #include + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + isp: isp@ff000000 { + compatible = "amlogic,c3-isp"; + reg = <0x0 0xff000000 0x0 0xf000>; + reg-names = "isp"; + power-domains = <&pwrc PWRC_C3_ISP_TOP_ID>; + clocks = <&clkc_periphs CLKID_VAPB>, + <&clkc_periphs CLKID_ISP0>; + clock-names = "vapb", "isp0"; + assigned-clocks = <&clkc_periphs CLKID_VAPB>, + <&clkc_periphs CLKID_ISP0>; + assigned-clock-rates = <0>, <400000000>; + interrupts = ; + + port { + c3_isp_in: endpoint { + remote-endpoint = <&c3_adap_out>; + }; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index e9e5878f0c8c..4fe7e0dcd98a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1254,6 +1254,12 @@ F: Documentation/devicetree/bindings/perf/amlogic,g12-ddr-pmu.yaml F: drivers/perf/amlogic/ F: include/soc/amlogic/ +AMLOGIC ISP DRIVER +M: Keke Li +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/amlogic,c3-isp.yaml + AMLOGIC MIPI ADAPTER DRIVER M: Keke Li L: linux-media@vger.kernel.org -- cgit v1.2.3-59-g8ed1b From a3aa115af25473c1309bc99cac6b2b6cd180fdd9 Mon Sep 17 00:00:00 2001 From: Keke Li Date: Sun, 27 Apr 2025 14:27:14 +0800 Subject: media: Add C3ISP_PARAMS and C3ISP_STATS meta formats C3ISP_PARAMS is the C3 ISP Parameters format. C3ISP_STATS is the C3 ISP Statistics format. Reviewed-by: Daniel Scally Reviewed-by: Jacopo Mondi Signed-off-by: Keke Li Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-ioctl.c | 2 ++ include/uapi/linux/videodev2.h | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 78f217768ae7..650dc1956f73 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1464,6 +1464,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_META_FMT_RK_ISP1_PARAMS: descr = "Rockchip ISP1 3A Parameters"; break; case V4L2_META_FMT_RK_ISP1_STAT_3A: descr = "Rockchip ISP1 3A Statistics"; break; case V4L2_META_FMT_RK_ISP1_EXT_PARAMS: descr = "Rockchip ISP1 Ext 3A Params"; break; + case V4L2_META_FMT_C3ISP_PARAMS: descr = "Amlogic C3 ISP Parameters"; break; + case V4L2_META_FMT_C3ISP_STATS: descr = "Amlogic C3 ISP Statistics"; break; case V4L2_PIX_FMT_NV12_8L128: descr = "NV12 (8x128 Linear)"; break; case V4L2_PIX_FMT_NV12M_8L128: descr = "NV12M (8x128 Linear)"; break; case V4L2_PIX_FMT_NV12_10BE_8L128: descr = "10-bit NV12 (8x128 Linear, BE)"; break; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index ca7b3e8863ca..9e3b366d5fc7 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -868,6 +868,10 @@ struct v4l2_pix_format { #define V4L2_META_FMT_RK_ISP1_STAT_3A v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */ #define V4L2_META_FMT_RK_ISP1_EXT_PARAMS v4l2_fourcc('R', 'K', '1', 'E') /* Rockchip ISP1 3a Extensible Parameters */ +/* Vendor specific - used for C3_ISP */ +#define V4L2_META_FMT_C3ISP_PARAMS v4l2_fourcc('C', '3', 'P', 'M') /* Amlogic C3 ISP Parameters */ +#define V4L2_META_FMT_C3ISP_STATS v4l2_fourcc('C', '3', 'S', 'T') /* Amlogic C3 ISP Statistics */ + /* Vendor specific - used for RaspberryPi PiSP */ #define V4L2_META_FMT_RPI_BE_CFG v4l2_fourcc('R', 'P', 'B', 'C') /* PiSP BE configuration */ #define V4L2_META_FMT_RPI_FE_CFG v4l2_fourcc('R', 'P', 'F', 'C') /* PiSP FE configuration */ -- cgit v1.2.3-59-g8ed1b From 6d406187ebc092ebccf4fb5a1bc16b92c4bc109a Mon Sep 17 00:00:00 2001 From: Keke Li Date: Sun, 27 Apr 2025 14:27:15 +0800 Subject: media: uapi: Add stats info and parameters buffer for C3 ISP Add a header that describes the 3A statistics buffer and the parameters buffer for C3 ISP Reviewed-by: Jacopo Mondi Signed-off-by: Keke Li Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- MAINTAINERS | 1 + include/uapi/linux/media/amlogic/c3-isp-config.h | 564 +++++++++++++++++++++++ 2 files changed, 565 insertions(+) create mode 100644 include/uapi/linux/media/amlogic/c3-isp-config.h diff --git a/MAINTAINERS b/MAINTAINERS index 4fe7e0dcd98a..e36c2ceaca79 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1259,6 +1259,7 @@ M: Keke Li L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/amlogic,c3-isp.yaml +F: include/uapi/linux/media/amlogic/ AMLOGIC MIPI ADAPTER DRIVER M: Keke Li diff --git a/include/uapi/linux/media/amlogic/c3-isp-config.h b/include/uapi/linux/media/amlogic/c3-isp-config.h new file mode 100644 index 000000000000..ed085ea62a57 --- /dev/null +++ b/include/uapi/linux/media/amlogic/c3-isp-config.h @@ -0,0 +1,564 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + */ + +#ifndef _UAPI_C3_ISP_CONFIG_H_ +#define _UAPI_C3_ISP_CONFIG_H_ + +#include + +/* + * Frames are split into zones of almost equal width and height - a zone is a + * rectangular tile of a frame. The metering blocks within the ISP collect + * aggregated statistics per zone. + */ +#define C3_ISP_AE_MAX_ZONES (17 * 15) +#define C3_ISP_AF_MAX_ZONES (17 * 15) +#define C3_ISP_AWB_MAX_ZONES (32 * 24) + +/* The maximum number of point on the diagonal of the frame for statistics */ +#define C3_ISP_AE_MAX_PT_NUM 18 +#define C3_ISP_AF_MAX_PT_NUM 18 +#define C3_ISP_AWB_MAX_PT_NUM 33 + +/** + * struct c3_isp_awb_zone_stats - AWB statistics of a zone + * + * AWB zone stats is aligned with 8 bytes + * + * @rg: the ratio of R / G in a zone + * @bg: the ratio of B / G in a zone + * @pixel_sum: the total number of pixels used in a zone + */ +struct c3_isp_awb_zone_stats { + __u16 rg; + __u16 bg; + __u32 pixel_sum; +}; + +/** + * struct c3_isp_awb_stats - Auto white balance statistics information. + * + * AWB statistical information of all zones. + * + * @stats: array of auto white balance statistics + */ +struct c3_isp_awb_stats { + struct c3_isp_awb_zone_stats stats[C3_ISP_AWB_MAX_ZONES]; +} __attribute__((aligned(16))); + +/** + * struct c3_isp_ae_zone_stats - AE statistics of a zone + * + * AE zone stats is aligned with 8 bytes. + * This is a 5-bin histogram and the total sum is normalized to 0xffff. + * So hist2 = 0xffff - (hist0 + hist1 + hist3 + hist4) + * + * @hist0: the global normalized pixel count for bin 0 + * @hist1: the global normalized pixel count for bin 1 + * @hist3: the global normalized pixel count for bin 3 + * @hist4: the global normalized pixel count for bin 4 + */ +struct c3_isp_ae_zone_stats { + __u16 hist0; + __u16 hist1; + __u16 hist3; + __u16 hist4; +}; + +/** + * struct c3_isp_ae_stats - Exposure statistics information + * + * AE statistical information consists of all blocks information and a 1024-bin + * histogram. + * + * @stats: array of auto exposure block statistics + * @reserved: undefined buffer space + * @hist: a 1024-bin histogram for the entire image + */ +struct c3_isp_ae_stats { + struct c3_isp_ae_zone_stats stats[C3_ISP_AE_MAX_ZONES]; + __u32 reserved[2]; + __u32 hist[1024]; +} __attribute__((aligned(16))); + +/** + * struct c3_isp_af_zone_stats - AF statistics of a zone + * + * AF zone stats is aligned with 8 bytes. + * The zonal accumulated contrast metrics are stored in floating point format + * with 16 bits mantissa and 5 or 6 bits exponent. Apart from contrast metrics + * we accumulate squared image and quartic image data over the zone. + * + * @i2_mat: the mantissa of zonal squared image pixel sum + * @i4_mat: the mantissa of zonal quartic image pixel sum + * @e4_mat: the mantissa of zonal multi-directional quartic edge sum + * @e4_exp: the exponent of zonal multi-directional quartic edge sum + * @i2_exp: the exponent of zonal squared image pixel sum + * @i4_exp: the exponent of zonal quartic image pixel sum + */ +struct c3_isp_af_zone_stats { + __u16 i2_mat; + __u16 i4_mat; + __u16 e4_mat; + __u16 e4_exp : 5; + __u16 i2_exp : 5; + __u16 i4_exp : 6; +}; + +/** + * struct c3_isp_af_stats - Auto Focus statistics information + * + * AF statistical information of each zone + * + * @stats: array of auto focus block statistics + * @reserved: undefined buffer space + */ +struct c3_isp_af_stats { + struct c3_isp_af_zone_stats stats[C3_ISP_AF_MAX_ZONES]; + __u32 reserved[2]; +} __attribute__((aligned(16))); + +/** + * struct c3_isp_stats_info - V4L2_META_FMT_C3ISP_STATS + * + * Contains ISP statistics + * + * @awb: auto white balance stats + * @ae: auto exposure stats + * @af: auto focus stats + */ +struct c3_isp_stats_info { + struct c3_isp_awb_stats awb; + struct c3_isp_ae_stats ae; + struct c3_isp_af_stats af; +}; + +/** + * enum c3_isp_params_buffer_version - C3 ISP parameters block versioning + * + * @C3_ISP_PARAMS_BUFFER_V0: First version of C3 ISP parameters block + */ +enum c3_isp_params_buffer_version { + C3_ISP_PARAMS_BUFFER_V0, +}; + +/** + * enum c3_isp_params_block_type - Enumeration of C3 ISP parameter blocks + * + * Each block configures a specific processing block of the C3 ISP. + * The block type allows the driver to correctly interpret the parameters block + * data. + * + * @C3_ISP_PARAMS_BLOCK_AWB_GAINS: White balance gains + * @C3_ISP_PARAMS_BLOCK_AWB_CONFIG: AWB statistic format configuration for all + * blocks that control how stats are generated + * @C3_ISP_PARAMS_BLOCK_AE_CONFIG: AE statistic format configuration for all + * blocks that control how stats are generated + * @C3_ISP_PARAMS_BLOCK_AF_CONFIG: AF statistic format configuration for all + * blocks that control how stats are generated + * @C3_ISP_PARAMS_BLOCK_PST_GAMMA: post gamma parameters + * @C3_ISP_PARAMS_BLOCK_CCM: Color correction matrix parameters + * @C3_ISP_PARAMS_BLOCK_CSC: Color space conversion parameters + * @C3_ISP_PARAMS_BLOCK_BLC: Black level correction parameters + * @C3_ISP_PARAMS_BLOCK_SENTINEL: First non-valid block index + */ +enum c3_isp_params_block_type { + C3_ISP_PARAMS_BLOCK_AWB_GAINS, + C3_ISP_PARAMS_BLOCK_AWB_CONFIG, + C3_ISP_PARAMS_BLOCK_AE_CONFIG, + C3_ISP_PARAMS_BLOCK_AF_CONFIG, + C3_ISP_PARAMS_BLOCK_PST_GAMMA, + C3_ISP_PARAMS_BLOCK_CCM, + C3_ISP_PARAMS_BLOCK_CSC, + C3_ISP_PARAMS_BLOCK_BLC, + C3_ISP_PARAMS_BLOCK_SENTINEL +}; + +#define C3_ISP_PARAMS_BLOCK_FL_DISABLE (1U << 0) +#define C3_ISP_PARAMS_BLOCK_FL_ENABLE (1U << 1) + +/** + * struct c3_isp_params_block_header - C3 ISP parameter block header + * + * This structure represents the common part of all the ISP configuration + * blocks. Each parameters block shall embed an instance of this structure type + * as its first member, followed by the block-specific configuration data. The + * driver inspects this common header to discern the block type and its size and + * properly handle the block content by casting it to the correct block-specific + * type. + * + * The @type field is one of the values enumerated by + * :c:type:`c3_isp_params_block_type` and specifies how the data should be + * interpreted by the driver. The @size field specifies the size of the + * parameters block and is used by the driver for validation purposes. The + * @flags field is a bitmask of per-block flags C3_ISP_PARAMS_FL*. + * + * When userspace wants to disable an ISP block the + * C3_ISP_PARAMS_BLOCK_FL_DISABLED bit should be set in the @flags field. In + * this case userspace may optionally omit the remainder of the configuration + * block, which will be ignored by the driver. + * + * When a new configuration of an ISP block needs to be applied userspace + * shall fully populate the ISP block and omit setting the + * C3_ISP_PARAMS_BLOCK_FL_DISABLED bit in the @flags field. + * + * Userspace is responsible for correctly populating the parameters block header + * fields (@type, @flags and @size) and the block-specific parameters. + * + * For example: + * + * .. code-block:: c + * + * void populate_pst_gamma(struct c3_isp_params_block_header *block) { + * struct c3_isp_params_pst_gamma *gamma = + * (struct c3_isp_params_pst_gamma *)block; + * + * gamma->header.type = C3_ISP_PARAMS_BLOCK_PST_GAMMA; + * gamma->header.flags = C3_ISP_PARAMS_BLOCK_FL_ENABLE; + * gamma->header.size = sizeof(*gamma); + * + * for (unsigned int i = 0; i < 129; i++) + * gamma->pst_gamma_lut[i] = i; + * } + * + * @type: The parameters block type from :c:type:`c3_isp_params_block_type` + * @flags: A bitmask of block flags + * @size: Size (in bytes) of the parameters block, including this header + */ +struct c3_isp_params_block_header { + __u16 type; + __u16 flags; + __u32 size; +}; + +/** + * struct c3_isp_params_awb_gains - Gains for auto-white balance + * + * This struct allows users to configure the gains for white balance. + * There are four gain settings corresponding to each colour channel in + * the bayer domain. All of the gains are stored in Q4.8 format. + * + * header.type should be set to C3_ISP_PARAMS_BLOCK_AWB_GAINS + * from :c:type:`c3_isp_params_block_type` + * + * @header: The C3 ISP parameters block header + * @gr_gain: Multiplier for Gr channel (Q4.8 format) + * @r_gain: Multiplier for R channel (Q4.8 format) + * @b_gain: Multiplier for B channel (Q4.8 format) + * @gb_gain: Multiplier for Gb channel (Q4.8 format) + */ +struct c3_isp_params_awb_gains { + struct c3_isp_params_block_header header; + __u16 gr_gain; + __u16 r_gain; + __u16 b_gain; + __u16 gb_gain; +} __attribute__((aligned(8))); + +/** + * enum c3_isp_params_awb_tap_points - Tap points for the AWB statistics + * @C3_ISP_AWB_STATS_TAP_OFE: immediately after the optical frontend block + * @C3_ISP_AWB_STATS_TAP_GE: immediately after the green equal block + * @C3_ISP_AWB_STATS_TAP_BEFORE_WB: immediately before the white balance block + * @C3_ISP_AWB_STATS_TAP_AFTER_WB: immediately after the white balance block + */ +enum c3_isp_params_awb_tap_points { + C3_ISP_AWB_STATS_TAP_OFE = 0, + C3_ISP_AWB_STATS_TAP_GE, + C3_ISP_AWB_STATS_TAP_BEFORE_WB, + C3_ISP_AWB_STATS_TAP_AFTER_WB, +}; + +/** + * struct c3_isp_params_awb_config - Stats settings for auto-white balance + * + * This struct allows the configuration of the statistics generated for auto + * white balance. + * + * header.type should be set to C3_ISP_PARAMS_BLOCK_AWB_CONFIG + * from :c:type:`c3_isp_params_block_type` + * + * @header: the C3 ISP parameters block header + * @tap_point: the tap point from enum c3_isp_params_awb_tap_point + * @satur_vald: AWB statistic over saturation control + * value: 0: disable, 1: enable + * @horiz_zones_num: active number of hotizontal zones [0..32] + * @vert_zones_num: active number of vertical zones [0..24] + * @rg_min: minimum R/G ratio (Q4.8 format) + * @rg_max: maximum R/G ratio (Q4.8 format) + * @bg_min: minimum B/G ratio (Q4.8 format) + * @bg_max: maximum B/G ratio (Q4.8 format) + * @rg_low: R/G ratio trim low (Q4.8 format) + * @rg_high: R/G ratio trim hight (Q4.8 format) + * @bg_low: B/G ratio trim low (Q4.8 format) + * @bg_high: B/G ratio trim high (Q4.8 format) + * @zone_weight: array of weights for AWB statistics zones [0..15] + * @horiz_coord: the horizontal coordinate of points on the diagonal [0..2888] + * @vert_coord: the vertical coordinate of points on the diagonal [0..2240] + */ +struct c3_isp_params_awb_config { + struct c3_isp_params_block_header header; + __u8 tap_point; + __u8 satur_vald; + __u8 horiz_zones_num; + __u8 vert_zones_num; + __u16 rg_min; + __u16 rg_max; + __u16 bg_min; + __u16 bg_max; + __u16 rg_low; + __u16 rg_high; + __u16 bg_low; + __u16 bg_high; + __u8 zone_weight[C3_ISP_AWB_MAX_ZONES]; + __u16 horiz_coord[C3_ISP_AWB_MAX_PT_NUM]; + __u16 vert_coord[C3_ISP_AWB_MAX_PT_NUM]; +} __attribute__((aligned(8))); + +/** + * enum c3_isp_params_ae_tap_points - Tap points for the AE statistics + * @C3_ISP_AE_STATS_TAP_GE: immediately after the green equal block + * @C3_ISP_AE_STATS_TAP_MLS: immediately after the mesh lens shading block + */ +enum c3_isp_params_ae_tap_points { + C3_ISP_AE_STATS_TAP_GE = 0, + C3_ISP_AE_STATS_TAP_MLS, +}; + +/** + * struct c3_isp_params_ae_config - Stats settings for auto-exposure + * + * This struct allows the configuration of the statistics generated for + * auto exposure. + * + * header.type should be set to C3_ISP_PARAMS_BLOCK_AE_CONFIG + * from :c:type:`c3_isp_params_block_type` + * + * @header: the C3 ISP parameters block header + * @horiz_zones_num: active number of horizontal zones [0..17] + * @vert_zones_num: active number of vertical zones [0..15] + * @tap_point: the tap point from enum c3_isp_params_ae_tap_point + * @zone_weight: array of weights for AE statistics zones [0..15] + * @horiz_coord: the horizontal coordinate of points on the diagonal [0..2888] + * @vert_coord: the vertical coordinate of points on the diagonal [0..2240] + * @reserved: applications must zero this array + */ +struct c3_isp_params_ae_config { + struct c3_isp_params_block_header header; + __u8 tap_point; + __u8 horiz_zones_num; + __u8 vert_zones_num; + __u8 zone_weight[C3_ISP_AE_MAX_ZONES]; + __u16 horiz_coord[C3_ISP_AE_MAX_PT_NUM]; + __u16 vert_coord[C3_ISP_AE_MAX_PT_NUM]; + __u16 reserved[3]; +} __attribute__((aligned(8))); + +/** + * enum c3_isp_params_af_tap_points - Tap points for the AF statistics + * @C3_ISP_AF_STATS_TAP_SNR: immediately after the spatial noise reduce block + * @C3_ISP_AF_STATS_TAP_DMS: immediately after the demosaic block + */ +enum c3_isp_params_af_tap_points { + C3_ISP_AF_STATS_TAP_SNR = 0, + C3_ISP_AF_STATS_TAP_DMS, +}; + +/** + * struct c3_isp_params_af_config - Stats settings for auto-focus + * + * This struct allows the configuration of the statistics generated for + * auto focus. + * + * header.type should be set to C3_ISP_PARAMS_BLOCK_AF_CONFIG + * from :c:type:`c3_isp_params_block_type` + * + * @header: the C3 ISP parameters block header + * @tap_point: the tap point from enum c3_isp_params_af_tap_point + * @horiz_zones_num: active number of hotizontal zones [0..17] + * @vert_zones_num: active number of vertical zones [0..15] + * @reserved: applications must zero this array + * @horiz_coord: the horizontal coordinate of points on the diagonal [0..2888] + * @vert_coord: the vertical coordinate of points on the diagonal [0..2240] + */ +struct c3_isp_params_af_config { + struct c3_isp_params_block_header header; + __u8 tap_point; + __u8 horiz_zones_num; + __u8 vert_zones_num; + __u8 reserved[5]; + __u16 horiz_coord[C3_ISP_AF_MAX_PT_NUM]; + __u16 vert_coord[C3_ISP_AF_MAX_PT_NUM]; +} __attribute__((aligned(8))); + +/** + * struct c3_isp_params_pst_gamma - Post gamma configuration + * + * This struct allows the configuration of the look up table for + * post gamma. The gamma curve consists of 129 points, so need to + * set lut[129]. + * + * header.type should be set to C3_ISP_PARAMS_BLOCK_PST_GAMMA + * from :c:type:`c3_isp_params_block_type` + * + * @header: the C3 ISP parameters block header + * @lut: lookup table for P-Stitch gamma [0..1023] + * @reserved: applications must zero this array + */ +struct c3_isp_params_pst_gamma { + struct c3_isp_params_block_header header; + __u16 lut[129]; + __u16 reserved[3]; +} __attribute__((aligned(8))); + +/** + * struct c3_isp_params_ccm - ISP CCM configuration + * + * This struct allows the configuration of the matrix for + * color correction. The matrix consists of 3 x 3 points, + * so need to set matrix[3][3]. + * + * header.type should be set to C3_ISP_PARAMS_BLOCK_CCM + * from :c:type:`c3_isp_params_block_type` + * + * @header: the C3 ISP parameters block header + * @matrix: a 3 x 3 matrix used for color correction, + * the value of matrix[x][y] is orig_value x 256. [-4096..4095] + * @reserved: applications must zero this array + */ +struct c3_isp_params_ccm { + struct c3_isp_params_block_header header; + __s16 matrix[3][3]; + __u16 reserved[3]; +} __attribute__((aligned(8))); + +/** + * struct c3_isp_params_csc - ISP Color Space Conversion configuration + * + * This struct allows the configuration of the matrix for color space + * conversion. The matrix consists of 3 x 3 points, so need to set matrix[3][3]. + * + * header.type should be set to C3_ISP_PARAMS_BLOCK_CSC + * from :c:type:`c3_isp_params_block_type` + * + * @header: the C3 ISP parameters block header + * @matrix: a 3x3 matrix used for the color space conversion, + * the value of matrix[x][y] is orig_value x 256. [-4096..4095] + * @reserved: applications must zero this array + */ +struct c3_isp_params_csc { + struct c3_isp_params_block_header header; + __s16 matrix[3][3]; + __u16 reserved[3]; +} __attribute__((aligned(8))); + +/** + * struct c3_isp_params_blc - ISP Black Level Correction configuration + * + * This struct allows the configuration of the block level offset for each + * color channel. + * + * header.type should be set to C3_ISP_PARAMS_BLOCK_BLC + * from :c:type:`c3_isp_params_block_type` + * + * @header: the C3 ISP parameters block header + * @gr_ofst: Gr blc offset (Q4.12 format) + * @r_ofst: R blc offset (Q4.12 format) + * @b_ofst: B blc offset (Q4.12 format) + * @gb_ofst: Gb blc offset(Q4.12 format) + */ +struct c3_isp_params_blc { + struct c3_isp_params_block_header header; + __u16 gr_ofst; + __u16 r_ofst; + __u16 b_ofst; + __u16 gb_ofst; +}; + +/** + * define C3_ISP_PARAMS_MAX_SIZE - Maximum size of all C3 ISP Parameters + * + * Though the parameters for the C3 ISP are passed as optional blocks, the + * driver still needs to know the absolute maximum size so that it can allocate + * a buffer sized appropriately to accommodate userspace attempting to set all + * possible parameters in a single frame. + */ +#define C3_ISP_PARAMS_MAX_SIZE \ + (sizeof(struct c3_isp_params_awb_gains) + \ + sizeof(struct c3_isp_params_awb_config) + \ + sizeof(struct c3_isp_params_ae_config) + \ + sizeof(struct c3_isp_params_af_config) + \ + sizeof(struct c3_isp_params_pst_gamma) + \ + sizeof(struct c3_isp_params_ccm) + \ + sizeof(struct c3_isp_params_csc) + \ + sizeof(struct c3_isp_params_blc)) + +/** + * struct c3_isp_params_cfg - C3 ISP configuration parameters + * + * This struct contains the configuration parameters of the C3 ISP + * algorithms, serialized by userspace into an opaque data buffer. Each + * configuration parameter block is represented by a block-specific structure + * which contains a :c:type:`c3_isp_param_block_header` entry as first + * member. Userspace populates the @data buffer with configuration parameters + * for the blocks that it intends to configure. As a consequence, the data + * buffer effective size changes according to the number of ISP blocks that + * userspace intends to configure. + * + * The parameters buffer is versioned by the @version field to allow modifying + * and extending its definition. Userspace should populate the @version field to + * inform the driver about the version it intends to use. The driver will parse + * and handle the @data buffer according to the data layout specific to the + * indicated revision and return an error if the desired revision is not + * supported. + * + * For each ISP block that userspace wants to configure, a block-specific + * structure is appended to the @data buffer, one after the other without gaps + * in between nor overlaps. Userspace shall populate the @total_size field with + * the effective size, in bytes, of the @data buffer. + * + * The expected memory layout of the parameters buffer is:: + * + * +-------------------- struct c3_isp_params_cfg ---- ------------------+ + * | version = C3_ISP_PARAM_BUFFER_V0; | + * | data_size = sizeof(struct c3_isp_params_awb_gains) + | + * | sizeof(struct c3_isp_params_awb_config); | + * | +------------------------- data ---------------------------------+ | + * | | +------------ struct c3_isp_params_awb_gains) ------------------+ | + * | | | +--------- struct c3_isp_params_block_header header -----+ | | | + * | | | | type = C3_ISP_PARAMS_BLOCK_AWB_GAINS; | | | | + * | | | | flags = C3_ISP_PARAMS_BLOCK_FL_NONE; | | | | + * | | | | size = sizeof(struct c3_isp_params_awb_gains); | | | | + * | | | +---------------------------------------------------------+ | | | + * | | | gr_gain = ...; | | | + * | | | r_gain = ...; | | | + * | | | b_gain = ...; | | | + * | | | gb_gain = ...; | | | + * | | +------------------ struct c3_isp_params_awb_config ----------+ | | + * | | | +---------- struct c3_isp_param_block_header header ------+ | | | + * | | | | type = C3_ISP_PARAMS_BLOCK_AWB_CONFIG; | | | | + * | | | | flags = C3_ISP_PARAMS_BLOCK_FL_NONE; | | | | + * | | | | size = sizeof(struct c3_isp_params_awb_config) | | | | + * | | | +---------------------------------------------------------+ | | | + * | | | tap_point = ...; | | | + * | | | satur_vald = ...; | | | + * | | | horiz_zones_num = ...; | | | + * | | | vert_zones_num = ...; | | | + * | | +-------------------------------------------------------------+ | | + * | +-----------------------------------------------------------------+ | + * +---------------------------------------------------------------------+ + * + * @version: The C3 ISP parameters buffer version + * @data_size: The C3 ISP configuration data effective size, excluding this + * header + * @data: The C3 ISP configuration blocks data + */ +struct c3_isp_params_cfg { + __u32 version; + __u32 data_size; + __u8 data[C3_ISP_PARAMS_MAX_SIZE]; +}; + +#endif -- cgit v1.2.3-59-g8ed1b From fb2e135208f364f26b7579c191434d38655e813f Mon Sep 17 00:00:00 2001 From: Keke Li Date: Sun, 27 Apr 2025 14:27:16 +0800 Subject: media: platform: Add C3 ISP driver The C3 ISP supports multi-camera and multi-exposure HDR, integrates advanced imaging technologies for optimal quality, and drives the core pipeline to transform raw sensor data into high-fidelity images through demosaicing, color correction, and tone mapping operations. Reviewed-by: Jacopo Mondi Signed-off-by: Keke Li Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil [hverkuil: drop unnecessary vb2_ops_wait_prepare/finish callbacks] --- MAINTAINERS | 1 + drivers/media/platform/amlogic/c3/Kconfig | 1 + drivers/media/platform/amlogic/c3/Makefile | 1 + drivers/media/platform/amlogic/c3/isp/Kconfig | 18 + drivers/media/platform/amlogic/c3/isp/Makefile | 10 + .../media/platform/amlogic/c3/isp/c3-isp-capture.c | 804 ++++++++++++++++ .../media/platform/amlogic/c3/isp/c3-isp-common.h | 340 +++++++ .../media/platform/amlogic/c3/isp/c3-isp-core.c | 641 +++++++++++++ drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c | 421 ++++++++ .../media/platform/amlogic/c3/isp/c3-isp-params.c | 1008 ++++++++++++++++++++ .../media/platform/amlogic/c3/isp/c3-isp-regs.h | 618 ++++++++++++ .../media/platform/amlogic/c3/isp/c3-isp-resizer.c | 892 +++++++++++++++++ .../media/platform/amlogic/c3/isp/c3-isp-stats.c | 326 +++++++ 13 files changed, 5081 insertions(+) create mode 100644 drivers/media/platform/amlogic/c3/isp/Kconfig create mode 100644 drivers/media/platform/amlogic/c3/isp/Makefile create mode 100644 drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c create mode 100644 drivers/media/platform/amlogic/c3/isp/c3-isp-common.h create mode 100644 drivers/media/platform/amlogic/c3/isp/c3-isp-core.c create mode 100644 drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c create mode 100644 drivers/media/platform/amlogic/c3/isp/c3-isp-params.c create mode 100644 drivers/media/platform/amlogic/c3/isp/c3-isp-regs.h create mode 100644 drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c create mode 100644 drivers/media/platform/amlogic/c3/isp/c3-isp-stats.c diff --git a/MAINTAINERS b/MAINTAINERS index e36c2ceaca79..e4795e990fbb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1259,6 +1259,7 @@ M: Keke Li L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/amlogic,c3-isp.yaml +F: drivers/media/platform/amlogic/c3/isp/ F: include/uapi/linux/media/amlogic/ AMLOGIC MIPI ADAPTER DRIVER diff --git a/drivers/media/platform/amlogic/c3/Kconfig b/drivers/media/platform/amlogic/c3/Kconfig index a504a1eb22e6..d355d3a9358d 100644 --- a/drivers/media/platform/amlogic/c3/Kconfig +++ b/drivers/media/platform/amlogic/c3/Kconfig @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +source "drivers/media/platform/amlogic/c3/isp/Kconfig" source "drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig" source "drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig" diff --git a/drivers/media/platform/amlogic/c3/Makefile b/drivers/media/platform/amlogic/c3/Makefile index 770b2a2903ad..14f305a493d2 100644 --- a/drivers/media/platform/amlogic/c3/Makefile +++ b/drivers/media/platform/amlogic/c3/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-y += isp/ obj-y += mipi-adapter/ obj-y += mipi-csi2/ diff --git a/drivers/media/platform/amlogic/c3/isp/Kconfig b/drivers/media/platform/amlogic/c3/isp/Kconfig new file mode 100644 index 000000000000..02c62a50a5e8 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/isp/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_C3_ISP + tristate "Amlogic C3 Image Signal Processor (ISP) driver" + depends on ARCH_MESON || COMPILE_TEST + depends on VIDEO_DEV + depends on OF + select MEDIA_CONTROLLER + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + help + Video4Linux2 driver for Amlogic C3 ISP pipeline. + The C3 ISP is used for processing raw images and + outputing results to memory. + + To compile this driver as a module choose m here. diff --git a/drivers/media/platform/amlogic/c3/isp/Makefile b/drivers/media/platform/amlogic/c3/isp/Makefile new file mode 100644 index 000000000000..b1b064170b57 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/isp/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only + +c3-isp-objs := c3-isp-dev.o \ + c3-isp-params.o \ + c3-isp-stats.o \ + c3-isp-capture.o \ + c3-isp-core.o \ + c3-isp-resizer.o + +obj-$(CONFIG_VIDEO_C3_ISP) += c3-isp.o diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c new file mode 100644 index 000000000000..11d85f5342f0 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c @@ -0,0 +1,804 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "c3-isp-common.h" +#include "c3-isp-regs.h" + +#define C3_ISP_WRMIFX3_REG(addr, id) ((addr) + (id) * 0x100) + +static const struct c3_isp_cap_format_info cap_formats[] = { + /* YUV formats */ + { + .mbus_code = MEDIA_BUS_FMT_YUV10_1X30, + .fourcc = V4L2_PIX_FMT_GREY, + .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_Y_ONLY, + .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1, + .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS, + .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU, + .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT, + .hdiv = 1, + .vdiv = 1, + }, { + .mbus_code = MEDIA_BUS_FMT_YUV10_1X30, + .fourcc = V4L2_PIX_FMT_NV12M, + .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV420, + .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2, + .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS, + .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_UV, + .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT, + .hdiv = 2, + .vdiv = 2, + }, { + .mbus_code = MEDIA_BUS_FMT_YUV10_1X30, + .fourcc = V4L2_PIX_FMT_NV21M, + .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV420, + .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2, + .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS, + .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU, + .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT, + .hdiv = 2, + .vdiv = 2, + }, { + .mbus_code = MEDIA_BUS_FMT_YUV10_1X30, + .fourcc = V4L2_PIX_FMT_NV16M, + .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV422, + .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2, + .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS, + .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_UV, + .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT, + .hdiv = 1, + .vdiv = 2 + }, { + .mbus_code = MEDIA_BUS_FMT_YUV10_1X30, + .fourcc = V4L2_PIX_FMT_NV61M, + .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV422, + .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2, + .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS, + .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU, + .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT, + .hdiv = 1, + .vdiv = 2, + }, + /* RAW formats */ + { + .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16, + .fourcc = V4L2_PIX_FMT_SRGGB12, + .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW, + .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1, + .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS, + .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU, + .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT, + .hdiv = 1, + .vdiv = 1, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16, + .fourcc = V4L2_PIX_FMT_SBGGR12, + .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW, + .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1, + .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS, + .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU, + .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT, + .hdiv = 1, + .vdiv = 1, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16, + .fourcc = V4L2_PIX_FMT_SGRBG12, + .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW, + .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1, + .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS, + .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU, + .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT, + .hdiv = 1, + .vdiv = 1, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16, + .fourcc = V4L2_PIX_FMT_SGBRG12, + .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW, + .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1, + .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS, + .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU, + .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT, + .hdiv = 1, + .vdiv = 1, + }, +}; + +/* Hardware configuration */ + +/* Set the address of wrmifx3(write memory interface) */ +static void c3_isp_cap_wrmifx3_buff(struct c3_isp_capture *cap) +{ + dma_addr_t y_dma_addr; + dma_addr_t uv_dma_addr; + + if (cap->buff) { + y_dma_addr = cap->buff->dma_addr[C3_ISP_PLANE_Y]; + uv_dma_addr = cap->buff->dma_addr[C3_ISP_PLANE_UV]; + } else { + y_dma_addr = cap->dummy_buff.dma_addr; + uv_dma_addr = cap->dummy_buff.dma_addr; + } + + c3_isp_write(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH0_BADDR, cap->id), + ISP_WRMIFX3_0_CH0_BASE_ADDR(y_dma_addr)); + + c3_isp_write(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH1_BADDR, cap->id), + ISP_WRMIFX3_0_CH1_BASE_ADDR(uv_dma_addr)); +} + +static void c3_isp_cap_wrmifx3_format(struct c3_isp_capture *cap) +{ + struct v4l2_pix_format_mplane *pix_mp = &cap->format.pix_mp; + const struct c3_isp_cap_format_info *info = cap->format.info; + u32 stride; + u32 chrom_h; + u32 chrom_v; + + c3_isp_write(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_SIZE, cap->id), + ISP_WRMIFX3_0_FMT_SIZE_HSIZE(pix_mp->width) | + ISP_WRMIFX3_0_FMT_SIZE_VSIZE(pix_mp->height)); + + c3_isp_update_bits(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_CTRL, cap->id), + ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_MASK, info->format); + + c3_isp_update_bits(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_CTRL, cap->id), + ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_MASK, + info->in_bits); + + c3_isp_update_bits(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_CTRL, cap->id), + ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_MASK, info->planes); + + c3_isp_update_bits(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_CTRL, cap->id), + ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_MASK, + info->uv_swap); + + stride = DIV_ROUND_UP(pix_mp->plane_fmt[C3_ISP_PLANE_Y].bytesperline, + C3_ISP_DMA_SIZE_ALIGN_BYTES); + c3_isp_update_bits(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH0_CTRL0, cap->id), + ISP_WRMIFX3_0_CH0_CTRL0_STRIDE_MASK, + ISP_WRMIFX3_0_CH0_CTRL0_STRIDE(stride)); + + c3_isp_update_bits(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH0_CTRL1, cap->id), + ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_MODE_MASK, + info->ch0_pix_bits); + + c3_isp_write(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_WIN_LUMA_H, cap->id), + ISP_WRMIFX3_0_WIN_LUMA_H_LUMA_HEND(pix_mp->width)); + + c3_isp_write(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_WIN_LUMA_V, cap->id), + ISP_WRMIFX3_0_WIN_LUMA_V_LUMA_VEND(pix_mp->height)); + + stride = DIV_ROUND_UP(pix_mp->plane_fmt[C3_ISP_PLANE_UV].bytesperline, + C3_ISP_DMA_SIZE_ALIGN_BYTES); + c3_isp_update_bits(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH1_CTRL0, cap->id), + ISP_WRMIFX3_0_CH1_CTRL0_STRIDE_MASK, + ISP_WRMIFX3_0_CH1_CTRL0_STRIDE(stride)); + + c3_isp_update_bits(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH1_CTRL1, cap->id), + ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_MODE_MASK, + ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_16BITS); + + chrom_h = DIV_ROUND_UP(pix_mp->width, info->hdiv); + c3_isp_write(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_WIN_CHROM_H, cap->id), + ISP_WRMIFX3_0_WIN_CHROM_H_CHROM_HEND(chrom_h)); + + chrom_v = DIV_ROUND_UP(pix_mp->height, info->vdiv); + c3_isp_write(cap->isp, + C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_WIN_CHROM_V, cap->id), + ISP_WRMIFX3_0_WIN_CHROM_V_CHROM_VEND(chrom_v)); +} + +static int c3_isp_cap_dummy_buff_create(struct c3_isp_capture *cap) +{ + struct c3_isp_dummy_buffer *dummy_buff = &cap->dummy_buff; + struct v4l2_pix_format_mplane *pix_mp = &cap->format.pix_mp; + + if (pix_mp->num_planes == 1) + dummy_buff->size = pix_mp->plane_fmt[C3_ISP_PLANE_Y].sizeimage; + else + dummy_buff->size = + max(pix_mp->plane_fmt[C3_ISP_PLANE_Y].sizeimage, + pix_mp->plane_fmt[C3_ISP_PLANE_UV].sizeimage); + + /* The driver never access vaddr, no mapping is required */ + dummy_buff->vaddr = dma_alloc_attrs(cap->isp->dev, dummy_buff->size, + &dummy_buff->dma_addr, GFP_KERNEL, + DMA_ATTR_NO_KERNEL_MAPPING); + if (!dummy_buff->vaddr) + return -ENOMEM; + + return 0; +} + +static void c3_isp_cap_dummy_buff_destroy(struct c3_isp_capture *cap) +{ + dma_free_attrs(cap->isp->dev, cap->dummy_buff.size, + cap->dummy_buff.vaddr, cap->dummy_buff.dma_addr, + DMA_ATTR_NO_KERNEL_MAPPING); +} + +static void c3_isp_cap_cfg_buff(struct c3_isp_capture *cap) +{ + cap->buff = list_first_entry_or_null(&cap->pending, + struct c3_isp_cap_buffer, list); + + c3_isp_cap_wrmifx3_buff(cap); + + if (cap->buff) + list_del(&cap->buff->list); +} + +static void c3_isp_cap_start(struct c3_isp_capture *cap) +{ + u32 mask; + u32 val; + + scoped_guard(spinlock_irqsave, &cap->buff_lock) + c3_isp_cap_cfg_buff(cap); + + c3_isp_cap_wrmifx3_format(cap); + + if (cap->id == C3_ISP_CAP_DEV_0) { + mask = ISP_TOP_PATH_EN_WRMIF0_EN_MASK; + val = ISP_TOP_PATH_EN_WRMIF0_EN; + } else if (cap->id == C3_ISP_CAP_DEV_1) { + mask = ISP_TOP_PATH_EN_WRMIF1_EN_MASK; + val = ISP_TOP_PATH_EN_WRMIF1_EN; + } else { + mask = ISP_TOP_PATH_EN_WRMIF2_EN_MASK; + val = ISP_TOP_PATH_EN_WRMIF2_EN; + } + + c3_isp_update_bits(cap->isp, ISP_TOP_PATH_EN, mask, val); +} + +static void c3_isp_cap_stop(struct c3_isp_capture *cap) +{ + u32 mask; + u32 val; + + if (cap->id == C3_ISP_CAP_DEV_0) { + mask = ISP_TOP_PATH_EN_WRMIF0_EN_MASK; + val = ISP_TOP_PATH_EN_WRMIF0_DIS; + } else if (cap->id == C3_ISP_CAP_DEV_1) { + mask = ISP_TOP_PATH_EN_WRMIF1_EN_MASK; + val = ISP_TOP_PATH_EN_WRMIF1_DIS; + } else { + mask = ISP_TOP_PATH_EN_WRMIF2_EN_MASK; + val = ISP_TOP_PATH_EN_WRMIF2_DIS; + } + + c3_isp_update_bits(cap->isp, ISP_TOP_PATH_EN, mask, val); +} + +static void c3_isp_cap_done(struct c3_isp_capture *cap) +{ + struct c3_isp_cap_buffer *buff = cap->buff; + + guard(spinlock_irqsave)(&cap->buff_lock); + + if (buff) { + buff->vb.sequence = cap->isp->frm_sequence; + buff->vb.vb2_buf.timestamp = ktime_get(); + buff->vb.field = V4L2_FIELD_NONE; + vb2_buffer_done(&buff->vb.vb2_buf, VB2_BUF_STATE_DONE); + } + + c3_isp_cap_cfg_buff(cap); +} + +/* V4L2 video operations */ + +static const struct c3_isp_cap_format_info *c3_cap_find_fmt(u32 fourcc) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(cap_formats); i++) { + if (cap_formats[i].fourcc == fourcc) + return &cap_formats[i]; + } + + return NULL; +} + +static void c3_cap_try_fmt(struct v4l2_pix_format_mplane *pix_mp) +{ + const struct c3_isp_cap_format_info *fmt; + const struct v4l2_format_info *info; + struct v4l2_plane_pix_format *plane; + + fmt = c3_cap_find_fmt(pix_mp->pixelformat); + if (!fmt) + fmt = &cap_formats[0]; + + pix_mp->width = clamp(pix_mp->width, C3_ISP_MIN_WIDTH, + C3_ISP_MAX_WIDTH); + pix_mp->height = clamp(pix_mp->height, C3_ISP_MIN_HEIGHT, + C3_ISP_MAX_HEIGHT); + pix_mp->pixelformat = fmt->fourcc; + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->colorspace = V4L2_COLORSPACE_SRGB; + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_601; + pix_mp->quantization = V4L2_QUANTIZATION_LIM_RANGE; + + info = v4l2_format_info(fmt->fourcc); + pix_mp->num_planes = info->mem_planes; + memset(pix_mp->plane_fmt, 0, sizeof(pix_mp->plane_fmt)); + + for (unsigned int i = 0; i < info->comp_planes; i++) { + unsigned int hdiv = (i == 0) ? 1 : info->hdiv; + unsigned int vdiv = (i == 0) ? 1 : info->vdiv; + + plane = &pix_mp->plane_fmt[i]; + + plane->bytesperline = DIV_ROUND_UP(pix_mp->width, hdiv) * + info->bpp[i] / info->bpp_div[i]; + plane->bytesperline = ALIGN(plane->bytesperline, + C3_ISP_DMA_SIZE_ALIGN_BYTES); + plane->sizeimage = plane->bytesperline * + DIV_ROUND_UP(pix_mp->height, vdiv); + } +} + +static void c3_isp_cap_return_buffers(struct c3_isp_capture *cap, + enum vb2_buffer_state state) +{ + struct c3_isp_cap_buffer *buff; + + guard(spinlock_irqsave)(&cap->buff_lock); + + if (cap->buff) { + vb2_buffer_done(&cap->buff->vb.vb2_buf, state); + cap->buff = NULL; + } + + while (!list_empty(&cap->pending)) { + buff = list_first_entry(&cap->pending, + struct c3_isp_cap_buffer, list); + list_del(&buff->list); + vb2_buffer_done(&buff->vb.vb2_buf, state); + } +} + +static int c3_isp_cap_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, C3_ISP_DRIVER_NAME, sizeof(cap->driver)); + strscpy(cap->card, "AML C3 ISP", sizeof(cap->card)); + + return 0; +} + +static int c3_isp_cap_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + const struct c3_isp_cap_format_info *fmt; + unsigned int index = 0; + unsigned int i; + + if (!f->mbus_code) { + if (f->index >= ARRAY_SIZE(cap_formats)) + return -EINVAL; + + fmt = &cap_formats[f->index]; + f->pixelformat = fmt->fourcc; + return 0; + } + + for (i = 0; i < ARRAY_SIZE(cap_formats); i++) { + fmt = &cap_formats[i]; + if (f->mbus_code != fmt->mbus_code) + continue; + + if (index++ == f->index) { + f->pixelformat = cap_formats[i].fourcc; + return 0; + } + } + + return -EINVAL; +} + +static int c3_isp_cap_g_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct c3_isp_capture *cap = video_drvdata(file); + + f->fmt.pix_mp = cap->format.pix_mp; + + return 0; +} + +static int c3_isp_cap_s_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct c3_isp_capture *cap = video_drvdata(file); + + c3_cap_try_fmt(&f->fmt.pix_mp); + + cap->format.pix_mp = f->fmt.pix_mp; + cap->format.info = c3_cap_find_fmt(f->fmt.pix_mp.pixelformat); + + return 0; +} + +static int c3_isp_cap_try_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + c3_cap_try_fmt(&f->fmt.pix_mp); + + return 0; +} + +static int c3_isp_cap_enum_frmsize(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + const struct c3_isp_cap_format_info *fmt; + + if (fsize->index) + return -EINVAL; + + fmt = c3_cap_find_fmt(fsize->pixel_format); + if (!fmt) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = C3_ISP_MIN_WIDTH; + fsize->stepwise.min_height = C3_ISP_MIN_HEIGHT; + fsize->stepwise.max_width = C3_ISP_MAX_WIDTH; + fsize->stepwise.max_height = C3_ISP_MAX_HEIGHT; + fsize->stepwise.step_width = 2; + fsize->stepwise.step_height = 2; + + return 0; +} + +static const struct v4l2_ioctl_ops isp_cap_v4l2_ioctl_ops = { + .vidioc_querycap = c3_isp_cap_querycap, + .vidioc_enum_fmt_vid_cap = c3_isp_cap_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = c3_isp_cap_g_fmt_mplane, + .vidioc_s_fmt_vid_cap_mplane = c3_isp_cap_s_fmt_mplane, + .vidioc_try_fmt_vid_cap_mplane = c3_isp_cap_try_fmt_mplane, + .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_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_framesizes = c3_isp_cap_enum_frmsize, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations isp_cap_v4l2_fops = { + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int c3_isp_cap_link_validate(struct media_link *link) +{ + struct video_device *vdev = + media_entity_to_video_device(link->sink->entity); + struct v4l2_subdev *sd = + media_entity_to_v4l2_subdev(link->source->entity); + struct c3_isp_capture *cap = video_get_drvdata(vdev); + struct v4l2_subdev_format src_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = link->source->index, + }; + int ret; + + ret = v4l2_subdev_call_state_active(sd, pad, get_fmt, &src_fmt); + if (ret) + return ret; + + if (src_fmt.format.width != cap->format.pix_mp.width || + src_fmt.format.height != cap->format.pix_mp.height || + src_fmt.format.code != cap->format.info->mbus_code) { + dev_err(cap->isp->dev, + "link %s: %u -> %s: %u not valid: 0x%04x/%ux%u not match 0x%04x/%ux%u\n", + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index, + src_fmt.format.code, src_fmt.format.width, + src_fmt.format.height, cap->format.info->mbus_code, + cap->format.pix_mp.width, cap->format.pix_mp.height); + + return -EPIPE; + } + + return 0; +} + +static const struct media_entity_operations isp_cap_entity_ops = { + .link_validate = c3_isp_cap_link_validate, +}; + +static int c3_isp_vb2_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct c3_isp_capture *cap = vb2_get_drv_priv(q); + const struct v4l2_pix_format_mplane *pix_mp = &cap->format.pix_mp; + unsigned int i; + + if (*num_planes) { + if (*num_planes != pix_mp->num_planes) + return -EINVAL; + + for (i = 0; i < pix_mp->num_planes; i++) + if (sizes[i] < pix_mp->plane_fmt[i].sizeimage) + return -EINVAL; + + return 0; + } + + *num_planes = pix_mp->num_planes; + for (i = 0; i < pix_mp->num_planes; i++) + sizes[i] = pix_mp->plane_fmt[i].sizeimage; + + return 0; +} + +static void c3_isp_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb); + struct c3_isp_cap_buffer *buf = + container_of(v4l2_buf, struct c3_isp_cap_buffer, vb); + struct c3_isp_capture *cap = vb2_get_drv_priv(vb->vb2_queue); + + guard(spinlock_irqsave)(&cap->buff_lock); + + list_add_tail(&buf->list, &cap->pending); +} + +static int c3_isp_vb2_buf_prepare(struct vb2_buffer *vb) +{ + struct c3_isp_capture *cap = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size; + + for (unsigned int i = 0; i < cap->format.pix_mp.num_planes; i++) { + size = cap->format.pix_mp.plane_fmt[i].sizeimage; + if (vb2_plane_size(vb, i) < size) { + dev_err(cap->isp->dev, + "User buffer too small (%ld < %lu)\n", + vb2_plane_size(vb, i), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, i, size); + } + + return 0; +} + +static int c3_isp_vb2_buf_init(struct vb2_buffer *vb) +{ + struct c3_isp_capture *cap = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb); + struct c3_isp_cap_buffer *buf = + container_of(v4l2_buf, struct c3_isp_cap_buffer, vb); + + for (unsigned int i = 0; i < cap->format.pix_mp.num_planes; i++) + buf->dma_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); + + return 0; +} + +static int c3_isp_vb2_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + struct c3_isp_capture *cap = vb2_get_drv_priv(q); + int ret; + + ret = video_device_pipeline_start(&cap->vdev, &cap->isp->pipe); + if (ret) { + dev_err(cap->isp->dev, + "Failed to start cap%u pipeline: %d\n", cap->id, ret); + goto err_return_buffers; + } + + ret = c3_isp_cap_dummy_buff_create(cap); + if (ret) + goto err_pipeline_stop; + + ret = pm_runtime_resume_and_get(cap->isp->dev); + if (ret) + goto err_dummy_destroy; + + c3_isp_cap_start(cap); + + ret = v4l2_subdev_enable_streams(&cap->rsz->sd, C3_ISP_RSZ_PAD_SOURCE, + BIT(0)); + if (ret) + goto err_pm_put; + + return 0; + +err_pm_put: + pm_runtime_put(cap->isp->dev); +err_dummy_destroy: + c3_isp_cap_dummy_buff_destroy(cap); +err_pipeline_stop: + video_device_pipeline_stop(&cap->vdev); +err_return_buffers: + c3_isp_cap_return_buffers(cap, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void c3_isp_vb2_stop_streaming(struct vb2_queue *q) +{ + struct c3_isp_capture *cap = vb2_get_drv_priv(q); + + c3_isp_cap_stop(cap); + + c3_isp_cap_return_buffers(cap, VB2_BUF_STATE_ERROR); + + v4l2_subdev_disable_streams(&cap->rsz->sd, C3_ISP_RSZ_PAD_SOURCE, + BIT(0)); + + pm_runtime_put(cap->isp->dev); + + c3_isp_cap_dummy_buff_destroy(cap); + + video_device_pipeline_stop(&cap->vdev); +} + +static const struct vb2_ops isp_video_vb2_ops = { + .queue_setup = c3_isp_vb2_queue_setup, + .buf_queue = c3_isp_vb2_buf_queue, + .buf_prepare = c3_isp_vb2_buf_prepare, + .buf_init = c3_isp_vb2_buf_init, + .start_streaming = c3_isp_vb2_start_streaming, + .stop_streaming = c3_isp_vb2_stop_streaming, +}; + +static int c3_isp_register_capture(struct c3_isp_capture *cap) +{ + struct video_device *vdev = &cap->vdev; + struct vb2_queue *vb2_q = &cap->vb2_q; + int ret; + + snprintf(vdev->name, sizeof(vdev->name), "c3-isp-cap%u", cap->id); + vdev->fops = &isp_cap_v4l2_fops; + vdev->ioctl_ops = &isp_cap_v4l2_ioctl_ops; + vdev->v4l2_dev = &cap->isp->v4l2_dev; + vdev->entity.ops = &isp_cap_entity_ops; + vdev->lock = &cap->lock; + vdev->minor = -1; + vdev->queue = vb2_q; + vdev->release = video_device_release_empty; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_STREAMING; + vdev->vfl_dir = VFL_DIR_RX; + video_set_drvdata(vdev, cap); + + vb2_q->drv_priv = cap; + vb2_q->mem_ops = &vb2_dma_contig_memops; + vb2_q->ops = &isp_video_vb2_ops; + vb2_q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + vb2_q->io_modes = VB2_DMABUF | VB2_MMAP; + vb2_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vb2_q->buf_struct_size = sizeof(struct c3_isp_cap_buffer); + vb2_q->dev = cap->isp->dev; + vb2_q->lock = &cap->lock; + + ret = vb2_queue_init(vb2_q); + if (ret) + goto err_destroy; + + cap->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vdev->entity, 1, &cap->pad); + if (ret) + goto err_queue_release; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(cap->isp->dev, + "Failed to register %s: %d\n", vdev->name, ret); + goto err_entity_cleanup; + } + + return 0; + +err_entity_cleanup: + media_entity_cleanup(&vdev->entity); +err_queue_release: + vb2_queue_release(vb2_q); +err_destroy: + mutex_destroy(&cap->lock); + return ret; +} + +int c3_isp_captures_register(struct c3_isp_device *isp) +{ + int ret; + unsigned int i; + struct c3_isp_capture *cap; + + for (i = C3_ISP_CAP_DEV_0; i < C3_ISP_NUM_CAP_DEVS; i++) { + cap = &isp->caps[i]; + memset(cap, 0, sizeof(*cap)); + + cap->format.pix_mp.width = C3_ISP_DEFAULT_WIDTH; + cap->format.pix_mp.height = C3_ISP_DEFAULT_HEIGHT; + cap->format.pix_mp.field = V4L2_FIELD_NONE; + cap->format.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M; + cap->format.pix_mp.colorspace = V4L2_COLORSPACE_SRGB; + cap->format.info = + c3_cap_find_fmt(cap->format.pix_mp.pixelformat); + + c3_cap_try_fmt(&cap->format.pix_mp); + + cap->id = i; + cap->rsz = &isp->resizers[i]; + cap->isp = isp; + INIT_LIST_HEAD(&cap->pending); + spin_lock_init(&cap->buff_lock); + mutex_init(&cap->lock); + + ret = c3_isp_register_capture(cap); + if (ret) { + cap->isp = NULL; + mutex_destroy(&cap->lock); + c3_isp_captures_unregister(isp); + return ret; + } + } + + return 0; +} + +void c3_isp_captures_unregister(struct c3_isp_device *isp) +{ + unsigned int i; + struct c3_isp_capture *cap; + + for (i = C3_ISP_CAP_DEV_0; i < C3_ISP_NUM_CAP_DEVS; i++) { + cap = &isp->caps[i]; + + if (!cap->isp) + continue; + vb2_queue_release(&cap->vb2_q); + media_entity_cleanup(&cap->vdev.entity); + video_unregister_device(&cap->vdev); + mutex_destroy(&cap->lock); + } +} + +void c3_isp_captures_isr(struct c3_isp_device *isp) +{ + c3_isp_cap_done(&isp->caps[C3_ISP_CAP_DEV_0]); + c3_isp_cap_done(&isp->caps[C3_ISP_CAP_DEV_1]); + c3_isp_cap_done(&isp->caps[C3_ISP_CAP_DEV_2]); +} diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-common.h b/drivers/media/platform/amlogic/c3/isp/c3-isp-common.h new file mode 100644 index 000000000000..cb470802e61e --- /dev/null +++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-common.h @@ -0,0 +1,340 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */ +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + */ + +#ifndef __C3_ISP_COMMON_H__ +#define __C3_ISP_COMMON_H__ + +#include + +#include +#include +#include +#include +#include + +#define C3_ISP_DRIVER_NAME "c3-isp" +#define C3_ISP_CLOCK_NUM_MAX 3 + +#define C3_ISP_DEFAULT_WIDTH 1920 +#define C3_ISP_DEFAULT_HEIGHT 1080 +#define C3_ISP_MAX_WIDTH 2888 +#define C3_ISP_MAX_HEIGHT 2240 +#define C3_ISP_MIN_WIDTH 160 +#define C3_ISP_MIN_HEIGHT 120 + +#define C3_ISP_DMA_SIZE_ALIGN_BYTES 16 + +enum c3_isp_core_pads { + C3_ISP_CORE_PAD_SINK_VIDEO, + C3_ISP_CORE_PAD_SINK_PARAMS, + C3_ISP_CORE_PAD_SOURCE_STATS, + C3_ISP_CORE_PAD_SOURCE_VIDEO_0, + C3_ISP_CORE_PAD_SOURCE_VIDEO_1, + C3_ISP_CORE_PAD_SOURCE_VIDEO_2, + C3_ISP_CORE_PAD_MAX +}; + +enum c3_isp_resizer_ids { + C3_ISP_RSZ_0, + C3_ISP_RSZ_1, + C3_ISP_RSZ_2, + C3_ISP_NUM_RSZ +}; + +enum c3_isp_resizer_pads { + C3_ISP_RSZ_PAD_SINK, + C3_ISP_RSZ_PAD_SOURCE, + C3_ISP_RSZ_PAD_MAX +}; + +enum c3_isp_cap_devs { + C3_ISP_CAP_DEV_0, + C3_ISP_CAP_DEV_1, + C3_ISP_CAP_DEV_2, + C3_ISP_NUM_CAP_DEVS +}; + +enum c3_isp_planes { + C3_ISP_PLANE_Y, + C3_ISP_PLANE_UV, + C3_ISP_NUM_PLANES +}; + +/* + * struct c3_isp_cap_format_info - The image format of capture device + * + * @mbus_code: the mbus code + * @fourcc: the pixel format + * @format: defines the output format of hardware + * @planes: defines the mutil plane of hardware + * @ch0_pix_bits: defines the channel 0 pixel bits mode of hardware + * @uv_swap: defines the uv swap flag of hardware + * @in_bits: defines the input bits of hardware + * @hdiv: horizontal chroma subsampling factor of hardware + * @vdiv: vertical chroma subsampling factor of hardware + */ +struct c3_isp_cap_format_info { + u32 mbus_code; + u32 fourcc; + u32 format; + u32 planes; + u32 ch0_pix_bits; + u8 uv_swap; + u8 in_bits; + u8 hdiv; + u8 vdiv; +}; + +/* + * struct c3_isp_cap_buffer - A container of vb2 buffer used by the video + * devices: capture video devices + * + * @vb: vb2 buffer + * @dma_addr: buffer physical address + * @list: entry of the buffer in the queue + */ +struct c3_isp_cap_buffer { + struct vb2_v4l2_buffer vb; + dma_addr_t dma_addr[C3_ISP_NUM_PLANES]; + struct list_head list; +}; + +/* + * struct c3_isp_stats_dma_buffer - A container of vb2 buffer used by the video + * devices: stats video devices + * + * @vb: vb2 buffer + * @dma_addr: buffer physical address + * @list: entry of the buffer in the queue + */ +struct c3_isp_stats_buffer { + struct vb2_v4l2_buffer vb; + dma_addr_t dma_addr; + struct list_head list; +}; + +/* + * struct c3_isp_params_buffer - A container of vb2 buffer used by the + * params video device + * + * @vb: vb2 buffer + * @cfg: scratch buffer used for caching the ISP configuration parameters + * @list: entry of the buffer in the queue + */ +struct c3_isp_params_buffer { + struct vb2_v4l2_buffer vb; + void *cfg; + struct list_head list; +}; + +/* + * struct c3_isp_dummy_buffer - A buffer to write the next frame to in case + * there are no vb2 buffers available. + * + * @vaddr: return value of call to dma_alloc_attrs + * @dma_addr: dma address of the buffer + * @size: size of the buffer + */ +struct c3_isp_dummy_buffer { + void *vaddr; + dma_addr_t dma_addr; + u32 size; +}; + +/* + * struct c3_isp_core - ISP core subdev + * + * @sd: ISP sub-device + * @pads: ISP sub-device pads + * @src_pad: source sub-device pad + * @isp: pointer to c3_isp_device + */ +struct c3_isp_core { + struct v4l2_subdev sd; + struct media_pad pads[C3_ISP_CORE_PAD_MAX]; + struct media_pad *src_pad; + struct c3_isp_device *isp; +}; + +/* + * struct c3_isp_resizer - ISP resizer subdev + * + * @id: resizer id + * @sd: resizer sub-device + * @pads: resizer sub-device pads + * @src_sd: source sub-device + * @isp: pointer to c3_isp_device + * @src_pad: the pad of source sub-device + */ +struct c3_isp_resizer { + enum c3_isp_resizer_ids id; + struct v4l2_subdev sd; + struct media_pad pads[C3_ISP_RSZ_PAD_MAX]; + struct v4l2_subdev *src_sd; + struct c3_isp_device *isp; + u32 src_pad; +}; + +/* + * struct c3_isp_stats - ISP statistics device + * + * @vb2_q: vb2 buffer queue + * @vdev: video node + * @vfmt: v4l2_format of the metadata format + * @pad: media pad + * @lock: protects vb2_q, vdev + * @isp: pointer to c3_isp_device + * @buff: in use buffer + * @buff_lock: protects stats buffer + * @pending: stats buffer list head + */ +struct c3_isp_stats { + struct vb2_queue vb2_q; + struct video_device vdev; + struct v4l2_format vfmt; + struct media_pad pad; + + struct mutex lock; /* Protects vb2_q, vdev */ + struct c3_isp_device *isp; + + struct c3_isp_stats_buffer *buff; + spinlock_t buff_lock; /* Protects stats buffer */ + struct list_head pending; +}; + +/* + * struct c3_isp_params - ISP parameters device + * + * @vb2_q: vb2 buffer queue + * @vdev: video node + * @vfmt: v4l2_format of the metadata format + * @pad: media pad + * @lock: protects vb2_q, vdev + * @isp: pointer to c3_isp_device + * @buff: in use buffer + * @buff_lock: protects stats buffer + * @pending: stats buffer list head + */ +struct c3_isp_params { + struct vb2_queue vb2_q; + struct video_device vdev; + struct v4l2_format vfmt; + struct media_pad pad; + + struct mutex lock; /* Protects vb2_q, vdev */ + struct c3_isp_device *isp; + + struct c3_isp_params_buffer *buff; + spinlock_t buff_lock; /* Protects params buffer */ + struct list_head pending; +}; + +/* + * struct c3_isp_capture - ISP capture device + * + * @id: capture device ID + * @vb2_q: vb2 buffer queue + * @vdev: video node + * @pad: media pad + * @lock: protects vb2_q, vdev + * @isp: pointer to c3_isp_device + * @rsz: pointer to c3_isp_resizer + * @buff: in use buffer + * @buff_lock: protects capture buffer + * @pending: capture buffer list head + * @format.info: a pointer to the c3_isp_capture_format of the pixel format + * @format.fmt: buffer format + */ +struct c3_isp_capture { + enum c3_isp_cap_devs id; + struct vb2_queue vb2_q; + struct video_device vdev; + struct media_pad pad; + + struct mutex lock; /* Protects vb2_q, vdev */ + struct c3_isp_device *isp; + struct c3_isp_resizer *rsz; + + struct c3_isp_dummy_buffer dummy_buff; + struct c3_isp_cap_buffer *buff; + spinlock_t buff_lock; /* Protects stream buffer */ + struct list_head pending; + struct { + const struct c3_isp_cap_format_info *info; + struct v4l2_pix_format_mplane pix_mp; + } format; +}; + +/** + * struct c3_isp_info - ISP information + * + * @clocks: array of ISP clock names + * @clock_num: actual clock number + */ +struct c3_isp_info { + char *clocks[C3_ISP_CLOCK_NUM_MAX]; + u32 clock_num; +}; + +/** + * struct c3_isp_device - ISP platform device + * + * @dev: pointer to the struct device + * @base: base register address + * @clks: array of clocks + * @notifier: notifier to register on the v4l2-async API + * @v4l2_dev: v4l2_device variable + * @media_dev: media device variable + * @pipe: media pipeline + * @core: ISP core subdev + * @resizers: ISP resizer subdev + * @stats: ISP stats device + * @params: ISP params device + * @caps: array of ISP capture device + * @frm_sequence: used to record frame id + * @info: version-specific ISP information + */ +struct c3_isp_device { + struct device *dev; + void __iomem *base; + struct clk_bulk_data clks[C3_ISP_CLOCK_NUM_MAX]; + + struct v4l2_async_notifier notifier; + struct v4l2_device v4l2_dev; + struct media_device media_dev; + struct media_pipeline pipe; + + struct c3_isp_core core; + struct c3_isp_resizer resizers[C3_ISP_NUM_RSZ]; + struct c3_isp_stats stats; + struct c3_isp_params params; + struct c3_isp_capture caps[C3_ISP_NUM_CAP_DEVS]; + + u32 frm_sequence; + const struct c3_isp_info *info; +}; + +u32 c3_isp_read(struct c3_isp_device *isp, u32 reg); +void c3_isp_write(struct c3_isp_device *isp, u32 reg, u32 val); +void c3_isp_update_bits(struct c3_isp_device *isp, u32 reg, u32 mask, u32 val); + +void c3_isp_core_queue_sof(struct c3_isp_device *isp); +int c3_isp_core_register(struct c3_isp_device *isp); +void c3_isp_core_unregister(struct c3_isp_device *isp); +int c3_isp_resizers_register(struct c3_isp_device *isp); +void c3_isp_resizers_unregister(struct c3_isp_device *isp); +int c3_isp_captures_register(struct c3_isp_device *isp); +void c3_isp_captures_unregister(struct c3_isp_device *isp); +void c3_isp_captures_isr(struct c3_isp_device *isp); +void c3_isp_stats_pre_cfg(struct c3_isp_device *isp); +int c3_isp_stats_register(struct c3_isp_device *isp); +void c3_isp_stats_unregister(struct c3_isp_device *isp); +void c3_isp_stats_isr(struct c3_isp_device *isp); +void c3_isp_params_pre_cfg(struct c3_isp_device *isp); +int c3_isp_params_register(struct c3_isp_device *isp); +void c3_isp_params_unregister(struct c3_isp_device *isp); +void c3_isp_params_isr(struct c3_isp_device *isp); + +#endif diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-core.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-core.c new file mode 100644 index 000000000000..ff6413fff889 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-core.c @@ -0,0 +1,641 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + */ + +#include +#include + +#include + +#include "c3-isp-common.h" +#include "c3-isp-regs.h" + +#define C3_ISP_CORE_SUBDEV_NAME "c3-isp-core" + +#define C3_ISP_PHASE_OFFSET_0 0 +#define C3_ISP_PHASE_OFFSET_1 1 +#define C3_ISP_PHASE_OFFSET_NONE 0xff + +#define C3_ISP_CORE_DEF_SINK_PAD_FMT MEDIA_BUS_FMT_SRGGB10_1X10 +#define C3_ISP_CORE_DEF_SRC_PAD_FMT MEDIA_BUS_FMT_YUV10_1X30 + +/* + * struct c3_isp_core_format_info - ISP core format information + * + * @mbus_code: the mbus code + * @pads: bitmask detailing valid pads for this mbus_code + * @xofst: horizontal phase offset of hardware + * @yofst: vertical phase offset of hardware + * @is_raw: the raw format flag of mbus code + */ +struct c3_isp_core_format_info { + u32 mbus_code; + u32 pads; + u8 xofst; + u8 yofst; + bool is_raw; +}; + +static const struct c3_isp_core_format_info c3_isp_core_fmts[] = { + /* RAW formats */ + { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO), + .xofst = C3_ISP_PHASE_OFFSET_0, + .yofst = C3_ISP_PHASE_OFFSET_1, + .is_raw = true, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO), + .xofst = C3_ISP_PHASE_OFFSET_1, + .yofst = C3_ISP_PHASE_OFFSET_1, + .is_raw = true, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO), + .xofst = C3_ISP_PHASE_OFFSET_0, + .yofst = C3_ISP_PHASE_OFFSET_0, + .is_raw = true, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO), + .xofst = C3_ISP_PHASE_OFFSET_1, + .yofst = C3_ISP_PHASE_OFFSET_0, + .is_raw = true, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO), + .xofst = C3_ISP_PHASE_OFFSET_0, + .yofst = C3_ISP_PHASE_OFFSET_1, + .is_raw = true, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO), + .xofst = C3_ISP_PHASE_OFFSET_1, + .yofst = C3_ISP_PHASE_OFFSET_1, + .is_raw = true, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO), + .xofst = C3_ISP_PHASE_OFFSET_0, + .yofst = C3_ISP_PHASE_OFFSET_0, + .is_raw = true, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO), + .xofst = C3_ISP_PHASE_OFFSET_1, + .yofst = C3_ISP_PHASE_OFFSET_0, + .is_raw = true, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16, + .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0) + | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1) + | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2), + .xofst = C3_ISP_PHASE_OFFSET_NONE, + .yofst = C3_ISP_PHASE_OFFSET_NONE, + .is_raw = true, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16, + .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0) + | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1) + | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2), + .xofst = C3_ISP_PHASE_OFFSET_NONE, + .yofst = C3_ISP_PHASE_OFFSET_NONE, + .is_raw = true, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16, + .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0) + | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1) + | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2), + .xofst = C3_ISP_PHASE_OFFSET_NONE, + .yofst = C3_ISP_PHASE_OFFSET_NONE, + .is_raw = true, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16, + .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0) + | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1) + | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2), + .xofst = C3_ISP_PHASE_OFFSET_NONE, + .yofst = C3_ISP_PHASE_OFFSET_NONE, + .is_raw = true, + }, + /* YUV formats */ + { + .mbus_code = MEDIA_BUS_FMT_YUV10_1X30, + .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0) | + BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1) | + BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2), + .xofst = C3_ISP_PHASE_OFFSET_NONE, + .yofst = C3_ISP_PHASE_OFFSET_NONE, + .is_raw = false, + }, +}; + +static const struct c3_isp_core_format_info +*core_find_format_by_code(u32 code, u32 pad) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(c3_isp_core_fmts); i++) { + const struct c3_isp_core_format_info *info = + &c3_isp_core_fmts[i]; + + if (info->mbus_code == code && info->pads & BIT(pad)) + return info; + } + + return NULL; +} + +static const struct c3_isp_core_format_info +*core_find_format_by_index(u32 index, u32 pad) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(c3_isp_core_fmts); i++) { + const struct c3_isp_core_format_info *info = + &c3_isp_core_fmts[i]; + + if (!(info->pads & BIT(pad))) + continue; + + if (!index) + return info; + + index--; + } + + return NULL; +} + +static void c3_isp_core_enable(struct c3_isp_device *isp) +{ + c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_END_MASK, + ISP_TOP_IRQ_EN_FRM_END_EN); + c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_RST_MASK, + ISP_TOP_IRQ_EN_FRM_RST_EN); + + /* Enable image data to ISP core */ + c3_isp_update_bits(isp, ISP_TOP_PATH_SEL, ISP_TOP_PATH_SEL_CORE_MASK, + ISP_TOP_PATH_SEL_CORE_MIPI_CORE); +} + +static void c3_isp_core_disable(struct c3_isp_device *isp) +{ + /* Disable image data to ISP core */ + c3_isp_update_bits(isp, ISP_TOP_PATH_SEL, ISP_TOP_PATH_SEL_CORE_MASK, + ISP_TOP_PATH_SEL_CORE_CORE_DIS); + + c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_END_MASK, + ISP_TOP_IRQ_EN_FRM_END_DIS); + c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_RST_MASK, + ISP_TOP_IRQ_EN_FRM_RST_DIS); +} + +/* Set the phase offset of blc, wb and lns */ +static void c3_isp_core_lswb_ofst(struct c3_isp_device *isp, + u8 xofst, u8 yofst) +{ + c3_isp_update_bits(isp, ISP_LSWB_BLC_PHSOFST, + ISP_LSWB_BLC_PHSOFST_HORIZ_OFST_MASK, + ISP_LSWB_BLC_PHSOFST_HORIZ_OFST(xofst)); + c3_isp_update_bits(isp, ISP_LSWB_BLC_PHSOFST, + ISP_LSWB_BLC_PHSOFST_VERT_OFST_MASK, + ISP_LSWB_BLC_PHSOFST_VERT_OFST(yofst)); + + c3_isp_update_bits(isp, ISP_LSWB_WB_PHSOFST, + ISP_LSWB_WB_PHSOFST_HORIZ_OFST_MASK, + ISP_LSWB_WB_PHSOFST_HORIZ_OFST(xofst)); + c3_isp_update_bits(isp, ISP_LSWB_WB_PHSOFST, + ISP_LSWB_WB_PHSOFST_VERT_OFST_MASK, + ISP_LSWB_WB_PHSOFST_VERT_OFST(yofst)); + + c3_isp_update_bits(isp, ISP_LSWB_LNS_PHSOFST, + ISP_LSWB_LNS_PHSOFST_HORIZ_OFST_MASK, + ISP_LSWB_LNS_PHSOFST_HORIZ_OFST(xofst)); + c3_isp_update_bits(isp, ISP_LSWB_LNS_PHSOFST, + ISP_LSWB_LNS_PHSOFST_VERT_OFST_MASK, + ISP_LSWB_LNS_PHSOFST_VERT_OFST(yofst)); +} + +/* Set the phase offset of af, ae and awb */ +static void c3_isp_core_3a_ofst(struct c3_isp_device *isp, + u8 xofst, u8 yofst) +{ + c3_isp_update_bits(isp, ISP_AF_CTRL, ISP_AF_CTRL_HORIZ_OFST_MASK, + ISP_AF_CTRL_HORIZ_OFST(xofst)); + c3_isp_update_bits(isp, ISP_AF_CTRL, ISP_AF_CTRL_VERT_OFST_MASK, + ISP_AF_CTRL_VERT_OFST(yofst)); + + c3_isp_update_bits(isp, ISP_AE_CTRL, ISP_AE_CTRL_HORIZ_OFST_MASK, + ISP_AE_CTRL_HORIZ_OFST(xofst)); + c3_isp_update_bits(isp, ISP_AE_CTRL, ISP_AE_CTRL_VERT_OFST_MASK, + ISP_AE_CTRL_VERT_OFST(yofst)); + + c3_isp_update_bits(isp, ISP_AWB_CTRL, ISP_AWB_CTRL_HORIZ_OFST_MASK, + ISP_AWB_CTRL_HORIZ_OFST(xofst)); + c3_isp_update_bits(isp, ISP_AWB_CTRL, ISP_AWB_CTRL_VERT_OFST_MASK, + ISP_AWB_CTRL_VERT_OFST(yofst)); +} + +/* Set the phase offset of demosaic */ +static void c3_isp_core_dms_ofst(struct c3_isp_device *isp, + u8 xofst, u8 yofst) +{ + c3_isp_update_bits(isp, ISP_DMS_COMMON_PARAM0, + ISP_DMS_COMMON_PARAM0_HORIZ_PHS_OFST_MASK, + ISP_DMS_COMMON_PARAM0_HORIZ_PHS_OFST(xofst)); + c3_isp_update_bits(isp, ISP_DMS_COMMON_PARAM0, + ISP_DMS_COMMON_PARAM0_VERT_PHS_OFST_MASK, + ISP_DMS_COMMON_PARAM0_VERT_PHS_OFST(yofst)); +} + +static void c3_isp_core_cfg_format(struct c3_isp_device *isp, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt; + const struct c3_isp_core_format_info *isp_fmt; + + fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SINK_VIDEO); + isp_fmt = core_find_format_by_code(fmt->code, + C3_ISP_CORE_PAD_SINK_VIDEO); + + c3_isp_write(isp, ISP_TOP_INPUT_SIZE, + ISP_TOP_INPUT_SIZE_HORIZ_SIZE(fmt->width) | + ISP_TOP_INPUT_SIZE_VERT_SIZE(fmt->height)); + c3_isp_write(isp, ISP_TOP_FRM_SIZE, + ISP_TOP_FRM_SIZE_CORE_HORIZ_SIZE(fmt->width) | + ISP_TOP_FRM_SIZE_CORE_VERT_SIZE(fmt->height)); + + c3_isp_update_bits(isp, ISP_TOP_HOLD_SIZE, + ISP_TOP_HOLD_SIZE_CORE_HORIZ_SIZE_MASK, + ISP_TOP_HOLD_SIZE_CORE_HORIZ_SIZE(fmt->width)); + + c3_isp_write(isp, ISP_AF_HV_SIZE, + ISP_AF_HV_SIZE_GLB_WIN_XSIZE(fmt->width) | + ISP_AF_HV_SIZE_GLB_WIN_YSIZE(fmt->height)); + c3_isp_write(isp, ISP_AE_HV_SIZE, + ISP_AE_HV_SIZE_HORIZ_SIZE(fmt->width) | + ISP_AE_HV_SIZE_VERT_SIZE(fmt->height)); + c3_isp_write(isp, ISP_AWB_HV_SIZE, + ISP_AWB_HV_SIZE_HORIZ_SIZE(fmt->width) | + ISP_AWB_HV_SIZE_VERT_SIZE(fmt->height)); + + c3_isp_core_lswb_ofst(isp, isp_fmt->xofst, isp_fmt->yofst); + c3_isp_core_3a_ofst(isp, isp_fmt->xofst, isp_fmt->yofst); + c3_isp_core_dms_ofst(isp, isp_fmt->xofst, isp_fmt->yofst); +} + +static bool c3_isp_core_streams_ready(struct c3_isp_core *core) +{ + unsigned int n_links = 0; + struct media_link *link; + + for_each_media_entity_data_link(&core->sd.entity, link) { + if ((link->source->index == C3_ISP_CORE_PAD_SOURCE_VIDEO_0 || + link->source->index == C3_ISP_CORE_PAD_SOURCE_VIDEO_1 || + link->source->index == C3_ISP_CORE_PAD_SOURCE_VIDEO_2) && + link->flags == MEDIA_LNK_FL_ENABLED) + n_links++; + } + + return n_links == core->isp->pipe.start_count; +} + +static int c3_isp_core_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct c3_isp_core *core = v4l2_get_subdevdata(sd); + struct media_pad *sink_pad; + struct v4l2_subdev *src_sd; + int ret; + + if (!c3_isp_core_streams_ready(core)) + return 0; + + core->isp->frm_sequence = 0; + c3_isp_core_cfg_format(core->isp, state); + c3_isp_core_enable(core->isp); + + sink_pad = &core->pads[C3_ISP_CORE_PAD_SINK_VIDEO]; + core->src_pad = media_pad_remote_pad_unique(sink_pad); + if (IS_ERR(core->src_pad)) { + dev_dbg(core->isp->dev, + "Failed to get source pad for ISP core\n"); + return -EPIPE; + } + + src_sd = media_entity_to_v4l2_subdev(core->src_pad->entity); + + ret = v4l2_subdev_enable_streams(src_sd, core->src_pad->index, BIT(0)); + if (ret) { + c3_isp_core_disable(core->isp); + return ret; + } + + return 0; +} + +static int c3_isp_core_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct c3_isp_core *core = v4l2_get_subdevdata(sd); + struct v4l2_subdev *src_sd; + + if (core->isp->pipe.start_count != 1) + return 0; + + if (core->src_pad) { + src_sd = media_entity_to_v4l2_subdev(core->src_pad->entity); + v4l2_subdev_disable_streams(src_sd, core->src_pad->index, + BIT(0)); + } + core->src_pad = NULL; + + c3_isp_core_disable(core->isp); + + return 0; +} + +static int c3_isp_core_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct c3_isp_core_format_info *info; + + switch (code->pad) { + case C3_ISP_CORE_PAD_SINK_VIDEO: + case C3_ISP_CORE_PAD_SOURCE_VIDEO_0: + case C3_ISP_CORE_PAD_SOURCE_VIDEO_1: + case C3_ISP_CORE_PAD_SOURCE_VIDEO_2: + info = core_find_format_by_index(code->index, code->pad); + if (!info) + return -EINVAL; + + code->code = info->mbus_code; + + break; + case C3_ISP_CORE_PAD_SINK_PARAMS: + case C3_ISP_CORE_PAD_SOURCE_STATS: + if (code->index) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_METADATA_FIXED; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static void c3_isp_core_set_sink_fmt(struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_mbus_framefmt *src_fmt; + const struct c3_isp_core_format_info *isp_fmt; + + sink_fmt = v4l2_subdev_state_get_format(state, format->pad); + + isp_fmt = core_find_format_by_code(format->format.code, format->pad); + if (!isp_fmt) + sink_fmt->code = C3_ISP_CORE_DEF_SINK_PAD_FMT; + else + sink_fmt->code = format->format.code; + + sink_fmt->width = clamp_t(u32, format->format.width, + C3_ISP_MIN_WIDTH, C3_ISP_MAX_WIDTH); + sink_fmt->height = clamp_t(u32, format->format.height, + C3_ISP_MIN_HEIGHT, C3_ISP_MAX_HEIGHT); + sink_fmt->field = V4L2_FIELD_NONE; + sink_fmt->colorspace = V4L2_COLORSPACE_RAW; + sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE; + sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + + for (unsigned int i = C3_ISP_CORE_PAD_SOURCE_VIDEO_0; + i < C3_ISP_CORE_PAD_MAX; i++) { + src_fmt = v4l2_subdev_state_get_format(state, i); + + src_fmt->width = sink_fmt->width; + src_fmt->height = sink_fmt->height; + } + + format->format = *sink_fmt; +} + +static void c3_isp_core_set_source_fmt(struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + const struct c3_isp_core_format_info *isp_fmt; + struct v4l2_mbus_framefmt *src_fmt; + struct v4l2_mbus_framefmt *sink_fmt; + + sink_fmt = v4l2_subdev_state_get_format(state, + C3_ISP_CORE_PAD_SINK_VIDEO); + src_fmt = v4l2_subdev_state_get_format(state, format->pad); + + isp_fmt = core_find_format_by_code(format->format.code, format->pad); + if (!isp_fmt) + src_fmt->code = C3_ISP_CORE_DEF_SRC_PAD_FMT; + else + src_fmt->code = format->format.code; + + src_fmt->width = sink_fmt->width; + src_fmt->height = sink_fmt->height; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + + if (isp_fmt && isp_fmt->is_raw) { + src_fmt->colorspace = V4L2_COLORSPACE_RAW; + src_fmt->xfer_func = V4L2_XFER_FUNC_NONE; + src_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + } else { + src_fmt->colorspace = V4L2_COLORSPACE_SRGB; + src_fmt->xfer_func = V4L2_XFER_FUNC_SRGB; + src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; + } + + format->format = *src_fmt; +} + +static int c3_isp_core_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + if (format->pad == C3_ISP_CORE_PAD_SINK_VIDEO) + c3_isp_core_set_sink_fmt(state, format); + else if (format->pad == C3_ISP_CORE_PAD_SOURCE_VIDEO_0 || + format->pad == C3_ISP_CORE_PAD_SOURCE_VIDEO_1 || + format->pad == C3_ISP_CORE_PAD_SOURCE_VIDEO_2) + c3_isp_core_set_source_fmt(state, format); + else + format->format = + *v4l2_subdev_state_get_format(state, format->pad); + + return 0; +} + +static int c3_isp_core_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt; + + /* Video sink pad */ + fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SINK_VIDEO); + fmt->width = C3_ISP_DEFAULT_WIDTH; + fmt->height = C3_ISP_DEFAULT_HEIGHT; + fmt->field = V4L2_FIELD_NONE; + fmt->code = C3_ISP_CORE_DEF_SINK_PAD_FMT; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + + /* Video source pad */ + for (unsigned int i = C3_ISP_CORE_PAD_SOURCE_VIDEO_0; + i < C3_ISP_CORE_PAD_MAX; i++) { + fmt = v4l2_subdev_state_get_format(state, i); + fmt->width = C3_ISP_DEFAULT_WIDTH; + fmt->height = C3_ISP_DEFAULT_HEIGHT; + fmt->field = V4L2_FIELD_NONE; + fmt->code = C3_ISP_CORE_DEF_SRC_PAD_FMT; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->xfer_func = V4L2_XFER_FUNC_SRGB; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; + } + + /* Parameters pad */ + fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SINK_PARAMS); + fmt->width = 0; + fmt->height = 0; + fmt->field = V4L2_FIELD_NONE; + fmt->code = MEDIA_BUS_FMT_METADATA_FIXED; + + /* Statistics pad */ + fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SOURCE_STATS); + fmt->width = 0; + fmt->height = 0; + fmt->field = V4L2_FIELD_NONE; + fmt->code = MEDIA_BUS_FMT_METADATA_FIXED; + + return 0; +} + +static int c3_isp_core_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + if (sub->type != V4L2_EVENT_FRAME_SYNC) + return -EINVAL; + + /* V4L2_EVENT_FRAME_SYNC doesn't need id, so should set 0 */ + if (sub->id != 0) + return -EINVAL; + + return v4l2_event_subscribe(fh, sub, 0, NULL); +} + +static const struct v4l2_subdev_pad_ops c3_isp_core_pad_ops = { + .enum_mbus_code = c3_isp_core_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = c3_isp_core_set_fmt, + .enable_streams = c3_isp_core_enable_streams, + .disable_streams = c3_isp_core_disable_streams, +}; + +static const struct v4l2_subdev_core_ops c3_isp_core_core_ops = { + .subscribe_event = c3_isp_core_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops c3_isp_core_subdev_ops = { + .core = &c3_isp_core_core_ops, + .pad = &c3_isp_core_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops c3_isp_core_internal_ops = { + .init_state = c3_isp_core_init_state, +}; + +static int c3_isp_core_link_validate(struct media_link *link) +{ + if (link->sink->index == C3_ISP_CORE_PAD_SINK_PARAMS) + return 0; + + return v4l2_subdev_link_validate(link); +} + +/* Media entity operations */ +static const struct media_entity_operations c3_isp_core_entity_ops = { + .link_validate = c3_isp_core_link_validate, +}; + +void c3_isp_core_queue_sof(struct c3_isp_device *isp) +{ + struct v4l2_event event = { + .type = V4L2_EVENT_FRAME_SYNC, + }; + + event.u.frame_sync.frame_sequence = isp->frm_sequence; + v4l2_event_queue(isp->core.sd.devnode, &event); +} + +int c3_isp_core_register(struct c3_isp_device *isp) +{ + struct c3_isp_core *core = &isp->core; + struct v4l2_subdev *sd = &core->sd; + int ret; + + v4l2_subdev_init(sd, &c3_isp_core_subdev_ops); + sd->owner = THIS_MODULE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + sd->internal_ops = &c3_isp_core_internal_ops; + snprintf(sd->name, sizeof(sd->name), "%s", C3_ISP_CORE_SUBDEV_NAME); + + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + sd->entity.ops = &c3_isp_core_entity_ops; + + core->isp = isp; + sd->dev = isp->dev; + v4l2_set_subdevdata(sd, core); + + core->pads[C3_ISP_CORE_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK; + core->pads[C3_ISP_CORE_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK; + core->pads[C3_ISP_CORE_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE; + core->pads[C3_ISP_CORE_PAD_SOURCE_VIDEO_0].flags = MEDIA_PAD_FL_SOURCE; + core->pads[C3_ISP_CORE_PAD_SOURCE_VIDEO_1].flags = MEDIA_PAD_FL_SOURCE; + core->pads[C3_ISP_CORE_PAD_SOURCE_VIDEO_2].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, C3_ISP_CORE_PAD_MAX, + core->pads); + if (ret) + return ret; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_device_register_subdev(&isp->v4l2_dev, sd); + if (ret) + goto err_subdev_cleanup; + + return 0; + +err_subdev_cleanup: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: + media_entity_cleanup(&sd->entity); + return ret; +} + +void c3_isp_core_unregister(struct c3_isp_device *isp) +{ + struct c3_isp_core *core = &isp->core; + struct v4l2_subdev *sd = &core->sd; + + v4l2_device_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); +} diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c new file mode 100644 index 000000000000..c3b779f63088 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "c3-isp-common.h" +#include "c3-isp-regs.h" + +u32 c3_isp_read(struct c3_isp_device *isp, u32 reg) +{ + return readl(isp->base + reg); +} + +void c3_isp_write(struct c3_isp_device *isp, u32 reg, u32 val) +{ + writel(val, isp->base + reg); +} + +void c3_isp_update_bits(struct c3_isp_device *isp, u32 reg, u32 mask, u32 val) +{ + u32 orig, tmp; + + orig = c3_isp_read(isp, reg); + + tmp = orig & ~mask; + tmp |= val & mask; + + if (tmp != orig) + c3_isp_write(isp, reg, tmp); +} + +/* PM runtime suspend */ +static int c3_isp_runtime_suspend(struct device *dev) +{ + struct c3_isp_device *isp = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(isp->info->clock_num, isp->clks); + + return 0; +} + +/* PM runtime resume */ +static int c3_isp_runtime_resume(struct device *dev) +{ + struct c3_isp_device *isp = dev_get_drvdata(dev); + + return clk_bulk_prepare_enable(isp->info->clock_num, isp->clks); +} + +static const struct dev_pm_ops c3_isp_pm_ops = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + RUNTIME_PM_OPS(c3_isp_runtime_suspend, + c3_isp_runtime_resume, NULL) +}; + +/* IRQ handling */ +static irqreturn_t c3_isp_irq_handler(int irq, void *dev) +{ + struct c3_isp_device *isp = dev; + u32 status; + + /* Get irq status and clear irq status */ + status = c3_isp_read(isp, ISP_TOP_RO_IRQ_STAT); + c3_isp_write(isp, ISP_TOP_IRQ_CLR, status); + + if (status & ISP_TOP_RO_IRQ_STAT_FRM_END_MASK) { + c3_isp_stats_isr(isp); + c3_isp_params_isr(isp); + c3_isp_captures_isr(isp); + isp->frm_sequence++; + } + + if (status & ISP_TOP_RO_IRQ_STAT_FRM_RST_MASK) + c3_isp_core_queue_sof(isp); + + return IRQ_HANDLED; +} + +/* Subdev notifier register */ +static int c3_isp_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asc) +{ + struct c3_isp_device *isp = + container_of(notifier, struct c3_isp_device, notifier); + struct media_pad *sink = + &isp->core.sd.entity.pads[C3_ISP_CORE_PAD_SINK_VIDEO]; + + return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} + +static int c3_isp_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct c3_isp_device *isp = + container_of(notifier, struct c3_isp_device, notifier); + + return v4l2_device_register_subdev_nodes(&isp->v4l2_dev); +} + +static const struct v4l2_async_notifier_operations c3_isp_notify_ops = { + .bound = c3_isp_notify_bound, + .complete = c3_isp_notify_complete, +}; + +static int c3_isp_async_nf_register(struct c3_isp_device *isp) +{ + struct v4l2_async_connection *asc; + struct fwnode_handle *ep; + int ret; + + v4l2_async_nf_init(&isp->notifier, &isp->v4l2_dev); + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(isp->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) + return -ENOTCONN; + + asc = v4l2_async_nf_add_fwnode_remote(&isp->notifier, ep, + struct v4l2_async_connection); + fwnode_handle_put(ep); + + if (IS_ERR(asc)) + return PTR_ERR(asc); + + isp->notifier.ops = &c3_isp_notify_ops; + ret = v4l2_async_nf_register(&isp->notifier); + if (ret) + v4l2_async_nf_cleanup(&isp->notifier); + + return ret; +} + +static void c3_isp_async_nf_unregister(struct c3_isp_device *isp) +{ + v4l2_async_nf_unregister(&isp->notifier); + v4l2_async_nf_cleanup(&isp->notifier); +} + +static int c3_isp_media_register(struct c3_isp_device *isp) +{ + struct media_device *media_dev = &isp->media_dev; + struct v4l2_device *v4l2_dev = &isp->v4l2_dev; + int ret; + + /* Initialize media device */ + strscpy(media_dev->model, C3_ISP_DRIVER_NAME, sizeof(media_dev->model)); + media_dev->dev = isp->dev; + + media_device_init(media_dev); + + /* Initialize v4l2 device */ + v4l2_dev->mdev = media_dev; + strscpy(v4l2_dev->name, C3_ISP_DRIVER_NAME, sizeof(v4l2_dev->name)); + + ret = v4l2_device_register(isp->dev, v4l2_dev); + if (ret) + goto err_media_dev_cleanup; + + ret = media_device_register(&isp->media_dev); + if (ret) { + dev_err(isp->dev, "Failed to register media device: %d\n", ret); + goto err_unreg_v4l2_dev; + } + + return 0; + +err_unreg_v4l2_dev: + v4l2_device_unregister(&isp->v4l2_dev); +err_media_dev_cleanup: + media_device_cleanup(media_dev); + return ret; +} + +static void c3_isp_media_unregister(struct c3_isp_device *isp) +{ + media_device_unregister(&isp->media_dev); + v4l2_device_unregister(&isp->v4l2_dev); + media_device_cleanup(&isp->media_dev); +} + +static void c3_isp_remove_links(struct c3_isp_device *isp) +{ + unsigned int i; + + media_entity_remove_links(&isp->core.sd.entity); + + for (i = 0; i < C3_ISP_NUM_RSZ; i++) + media_entity_remove_links(&isp->resizers[i].sd.entity); + + for (i = 0; i < C3_ISP_NUM_CAP_DEVS; i++) + media_entity_remove_links(&isp->caps[i].vdev.entity); +} + +static int c3_isp_create_links(struct c3_isp_device *isp) +{ + unsigned int i; + int ret; + + for (i = 0; i < C3_ISP_NUM_RSZ; i++) { + ret = media_create_pad_link(&isp->resizers[i].sd.entity, + C3_ISP_RSZ_PAD_SOURCE, + &isp->caps[i].vdev.entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(isp->dev, + "Failed to link rsz %u and cap %u\n", i, i); + goto err_remove_links; + } + + ret = media_create_pad_link(&isp->core.sd.entity, + C3_ISP_CORE_PAD_SOURCE_VIDEO_0 + i, + &isp->resizers[i].sd.entity, + C3_ISP_RSZ_PAD_SINK, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(isp->dev, + "Failed to link core and rsz %u\n", i); + goto err_remove_links; + } + } + + ret = media_create_pad_link(&isp->core.sd.entity, + C3_ISP_CORE_PAD_SOURCE_STATS, + &isp->stats.vdev.entity, + 0, MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(isp->dev, "Failed to link core and stats\n"); + goto err_remove_links; + } + + ret = media_create_pad_link(&isp->params.vdev.entity, 0, + &isp->core.sd.entity, + C3_ISP_CORE_PAD_SINK_PARAMS, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(isp->dev, "Failed to link params and core\n"); + goto err_remove_links; + } + + return 0; + +err_remove_links: + c3_isp_remove_links(isp); + return ret; +} + +static int c3_isp_videos_register(struct c3_isp_device *isp) +{ + int ret; + + ret = c3_isp_captures_register(isp); + if (ret) + return ret; + + ret = c3_isp_stats_register(isp); + if (ret) + goto err_captures_unregister; + + ret = c3_isp_params_register(isp); + if (ret) + goto err_stats_unregister; + + ret = c3_isp_create_links(isp); + if (ret) + goto err_params_unregister; + + return 0; + +err_params_unregister: + c3_isp_params_unregister(isp); +err_stats_unregister: + c3_isp_stats_unregister(isp); +err_captures_unregister: + c3_isp_captures_unregister(isp); + return ret; +} + +static void c3_isp_videos_unregister(struct c3_isp_device *isp) +{ + c3_isp_remove_links(isp); + c3_isp_params_unregister(isp); + c3_isp_stats_unregister(isp); + c3_isp_captures_unregister(isp); +} + +static int c3_isp_get_clocks(struct c3_isp_device *isp) +{ + const struct c3_isp_info *info = isp->info; + + for (unsigned int i = 0; i < info->clock_num; i++) + isp->clks[i].id = info->clocks[i]; + + return devm_clk_bulk_get(isp->dev, info->clock_num, isp->clks); +} + +static int c3_isp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct c3_isp_device *isp; + int irq; + int ret; + + isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); + if (!isp) + return -ENOMEM; + + isp->info = of_device_get_match_data(dev); + isp->dev = dev; + + isp->base = devm_platform_ioremap_resource_byname(pdev, "isp"); + if (IS_ERR(isp->base)) + return dev_err_probe(dev, PTR_ERR(isp->base), + "Failed to ioremap resource\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = c3_isp_get_clocks(isp); + if (ret) + return dev_err_probe(dev, ret, "Failed to get clocks\n"); + + platform_set_drvdata(pdev, isp); + + pm_runtime_enable(dev); + + ret = c3_isp_media_register(isp); + if (ret) + goto err_runtime_disable; + + ret = c3_isp_core_register(isp); + if (ret) + goto err_v4l2_unregister; + + ret = c3_isp_resizers_register(isp); + if (ret) + goto err_core_unregister; + + ret = c3_isp_async_nf_register(isp); + if (ret) + goto err_resizers_unregister; + + ret = devm_request_irq(dev, irq, + c3_isp_irq_handler, IRQF_SHARED, + dev_driver_string(dev), isp); + if (ret) + goto err_nf_unregister; + + ret = c3_isp_videos_register(isp); + if (ret) + goto err_nf_unregister; + + return 0; + +err_nf_unregister: + c3_isp_async_nf_unregister(isp); +err_resizers_unregister: + c3_isp_resizers_unregister(isp); +err_core_unregister: + c3_isp_core_unregister(isp); +err_v4l2_unregister: + c3_isp_media_unregister(isp); +err_runtime_disable: + pm_runtime_disable(dev); + return ret; +}; + +static void c3_isp_remove(struct platform_device *pdev) +{ + struct c3_isp_device *isp = platform_get_drvdata(pdev); + + c3_isp_videos_unregister(isp); + c3_isp_async_nf_unregister(isp); + c3_isp_core_unregister(isp); + c3_isp_resizers_unregister(isp); + c3_isp_media_unregister(isp); + pm_runtime_disable(isp->dev); +}; + +static const struct c3_isp_info isp_info = { + .clocks = {"vapb", "isp0"}, + .clock_num = 2 +}; + +static const struct of_device_id c3_isp_of_match[] = { + { .compatible = "amlogic,c3-isp", + .data = &isp_info }, + { }, +}; +MODULE_DEVICE_TABLE(of, c3_isp_of_match); + +static struct platform_driver c3_isp_driver = { + .probe = c3_isp_probe, + .remove = c3_isp_remove, + .driver = { + .name = "c3-isp", + .of_match_table = c3_isp_of_match, + .pm = pm_ptr(&c3_isp_pm_ops), + }, +}; + +module_platform_driver(c3_isp_driver); + +MODULE_AUTHOR("Keke Li "); +MODULE_DESCRIPTION("Amlogic C3 ISP pipeline"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c new file mode 100644 index 000000000000..c80667dd7662 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c @@ -0,0 +1,1008 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + */ + +#include +#include +#include + +#include +#include +#include + +#include "c3-isp-common.h" +#include "c3-isp-regs.h" + +/* + * union c3_isp_params_block - Generalisation of a parameter block + * + * This union allows the driver to treat a block as a generic struct to this + * union and safely access the header and block-specific struct without having + * to resort to casting. The header member is accessed first, and the type field + * checked which allows the driver to determine which of the other members + * should be used. + * + * @header: The shared header struct embedded as the first member + * of all the possible other members. This member would be + * accessed first and the type field checked to determine + * which of the other members should be accessed. + * @awb_gains: For header.type == C3_ISP_PARAMS_BLOCK_AWB_GAINS + * @awb_cfg: For header.type == C3_ISP_PARAMS_BLOCK_AWB_CONFIG + * @ae_cfg: For header.type == C3_ISP_PARAMS_BLOCK_AE_CONFIG + * @af_cfg: For header.type == C3_ISP_PARAMS_BLOCK_AF_CONFIG + * @pst_gamma: For header.type == C3_ISP_PARAMS_BLOCK_PST_GAMMA + * @ccm: For header.type == C3_ISP_PARAMS_BLOCK_CCM + * @csc: For header.type == C3_ISP_PARAMS_BLOCK_CSC + * @blc: For header.type == C3_ISP_PARAMS_BLOCK_BLC + */ +union c3_isp_params_block { + struct c3_isp_params_block_header header; + struct c3_isp_params_awb_gains awb_gains; + struct c3_isp_params_awb_config awb_cfg; + struct c3_isp_params_ae_config ae_cfg; + struct c3_isp_params_af_config af_cfg; + struct c3_isp_params_pst_gamma pst_gamma; + struct c3_isp_params_ccm ccm; + struct c3_isp_params_csc csc; + struct c3_isp_params_blc blc; +}; + +typedef void (*c3_isp_block_handler)(struct c3_isp_device *isp, + const union c3_isp_params_block *block); + +struct c3_isp_params_handler { + size_t size; + c3_isp_block_handler handler; +}; + +#define to_c3_isp_params_buffer(vbuf) \ + container_of(vbuf, struct c3_isp_params_buffer, vb) + +/* Hardware configuration */ + +static void c3_isp_params_cfg_awb_gains(struct c3_isp_device *isp, + const union c3_isp_params_block *block) +{ + const struct c3_isp_params_awb_gains *awb_gains = &block->awb_gains; + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) { + c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL, + ISP_TOP_BEO_CTRL_WB_EN_MASK, + ISP_TOP_BEO_CTRL_WB_DIS); + return; + } + + c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN0, + ISP_LSWB_WB_GAIN0_GR_GAIN_MASK, + ISP_LSWB_WB_GAIN0_GR_GAIN(awb_gains->gr_gain)); + c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN0, + ISP_LSWB_WB_GAIN0_R_GAIN_MASK, + ISP_LSWB_WB_GAIN0_R_GAIN(awb_gains->r_gain)); + c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN1, + ISP_LSWB_WB_GAIN1_B_GAIN_MASK, + ISP_LSWB_WB_GAIN1_B_GAIN(awb_gains->b_gain)); + c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN1, + ISP_LSWB_WB_GAIN1_GB_GAIN_MASK, + ISP_LSWB_WB_GAIN1_GB_GAIN(awb_gains->gb_gain)); + c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN2, + ISP_LSWB_WB_GAIN2_IR_GAIN_MASK, + ISP_LSWB_WB_GAIN2_IR_GAIN(awb_gains->gb_gain)); + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE) + c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL, + ISP_TOP_BEO_CTRL_WB_EN_MASK, + ISP_TOP_BEO_CTRL_WB_EN); +} + +static void c3_isp_params_awb_wt(struct c3_isp_device *isp, + const struct c3_isp_params_awb_config *cfg) +{ + unsigned int zones_num; + unsigned int base; + unsigned int data; + unsigned int i; + + /* Set the weight address to 0 position */ + c3_isp_write(isp, ISP_AWB_BLK_WT_ADDR, 0); + + zones_num = cfg->horiz_zones_num * cfg->vert_zones_num; + + /* Need to write 8 weights at once */ + for (i = 0; i < zones_num / 8; i++) { + base = i * 8; + data = ISP_AWB_BLK_WT_DATA_WT(0, cfg->zone_weight[base + 0]) | + ISP_AWB_BLK_WT_DATA_WT(1, cfg->zone_weight[base + 1]) | + ISP_AWB_BLK_WT_DATA_WT(2, cfg->zone_weight[base + 2]) | + ISP_AWB_BLK_WT_DATA_WT(3, cfg->zone_weight[base + 3]) | + ISP_AWB_BLK_WT_DATA_WT(4, cfg->zone_weight[base + 4]) | + ISP_AWB_BLK_WT_DATA_WT(5, cfg->zone_weight[base + 5]) | + ISP_AWB_BLK_WT_DATA_WT(6, cfg->zone_weight[base + 6]) | + ISP_AWB_BLK_WT_DATA_WT(7, cfg->zone_weight[base + 7]); + c3_isp_write(isp, ISP_AWB_BLK_WT_DATA, data); + } + + if (zones_num % 8 == 0) + return; + + data = 0; + base = i * 8; + + for (i = 0; i < zones_num % 8; i++) + data |= ISP_AWB_BLK_WT_DATA_WT(i, cfg->zone_weight[base + i]); + + c3_isp_write(isp, ISP_AWB_BLK_WT_DATA, data); +} + +static void c3_isp_params_awb_cood(struct c3_isp_device *isp, + const struct c3_isp_params_awb_config *cfg) +{ + unsigned int max_point_num; + + /* The number of points is one more than the number of edges */ + max_point_num = max(cfg->horiz_zones_num, cfg->vert_zones_num) + 1; + + /* Set the index address to 0 position */ + c3_isp_write(isp, ISP_AWB_IDX_ADDR, 0); + + for (unsigned int i = 0; i < max_point_num; i++) + c3_isp_write(isp, ISP_AWB_IDX_DATA, + ISP_AWB_IDX_DATA_HIDX_DATA(cfg->horiz_coord[i]) | + ISP_AWB_IDX_DATA_VIDX_DATA(cfg->vert_coord[i])); +} + +static void c3_isp_params_cfg_awb_config(struct c3_isp_device *isp, + const union c3_isp_params_block *block) +{ + const struct c3_isp_params_awb_config *awb_cfg = &block->awb_cfg; + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) { + c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL, + ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK, + ISP_TOP_3A_STAT_CRTL_AWB_STAT_DIS); + return; + } + + c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL, + ISP_TOP_3A_STAT_CRTL_AWB_POINT_MASK, + ISP_TOP_3A_STAT_CRTL_AWB_POINT(awb_cfg->tap_point)); + + c3_isp_update_bits(isp, ISP_AWB_STAT_CTRL2, + ISP_AWB_STAT_CTRL2_SATUR_CTRL_MASK, + ISP_AWB_STAT_CTRL2_SATUR_CTRL(awb_cfg->satur_vald)); + + c3_isp_update_bits(isp, ISP_AWB_HV_BLKNUM, + ISP_AWB_HV_BLKNUM_H_NUM_MASK, + ISP_AWB_HV_BLKNUM_H_NUM(awb_cfg->horiz_zones_num)); + c3_isp_update_bits(isp, ISP_AWB_HV_BLKNUM, + ISP_AWB_HV_BLKNUM_V_NUM_MASK, + ISP_AWB_HV_BLKNUM_V_NUM(awb_cfg->vert_zones_num)); + + c3_isp_update_bits(isp, ISP_AWB_STAT_RG, ISP_AWB_STAT_RG_MIN_VALUE_MASK, + ISP_AWB_STAT_RG_MIN_VALUE(awb_cfg->rg_min)); + c3_isp_update_bits(isp, ISP_AWB_STAT_RG, ISP_AWB_STAT_RG_MAX_VALUE_MASK, + ISP_AWB_STAT_RG_MAX_VALUE(awb_cfg->rg_max)); + + c3_isp_update_bits(isp, ISP_AWB_STAT_BG, ISP_AWB_STAT_BG_MIN_VALUE_MASK, + ISP_AWB_STAT_BG_MIN_VALUE(awb_cfg->bg_min)); + c3_isp_update_bits(isp, ISP_AWB_STAT_BG, ISP_AWB_STAT_BG_MAX_VALUE_MASK, + ISP_AWB_STAT_BG_MAX_VALUE(awb_cfg->bg_max)); + + c3_isp_update_bits(isp, ISP_AWB_STAT_RG_HL, + ISP_AWB_STAT_RG_HL_LOW_VALUE_MASK, + ISP_AWB_STAT_RG_HL_LOW_VALUE(awb_cfg->rg_low)); + c3_isp_update_bits(isp, ISP_AWB_STAT_RG_HL, + ISP_AWB_STAT_RG_HL_HIGH_VALUE_MASK, + ISP_AWB_STAT_RG_HL_HIGH_VALUE(awb_cfg->rg_high)); + + c3_isp_update_bits(isp, ISP_AWB_STAT_BG_HL, + ISP_AWB_STAT_BG_HL_LOW_VALUE_MASK, + ISP_AWB_STAT_BG_HL_LOW_VALUE(awb_cfg->bg_low)); + c3_isp_update_bits(isp, ISP_AWB_STAT_BG_HL, + ISP_AWB_STAT_BG_HL_HIGH_VALUE_MASK, + ISP_AWB_STAT_BG_HL_HIGH_VALUE(awb_cfg->bg_high)); + + c3_isp_params_awb_wt(isp, awb_cfg); + c3_isp_params_awb_cood(isp, awb_cfg); + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE) + c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL, + ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK, + ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN); +} + +static void c3_isp_params_ae_wt(struct c3_isp_device *isp, + const struct c3_isp_params_ae_config *cfg) +{ + unsigned int zones_num; + unsigned int base; + unsigned int data; + unsigned int i; + + /* Set the weight address to 0 position */ + c3_isp_write(isp, ISP_AE_BLK_WT_ADDR, 0); + + zones_num = cfg->horiz_zones_num * cfg->vert_zones_num; + + /* Need to write 8 weights at once */ + for (i = 0; i < zones_num / 8; i++) { + base = i * 8; + data = ISP_AE_BLK_WT_DATA_WT(0, cfg->zone_weight[base + 0]) | + ISP_AE_BLK_WT_DATA_WT(1, cfg->zone_weight[base + 1]) | + ISP_AE_BLK_WT_DATA_WT(2, cfg->zone_weight[base + 2]) | + ISP_AE_BLK_WT_DATA_WT(3, cfg->zone_weight[base + 3]) | + ISP_AE_BLK_WT_DATA_WT(4, cfg->zone_weight[base + 4]) | + ISP_AE_BLK_WT_DATA_WT(5, cfg->zone_weight[base + 5]) | + ISP_AE_BLK_WT_DATA_WT(6, cfg->zone_weight[base + 6]) | + ISP_AE_BLK_WT_DATA_WT(7, cfg->zone_weight[base + 7]); + c3_isp_write(isp, ISP_AE_BLK_WT_DATA, data); + } + + if (zones_num % 8 == 0) + return; + + data = 0; + base = i * 8; + + /* Write the last weights data */ + for (i = 0; i < zones_num % 8; i++) + data |= ISP_AE_BLK_WT_DATA_WT(i, cfg->zone_weight[base + i]); + + c3_isp_write(isp, ISP_AE_BLK_WT_DATA, data); +} + +static void c3_isp_params_ae_cood(struct c3_isp_device *isp, + const struct c3_isp_params_ae_config *cfg) +{ + unsigned int max_point_num; + + /* The number of points is one more than the number of edges */ + max_point_num = max(cfg->horiz_zones_num, cfg->vert_zones_num) + 1; + + /* Set the index address to 0 position */ + c3_isp_write(isp, ISP_AE_IDX_ADDR, 0); + + for (unsigned int i = 0; i < max_point_num; i++) + c3_isp_write(isp, ISP_AE_IDX_DATA, + ISP_AE_IDX_DATA_HIDX_DATA(cfg->horiz_coord[i]) | + ISP_AE_IDX_DATA_VIDX_DATA(cfg->vert_coord[i])); +} + +static void c3_isp_params_cfg_ae_config(struct c3_isp_device *isp, + const union c3_isp_params_block *block) +{ + const struct c3_isp_params_ae_config *ae_cfg = &block->ae_cfg; + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) { + c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL, + ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK, + ISP_TOP_3A_STAT_CRTL_AE_STAT_DIS); + return; + } + + c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL, + ISP_TOP_3A_STAT_CRTL_AE_POINT_MASK, + ISP_TOP_3A_STAT_CRTL_AE_POINT(ae_cfg->tap_point)); + + if (ae_cfg->tap_point == C3_ISP_AE_STATS_TAP_GE) + c3_isp_update_bits(isp, ISP_AE_CTRL, + ISP_AE_CTRL_INPUT_2LINE_MASK, + ISP_AE_CTRL_INPUT_2LINE_EN); + else + c3_isp_update_bits(isp, ISP_AE_CTRL, + ISP_AE_CTRL_INPUT_2LINE_MASK, + ISP_AE_CTRL_INPUT_2LINE_DIS); + + c3_isp_update_bits(isp, ISP_AE_HV_BLKNUM, + ISP_AE_HV_BLKNUM_H_NUM_MASK, + ISP_AE_HV_BLKNUM_H_NUM(ae_cfg->horiz_zones_num)); + c3_isp_update_bits(isp, ISP_AE_HV_BLKNUM, + ISP_AE_HV_BLKNUM_V_NUM_MASK, + ISP_AE_HV_BLKNUM_V_NUM(ae_cfg->vert_zones_num)); + + c3_isp_params_ae_wt(isp, ae_cfg); + c3_isp_params_ae_cood(isp, ae_cfg); + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE) + c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL, + ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK, + ISP_TOP_3A_STAT_CRTL_AE_STAT_EN); +} + +static void c3_isp_params_af_cood(struct c3_isp_device *isp, + const struct c3_isp_params_af_config *cfg) +{ + unsigned int max_point_num; + + /* The number of points is one more than the number of edges */ + max_point_num = max(cfg->horiz_zones_num, cfg->vert_zones_num) + 1; + + /* Set the index address to 0 position */ + c3_isp_write(isp, ISP_AF_IDX_ADDR, 0); + + for (unsigned int i = 0; i < max_point_num; i++) + c3_isp_write(isp, ISP_AF_IDX_DATA, + ISP_AF_IDX_DATA_HIDX_DATA(cfg->horiz_coord[i]) | + ISP_AF_IDX_DATA_VIDX_DATA(cfg->vert_coord[i])); +} + +static void c3_isp_params_cfg_af_config(struct c3_isp_device *isp, + const union c3_isp_params_block *block) +{ + const struct c3_isp_params_af_config *af_cfg = &block->af_cfg; + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) { + c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL, + ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK, + ISP_TOP_3A_STAT_CRTL_AF_STAT_DIS); + return; + } + + c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL, + ISP_TOP_3A_STAT_CRTL_AF_POINT_MASK, + ISP_TOP_3A_STAT_CRTL_AF_POINT(af_cfg->tap_point)); + + c3_isp_update_bits(isp, ISP_AF_HV_BLKNUM, + ISP_AF_HV_BLKNUM_H_NUM_MASK, + ISP_AF_HV_BLKNUM_H_NUM(af_cfg->horiz_zones_num)); + c3_isp_update_bits(isp, ISP_AF_HV_BLKNUM, + ISP_AF_HV_BLKNUM_V_NUM_MASK, + ISP_AF_HV_BLKNUM_V_NUM(af_cfg->vert_zones_num)); + + c3_isp_params_af_cood(isp, af_cfg); + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE) + c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL, + ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK, + ISP_TOP_3A_STAT_CRTL_AF_STAT_EN); +} + +static void c3_isp_params_cfg_pst_gamma(struct c3_isp_device *isp, + const union c3_isp_params_block *block) +{ + const struct c3_isp_params_pst_gamma *gm = &block->pst_gamma; + unsigned int base; + unsigned int i; + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) { + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, + ISP_TOP_BED_CTRL_PST_GAMMA_EN_MASK, + ISP_TOP_BED_CTRL_PST_GAMMA_DIS); + return; + } + + /* R, G and B channels use the same gamma lut */ + for (unsigned int j = 0; j < 3; j++) { + /* Set the channel lut address */ + c3_isp_write(isp, ISP_PST_GAMMA_LUT_ADDR, + ISP_PST_GAMMA_LUT_ADDR_IDX_ADDR(j)); + + /* Need to write 2 lut values at once */ + for (i = 0; i < ARRAY_SIZE(gm->lut) / 2; i++) { + base = i * 2; + c3_isp_write(isp, ISP_PST_GAMMA_LUT_DATA, + ISP_PST_GM_LUT_DATA0(gm->lut[base]) | + ISP_PST_GM_LUT_DATA1(gm->lut[base + 1])); + } + + /* Write the last one */ + if (ARRAY_SIZE(gm->lut) % 2) { + base = i * 2; + c3_isp_write(isp, ISP_PST_GAMMA_LUT_DATA, + ISP_PST_GM_LUT_DATA0(gm->lut[base])); + } + } + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE) + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, + ISP_TOP_BED_CTRL_PST_GAMMA_EN_MASK, + ISP_TOP_BED_CTRL_PST_GAMMA_EN); +} + +/* Configure 3 x 3 ccm matrix */ +static void c3_isp_params_cfg_ccm(struct c3_isp_device *isp, + const union c3_isp_params_block *block) +{ + const struct c3_isp_params_ccm *ccm = &block->ccm; + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) { + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, + ISP_TOP_BED_CTRL_CCM_EN_MASK, + ISP_TOP_BED_CTRL_CCM_DIS); + return; + } + + c3_isp_update_bits(isp, ISP_CCM_MTX_00_01, + ISP_CCM_MTX_00_01_MTX_00_MASK, + ISP_CCM_MTX_00_01_MTX_00(ccm->matrix[0][0])); + c3_isp_update_bits(isp, ISP_CCM_MTX_00_01, + ISP_CCM_MTX_00_01_MTX_01_MASK, + ISP_CCM_MTX_00_01_MTX_01(ccm->matrix[0][1])); + c3_isp_update_bits(isp, ISP_CCM_MTX_02_03, + ISP_CCM_MTX_02_03_MTX_02_MASK, + ISP_CCM_MTX_02_03_MTX_02(ccm->matrix[0][2])); + + c3_isp_update_bits(isp, ISP_CCM_MTX_10_11, + ISP_CCM_MTX_10_11_MTX_10_MASK, + ISP_CCM_MTX_10_11_MTX_10(ccm->matrix[1][0])); + c3_isp_update_bits(isp, ISP_CCM_MTX_10_11, + ISP_CCM_MTX_10_11_MTX_11_MASK, + ISP_CCM_MTX_10_11_MTX_11(ccm->matrix[1][1])); + c3_isp_update_bits(isp, ISP_CCM_MTX_12_13, + ISP_CCM_MTX_12_13_MTX_12_MASK, + ISP_CCM_MTX_12_13_MTX_12(ccm->matrix[1][2])); + + c3_isp_update_bits(isp, ISP_CCM_MTX_20_21, + ISP_CCM_MTX_20_21_MTX_20_MASK, + ISP_CCM_MTX_20_21_MTX_20(ccm->matrix[2][0])); + c3_isp_update_bits(isp, ISP_CCM_MTX_20_21, + ISP_CCM_MTX_20_21_MTX_21_MASK, + ISP_CCM_MTX_20_21_MTX_21(ccm->matrix[2][1])); + c3_isp_update_bits(isp, ISP_CCM_MTX_22_23_RS, + ISP_CCM_MTX_22_23_RS_MTX_22_MASK, + ISP_CCM_MTX_22_23_RS_MTX_22(ccm->matrix[2][2])); + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE) + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, + ISP_TOP_BED_CTRL_CCM_EN_MASK, + ISP_TOP_BED_CTRL_CCM_EN); +} + +/* Configure color space conversion matrix parameters */ +static void c3_isp_params_cfg_csc(struct c3_isp_device *isp, + const union c3_isp_params_block *block) +{ + const struct c3_isp_params_csc *csc = &block->csc; + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) { + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, + ISP_TOP_BED_CTRL_CM0_EN_MASK, + ISP_TOP_BED_CTRL_CM0_DIS); + return; + } + + c3_isp_update_bits(isp, ISP_CM0_COEF00_01, + ISP_CM0_COEF00_01_MTX_00_MASK, + ISP_CM0_COEF00_01_MTX_00(csc->matrix[0][0])); + c3_isp_update_bits(isp, ISP_CM0_COEF00_01, + ISP_CM0_COEF00_01_MTX_01_MASK, + ISP_CM0_COEF00_01_MTX_01(csc->matrix[0][1])); + c3_isp_update_bits(isp, ISP_CM0_COEF02_10, + ISP_CM0_COEF02_10_MTX_02_MASK, + ISP_CM0_COEF02_10_MTX_02(csc->matrix[0][2])); + + c3_isp_update_bits(isp, ISP_CM0_COEF02_10, + ISP_CM0_COEF02_10_MTX_10_MASK, + ISP_CM0_COEF02_10_MTX_10(csc->matrix[1][0])); + c3_isp_update_bits(isp, ISP_CM0_COEF11_12, + ISP_CM0_COEF11_12_MTX_11_MASK, + ISP_CM0_COEF11_12_MTX_11(csc->matrix[1][1])); + c3_isp_update_bits(isp, ISP_CM0_COEF11_12, + ISP_CM0_COEF11_12_MTX_12_MASK, + ISP_CM0_COEF11_12_MTX_12(csc->matrix[1][2])); + + c3_isp_update_bits(isp, ISP_CM0_COEF20_21, + ISP_CM0_COEF20_21_MTX_20_MASK, + ISP_CM0_COEF20_21_MTX_20(csc->matrix[2][0])); + c3_isp_update_bits(isp, ISP_CM0_COEF20_21, + ISP_CM0_COEF20_21_MTX_21_MASK, + ISP_CM0_COEF20_21_MTX_21(csc->matrix[2][1])); + c3_isp_update_bits(isp, ISP_CM0_COEF22_OUP_OFST0, + ISP_CM0_COEF22_OUP_OFST0_MTX_22_MASK, + ISP_CM0_COEF22_OUP_OFST0_MTX_22(csc->matrix[2][2])); + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE) + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, + ISP_TOP_BED_CTRL_CM0_EN_MASK, + ISP_TOP_BED_CTRL_CM0_EN); +} + +/* Set blc offset of each color channel */ +static void c3_isp_params_cfg_blc(struct c3_isp_device *isp, + const union c3_isp_params_block *block) +{ + const struct c3_isp_params_blc *blc = &block->blc; + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) { + c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL, + ISP_TOP_BEO_CTRL_BLC_EN_MASK, + ISP_TOP_BEO_CTRL_BLC_DIS); + return; + } + + c3_isp_write(isp, ISP_LSWB_BLC_OFST0, + ISP_LSWB_BLC_OFST0_R_OFST(blc->r_ofst) | + ISP_LSWB_BLC_OFST0_GR_OFST(blc->gr_ofst)); + c3_isp_write(isp, ISP_LSWB_BLC_OFST1, + ISP_LSWB_BLC_OFST1_GB_OFST(blc->gb_ofst) | + ISP_LSWB_BLC_OFST1_B_OFST(blc->b_ofst)); + + if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE) + c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL, + ISP_TOP_BEO_CTRL_BLC_EN_MASK, + ISP_TOP_BEO_CTRL_BLC_EN); +} + +static const struct c3_isp_params_handler c3_isp_params_handlers[] = { + [C3_ISP_PARAMS_BLOCK_AWB_GAINS] = { + .size = sizeof(struct c3_isp_params_awb_gains), + .handler = c3_isp_params_cfg_awb_gains, + }, + [C3_ISP_PARAMS_BLOCK_AWB_CONFIG] = { + .size = sizeof(struct c3_isp_params_awb_config), + .handler = c3_isp_params_cfg_awb_config, + }, + [C3_ISP_PARAMS_BLOCK_AE_CONFIG] = { + .size = sizeof(struct c3_isp_params_ae_config), + .handler = c3_isp_params_cfg_ae_config, + }, + [C3_ISP_PARAMS_BLOCK_AF_CONFIG] = { + .size = sizeof(struct c3_isp_params_af_config), + .handler = c3_isp_params_cfg_af_config, + }, + [C3_ISP_PARAMS_BLOCK_PST_GAMMA] = { + .size = sizeof(struct c3_isp_params_pst_gamma), + .handler = c3_isp_params_cfg_pst_gamma, + }, + [C3_ISP_PARAMS_BLOCK_CCM] = { + .size = sizeof(struct c3_isp_params_ccm), + .handler = c3_isp_params_cfg_ccm, + }, + [C3_ISP_PARAMS_BLOCK_CSC] = { + .size = sizeof(struct c3_isp_params_csc), + .handler = c3_isp_params_cfg_csc, + }, + [C3_ISP_PARAMS_BLOCK_BLC] = { + .size = sizeof(struct c3_isp_params_blc), + .handler = c3_isp_params_cfg_blc, + }, +}; + +static void c3_isp_params_cfg_blocks(struct c3_isp_params *params) +{ + struct c3_isp_params_cfg *config = params->buff->cfg; + size_t block_offset = 0; + + if (WARN_ON(!config)) + return; + + /* Walk the list of parameter blocks and process them */ + while (block_offset < config->data_size) { + const struct c3_isp_params_handler *block_handler; + const union c3_isp_params_block *block; + + block = (const union c3_isp_params_block *) + &config->data[block_offset]; + + block_handler = &c3_isp_params_handlers[block->header.type]; + block_handler->handler(params->isp, block); + + block_offset += block->header.size; + } +} + +void c3_isp_params_pre_cfg(struct c3_isp_device *isp) +{ + struct c3_isp_params *params = &isp->params; + + /* Disable some unused modules */ + c3_isp_update_bits(isp, ISP_TOP_FEO_CTRL0, + ISP_TOP_FEO_CTRL0_INPUT_FMT_EN_MASK, + ISP_TOP_FEO_CTRL0_INPUT_FMT_DIS); + + c3_isp_update_bits(isp, ISP_TOP_FEO_CTRL1_0, + ISP_TOP_FEO_CTRL1_0_DPC_EN_MASK, + ISP_TOP_FEO_CTRL1_0_DPC_DIS); + c3_isp_update_bits(isp, ISP_TOP_FEO_CTRL1_0, + ISP_TOP_FEO_CTRL1_0_OG_EN_MASK, + ISP_TOP_FEO_CTRL1_0_OG_DIS); + + c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_PDPC_EN_MASK, + ISP_TOP_FED_CTRL_PDPC_DIS); + c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, + ISP_TOP_FED_CTRL_RAWCNR_EN_MASK, + ISP_TOP_FED_CTRL_RAWCNR_DIS); + c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_SNR1_EN_MASK, + ISP_TOP_FED_CTRL_SNR1_DIS); + c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_TNR0_EN_MASK, + ISP_TOP_FED_CTRL_TNR0_DIS); + c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, + ISP_TOP_FED_CTRL_CUBIC_CS_EN_MASK, + ISP_TOP_FED_CTRL_CUBIC_CS_DIS); + c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_SQRT_EN_MASK, + ISP_TOP_FED_CTRL_SQRT_DIS); + c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, + ISP_TOP_FED_CTRL_DGAIN_EN_MASK, + ISP_TOP_FED_CTRL_DGAIN_DIS); + + c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL, + ISP_TOP_BEO_CTRL_INV_DGAIN_EN_MASK, + ISP_TOP_BEO_CTRL_INV_DGAIN_DIS); + c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL, ISP_TOP_BEO_CTRL_EOTF_EN_MASK, + ISP_TOP_BEO_CTRL_EOTF_DIS); + + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, + ISP_TOP_BED_CTRL_YHS_STAT_EN_MASK, + ISP_TOP_BED_CTRL_YHS_STAT_DIS); + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, + ISP_TOP_BED_CTRL_GRPH_STAT_EN_MASK, + ISP_TOP_BED_CTRL_GRPH_STAT_DIS); + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, + ISP_TOP_BED_CTRL_FMETER_EN_MASK, + ISP_TOP_BED_CTRL_FMETER_DIS); + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_BSC_EN_MASK, + ISP_TOP_BED_CTRL_BSC_DIS); + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_CNR2_EN_MASK, + ISP_TOP_BED_CTRL_CNR2_DIS); + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_CM1_EN_MASK, + ISP_TOP_BED_CTRL_CM1_DIS); + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, + ISP_TOP_BED_CTRL_LUT3D_EN_MASK, + ISP_TOP_BED_CTRL_LUT3D_DIS); + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, + ISP_TOP_BED_CTRL_PST_TNR_LITE_EN_MASK, + ISP_TOP_BED_CTRL_PST_TNR_LITE_DIS); + c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_AMCM_EN_MASK, + ISP_TOP_BED_CTRL_AMCM_DIS); + + /* + * Disable AE, AF and AWB stat module. Please configure the parameters + * in userspace algorithm if need to enable these switch. + */ + c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL, + ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK, + ISP_TOP_3A_STAT_CRTL_AE_STAT_DIS); + c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL, + ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK, + ISP_TOP_3A_STAT_CRTL_AWB_STAT_DIS); + c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL, + ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK, + ISP_TOP_3A_STAT_CRTL_AF_STAT_DIS); + + c3_isp_write(isp, ISP_LSWB_WB_LIMIT0, + ISP_LSWB_WB_LIMIT0_WB_LIMIT_R_MAX | + ISP_LSWB_WB_LIMIT0_WB_LIMIT_GR_MAX); + c3_isp_write(isp, ISP_LSWB_WB_LIMIT1, + ISP_LSWB_WB_LIMIT1_WB_LIMIT_GB_MAX | + ISP_LSWB_WB_LIMIT1_WB_LIMIT_B_MAX); + + guard(spinlock_irqsave)(¶ms->buff_lock); + + /* Only use the first buffer to initialize ISP */ + params->buff = + list_first_entry_or_null(¶ms->pending, + struct c3_isp_params_buffer, list); + if (params->buff) + c3_isp_params_cfg_blocks(params); +} + +/* V4L2 video operations */ + +static int c3_isp_params_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, C3_ISP_DRIVER_NAME, sizeof(cap->driver)); + strscpy(cap->card, "AML C3 ISP", sizeof(cap->card)); + + return 0; +} + +static int c3_isp_params_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + + f->pixelformat = V4L2_META_FMT_C3ISP_PARAMS; + + return 0; +} + +static int c3_isp_params_g_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct c3_isp_params *params = video_drvdata(file); + + f->fmt.meta = params->vfmt.fmt.meta; + + return 0; +} + +static const struct v4l2_ioctl_ops isp_params_v4l2_ioctl_ops = { + .vidioc_querycap = c3_isp_params_querycap, + .vidioc_enum_fmt_meta_out = c3_isp_params_enum_fmt, + .vidioc_g_fmt_meta_out = c3_isp_params_g_fmt, + .vidioc_s_fmt_meta_out = c3_isp_params_g_fmt, + .vidioc_try_fmt_meta_out = c3_isp_params_g_fmt, + .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_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct v4l2_file_operations isp_params_v4l2_fops = { + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int c3_isp_params_vb2_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + if (*num_planes) { + if (*num_planes != 1) + return -EINVAL; + + if (sizes[0] < sizeof(struct c3_isp_params_cfg)) + return -EINVAL; + + return 0; + } + + *num_planes = 1; + sizes[0] = sizeof(struct c3_isp_params_cfg); + + return 0; +} + +static void c3_isp_params_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb); + struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(v4l2_buf); + struct c3_isp_params *params = vb2_get_drv_priv(vb->vb2_queue); + + guard(spinlock_irqsave)(¶ms->buff_lock); + + list_add_tail(&buf->list, ¶ms->pending); +} + +static int c3_isp_params_vb2_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(vbuf); + struct c3_isp_params *params = vb2_get_drv_priv(vb->vb2_queue); + struct c3_isp_params_cfg *cfg = buf->cfg; + struct c3_isp_params_cfg *usr_cfg = vb2_plane_vaddr(vb, 0); + size_t payload_size = vb2_get_plane_payload(vb, 0); + size_t header_size = offsetof(struct c3_isp_params_cfg, data); + size_t block_offset = 0; + size_t cfg_size; + + /* Payload size can't be greater than the destination buffer size */ + if (payload_size > params->vfmt.fmt.meta.buffersize) { + dev_dbg(params->isp->dev, + "Payload size is too large: %zu\n", payload_size); + return -EINVAL; + } + + /* Payload size can't be smaller than the header size */ + if (payload_size < header_size) { + dev_dbg(params->isp->dev, + "Payload size is too small: %zu\n", payload_size); + return -EINVAL; + } + + /* + * Use the internal scratch buffer to avoid userspace modifying + * the buffer content while the driver is processing it. + */ + memcpy(cfg, usr_cfg, payload_size); + + /* Only v0 is supported at the moment */ + if (cfg->version != C3_ISP_PARAMS_BUFFER_V0) { + dev_dbg(params->isp->dev, + "Invalid params buffer version: %u\n", cfg->version); + return -EINVAL; + } + + /* Validate the size reported in the parameter buffer header */ + cfg_size = header_size + cfg->data_size; + if (cfg_size != payload_size) { + dev_dbg(params->isp->dev, + "Data size %zu and payload size %zu are different\n", + cfg_size, payload_size); + return -EINVAL; + } + + /* Walk the list of parameter blocks and validate them */ + cfg_size = cfg->data_size; + while (cfg_size >= sizeof(struct c3_isp_params_block_header)) { + const struct c3_isp_params_block_header *block; + const struct c3_isp_params_handler *handler; + + block = (struct c3_isp_params_block_header *) + &cfg->data[block_offset]; + + if (block->type >= ARRAY_SIZE(c3_isp_params_handlers)) { + dev_dbg(params->isp->dev, + "Invalid params block type\n"); + return -EINVAL; + } + + if (block->size > cfg_size) { + dev_dbg(params->isp->dev, + "Block size is greater than cfg size\n"); + return -EINVAL; + } + + if ((block->flags & (C3_ISP_PARAMS_BLOCK_FL_ENABLE | + C3_ISP_PARAMS_BLOCK_FL_DISABLE)) == + (C3_ISP_PARAMS_BLOCK_FL_ENABLE | + C3_ISP_PARAMS_BLOCK_FL_DISABLE)) { + dev_dbg(params->isp->dev, + "Invalid parameters block flags\n"); + return -EINVAL; + } + + handler = &c3_isp_params_handlers[block->type]; + if (block->size != handler->size) { + dev_dbg(params->isp->dev, + "Invalid params block size\n"); + return -EINVAL; + } + + block_offset += block->size; + cfg_size -= block->size; + } + + if (cfg_size) { + dev_dbg(params->isp->dev, + "Unexpected data after the params buffer end\n"); + return -EINVAL; + } + + return 0; +} + +static int c3_isp_params_vb2_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb); + struct c3_isp_params *params = vb2_get_drv_priv(vb->vb2_queue); + struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(v4l2_buf); + + buf->cfg = kvmalloc(params->vfmt.fmt.meta.buffersize, GFP_KERNEL); + if (!buf->cfg) + return -ENOMEM; + + return 0; +} + +static void c3_isp_params_vb2_buf_cleanup(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb); + struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(v4l2_buf); + + kvfree(buf->cfg); + buf->cfg = NULL; +} + +static void c3_isp_params_vb2_stop_streaming(struct vb2_queue *q) +{ + struct c3_isp_params *params = vb2_get_drv_priv(q); + struct c3_isp_params_buffer *buff; + + guard(spinlock_irqsave)(¶ms->buff_lock); + + while (!list_empty(¶ms->pending)) { + buff = list_first_entry(¶ms->pending, + struct c3_isp_params_buffer, list); + list_del(&buff->list); + vb2_buffer_done(&buff->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } +} + +static const struct vb2_ops isp_params_vb2_ops = { + .queue_setup = c3_isp_params_vb2_queue_setup, + .buf_queue = c3_isp_params_vb2_buf_queue, + .buf_prepare = c3_isp_params_vb2_buf_prepare, + .buf_init = c3_isp_params_vb2_buf_init, + .buf_cleanup = c3_isp_params_vb2_buf_cleanup, + .stop_streaming = c3_isp_params_vb2_stop_streaming, +}; + +int c3_isp_params_register(struct c3_isp_device *isp) +{ + struct c3_isp_params *params = &isp->params; + struct video_device *vdev = ¶ms->vdev; + struct vb2_queue *vb2_q = ¶ms->vb2_q; + int ret; + + memset(params, 0, sizeof(*params)); + params->vfmt.fmt.meta.dataformat = V4L2_META_FMT_C3ISP_PARAMS; + params->vfmt.fmt.meta.buffersize = sizeof(struct c3_isp_params_cfg); + params->isp = isp; + INIT_LIST_HEAD(¶ms->pending); + spin_lock_init(¶ms->buff_lock); + mutex_init(¶ms->lock); + + snprintf(vdev->name, sizeof(vdev->name), "c3-isp-params"); + vdev->fops = &isp_params_v4l2_fops; + vdev->ioctl_ops = &isp_params_v4l2_ioctl_ops; + vdev->v4l2_dev = &isp->v4l2_dev; + vdev->lock = ¶ms->lock; + vdev->minor = -1; + vdev->queue = vb2_q; + vdev->release = video_device_release_empty; + vdev->device_caps = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING; + vdev->vfl_dir = VFL_DIR_TX; + video_set_drvdata(vdev, params); + + vb2_q->drv_priv = params; + vb2_q->mem_ops = &vb2_vmalloc_memops; + vb2_q->ops = &isp_params_vb2_ops; + vb2_q->type = V4L2_BUF_TYPE_META_OUTPUT; + vb2_q->io_modes = VB2_DMABUF | VB2_MMAP; + vb2_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vb2_q->buf_struct_size = sizeof(struct c3_isp_params_buffer); + vb2_q->dev = isp->dev; + vb2_q->lock = ¶ms->lock; + vb2_q->min_queued_buffers = 1; + + ret = vb2_queue_init(vb2_q); + if (ret) + goto err_detroy; + + params->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&vdev->entity, 1, ¶ms->pad); + if (ret) + goto err_queue_release; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + dev_err(isp->dev, + "Failed to register %s: %d\n", vdev->name, ret); + goto err_entity_cleanup; + } + + return 0; + +err_entity_cleanup: + media_entity_cleanup(&vdev->entity); +err_queue_release: + vb2_queue_release(vb2_q); +err_detroy: + mutex_destroy(¶ms->lock); + return ret; +} + +void c3_isp_params_unregister(struct c3_isp_device *isp) +{ + struct c3_isp_params *params = &isp->params; + + vb2_queue_release(¶ms->vb2_q); + media_entity_cleanup(¶ms->vdev.entity); + video_unregister_device(¶ms->vdev); + mutex_destroy(¶ms->lock); +} + +void c3_isp_params_isr(struct c3_isp_device *isp) +{ + struct c3_isp_params *params = &isp->params; + + guard(spinlock_irqsave)(¶ms->buff_lock); + + params->buff = + list_first_entry_or_null(¶ms->pending, + struct c3_isp_params_buffer, list); + if (!params->buff) + return; + + list_del(¶ms->buff->list); + + c3_isp_params_cfg_blocks(params); + + params->buff->vb.sequence = params->isp->frm_sequence; + params->buff->vb.vb2_buf.timestamp = ktime_get(); + params->buff->vb.field = V4L2_FIELD_NONE; + vb2_buffer_done(¶ms->buff->vb.vb2_buf, VB2_BUF_STATE_DONE); +} diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-regs.h b/drivers/media/platform/amlogic/c3/isp/c3-isp-regs.h new file mode 100644 index 000000000000..fa249985a771 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-regs.h @@ -0,0 +1,618 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */ +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + */ + +#ifndef __C3_ISP_REGS_H__ +#define __C3_ISP_REGS_H__ + +#define ISP_TOP_INPUT_SIZE 0x0000 +#define ISP_TOP_INPUT_SIZE_VERT_SIZE_MASK GENMASK(15, 0) +#define ISP_TOP_INPUT_SIZE_VERT_SIZE(x) ((x) << 0) +#define ISP_TOP_INPUT_SIZE_HORIZ_SIZE_MASK GENMASK(31, 16) +#define ISP_TOP_INPUT_SIZE_HORIZ_SIZE(x) ((x) << 16) + +#define ISP_TOP_FRM_SIZE 0x0004 +#define ISP_TOP_FRM_SIZE_CORE_VERT_SIZE_MASK GENMASK(15, 0) +#define ISP_TOP_FRM_SIZE_CORE_VERT_SIZE(x) ((x) << 0) +#define ISP_TOP_FRM_SIZE_CORE_HORIZ_SIZE_MASK GENMASK(31, 16) +#define ISP_TOP_FRM_SIZE_CORE_HORIZ_SIZE(x) ((x) << 16) + +#define ISP_TOP_HOLD_SIZE 0x0008 +#define ISP_TOP_HOLD_SIZE_CORE_HORIZ_SIZE_MASK GENMASK(31, 16) +#define ISP_TOP_HOLD_SIZE_CORE_HORIZ_SIZE(x) ((x) << 16) + +#define ISP_TOP_PATH_EN 0x0010 +#define ISP_TOP_PATH_EN_DISP0_EN_MASK BIT(0) +#define ISP_TOP_PATH_EN_DISP0_EN BIT(0) +#define ISP_TOP_PATH_EN_DISP0_DIS (0 << 0) +#define ISP_TOP_PATH_EN_DISP1_EN_MASK BIT(1) +#define ISP_TOP_PATH_EN_DISP1_EN BIT(1) +#define ISP_TOP_PATH_EN_DISP1_DIS (0 << 1) +#define ISP_TOP_PATH_EN_DISP2_EN_MASK BIT(2) +#define ISP_TOP_PATH_EN_DISP2_EN BIT(2) +#define ISP_TOP_PATH_EN_DISP2_DIS (0 << 2) +#define ISP_TOP_PATH_EN_WRMIF0_EN_MASK BIT(8) +#define ISP_TOP_PATH_EN_WRMIF0_EN BIT(8) +#define ISP_TOP_PATH_EN_WRMIF0_DIS (0 << 8) +#define ISP_TOP_PATH_EN_WRMIF1_EN_MASK BIT(9) +#define ISP_TOP_PATH_EN_WRMIF1_EN BIT(9) +#define ISP_TOP_PATH_EN_WRMIF1_DIS (0 << 9) +#define ISP_TOP_PATH_EN_WRMIF2_EN_MASK BIT(10) +#define ISP_TOP_PATH_EN_WRMIF2_EN BIT(10) +#define ISP_TOP_PATH_EN_WRMIF2_DIS (0 << 10) + +#define ISP_TOP_PATH_SEL 0x0014 +#define ISP_TOP_PATH_SEL_CORE_MASK GENMASK(18, 16) +#define ISP_TOP_PATH_SEL_CORE_CORE_DIS (0 << 16) +#define ISP_TOP_PATH_SEL_CORE_MIPI_CORE BIT(16) + +#define ISP_TOP_DISPIN_SEL 0x0018 +#define ISP_TOP_DISPIN_SEL_DISP0_MASK GENMASK(3, 0) +#define ISP_TOP_DISPIN_SEL_DISP0_CORE_OUT (0 << 0) +#define ISP_TOP_DISPIN_SEL_DISP0_MIPI_OUT (2 << 0) +#define ISP_TOP_DISPIN_SEL_DISP1_MASK GENMASK(7, 4) +#define ISP_TOP_DISPIN_SEL_DISP1_CORE_OUT (0 << 4) +#define ISP_TOP_DISPIN_SEL_DISP1_MIPI_OUT (2 << 4) +#define ISP_TOP_DISPIN_SEL_DISP2_MASK GENMASK(11, 8) +#define ISP_TOP_DISPIN_SEL_DISP2_CORE_OUT (0 << 8) +#define ISP_TOP_DISPIN_SEL_DISP2_MIPI_OUT (2 << 8) + +#define ISP_TOP_IRQ_EN 0x0080 +#define ISP_TOP_IRQ_EN_FRM_END_MASK BIT(0) +#define ISP_TOP_IRQ_EN_FRM_END_EN BIT(0) +#define ISP_TOP_IRQ_EN_FRM_END_DIS (0 << 0) +#define ISP_TOP_IRQ_EN_FRM_RST_MASK BIT(1) +#define ISP_TOP_IRQ_EN_FRM_RST_EN BIT(1) +#define ISP_TOP_IRQ_EN_FRM_RST_DIS (0 << 1) +#define ISP_TOP_IRQ_EN_3A_DMA_ERR_MASK BIT(5) +#define ISP_TOP_IRQ_EN_3A_DMA_ERR_EN BIT(5) +#define ISP_TOP_IRQ_EN_3A_DMA_ERR_DIS (0 << 5) + +#define ISP_TOP_IRQ_CLR 0x0084 +#define ISP_TOP_RO_IRQ_STAT 0x01c4 +#define ISP_TOP_RO_IRQ_STAT_FRM_END_MASK BIT(0) +#define ISP_TOP_RO_IRQ_STAT_FRM_RST_MASK BIT(1) +#define ISP_TOP_RO_IRQ_STAT_3A_DMA_ERR_MASK BIT(5) + +#define ISP_TOP_MODE_CTRL 0x0400 +#define ISP_TOP_FEO_CTRL0 0x040c +#define ISP_TOP_FEO_CTRL0_INPUT_FMT_EN_MASK BIT(8) +#define ISP_TOP_FEO_CTRL0_INPUT_FMT_DIS (0 << 8) +#define ISP_TOP_FEO_CTRL0_INPUT_FMT_EN BIT(8) + +#define ISP_TOP_FEO_CTRL1_0 0x0410 +#define ISP_TOP_FEO_CTRL1_0_DPC_EN_MASK BIT(3) +#define ISP_TOP_FEO_CTRL1_0_DPC_DIS (0 << 3) +#define ISP_TOP_FEO_CTRL1_0_DPC_EN BIT(3) +#define ISP_TOP_FEO_CTRL1_0_OG_EN_MASK BIT(5) +#define ISP_TOP_FEO_CTRL1_0_OG_DIS (0 << 5) +#define ISP_TOP_FEO_CTRL1_0_OG_EN BIT(5) + +#define ISP_TOP_FED_CTRL 0x0418 +#define ISP_TOP_FED_CTRL_PDPC_EN_MASK BIT(1) +#define ISP_TOP_FED_CTRL_PDPC_DIS (0 << 1) +#define ISP_TOP_FED_CTRL_PDPC_EN BIT(1) +#define ISP_TOP_FED_CTRL_RAWCNR_EN_MASK GENMASK(6, 5) +#define ISP_TOP_FED_CTRL_RAWCNR_DIS (0 << 5) +#define ISP_TOP_FED_CTRL_RAWCNR_EN BIT(5) +#define ISP_TOP_FED_CTRL_SNR1_EN_MASK BIT(9) +#define ISP_TOP_FED_CTRL_SNR1_DIS (0 << 9) +#define ISP_TOP_FED_CTRL_SNR1_EN BIT(9) +#define ISP_TOP_FED_CTRL_TNR0_EN_MASK BIT(11) +#define ISP_TOP_FED_CTRL_TNR0_DIS (0 << 11) +#define ISP_TOP_FED_CTRL_TNR0_EN BIT(11) +#define ISP_TOP_FED_CTRL_CUBIC_CS_EN_MASK BIT(12) +#define ISP_TOP_FED_CTRL_CUBIC_CS_DIS (0 << 12) +#define ISP_TOP_FED_CTRL_CUBIC_CS_EN BIT(12) +#define ISP_TOP_FED_CTRL_SQRT_EN_MASK BIT(14) +#define ISP_TOP_FED_CTRL_SQRT_DIS (0 << 14) +#define ISP_TOP_FED_CTRL_SQRT_EN BIT(14) +#define ISP_TOP_FED_CTRL_DGAIN_EN_MASK BIT(16) +#define ISP_TOP_FED_CTRL_DGAIN_DIS (0 << 16) +#define ISP_TOP_FED_CTRL_DGAIN_EN BIT(16) + +#define ISP_TOP_BEO_CTRL 0x041c +#define ISP_TOP_BEO_CTRL_WB_EN_MASK BIT(6) +#define ISP_TOP_BEO_CTRL_WB_DIS (0 << 6) +#define ISP_TOP_BEO_CTRL_WB_EN BIT(6) +#define ISP_TOP_BEO_CTRL_BLC_EN_MASK BIT(7) +#define ISP_TOP_BEO_CTRL_BLC_DIS (0 << 7) +#define ISP_TOP_BEO_CTRL_BLC_EN BIT(7) +#define ISP_TOP_BEO_CTRL_INV_DGAIN_EN_MASK BIT(8) +#define ISP_TOP_BEO_CTRL_INV_DGAIN_DIS (0 << 8) +#define ISP_TOP_BEO_CTRL_INV_DGAIN_EN BIT(8) +#define ISP_TOP_BEO_CTRL_EOTF_EN_MASK BIT(9) +#define ISP_TOP_BEO_CTRL_EOTF_DIS (0 << 9) +#define ISP_TOP_BEO_CTRL_EOTF_EN BIT(9) + +#define ISP_TOP_BED_CTRL 0x0420 +#define ISP_TOP_BED_CTRL_YHS_STAT_EN_MASK GENMASK(1, 0) +#define ISP_TOP_BED_CTRL_YHS_STAT_DIS (0 << 0) +#define ISP_TOP_BED_CTRL_YHS_STAT_EN BIT(0) +#define ISP_TOP_BED_CTRL_GRPH_STAT_EN_MASK BIT(2) +#define ISP_TOP_BED_CTRL_GRPH_STAT_DIS (0 << 2) +#define ISP_TOP_BED_CTRL_GRPH_STAT_EN BIT(2) +#define ISP_TOP_BED_CTRL_FMETER_EN_MASK BIT(3) +#define ISP_TOP_BED_CTRL_FMETER_DIS (0 << 3) +#define ISP_TOP_BED_CTRL_FMETER_EN BIT(3) +#define ISP_TOP_BED_CTRL_BSC_EN_MASK BIT(10) +#define ISP_TOP_BED_CTRL_BSC_DIS (0 << 10) +#define ISP_TOP_BED_CTRL_BSC_EN BIT(10) +#define ISP_TOP_BED_CTRL_CNR2_EN_MASK BIT(11) +#define ISP_TOP_BED_CTRL_CNR2_DIS (0 << 11) +#define ISP_TOP_BED_CTRL_CNR2_EN BIT(11) +#define ISP_TOP_BED_CTRL_CM1_EN_MASK BIT(13) +#define ISP_TOP_BED_CTRL_CM1_DIS (0 << 13) +#define ISP_TOP_BED_CTRL_CM1_EN BIT(13) +#define ISP_TOP_BED_CTRL_CM0_EN_MASK BIT(14) +#define ISP_TOP_BED_CTRL_CM0_DIS (0 << 14) +#define ISP_TOP_BED_CTRL_CM0_EN BIT(14) +#define ISP_TOP_BED_CTRL_PST_GAMMA_EN_MASK BIT(16) +#define ISP_TOP_BED_CTRL_PST_GAMMA_DIS (0 << 16) +#define ISP_TOP_BED_CTRL_PST_GAMMA_EN BIT(16) +#define ISP_TOP_BED_CTRL_LUT3D_EN_MASK BIT(17) +#define ISP_TOP_BED_CTRL_LUT3D_DIS (0 << 17) +#define ISP_TOP_BED_CTRL_LUT3D_EN BIT(17) +#define ISP_TOP_BED_CTRL_CCM_EN_MASK BIT(18) +#define ISP_TOP_BED_CTRL_CCM_DIS (0 << 18) +#define ISP_TOP_BED_CTRL_CCM_EN BIT(18) +#define ISP_TOP_BED_CTRL_PST_TNR_LITE_EN_MASK BIT(21) +#define ISP_TOP_BED_CTRL_PST_TNR_LITE_DIS (0 << 21) +#define ISP_TOP_BED_CTRL_PST_TNR_LITE_EN BIT(21) +#define ISP_TOP_BED_CTRL_AMCM_EN_MASK BIT(25) +#define ISP_TOP_BED_CTRL_AMCM_DIS (0 << 25) +#define ISP_TOP_BED_CTRL_AMCM_EN BIT(25) + +#define ISP_TOP_3A_STAT_CRTL 0x0424 +#define ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK BIT(0) +#define ISP_TOP_3A_STAT_CRTL_AE_STAT_DIS (0 << 0) +#define ISP_TOP_3A_STAT_CRTL_AE_STAT_EN BIT(0) +#define ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK BIT(1) +#define ISP_TOP_3A_STAT_CRTL_AWB_STAT_DIS (0 << 1) +#define ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN BIT(1) +#define ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK BIT(2) +#define ISP_TOP_3A_STAT_CRTL_AF_STAT_DIS (0 << 2) +#define ISP_TOP_3A_STAT_CRTL_AF_STAT_EN BIT(2) +#define ISP_TOP_3A_STAT_CRTL_AWB_POINT_MASK GENMASK(6, 4) +#define ISP_TOP_3A_STAT_CRTL_AWB_POINT(x) ((x) << 4) +#define ISP_TOP_3A_STAT_CRTL_AE_POINT_MASK GENMASK(9, 8) +#define ISP_TOP_3A_STAT_CRTL_AE_POINT(x) ((x) << 8) +#define ISP_TOP_3A_STAT_CRTL_AF_POINT_MASK GENMASK(13, 12) +#define ISP_TOP_3A_STAT_CRTL_AF_POINT(x) ((x) << 12) + +#define ISP_LSWB_BLC_OFST0 0x4028 +#define ISP_LSWB_BLC_OFST0_R_OFST_MASK GENMASK(15, 0) +#define ISP_LSWB_BLC_OFST0_R_OFST(x) ((x) << 0) +#define ISP_LSWB_BLC_OFST0_GR_OFST_MASK GENMASK(31, 16) +#define ISP_LSWB_BLC_OFST0_GR_OFST(x) ((x) << 16) + +#define ISP_LSWB_BLC_OFST1 0x402c +#define ISP_LSWB_BLC_OFST1_GB_OFST_MASK GENMASK(15, 0) +#define ISP_LSWB_BLC_OFST1_GB_OFST(x) ((x) << 0) +#define ISP_LSWB_BLC_OFST1_B_OFST_MASK GENMASK(31, 16) +#define ISP_LSWB_BLC_OFST1_B_OFST(x) ((x) << 16) + +#define ISP_LSWB_BLC_PHSOFST 0x4034 +#define ISP_LSWB_BLC_PHSOFST_VERT_OFST_MASK GENMASK(1, 0) +#define ISP_LSWB_BLC_PHSOFST_VERT_OFST(x) ((x) << 0) +#define ISP_LSWB_BLC_PHSOFST_HORIZ_OFST_MASK GENMASK(3, 2) +#define ISP_LSWB_BLC_PHSOFST_HORIZ_OFST(x) ((x) << 2) + +#define ISP_LSWB_WB_GAIN0 0x4038 +#define ISP_LSWB_WB_GAIN0_R_GAIN_MASK GENMASK(11, 0) +#define ISP_LSWB_WB_GAIN0_R_GAIN(x) ((x) << 0) +#define ISP_LSWB_WB_GAIN0_GR_GAIN_MASK GENMASK(27, 16) +#define ISP_LSWB_WB_GAIN0_GR_GAIN(x) ((x) << 16) + +#define ISP_LSWB_WB_GAIN1 0x403c +#define ISP_LSWB_WB_GAIN1_GB_GAIN_MASK GENMASK(11, 0) +#define ISP_LSWB_WB_GAIN1_GB_GAIN(x) ((x) << 0) +#define ISP_LSWB_WB_GAIN1_B_GAIN_MASK GENMASK(27, 16) +#define ISP_LSWB_WB_GAIN1_B_GAIN(x) ((x) << 16) + +#define ISP_LSWB_WB_GAIN2 0x4040 +#define ISP_LSWB_WB_GAIN2_IR_GAIN_MASK GENMASK(11, 0) +#define ISP_LSWB_WB_GAIN2_IR_GAIN(x) ((x) << 0) + +#define ISP_LSWB_WB_LIMIT0 0x4044 +#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_R_MASK GENMASK(15, 0) +#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_R(x) ((x) << 0) +#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_R_MAX (0x8fff << 0) +#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_GR_MASK GENMASK(31, 16) +#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_GR(x) ((x) << 16) +#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_GR_MAX (0x8fff << 16) + +#define ISP_LSWB_WB_LIMIT1 0x4048 +#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_GB_MASK GENMASK(15, 0) +#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_GB(x) ((x) << 0) +#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_GB_MAX (0x8fff << 0) +#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_B_MASK GENMASK(31, 16) +#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_B(x) ((x) << 16) +#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_B_MAX (0x8fff << 16) + +#define ISP_LSWB_WB_PHSOFST 0x4050 +#define ISP_LSWB_WB_PHSOFST_VERT_OFST_MASK GENMASK(1, 0) +#define ISP_LSWB_WB_PHSOFST_VERT_OFST(x) ((x) << 0) +#define ISP_LSWB_WB_PHSOFST_HORIZ_OFST_MASK GENMASK(3, 2) +#define ISP_LSWB_WB_PHSOFST_HORIZ_OFST(x) ((x) << 2) + +#define ISP_LSWB_LNS_PHSOFST 0x4054 +#define ISP_LSWB_LNS_PHSOFST_VERT_OFST_MASK GENMASK(1, 0) +#define ISP_LSWB_LNS_PHSOFST_VERT_OFST(x) ((x) << 0) +#define ISP_LSWB_LNS_PHSOFST_HORIZ_OFST_MASK GENMASK(3, 2) +#define ISP_LSWB_LNS_PHSOFST_HORIZ_OFST(x) ((x) << 2) + +#define ISP_DMS_COMMON_PARAM0 0x5000 +#define ISP_DMS_COMMON_PARAM0_VERT_PHS_OFST_MASK GENMASK(1, 0) +#define ISP_DMS_COMMON_PARAM0_VERT_PHS_OFST(x) ((x) << 0) +#define ISP_DMS_COMMON_PARAM0_HORIZ_PHS_OFST_MASK GENMASK(3, 2) +#define ISP_DMS_COMMON_PARAM0_HORIZ_PHS_OFST(x) ((x) << 2) + +#define ISP_CM0_COEF00_01 0x6048 +#define ISP_CM0_COEF00_01_MTX_00_MASK GENMASK(12, 0) +#define ISP_CM0_COEF00_01_MTX_00(x) ((x) << 0) +#define ISP_CM0_COEF00_01_MTX_01_MASK GENMASK(28, 16) +#define ISP_CM0_COEF00_01_MTX_01(x) ((x) << 16) + +#define ISP_CM0_COEF02_10 0x604c +#define ISP_CM0_COEF02_10_MTX_02_MASK GENMASK(12, 0) +#define ISP_CM0_COEF02_10_MTX_02(x) ((x) << 0) +#define ISP_CM0_COEF02_10_MTX_10_MASK GENMASK(28, 16) +#define ISP_CM0_COEF02_10_MTX_10(x) ((x) << 16) + +#define ISP_CM0_COEF11_12 0x6050 +#define ISP_CM0_COEF11_12_MTX_11_MASK GENMASK(12, 0) +#define ISP_CM0_COEF11_12_MTX_11(x) ((x) << 0) +#define ISP_CM0_COEF11_12_MTX_12_MASK GENMASK(28, 16) +#define ISP_CM0_COEF11_12_MTX_12(x) ((x) << 16) + +#define ISP_CM0_COEF20_21 0x6054 +#define ISP_CM0_COEF20_21_MTX_20_MASK GENMASK(12, 0) +#define ISP_CM0_COEF20_21_MTX_20(x) ((x) << 0) +#define ISP_CM0_COEF20_21_MTX_21_MASK GENMASK(28, 16) +#define ISP_CM0_COEF20_21_MTX_21(x) ((x) << 16) + +#define ISP_CM0_COEF22_OUP_OFST0 0x6058 +#define ISP_CM0_COEF22_OUP_OFST0_MTX_22_MASK GENMASK(12, 0) +#define ISP_CM0_COEF22_OUP_OFST0_MTX_22(x) ((x) << 0) + +#define ISP_CCM_MTX_00_01 0x6098 +#define ISP_CCM_MTX_00_01_MTX_00_MASK GENMASK(12, 0) +#define ISP_CCM_MTX_00_01_MTX_00(x) ((x) << 0) +#define ISP_CCM_MTX_00_01_MTX_01_MASK GENMASK(28, 16) +#define ISP_CCM_MTX_00_01_MTX_01(x) ((x) << 16) + +#define ISP_CCM_MTX_02_03 0x609c +#define ISP_CCM_MTX_02_03_MTX_02_MASK GENMASK(12, 0) +#define ISP_CCM_MTX_02_03_MTX_02(x) ((x) << 0) + +#define ISP_CCM_MTX_10_11 0x60A0 +#define ISP_CCM_MTX_10_11_MTX_10_MASK GENMASK(12, 0) +#define ISP_CCM_MTX_10_11_MTX_10(x) ((x) << 0) +#define ISP_CCM_MTX_10_11_MTX_11_MASK GENMASK(28, 16) +#define ISP_CCM_MTX_10_11_MTX_11(x) ((x) << 16) + +#define ISP_CCM_MTX_12_13 0x60A4 +#define ISP_CCM_MTX_12_13_MTX_12_MASK GENMASK(12, 0) +#define ISP_CCM_MTX_12_13_MTX_12(x) ((x) << 0) + +#define ISP_CCM_MTX_20_21 0x60A8 +#define ISP_CCM_MTX_20_21_MTX_20_MASK GENMASK(12, 0) +#define ISP_CCM_MTX_20_21_MTX_20(x) ((x) << 0) +#define ISP_CCM_MTX_20_21_MTX_21_MASK GENMASK(28, 16) +#define ISP_CCM_MTX_20_21_MTX_21(x) ((x) << 16) + +#define ISP_CCM_MTX_22_23_RS 0x60Ac +#define ISP_CCM_MTX_22_23_RS_MTX_22_MASK GENMASK(12, 0) +#define ISP_CCM_MTX_22_23_RS_MTX_22(x) ((x) << 0) + +#define ISP_PST_GAMMA_LUT_ADDR 0x60cc +#define ISP_PST_GAMMA_LUT_ADDR_IDX_ADDR(x) ((x) << 7) + +#define ISP_PST_GAMMA_LUT_DATA 0x60d0 +#define ISP_PST_GM_LUT_DATA0(x) (((x) & GENMASK(15, 0)) << 0) +#define ISP_PST_GM_LUT_DATA1(x) (((x) & GENMASK(15, 0)) << 16) + +#define DISP0_TOP_TOP_CTRL 0x8000 +#define DISP0_TOP_TOP_CTRL_CROP2_EN_MASK BIT(5) +#define DISP0_TOP_TOP_CTRL_CROP2_EN BIT(5) +#define DISP0_TOP_TOP_CTRL_CROP2_DIS (0 << 5) + +#define DISP0_TOP_CRP2_START 0x8004 +#define DISP0_TOP_CRP2_START_V_START_MASK GENMASK(15, 0) +#define DISP0_TOP_CRP2_START_V_START(x) ((x) << 0) +#define DISP0_TOP_CRP2_START_H_START_MASK GENMASK(31, 16) +#define DISP0_TOP_CRP2_START_H_START(x) ((x) << 16) + +#define DISP0_TOP_CRP2_SIZE 0x8008 +#define DISP0_TOP_CRP2_SIZE_V_SIZE_MASK GENMASK(15, 0) +#define DISP0_TOP_CRP2_SIZE_V_SIZE(x) ((x) << 0) +#define DISP0_TOP_CRP2_SIZE_H_SIZE_MASK GENMASK(31, 16) +#define DISP0_TOP_CRP2_SIZE_H_SIZE(x) ((x) << 16) + +#define DISP0_TOP_OUT_SIZE 0x800c +#define DISP0_TOP_OUT_SIZE_SCL_OUT_HEIGHT_MASK GENMASK(12, 0) +#define DISP0_TOP_OUT_SIZE_SCL_OUT_HEIGHT(x) ((x) << 0) +#define DISP0_TOP_OUT_SIZE_SCL_OUT_WIDTH_MASK GENMASK(28, 16) +#define DISP0_TOP_OUT_SIZE_SCL_OUT_WIDTH(x) ((x) << 16) + +#define ISP_DISP0_TOP_IN_SIZE 0x804c +#define ISP_DISP0_TOP_IN_SIZE_VSIZE_MASK GENMASK(12, 0) +#define ISP_DISP0_TOP_IN_SIZE_VSIZE(x) ((x) << 0) +#define ISP_DISP0_TOP_IN_SIZE_HSIZE_MASK GENMASK(28, 16) +#define ISP_DISP0_TOP_IN_SIZE_HSIZE(x) ((x) << 16) + +#define DISP0_PPS_SCALE_EN 0x8200 +#define DISP0_PPS_SCALE_EN_VSC_TAP_NUM_MASK GENMASK(3, 0) +#define DISP0_PPS_SCALE_EN_VSC_TAP_NUM(x) ((x) << 0) +#define DISP0_PPS_SCALE_EN_HSC_TAP_NUM_MASK GENMASK(7, 4) +#define DISP0_PPS_SCALE_EN_HSC_TAP_NUM(x) ((x) << 4) +#define DISP0_PPS_SCALE_EN_PREVSC_FLT_NUM_MASK GENMASK(11, 8) +#define DISP0_PPS_SCALE_EN_PREVSC_FLT_NUM(x) ((x) << 8) +#define DISP0_PPS_SCALE_EN_PREHSC_FLT_NUM_MASK GENMASK(15, 12) +#define DISP0_PPS_SCALE_EN_PREHSC_FLT_NUM(x) ((x) << 12) +#define DISP0_PPS_SCALE_EN_PREVSC_RATE_MASK GENMASK(17, 16) +#define DISP0_PPS_SCALE_EN_PREVSC_RATE(x) ((x) << 16) +#define DISP0_PPS_SCALE_EN_PREHSC_RATE_MASK GENMASK(19, 18) +#define DISP0_PPS_SCALE_EN_PREHSC_RATE(x) ((x) << 18) +#define DISP0_PPS_SCALE_EN_HSC_EN_MASK BIT(20) +#define DISP0_PPS_SCALE_EN_HSC_EN(x) ((x) << 20) +#define DISP0_PPS_SCALE_EN_HSC_DIS (0 << 20) +#define DISP0_PPS_SCALE_EN_VSC_EN_MASK BIT(21) +#define DISP0_PPS_SCALE_EN_VSC_EN(x) ((x) << 21) +#define DISP0_PPS_SCALE_EN_VSC_DIS (0 << 21) +#define DISP0_PPS_SCALE_EN_PREVSC_EN_MASK BIT(22) +#define DISP0_PPS_SCALE_EN_PREVSC_EN(x) ((x) << 22) +#define DISP0_PPS_SCALE_EN_PREVSC_DIS (0 << 22) +#define DISP0_PPS_SCALE_EN_PREHSC_EN_MASK BIT(23) +#define DISP0_PPS_SCALE_EN_PREHSC_EN(x) ((x) << 23) +#define DISP0_PPS_SCALE_EN_PREHSC_DIS (0 << 23) +#define DISP0_PPS_SCALE_EN_HSC_NOR_RS_BITS_MASK GENMASK(27, 24) +#define DISP0_PPS_SCALE_EN_HSC_NOR_RS_BITS(x) ((x) << 24) +#define DISP0_PPS_SCALE_EN_VSC_NOR_RS_BITS_MASK GENMASK(31, 28) +#define DISP0_PPS_SCALE_EN_VSC_NOR_RS_BITS(x) ((x) << 28) + +#define DISP0_PPS_VSC_START_PHASE_STEP 0x8224 +#define DISP0_PPS_VSC_START_PHASE_STEP_VERT_FRAC_MASK GENMASK(23, 0) +#define DISP0_PPS_VSC_START_PHASE_STEP_VERT_FRAC(x) ((x) << 0) +#define DISP0_PPS_VSC_START_PHASE_STEP_VERT_INTE_MASK GENMASK(27, 24) +#define DISP0_PPS_VSC_START_PHASE_STEP_VERT_INTE(x) ((x) << 24) + +#define DISP0_PPS_HSC_START_PHASE_STEP 0x8230 +#define DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_FRAC_MASK GENMASK(23, 0) +#define DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_FRAC(x) ((x) << 0) +#define DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_INTE_MASK GENMASK(27, 24) +#define DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_INTE(x) ((x) << 24) + +#define DISP0_PPS_444TO422 0x823c +#define DISP0_PPS_444TO422_EN_MASK BIT(0) +#define DISP0_PPS_444TO422_EN(x) ((x) << 0) + +#define ISP_SCALE0_COEF_IDX_LUMA 0x8240 +#define ISP_SCALE0_COEF_IDX_LUMA_COEF_S11_MODE_MASK BIT(9) +#define ISP_SCALE0_COEF_IDX_LUMA_COEF_S11_MODE_EN BIT(9) +#define ISP_SCALE0_COEF_IDX_LUMA_COEF_S11_MODE_DIS (0 << 9) +#define ISP_SCALE0_COEF_IDX_LUMA_CTYPE_MASK GENMASK(12, 10) +#define ISP_SCALE0_COEF_IDX_LUMA_CTYPE(x) ((x) << 10) + +#define ISP_SCALE0_COEF_LUMA 0x8244 +#define ISP_SCALE0_COEF_LUMA_DATA1(x) (((x) & GENMASK(10, 0)) << 0) +#define ISP_SCALE0_COEF_LUMA_DATA0(x) (((x) & GENMASK(10, 0)) << 16) + +#define ISP_SCALE0_COEF_IDX_CHRO 0x8248 +#define ISP_SCALE0_COEF_IDX_CHRO_COEF_S11_MODE_MASK BIT(9) +#define ISP_SCALE0_COEF_IDX_CHRO_COEF_S11_MODE_EN BIT(9) +#define ISP_SCALE0_COEF_IDX_CHRO_COEF_S11_MODE_DIS (0 << 9) +#define ISP_SCALE0_COEF_IDX_CHRO_CTYPE_MASK GENMASK(12, 10) +#define ISP_SCALE0_COEF_IDX_CHRO_CTYPE(x) ((x) << 10) + +#define ISP_SCALE0_COEF_CHRO 0x824c +#define ISP_SCALE0_COEF_CHRO_DATA1(x) (((x) & GENMASK(10, 0)) << 0) +#define ISP_SCALE0_COEF_CHRO_DATA0(x) (((x) & GENMASK(10, 0)) << 16) + +#define ISP_AF_CTRL 0xa044 +#define ISP_AF_CTRL_VERT_OFST_MASK GENMASK(15, 14) +#define ISP_AF_CTRL_VERT_OFST(x) ((x) << 14) +#define ISP_AF_CTRL_HORIZ_OFST_MASK GENMASK(17, 16) +#define ISP_AF_CTRL_HORIZ_OFST(x) ((x) << 16) + +#define ISP_AF_HV_SIZE 0xa04c +#define ISP_AF_HV_SIZE_GLB_WIN_YSIZE_MASK GENMASK(15, 0) +#define ISP_AF_HV_SIZE_GLB_WIN_YSIZE(x) ((x) << 0) +#define ISP_AF_HV_SIZE_GLB_WIN_XSIZE_MASK GENMASK(31, 16) +#define ISP_AF_HV_SIZE_GLB_WIN_XSIZE(x) ((x) << 16) + +#define ISP_AF_HV_BLKNUM 0xa050 +#define ISP_AF_HV_BLKNUM_V_NUM_MASK GENMASK(5, 0) +#define ISP_AF_HV_BLKNUM_V_NUM(x) ((x) << 0) +#define ISP_AF_HV_BLKNUM_H_NUM_MASK GENMASK(21, 16) +#define ISP_AF_HV_BLKNUM_H_NUM(x) ((x) << 16) + +#define ISP_AF_EN_CTRL 0xa054 +#define ISP_AF_EN_CTRL_STAT_SEL_MASK BIT(21) +#define ISP_AF_EN_CTRL_STAT_SEL_OLD (0 << 21) +#define ISP_AF_EN_CTRL_STAT_SEL_NEW BIT(21) + +#define ISP_AF_IDX_ADDR 0xa1c0 +#define ISP_AF_IDX_DATA 0xa1c4 +#define ISP_AF_IDX_DATA_VIDX_DATA(x) (((x) & GENMASK(15, 0)) << 0) +#define ISP_AF_IDX_DATA_HIDX_DATA(x) (((x) & GENMASK(15, 0)) << 16) + +#define ISP_AE_CTRL 0xa448 +#define ISP_AE_CTRL_INPUT_2LINE_MASK BIT(7) +#define ISP_AE_CTRL_INPUT_2LINE_EN BIT(7) +#define ISP_AE_CTRL_INPUT_2LINE_DIS (0 << 7) +#define ISP_AE_CTRL_LUMA_MODE_MASK GENMASK(9, 8) +#define ISP_AE_CTRL_LUMA_MODE_CUR (0 << 8) +#define ISP_AE_CTRL_LUMA_MODE_MAX BIT(8) +#define ISP_AE_CTRL_LUMA_MODE_FILTER (2 << 8) +#define ISP_AE_CTRL_VERT_OFST_MASK GENMASK(25, 24) +#define ISP_AE_CTRL_VERT_OFST(x) ((x) << 24) +#define ISP_AE_CTRL_HORIZ_OFST_MASK GENMASK(27, 26) +#define ISP_AE_CTRL_HORIZ_OFST(x) ((x) << 26) + +#define ISP_AE_HV_SIZE 0xa464 +#define ISP_AE_HV_SIZE_VERT_SIZE_MASK GENMASK(15, 0) +#define ISP_AE_HV_SIZE_VERT_SIZE(x) ((x) << 0) +#define ISP_AE_HV_SIZE_HORIZ_SIZE_MASK GENMASK(31, 16) +#define ISP_AE_HV_SIZE_HORIZ_SIZE(x) ((x) << 16) + +#define ISP_AE_HV_BLKNUM 0xa468 +#define ISP_AE_HV_BLKNUM_V_NUM_MASK GENMASK(6, 0) +#define ISP_AE_HV_BLKNUM_V_NUM(x) ((x) << 0) +#define ISP_AE_HV_BLKNUM_H_NUM_MASK GENMASK(22, 16) +#define ISP_AE_HV_BLKNUM_H_NUM(x) ((x) << 16) + +#define ISP_AE_IDX_ADDR 0xa600 +#define ISP_AE_IDX_DATA 0xa604 +#define ISP_AE_IDX_DATA_VIDX_DATA(x) (((x) & GENMASK(15, 0)) << 0) +#define ISP_AE_IDX_DATA_HIDX_DATA(x) (((x) & GENMASK(15, 0)) << 16) + +#define ISP_AE_BLK_WT_ADDR 0xa608 +#define ISP_AE_BLK_WT_DATA 0xa60c +#define ISP_AE_BLK_WT_DATA_WT(i, x) (((x) & GENMASK(3, 0)) << ((i) * 4)) + +#define ISP_AWB_CTRL 0xa834 +#define ISP_AWB_CTRL_VERT_OFST_MASK GENMASK(1, 0) +#define ISP_AWB_CTRL_VERT_OFST(x) ((x) << 0) +#define ISP_AWB_CTRL_HORIZ_OFST_MASK GENMASK(3, 2) +#define ISP_AWB_CTRL_HORIZ_OFST(x) ((x) << 2) + +#define ISP_AWB_HV_SIZE 0xa83c +#define ISP_AWB_HV_SIZE_VERT_SIZE_MASK GENMASK(15, 0) +#define ISP_AWB_HV_SIZE_VERT_SIZE(x) ((x) << 0) +#define ISP_AWB_HV_SIZE_HORIZ_SIZE_MASK GENMASK(31, 16) +#define ISP_AWB_HV_SIZE_HORIZ_SIZE(x) ((x) << 16) + +#define ISP_AWB_HV_BLKNUM 0xa840 +#define ISP_AWB_HV_BLKNUM_V_NUM_MASK GENMASK(5, 0) +#define ISP_AWB_HV_BLKNUM_V_NUM(x) ((x) << 0) +#define ISP_AWB_HV_BLKNUM_H_NUM_MASK GENMASK(21, 16) +#define ISP_AWB_HV_BLKNUM_H_NUM(x) ((x) << 16) + +#define ISP_AWB_STAT_RG 0xa848 +#define ISP_AWB_STAT_RG_MIN_VALUE_MASK GENMASK(11, 0) +#define ISP_AWB_STAT_RG_MIN_VALUE(x) ((x) << 0) +#define ISP_AWB_STAT_RG_MAX_VALUE_MASK GENMASK(27, 16) +#define ISP_AWB_STAT_RG_MAX_VALUE(x) ((x) << 16) + +#define ISP_AWB_STAT_BG 0xa84c +#define ISP_AWB_STAT_BG_MIN_VALUE_MASK GENMASK(11, 0) +#define ISP_AWB_STAT_BG_MIN_VALUE(x) ((x) << 0) +#define ISP_AWB_STAT_BG_MAX_VALUE_MASK GENMASK(27, 16) +#define ISP_AWB_STAT_BG_MAX_VALUE(x) ((x) << 16) + +#define ISP_AWB_STAT_RG_HL 0xa850 +#define ISP_AWB_STAT_RG_HL_LOW_VALUE_MASK GENMASK(11, 0) +#define ISP_AWB_STAT_RG_HL_LOW_VALUE(x) ((x) << 0) +#define ISP_AWB_STAT_RG_HL_HIGH_VALUE_MASK GENMASK(27, 16) +#define ISP_AWB_STAT_RG_HL_HIGH_VALUE(x) ((x) << 16) + +#define ISP_AWB_STAT_BG_HL 0xa854 +#define ISP_AWB_STAT_BG_HL_LOW_VALUE_MASK GENMASK(11, 0) +#define ISP_AWB_STAT_BG_HL_LOW_VALUE(x) ((x) << 0) +#define ISP_AWB_STAT_BG_HL_HIGH_VALUE_MASK GENMASK(27, 16) +#define ISP_AWB_STAT_BG_HL_HIGH_VALUE(x) ((x) << 16) + +#define ISP_AWB_STAT_CTRL2 0xa858 +#define ISP_AWB_STAT_CTRL2_SATUR_CTRL_MASK BIT(0) +#define ISP_AWB_STAT_CTRL2_SATUR_CTRL(x) ((x) << 0) + +#define ISP_AWB_IDX_ADDR 0xaa00 +#define ISP_AWB_IDX_DATA 0xaa04 +#define ISP_AWB_IDX_DATA_VIDX_DATA(x) (((x) & GENMASK(15, 0)) << 0) +#define ISP_AWB_IDX_DATA_HIDX_DATA(x) (((x) & GENMASK(15, 0)) << 16) + +#define ISP_AWB_BLK_WT_ADDR 0xaa08 +#define ISP_AWB_BLK_WT_DATA 0xaa0c +#define ISP_AWB_BLK_WT_DATA_WT(i, x) (((x) & GENMASK(3, 0)) << ((i) * 4)) + +#define ISP_WRMIFX3_0_CH0_CTRL0 0xc400 +#define ISP_WRMIFX3_0_CH0_CTRL0_STRIDE_MASK GENMASK(28, 16) +#define ISP_WRMIFX3_0_CH0_CTRL0_STRIDE(x) ((x) << 16) + +#define ISP_WRMIFX3_0_CH0_CTRL1 0xc404 +#define ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_MODE_MASK GENMASK(30, 27) +#define ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS BIT(27) +#define ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS (2 << 27) + +#define ISP_WRMIFX3_0_CH1_CTRL0 0xc408 +#define ISP_WRMIFX3_0_CH1_CTRL0_STRIDE_MASK GENMASK(28, 16) +#define ISP_WRMIFX3_0_CH1_CTRL0_STRIDE(x) ((x) << 16) + +#define ISP_WRMIFX3_0_CH1_CTRL1 0xc40c +#define ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_MODE_MASK GENMASK(30, 27) +#define ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_8BITS BIT(27) +#define ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_16BITS (2 << 27) +#define ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_32BITS (3 << 27) + +#define ISP_WRMIFX3_0_WIN_LUMA_H 0xc420 +#define ISP_WRMIFX3_0_WIN_LUMA_H_LUMA_HEND_MASK GENMASK(28, 16) +#define ISP_WRMIFX3_0_WIN_LUMA_H_LUMA_HEND(x) (((x) - 1) << 16) + +#define ISP_WRMIFX3_0_WIN_LUMA_V 0xc424 +#define ISP_WRMIFX3_0_WIN_LUMA_V_LUMA_VEND_MASK GENMASK(28, 16) +#define ISP_WRMIFX3_0_WIN_LUMA_V_LUMA_VEND(x) (((x) - 1) << 16) + +#define ISP_WRMIFX3_0_WIN_CHROM_H 0xc428 +#define ISP_WRMIFX3_0_WIN_CHROM_H_CHROM_HEND_MASK GENMASK(28, 16) +#define ISP_WRMIFX3_0_WIN_CHROM_H_CHROM_HEND(x) (((x) - 1) << 16) + +#define ISP_WRMIFX3_0_WIN_CHROM_V 0xc42c +#define ISP_WRMIFX3_0_WIN_CHROM_V_CHROM_VEND_MASK GENMASK(28, 16) +#define ISP_WRMIFX3_0_WIN_CHROM_V_CHROM_VEND(x) (((x) - 1) << 16) + +#define ISP_WRMIFX3_0_CH0_BADDR 0xc440 +#define ISP_WRMIFX3_0_CH0_BASE_ADDR(x) ((x) >> 4) + +#define ISP_WRMIFX3_0_CH1_BADDR 0xc444 +#define ISP_WRMIFX3_0_CH1_BASE_ADDR(x) ((x) >> 4) + +#define ISP_WRMIFX3_0_FMT_SIZE 0xc464 +#define ISP_WRMIFX3_0_FMT_SIZE_HSIZE_MASK GENMASK(15, 0) +#define ISP_WRMIFX3_0_FMT_SIZE_HSIZE(x) ((x) << 0) +#define ISP_WRMIFX3_0_FMT_SIZE_VSIZE_MASK GENMASK(31, 16) +#define ISP_WRMIFX3_0_FMT_SIZE_VSIZE(x) ((x) << 16) + +#define ISP_WRMIFX3_0_FMT_CTRL 0xc468 +#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_MASK GENMASK(1, 0) +#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT (0 << 0) +#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_10BIT BIT(0) +#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_12BIT (2 << 0) +#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT (3 << 0) +#define ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_MASK BIT(2) +#define ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU (0 << 2) +#define ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_UV BIT(2) +#define ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_MASK GENMASK(5, 4) +#define ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1 (0 << 4) +#define ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2 BIT(4) +#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_MASK GENMASK(18, 16) +#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV422 BIT(16) +#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV420 (2 << 16) +#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_Y_ONLY (3 << 16) +#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW (4 << 16) + +#define VIU_DMAWR_BADDR0 0xc840 +#define VIU_DMAWR_BADDR0_AF_STATS_BASE_ADDR_MASK GENMASK(27, 0) +#define VIU_DMAWR_BADDR0_AF_STATS_BASE_ADDR(x) ((x) >> 4) + +#define VIU_DMAWR_BADDR1 0xc844 +#define VIU_DMAWR_BADDR1_AWB_STATS_BASE_ADDR_MASK GENMASK(27, 0) +#define VIU_DMAWR_BADDR1_AWB_STATS_BASE_ADDR(x) ((x) >> 4) + +#define VIU_DMAWR_BADDR2 0xc848 +#define VIU_DMAWR_BADDR2_AE_STATS_BASE_ADDR_MASK GENMASK(27, 0) +#define VIU_DMAWR_BADDR2_AE_STATS_BASE_ADDR(x) ((x) >> 4) + +#define VIU_DMAWR_SIZE0 0xc854 +#define VIU_DMAWR_SIZE0_AF_STATS_SIZE_MASK GENMASK(15, 0) +#define VIU_DMAWR_SIZE0_AF_STATS_SIZE(x) ((x) << 0) +#define VIU_DMAWR_SIZE0_AWB_STATS_SIZE_MASK GENMASK(31, 16) +#define VIU_DMAWR_SIZE0_AWB_STATS_SIZE(x) ((x) << 16) + +#define VIU_DMAWR_SIZE1 0xc858 +#define VIU_DMAWR_SIZE1_AE_STATS_SIZE_MASK GENMASK(15, 0) +#define VIU_DMAWR_SIZE1_AE_STATS_SIZE(x) ((x) << 0) + +#endif diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c new file mode 100644 index 000000000000..453a889e0b27 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c @@ -0,0 +1,892 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + */ + +#include + +#include "c3-isp-common.h" +#include "c3-isp-regs.h" + +#define C3_ISP_RSZ_DEF_PAD_FMT MEDIA_BUS_FMT_YUV10_1X30 +#define C3_ISP_DISP_REG(base, id) ((base) + (id) * 0x400) +#define C3_ISP_PPS_LUT_H_NUM 33 +#define C3_ISP_PPS_LUT_CTYPE_0 0 +#define C3_ISP_PPS_LUT_CTYPE_2 2 +#define C3_ISP_SCL_EN 1 +#define C3_ISP_SCL_DIS 0 + +/* + * struct c3_isp_rsz_format_info - ISP resizer format information + * + * @mbus_code: the mbus code + * @pads: bitmask detailing valid pads for this mbus_code + * @is_raw: the raw format flag of mbus code + */ +struct c3_isp_rsz_format_info { + u32 mbus_code; + u32 pads; + bool is_raw; +}; + +static const struct c3_isp_rsz_format_info c3_isp_rsz_fmts[] = { + /* RAW formats */ + { + .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16, + .pads = BIT(C3_ISP_RSZ_PAD_SINK) + | BIT(C3_ISP_RSZ_PAD_SOURCE), + .is_raw = true, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16, + .pads = BIT(C3_ISP_RSZ_PAD_SINK) + | BIT(C3_ISP_RSZ_PAD_SOURCE), + .is_raw = true, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16, + .pads = BIT(C3_ISP_RSZ_PAD_SINK) + | BIT(C3_ISP_RSZ_PAD_SOURCE), + .is_raw = true, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16, + .pads = BIT(C3_ISP_RSZ_PAD_SINK) + | BIT(C3_ISP_RSZ_PAD_SOURCE), + .is_raw = true, + }, + /* YUV formats */ + { + .mbus_code = MEDIA_BUS_FMT_YUV10_1X30, + .pads = BIT(C3_ISP_RSZ_PAD_SINK) + | BIT(C3_ISP_RSZ_PAD_SOURCE), + .is_raw = false, + }, +}; + +/* + * struct c3_isp_pps_io_size - ISP scaler input and output size + * + * @thsize: input horizontal size of after preprocessing + * @tvsize: input vertical size of after preprocessing + * @ohsize: output horizontal size + * @ovsize: output vertical size + * @ihsize: input horizontal size + * @max_hsize: maximum horizontal size + */ +struct c3_isp_pps_io_size { + u32 thsize; + u32 tvsize; + u32 ohsize; + u32 ovsize; + u32 ihsize; + u32 max_hsize; +}; + +/* The normal parameters of pps module */ +static const int c3_isp_pps_lut[C3_ISP_PPS_LUT_H_NUM][4] = { + { 0, 511, 0, 0}, { -5, 511, 5, 0}, {-10, 511, 11, 0}, + {-14, 510, 17, -1}, {-18, 508, 23, -1}, {-22, 506, 29, -1}, + {-25, 503, 36, -2}, {-28, 500, 43, -3}, {-32, 496, 51, -3}, + {-34, 491, 59, -4}, {-37, 487, 67, -5}, {-39, 482, 75, -6}, + {-41, 476, 84, -7}, {-42, 470, 92, -8}, {-44, 463, 102, -9}, + {-45, 456, 111, -10}, {-45, 449, 120, -12}, {-47, 442, 130, -13}, + {-47, 434, 140, -15}, {-47, 425, 151, -17}, {-47, 416, 161, -18}, + {-47, 407, 172, -20}, {-47, 398, 182, -21}, {-47, 389, 193, -23}, + {-46, 379, 204, -25}, {-45, 369, 215, -27}, {-44, 358, 226, -28}, + {-43, 348, 237, -30}, {-43, 337, 249, -31}, {-41, 326, 260, -33}, + {-40, 316, 271, -35}, {-39, 305, 282, -36}, {-37, 293, 293, -37} +}; + +static const struct c3_isp_rsz_format_info +*rsz_find_format_by_code(u32 code, u32 pad) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(c3_isp_rsz_fmts); i++) { + const struct c3_isp_rsz_format_info *info = &c3_isp_rsz_fmts[i]; + + if (info->mbus_code == code && info->pads & BIT(pad)) + return info; + } + + return NULL; +} + +static const struct c3_isp_rsz_format_info +*rsz_find_format_by_index(u32 index, u32 pad) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(c3_isp_rsz_fmts); i++) { + const struct c3_isp_rsz_format_info *info = &c3_isp_rsz_fmts[i]; + + if (!(info->pads & BIT(pad))) + continue; + + if (!index) + return info; + + index--; + } + + return NULL; +} + +static void c3_isp_rsz_pps_size(struct c3_isp_resizer *rsz, + struct c3_isp_pps_io_size *io_size) +{ + int thsize = io_size->thsize; + int tvsize = io_size->tvsize; + u32 ohsize = io_size->ohsize; + u32 ovsize = io_size->ovsize; + u32 ihsize = io_size->ihsize; + u32 max_hsize = io_size->max_hsize; + int h_int; + int v_int; + int h_fract; + int v_fract; + int yuv444to422_en; + + /* Calculate the integer part of horizonal scaler step */ + h_int = thsize / ohsize; + + /* Calculate the vertical part of horizonal scaler step */ + v_int = tvsize / ovsize; + + /* + * Calculate the fraction part of horizonal scaler step. + * step_h_fraction = (source / dest) * 2^24, + * so step_h_fraction = ((source << 12) / dest) << 12. + */ + h_fract = ((thsize << 12) / ohsize) << 12; + + /* + * Calculate the fraction part of vertical scaler step + * step_v_fraction = (source / dest) * 2^24, + * so step_v_fraction = ((source << 12) / dest) << 12. + */ + v_fract = ((tvsize << 12) / ovsize) << 12; + + yuv444to422_en = ihsize > (max_hsize / 2) ? 1 : 0; + + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_444TO422, rsz->id), + DISP0_PPS_444TO422_EN_MASK, + DISP0_PPS_444TO422_EN(yuv444to422_en)); + + c3_isp_write(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_VSC_START_PHASE_STEP, rsz->id), + DISP0_PPS_VSC_START_PHASE_STEP_VERT_FRAC(v_fract) | + DISP0_PPS_VSC_START_PHASE_STEP_VERT_INTE(v_int)); + + c3_isp_write(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_HSC_START_PHASE_STEP, rsz->id), + DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_FRAC(h_fract) | + DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_INTE(h_int)); +} + +static void c3_isp_rsz_pps_lut(struct c3_isp_resizer *rsz, u32 ctype) +{ + unsigned int i; + + /* + * Default value of this register is 0, so only need to set + * SCALE_LUMA_COEF_S11_MODE and SCALE_LUMA_CTYPE. This register needs + * to be written in one time. + */ + c3_isp_write(rsz->isp, + C3_ISP_DISP_REG(ISP_SCALE0_COEF_IDX_LUMA, rsz->id), + ISP_SCALE0_COEF_IDX_LUMA_COEF_S11_MODE_EN | + ISP_SCALE0_COEF_IDX_LUMA_CTYPE(ctype)); + + for (i = 0; i < C3_ISP_PPS_LUT_H_NUM; i++) { + c3_isp_write(rsz->isp, + C3_ISP_DISP_REG(ISP_SCALE0_COEF_LUMA, rsz->id), + ISP_SCALE0_COEF_LUMA_DATA0(c3_isp_pps_lut[i][0]) | + ISP_SCALE0_COEF_LUMA_DATA1(c3_isp_pps_lut[i][1])); + c3_isp_write(rsz->isp, + C3_ISP_DISP_REG(ISP_SCALE0_COEF_LUMA, rsz->id), + ISP_SCALE0_COEF_LUMA_DATA0(c3_isp_pps_lut[i][2]) | + ISP_SCALE0_COEF_LUMA_DATA1(c3_isp_pps_lut[i][3])); + } + + /* + * Default value of this register is 0, so only need to set + * SCALE_CHRO_COEF_S11_MODE and SCALE_CHRO_CTYPE. This register needs + * to be written in one time. + */ + c3_isp_write(rsz->isp, + C3_ISP_DISP_REG(ISP_SCALE0_COEF_IDX_CHRO, rsz->id), + ISP_SCALE0_COEF_IDX_CHRO_COEF_S11_MODE_EN | + ISP_SCALE0_COEF_IDX_CHRO_CTYPE(ctype)); + + for (i = 0; i < C3_ISP_PPS_LUT_H_NUM; i++) { + c3_isp_write(rsz->isp, + C3_ISP_DISP_REG(ISP_SCALE0_COEF_CHRO, rsz->id), + ISP_SCALE0_COEF_CHRO_DATA0(c3_isp_pps_lut[i][0]) | + ISP_SCALE0_COEF_CHRO_DATA1(c3_isp_pps_lut[i][1])); + c3_isp_write(rsz->isp, + C3_ISP_DISP_REG(ISP_SCALE0_COEF_CHRO, rsz->id), + ISP_SCALE0_COEF_CHRO_DATA0(c3_isp_pps_lut[i][2]) | + ISP_SCALE0_COEF_CHRO_DATA1(c3_isp_pps_lut[i][3])); + } +} + +static void c3_isp_rsz_pps_disable(struct c3_isp_resizer *rsz) +{ + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id), + DISP0_PPS_SCALE_EN_HSC_EN_MASK, + DISP0_PPS_SCALE_EN_HSC_DIS); + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id), + DISP0_PPS_SCALE_EN_VSC_EN_MASK, + DISP0_PPS_SCALE_EN_VSC_DIS); + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id), + DISP0_PPS_SCALE_EN_PREVSC_EN_MASK, + DISP0_PPS_SCALE_EN_PREVSC_DIS); + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id), + DISP0_PPS_SCALE_EN_PREHSC_EN_MASK, + DISP0_PPS_SCALE_EN_PREHSC_DIS); +} + +static int c3_isp_rsz_pps_enable(struct c3_isp_resizer *rsz, + struct v4l2_subdev_state *state) +{ + struct v4l2_rect *crop; + struct v4l2_rect *cmps; + int max_hsize; + int hsc_en, vsc_en; + int preh_en, prev_en; + u32 prehsc_rate; + u32 prevsc_flt_num; + int pre_vscale_max_hsize; + u32 ihsize_after_pre_hsc; + u32 ihsize_after_pre_hsc_alt; + u32 vsc_tap_num_alt; + u32 ihsize; + u32 ivsize; + struct c3_isp_pps_io_size io_size; + + crop = v4l2_subdev_state_get_crop(state, C3_ISP_RSZ_PAD_SINK); + cmps = v4l2_subdev_state_get_compose(state, C3_ISP_RSZ_PAD_SINK); + + ihsize = crop->width; + ivsize = crop->height; + + hsc_en = (ihsize == cmps->width) ? C3_ISP_SCL_DIS : C3_ISP_SCL_EN; + vsc_en = (ivsize == cmps->height) ? C3_ISP_SCL_DIS : C3_ISP_SCL_EN; + + /* Disable pps when there no need to use pps */ + if (!hsc_en && !vsc_en) { + c3_isp_rsz_pps_disable(rsz); + return 0; + } + + /* Pre-scale needs to be enable if the down scaling factor exceeds 4 */ + preh_en = (ihsize > cmps->width * 4) ? C3_ISP_SCL_EN : C3_ISP_SCL_DIS; + prev_en = (ivsize > cmps->height * 4) ? C3_ISP_SCL_EN : C3_ISP_SCL_DIS; + + if (rsz->id == C3_ISP_RSZ_2) { + max_hsize = C3_ISP_MAX_WIDTH; + + /* Set vertical tap number */ + prevsc_flt_num = 4; + + /* Set the max hsize of pre-vertical scale */ + pre_vscale_max_hsize = max_hsize / 2; + } else { + max_hsize = C3_ISP_DEFAULT_WIDTH; + + /* Set vertical tap number and the max hsize of pre-vertical */ + if (ihsize > (max_hsize / 2) && + ihsize <= max_hsize && prev_en) { + prevsc_flt_num = 2; + pre_vscale_max_hsize = max_hsize; + } else { + prevsc_flt_num = 4; + pre_vscale_max_hsize = max_hsize / 2; + } + } + + /* + * Set pre-horizonal scale rate and the hsize of after + * pre-horizonal scale. + */ + if (preh_en) { + prehsc_rate = 1; + ihsize_after_pre_hsc = DIV_ROUND_UP(ihsize, 2); + } else { + prehsc_rate = 0; + ihsize_after_pre_hsc = ihsize; + } + + /* Change pre-horizonal scale rate */ + if (prev_en && ihsize_after_pre_hsc >= pre_vscale_max_hsize) + prehsc_rate += 1; + + /* Set the actual hsize of after pre-horizonal scale */ + if (preh_en) + ihsize_after_pre_hsc_alt = + DIV_ROUND_UP(ihsize, 1 << prehsc_rate); + else + ihsize_after_pre_hsc_alt = ihsize; + + /* Set vertical scaler bank length */ + if (ihsize_after_pre_hsc_alt <= (max_hsize / 2)) + vsc_tap_num_alt = 4; + else if (ihsize_after_pre_hsc_alt <= max_hsize) + vsc_tap_num_alt = prev_en ? 2 : 4; + else + vsc_tap_num_alt = prev_en ? 4 : 2; + + io_size.thsize = ihsize_after_pre_hsc_alt; + io_size.tvsize = prev_en ? DIV_ROUND_UP(ivsize, 2) : ivsize; + io_size.ohsize = cmps->width; + io_size.ovsize = cmps->height; + io_size.ihsize = ihsize; + io_size.max_hsize = max_hsize; + + c3_isp_rsz_pps_size(rsz, &io_size); + c3_isp_rsz_pps_lut(rsz, C3_ISP_PPS_LUT_CTYPE_0); + c3_isp_rsz_pps_lut(rsz, C3_ISP_PPS_LUT_CTYPE_2); + + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id), + DISP0_PPS_SCALE_EN_VSC_TAP_NUM_MASK, + DISP0_PPS_SCALE_EN_VSC_TAP_NUM(vsc_tap_num_alt)); + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id), + DISP0_PPS_SCALE_EN_PREVSC_FLT_NUM_MASK, + DISP0_PPS_SCALE_EN_PREVSC_FLT_NUM(prevsc_flt_num)); + + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id), + DISP0_PPS_SCALE_EN_PREVSC_RATE_MASK, + DISP0_PPS_SCALE_EN_PREVSC_RATE(prev_en)); + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id), + DISP0_PPS_SCALE_EN_PREHSC_RATE_MASK, + DISP0_PPS_SCALE_EN_PREHSC_RATE(prehsc_rate)); + + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id), + DISP0_PPS_SCALE_EN_HSC_EN_MASK, + DISP0_PPS_SCALE_EN_HSC_EN(hsc_en)); + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id), + DISP0_PPS_SCALE_EN_VSC_EN_MASK, + DISP0_PPS_SCALE_EN_VSC_EN(vsc_en)); + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id), + DISP0_PPS_SCALE_EN_PREVSC_EN_MASK, + DISP0_PPS_SCALE_EN_PREVSC_EN(prev_en)); + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id), + DISP0_PPS_SCALE_EN_PREHSC_EN_MASK, + DISP0_PPS_SCALE_EN_PREHSC_EN(preh_en)); + + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id), + DISP0_PPS_SCALE_EN_HSC_NOR_RS_BITS_MASK, + DISP0_PPS_SCALE_EN_HSC_NOR_RS_BITS(9)); + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id), + DISP0_PPS_SCALE_EN_VSC_NOR_RS_BITS_MASK, + DISP0_PPS_SCALE_EN_VSC_NOR_RS_BITS(9)); + + return 0; +} + +static void c3_isp_rsz_start(struct c3_isp_resizer *rsz, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_mbus_framefmt *src_fmt; + const struct c3_isp_rsz_format_info *rsz_fmt; + struct v4l2_rect *sink_crop; + u32 mask; + u32 val; + + sink_fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SINK); + sink_crop = v4l2_subdev_state_get_crop(state, C3_ISP_RSZ_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SOURCE); + rsz_fmt = rsz_find_format_by_code(sink_fmt->code, C3_ISP_RSZ_PAD_SINK); + + if (rsz->id == C3_ISP_RSZ_0) { + mask = ISP_TOP_DISPIN_SEL_DISP0_MASK; + val = rsz_fmt->is_raw ? ISP_TOP_DISPIN_SEL_DISP0_MIPI_OUT + : ISP_TOP_DISPIN_SEL_DISP0_CORE_OUT; + } else if (rsz->id == C3_ISP_RSZ_1) { + mask = ISP_TOP_DISPIN_SEL_DISP1_MASK; + val = rsz_fmt->is_raw ? ISP_TOP_DISPIN_SEL_DISP1_MIPI_OUT + : ISP_TOP_DISPIN_SEL_DISP1_CORE_OUT; + } else { + mask = ISP_TOP_DISPIN_SEL_DISP2_MASK; + val = rsz_fmt->is_raw ? ISP_TOP_DISPIN_SEL_DISP2_MIPI_OUT + : ISP_TOP_DISPIN_SEL_DISP2_CORE_OUT; + } + + c3_isp_update_bits(rsz->isp, ISP_TOP_DISPIN_SEL, mask, val); + + c3_isp_write(rsz->isp, C3_ISP_DISP_REG(ISP_DISP0_TOP_IN_SIZE, rsz->id), + ISP_DISP0_TOP_IN_SIZE_HSIZE(sink_fmt->width) | + ISP_DISP0_TOP_IN_SIZE_VSIZE(sink_fmt->height)); + + c3_isp_write(rsz->isp, C3_ISP_DISP_REG(DISP0_TOP_CRP2_START, rsz->id), + DISP0_TOP_CRP2_START_V_START(sink_crop->top) | + DISP0_TOP_CRP2_START_H_START(sink_crop->left)); + + c3_isp_write(rsz->isp, C3_ISP_DISP_REG(DISP0_TOP_CRP2_SIZE, rsz->id), + DISP0_TOP_CRP2_SIZE_V_SIZE(sink_crop->height) | + DISP0_TOP_CRP2_SIZE_H_SIZE(sink_crop->width)); + + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_TOP_TOP_CTRL, rsz->id), + DISP0_TOP_TOP_CTRL_CROP2_EN_MASK, + DISP0_TOP_TOP_CTRL_CROP2_EN); + + if (!rsz_fmt->is_raw) + c3_isp_rsz_pps_enable(rsz, state); + + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_TOP_OUT_SIZE, rsz->id), + DISP0_TOP_OUT_SIZE_SCL_OUT_HEIGHT_MASK, + DISP0_TOP_OUT_SIZE_SCL_OUT_HEIGHT(src_fmt->height)); + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_TOP_OUT_SIZE, rsz->id), + DISP0_TOP_OUT_SIZE_SCL_OUT_WIDTH_MASK, + DISP0_TOP_OUT_SIZE_SCL_OUT_WIDTH(src_fmt->width)); + + if (rsz->id == C3_ISP_RSZ_0) { + mask = ISP_TOP_PATH_EN_DISP0_EN_MASK; + val = ISP_TOP_PATH_EN_DISP0_EN; + } else if (rsz->id == C3_ISP_RSZ_1) { + mask = ISP_TOP_PATH_EN_DISP1_EN_MASK; + val = ISP_TOP_PATH_EN_DISP1_EN; + } else { + mask = ISP_TOP_PATH_EN_DISP2_EN_MASK; + val = ISP_TOP_PATH_EN_DISP2_EN; + } + + c3_isp_update_bits(rsz->isp, ISP_TOP_PATH_EN, mask, val); +} + +static void c3_isp_rsz_stop(struct c3_isp_resizer *rsz) +{ + u32 mask; + u32 val; + + if (rsz->id == C3_ISP_RSZ_0) { + mask = ISP_TOP_PATH_EN_DISP0_EN_MASK; + val = ISP_TOP_PATH_EN_DISP0_DIS; + } else if (rsz->id == C3_ISP_RSZ_1) { + mask = ISP_TOP_PATH_EN_DISP1_EN_MASK; + val = ISP_TOP_PATH_EN_DISP1_DIS; + } else { + mask = ISP_TOP_PATH_EN_DISP2_EN_MASK; + val = ISP_TOP_PATH_EN_DISP2_DIS; + } + + c3_isp_update_bits(rsz->isp, ISP_TOP_PATH_EN, mask, val); + + c3_isp_update_bits(rsz->isp, + C3_ISP_DISP_REG(DISP0_TOP_TOP_CTRL, rsz->id), + DISP0_TOP_TOP_CTRL_CROP2_EN_MASK, + DISP0_TOP_TOP_CTRL_CROP2_DIS); + + c3_isp_rsz_pps_disable(rsz); +} + +static int c3_isp_rsz_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct c3_isp_resizer *rsz = v4l2_get_subdevdata(sd); + + c3_isp_rsz_start(rsz, state); + + c3_isp_params_pre_cfg(rsz->isp); + c3_isp_stats_pre_cfg(rsz->isp); + + return v4l2_subdev_enable_streams(rsz->src_sd, rsz->src_pad, BIT(0)); +} + +static int c3_isp_rsz_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct c3_isp_resizer *rsz = v4l2_get_subdevdata(sd); + + c3_isp_rsz_stop(rsz); + + return v4l2_subdev_disable_streams(rsz->src_sd, rsz->src_pad, BIT(0)); +} + +static int c3_isp_rsz_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct c3_isp_rsz_format_info *info; + + info = rsz_find_format_by_index(code->index, code->pad); + if (!info) + return -EINVAL; + + code->code = info->mbus_code; + + return 0; +} + +static void c3_isp_rsz_set_sink_fmt(struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_mbus_framefmt *src_fmt; + struct v4l2_rect *sink_crop; + struct v4l2_rect *sink_cmps; + const struct c3_isp_rsz_format_info *rsz_fmt; + + sink_fmt = v4l2_subdev_state_get_format(state, format->pad); + sink_crop = v4l2_subdev_state_get_crop(state, format->pad); + sink_cmps = v4l2_subdev_state_get_compose(state, format->pad); + src_fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SOURCE); + + rsz_fmt = rsz_find_format_by_code(format->format.code, format->pad); + if (rsz_fmt) + sink_fmt->code = format->format.code; + else + sink_fmt->code = C3_ISP_RSZ_DEF_PAD_FMT; + + sink_fmt->width = clamp_t(u32, format->format.width, + C3_ISP_MIN_WIDTH, C3_ISP_MAX_WIDTH); + sink_fmt->height = clamp_t(u32, format->format.height, + C3_ISP_MIN_HEIGHT, C3_ISP_MAX_HEIGHT); + sink_fmt->field = V4L2_FIELD_NONE; + sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + + if (rsz_fmt && rsz_fmt->is_raw) { + sink_fmt->colorspace = V4L2_COLORSPACE_RAW; + sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE; + sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + } else { + sink_fmt->colorspace = V4L2_COLORSPACE_SRGB; + sink_fmt->xfer_func = V4L2_XFER_FUNC_SRGB; + sink_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; + } + + sink_crop->width = sink_fmt->width; + sink_crop->height = sink_fmt->height; + sink_crop->left = 0; + sink_crop->top = 0; + + sink_cmps->width = sink_crop->width; + sink_cmps->height = sink_crop->height; + sink_cmps->left = 0; + sink_cmps->top = 0; + + src_fmt->code = sink_fmt->code; + src_fmt->width = sink_cmps->width; + src_fmt->height = sink_cmps->height; + + format->format = *sink_fmt; +} + +static void c3_isp_rsz_set_source_fmt(struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *src_fmt; + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_rect *sink_cmps; + const struct c3_isp_rsz_format_info *rsz_fmt; + + src_fmt = v4l2_subdev_state_get_format(state, format->pad); + sink_fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SINK); + sink_cmps = v4l2_subdev_state_get_compose(state, C3_ISP_RSZ_PAD_SINK); + + src_fmt->code = sink_fmt->code; + src_fmt->width = sink_cmps->width; + src_fmt->height = sink_cmps->height; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + + rsz_fmt = rsz_find_format_by_code(src_fmt->code, format->pad); + if (rsz_fmt->is_raw) { + src_fmt->colorspace = V4L2_COLORSPACE_RAW; + src_fmt->xfer_func = V4L2_XFER_FUNC_NONE; + src_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + } else { + src_fmt->colorspace = V4L2_COLORSPACE_SRGB; + src_fmt->xfer_func = V4L2_XFER_FUNC_SRGB; + src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; + } + + format->format = *src_fmt; +} + +static int c3_isp_rsz_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + if (format->pad == C3_ISP_RSZ_PAD_SINK) + c3_isp_rsz_set_sink_fmt(state, format); + else + c3_isp_rsz_set_source_fmt(state, format); + + return 0; +} + +static int c3_isp_rsz_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_mbus_framefmt *fmt; + struct v4l2_rect *crop; + struct v4l2_rect *cmps; + + if (sel->pad == C3_ISP_RSZ_PAD_SOURCE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + fmt = v4l2_subdev_state_get_format(state, sel->pad); + sel->r.width = fmt->width; + sel->r.height = fmt->height; + sel->r.left = 0; + sel->r.top = 0; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + crop = v4l2_subdev_state_get_crop(state, sel->pad); + sel->r.width = crop->width; + sel->r.height = crop->height; + sel->r.left = 0; + sel->r.top = 0; + break; + case V4L2_SEL_TGT_CROP: + crop = v4l2_subdev_state_get_crop(state, sel->pad); + sel->r = *crop; + break; + case V4L2_SEL_TGT_COMPOSE: + cmps = v4l2_subdev_state_get_compose(state, sel->pad); + sel->r = *cmps; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int c3_isp_rsz_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_mbus_framefmt *fmt; + struct v4l2_mbus_framefmt *src_fmt; + struct v4l2_rect *crop; + struct v4l2_rect *cmps; + + if (sel->pad == C3_ISP_RSZ_PAD_SOURCE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + fmt = v4l2_subdev_state_get_format(state, sel->pad); + crop = v4l2_subdev_state_get_crop(state, sel->pad); + cmps = v4l2_subdev_state_get_compose(state, sel->pad); + src_fmt = v4l2_subdev_state_get_format(state, + C3_ISP_RSZ_PAD_SOURCE); + + sel->r.left = clamp_t(s32, sel->r.left, 0, fmt->width - 1); + sel->r.top = clamp_t(s32, sel->r.top, 0, fmt->height - 1); + sel->r.width = clamp(sel->r.width, C3_ISP_MIN_WIDTH, + fmt->width - sel->r.left); + sel->r.height = clamp(sel->r.height, C3_ISP_MIN_HEIGHT, + fmt->height - sel->r.top); + + crop->width = ALIGN(sel->r.width, 2); + crop->height = ALIGN(sel->r.height, 2); + crop->left = sel->r.left; + crop->top = sel->r.top; + + *cmps = *crop; + + src_fmt->code = fmt->code; + src_fmt->width = cmps->width; + src_fmt->height = cmps->height; + + sel->r = *crop; + break; + case V4L2_SEL_TGT_COMPOSE: + crop = v4l2_subdev_state_get_crop(state, sel->pad); + cmps = v4l2_subdev_state_get_compose(state, sel->pad); + + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = clamp(sel->r.width, C3_ISP_MIN_WIDTH, + crop->width); + sel->r.height = clamp(sel->r.height, C3_ISP_MIN_HEIGHT, + crop->height); + + cmps->width = ALIGN(sel->r.width, 2); + cmps->height = ALIGN(sel->r.height, 2); + cmps->left = sel->r.left; + cmps->top = sel->r.top; + + sel->r = *cmps; + + fmt = v4l2_subdev_state_get_format(state, + C3_ISP_RSZ_PAD_SOURCE); + fmt->width = cmps->width; + fmt->height = cmps->height; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int c3_isp_rsz_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt; + struct v4l2_rect *crop; + struct v4l2_rect *cmps; + + fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SINK); + fmt->width = C3_ISP_DEFAULT_WIDTH; + fmt->height = C3_ISP_DEFAULT_HEIGHT; + fmt->field = V4L2_FIELD_NONE; + fmt->code = C3_ISP_RSZ_DEF_PAD_FMT; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->xfer_func = V4L2_XFER_FUNC_SRGB; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; + + crop = v4l2_subdev_state_get_crop(state, C3_ISP_RSZ_PAD_SINK); + crop->width = C3_ISP_DEFAULT_WIDTH; + crop->height = C3_ISP_DEFAULT_HEIGHT; + crop->left = 0; + crop->top = 0; + + cmps = v4l2_subdev_state_get_compose(state, C3_ISP_RSZ_PAD_SINK); + cmps->width = C3_ISP_DEFAULT_WIDTH; + cmps->height = C3_ISP_DEFAULT_HEIGHT; + cmps->left = 0; + cmps->top = 0; + + fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SOURCE); + fmt->width = cmps->width; + fmt->height = cmps->height; + fmt->field = V4L2_FIELD_NONE; + fmt->code = C3_ISP_RSZ_DEF_PAD_FMT; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->xfer_func = V4L2_XFER_FUNC_SRGB; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE; + + return 0; +} + +static const struct v4l2_subdev_pad_ops c3_isp_rsz_pad_ops = { + .enum_mbus_code = c3_isp_rsz_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = c3_isp_rsz_set_fmt, + .get_selection = c3_isp_rsz_get_selection, + .set_selection = c3_isp_rsz_set_selection, + .enable_streams = c3_isp_rsz_enable_streams, + .disable_streams = c3_isp_rsz_disable_streams, +}; + +static const struct v4l2_subdev_ops c3_isp_rsz_subdev_ops = { + .pad = &c3_isp_rsz_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops c3_isp_rsz_internal_ops = { + .init_state = c3_isp_rsz_init_state, +}; + +/* Media entity operations */ +static const struct media_entity_operations c3_isp_rsz_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int c3_isp_rsz_register(struct c3_isp_resizer *rsz) +{ + struct v4l2_subdev *sd = &rsz->sd; + int ret; + + v4l2_subdev_init(sd, &c3_isp_rsz_subdev_ops); + sd->owner = THIS_MODULE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->internal_ops = &c3_isp_rsz_internal_ops; + snprintf(sd->name, sizeof(sd->name), "c3-isp-resizer%u", rsz->id); + + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; + sd->entity.ops = &c3_isp_rsz_entity_ops; + + sd->dev = rsz->isp->dev; + v4l2_set_subdevdata(sd, rsz); + + rsz->pads[C3_ISP_RSZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + rsz->pads[C3_ISP_RSZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, C3_ISP_RSZ_PAD_MAX, + rsz->pads); + if (ret) + return ret; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_device_register_subdev(&rsz->isp->v4l2_dev, sd); + if (ret) + goto err_subdev_cleanup; + + return 0; + +err_subdev_cleanup: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: + media_entity_cleanup(&sd->entity); + return ret; +} + +static void c3_isp_rsz_unregister(struct c3_isp_resizer *rsz) +{ + struct v4l2_subdev *sd = &rsz->sd; + + v4l2_device_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); +} + +int c3_isp_resizers_register(struct c3_isp_device *isp) +{ + int ret; + + for (unsigned int i = C3_ISP_RSZ_0; i < C3_ISP_NUM_RSZ; i++) { + struct c3_isp_resizer *rsz = &isp->resizers[i]; + + rsz->id = i; + rsz->isp = isp; + rsz->src_sd = &isp->core.sd; + rsz->src_pad = C3_ISP_CORE_PAD_SOURCE_VIDEO_0 + i; + + ret = c3_isp_rsz_register(rsz); + if (ret) { + rsz->isp = NULL; + c3_isp_resizers_unregister(isp); + return ret; + } + } + + return 0; +} + +void c3_isp_resizers_unregister(struct c3_isp_device *isp) +{ + for (unsigned int i = C3_ISP_RSZ_0; i < C3_ISP_NUM_RSZ; i++) { + struct c3_isp_resizer *rsz = &isp->resizers[i]; + + if (rsz->isp) + c3_isp_rsz_unregister(rsz); + } +} diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-stats.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-stats.c new file mode 100644 index 000000000000..8a5d7e1a30c9 --- /dev/null +++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-stats.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + */ + +#include +#include +#include + +#include +#include +#include + +#include "c3-isp-common.h" +#include "c3-isp-regs.h" + +/* Hardware configuration */ + +static void c3_isp_stats_cfg_dmawr_addr(struct c3_isp_stats *stats) +{ + u32 awb_dma_size = sizeof(struct c3_isp_awb_stats); + u32 ae_dma_size = sizeof(struct c3_isp_ae_stats); + u32 awb_dma_addr = stats->buff->dma_addr; + u32 af_dma_addr; + u32 ae_dma_addr; + + ae_dma_addr = awb_dma_addr + awb_dma_size; + af_dma_addr = ae_dma_addr + ae_dma_size; + + c3_isp_update_bits(stats->isp, VIU_DMAWR_BADDR0, + VIU_DMAWR_BADDR0_AF_STATS_BASE_ADDR_MASK, + VIU_DMAWR_BADDR0_AF_STATS_BASE_ADDR(af_dma_addr)); + + c3_isp_update_bits(stats->isp, VIU_DMAWR_BADDR1, + VIU_DMAWR_BADDR1_AWB_STATS_BASE_ADDR_MASK, + VIU_DMAWR_BADDR1_AWB_STATS_BASE_ADDR(awb_dma_addr)); + + c3_isp_update_bits(stats->isp, VIU_DMAWR_BADDR2, + VIU_DMAWR_BADDR2_AE_STATS_BASE_ADDR_MASK, + VIU_DMAWR_BADDR2_AE_STATS_BASE_ADDR(ae_dma_addr)); +} + +static void c3_isp_stats_cfg_buff(struct c3_isp_stats *stats) +{ + stats->buff = + list_first_entry_or_null(&stats->pending, + struct c3_isp_stats_buffer, list); + if (stats->buff) { + c3_isp_stats_cfg_dmawr_addr(stats); + list_del(&stats->buff->list); + } +} + +void c3_isp_stats_pre_cfg(struct c3_isp_device *isp) +{ + struct c3_isp_stats *stats = &isp->stats; + u32 dma_size; + + c3_isp_update_bits(stats->isp, ISP_AF_EN_CTRL, + ISP_AF_EN_CTRL_STAT_SEL_MASK, + ISP_AF_EN_CTRL_STAT_SEL_NEW); + c3_isp_update_bits(stats->isp, ISP_AE_CTRL, + ISP_AE_CTRL_LUMA_MODE_MASK, + ISP_AE_CTRL_LUMA_MODE_FILTER); + + /* The unit of dma_size is 16 bytes */ + dma_size = sizeof(struct c3_isp_af_stats) / C3_ISP_DMA_SIZE_ALIGN_BYTES; + c3_isp_update_bits(stats->isp, VIU_DMAWR_SIZE0, + VIU_DMAWR_SIZE0_AF_STATS_SIZE_MASK, + VIU_DMAWR_SIZE0_AF_STATS_SIZE(dma_size)); + + dma_size = sizeof(struct c3_isp_awb_stats) / + C3_ISP_DMA_SIZE_ALIGN_BYTES; + c3_isp_update_bits(stats->isp, VIU_DMAWR_SIZE0, + VIU_DMAWR_SIZE0_AWB_STATS_SIZE_MASK, + VIU_DMAWR_SIZE0_AWB_STATS_SIZE(dma_size)); + + dma_size = sizeof(struct c3_isp_ae_stats) / C3_ISP_DMA_SIZE_ALIGN_BYTES; + c3_isp_update_bits(stats->isp, VIU_DMAWR_SIZE1, + VIU_DMAWR_SIZE1_AE_STATS_SIZE_MASK, + VIU_DMAWR_SIZE1_AE_STATS_SIZE(dma_size)); + + guard(spinlock_irqsave)(&stats->buff_lock); + + c3_isp_stats_cfg_buff(stats); +} + +static int c3_isp_stats_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, C3_ISP_DRIVER_NAME, sizeof(cap->driver)); + strscpy(cap->card, "AML C3 ISP", sizeof(cap->card)); + + return 0; +} + +static int c3_isp_stats_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct c3_isp_stats *stats = video_drvdata(file); + + if (f->index > 0 || f->type != stats->vb2_q.type) + return -EINVAL; + + f->pixelformat = V4L2_META_FMT_C3ISP_STATS; + + return 0; +} + +static int c3_isp_stats_g_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct c3_isp_stats *stats = video_drvdata(file); + + f->fmt.meta = stats->vfmt.fmt.meta; + + return 0; +} + +static const struct v4l2_ioctl_ops isp_stats_v4l2_ioctl_ops = { + .vidioc_querycap = c3_isp_stats_querycap, + .vidioc_enum_fmt_meta_cap = c3_isp_stats_enum_fmt, + .vidioc_g_fmt_meta_cap = c3_isp_stats_g_fmt, + .vidioc_s_fmt_meta_cap = c3_isp_stats_g_fmt, + .vidioc_try_fmt_meta_cap = c3_isp_stats_g_fmt, + .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_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct v4l2_file_operations isp_stats_v4l2_fops = { + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static int c3_isp_stats_vb2_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + if (*num_planes) { + if (*num_planes != 1) + return -EINVAL; + + if (sizes[0] < sizeof(struct c3_isp_stats_info)) + return -EINVAL; + + return 0; + } + + *num_planes = 1; + sizes[0] = sizeof(struct c3_isp_stats_info); + + return 0; +} + +static void c3_isp_stats_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb); + struct c3_isp_stats_buffer *buf = + container_of(v4l2_buf, struct c3_isp_stats_buffer, vb); + struct c3_isp_stats *stats = vb2_get_drv_priv(vb->vb2_queue); + + guard(spinlock_irqsave)(&stats->buff_lock); + + list_add_tail(&buf->list, &stats->pending); +} + +static int c3_isp_stats_vb2_buf_prepare(struct vb2_buffer *vb) +{ + struct c3_isp_stats *stats = vb2_get_drv_priv(vb->vb2_queue); + unsigned int size = stats->vfmt.fmt.meta.buffersize; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(stats->isp->dev, + "User buffer too small (%ld < %u)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + + return 0; +} + +static int c3_isp_stats_vb2_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb); + struct c3_isp_stats_buffer *buf = + container_of(v4l2_buf, struct c3_isp_stats_buffer, vb); + + buf->dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + + return 0; +} + +static void c3_isp_stats_vb2_stop_streaming(struct vb2_queue *q) +{ + struct c3_isp_stats *stats = vb2_get_drv_priv(q); + + guard(spinlock_irqsave)(&stats->buff_lock); + + if (stats->buff) { + vb2_buffer_done(&stats->buff->vb.vb2_buf, VB2_BUF_STATE_ERROR); + stats->buff = NULL; + } + + while (!list_empty(&stats->pending)) { + struct c3_isp_stats_buffer *buff; + + buff = list_first_entry(&stats->pending, + struct c3_isp_stats_buffer, list); + list_del(&buff->list); + vb2_buffer_done(&buff->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } +} + +static const struct vb2_ops isp_stats_vb2_ops = { + .queue_setup = c3_isp_stats_vb2_queue_setup, + .buf_queue = c3_isp_stats_vb2_buf_queue, + .buf_prepare = c3_isp_stats_vb2_buf_prepare, + .buf_init = c3_isp_stats_vb2_buf_init, + .stop_streaming = c3_isp_stats_vb2_stop_streaming, +}; + +int c3_isp_stats_register(struct c3_isp_device *isp) +{ + struct c3_isp_stats *stats = &isp->stats; + struct video_device *vdev = &stats->vdev; + struct vb2_queue *vb2_q = &stats->vb2_q; + int ret; + + memset(stats, 0, sizeof(*stats)); + stats->vfmt.fmt.meta.dataformat = V4L2_META_FMT_C3ISP_STATS; + stats->vfmt.fmt.meta.buffersize = sizeof(struct c3_isp_stats_info); + stats->isp = isp; + INIT_LIST_HEAD(&stats->pending); + spin_lock_init(&stats->buff_lock); + + mutex_init(&stats->lock); + + snprintf(vdev->name, sizeof(vdev->name), "c3-isp-stats"); + vdev->fops = &isp_stats_v4l2_fops; + vdev->ioctl_ops = &isp_stats_v4l2_ioctl_ops; + vdev->v4l2_dev = &isp->v4l2_dev; + vdev->lock = &stats->lock; + vdev->minor = -1; + vdev->queue = vb2_q; + vdev->release = video_device_release_empty; + vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; + vdev->vfl_dir = VFL_DIR_RX; + video_set_drvdata(vdev, stats); + + vb2_q->drv_priv = stats; + vb2_q->mem_ops = &vb2_dma_contig_memops; + vb2_q->ops = &isp_stats_vb2_ops; + vb2_q->type = V4L2_BUF_TYPE_META_CAPTURE; + vb2_q->io_modes = VB2_DMABUF | VB2_MMAP; + vb2_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vb2_q->buf_struct_size = sizeof(struct c3_isp_stats_buffer); + vb2_q->dev = isp->dev; + vb2_q->lock = &stats->lock; + vb2_q->min_queued_buffers = 2; + + ret = vb2_queue_init(vb2_q); + if (ret) + goto err_destroy; + + stats->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vdev->entity, 1, &stats->pad); + if (ret) + goto err_queue_release; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(isp->dev, + "Failed to register %s: %d\n", vdev->name, ret); + goto err_entity_cleanup; + } + + return 0; + +err_entity_cleanup: + media_entity_cleanup(&vdev->entity); +err_queue_release: + vb2_queue_release(vb2_q); +err_destroy: + mutex_destroy(&stats->lock); + return ret; +} + +void c3_isp_stats_unregister(struct c3_isp_device *isp) +{ + struct c3_isp_stats *stats = &isp->stats; + + vb2_queue_release(&stats->vb2_q); + media_entity_cleanup(&stats->vdev.entity); + video_unregister_device(&stats->vdev); + mutex_destroy(&stats->lock); +} + +void c3_isp_stats_isr(struct c3_isp_device *isp) +{ + struct c3_isp_stats *stats = &isp->stats; + + guard(spinlock_irqsave)(&stats->buff_lock); + + if (stats->buff) { + stats->buff->vb.sequence = stats->isp->frm_sequence; + stats->buff->vb.vb2_buf.timestamp = ktime_get(); + stats->buff->vb.field = V4L2_FIELD_NONE; + vb2_buffer_done(&stats->buff->vb.vb2_buf, VB2_BUF_STATE_DONE); + } + + c3_isp_stats_cfg_buff(stats); +} -- cgit v1.2.3-59-g8ed1b From f0d3a857ae4e0ea67b99bab3cf00c5c46eb7cefb Mon Sep 17 00:00:00 2001 From: Keke Li Date: Sun, 27 Apr 2025 14:27:17 +0800 Subject: Documentation: media: Add documentation file metafmt-c3-isp.rst Add the file 'metafmt-c3-isp.rst' that documents the meta format of c3-isp. Reviewed-by: Jacopo Mondi Signed-off-by: Keke Li Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- .../userspace-api/media/v4l/meta-formats.rst | 1 + .../userspace-api/media/v4l/metafmt-c3-isp.rst | 86 ++++++++++++++++++++++ MAINTAINERS | 1 + 3 files changed, 88 insertions(+) create mode 100644 Documentation/userspace-api/media/v4l/metafmt-c3-isp.rst diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst index 86ffb3bc8ade..bb6876cfc271 100644 --- a/Documentation/userspace-api/media/v4l/meta-formats.rst +++ b/Documentation/userspace-api/media/v4l/meta-formats.rst @@ -12,6 +12,7 @@ These formats are used for the :ref:`metadata` interface only. .. toctree:: :maxdepth: 1 + metafmt-c3-isp metafmt-d4xx metafmt-generic metafmt-intel-ipu3 diff --git a/Documentation/userspace-api/media/v4l/metafmt-c3-isp.rst b/Documentation/userspace-api/media/v4l/metafmt-c3-isp.rst new file mode 100644 index 000000000000..449b45c2ec24 --- /dev/null +++ b/Documentation/userspace-api/media/v4l/metafmt-c3-isp.rst @@ -0,0 +1,86 @@ +.. SPDX-License-Identifier: (GPL-2.0-only OR MIT) + +.. _v4l2-meta-fmt-c3isp-stats: +.. _v4l2-meta-fmt-c3isp-params: + +*********************************************************************** +V4L2_META_FMT_C3ISP_STATS ('C3ST'), V4L2_META_FMT_C3ISP_PARAMS ('C3PM') +*********************************************************************** + +.. c3_isp_stats_info + +3A Statistics +============= + +The C3 ISP can collect different statistics over an input Bayer frame. +Those statistics are obtained from the "c3-isp-stats" metadata capture video nodes, +using the :c:type:`v4l2_meta_format` interface. +They are formatted as described by the :c:type:`c3_isp_stats_info` structure. + +The statistics collected are Auto-white balance, +Auto-exposure and Auto-focus information. + +.. c3_isp_params_cfg + +Configuration Parameters +======================== + +The configuration parameters are passed to the c3-isp-params metadata output video node, +using the :c:type:`v4l2_meta_format` interface. Rather than a single struct containing +sub-structs for each configurable area of the ISP, parameters for the C3-ISP +are defined as distinct structs or "blocks" which may be added to the data +member of :c:type:`c3_isp_params_cfg`. Userspace is responsible for +populating the data member with the blocks that need to be configured by the driver, but +need not populate it with **all** the blocks, or indeed with any at all if there +are no configuration changes to make. Populated blocks **must** be consecutive +in the buffer. To assist both userspace and the driver in identifying the +blocks each block-specific struct embeds +:c:type:`c3_isp_params_block_header` as its first member and userspace +must populate the type member with a value from +:c:type:`c3_isp_params_block_type`. Once the blocks have been populated +into the data buffer, the combined size of all populated blocks shall be set in +the data_size member of :c:type:`c3_isp_params_cfg`. For example: + +.. code-block:: c + + struct c3_isp_params_cfg *params = + (struct c3_isp_params_cfg *)buffer; + + params->version = C3_ISP_PARAM_BUFFER_V0; + params->data_size = 0; + + void *data = (void *)params->data; + + struct c3_isp_params_awb_gains *gains = + (struct c3_isp_params_awb_gains *)data; + + gains->header.type = C3_ISP_PARAMS_BLOCK_AWB_GAINS; + gains->header.flags = C3_ISP_PARAMS_BLOCK_FL_ENABLE; + gains->header.size = sizeof(struct c3_isp_params_awb_gains); + + gains->gr_gain = 256; + gains->r_gain = 256; + gains->b_gain = 256; + gains->gb_gain = 256; + + data += sizeof(struct c3_isp__params_awb_gains); + params->data_size += sizeof(struct c3_isp_params_awb_gains); + + struct c3_isp_params_awb_config *awb_cfg = + (struct c3_isp_params_awb_config *)data; + + awb_cfg->header.type = C3_ISP_PARAMS_BLOCK_AWB_CONFIG; + awb_cfg->header.flags = C3_ISP_PARAMS_BLOCK_FL_ENABLE; + awb_cfg->header.size = sizeof(struct c3_isp_params_awb_config); + + awb_cfg->tap_point = C3_ISP_AWB_STATS_TAP_BEFORE_WB; + awb_cfg->satur = 1; + awb_cfg->horiz_zones_num = 32; + awb_cfg->vert_zones_num = 24; + + params->data_size += sizeof(struct c3_isp_params_awb_config); + +Amlogic C3 ISP uAPI data types +=============================== + +.. kernel-doc:: include/uapi/linux/media/amlogic/c3-isp-config.h diff --git a/MAINTAINERS b/MAINTAINERS index e4795e990fbb..627d6e755b16 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1259,6 +1259,7 @@ M: Keke Li L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/amlogic,c3-isp.yaml +F: Documentation/userspace-api/media/v4l/metafmt-c3-isp.rst F: drivers/media/platform/amlogic/c3/isp/ F: include/uapi/linux/media/amlogic/ -- cgit v1.2.3-59-g8ed1b From f8953ee959546ae8faaa20f405647e5aecc42614 Mon Sep 17 00:00:00 2001 From: Keke Li Date: Sun, 27 Apr 2025 14:27:18 +0800 Subject: Documentation: media: Add documentation file c3-isp.rst Add the file 'c3-isp.rst' that documents the c3-isp driver. Signed-off-by: Keke Li Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- Documentation/admin-guide/media/c3-isp.dot | 26 ++++++ Documentation/admin-guide/media/c3-isp.rst | 101 ++++++++++++++++++++++++ Documentation/admin-guide/media/v4l-drivers.rst | 1 + MAINTAINERS | 2 + 4 files changed, 130 insertions(+) create mode 100644 Documentation/admin-guide/media/c3-isp.dot create mode 100644 Documentation/admin-guide/media/c3-isp.rst diff --git a/Documentation/admin-guide/media/c3-isp.dot b/Documentation/admin-guide/media/c3-isp.dot new file mode 100644 index 000000000000..42dc931ee84a --- /dev/null +++ b/Documentation/admin-guide/media/c3-isp.dot @@ -0,0 +1,26 @@ +digraph board { + rankdir=TB + n00000001 [label="{{ 0 | 1} | c3-isp-core\n/dev/v4l-subdev0 | { 2 | 3 | 4 | 5}}", shape=Mrecord, style=filled, fillcolor=green] + n00000001:port3 -> n00000008:port0 + n00000001:port4 -> n0000000b:port0 + n00000001:port5 -> n0000000e:port0 + n00000001:port2 -> n00000027 + n00000008 [label="{{ 0} | c3-isp-resizer0\n/dev/v4l-subdev1 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000008:port1 -> n00000016 [style=bold] + n0000000b [label="{{ 0} | c3-isp-resizer1\n/dev/v4l-subdev2 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n0000000b:port1 -> n0000001a [style=bold] + n0000000e [label="{{ 0} | c3-isp-resizer2\n/dev/v4l-subdev3 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n0000000e:port1 -> n00000023 [style=bold] + n00000011 [label="{{ 0} | c3-mipi-adapter\n/dev/v4l-subdev4 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000011:port1 -> n00000001:port0 [style=bold] + n00000016 [label="c3-isp-cap0\n/dev/video0", shape=box, style=filled, fillcolor=yellow] + n0000001a [label="c3-isp-cap1\n/dev/video1", shape=box, style=filled, fillcolor=yellow] + n0000001e [label="{{ 0} | c3-mipi-csi2\n/dev/v4l-subdev5 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n0000001e:port1 -> n00000011:port0 [style=bold] + n00000023 [label="c3-isp-cap2\n/dev/video2", shape=box, style=filled, fillcolor=yellow] + n00000027 [label="c3-isp-stats\n/dev/video3", shape=box, style=filled, fillcolor=yellow] + n0000002b [label="c3-isp-params\n/dev/video4", shape=box, style=filled, fillcolor=yellow] + n0000002b -> n00000001:port1 + n0000003f [label="{{} | imx290 2-001a\n/dev/v4l-subdev6 | { 0}}", shape=Mrecord, style=filled, fillcolor=green] + n0000003f:port0 -> n0000001e:port0 [style=bold] +} diff --git a/Documentation/admin-guide/media/c3-isp.rst b/Documentation/admin-guide/media/c3-isp.rst new file mode 100644 index 000000000000..ac508b8c6831 --- /dev/null +++ b/Documentation/admin-guide/media/c3-isp.rst @@ -0,0 +1,101 @@ +.. SPDX-License-Identifier: (GPL-2.0-only OR MIT) + +.. include:: + +================================================= +Amlogic C3 Image Signal Processing (C3ISP) driver +================================================= + +Introduction +============ + +This file documents the Amlogic C3ISP driver located under +drivers/media/platform/amlogic/c3/isp. + +The current version of the driver supports the C3ISP found on +Amlogic C308L processor. + +The driver implements V4L2, Media controller and V4L2 subdev interfaces. +Camera sensor using V4L2 subdev interface in the kernel is supported. + +The driver has been tested on AW419-C308L-Socket platform. + +Amlogic C3 ISP +============== + +The Camera hardware found on C308L processors and supported by +the driver consists of: + +- 1 MIPI-CSI-2 module: handles the physical layer of the MIPI CSI-2 receiver and + receives data from the connected camera sensor. +- 1 MIPI-ADAPTER module: organizes MIPI data to meet ISP input requirements and + send MIPI data to ISP. +- 1 ISP (Image Signal Processing) module: contains a pipeline of image processing + hardware blocks. The ISP pipeline contains three resizers at the end each of + them connected to a DMA interface which writes the output data to memory. + +A high-level functional view of the C3 ISP is presented below.:: + + +----------+ +-------+ + | Resizer |--->| WRMIF | + +---------+ +------------+ +--------------+ +-------+ |----------+ +-------+ + | Sensor |--->| MIPI CSI-2 |--->| MIPI ADAPTER |--->| ISP |---|----------+ +-------+ + +---------+ +------------+ +--------------+ +-------+ | Resizer |--->| WRMIF | + +----------+ +-------+ + |----------+ +-------+ + | Resizer |--->| WRMIF | + +----------+ +-------+ + +Driver architecture and design +============================== + +With the goal to model the hardware links between the modules and to expose a +clean, logical and usable interface, the driver registers the following V4L2 +sub-devices: + +- 1 `c3-mipi-csi2` sub-device - the MIPI CSI-2 receiver +- 1 `c3-mipi-adapter` sub-device - the MIPI adapter +- 1 `c3-isp-core` sub-device - the ISP core +- 3 `c3-isp-resizer` sub-devices - the ISP resizers + +The `c3-isp-core` sub-device is linked to 2 video device nodes for statistics +capture and parameters programming: + +- the `c3-isp-stats` capture video device node for statistics capture +- the `c3-isp-params` output video device for parameters programming + +Each `c3-isp-resizer` sub-device is linked to a capture video device node where +frames are captured from: + +- `c3-isp-resizer0` is linked to the `c3-isp-cap0` capture video device +- `c3-isp-resizer1` is linked to the `c3-isp-cap1` capture video device +- `c3-isp-resizer2` is linked to the `c3-isp-cap2` capture video device + +The media controller pipeline graph is as follows (with connected a +IMX290 camera sensor): + +.. _isp_topology_graph: + +.. kernel-figure:: c3-isp.dot + :alt: c3-isp.dot + :align: center + + Media pipeline topology + +Implementation +============== + +Runtime configuration of the ISP hardware is performed on the `c3-isp-params` +video device node using the :ref:`V4L2_META_FMT_C3ISP_PARAMS +` as data format. The buffer structure is defined by +:c:type:`c3_isp_params_cfg`. + +Statistics are captured from the `c3-isp-stats` video device node using the +:ref:`V4L2_META_FMT_C3ISP_STATS ` data format. + +The final picture size and format is configured using the V4L2 video +capture interface on the `c3-isp-cap[0, 2]` video device nodes. + +The Amlogic C3 ISP is supported by `libcamera `_ with a +dedicated pipeline handler and algorithms that perform run-time image correction +and enhancement. diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst index e8761561b2fe..3bac5165b134 100644 --- a/Documentation/admin-guide/media/v4l-drivers.rst +++ b/Documentation/admin-guide/media/v4l-drivers.rst @@ -10,6 +10,7 @@ Video4Linux (V4L) driver-specific documentation :maxdepth: 2 bttv + c3-isp cafe_ccic cx88 fimc diff --git a/MAINTAINERS b/MAINTAINERS index 627d6e755b16..137122d3dc4e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1258,6 +1258,8 @@ AMLOGIC ISP DRIVER M: Keke Li L: linux-media@vger.kernel.org S: Maintained +F: Documentation/admin-guide/media/c3-isp.dot +F: Documentation/admin-guide/media/c3-isp.rst F: Documentation/devicetree/bindings/media/amlogic,c3-isp.yaml F: Documentation/userspace-api/media/v4l/metafmt-c3-isp.rst F: drivers/media/platform/amlogic/c3/isp/ -- cgit v1.2.3-59-g8ed1b From 14f6e205e5599c2217b68c05b903ce162e7c1e27 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 27 Mar 2025 21:05:27 +0000 Subject: media: uvcvideo: Keep streaming state in the file handle Add a variable in the file handle state to figure out if a camera is in the streaming state or not. This variable will be used in the future for power management policies. Now that we are at it, make use of guards to simplify the code. Reviewed-by: Laurent Pinchart Reviewed-by: Hans de Goede Signed-off-by: Ricardo Ribalda Message-ID: <20250327-uvc-granpower-ng-v6-1-35a2357ff348@chromium.org> Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_v4l2.c | 18 +++++++++++++----- drivers/media/usb/uvc/uvcvideo.h | 1 + 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 39065db44e86..22886b47d81c 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -841,11 +841,18 @@ static int uvc_ioctl_streamon(struct file *file, void *fh, if (!uvc_has_privileges(handle)) return -EBUSY; - mutex_lock(&stream->mutex); + guard(mutex)(&stream->mutex); + + if (handle->is_streaming) + return 0; + ret = uvc_queue_streamon(&stream->queue, type); - mutex_unlock(&stream->mutex); + if (ret) + return ret; - return ret; + handle->is_streaming = true; + + return 0; } static int uvc_ioctl_streamoff(struct file *file, void *fh, @@ -857,9 +864,10 @@ static int uvc_ioctl_streamoff(struct file *file, void *fh, if (!uvc_has_privileges(handle)) return -EBUSY; - mutex_lock(&stream->mutex); + guard(mutex)(&stream->mutex); + uvc_queue_streamoff(&stream->queue, type); - mutex_unlock(&stream->mutex); + handle->is_streaming = false; return 0; } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index b4ee701835fc..5ceb01e7831a 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -630,6 +630,7 @@ struct uvc_fh { struct uvc_streaming *stream; enum uvc_handle_state state; unsigned int pending_async_ctrls; + bool is_streaming; }; /* ------------------------------------------------------------------------ -- cgit v1.2.3-59-g8ed1b From 2f101572c0a3ae4630f2a57c8033b78ee84ac5a6 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 27 Mar 2025 21:05:28 +0000 Subject: media: uvcvideo: Create uvc_pm_(get|put) functions Most of the times that we have to call uvc_status_(get|put) we need to call the usb_autopm_ functions. Create a new pair of functions that automate this for us. This simplifies the current code and future PM changes in the driver. Reviewed-by: Hans de Goede Signed-off-by: Ricardo Ribalda Message-ID: <20250327-uvc-granpower-ng-v6-2-35a2357ff348@chromium.org> Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_v4l2.c | 36 ++++++++++++++++++++++++------------ drivers/media/usb/uvc/uvcvideo.h | 4 ++++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 22886b47d81c..1d5be045d04e 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -26,6 +26,27 @@ #include "uvcvideo.h" +int uvc_pm_get(struct uvc_device *dev) +{ + int ret; + + ret = usb_autopm_get_interface(dev->intf); + if (ret) + return ret; + + ret = uvc_status_get(dev); + if (ret) + usb_autopm_put_interface(dev->intf); + + return ret; +} + +void uvc_pm_put(struct uvc_device *dev) +{ + uvc_status_put(dev); + usb_autopm_put_interface(dev->intf); +} + static int uvc_acquire_privileges(struct uvc_fh *handle); static int uvc_control_add_xu_mapping(struct uvc_video_chain *chain, @@ -642,20 +663,13 @@ static int uvc_v4l2_open(struct file *file) stream = video_drvdata(file); uvc_dbg(stream->dev, CALLS, "%s\n", __func__); - ret = usb_autopm_get_interface(stream->dev->intf); - if (ret < 0) - return ret; - /* Create the device handle. */ handle = kzalloc(sizeof(*handle), GFP_KERNEL); - if (handle == NULL) { - usb_autopm_put_interface(stream->dev->intf); + if (!handle) return -ENOMEM; - } - ret = uvc_status_get(stream->dev); + ret = uvc_pm_get(stream->dev); if (ret) { - usb_autopm_put_interface(stream->dev->intf); kfree(handle); return ret; } @@ -690,9 +704,7 @@ static int uvc_v4l2_release(struct file *file) kfree(handle); file->private_data = NULL; - uvc_status_put(stream->dev); - - usb_autopm_put_interface(stream->dev->intf); + uvc_pm_put(stream->dev); return 0; } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 5ceb01e7831a..b9f8eb62ba1d 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -768,6 +768,10 @@ void uvc_status_suspend(struct uvc_device *dev); int uvc_status_get(struct uvc_device *dev); void uvc_status_put(struct uvc_device *dev); +/* PM */ +int uvc_pm_get(struct uvc_device *dev); +void uvc_pm_put(struct uvc_device *dev); + /* Controls */ extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops; -- cgit v1.2.3-59-g8ed1b From 10acb9101355484c3e4f2625003cd1b6c203cfe4 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 27 Mar 2025 21:05:29 +0000 Subject: media: uvcvideo: Increase/decrease the PM counter per IOCTL Now we call uvc_pm_get/put from the device open/close. This low level of granularity might leave the camera powered on in situations where it is not needed. Increase the granularity by increasing and decreasing the Power Management counter per ioctl. There are two special cases where the power management outlives the ioctl: async controls and streamon. Handle those cases as well. In a future patch, we will remove the uvc_pm_get/put from open/close. Reviewed-by: Hans de Goede Signed-off-by: Ricardo Ribalda Message-ID: <20250327-uvc-granpower-ng-v6-3-35a2357ff348@chromium.org> Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_ctrl.c | 38 +++++++++++++++++++++++++++----------- drivers/media/usb/uvc/uvc_v4l2.c | 39 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index cbf19aa1d823..e2052130f4c9 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1812,38 +1812,49 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain, uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes); } -static void uvc_ctrl_set_handle(struct uvc_fh *handle, struct uvc_control *ctrl, - struct uvc_fh *new_handle) +static int uvc_ctrl_set_handle(struct uvc_fh *handle, struct uvc_control *ctrl, + struct uvc_fh *new_handle) { lockdep_assert_held(&handle->chain->ctrl_mutex); if (new_handle) { + int ret; + if (ctrl->handle) dev_warn_ratelimited(&handle->stream->dev->udev->dev, "UVC non compliance: Setting an async control with a pending operation."); if (new_handle == ctrl->handle) - return; + return 0; if (ctrl->handle) { WARN_ON(!ctrl->handle->pending_async_ctrls); if (ctrl->handle->pending_async_ctrls) ctrl->handle->pending_async_ctrls--; + ctrl->handle = new_handle; + handle->pending_async_ctrls++; + return 0; } + ret = uvc_pm_get(handle->chain->dev); + if (ret) + return ret; + ctrl->handle = new_handle; handle->pending_async_ctrls++; - return; + return 0; } /* Cannot clear the handle for a control not owned by us.*/ if (WARN_ON(ctrl->handle != handle)) - return; + return -EINVAL; ctrl->handle = NULL; if (WARN_ON(!handle->pending_async_ctrls)) - return; + return -EINVAL; handle->pending_async_ctrls--; + uvc_pm_put(handle->chain->dev); + return 0; } void uvc_ctrl_status_event(struct uvc_video_chain *chain, @@ -2137,15 +2148,15 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, ctrl->dirty = 0; + if (!rollback && handle && !ret && + ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) + ret = uvc_ctrl_set_handle(handle, ctrl, handle); + if (ret < 0) { if (err_ctrl) *err_ctrl = ctrl; return ret; } - - if (!rollback && handle && - ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) - uvc_ctrl_set_handle(handle, ctrl, handle); } return 0; @@ -3222,6 +3233,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev) void uvc_ctrl_cleanup_fh(struct uvc_fh *handle) { struct uvc_entity *entity; + int i; guard(mutex)(&handle->chain->ctrl_mutex); @@ -3236,7 +3248,11 @@ void uvc_ctrl_cleanup_fh(struct uvc_fh *handle) } } - WARN_ON(handle->pending_async_ctrls); + if (!WARN_ON(handle->pending_async_ctrls)) + return; + + for (i = 0; i < handle->pending_async_ctrls; i++) + uvc_pm_put(handle->stream->dev); } /* diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 1d5be045d04e..8bccf7e17528 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -697,6 +697,9 @@ static int uvc_v4l2_release(struct file *file) if (uvc_has_privileges(handle)) uvc_queue_release(&stream->queue); + if (handle->is_streaming) + uvc_pm_put(stream->dev); + /* Release the file handle. */ uvc_dismiss_privileges(handle); v4l2_fh_del(&handle->vfh); @@ -862,6 +865,11 @@ static int uvc_ioctl_streamon(struct file *file, void *fh, if (ret) return ret; + ret = uvc_pm_get(stream->dev); + if (ret) { + uvc_queue_streamoff(&stream->queue, type); + return ret; + } handle->is_streaming = true; return 0; @@ -879,7 +887,10 @@ static int uvc_ioctl_streamoff(struct file *file, void *fh, guard(mutex)(&stream->mutex); uvc_queue_streamoff(&stream->queue, type); - handle->is_streaming = false; + if (handle->is_streaming) { + handle->is_streaming = false; + uvc_pm_put(stream->dev); + } return 0; } @@ -1378,9 +1389,11 @@ static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp, #define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32) #define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32) +DEFINE_FREE(uvc_pm_put, struct uvc_device *, if (_T) uvc_pm_put(_T)) static long uvc_v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) { + struct uvc_device *uvc_device __free(uvc_pm_put) = NULL; struct uvc_fh *handle = file->private_data; union { struct uvc_xu_control_mapping xmap; @@ -1389,6 +1402,12 @@ static long uvc_v4l2_compat_ioctl32(struct file *file, void __user *up = compat_ptr(arg); long ret; + ret = uvc_pm_get(handle->stream->dev); + if (ret) + return ret; + + uvc_device = handle->stream->dev; + switch (cmd) { case UVCIOC_CTRL_MAP32: ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up); @@ -1423,6 +1442,22 @@ static long uvc_v4l2_compat_ioctl32(struct file *file, } #endif +static long uvc_v4l2_unlocked_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct uvc_fh *handle = file->private_data; + int ret; + + ret = uvc_pm_get(handle->stream->dev); + if (ret) + return ret; + + ret = video_ioctl2(file, cmd, arg); + + uvc_pm_put(handle->stream->dev); + return ret; +} + static ssize_t uvc_v4l2_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { @@ -1507,7 +1542,7 @@ const struct v4l2_file_operations uvc_fops = { .owner = THIS_MODULE, .open = uvc_v4l2_open, .release = uvc_v4l2_release, - .unlocked_ioctl = video_ioctl2, + .unlocked_ioctl = uvc_v4l2_unlocked_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl32 = uvc_v4l2_compat_ioctl32, #endif -- cgit v1.2.3-59-g8ed1b From a32d9c41bdb86e09ce731aa5fd3add89ac2103a5 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 27 Mar 2025 21:05:30 +0000 Subject: media: uvcvideo: Make power management granular Now that every ioctl takes care of their power management we can remove the "global" power management. Despite its size, this is a relatively big change. We hope that there are no size effects of it. If there are some specific devices that miss-behave, we can add a small quirk for them. This patch introduces a behavioral change for the uvc "trigger" button. Before the "trigger" button would work as long as userspace has opened /dev/videoX. Now it only works when the camera is actually streaming. We consider that this the most common (if not the only) usecase and therefore we do not think of this as a regression. Reviewed-by: Hans de Goede Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Message-ID: <20250327-uvc-granpower-ng-v6-4-35a2357ff348@chromium.org> Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_v4l2.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 8bccf7e17528..0f1ed0387b26 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -658,7 +658,6 @@ static int uvc_v4l2_open(struct file *file) { struct uvc_streaming *stream; struct uvc_fh *handle; - int ret = 0; stream = video_drvdata(file); uvc_dbg(stream->dev, CALLS, "%s\n", __func__); @@ -668,12 +667,6 @@ static int uvc_v4l2_open(struct file *file) if (!handle) return -ENOMEM; - ret = uvc_pm_get(stream->dev); - if (ret) { - kfree(handle); - return ret; - } - v4l2_fh_init(&handle->vfh, &stream->vdev); v4l2_fh_add(&handle->vfh); handle->chain = stream->chain; @@ -707,7 +700,6 @@ static int uvc_v4l2_release(struct file *file) kfree(handle); file->private_data = NULL; - uvc_pm_put(stream->dev); return 0; } -- cgit v1.2.3-59-g8ed1b From d1b618e7954802fe5a08f71b1ef33e9b3518479b Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 27 Mar 2025 21:05:31 +0000 Subject: media: uvcvideo: Do not turn on the camera for some ioctls There are some ioctls that do not need to turn on the camera. Do not call uvc_pm_get in those cases. Reviewed-by: Hans de Goede Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Message-ID: <20250327-uvc-granpower-ng-v6-5-35a2357ff348@chromium.org> Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_v4l2.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 0f1ed0387b26..668a4e9d772c 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -1440,6 +1440,26 @@ static long uvc_v4l2_unlocked_ioctl(struct file *file, struct uvc_fh *handle = file->private_data; int ret; + /* The following IOCTLs do not need to turn on the camera. */ + switch (cmd) { + case VIDIOC_CREATE_BUFS: + case VIDIOC_DQBUF: + case VIDIOC_ENUM_FMT: + case VIDIOC_ENUM_FRAMEINTERVALS: + case VIDIOC_ENUM_FRAMESIZES: + case VIDIOC_ENUMINPUT: + case VIDIOC_EXPBUF: + case VIDIOC_G_FMT: + case VIDIOC_G_PARM: + case VIDIOC_G_SELECTION: + case VIDIOC_QBUF: + case VIDIOC_QUERYCAP: + case VIDIOC_REQBUFS: + case VIDIOC_SUBSCRIBE_EVENT: + case VIDIOC_UNSUBSCRIBE_EVENT: + return video_ioctl2(file, cmd, arg); + } + ret = uvc_pm_get(handle->stream->dev); if (ret) return ret; -- cgit v1.2.3-59-g8ed1b From ba4fafb02ad6a4eb2e00f861893b5db42ba54369 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 24 Feb 2025 10:34:53 +0000 Subject: media: uvcvideo: Return the number of processed controls If we let know our callers that we have not done anything, they will be able to optimize their decisions. Cc: stable@kernel.org Fixes: b4012002f3a3 ("[media] uvcvideo: Add support for control events") Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Message-ID: <20250224-uvc-data-backup-v2-1-de993ed9823b@chromium.org> Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_ctrl.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index e2052130f4c9..0c4d84eab42a 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -2101,12 +2101,17 @@ int uvc_ctrl_begin(struct uvc_video_chain *chain) return mutex_lock_interruptible(&chain->ctrl_mutex) ? -ERESTARTSYS : 0; } +/* + * Returns the number of uvc controls that have been correctly set, or a + * negative number if there has been an error. + */ static int uvc_ctrl_commit_entity(struct uvc_device *dev, struct uvc_fh *handle, struct uvc_entity *entity, int rollback, struct uvc_control **err_ctrl) { + unsigned int processed_ctrls = 0; struct uvc_control *ctrl; unsigned int i; int ret; @@ -2141,6 +2146,9 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, else ret = 0; + if (!ret) + processed_ctrls++; + if (rollback || ret < 0) memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), @@ -2159,7 +2167,7 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, } } - return 0; + return processed_ctrls; } static int uvc_ctrl_find_ctrl_idx(struct uvc_entity *entity, @@ -2206,6 +2214,7 @@ int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, if (!rollback) uvc_ctrl_send_events(handle, ctrls->controls, ctrls->count); + ret = 0; done: mutex_unlock(&chain->ctrl_mutex); return ret; -- cgit v1.2.3-59-g8ed1b From 5c791467aea6277430da5f089b9b6c2a9d8a4af7 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 24 Feb 2025 10:34:54 +0000 Subject: media: uvcvideo: Send control events for partial succeeds Today, when we are applying a change to entities A, B. If A succeeds and B fails the events for A are not sent. This change changes the code so the events for A are send right after they happen. Cc: stable@kernel.org Fixes: b4012002f3a3 ("[media] uvcvideo: Add support for control events") Signed-off-by: Ricardo Ribalda Message-ID: <20250224-uvc-data-backup-v2-2-de993ed9823b@chromium.org> Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_ctrl.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 0c4d84eab42a..636ce1eb2a6b 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1954,7 +1954,9 @@ static bool uvc_ctrl_xctrls_has_control(const struct v4l2_ext_control *xctrls, } static void uvc_ctrl_send_events(struct uvc_fh *handle, - const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) + struct uvc_entity *entity, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count) { struct uvc_control_mapping *mapping; struct uvc_control *ctrl; @@ -1966,6 +1968,9 @@ static void uvc_ctrl_send_events(struct uvc_fh *handle, s32 value; ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping); + if (ctrl->entity != entity) + continue; + if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) /* Notification will be sent from an Interrupt event. */ continue; @@ -2209,11 +2214,12 @@ int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, uvc_ctrl_find_ctrl_idx(entity, ctrls, err_ctrl); goto done; + } else if (ret > 0 && !rollback) { + uvc_ctrl_send_events(handle, entity, + ctrls->controls, ctrls->count); } } - if (!rollback) - uvc_ctrl_send_events(handle, ctrls->controls, ctrls->count); ret = 0; done: mutex_unlock(&chain->ctrl_mutex); -- cgit v1.2.3-59-g8ed1b From a70705d3c020d0d5c3ab6a5cc93e011ac35e7d48 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 24 Feb 2025 10:34:55 +0000 Subject: media: uvcvideo: Rollback non processed entities on error If we fail to commit an entity, we need to restore the UVC_CTRL_DATA_BACKUP for the other uncommitted entities. Otherwise the control cache and the device would be out of sync. Cc: stable@kernel.org Fixes: b4012002f3a3 ("[media] uvcvideo: Add support for control events") Reported-by: Hans de Goede Closes: https://lore.kernel.org/linux-media/fe845e04-9fde-46ee-9763-a6f00867929a@redhat.com/ Signed-off-by: Ricardo Ribalda Message-ID: <20250224-uvc-data-backup-v2-3-de993ed9823b@chromium.org> Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_ctrl.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 636ce1eb2a6b..44b6513c5264 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -2119,7 +2119,7 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, unsigned int processed_ctrls = 0; struct uvc_control *ctrl; unsigned int i; - int ret; + int ret = 0; if (entity == NULL) return 0; @@ -2148,8 +2148,6 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, dev->intfnum, ctrl->info.selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info.size); - else - ret = 0; if (!ret) processed_ctrls++; @@ -2165,13 +2163,20 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) ret = uvc_ctrl_set_handle(handle, ctrl, handle); - if (ret < 0) { + if (ret < 0 && !rollback) { if (err_ctrl) *err_ctrl = ctrl; - return ret; + /* + * If we fail to set a control, we need to rollback + * the next ones. + */ + rollback = 1; } } + if (ret) + return ret; + return processed_ctrls; } @@ -2202,7 +2207,8 @@ int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, struct uvc_video_chain *chain = handle->chain; struct uvc_control *err_ctrl; struct uvc_entity *entity; - int ret = 0; + int ret_out = 0; + int ret; /* Find the control. */ list_for_each_entry(entity, &chain->entities, chain) { @@ -2213,17 +2219,23 @@ int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, ctrls->error_idx = uvc_ctrl_find_ctrl_idx(entity, ctrls, err_ctrl); - goto done; + /* + * When we fail to commit an entity, we need to + * restore the UVC_CTRL_DATA_BACKUP for all the + * controls in the other entities, otherwise our cache + * and the hardware will be out of sync. + */ + rollback = 1; + + ret_out = ret; } else if (ret > 0 && !rollback) { uvc_ctrl_send_events(handle, entity, ctrls->controls, ctrls->count); } } - ret = 0; -done: mutex_unlock(&chain->ctrl_mutex); - return ret; + return ret_out; } static int uvc_mapping_get_xctrl_compound(struct uvc_video_chain *chain, -- cgit v1.2.3-59-g8ed1b From 387e8939307192d5a852a2afeeb83427fa477151 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 13 Mar 2025 12:20:39 +0000 Subject: media: uvcvideo: Fix deferred probing error uvc_gpio_parse() can return -EPROBE_DEFER when the GPIOs it depends on have not yet been probed. This return code should be propagated to the caller of uvc_probe() to ensure that probing is retried when the required GPIOs become available. Currently, this error code is incorrectly converted to -ENODEV, causing some internal cameras to be ignored. This commit fixes this issue by propagating the -EPROBE_DEFER error. Cc: stable@vger.kernel.org Fixes: 2886477ff987 ("media: uvcvideo: Implement UVC_EXT_GPIO_UNIT") Reviewed-by: Douglas Anderson Signed-off-by: Ricardo Ribalda Message-ID: <20250313-uvc-eprobedefer-v3-1-a1d312708eef@chromium.org> Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_driver.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 107e0fafd80f..25e9aea81196 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2232,13 +2232,16 @@ static int uvc_probe(struct usb_interface *intf, #endif /* Parse the Video Class control descriptor. */ - if (uvc_parse_control(dev) < 0) { + ret = uvc_parse_control(dev); + if (ret < 0) { + ret = -ENODEV; uvc_dbg(dev, PROBE, "Unable to parse UVC descriptors\n"); goto error; } /* Parse the associated GPIOs. */ - if (uvc_gpio_parse(dev) < 0) { + ret = uvc_gpio_parse(dev); + if (ret < 0) { uvc_dbg(dev, PROBE, "Unable to parse UVC GPIOs\n"); goto error; } @@ -2264,24 +2267,32 @@ static int uvc_probe(struct usb_interface *intf, } /* Register the V4L2 device. */ - if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) + ret = v4l2_device_register(&intf->dev, &dev->vdev); + if (ret < 0) goto error; /* Scan the device for video chains. */ - if (uvc_scan_device(dev) < 0) + if (uvc_scan_device(dev) < 0) { + ret = -ENODEV; goto error; + } /* Initialize controls. */ - if (uvc_ctrl_init_device(dev) < 0) + if (uvc_ctrl_init_device(dev) < 0) { + ret = -ENODEV; goto error; + } /* Register video device nodes. */ - if (uvc_register_chains(dev) < 0) + if (uvc_register_chains(dev) < 0) { + ret = -ENODEV; goto error; + } #ifdef CONFIG_MEDIA_CONTROLLER /* Register the media device node */ - if (media_device_register(&dev->mdev) < 0) + ret = media_device_register(&dev->mdev); + if (ret < 0) goto error; #endif /* Save our data pointer in the interface data. */ @@ -2315,7 +2326,7 @@ static int uvc_probe(struct usb_interface *intf, error: uvc_unregister_video(dev); kref_put(&dev->ref, uvc_delete); - return -ENODEV; + return ret; } static void uvc_disconnect(struct usb_interface *intf) -- cgit v1.2.3-59-g8ed1b From 3328eb4dfec23cb3055cda24087cd1cdee925676 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 13 Mar 2025 12:20:40 +0000 Subject: media: uvcvideo: Use dev_err_probe for devm_gpiod_get_optional Use the dev_err_probe() helper for devm_gpiod_get_optional(), like we do with gpiod_to_irq() That eventually calls device_set_deferred_probe_reason() which can be helpful for tracking down problems. Now that all the error paths in uvc_gpio_parse have dev_err_probe, we can remove the error message in uvc_probe. Suggested-by: Doug Anderson Reviewed-by: Douglas Anderson Signed-off-by: Ricardo Ribalda Message-ID: <20250313-uvc-eprobedefer-v3-2-a1d312708eef@chromium.org> Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/usb/uvc/uvc_driver.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 25e9aea81196..da24a655ab68 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1299,8 +1299,13 @@ static int uvc_gpio_parse(struct uvc_device *dev) gpio_privacy = devm_gpiod_get_optional(&dev->intf->dev, "privacy", GPIOD_IN); - if (IS_ERR_OR_NULL(gpio_privacy)) - return PTR_ERR_OR_ZERO(gpio_privacy); + if (!gpio_privacy) + return 0; + + if (IS_ERR(gpio_privacy)) + return dev_err_probe(&dev->intf->dev, + PTR_ERR(gpio_privacy), + "Can't get privacy GPIO\n"); irq = gpiod_to_irq(gpio_privacy); if (irq < 0) @@ -2241,10 +2246,8 @@ static int uvc_probe(struct usb_interface *intf, /* Parse the associated GPIOs. */ ret = uvc_gpio_parse(dev); - if (ret < 0) { - uvc_dbg(dev, PROBE, "Unable to parse UVC GPIOs\n"); + if (ret < 0) goto error; - } dev_info(&dev->udev->dev, "Found UVC %u.%02x device %s (%04x:%04x)\n", dev->uvc_version >> 8, dev->uvc_version & 0xff, -- cgit v1.2.3-59-g8ed1b From 1b83a9f41bd13dae09fabf594918ea36a9bc0cfc Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Sat, 10 May 2025 10:48:52 +0200 Subject: media: amlogic: c3-mipi-csi2: Handle 64-bits division The kernel test robot reports the following error when building on Hexagon with hexagon-allmodconfig. ERROR: modpost: "__hexagon_divdi3" [drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.ko] undefined! The error is caused by using DIV_ROUND_UP() with a 64 bits divisor with a 32-bit dividend, which on Hexagon and clang-17 is resolved with a call to the __hexagon_divdi3() helper function, part of the compiler support library and not available when building Linux. Use DIV_ROUND_UP_ULL() to fix the build error and avoid calling the __hexagon_divdi3() helper function. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202505101334.UHxNcUUO-lkp@intel.com/ Signed-off-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c b/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c index f92815ffa4ae..1011ab3ebac7 100644 --- a/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c +++ b/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -321,7 +322,7 @@ static void c3_mipi_csi_cfg_dphy(struct c3_csi_device *csi, s64 rate) u32 settle; /* Calculate the high speed settle */ - val = DIV_ROUND_UP(1000000000, rate); + val = DIV_ROUND_UP_ULL(1000000000, rate); settle = (16 * val + 230) / 10; c3_mipi_csi_write(csi, MIPI_PHY_CLK_LANE_CTRL, -- cgit v1.2.3-59-g8ed1b From 59f94c57b5175cd094f2ded4f0437217bce2447d Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Wed, 5 Feb 2025 02:18:34 +0000 Subject: media: platform: mtk-mdp3: Remove unused mdp_get_plat_device mdp_get_plat_device() was added in 2022 but has remained unused. Remove it. Fixes: 61890ccaefaf ("media: platform: mtk-mdp3: add MediaTek MDP3 driver") Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h | 2 -- drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c | 19 ------------------- 2 files changed, 21 deletions(-) diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h index 935ae9825728..222611e03a06 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h @@ -12,8 +12,6 @@ #include #include "mtk-img-ipi.h" -struct platform_device *mdp_get_plat_device(struct platform_device *pdev); - struct mdp_cmdq_param { struct img_config *config; struct img_ipi_frameparam *param; diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c index f571f561f070..8de2c8e4d333 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -79,25 +79,6 @@ static struct platform_device *__get_pdev_by_id(struct platform_device *pdev, return mdp_pdev; } -struct platform_device *mdp_get_plat_device(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *mdp_node; - struct platform_device *mdp_pdev; - - mdp_node = of_parse_phandle(dev->of_node, MDP_PHANDLE_NAME, 0); - if (!mdp_node) { - dev_err(dev, "can't get node %s\n", MDP_PHANDLE_NAME); - return NULL; - } - - mdp_pdev = of_find_device_by_node(mdp_node); - of_node_put(mdp_node); - - return mdp_pdev; -} -EXPORT_SYMBOL_GPL(mdp_get_plat_device); - int mdp_vpu_get_locked(struct mdp_dev *mdp) { int ret = 0; -- cgit v1.2.3-59-g8ed1b From 11beb0fc346e00c412b3bfd19013206f6b655604 Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Fri, 25 Apr 2025 15:24:47 -0400 Subject: media: verisilicon: Free post processor buffers on error During initialization, the post processor allocates the same number of buffers as the buf queue. As the init function is called in streamon(), if an allocation fails, streamon will return an error and streamoff() will not be called, keeping all post processor buffers allocated. To avoid that, all post proc buffers are freed in case of an allocation error. Fixes: 26711491a807 ("media: verisilicon: Refactor postprocessor to store more buffers") Signed-off-by: Detlev Casanova Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/hantro_postproc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/verisilicon/hantro_postproc.c b/drivers/media/platform/verisilicon/hantro_postproc.c index c435a393e0cb..9f559a13d409 100644 --- a/drivers/media/platform/verisilicon/hantro_postproc.c +++ b/drivers/media/platform/verisilicon/hantro_postproc.c @@ -250,8 +250,10 @@ int hantro_postproc_init(struct hantro_ctx *ctx) for (i = 0; i < num_buffers; i++) { ret = hantro_postproc_alloc(ctx, i); - if (ret) + if (ret) { + hantro_postproc_free(ctx); return ret; + } } return 0; -- cgit v1.2.3-59-g8ed1b From 7560349ee0d9441642da6d38c3822d08fffdd2fb Mon Sep 17 00:00:00 2001 From: Jianhua Lin Date: Thu, 24 Apr 2025 17:08:24 +0800 Subject: media: mediatek: jpeg: support 34bits The HW iommu is able to support a 34-bit iova address-space (16GB), enable this feature for the encoder/decoder driver by shifting the address by two bits and setting the extended address registers. Signed-off-by: Jianhua Lin Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../media/platform/mediatek/jpeg/mtk_jpeg_core.c | 5 +- .../media/platform/mediatek/jpeg/mtk_jpeg_core.h | 4 ++ .../media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c | 73 ++++++++++++++++------ .../media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h | 1 + .../platform/mediatek/jpeg/mtk_jpeg_dec_reg.h | 8 +++ .../media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c | 33 ++++++++-- .../media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h | 7 ++- 7 files changed, 104 insertions(+), 27 deletions(-) diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c index 834d2a354692..7eb12449b63a 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c @@ -1026,6 +1026,7 @@ static void mtk_jpeg_dec_device_run(void *priv) spin_lock_irqsave(&jpeg->hw_lock, flags); mtk_jpeg_dec_reset(jpeg->reg_base); mtk_jpeg_dec_set_config(jpeg->reg_base, + jpeg->variant->support_34bit, &jpeg_src_buf->dec_param, jpeg_src_buf->bs_size, &bs, @@ -1570,7 +1571,8 @@ static irqreturn_t mtk_jpeg_enc_done(struct mtk_jpeg_dev *jpeg) src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - result_size = mtk_jpeg_enc_get_file_size(jpeg->reg_base); + result_size = mtk_jpeg_enc_get_file_size(jpeg->reg_base, + jpeg->variant->support_34bit); vb2_set_plane_payload(&dst_buf->vb2_buf, 0, result_size); buf_state = VB2_BUF_STATE_DONE; @@ -1770,6 +1772,7 @@ retry_select: ctx->total_frame_num++; mtk_jpeg_dec_reset(comp_jpeg[hw_id]->reg_base); mtk_jpeg_dec_set_config(comp_jpeg[hw_id]->reg_base, + jpeg->variant->support_34bit, &jpeg_src_buf->dec_param, jpeg_src_buf->bs_size, &bs, diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h index 8877eb39e807..02ed0ed5b736 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h @@ -34,6 +34,8 @@ #define MTK_JPEG_MAX_EXIF_SIZE (64 * 1024) +#define MTK_JPEG_ADDR_MASK GENMASK(1, 0) + /** * enum mtk_jpeg_ctx_state - states of the context state machine * @MTK_JPEG_INIT: current state is initialized @@ -62,6 +64,7 @@ enum mtk_jpeg_ctx_state { * @cap_q_default_fourcc: capture queue default fourcc * @multi_core: mark jpeg hw is multi_core or not * @jpeg_worker: jpeg dec or enc worker + * @support_34bit: flag to check support for 34-bit DMA address */ struct mtk_jpeg_variant { struct clk_bulk_data *clks; @@ -78,6 +81,7 @@ struct mtk_jpeg_variant { u32 cap_q_default_fourcc; bool multi_core; void (*jpeg_worker)(struct work_struct *work); + bool support_34bit; }; struct mtk_jpeg_src_buf { diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c index 2c5d74939d0a..e78e1d11093c 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c @@ -5,6 +5,8 @@ * Rick Chang */ +#include +#include #include #include #include @@ -279,23 +281,43 @@ static void mtk_jpeg_dec_set_brz_factor(void __iomem *base, u8 yscale_w, writel(val, base + JPGDEC_REG_BRZ_FACTOR); } -static void mtk_jpeg_dec_set_dst_bank0(void __iomem *base, u32 addr_y, - u32 addr_u, u32 addr_v) +static void mtk_jpeg_dec_set_dst_bank0(void __iomem *base, bool support_34bit, + dma_addr_t addr_y, dma_addr_t addr_u, dma_addr_t addr_v) { + u32 val; + mtk_jpeg_verify_align(addr_y, 16, JPGDEC_REG_DEST_ADDR0_Y); - writel(addr_y, base + JPGDEC_REG_DEST_ADDR0_Y); + writel(lower_32_bits(addr_y), base + JPGDEC_REG_DEST_ADDR0_Y); mtk_jpeg_verify_align(addr_u, 16, JPGDEC_REG_DEST_ADDR0_U); - writel(addr_u, base + JPGDEC_REG_DEST_ADDR0_U); + writel(lower_32_bits(addr_u), base + JPGDEC_REG_DEST_ADDR0_U); mtk_jpeg_verify_align(addr_v, 16, JPGDEC_REG_DEST_ADDR0_V); - writel(addr_v, base + JPGDEC_REG_DEST_ADDR0_V); + writel(lower_32_bits(addr_v), base + JPGDEC_REG_DEST_ADDR0_V); + if (support_34bit) { + val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_y)); + writel(val, base + JPGDEC_REG_DEST_ADDR0_Y_EXT); + val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_u)); + writel(val, base + JPGDEC_REG_DEST_ADDR0_U_EXT); + val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_v)); + writel(val, base + JPGDEC_REG_DEST_ADDR0_V_EXT); + } } -static void mtk_jpeg_dec_set_dst_bank1(void __iomem *base, u32 addr_y, - u32 addr_u, u32 addr_v) +static void mtk_jpeg_dec_set_dst_bank1(void __iomem *base, bool support_34bit, + dma_addr_t addr_y, dma_addr_t addr_u, dma_addr_t addr_v) { - writel(addr_y, base + JPGDEC_REG_DEST_ADDR1_Y); - writel(addr_u, base + JPGDEC_REG_DEST_ADDR1_U); - writel(addr_v, base + JPGDEC_REG_DEST_ADDR1_V); + u32 val; + + writel(lower_32_bits(addr_y), base + JPGDEC_REG_DEST_ADDR1_Y); + writel(lower_32_bits(addr_u), base + JPGDEC_REG_DEST_ADDR1_U); + writel(lower_32_bits(addr_v), base + JPGDEC_REG_DEST_ADDR1_V); + if (support_34bit) { + val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_y)); + writel(val, base + JPGDEC_REG_DEST_ADDR1_Y_EXT); + val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_u)); + writel(val, base + JPGDEC_REG_DEST_ADDR1_U_EXT); + val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_v)); + writel(val, base + JPGDEC_REG_DEST_ADDR1_V_EXT); + } } static void mtk_jpeg_dec_set_mem_stride(void __iomem *base, u32 stride_y, @@ -322,18 +344,30 @@ static void mtk_jpeg_dec_set_dec_mode(void __iomem *base, u32 mode) writel(mode & 0x03, base + JPGDEC_REG_OPERATION_MODE); } -static void mtk_jpeg_dec_set_bs_write_ptr(void __iomem *base, u32 ptr) +static void mtk_jpeg_dec_set_bs_write_ptr(void __iomem *base, bool support_34bit, dma_addr_t ptr) { + u32 val; + mtk_jpeg_verify_align(ptr, 16, JPGDEC_REG_FILE_BRP); - writel(ptr, base + JPGDEC_REG_FILE_BRP); + writel(lower_32_bits(ptr), base + JPGDEC_REG_FILE_BRP); + if (support_34bit) { + val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(ptr)); + writel(val, base + JPGDEC_REG_FILE_BRP_EXT); + } } -static void mtk_jpeg_dec_set_bs_info(void __iomem *base, u32 addr, u32 size, - u32 bitstream_size) +static void mtk_jpeg_dec_set_bs_info(void __iomem *base, bool support_34bit, + dma_addr_t addr, u32 size, u32 bitstream_size) { + u32 val; + mtk_jpeg_verify_align(addr, 16, JPGDEC_REG_FILE_ADDR); mtk_jpeg_verify_align(size, 128, JPGDEC_REG_FILE_TOTAL_SIZE); - writel(addr, base + JPGDEC_REG_FILE_ADDR); + writel(lower_32_bits(addr), base + JPGDEC_REG_FILE_ADDR); + if (support_34bit) { + val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr)); + writel(val, base + JPGDEC_REG_FILE_ADDR_EXT); + } writel(size, base + JPGDEC_REG_FILE_TOTAL_SIZE); writel(bitstream_size, base + JPGDEC_REG_BIT_STREAM_SIZE); } @@ -404,6 +438,7 @@ static void mtk_jpeg_dec_set_sampling_factor(void __iomem *base, u32 comp_num, } void mtk_jpeg_dec_set_config(void __iomem *base, + bool support_34bits, struct mtk_jpeg_dec_param *cfg, u32 bitstream_size, struct mtk_jpeg_bs *bs, @@ -413,8 +448,8 @@ void mtk_jpeg_dec_set_config(void __iomem *base, mtk_jpeg_dec_set_dec_mode(base, 0); mtk_jpeg_dec_set_comp0_du(base, cfg->unit_num); mtk_jpeg_dec_set_total_mcu(base, cfg->total_mcu); - mtk_jpeg_dec_set_bs_info(base, bs->str_addr, bs->size, bitstream_size); - mtk_jpeg_dec_set_bs_write_ptr(base, bs->end_addr); + mtk_jpeg_dec_set_bs_info(base, support_34bits, bs->str_addr, bs->size, bitstream_size); + mtk_jpeg_dec_set_bs_write_ptr(base, support_34bits, bs->end_addr); mtk_jpeg_dec_set_du_membership(base, cfg->membership, 1, (cfg->comp_num == 1) ? 1 : 0); mtk_jpeg_dec_set_comp_id(base, cfg->comp_id[0], cfg->comp_id[1], @@ -432,9 +467,9 @@ void mtk_jpeg_dec_set_config(void __iomem *base, cfg->mem_stride[1]); mtk_jpeg_dec_set_img_stride(base, cfg->img_stride[0], cfg->img_stride[1]); - mtk_jpeg_dec_set_dst_bank0(base, fb->plane_addr[0], + mtk_jpeg_dec_set_dst_bank0(base, support_34bits, fb->plane_addr[0], fb->plane_addr[1], fb->plane_addr[2]); - mtk_jpeg_dec_set_dst_bank1(base, 0, 0, 0); + mtk_jpeg_dec_set_dst_bank1(base, support_34bits, 0, 0, 0); mtk_jpeg_dec_set_dma_group(base, cfg->dma_mcu, cfg->dma_group, cfg->dma_last_mcu); mtk_jpeg_dec_set_pause_mcu_idx(base, cfg->total_mcu); diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h index 8c31c6b12417..2948c9c300a4 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h @@ -71,6 +71,7 @@ int mtk_jpeg_dec_fill_param(struct mtk_jpeg_dec_param *param); u32 mtk_jpeg_dec_get_int_status(void __iomem *dec_reg_base); u32 mtk_jpeg_dec_enum_result(u32 irq_result); void mtk_jpeg_dec_set_config(void __iomem *base, + bool support_34bits, struct mtk_jpeg_dec_param *cfg, u32 bitstream_size, struct mtk_jpeg_bs *bs, diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h index 27b7711ca341..e94f52de7c69 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h @@ -46,5 +46,13 @@ #define JPGDEC_REG_INTERRUPT_STATUS 0x0274 #define JPGDEC_REG_STATUS 0x0278 #define JPGDEC_REG_BIT_STREAM_SIZE 0x0344 +#define JPGDEC_REG_DEST_ADDR0_Y_EXT 0x0360 +#define JPGDEC_REG_DEST_ADDR0_U_EXT 0x0364 +#define JPGDEC_REG_DEST_ADDR0_V_EXT 0x0368 +#define JPGDEC_REG_DEST_ADDR1_Y_EXT 0x036c +#define JPGDEC_REG_DEST_ADDR1_U_EXT 0x0370 +#define JPGDEC_REG_DEST_ADDR1_V_EXT 0x0374 +#define JPGDEC_REG_FILE_ADDR_EXT 0x0378 +#define JPGDEC_REG_FILE_BRP_EXT 0x037c #endif /* _MTK_JPEG_REG_H */ diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c index f8fa3b841ccf..9ab27aee302a 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c @@ -5,6 +5,8 @@ * */ +#include +#include #include #include #include @@ -62,9 +64,9 @@ void mtk_jpeg_enc_reset(void __iomem *base) } EXPORT_SYMBOL_GPL(mtk_jpeg_enc_reset); -u32 mtk_jpeg_enc_get_file_size(void __iomem *base) +u32 mtk_jpeg_enc_get_file_size(void __iomem *base, bool support_34bit) { - return readl(base + JPEG_ENC_DMA_ADDR0) - + return (readl(base + JPEG_ENC_DMA_ADDR0) << ((support_34bit) ? 2 : 0)) - readl(base + JPEG_ENC_DST_ADDR0); } EXPORT_SYMBOL_GPL(mtk_jpeg_enc_get_file_size); @@ -84,14 +86,24 @@ void mtk_jpeg_set_enc_src(struct mtk_jpeg_ctx *ctx, void __iomem *base, { int i; dma_addr_t dma_addr; + u32 addr_ext; + bool support_34bit = ctx->jpeg->variant->support_34bit; for (i = 0; i < src_buf->num_planes; i++) { dma_addr = vb2_dma_contig_plane_dma_addr(src_buf, i) + src_buf->planes[i].data_offset; - if (!i) - writel(dma_addr, base + JPEG_ENC_SRC_LUMA_ADDR); + if (i == 0) + writel(lower_32_bits(dma_addr), base + JPEG_ENC_SRC_LUMA_ADDR); else - writel(dma_addr, base + JPEG_ENC_SRC_CHROMA_ADDR); + writel(lower_32_bits(dma_addr), base + JPEG_ENC_SRC_CHROMA_ADDR); + + if (support_34bit) { + addr_ext = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(dma_addr)); + if (i == 0) + writel(addr_ext, base + JPEG_ENC_SRC_LUMA_ADDR_EXT); + else + writel(addr_ext, base + JPEG_ENC_SRC_CHRO_ADDR_EXT); + } } } EXPORT_SYMBOL_GPL(mtk_jpeg_set_enc_src); @@ -103,6 +115,8 @@ void mtk_jpeg_set_enc_dst(struct mtk_jpeg_ctx *ctx, void __iomem *base, size_t size; u32 dma_addr_offset; u32 dma_addr_offsetmask; + u32 addr_ext; + bool support_34bit = ctx->jpeg->variant->support_34bit; dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); dma_addr_offset = ctx->enable_exif ? MTK_JPEG_MAX_EXIF_SIZE : 0; @@ -113,6 +127,12 @@ void mtk_jpeg_set_enc_dst(struct mtk_jpeg_ctx *ctx, void __iomem *base, writel(dma_addr_offsetmask & 0xf, base + JPEG_ENC_BYTE_OFFSET_MASK); writel(dma_addr & ~0xf, base + JPEG_ENC_DST_ADDR0); writel((dma_addr + size) & ~0xf, base + JPEG_ENC_STALL_ADDR0); + + if (support_34bit) { + addr_ext = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(dma_addr)); + writel(addr_ext, base + JPEG_ENC_DEST_ADDR0_EXT); + writel(addr_ext + size, base + JPEG_ENC_STALL_ADDR0_EXT); + } } EXPORT_SYMBOL_GPL(mtk_jpeg_set_enc_dst); @@ -278,7 +298,8 @@ static irqreturn_t mtk_jpegenc_hw_irq_handler(int irq, void *priv) if (!(irq_status & JPEG_ENC_INT_STATUS_DONE)) dev_warn(jpeg->dev, "Jpg Enc occurs unknown Err."); - result_size = mtk_jpeg_enc_get_file_size(jpeg->reg_base); + result_size = mtk_jpeg_enc_get_file_size(jpeg->reg_base, + ctx->jpeg->variant->support_34bit); vb2_set_plane_payload(&dst_buf->vb2_buf, 0, result_size); buf_state = VB2_BUF_STATE_DONE; v4l2_m2m_buf_done(src_buf, buf_state); diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h index 61c60e4e58ea..31ec9030ae88 100644 --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h @@ -68,6 +68,11 @@ #define JPEG_ENC_DCM_CTRL 0x300 #define JPEG_ENC_CODEC_SEL 0x314 #define JPEG_ENC_ULTRA_THRES 0x318 +#define JPEG_ENC_SRC_LUMA_ADDR_EXT 0x584 +#define JPEG_ENC_SRC_CHRO_ADDR_EXT 0x588 +#define JPEG_ENC_Q_TBL_ADDR_EXT 0x58C +#define JPEG_ENC_DEST_ADDR0_EXT 0x590 +#define JPEG_ENC_STALL_ADDR0_EXT 0x594 /** * struct mtk_jpeg_enc_qlt - JPEG encoder quality data @@ -80,7 +85,7 @@ struct mtk_jpeg_enc_qlt { }; void mtk_jpeg_enc_reset(void __iomem *base); -u32 mtk_jpeg_enc_get_file_size(void __iomem *base); +u32 mtk_jpeg_enc_get_file_size(void __iomem *base, bool support_34bit); void mtk_jpeg_enc_start(void __iomem *enc_reg_base); void mtk_jpeg_set_enc_src(struct mtk_jpeg_ctx *ctx, void __iomem *base, struct vb2_buffer *src_buf); -- cgit v1.2.3-59-g8ed1b From 45029d3ee28c6bb85173f21adace914979fa33a6 Mon Sep 17 00:00:00 2001 From: Sebastian Fricke Date: Thu, 1 May 2025 15:55:47 -0400 Subject: media: rkvdec: h264: Limit minimum profile to constrained baseline Neither the hardware nor the kernel API support FMO/ASO features required by the full baseline profile. Therefore limit the minimum profile to the constrained baseline profile explicitly. Suggested-by: Nicolas Dufresne Signed-off-by: Sebastian Fricke Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/rkvdec/rkvdec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index dd7e57a90264..65c6f1d07a49 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -150,7 +150,7 @@ static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { }, { .cfg.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, - .cfg.min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .cfg.min = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE, .cfg.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, .cfg.menu_skip_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), -- cgit v1.2.3-59-g8ed1b From d43d7db3c8a1868dcbc6cb8de90a3cdf309d6cbb Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Thu, 1 May 2025 15:55:48 -0400 Subject: media: rkvdec: Initialize the m2m context before the controls Setting up the control handler calls into .s_ctrl ops. While validating the controls the ops may need to access some of the context state, which could lead to a crash if not properly initialized. Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/rkvdec/rkvdec.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index 65c6f1d07a49..7b780392bb6a 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -871,24 +871,24 @@ static int rkvdec_open(struct file *filp) rkvdec_reset_decoded_fmt(ctx); v4l2_fh_init(&ctx->fh, video_devdata(filp)); - ret = rkvdec_init_ctrls(ctx); - if (ret) - goto err_free_ctx; - ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rkvdec->m2m_dev, ctx, rkvdec_queue_init); if (IS_ERR(ctx->fh.m2m_ctx)) { ret = PTR_ERR(ctx->fh.m2m_ctx); - goto err_cleanup_ctrls; + goto err_free_ctx; } + ret = rkvdec_init_ctrls(ctx); + if (ret) + goto err_cleanup_m2m_ctx; + filp->private_data = &ctx->fh; v4l2_fh_add(&ctx->fh); return 0; -err_cleanup_ctrls: - v4l2_ctrl_handler_free(&ctx->ctrl_hdl); +err_cleanup_m2m_ctx: + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); err_free_ctx: kfree(ctx); -- cgit v1.2.3-59-g8ed1b From d35c64eccf3b15a38a168a8c36096b960e8fbc1d Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Thu, 1 May 2025 15:55:49 -0400 Subject: media: rkvdec: Add get_image_fmt ops Add support for a get_image_fmt() ops that returns the required image format. The CAPTURE format is reset when the required image format changes and the buffer queue is not busy. Signed-off-by: Jonas Karlman Tested-by: Nicolas Dufresne Reviewed-by: Jonas Karlman Co-developed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/rkvdec/rkvdec.c | 35 +++++++++++++++++++++++++++++++++++ drivers/staging/media/rkvdec/rkvdec.h | 2 ++ 2 files changed, 37 insertions(+) diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index 7b780392bb6a..f7eb67520ab0 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -34,6 +34,15 @@ static bool rkvdec_image_fmt_match(enum rkvdec_image_fmt fmt1, fmt1 == RKVDEC_IMG_FMT_ANY; } +static bool rkvdec_image_fmt_changed(struct rkvdec_ctx *ctx, + enum rkvdec_image_fmt image_fmt) +{ + if (image_fmt == RKVDEC_IMG_FMT_ANY) + return false; + + return ctx->image_fmt != image_fmt; +} + static u32 rkvdec_enum_decoded_fmt(struct rkvdec_ctx *ctx, int index, enum rkvdec_image_fmt image_fmt) { @@ -118,8 +127,34 @@ static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) return 0; } +static int rkvdec_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl); + const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; + enum rkvdec_image_fmt image_fmt; + struct vb2_queue *vq; + + /* Check if this change requires a capture format reset */ + if (!desc->ops->get_image_fmt) + return 0; + + image_fmt = desc->ops->get_image_fmt(ctx, ctrl); + if (rkvdec_image_fmt_changed(ctx, image_fmt)) { + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (vb2_is_busy(vq)) + return -EBUSY; + + ctx->image_fmt = image_fmt; + rkvdec_reset_decoded_fmt(ctx); + } + + return 0; +} + static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = { .try_ctrl = rkvdec_try_ctrl, + .s_ctrl = rkvdec_s_ctrl, }; static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { diff --git a/drivers/staging/media/rkvdec/rkvdec.h b/drivers/staging/media/rkvdec/rkvdec.h index 6f8cf50c5d99..e466a2753ccf 100644 --- a/drivers/staging/media/rkvdec/rkvdec.h +++ b/drivers/staging/media/rkvdec/rkvdec.h @@ -73,6 +73,8 @@ struct rkvdec_coded_fmt_ops { struct vb2_v4l2_buffer *dst_buf, enum vb2_buffer_state result); int (*try_ctrl)(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl); + enum rkvdec_image_fmt (*get_image_fmt)(struct rkvdec_ctx *ctx, + struct v4l2_ctrl *ctrl); }; enum rkvdec_image_fmt { -- cgit v1.2.3-59-g8ed1b From 5e1ff2314797bf53636468a97719a8222deca9ae Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Thu, 1 May 2025 15:55:50 -0400 Subject: media: rkvdec: h264: Support High 10 and 4:2:2 profiles Add support and enable decoding of H264 High 10 and 4:2:2 profiles. Decoded CAPTURE buffer width is aligned to 64 pixels to accommodate HW requirement of 10-bit format buffers, fixes decoding of all the 4:2:2 and 10bit 4:2:2 flusters tests except two stream that present some visual artifacts. - Hi422FREXT17_SONY_A - Hi422FREXT19_SONY_A The get_image_fmt() ops is implemented to select an image format required for the provided SPS control, and returns RKVDEC_IMG_FMT_ANY for other controls. Signed-off-by: Jonas Karlman Reviewed-by: Nicolas Dufresne Tested-by: Nicolas Dufresne Tested-by: Christopher Obbard Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/rkvdec/rkvdec-h264.c | 37 ++++++++++++++++++++++------- drivers/staging/media/rkvdec/rkvdec.c | 38 +++++++++++++++++++++++------- drivers/staging/media/rkvdec/rkvdec.h | 3 +++ 3 files changed, 60 insertions(+), 18 deletions(-) diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c index 8bce8902b8dd..d14b4d173448 100644 --- a/drivers/staging/media/rkvdec/rkvdec-h264.c +++ b/drivers/staging/media/rkvdec/rkvdec-h264.c @@ -1027,24 +1027,42 @@ static int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx, return 0; } +static enum rkvdec_image_fmt rkvdec_h264_get_image_fmt(struct rkvdec_ctx *ctx, + struct v4l2_ctrl *ctrl) +{ + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; + + if (ctrl->id != V4L2_CID_STATELESS_H264_SPS) + return RKVDEC_IMG_FMT_ANY; + + if (sps->bit_depth_luma_minus8 == 0) { + if (sps->chroma_format_idc == 2) + return RKVDEC_IMG_FMT_422_8BIT; + else + return RKVDEC_IMG_FMT_420_8BIT; + } else if (sps->bit_depth_luma_minus8 == 2) { + if (sps->chroma_format_idc == 2) + return RKVDEC_IMG_FMT_422_10BIT; + else + return RKVDEC_IMG_FMT_420_10BIT; + } + + return RKVDEC_IMG_FMT_ANY; +} + static int rkvdec_h264_validate_sps(struct rkvdec_ctx *ctx, const struct v4l2_ctrl_h264_sps *sps) { unsigned int width, height; - /* - * TODO: The hardware supports 10-bit and 4:2:2 profiles, - * but it's currently broken in the driver. - * Reject them for now, until it's fixed. - */ - if (sps->chroma_format_idc > 1) - /* Only 4:0:0 and 4:2:0 are supported */ + if (sps->chroma_format_idc > 2) + /* Only 4:0:0, 4:2:0 and 4:2:2 are supported */ return -EINVAL; if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) /* Luma and chroma bit depth mismatch */ return -EINVAL; - if (sps->bit_depth_luma_minus8 != 0) - /* Only 8-bit is supported */ + if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2) + /* Only 8-bit and 10-bit is supported */ return -EINVAL; width = (sps->pic_width_in_mbs_minus1 + 1) * 16; @@ -1190,4 +1208,5 @@ const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops = { .stop = rkvdec_h264_stop, .run = rkvdec_h264_run, .try_ctrl = rkvdec_h264_try_ctrl, + .get_image_fmt = rkvdec_h264_get_image_fmt, }; diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index f7eb67520ab0..3367902f22de 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -186,9 +186,10 @@ static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { { .cfg.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, .cfg.min = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE, - .cfg.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + .cfg.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA, .cfg.menu_skip_mask = - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE), .cfg.def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, }, { @@ -203,11 +204,23 @@ static const struct rkvdec_ctrls rkvdec_h264_ctrls = { .num_ctrls = ARRAY_SIZE(rkvdec_h264_ctrl_descs), }; -static const struct rkvdec_decoded_fmt_desc rkvdec_h264_vp9_decoded_fmts[] = { +static const struct rkvdec_decoded_fmt_desc rkvdec_h264_decoded_fmts[] = { { .fourcc = V4L2_PIX_FMT_NV12, .image_fmt = RKVDEC_IMG_FMT_420_8BIT, }, + { + .fourcc = V4L2_PIX_FMT_NV15, + .image_fmt = RKVDEC_IMG_FMT_420_10BIT, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .image_fmt = RKVDEC_IMG_FMT_422_8BIT, + }, + { + .fourcc = V4L2_PIX_FMT_NV20, + .image_fmt = RKVDEC_IMG_FMT_422_10BIT, + }, }; static const struct rkvdec_ctrl_desc rkvdec_vp9_ctrl_descs[] = { @@ -230,21 +243,28 @@ static const struct rkvdec_ctrls rkvdec_vp9_ctrls = { .num_ctrls = ARRAY_SIZE(rkvdec_vp9_ctrl_descs), }; +static const struct rkvdec_decoded_fmt_desc rkvdec_vp9_decoded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .image_fmt = RKVDEC_IMG_FMT_420_8BIT, + }, +}; + static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = { { .fourcc = V4L2_PIX_FMT_H264_SLICE, .frmsize = { - .min_width = 48, + .min_width = 64, .max_width = 4096, - .step_width = 16, + .step_width = 64, .min_height = 48, .max_height = 2560, .step_height = 16, }, .ctrls = &rkvdec_h264_ctrls, .ops = &rkvdec_h264_fmt_ops, - .num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_vp9_decoded_fmts), - .decoded_fmts = rkvdec_h264_vp9_decoded_fmts, + .num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_decoded_fmts), + .decoded_fmts = rkvdec_h264_decoded_fmts, .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF, }, { @@ -259,8 +279,8 @@ static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = { }, .ctrls = &rkvdec_vp9_ctrls, .ops = &rkvdec_vp9_fmt_ops, - .num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_vp9_decoded_fmts), - .decoded_fmts = rkvdec_h264_vp9_decoded_fmts, + .num_decoded_fmts = ARRAY_SIZE(rkvdec_vp9_decoded_fmts), + .decoded_fmts = rkvdec_vp9_decoded_fmts, } }; diff --git a/drivers/staging/media/rkvdec/rkvdec.h b/drivers/staging/media/rkvdec/rkvdec.h index e466a2753ccf..9a9f4fced7a1 100644 --- a/drivers/staging/media/rkvdec/rkvdec.h +++ b/drivers/staging/media/rkvdec/rkvdec.h @@ -80,6 +80,9 @@ struct rkvdec_coded_fmt_ops { enum rkvdec_image_fmt { RKVDEC_IMG_FMT_ANY = 0, RKVDEC_IMG_FMT_420_8BIT, + RKVDEC_IMG_FMT_420_10BIT, + RKVDEC_IMG_FMT_422_8BIT, + RKVDEC_IMG_FMT_422_10BIT, }; struct rkvdec_decoded_fmt_desc { -- cgit v1.2.3-59-g8ed1b