media: i2c: imx258: Follow normal V4L2 behaviours for clipping exposure
authorDave Stevenson <dave.stevenson@raspberrypi.com>
Wed, 16 Jun 2021 16:19:17 +0000 (17:19 +0100)
committerDom Cobley <popcornmix@gmail.com>
Mon, 19 Feb 2024 11:33:33 +0000 (11:33 +0000)
V4L2 sensor drivers are expected are expected to clip the supported
exposure range based on the VBLANK configured.
IMX258 wasn't doing that as register 0x350 (FRM_LENGTH_CTL)
switches it to a mode where frame length tracks coarse exposure time.

Disable this mode and clip the range for V4L2_CID_EXPOSURE appropriately
based on V4L2_CID_VBLANK.

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

index 6065667..a034e66 100644 (file)
 
 #define IMX258_REG_VTS                 0x0340
 
-/* HBLANK control - read only */
-#define IMX258_PPL_DEFAULT             5352
-
 /* Exposure control */
 #define IMX258_REG_EXPOSURE            0x0202
+#define IMX258_EXPOSURE_OFFSET         10
 #define IMX258_EXPOSURE_MIN            4
 #define IMX258_EXPOSURE_STEP           1
 #define IMX258_EXPOSURE_DEFAULT                0x640
-#define IMX258_EXPOSURE_MAX            65535
+#define IMX258_EXPOSURE_MAX            (IMX258_VTS_MAX - IMX258_EXPOSURE_OFFSET)
+
+/* HBLANK control - read only */
+#define IMX258_PPL_DEFAULT             5352
 
 /* Analog gain control */
 #define IMX258_REG_ANALOG_GAIN         0x0204
@@ -376,7 +377,7 @@ static const struct imx258_reg mode_4208x3120_regs[] = {
        { 0x034D, 0x70 },
        { 0x034E, 0x0C },
        { 0x034F, 0x30 },
-       { 0x0350, 0x01 },
+       { 0x0350, 0x00 },
        { 0x0204, 0x00 },
        { 0x0205, 0x00 },
        { 0x020E, 0x01 },
@@ -488,7 +489,7 @@ static const struct imx258_reg mode_2104_1560_regs[] = {
        { 0x034D, 0x38 },
        { 0x034E, 0x06 },
        { 0x034F, 0x18 },
-       { 0x0350, 0x01 },
+       { 0x0350, 0x00 },
        { 0x0204, 0x00 },
        { 0x0205, 0x00 },
        { 0x020E, 0x01 },
@@ -600,7 +601,7 @@ static const struct imx258_reg mode_1048_780_regs[] = {
        { 0x034D, 0x18 },
        { 0x034E, 0x03 },
        { 0x034F, 0x0C },
-       { 0x0350, 0x01 },
+       { 0x0350, 0x00 },
        { 0x0204, 0x00 },
        { 0x0205, 0x00 },
        { 0x020E, 0x01 },
@@ -934,6 +935,19 @@ static int imx258_update_digital_gain(struct imx258 *imx258, u32 len, u32 val)
        return 0;
 }
 
+static void imx258_adjust_exposure_range(struct imx258 *imx258)
+{
+       int exposure_max, exposure_def;
+
+       /* Honour the VBLANK limits when setting exposure. */
+       exposure_max = imx258->cur_mode->height + imx258->vblank->val -
+                      IMX258_EXPOSURE_OFFSET;
+       exposure_def = min(exposure_max, imx258->exposure->val);
+       __v4l2_ctrl_modify_range(imx258->exposure, imx258->exposure->minimum,
+                                exposure_max, imx258->exposure->step,
+                                exposure_def);
+}
+
 static int imx258_set_ctrl(struct v4l2_ctrl *ctrl)
 {
        struct imx258 *imx258 =
@@ -942,6 +956,13 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl)
        int ret = 0;
 
        /*
+        * The VBLANK control may change the limits of usable exposure, so check
+        * and adjust if necessary.
+        */
+       if (ctrl->id == V4L2_CID_VBLANK)
+               imx258_adjust_exposure_range(imx258);
+
+       /*
         * Applying V4L2 control value only happens
         * when power is up for streaming
         */