]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
media: i2c: imx219: make HBLANK r/w to allow longer exposures
authorDave Stevenson <dave.stevenson@raspberrypi.com>
Tue, 4 Feb 2025 07:04:38 +0000 (12:34 +0530)
committerHans Verkuil <hverkuil@xs4all.nl>
Sat, 15 Feb 2025 14:23:00 +0000 (15:23 +0100)
The HBLANK control was read-only, and always configured such that the
sensor line length register was 3448. This limited the maximum exposure
time that could be achieved to around 1.26 secs.

Make HBLANK read/write so that the line time can be extended, and
thereby allow longer exposures (and slower frame rates). Retain the
overall line length setting when changing modes rather than resetting it
to a default.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
drivers/media/i2c/imx219.c

index 542f59892b20c577208f9d459a27e44721578b1f..72f4f7716ca1ad400a452abfedcd1dd9f4f2b8a6 100644 (file)
 #define IMX219_REG_FRM_LENGTH_A                CCI_REG16(0x0160)
 #define IMX219_FLL_MAX                 0xffff
 #define IMX219_VBLANK_MIN              32
-
-/* HBLANK control - read only */
-#define IMX219_PPL_DEFAULT             3448
-
 #define IMX219_REG_LINE_LENGTH_A       CCI_REG16(0x0162)
+#define IMX219_LLP_MIN                 0x0d78
+#define IMX219_LLP_MAX                 0x7ff0
+
 #define IMX219_REG_X_ADD_STA_A         CCI_REG16(0x0164)
 #define IMX219_REG_X_ADD_END_A         CCI_REG16(0x0166)
 #define IMX219_REG_Y_ADD_STA_A         CCI_REG16(0x0168)
@@ -183,7 +182,6 @@ static const struct cci_reg_sequence imx219_common_regs[] = {
        { CCI_REG8(0x479b), 0x0e },
 
        /* Frame Bank Register Group "A" */
-       { IMX219_REG_LINE_LENGTH_A, 3448 },
        { IMX219_REG_X_ODD_INC_A, 1 },
        { IMX219_REG_Y_ODD_INC_A, 1 },
 
@@ -445,6 +443,10 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl)
                cci_write(imx219->regmap, IMX219_REG_FRM_LENGTH_A,
                          format->height + ctrl->val, &ret);
                break;
+       case V4L2_CID_HBLANK:
+               cci_write(imx219->regmap, IMX219_REG_LINE_LENGTH_A,
+                         format->width + ctrl->val, &ret);
+               break;
        case V4L2_CID_TEST_PATTERN_RED:
                cci_write(imx219->regmap, IMX219_REG_TESTP_RED,
                          ctrl->val, &ret);
@@ -490,7 +492,7 @@ static int imx219_init_controls(struct imx219 *imx219)
        const struct imx219_mode *mode = &supported_modes[0];
        struct v4l2_ctrl_handler *ctrl_hdlr;
        struct v4l2_fwnode_device_properties props;
-       int exposure_max, exposure_def, hblank;
+       int exposure_max, exposure_def;
        int i, ret;
 
        ctrl_hdlr = &imx219->ctrl_handler;
@@ -514,17 +516,16 @@ static int imx219_init_controls(struct imx219 *imx219)
        if (imx219->link_freq)
                imx219->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
-       /* Initial vblank/hblank/exposure parameters based on current mode */
+       /* Initial blanking and exposure. Limits are updated during set_fmt */
        imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
                                           V4L2_CID_VBLANK, IMX219_VBLANK_MIN,
                                           IMX219_FLL_MAX - mode->height, 1,
                                           mode->fll_def - mode->height);
-       hblank = IMX219_PPL_DEFAULT - mode->width;
        imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-                                          V4L2_CID_HBLANK, hblank, hblank,
-                                          1, hblank);
-       if (imx219->hblank)
-               imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+                                          V4L2_CID_HBLANK,
+                                          IMX219_LLP_MIN - mode->width,
+                                          IMX219_LLP_MAX - mode->width, 1,
+                                          IMX219_LLP_MIN - mode->width);
        exposure_max = mode->fll_def - 4;
        exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
                exposure_max : IMX219_EXPOSURE_DEFAULT;
@@ -842,6 +843,10 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
        struct v4l2_mbus_framefmt *format;
        struct v4l2_rect *crop;
        unsigned int bin_h, bin_v;
+       u32 prev_line_len;
+
+       format = v4l2_subdev_state_get_format(state, 0);
+       prev_line_len = format->width + imx219->hblank->val;
 
        mode = v4l2_find_nearest_size(supported_modes,
                                      ARRAY_SIZE(supported_modes),
@@ -849,8 +854,6 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
                                      fmt->format.width, fmt->format.height);
 
        imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code);
-
-       format = v4l2_subdev_state_get_format(state, 0);
        *format = fmt->format;
 
        /*
@@ -886,13 +889,18 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
                                         exposure_max, imx219->exposure->step,
                                         exposure_def);
                /*
-                * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank
-                * depends on mode->width only, and is not changeble in any
-                * way other than changing the mode.
+                * Retain PPL setting from previous mode so that the
+                * line time does not change on a mode change.
+                * Limits have to be recomputed as the controls define
+                * the blanking only, so PPL values need to have the
+                * mode width subtracted.
                 */
-               hblank = IMX219_PPL_DEFAULT - mode->width;
-               __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1,
-                                        hblank);
+               hblank = prev_line_len - mode->width;
+               __v4l2_ctrl_modify_range(imx219->hblank,
+                                        IMX219_LLP_MIN - mode->width,
+                                        IMX219_LLP_MAX - mode->width, 1,
+                                        IMX219_LLP_MIN - mode->width);
+               __v4l2_ctrl_s_ctrl(imx219->hblank, hblank);
        }
 
        return 0;