media: i2c: imx258: Add support for long exposure modes
authorDave Stevenson <dave.stevenson@raspberrypi.com>
Thu, 31 Mar 2022 15:45:36 +0000 (16:45 +0100)
committerDom Cobley <popcornmix@gmail.com>
Mon, 19 Feb 2024 11:33:33 +0000 (11:33 +0000)
The sensor has a register CIT_LSHIFT which extends the exposure
and frame times by the specified power of 2 for longer
exposure times.

Add support for this by configuring this register via V4L2_CID_VBLANK
and extending the V4L2_CID_EXPOSURE range accordingly.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
drivers/media/i2c/imx258.c

index ded4e10..416f1b7 100644 (file)
 #define IMX258_HDR_RATIO_STEP          1
 #define IMX258_HDR_RATIO_DEFAULT       0x0
 
+/* Long exposure multiplier */
+#define IMX258_LONG_EXP_SHIFT_MAX      7
+#define IMX258_LONG_EXP_SHIFT_REG      0x3002
+
 /* Test Pattern Control */
 #define IMX258_REG_TEST_PATTERN                0x0600
 
@@ -824,6 +828,8 @@ struct imx258 {
        struct v4l2_ctrl *vblank;
        struct v4l2_ctrl *hblank;
        struct v4l2_ctrl *exposure;
+       /* Current long exposure factor in use. Set through V4L2_CID_VBLANK */
+       unsigned int long_exp_shift;
 
        /* Current mode */
        const struct imx258_mode *cur_mode;
@@ -988,6 +994,26 @@ static void imx258_adjust_exposure_range(struct imx258 *imx258)
                                 exposure_def);
 }
 
+static int imx258_set_frame_length(struct imx258 *imx258, unsigned int val)
+{
+       int ret;
+
+       imx258->long_exp_shift = 0;
+
+       while (val > IMX258_VTS_MAX) {
+               imx258->long_exp_shift++;
+               val >>= 1;
+       }
+
+       ret = imx258_write_reg(imx258, IMX258_REG_VTS,
+                              IMX258_REG_VALUE_16BIT, val);
+       if (ret)
+               return ret;
+
+       return imx258_write_reg(imx258, IMX258_LONG_EXP_SHIFT_REG,
+                               IMX258_REG_VALUE_08BIT, imx258->long_exp_shift);
+}
+
 static int imx258_set_ctrl(struct v4l2_ctrl *ctrl)
 {
        struct imx258 *imx258 =
@@ -1018,7 +1044,7 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl)
        case V4L2_CID_EXPOSURE:
                ret = imx258_write_reg(imx258, IMX258_REG_EXPOSURE,
                                IMX258_REG_VALUE_16BIT,
-                               ctrl->val);
+                               ctrl->val >> imx258->long_exp_shift);
                break;
        case V4L2_CID_DIGITAL_GAIN:
                ret = imx258_update_digital_gain(imx258, IMX258_REG_VALUE_16BIT,
@@ -1050,9 +1076,8 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl)
                }
                break;
        case V4L2_CID_VBLANK:
-               ret = imx258_write_reg(imx258, IMX258_REG_VTS,
-                                      IMX258_REG_VALUE_16BIT,
-                                      imx258->cur_mode->height + ctrl->val);
+               ret = imx258_set_frame_length(imx258,
+                                             imx258->cur_mode->height + ctrl->val);
                break;
        default:
                dev_info(&client->dev,
@@ -1178,8 +1203,9 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd,
                             imx258->cur_mode->height;
                __v4l2_ctrl_modify_range(
                        imx258->vblank, vblank_min,
-                       IMX258_VTS_MAX - imx258->cur_mode->height, 1,
-                       vblank_def);
+                       ((1 << IMX258_LONG_EXP_SHIFT_MAX) * IMX258_VTS_MAX) -
+                                               imx258->cur_mode->height,
+                       1, vblank_def);
                __v4l2_ctrl_s_ctrl(imx258->vblank, vblank_def);
                h_blank =
                        imx258->link_freq_configs[mode->link_freq_index].pixels_per_line