aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.10/950-0485-media-i2c-imx477-Add-very-long-exposure-control-to-t.patch
blob: 1ad1f5ddbdc1f86c2d28ebd25bc5661a8b3acceb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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. */