127 lines
4.2 KiB
Diff
127 lines
4.2 KiB
Diff
From 973d774838b036ceef91e09a7994558c793b4d8a Mon Sep 17 00:00:00 2001
|
|
From: Naushir Patuck <naush@raspberrypi.com>
|
|
Date: Wed, 10 Feb 2021 10:50:32 +0000
|
|
Subject: [PATCH] media: i2c: imx477: Add very long exposure control to
|
|
the driver
|
|
|
|
Add support for very long exposures by using the exposure multiplier
|
|
register. Userland does not need to pass any additional controls to
|
|
enable long exposures, it simply requests a larger vblank to extend the
|
|
exposure control range appropriately.
|
|
|
|
Currently, since hblank is fixed, a maximum of approximately 124 seconds
|
|
of exposure time can be used. In a future change, hblank could also be
|
|
controlled in userland to give over 200 seconds of exposure time.
|
|
|
|
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
|
|
---
|
|
drivers/media/i2c/imx477.c | 47 +++++++++++++++++++++++++++++++++-----
|
|
1 file changed, 41 insertions(+), 6 deletions(-)
|
|
|
|
--- a/drivers/media/i2c/imx477.c
|
|
+++ b/drivers/media/i2c/imx477.c
|
|
@@ -44,6 +44,10 @@
|
|
#define IMX477_REG_FRAME_LENGTH 0x0340
|
|
#define IMX477_FRAME_LENGTH_MAX 0xffdc
|
|
|
|
+/* Long exposure multiplier */
|
|
+#define IMX477_LONG_EXP_SHIFT_MAX 7
|
|
+#define IMX477_LONG_EXP_SHIFT_REG 0x3100
|
|
+
|
|
/* Exposure control */
|
|
#define IMX477_REG_EXPOSURE 0x0202
|
|
#define IMX477_EXPOSURE_OFFSET 22
|
|
@@ -1097,6 +1101,9 @@ struct imx477 {
|
|
|
|
/* Rewrite common registers on stream on? */
|
|
bool common_regs_written;
|
|
+
|
|
+ /* Current long exposure factor in use. Set through V4L2_CID_VBLANK */
|
|
+ unsigned int long_exp_shift;
|
|
};
|
|
|
|
static inline struct imx477 *to_imx477(struct v4l2_subdev *_sd)
|
|
@@ -1285,13 +1292,33 @@ static void imx477_adjust_exposure_range
|
|
|
|
/* Honour the VBLANK limits when setting exposure. */
|
|
exposure_max = imx477->mode->height + imx477->vblank->val -
|
|
- IMX477_EXPOSURE_OFFSET;
|
|
+ (IMX477_EXPOSURE_OFFSET << imx477->long_exp_shift);
|
|
exposure_def = min(exposure_max, imx477->exposure->val);
|
|
__v4l2_ctrl_modify_range(imx477->exposure, imx477->exposure->minimum,
|
|
exposure_max, imx477->exposure->step,
|
|
exposure_def);
|
|
}
|
|
|
|
+static int imx477_set_frame_length(struct imx477 *imx477, unsigned int val)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ imx477->long_exp_shift = 0;
|
|
+
|
|
+ while (val > IMX477_FRAME_LENGTH_MAX) {
|
|
+ imx477->long_exp_shift++;
|
|
+ val >>= 1;
|
|
+ }
|
|
+
|
|
+ ret = imx477_write_reg(imx477, IMX477_REG_FRAME_LENGTH,
|
|
+ IMX477_REG_VALUE_16BIT, val);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return imx477_write_reg(imx477, IMX477_LONG_EXP_SHIFT_REG,
|
|
+ IMX477_REG_VALUE_08BIT, imx477->long_exp_shift);
|
|
+}
|
|
+
|
|
static int imx477_set_ctrl(struct v4l2_ctrl *ctrl)
|
|
{
|
|
struct imx477 *imx477 =
|
|
@@ -1299,6 +1326,10 @@ static int imx477_set_ctrl(struct v4l2_c
|
|
struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
|
|
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)
|
|
imx477_adjust_exposure_range(imx477, ctrl);
|
|
|
|
@@ -1316,7 +1347,8 @@ static int imx477_set_ctrl(struct v4l2_c
|
|
break;
|
|
case V4L2_CID_EXPOSURE:
|
|
ret = imx477_write_reg(imx477, IMX477_REG_EXPOSURE,
|
|
- IMX477_REG_VALUE_16BIT, ctrl->val);
|
|
+ IMX477_REG_VALUE_16BIT, ctrl->val >>
|
|
+ imx477->long_exp_shift);
|
|
break;
|
|
case V4L2_CID_DIGITAL_GAIN:
|
|
ret = imx477_write_reg(imx477, IMX477_REG_DIGITAL_GAIN,
|
|
@@ -1350,9 +1382,8 @@ static int imx477_set_ctrl(struct v4l2_c
|
|
imx477->vflip->val << 1);
|
|
break;
|
|
case V4L2_CID_VBLANK:
|
|
- ret = imx477_write_reg(imx477, IMX477_REG_FRAME_LENGTH,
|
|
- IMX477_REG_VALUE_16BIT,
|
|
- imx477->mode->height + ctrl->val);
|
|
+ ret = imx477_set_frame_length(imx477,
|
|
+ imx477->mode->height + ctrl->val);
|
|
break;
|
|
default:
|
|
dev_info(&client->dev,
|
|
@@ -1521,9 +1552,13 @@ static void imx477_set_framing_limits(st
|
|
frm_length_default =
|
|
imx477_get_frame_length(mode, &mode->timeperframe_default);
|
|
|
|
+ /* Default to no long exposure multiplier. */
|
|
+ imx477->long_exp_shift = 0;
|
|
+
|
|
/* Update limits and set FPS to default */
|
|
__v4l2_ctrl_modify_range(imx477->vblank, frm_length_min - mode->height,
|
|
- IMX477_FRAME_LENGTH_MAX - mode->height,
|
|
+ ((1 << IMX477_LONG_EXP_SHIFT_MAX) *
|
|
+ IMX477_FRAME_LENGTH_MAX) - mode->height,
|
|
1, frm_length_default - mode->height);
|
|
|
|
/* Setting this will adjust the exposure limits as well. */
|