71a625f2603a2af14654c26a8610b57ea6c289a1
[openwrt/staging/blocktrron.git] /
1 From 421e9f60f24504de605c7bc4ba98e51b73b2aa6d Mon Sep 17 00:00:00 2001
2 From: Dave Stevenson <dave.stevenson@raspberrypi.com>
3 Date: Thu, 16 Feb 2023 00:29:58 +0200
4 Subject: [PATCH] media: i2c: imx290: Convert V4L2_CID_VBLANK to
5 read/write
6
7 Should be upstream commit 97792a11021b
8
9 The driver exposed V4L2_CID_VBLANK as a read only control to allow
10 for exposure calculations and determination of the frame rate.
11
12 Convert to a read/write control so that the frame rate can be
13 controlled.
14 V4L2_CID_VBLANK also sets the limits for the exposure control,
15 therefore exposure ranges have to be updated when vblank changes
16 (either via s_ctrl, or via changing mode).
17
18 Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
19 Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
20 Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
21 Acked-by: Alexander Stein <alexander.stein@ew.tq-group.com>
22 Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
23 ---
24 drivers/media/i2c/imx290.c | 58 +++++++++++++++++++++++++++++++-------
25 1 file changed, 48 insertions(+), 10 deletions(-)
26
27 --- a/drivers/media/i2c/imx290.c
28 +++ b/drivers/media/i2c/imx290.c
29 @@ -49,6 +49,7 @@
30 #define IMX290_BLKLEVEL IMX290_REG_16BIT(0x300a)
31 #define IMX290_GAIN IMX290_REG_8BIT(0x3014)
32 #define IMX290_VMAX IMX290_REG_24BIT(0x3018)
33 +#define IMX290_VMAX_MAX 0x3ffff
34 #define IMX290_HMAX IMX290_REG_16BIT(0x301c)
35 #define IMX290_HMAX_MAX 0xffff
36 #define IMX290_SHS1 IMX290_REG_24BIT(0x3020)
37 @@ -109,6 +110,9 @@
38 #define IMX290_PGCTRL_THRU BIT(1)
39 #define IMX290_PGCTRL_MODE(n) ((n) << 4)
40
41 +/* Number of lines by which exposure must be less than VMAX */
42 +#define IMX290_EXPOSURE_OFFSET 2
43 +
44 #define IMX290_VMAX_DEFAULT 1125
45
46 #define IMX290_PIXEL_RATE 148500000
47 @@ -225,6 +229,7 @@ struct imx290 {
48 struct v4l2_ctrl *link_freq;
49 struct v4l2_ctrl *hblank;
50 struct v4l2_ctrl *vblank;
51 + struct v4l2_ctrl *exposure;
52 };
53
54 static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd)
55 @@ -238,7 +243,6 @@ static inline struct imx290 *to_imx290(s
56
57 static const struct imx290_regval imx290_global_init_settings[] = {
58 { IMX290_CTRL_07, IMX290_WINMODE_1080P },
59 - { IMX290_VMAX, IMX290_VMAX_DEFAULT },
60 { IMX290_EXTCK_FREQ, 0x2520 },
61 { IMX290_WINWV_OB, 12 },
62 { IMX290_WINPH, 0 },
63 @@ -664,6 +668,16 @@ static int imx290_setup_format(struct im
64 /* ----------------------------------------------------------------------------
65 * Controls
66 */
67 +static void imx290_exposure_update(struct imx290 *imx290,
68 + const struct imx290_mode *mode)
69 +{
70 + unsigned int exposure_max;
71 +
72 + exposure_max = imx290->vblank->val + mode->height -
73 + IMX290_EXPOSURE_OFFSET;
74 + __v4l2_ctrl_modify_range(imx290->exposure, 1, exposure_max, 1,
75 + exposure_max);
76 +}
77
78 static int imx290_set_ctrl(struct v4l2_ctrl *ctrl)
79 {
80 @@ -671,7 +685,7 @@ static int imx290_set_ctrl(struct v4l2_c
81 struct imx290, ctrls);
82 const struct v4l2_mbus_framefmt *format;
83 struct v4l2_subdev_state *state;
84 - int ret = 0;
85 + int ret = 0, vmax;
86
87 /*
88 * Return immediately for controls that don't need to be applied to the
89 @@ -680,6 +694,11 @@ static int imx290_set_ctrl(struct v4l2_c
90 if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
91 return 0;
92
93 + if (ctrl->id == V4L2_CID_VBLANK) {
94 + /* Changing vblank changes the allowed range for exposure. */
95 + imx290_exposure_update(imx290, imx290->current_mode);
96 + }
97 +
98 /* V4L2 controls values will be applied only when power is already up */
99 if (!pm_runtime_get_if_in_use(imx290->dev))
100 return 0;
101 @@ -692,9 +711,23 @@ static int imx290_set_ctrl(struct v4l2_c
102 ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL);
103 break;
104
105 + case V4L2_CID_VBLANK:
106 + ret = imx290_write(imx290, IMX290_VMAX,
107 + ctrl->val + imx290->current_mode->height,
108 + NULL);
109 + /*
110 + * Due to the way that exposure is programmed in this sensor in
111 + * relation to VMAX, we have to reprogramme it whenever VMAX is
112 + * changed.
113 + * Update ctrl so that the V4L2_CID_EXPOSURE case can refer to
114 + * it.
115 + */
116 + ctrl = imx290->exposure;
117 + fallthrough;
118 case V4L2_CID_EXPOSURE:
119 + vmax = imx290->vblank->val + imx290->current_mode->height;
120 ret = imx290_write(imx290, IMX290_SHS1,
121 - IMX290_VMAX_DEFAULT - ctrl->val - 1, NULL);
122 + vmax - ctrl->val - 1, NULL);
123 break;
124
125 case V4L2_CID_TEST_PATTERN:
126 @@ -751,13 +784,15 @@ static void imx290_ctrl_update(struct im
127 {
128 unsigned int hblank_min = mode->hmax_min - mode->width;
129 unsigned int hblank_max = IMX290_HMAX_MAX - mode->width;
130 - unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height;
131 + unsigned int vblank_min = IMX290_VMAX_DEFAULT - mode->height;
132 + unsigned int vblank_max = IMX290_VMAX_MAX - mode->height;
133
134 __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index);
135
136 __v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1,
137 hblank_min);
138 - __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank);
139 + __v4l2_ctrl_modify_range(imx290->vblank, vblank_min, vblank_max, 1,
140 + vblank_min);
141 }
142
143 static int imx290_ctrl_init(struct imx290 *imx290)
144 @@ -787,9 +822,13 @@ static int imx290_ctrl_init(struct imx29
145 v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
146 V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0);
147
148 - v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
149 - V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1,
150 - IMX290_VMAX_DEFAULT - 2);
151 + /*
152 + * Correct range will be determined through imx290_ctrl_update setting
153 + * V4L2_CID_VBLANK.
154 + */
155 + imx290->exposure = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
156 + V4L2_CID_EXPOSURE, 1, 65535, 1,
157 + 65535);
158
159 /*
160 * Set the link frequency, pixel rate, horizontal blanking and vertical
161 @@ -821,8 +860,6 @@ static int imx290_ctrl_init(struct imx29
162
163 imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops,
164 V4L2_CID_VBLANK, 1, 1, 1, 1);
165 - if (imx290->vblank)
166 - imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
167
168 v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops,
169 &props);
170 @@ -1008,6 +1045,7 @@ static int imx290_set_fmt(struct v4l2_su
171 imx290->current_mode = mode;
172
173 imx290_ctrl_update(imx290, &fmt->format, mode);
174 + imx290_exposure_update(imx290, mode);
175 }
176
177 *format = fmt->format;