49cb5c1d7f534817c4c2c5f588100b44f9f41544
[openwrt/staging/neocturne.git] /
1 From c9d976e455b911c977ffeb000b4342cd65c79dbc Mon Sep 17 00:00:00 2001
2 From: Dave Stevenson <dave.stevenson@raspberrypi.com>
3 Date: Tue, 15 Jun 2021 18:29:52 +0100
4 Subject: [PATCH] media: i2c: imx258: Implement HFLIP and VFLIP
5 controls.
6
7 The sensor supports H & V flips, so implement the relevant controls.
8 Note that the Bayer order changes with these flips, therefore
9 they set the V4L2_CTRL_FLAG_MODIFY_LAYOUT property.
10
11 As we now support flips, remove the restriction of the sensor only
12 probing if rotated 180 degrees, but do take that value and initialise
13 VFLIP and HFLIP based on it.
14
15 Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
16 ---
17 drivers/media/i2c/imx258.c | 106 +++++++++++++++++++++++++------------
18 1 file changed, 73 insertions(+), 33 deletions(-)
19
20 --- a/drivers/media/i2c/imx258.c
21 +++ b/drivers/media/i2c/imx258.c
22 @@ -9,6 +9,7 @@
23 #include <linux/pm_runtime.h>
24 #include <media/v4l2-ctrls.h>
25 #include <media/v4l2-device.h>
26 +#include <media/v4l2-fwnode.h>
27 #include <asm/unaligned.h>
28
29 #define IMX258_REG_VALUE_08BIT 1
30 @@ -69,7 +70,8 @@
31
32 /* Orientation */
33 #define REG_MIRROR_FLIP_CONTROL 0x0101
34 -#define REG_CONFIG_MIRROR_FLIP 0x03
35 +#define REG_CONFIG_MIRROR_HFLIP 0x01
36 +#define REG_CONFIG_MIRROR_VFLIP 0x02
37 #define REG_CONFIG_FLIP_TEST_PATTERN 0x02
38
39 /* Input clock frequency in Hz */
40 @@ -504,6 +506,22 @@ static const struct imx258_reg mode_1048
41 { 0x0220, 0x00 },
42 };
43
44 +/*
45 + * The supported formats.
46 + * This table MUST contain 4 entries per format, to cover the various flip
47 + * combinations in the order
48 + * - no flip
49 + * - h flip
50 + * - v flip
51 + * - h&v flips
52 + */
53 +static const u32 codes[] = {
54 + /* 10-bit modes. */
55 + MEDIA_BUS_FMT_SRGGB10_1X10,
56 + MEDIA_BUS_FMT_SGRBG10_1X10,
57 + MEDIA_BUS_FMT_SGBRG10_1X10,
58 + MEDIA_BUS_FMT_SBGGR10_1X10
59 +};
60 static const char * const imx258_test_pattern_menu[] = {
61 "Disabled",
62 "Solid Colour",
63 @@ -605,6 +623,8 @@ struct imx258 {
64 struct v4l2_ctrl *vblank;
65 struct v4l2_ctrl *hblank;
66 struct v4l2_ctrl *exposure;
67 + struct v4l2_ctrl *hflip;
68 + struct v4l2_ctrl *vflip;
69
70 /* Current mode */
71 const struct imx258_mode *cur_mode;
72 @@ -700,16 +720,29 @@ static int imx258_write_regs(struct imx2
73 return 0;
74 }
75
76 +/* Get bayer order based on flip setting. */
77 +static u32 imx258_get_format_code(struct imx258 *imx258)
78 +{
79 + unsigned int i;
80 +
81 + lockdep_assert_held(&imx258->mutex);
82 +
83 + i = (imx258->vflip->val ? 2 : 0) |
84 + (imx258->hflip->val ? 1 : 0);
85 +
86 + return codes[i];
87 +}
88 /* Open sub-device */
89 static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
90 {
91 + struct imx258 *imx258 = to_imx258(sd);
92 struct v4l2_mbus_framefmt *try_fmt =
93 v4l2_subdev_get_try_format(sd, fh->state, 0);
94
95 /* Initialize try_fmt */
96 try_fmt->width = supported_modes[0].width;
97 try_fmt->height = supported_modes[0].height;
98 - try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
99 + try_fmt->code = imx258_get_format_code(imx258);
100 try_fmt->field = V4L2_FIELD_NONE;
101
102 return 0;
103 @@ -775,10 +808,6 @@ static int imx258_set_ctrl(struct v4l2_c
104 ret = imx258_write_reg(imx258, IMX258_REG_TEST_PATTERN,
105 IMX258_REG_VALUE_16BIT,
106 ctrl->val);
107 - ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
108 - IMX258_REG_VALUE_08BIT,
109 - !ctrl->val ? REG_CONFIG_MIRROR_FLIP :
110 - REG_CONFIG_FLIP_TEST_PATTERN);
111 break;
112 case V4L2_CID_WIDE_DYNAMIC_RANGE:
113 if (!ctrl->val) {
114 @@ -796,6 +825,15 @@ static int imx258_set_ctrl(struct v4l2_c
115 BIT(IMX258_HDR_RATIO_MAX));
116 }
117 break;
118 + case V4L2_CID_VFLIP:
119 + case V4L2_CID_HFLIP:
120 + ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
121 + IMX258_REG_VALUE_08BIT,
122 + (imx258->hflip->val ?
123 + REG_CONFIG_MIRROR_HFLIP : 0) |
124 + (imx258->vflip->val ?
125 + REG_CONFIG_MIRROR_VFLIP : 0));
126 + break;
127 default:
128 dev_info(&client->dev,
129 "ctrl(id:0x%x,val:0x%x) is not handled\n",
130 @@ -817,11 +855,13 @@ static int imx258_enum_mbus_code(struct
131 struct v4l2_subdev_state *sd_state,
132 struct v4l2_subdev_mbus_code_enum *code)
133 {
134 - /* Only one bayer order(GRBG) is supported */
135 + struct imx258 *imx258 = to_imx258(sd);
136 +
137 + /* Only one bayer format (10 bit) is supported */
138 if (code->index > 0)
139 return -EINVAL;
140
141 - code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
142 + code->code = imx258_get_format_code(imx258);
143
144 return 0;
145 }
146 @@ -830,10 +870,11 @@ static int imx258_enum_frame_size(struct
147 struct v4l2_subdev_state *sd_state,
148 struct v4l2_subdev_frame_size_enum *fse)
149 {
150 + struct imx258 *imx258 = to_imx258(sd);
151 if (fse->index >= ARRAY_SIZE(supported_modes))
152 return -EINVAL;
153
154 - if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
155 + if (fse->code != imx258_get_format_code(imx258))
156 return -EINVAL;
157
158 fse->min_width = supported_modes[fse->index].width;
159 @@ -844,12 +885,13 @@ static int imx258_enum_frame_size(struct
160 return 0;
161 }
162
163 -static void imx258_update_pad_format(const struct imx258_mode *mode,
164 +static void imx258_update_pad_format(struct imx258 *imx258,
165 + const struct imx258_mode *mode,
166 struct v4l2_subdev_format *fmt)
167 {
168 fmt->format.width = mode->width;
169 fmt->format.height = mode->height;
170 - fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
171 + fmt->format.code = imx258_get_format_code(imx258);
172 fmt->format.field = V4L2_FIELD_NONE;
173 }
174
175 @@ -862,7 +904,7 @@ static int __imx258_get_pad_format(struc
176 sd_state,
177 fmt->pad);
178 else
179 - imx258_update_pad_format(imx258->cur_mode, fmt);
180 + imx258_update_pad_format(imx258, imx258->cur_mode, fmt);
181
182 return 0;
183 }
184 @@ -896,13 +938,12 @@ static int imx258_set_pad_format(struct
185
186 mutex_lock(&imx258->mutex);
187
188 - /* Only one raw bayer(GBRG) order is supported */
189 - fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
190 + fmt->format.code = imx258_get_format_code(imx258);
191
192 mode = v4l2_find_nearest_size(supported_modes,
193 ARRAY_SIZE(supported_modes), width, height,
194 fmt->format.width, fmt->format.height);
195 - imx258_update_pad_format(mode, fmt);
196 + imx258_update_pad_format(imx258, mode, fmt);
197 if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
198 framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
199 *framefmt = fmt->format;
200 @@ -959,15 +1000,6 @@ static int imx258_start_streaming(struct
201 return ret;
202 }
203
204 - /* Set Orientation be 180 degree */
205 - ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
206 - IMX258_REG_VALUE_08BIT, REG_CONFIG_MIRROR_FLIP);
207 - if (ret) {
208 - dev_err(&client->dev, "%s failed to set orientation\n",
209 - __func__);
210 - return ret;
211 - }
212 -
213 /* Apply customized values from user */
214 ret = __v4l2_ctrl_handler_setup(imx258->sd.ctrl_handler);
215 if (ret)
216 @@ -1142,6 +1174,7 @@ static const struct v4l2_subdev_internal
217 static int imx258_init_controls(struct imx258 *imx258)
218 {
219 struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
220 + struct v4l2_fwnode_device_properties props;
221 struct v4l2_ctrl_handler *ctrl_hdlr;
222 s64 vblank_def;
223 s64 vblank_min;
224 @@ -1150,7 +1183,7 @@ static int imx258_init_controls(struct i
225 int ret;
226
227 ctrl_hdlr = &imx258->ctrl_handler;
228 - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
229 + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
230 if (ret)
231 return ret;
232
233 @@ -1219,6 +1252,21 @@ static int imx258_init_controls(struct i
234 ARRAY_SIZE(imx258_test_pattern_menu) - 1,
235 0, 0, imx258_test_pattern_menu);
236
237 + ret = v4l2_fwnode_device_parse(&client->dev, &props);
238 + if (ret)
239 + goto error;
240 +
241 + imx258->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
242 + V4L2_CID_HFLIP, 0, 1, 1,
243 + props.rotation == 180 ? 1 : 0);
244 + if (imx258->hflip)
245 + imx258->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
246 +
247 + imx258->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
248 + V4L2_CID_VFLIP, 0, 1, 1,
249 + props.rotation == 180 ? 1 : 0);
250 + if (imx258->vflip)
251 + imx258->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
252 if (ctrl_hdlr->error) {
253 ret = ctrl_hdlr->error;
254 dev_err(&client->dev, "%s control init failed (%d)\n",
255 @@ -1270,14 +1318,6 @@ static int imx258_probe(struct i2c_clien
256 return -EINVAL;
257 }
258
259 - /*
260 - * Check that the device is mounted upside down. The driver only
261 - * supports a single pixel order right now.
262 - */
263 - ret = device_property_read_u32(&client->dev, "rotation", &val);
264 - if (ret || val != 180)
265 - return -EINVAL;
266 -
267 /* Initialize subdev */
268 v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops);
269