bcm27xx: switch to kernel v6.1
[openwrt/openwrt.git] / target / linux / bcm27xx / patches-5.15 / 950-0345-media-i2c-imx477-Add-very-long-exposure-control-to-t.patch
1 From 0633c60ff1ed5897d8996c5683ea07a5d345e660 Mon Sep 17 00:00:00 2001
2 From: Naushir Patuck <naush@raspberrypi.com>
3 Date: Wed, 10 Feb 2021 10:50:32 +0000
4 Subject: [PATCH] media: i2c: imx477: Add very long exposure control to
5 the driver
6
7 Add support for very long exposures by using the exposure multiplier
8 register. Userland does not need to pass any additional controls to
9 enable long exposures, it simply requests a larger vblank to extend the
10 exposure control range appropriately.
11
12 Currently, since hblank is fixed, a maximum of approximately 124 seconds
13 of exposure time can be used. In a future change, hblank could also be
14 controlled in userland to give over 200 seconds of exposure time.
15
16 Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
17 ---
18 drivers/media/i2c/imx477.c | 47 +++++++++++++++++++++++++++++++++-----
19 1 file changed, 41 insertions(+), 6 deletions(-)
20
21 --- a/drivers/media/i2c/imx477.c
22 +++ b/drivers/media/i2c/imx477.c
23 @@ -44,6 +44,10 @@
24 #define IMX477_REG_FRAME_LENGTH 0x0340
25 #define IMX477_FRAME_LENGTH_MAX 0xffdc
26
27 +/* Long exposure multiplier */
28 +#define IMX477_LONG_EXP_SHIFT_MAX 7
29 +#define IMX477_LONG_EXP_SHIFT_REG 0x3100
30 +
31 /* Exposure control */
32 #define IMX477_REG_EXPOSURE 0x0202
33 #define IMX477_EXPOSURE_OFFSET 22
34 @@ -1097,6 +1101,9 @@ struct imx477 {
35
36 /* Rewrite common registers on stream on? */
37 bool common_regs_written;
38 +
39 + /* Current long exposure factor in use. Set through V4L2_CID_VBLANK */
40 + unsigned int long_exp_shift;
41 };
42
43 static inline struct imx477 *to_imx477(struct v4l2_subdev *_sd)
44 @@ -1285,13 +1292,33 @@ static void imx477_adjust_exposure_range
45
46 /* Honour the VBLANK limits when setting exposure. */
47 exposure_max = imx477->mode->height + imx477->vblank->val -
48 - IMX477_EXPOSURE_OFFSET;
49 + (IMX477_EXPOSURE_OFFSET << imx477->long_exp_shift);
50 exposure_def = min(exposure_max, imx477->exposure->val);
51 __v4l2_ctrl_modify_range(imx477->exposure, imx477->exposure->minimum,
52 exposure_max, imx477->exposure->step,
53 exposure_def);
54 }
55
56 +static int imx477_set_frame_length(struct imx477 *imx477, unsigned int val)
57 +{
58 + int ret = 0;
59 +
60 + imx477->long_exp_shift = 0;
61 +
62 + while (val > IMX477_FRAME_LENGTH_MAX) {
63 + imx477->long_exp_shift++;
64 + val >>= 1;
65 + }
66 +
67 + ret = imx477_write_reg(imx477, IMX477_REG_FRAME_LENGTH,
68 + IMX477_REG_VALUE_16BIT, val);
69 + if (ret)
70 + return ret;
71 +
72 + return imx477_write_reg(imx477, IMX477_LONG_EXP_SHIFT_REG,
73 + IMX477_REG_VALUE_08BIT, imx477->long_exp_shift);
74 +}
75 +
76 static int imx477_set_ctrl(struct v4l2_ctrl *ctrl)
77 {
78 struct imx477 *imx477 =
79 @@ -1299,6 +1326,10 @@ static int imx477_set_ctrl(struct v4l2_c
80 struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
81 int ret = 0;
82
83 + /*
84 + * The VBLANK control may change the limits of usable exposure, so check
85 + * and adjust if necessary.
86 + */
87 if (ctrl->id == V4L2_CID_VBLANK)
88 imx477_adjust_exposure_range(imx477, ctrl);
89
90 @@ -1316,7 +1347,8 @@ static int imx477_set_ctrl(struct v4l2_c
91 break;
92 case V4L2_CID_EXPOSURE:
93 ret = imx477_write_reg(imx477, IMX477_REG_EXPOSURE,
94 - IMX477_REG_VALUE_16BIT, ctrl->val);
95 + IMX477_REG_VALUE_16BIT, ctrl->val >>
96 + imx477->long_exp_shift);
97 break;
98 case V4L2_CID_DIGITAL_GAIN:
99 ret = imx477_write_reg(imx477, IMX477_REG_DIGITAL_GAIN,
100 @@ -1350,9 +1382,8 @@ static int imx477_set_ctrl(struct v4l2_c
101 imx477->vflip->val << 1);
102 break;
103 case V4L2_CID_VBLANK:
104 - ret = imx477_write_reg(imx477, IMX477_REG_FRAME_LENGTH,
105 - IMX477_REG_VALUE_16BIT,
106 - imx477->mode->height + ctrl->val);
107 + ret = imx477_set_frame_length(imx477,
108 + imx477->mode->height + ctrl->val);
109 break;
110 default:
111 dev_info(&client->dev,
112 @@ -1521,9 +1552,13 @@ static void imx477_set_framing_limits(st
113 frm_length_default =
114 imx477_get_frame_length(mode, &mode->timeperframe_default);
115
116 + /* Default to no long exposure multiplier. */
117 + imx477->long_exp_shift = 0;
118 +
119 /* Update limits and set FPS to default */
120 __v4l2_ctrl_modify_range(imx477->vblank, frm_length_min - mode->height,
121 - IMX477_FRAME_LENGTH_MAX - mode->height,
122 + ((1 << IMX477_LONG_EXP_SHIFT_MAX) *
123 + IMX477_FRAME_LENGTH_MAX) - mode->height,
124 1, frm_length_default - mode->height);
125
126 /* Setting this will adjust the exposure limits as well. */