]>
Commit | Line | Data |
---|---|---|
0f2ce168 | 1 | /* |
d8dde6c8 | 2 | * Driver for MT9V022, MT9V024, MT9V032, and MT9V034 CMOS Image Sensors |
0f2ce168 DC |
3 | * |
4 | * Copyright (C) 2010, Laurent Pinchart <laurent.pinchart@ideasonboard.com> | |
5 | * | |
6 | * Based on the MT9M001 driver, | |
7 | * | |
8 | * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | */ | |
14 | ||
3300a8fd | 15 | #include <linux/clk.h> |
0f2ce168 | 16 | #include <linux/delay.h> |
28d5bdbe | 17 | #include <linux/gpio/consumer.h> |
0f2ce168 DC |
18 | #include <linux/i2c.h> |
19 | #include <linux/log2.h> | |
20 | #include <linux/mutex.h> | |
f2272e13 | 21 | #include <linux/of.h> |
859969b3 | 22 | #include <linux/of_graph.h> |
80b44ef2 | 23 | #include <linux/regmap.h> |
0f2ce168 DC |
24 | #include <linux/slab.h> |
25 | #include <linux/videodev2.h> | |
26 | #include <linux/v4l2-mediabus.h> | |
7a707b89 | 27 | #include <linux/module.h> |
0f2ce168 | 28 | |
b5dcee22 | 29 | #include <media/i2c/mt9v032.h> |
0f2ce168 DC |
30 | #include <media/v4l2-ctrls.h> |
31 | #include <media/v4l2-device.h> | |
859969b3 | 32 | #include <media/v4l2-fwnode.h> |
0f2ce168 DC |
33 | #include <media/v4l2-subdev.h> |
34 | ||
2b9e9f77 LP |
35 | /* The first four rows are black rows. The active area spans 753x481 pixels. */ |
36 | #define MT9V032_PIXEL_ARRAY_HEIGHT 485 | |
37 | #define MT9V032_PIXEL_ARRAY_WIDTH 753 | |
0f2ce168 | 38 | |
e9a50e4c LP |
39 | #define MT9V032_SYSCLK_FREQ_DEF 26600000 |
40 | ||
0f2ce168 DC |
41 | #define MT9V032_CHIP_VERSION 0x00 |
42 | #define MT9V032_CHIP_ID_REV1 0x1311 | |
43 | #define MT9V032_CHIP_ID_REV3 0x1313 | |
daecfebc | 44 | #define MT9V034_CHIP_ID_REV1 0X1324 |
86cf786c | 45 | #define MT9V032_COLUMN_START 0x01 |
0f2ce168 | 46 | #define MT9V032_COLUMN_START_MIN 1 |
86cf786c | 47 | #define MT9V032_COLUMN_START_DEF 1 |
0f2ce168 | 48 | #define MT9V032_COLUMN_START_MAX 752 |
86cf786c LP |
49 | #define MT9V032_ROW_START 0x02 |
50 | #define MT9V032_ROW_START_MIN 4 | |
51 | #define MT9V032_ROW_START_DEF 5 | |
52 | #define MT9V032_ROW_START_MAX 482 | |
0f2ce168 DC |
53 | #define MT9V032_WINDOW_HEIGHT 0x03 |
54 | #define MT9V032_WINDOW_HEIGHT_MIN 1 | |
55 | #define MT9V032_WINDOW_HEIGHT_DEF 480 | |
56 | #define MT9V032_WINDOW_HEIGHT_MAX 480 | |
57 | #define MT9V032_WINDOW_WIDTH 0x04 | |
58 | #define MT9V032_WINDOW_WIDTH_MIN 1 | |
59 | #define MT9V032_WINDOW_WIDTH_DEF 752 | |
60 | #define MT9V032_WINDOW_WIDTH_MAX 752 | |
61 | #define MT9V032_HORIZONTAL_BLANKING 0x05 | |
62 | #define MT9V032_HORIZONTAL_BLANKING_MIN 43 | |
daecfebc | 63 | #define MT9V034_HORIZONTAL_BLANKING_MIN 61 |
9ec670e2 | 64 | #define MT9V032_HORIZONTAL_BLANKING_DEF 94 |
0f2ce168 DC |
65 | #define MT9V032_HORIZONTAL_BLANKING_MAX 1023 |
66 | #define MT9V032_VERTICAL_BLANKING 0x06 | |
67 | #define MT9V032_VERTICAL_BLANKING_MIN 4 | |
daecfebc | 68 | #define MT9V034_VERTICAL_BLANKING_MIN 2 |
9ec670e2 | 69 | #define MT9V032_VERTICAL_BLANKING_DEF 45 |
0f2ce168 | 70 | #define MT9V032_VERTICAL_BLANKING_MAX 3000 |
daecfebc | 71 | #define MT9V034_VERTICAL_BLANKING_MAX 32288 |
0f2ce168 DC |
72 | #define MT9V032_CHIP_CONTROL 0x07 |
73 | #define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3) | |
74 | #define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7) | |
75 | #define MT9V032_CHIP_CONTROL_SEQUENTIAL (1 << 8) | |
76 | #define MT9V032_SHUTTER_WIDTH1 0x08 | |
77 | #define MT9V032_SHUTTER_WIDTH2 0x09 | |
78 | #define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a | |
79 | #define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b | |
80 | #define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1 | |
daecfebc | 81 | #define MT9V034_TOTAL_SHUTTER_WIDTH_MIN 0 |
0f2ce168 DC |
82 | #define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480 |
83 | #define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767 | |
daecfebc | 84 | #define MT9V034_TOTAL_SHUTTER_WIDTH_MAX 32765 |
0f2ce168 DC |
85 | #define MT9V032_RESET 0x0c |
86 | #define MT9V032_READ_MODE 0x0d | |
87 | #define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0) | |
88 | #define MT9V032_READ_MODE_ROW_BIN_SHIFT 0 | |
89 | #define MT9V032_READ_MODE_COLUMN_BIN_MASK (3 << 2) | |
90 | #define MT9V032_READ_MODE_COLUMN_BIN_SHIFT 2 | |
91 | #define MT9V032_READ_MODE_ROW_FLIP (1 << 4) | |
92 | #define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5) | |
93 | #define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6) | |
94 | #define MT9V032_READ_MODE_DARK_ROWS (1 << 7) | |
d131e54b | 95 | #define MT9V032_READ_MODE_RESERVED 0x0300 |
0f2ce168 | 96 | #define MT9V032_PIXEL_OPERATION_MODE 0x0f |
daecfebc LP |
97 | #define MT9V034_PIXEL_OPERATION_MODE_HDR (1 << 0) |
98 | #define MT9V034_PIXEL_OPERATION_MODE_COLOR (1 << 1) | |
0f2ce168 DC |
99 | #define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2) |
100 | #define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6) | |
101 | #define MT9V032_ANALOG_GAIN 0x35 | |
102 | #define MT9V032_ANALOG_GAIN_MIN 16 | |
103 | #define MT9V032_ANALOG_GAIN_DEF 16 | |
104 | #define MT9V032_ANALOG_GAIN_MAX 64 | |
105 | #define MT9V032_MAX_ANALOG_GAIN 0x36 | |
106 | #define MT9V032_MAX_ANALOG_GAIN_MAX 127 | |
107 | #define MT9V032_FRAME_DARK_AVERAGE 0x42 | |
108 | #define MT9V032_DARK_AVG_THRESH 0x46 | |
109 | #define MT9V032_DARK_AVG_LOW_THRESH_MASK (255 << 0) | |
110 | #define MT9V032_DARK_AVG_LOW_THRESH_SHIFT 0 | |
111 | #define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8) | |
112 | #define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8 | |
113 | #define MT9V032_ROW_NOISE_CORR_CONTROL 0x70 | |
daecfebc LP |
114 | #define MT9V034_ROW_NOISE_CORR_ENABLE (1 << 0) |
115 | #define MT9V034_ROW_NOISE_CORR_USE_BLK_AVG (1 << 1) | |
0f2ce168 DC |
116 | #define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5) |
117 | #define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7) | |
118 | #define MT9V032_PIXEL_CLOCK 0x74 | |
daecfebc | 119 | #define MT9V034_PIXEL_CLOCK 0x72 |
0f2ce168 DC |
120 | #define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0) |
121 | #define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1) | |
122 | #define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2) | |
123 | #define MT9V032_PIXEL_CLOCK_CONT_LINE (1 << 3) | |
124 | #define MT9V032_PIXEL_CLOCK_INV_PXL_CLK (1 << 4) | |
125 | #define MT9V032_TEST_PATTERN 0x7f | |
126 | #define MT9V032_TEST_PATTERN_DATA_MASK (1023 << 0) | |
127 | #define MT9V032_TEST_PATTERN_DATA_SHIFT 0 | |
128 | #define MT9V032_TEST_PATTERN_USE_DATA (1 << 10) | |
129 | #define MT9V032_TEST_PATTERN_GRAY_MASK (3 << 11) | |
130 | #define MT9V032_TEST_PATTERN_GRAY_NONE (0 << 11) | |
131 | #define MT9V032_TEST_PATTERN_GRAY_VERTICAL (1 << 11) | |
132 | #define MT9V032_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11) | |
133 | #define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11) | |
134 | #define MT9V032_TEST_PATTERN_ENABLE (1 << 13) | |
135 | #define MT9V032_TEST_PATTERN_FLIP (1 << 14) | |
81ea4829 MP |
136 | #define MT9V032_AEGC_DESIRED_BIN 0xa5 |
137 | #define MT9V032_AEC_UPDATE_FREQUENCY 0xa6 | |
138 | #define MT9V032_AEC_LPF 0xa8 | |
139 | #define MT9V032_AGC_UPDATE_FREQUENCY 0xa9 | |
140 | #define MT9V032_AGC_LPF 0xaa | |
0f2ce168 DC |
141 | #define MT9V032_AEC_AGC_ENABLE 0xaf |
142 | #define MT9V032_AEC_ENABLE (1 << 0) | |
143 | #define MT9V032_AGC_ENABLE (1 << 1) | |
81ea4829 MP |
144 | #define MT9V034_AEC_MAX_SHUTTER_WIDTH 0xad |
145 | #define MT9V032_AEC_MAX_SHUTTER_WIDTH 0xbd | |
0f2ce168 DC |
146 | #define MT9V032_THERMAL_INFO 0xc1 |
147 | ||
220ddc7f | 148 | enum mt9v032_model { |
d8dde6c8 PZ |
149 | MT9V032_MODEL_V022_COLOR, /* MT9V022IX7ATC */ |
150 | MT9V032_MODEL_V022_MONO, /* MT9V022IX7ATM */ | |
151 | MT9V032_MODEL_V024_COLOR, /* MT9V024IA7XTC */ | |
152 | MT9V032_MODEL_V024_MONO, /* MT9V024IA7XTM */ | |
153 | MT9V032_MODEL_V032_COLOR, /* MT9V032C12STM */ | |
154 | MT9V032_MODEL_V032_MONO, /* MT9V032C12STC */ | |
daecfebc LP |
155 | MT9V032_MODEL_V034_COLOR, |
156 | MT9V032_MODEL_V034_MONO, | |
220ddc7f LP |
157 | }; |
158 | ||
0a466b60 LP |
159 | struct mt9v032_model_version { |
160 | unsigned int version; | |
161 | const char *name; | |
162 | }; | |
163 | ||
164 | struct mt9v032_model_data { | |
165 | unsigned int min_row_time; | |
166 | unsigned int min_hblank; | |
167 | unsigned int min_vblank; | |
168 | unsigned int max_vblank; | |
169 | unsigned int min_shutter; | |
170 | unsigned int max_shutter; | |
171 | unsigned int pclk_reg; | |
81ea4829 MP |
172 | unsigned int aec_max_shutter_reg; |
173 | const struct v4l2_ctrl_config * const aec_max_shutter_v4l2_ctrl; | |
0a466b60 LP |
174 | }; |
175 | ||
220ddc7f | 176 | struct mt9v032_model_info { |
0a466b60 | 177 | const struct mt9v032_model_data *data; |
220ddc7f LP |
178 | bool color; |
179 | }; | |
180 | ||
0a466b60 | 181 | static const struct mt9v032_model_version mt9v032_versions[] = { |
d8dde6c8 PZ |
182 | { MT9V032_CHIP_ID_REV1, "MT9V022/MT9V032 rev1/2" }, |
183 | { MT9V032_CHIP_ID_REV3, "MT9V022/MT9V032 rev3" }, | |
184 | { MT9V034_CHIP_ID_REV1, "MT9V024/MT9V034 rev1" }, | |
0a466b60 LP |
185 | }; |
186 | ||
0f2ce168 DC |
187 | struct mt9v032 { |
188 | struct v4l2_subdev subdev; | |
189 | struct media_pad pad; | |
190 | ||
191 | struct v4l2_mbus_framefmt format; | |
192 | struct v4l2_rect crop; | |
637f005e LP |
193 | unsigned int hratio; |
194 | unsigned int vratio; | |
0f2ce168 DC |
195 | |
196 | struct v4l2_ctrl_handler ctrls; | |
e9a50e4c LP |
197 | struct { |
198 | struct v4l2_ctrl *link_freq; | |
199 | struct v4l2_ctrl *pixel_rate; | |
200 | }; | |
0f2ce168 DC |
201 | |
202 | struct mutex power_lock; | |
203 | int power_count; | |
204 | ||
80b44ef2 | 205 | struct regmap *regmap; |
3300a8fd | 206 | struct clk *clk; |
28d5bdbe MP |
207 | struct gpio_desc *reset_gpio; |
208 | struct gpio_desc *standby_gpio; | |
3300a8fd | 209 | |
0f2ce168 | 210 | struct mt9v032_platform_data *pdata; |
220ddc7f | 211 | const struct mt9v032_model_info *model; |
0a466b60 | 212 | const struct mt9v032_model_version *version; |
e9a50e4c LP |
213 | |
214 | u32 sysclk; | |
0f2ce168 | 215 | u16 aec_agc; |
9ec670e2 | 216 | u16 hblank; |
b28d7017 LP |
217 | struct { |
218 | struct v4l2_ctrl *test_pattern; | |
219 | struct v4l2_ctrl *test_pattern_color; | |
220 | }; | |
0f2ce168 DC |
221 | }; |
222 | ||
223 | static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd) | |
224 | { | |
225 | return container_of(sd, struct mt9v032, subdev); | |
226 | } | |
227 | ||
0f2ce168 DC |
228 | static int |
229 | mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable) | |
230 | { | |
80b44ef2 | 231 | struct regmap *map = mt9v032->regmap; |
0f2ce168 DC |
232 | u16 value = mt9v032->aec_agc; |
233 | int ret; | |
234 | ||
235 | if (enable) | |
236 | value |= which; | |
237 | else | |
238 | value &= ~which; | |
239 | ||
80b44ef2 | 240 | ret = regmap_write(map, MT9V032_AEC_AGC_ENABLE, value); |
0f2ce168 DC |
241 | if (ret < 0) |
242 | return ret; | |
243 | ||
244 | mt9v032->aec_agc = value; | |
245 | return 0; | |
246 | } | |
247 | ||
9ec670e2 LP |
248 | static int |
249 | mt9v032_update_hblank(struct mt9v032 *mt9v032) | |
250 | { | |
9ec670e2 | 251 | struct v4l2_rect *crop = &mt9v032->crop; |
daecfebc | 252 | unsigned int min_hblank = mt9v032->model->data->min_hblank; |
0a466b60 | 253 | unsigned int hblank; |
9ec670e2 | 254 | |
daecfebc LP |
255 | if (mt9v032->version->version == MT9V034_CHIP_ID_REV1) |
256 | min_hblank += (mt9v032->hratio - 1) * 10; | |
f17bc3f4 PZ |
257 | min_hblank = max_t(int, mt9v032->model->data->min_row_time - crop->width, |
258 | min_hblank); | |
daecfebc LP |
259 | hblank = max_t(unsigned int, mt9v032->hblank, min_hblank); |
260 | ||
80b44ef2 PZ |
261 | return regmap_write(mt9v032->regmap, MT9V032_HORIZONTAL_BLANKING, |
262 | hblank); | |
9ec670e2 LP |
263 | } |
264 | ||
0f2ce168 DC |
265 | static int mt9v032_power_on(struct mt9v032 *mt9v032) |
266 | { | |
80b44ef2 | 267 | struct regmap *map = mt9v032->regmap; |
0f2ce168 DC |
268 | int ret; |
269 | ||
70829035 | 270 | gpiod_set_value_cansleep(mt9v032->reset_gpio, 1); |
28d5bdbe | 271 | |
79019190 LP |
272 | ret = clk_set_rate(mt9v032->clk, mt9v032->sysclk); |
273 | if (ret < 0) | |
274 | return ret; | |
275 | ||
28d5bdbe | 276 | /* System clock has to be enabled before releasing the reset */ |
79019190 LP |
277 | ret = clk_prepare_enable(mt9v032->clk); |
278 | if (ret) | |
279 | return ret; | |
280 | ||
3300a8fd | 281 | udelay(1); |
0f2ce168 | 282 | |
28d5bdbe MP |
283 | if (mt9v032->reset_gpio) { |
284 | gpiod_set_value_cansleep(mt9v032->reset_gpio, 0); | |
285 | ||
286 | /* After releasing reset we need to wait 10 clock cycles | |
287 | * before accessing the sensor over I2C. As the minimum SYSCLK | |
288 | * frequency is 13MHz, waiting 1µs will be enough in the worst | |
289 | * case. | |
290 | */ | |
291 | udelay(1); | |
292 | } | |
293 | ||
0f2ce168 | 294 | /* Reset the chip and stop data read out */ |
80b44ef2 | 295 | ret = regmap_write(map, MT9V032_RESET, 1); |
0f2ce168 DC |
296 | if (ret < 0) |
297 | return ret; | |
298 | ||
80b44ef2 | 299 | ret = regmap_write(map, MT9V032_RESET, 0); |
0f2ce168 DC |
300 | if (ret < 0) |
301 | return ret; | |
302 | ||
78060d51 MP |
303 | return regmap_write(map, MT9V032_CHIP_CONTROL, |
304 | MT9V032_CHIP_CONTROL_MASTER_MODE); | |
0f2ce168 DC |
305 | } |
306 | ||
307 | static void mt9v032_power_off(struct mt9v032 *mt9v032) | |
308 | { | |
3300a8fd | 309 | clk_disable_unprepare(mt9v032->clk); |
0f2ce168 DC |
310 | } |
311 | ||
312 | static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) | |
313 | { | |
80b44ef2 | 314 | struct regmap *map = mt9v032->regmap; |
0f2ce168 DC |
315 | int ret; |
316 | ||
317 | if (!on) { | |
318 | mt9v032_power_off(mt9v032); | |
319 | return 0; | |
320 | } | |
321 | ||
322 | ret = mt9v032_power_on(mt9v032); | |
323 | if (ret < 0) | |
324 | return ret; | |
325 | ||
326 | /* Configure the pixel clock polarity */ | |
327 | if (mt9v032->pdata && mt9v032->pdata->clk_pol) { | |
80b44ef2 | 328 | ret = regmap_write(map, mt9v032->model->data->pclk_reg, |
0f2ce168 DC |
329 | MT9V032_PIXEL_CLOCK_INV_PXL_CLK); |
330 | if (ret < 0) | |
331 | return ret; | |
332 | } | |
333 | ||
334 | /* Disable the noise correction algorithm and restore the controls. */ | |
80b44ef2 | 335 | ret = regmap_write(map, MT9V032_ROW_NOISE_CORR_CONTROL, 0); |
0f2ce168 DC |
336 | if (ret < 0) |
337 | return ret; | |
338 | ||
339 | return v4l2_ctrl_handler_setup(&mt9v032->ctrls); | |
340 | } | |
341 | ||
342 | /* ----------------------------------------------------------------------------- | |
343 | * V4L2 subdev video operations | |
344 | */ | |
345 | ||
346 | static struct v4l2_mbus_framefmt * | |
f7234138 | 347 | __mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *cfg, |
0f2ce168 DC |
348 | unsigned int pad, enum v4l2_subdev_format_whence which) |
349 | { | |
350 | switch (which) { | |
351 | case V4L2_SUBDEV_FORMAT_TRY: | |
f7234138 | 352 | return v4l2_subdev_get_try_format(&mt9v032->subdev, cfg, pad); |
0f2ce168 DC |
353 | case V4L2_SUBDEV_FORMAT_ACTIVE: |
354 | return &mt9v032->format; | |
355 | default: | |
356 | return NULL; | |
357 | } | |
358 | } | |
359 | ||
360 | static struct v4l2_rect * | |
f7234138 | 361 | __mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *cfg, |
0f2ce168 DC |
362 | unsigned int pad, enum v4l2_subdev_format_whence which) |
363 | { | |
364 | switch (which) { | |
365 | case V4L2_SUBDEV_FORMAT_TRY: | |
f7234138 | 366 | return v4l2_subdev_get_try_crop(&mt9v032->subdev, cfg, pad); |
0f2ce168 DC |
367 | case V4L2_SUBDEV_FORMAT_ACTIVE: |
368 | return &mt9v032->crop; | |
369 | default: | |
370 | return NULL; | |
371 | } | |
372 | } | |
373 | ||
374 | static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) | |
375 | { | |
78060d51 | 376 | const u16 mode = MT9V032_CHIP_CONTROL_DOUT_ENABLE |
0f2ce168 | 377 | | MT9V032_CHIP_CONTROL_SEQUENTIAL; |
0f2ce168 | 378 | struct mt9v032 *mt9v032 = to_mt9v032(subdev); |
0f2ce168 | 379 | struct v4l2_rect *crop = &mt9v032->crop; |
80b44ef2 | 380 | struct regmap *map = mt9v032->regmap; |
637f005e LP |
381 | unsigned int hbin; |
382 | unsigned int vbin; | |
0f2ce168 DC |
383 | int ret; |
384 | ||
385 | if (!enable) | |
80b44ef2 | 386 | return regmap_update_bits(map, MT9V032_CHIP_CONTROL, mode, 0); |
0f2ce168 DC |
387 | |
388 | /* Configure the window size and row/column bin */ | |
637f005e LP |
389 | hbin = fls(mt9v032->hratio) - 1; |
390 | vbin = fls(mt9v032->vratio) - 1; | |
80b44ef2 PZ |
391 | ret = regmap_update_bits(map, MT9V032_READ_MODE, |
392 | ~MT9V032_READ_MODE_RESERVED, | |
393 | hbin << MT9V032_READ_MODE_COLUMN_BIN_SHIFT | | |
394 | vbin << MT9V032_READ_MODE_ROW_BIN_SHIFT); | |
0f2ce168 DC |
395 | if (ret < 0) |
396 | return ret; | |
397 | ||
80b44ef2 | 398 | ret = regmap_write(map, MT9V032_COLUMN_START, crop->left); |
0f2ce168 DC |
399 | if (ret < 0) |
400 | return ret; | |
401 | ||
80b44ef2 | 402 | ret = regmap_write(map, MT9V032_ROW_START, crop->top); |
0f2ce168 DC |
403 | if (ret < 0) |
404 | return ret; | |
405 | ||
80b44ef2 | 406 | ret = regmap_write(map, MT9V032_WINDOW_WIDTH, crop->width); |
0f2ce168 DC |
407 | if (ret < 0) |
408 | return ret; | |
409 | ||
80b44ef2 | 410 | ret = regmap_write(map, MT9V032_WINDOW_HEIGHT, crop->height); |
0f2ce168 DC |
411 | if (ret < 0) |
412 | return ret; | |
413 | ||
9ec670e2 | 414 | ret = mt9v032_update_hblank(mt9v032); |
0f2ce168 DC |
415 | if (ret < 0) |
416 | return ret; | |
417 | ||
418 | /* Switch to master "normal" mode */ | |
80b44ef2 | 419 | return regmap_update_bits(map, MT9V032_CHIP_CONTROL, mode, mode); |
0f2ce168 DC |
420 | } |
421 | ||
422 | static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, | |
f7234138 | 423 | struct v4l2_subdev_pad_config *cfg, |
0f2ce168 DC |
424 | struct v4l2_subdev_mbus_code_enum *code) |
425 | { | |
426 | if (code->index > 0) | |
427 | return -EINVAL; | |
428 | ||
f5fe58fd | 429 | code->code = MEDIA_BUS_FMT_SGRBG10_1X10; |
0f2ce168 DC |
430 | return 0; |
431 | } | |
432 | ||
433 | static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, | |
f7234138 | 434 | struct v4l2_subdev_pad_config *cfg, |
0f2ce168 DC |
435 | struct v4l2_subdev_frame_size_enum *fse) |
436 | { | |
f5fe58fd | 437 | if (fse->index >= 3 || fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) |
0f2ce168 DC |
438 | return -EINVAL; |
439 | ||
637f005e | 440 | fse->min_width = MT9V032_WINDOW_WIDTH_DEF / (1 << fse->index); |
0f2ce168 | 441 | fse->max_width = fse->min_width; |
637f005e | 442 | fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / (1 << fse->index); |
0f2ce168 DC |
443 | fse->max_height = fse->min_height; |
444 | ||
445 | return 0; | |
446 | } | |
447 | ||
448 | static int mt9v032_get_format(struct v4l2_subdev *subdev, | |
f7234138 | 449 | struct v4l2_subdev_pad_config *cfg, |
0f2ce168 DC |
450 | struct v4l2_subdev_format *format) |
451 | { | |
452 | struct mt9v032 *mt9v032 = to_mt9v032(subdev); | |
453 | ||
f7234138 | 454 | format->format = *__mt9v032_get_pad_format(mt9v032, cfg, format->pad, |
0f2ce168 DC |
455 | format->which); |
456 | return 0; | |
457 | } | |
458 | ||
637f005e | 459 | static void mt9v032_configure_pixel_rate(struct mt9v032 *mt9v032) |
41a33a00 SA |
460 | { |
461 | struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); | |
462 | int ret; | |
463 | ||
e9a50e4c | 464 | ret = v4l2_ctrl_s_ctrl_int64(mt9v032->pixel_rate, |
637f005e | 465 | mt9v032->sysclk / mt9v032->hratio); |
41a33a00 SA |
466 | if (ret < 0) |
467 | dev_warn(&client->dev, "failed to set pixel rate (%d)\n", ret); | |
468 | } | |
469 | ||
637f005e LP |
470 | static unsigned int mt9v032_calc_ratio(unsigned int input, unsigned int output) |
471 | { | |
472 | /* Compute the power-of-two binning factor closest to the input size to | |
473 | * output size ratio. Given that the output size is bounded by input/4 | |
474 | * and input, a generic implementation would be an ineffective luxury. | |
475 | */ | |
476 | if (output * 3 > input * 2) | |
477 | return 1; | |
478 | if (output * 3 > input) | |
479 | return 2; | |
480 | return 4; | |
481 | } | |
482 | ||
0f2ce168 | 483 | static int mt9v032_set_format(struct v4l2_subdev *subdev, |
f7234138 | 484 | struct v4l2_subdev_pad_config *cfg, |
0f2ce168 DC |
485 | struct v4l2_subdev_format *format) |
486 | { | |
487 | struct mt9v032 *mt9v032 = to_mt9v032(subdev); | |
488 | struct v4l2_mbus_framefmt *__format; | |
489 | struct v4l2_rect *__crop; | |
490 | unsigned int width; | |
491 | unsigned int height; | |
492 | unsigned int hratio; | |
493 | unsigned int vratio; | |
494 | ||
f7234138 | 495 | __crop = __mt9v032_get_pad_crop(mt9v032, cfg, format->pad, |
0f2ce168 DC |
496 | format->which); |
497 | ||
498 | /* Clamp the width and height to avoid dividing by zero. */ | |
f90580ca RRD |
499 | width = clamp(ALIGN(format->format.width, 2), |
500 | max_t(unsigned int, __crop->width / 4, | |
501 | MT9V032_WINDOW_WIDTH_MIN), | |
502 | __crop->width); | |
503 | height = clamp(ALIGN(format->format.height, 2), | |
504 | max_t(unsigned int, __crop->height / 4, | |
505 | MT9V032_WINDOW_HEIGHT_MIN), | |
506 | __crop->height); | |
0f2ce168 | 507 | |
637f005e LP |
508 | hratio = mt9v032_calc_ratio(__crop->width, width); |
509 | vratio = mt9v032_calc_ratio(__crop->height, height); | |
0f2ce168 | 510 | |
f7234138 | 511 | __format = __mt9v032_get_pad_format(mt9v032, cfg, format->pad, |
0f2ce168 DC |
512 | format->which); |
513 | __format->width = __crop->width / hratio; | |
514 | __format->height = __crop->height / vratio; | |
637f005e LP |
515 | |
516 | if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { | |
517 | mt9v032->hratio = hratio; | |
518 | mt9v032->vratio = vratio; | |
519 | mt9v032_configure_pixel_rate(mt9v032); | |
520 | } | |
0f2ce168 DC |
521 | |
522 | format->format = *__format; | |
523 | ||
524 | return 0; | |
525 | } | |
526 | ||
1a023feb | 527 | static int mt9v032_get_selection(struct v4l2_subdev *subdev, |
f7234138 | 528 | struct v4l2_subdev_pad_config *cfg, |
1a023feb | 529 | struct v4l2_subdev_selection *sel) |
0f2ce168 DC |
530 | { |
531 | struct mt9v032 *mt9v032 = to_mt9v032(subdev); | |
532 | ||
1a023feb HV |
533 | if (sel->target != V4L2_SEL_TGT_CROP) |
534 | return -EINVAL; | |
535 | ||
f7234138 | 536 | sel->r = *__mt9v032_get_pad_crop(mt9v032, cfg, sel->pad, sel->which); |
0f2ce168 DC |
537 | return 0; |
538 | } | |
539 | ||
1a023feb | 540 | static int mt9v032_set_selection(struct v4l2_subdev *subdev, |
f7234138 | 541 | struct v4l2_subdev_pad_config *cfg, |
1a023feb | 542 | struct v4l2_subdev_selection *sel) |
0f2ce168 DC |
543 | { |
544 | struct mt9v032 *mt9v032 = to_mt9v032(subdev); | |
545 | struct v4l2_mbus_framefmt *__format; | |
546 | struct v4l2_rect *__crop; | |
547 | struct v4l2_rect rect; | |
548 | ||
1a023feb HV |
549 | if (sel->target != V4L2_SEL_TGT_CROP) |
550 | return -EINVAL; | |
551 | ||
86cf786c LP |
552 | /* Clamp the crop rectangle boundaries and align them to a non multiple |
553 | * of 2 pixels to ensure a GRBG Bayer pattern. | |
0f2ce168 | 554 | */ |
1a023feb | 555 | rect.left = clamp(ALIGN(sel->r.left + 1, 2) - 1, |
0f2ce168 DC |
556 | MT9V032_COLUMN_START_MIN, |
557 | MT9V032_COLUMN_START_MAX); | |
1a023feb | 558 | rect.top = clamp(ALIGN(sel->r.top + 1, 2) - 1, |
0f2ce168 DC |
559 | MT9V032_ROW_START_MIN, |
560 | MT9V032_ROW_START_MAX); | |
1a023feb | 561 | rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), |
f90580ca RRD |
562 | MT9V032_WINDOW_WIDTH_MIN, |
563 | MT9V032_WINDOW_WIDTH_MAX); | |
1a023feb | 564 | rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), |
f90580ca RRD |
565 | MT9V032_WINDOW_HEIGHT_MIN, |
566 | MT9V032_WINDOW_HEIGHT_MAX); | |
567 | ||
568 | rect.width = min_t(unsigned int, | |
569 | rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); | |
570 | rect.height = min_t(unsigned int, | |
571 | rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); | |
0f2ce168 | 572 | |
f7234138 | 573 | __crop = __mt9v032_get_pad_crop(mt9v032, cfg, sel->pad, sel->which); |
0f2ce168 DC |
574 | |
575 | if (rect.width != __crop->width || rect.height != __crop->height) { | |
576 | /* Reset the output image size if the crop rectangle size has | |
577 | * been modified. | |
578 | */ | |
f7234138 | 579 | __format = __mt9v032_get_pad_format(mt9v032, cfg, sel->pad, |
1a023feb | 580 | sel->which); |
0f2ce168 DC |
581 | __format->width = rect.width; |
582 | __format->height = rect.height; | |
1a023feb | 583 | if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { |
637f005e LP |
584 | mt9v032->hratio = 1; |
585 | mt9v032->vratio = 1; | |
586 | mt9v032_configure_pixel_rate(mt9v032); | |
587 | } | |
0f2ce168 DC |
588 | } |
589 | ||
590 | *__crop = rect; | |
1a023feb | 591 | sel->r = rect; |
0f2ce168 DC |
592 | |
593 | return 0; | |
594 | } | |
595 | ||
596 | /* ----------------------------------------------------------------------------- | |
597 | * V4L2 subdev control operations | |
598 | */ | |
599 | ||
b28d7017 | 600 | #define V4L2_CID_TEST_PATTERN_COLOR (V4L2_CID_USER_BASE | 0x1001) |
81ea4829 MP |
601 | /* |
602 | * Value between 1 and 64 to set the desired bin. This is effectively a measure | |
603 | * of how bright the image is supposed to be. Both AGC and AEC try to reach | |
604 | * this. | |
605 | */ | |
606 | #define V4L2_CID_AEGC_DESIRED_BIN (V4L2_CID_USER_BASE | 0x1002) | |
607 | /* | |
608 | * LPF is the low pass filter capability of the chip. Both AEC and AGC have | |
609 | * this setting. This limits the speed in which AGC/AEC adjust their settings. | |
610 | * Possible values are 0-2. 0 means no LPF. For 1 and 2 this equation is used: | |
611 | * | |
612 | * if |(calculated new exp - current exp)| > (current exp / 4) | |
613 | * next exp = calculated new exp | |
614 | * else | |
615 | * next exp = current exp + ((calculated new exp - current exp) / 2^LPF) | |
616 | */ | |
617 | #define V4L2_CID_AEC_LPF (V4L2_CID_USER_BASE | 0x1003) | |
618 | #define V4L2_CID_AGC_LPF (V4L2_CID_USER_BASE | 0x1004) | |
619 | /* | |
620 | * Value between 0 and 15. This is the number of frames being skipped before | |
621 | * updating the auto exposure/gain. | |
622 | */ | |
623 | #define V4L2_CID_AEC_UPDATE_INTERVAL (V4L2_CID_USER_BASE | 0x1005) | |
624 | #define V4L2_CID_AGC_UPDATE_INTERVAL (V4L2_CID_USER_BASE | 0x1006) | |
625 | /* | |
626 | * Maximum shutter width used for AEC. | |
627 | */ | |
628 | #define V4L2_CID_AEC_MAX_SHUTTER_WIDTH (V4L2_CID_USER_BASE | 0x1007) | |
0f2ce168 DC |
629 | |
630 | static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) | |
631 | { | |
632 | struct mt9v032 *mt9v032 = | |
633 | container_of(ctrl->handler, struct mt9v032, ctrls); | |
80b44ef2 | 634 | struct regmap *map = mt9v032->regmap; |
e9a50e4c | 635 | u32 freq; |
0f2ce168 DC |
636 | u16 data; |
637 | ||
638 | switch (ctrl->id) { | |
639 | case V4L2_CID_AUTOGAIN: | |
640 | return mt9v032_update_aec_agc(mt9v032, MT9V032_AGC_ENABLE, | |
641 | ctrl->val); | |
642 | ||
643 | case V4L2_CID_GAIN: | |
80b44ef2 | 644 | return regmap_write(map, MT9V032_ANALOG_GAIN, ctrl->val); |
0f2ce168 DC |
645 | |
646 | case V4L2_CID_EXPOSURE_AUTO: | |
647 | return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, | |
5c375981 | 648 | !ctrl->val); |
0f2ce168 DC |
649 | |
650 | case V4L2_CID_EXPOSURE: | |
80b44ef2 PZ |
651 | return regmap_write(map, MT9V032_TOTAL_SHUTTER_WIDTH, |
652 | ctrl->val); | |
0f2ce168 | 653 | |
9ec670e2 LP |
654 | case V4L2_CID_HBLANK: |
655 | mt9v032->hblank = ctrl->val; | |
656 | return mt9v032_update_hblank(mt9v032); | |
657 | ||
658 | case V4L2_CID_VBLANK: | |
80b44ef2 PZ |
659 | return regmap_write(map, MT9V032_VERTICAL_BLANKING, |
660 | ctrl->val); | |
9ec670e2 | 661 | |
e9a50e4c LP |
662 | case V4L2_CID_PIXEL_RATE: |
663 | case V4L2_CID_LINK_FREQ: | |
664 | if (mt9v032->link_freq == NULL) | |
665 | break; | |
666 | ||
667 | freq = mt9v032->pdata->link_freqs[mt9v032->link_freq->val]; | |
2a9ec373 | 668 | *mt9v032->pixel_rate->p_new.p_s64 = freq; |
e9a50e4c LP |
669 | mt9v032->sysclk = freq; |
670 | break; | |
671 | ||
0f2ce168 | 672 | case V4L2_CID_TEST_PATTERN: |
b28d7017 | 673 | switch (mt9v032->test_pattern->val) { |
0f2ce168 DC |
674 | case 0: |
675 | data = 0; | |
676 | break; | |
677 | case 1: | |
678 | data = MT9V032_TEST_PATTERN_GRAY_VERTICAL | |
679 | | MT9V032_TEST_PATTERN_ENABLE; | |
680 | break; | |
681 | case 2: | |
682 | data = MT9V032_TEST_PATTERN_GRAY_HORIZONTAL | |
683 | | MT9V032_TEST_PATTERN_ENABLE; | |
684 | break; | |
685 | case 3: | |
686 | data = MT9V032_TEST_PATTERN_GRAY_DIAGONAL | |
687 | | MT9V032_TEST_PATTERN_ENABLE; | |
688 | break; | |
689 | default: | |
b28d7017 LP |
690 | data = (mt9v032->test_pattern_color->val << |
691 | MT9V032_TEST_PATTERN_DATA_SHIFT) | |
0f2ce168 DC |
692 | | MT9V032_TEST_PATTERN_USE_DATA |
693 | | MT9V032_TEST_PATTERN_ENABLE | |
694 | | MT9V032_TEST_PATTERN_FLIP; | |
695 | break; | |
696 | } | |
80b44ef2 | 697 | return regmap_write(map, MT9V032_TEST_PATTERN, data); |
81ea4829 MP |
698 | |
699 | case V4L2_CID_AEGC_DESIRED_BIN: | |
700 | return regmap_write(map, MT9V032_AEGC_DESIRED_BIN, ctrl->val); | |
701 | ||
702 | case V4L2_CID_AEC_LPF: | |
703 | return regmap_write(map, MT9V032_AEC_LPF, ctrl->val); | |
704 | ||
705 | case V4L2_CID_AGC_LPF: | |
706 | return regmap_write(map, MT9V032_AGC_LPF, ctrl->val); | |
707 | ||
708 | case V4L2_CID_AEC_UPDATE_INTERVAL: | |
709 | return regmap_write(map, MT9V032_AEC_UPDATE_FREQUENCY, | |
710 | ctrl->val); | |
711 | ||
712 | case V4L2_CID_AGC_UPDATE_INTERVAL: | |
713 | return regmap_write(map, MT9V032_AGC_UPDATE_FREQUENCY, | |
714 | ctrl->val); | |
715 | ||
716 | case V4L2_CID_AEC_MAX_SHUTTER_WIDTH: | |
717 | return regmap_write(map, | |
718 | mt9v032->model->data->aec_max_shutter_reg, | |
719 | ctrl->val); | |
0f2ce168 DC |
720 | } |
721 | ||
722 | return 0; | |
723 | } | |
724 | ||
217bdb07 | 725 | static const struct v4l2_ctrl_ops mt9v032_ctrl_ops = { |
0f2ce168 DC |
726 | .s_ctrl = mt9v032_s_ctrl, |
727 | }; | |
728 | ||
b28d7017 LP |
729 | static const char * const mt9v032_test_pattern_menu[] = { |
730 | "Disabled", | |
731 | "Gray Vertical Shade", | |
732 | "Gray Horizontal Shade", | |
733 | "Gray Diagonal Shade", | |
734 | "Plain", | |
735 | }; | |
736 | ||
737 | static const struct v4l2_ctrl_config mt9v032_test_pattern_color = { | |
738 | .ops = &mt9v032_ctrl_ops, | |
739 | .id = V4L2_CID_TEST_PATTERN_COLOR, | |
740 | .type = V4L2_CTRL_TYPE_INTEGER, | |
741 | .name = "Test Pattern Color", | |
742 | .min = 0, | |
743 | .max = 1023, | |
744 | .step = 1, | |
745 | .def = 0, | |
746 | .flags = 0, | |
0f2ce168 DC |
747 | }; |
748 | ||
81ea4829 MP |
749 | static const struct v4l2_ctrl_config mt9v032_aegc_controls[] = { |
750 | { | |
751 | .ops = &mt9v032_ctrl_ops, | |
752 | .id = V4L2_CID_AEGC_DESIRED_BIN, | |
753 | .type = V4L2_CTRL_TYPE_INTEGER, | |
754 | .name = "AEC/AGC Desired Bin", | |
755 | .min = 1, | |
756 | .max = 64, | |
757 | .step = 1, | |
758 | .def = 58, | |
759 | .flags = 0, | |
760 | }, { | |
761 | .ops = &mt9v032_ctrl_ops, | |
762 | .id = V4L2_CID_AEC_LPF, | |
763 | .type = V4L2_CTRL_TYPE_INTEGER, | |
764 | .name = "AEC Low Pass Filter", | |
765 | .min = 0, | |
766 | .max = 2, | |
767 | .step = 1, | |
768 | .def = 0, | |
769 | .flags = 0, | |
770 | }, { | |
771 | .ops = &mt9v032_ctrl_ops, | |
772 | .id = V4L2_CID_AGC_LPF, | |
773 | .type = V4L2_CTRL_TYPE_INTEGER, | |
774 | .name = "AGC Low Pass Filter", | |
775 | .min = 0, | |
776 | .max = 2, | |
777 | .step = 1, | |
778 | .def = 2, | |
779 | .flags = 0, | |
780 | }, { | |
781 | .ops = &mt9v032_ctrl_ops, | |
782 | .id = V4L2_CID_AEC_UPDATE_INTERVAL, | |
783 | .type = V4L2_CTRL_TYPE_INTEGER, | |
784 | .name = "AEC Update Interval", | |
785 | .min = 0, | |
786 | .max = 16, | |
787 | .step = 1, | |
788 | .def = 2, | |
789 | .flags = 0, | |
790 | }, { | |
791 | .ops = &mt9v032_ctrl_ops, | |
792 | .id = V4L2_CID_AGC_UPDATE_INTERVAL, | |
793 | .type = V4L2_CTRL_TYPE_INTEGER, | |
794 | .name = "AGC Update Interval", | |
795 | .min = 0, | |
796 | .max = 16, | |
797 | .step = 1, | |
798 | .def = 2, | |
799 | .flags = 0, | |
800 | } | |
801 | }; | |
802 | ||
803 | static const struct v4l2_ctrl_config mt9v032_aec_max_shutter_width = { | |
804 | .ops = &mt9v032_ctrl_ops, | |
805 | .id = V4L2_CID_AEC_MAX_SHUTTER_WIDTH, | |
806 | .type = V4L2_CTRL_TYPE_INTEGER, | |
807 | .name = "AEC Max Shutter Width", | |
808 | .min = 1, | |
809 | .max = 2047, | |
810 | .step = 1, | |
811 | .def = 480, | |
812 | .flags = 0, | |
813 | }; | |
814 | ||
815 | static const struct v4l2_ctrl_config mt9v034_aec_max_shutter_width = { | |
816 | .ops = &mt9v032_ctrl_ops, | |
817 | .id = V4L2_CID_AEC_MAX_SHUTTER_WIDTH, | |
818 | .type = V4L2_CTRL_TYPE_INTEGER, | |
819 | .name = "AEC Max Shutter Width", | |
820 | .min = 1, | |
821 | .max = 32765, | |
822 | .step = 1, | |
823 | .def = 480, | |
824 | .flags = 0, | |
825 | }; | |
826 | ||
0f2ce168 DC |
827 | /* ----------------------------------------------------------------------------- |
828 | * V4L2 subdev core operations | |
829 | */ | |
830 | ||
831 | static int mt9v032_set_power(struct v4l2_subdev *subdev, int on) | |
832 | { | |
833 | struct mt9v032 *mt9v032 = to_mt9v032(subdev); | |
834 | int ret = 0; | |
835 | ||
836 | mutex_lock(&mt9v032->power_lock); | |
837 | ||
838 | /* If the power count is modified from 0 to != 0 or from != 0 to 0, | |
839 | * update the power state. | |
840 | */ | |
841 | if (mt9v032->power_count == !on) { | |
842 | ret = __mt9v032_set_power(mt9v032, !!on); | |
843 | if (ret < 0) | |
844 | goto done; | |
845 | } | |
846 | ||
847 | /* Update the power count. */ | |
848 | mt9v032->power_count += on ? 1 : -1; | |
849 | WARN_ON(mt9v032->power_count < 0); | |
850 | ||
851 | done: | |
852 | mutex_unlock(&mt9v032->power_lock); | |
853 | return ret; | |
854 | } | |
855 | ||
856 | /* ----------------------------------------------------------------------------- | |
857 | * V4L2 subdev internal operations | |
858 | */ | |
859 | ||
860 | static int mt9v032_registered(struct v4l2_subdev *subdev) | |
861 | { | |
862 | struct i2c_client *client = v4l2_get_subdevdata(subdev); | |
863 | struct mt9v032 *mt9v032 = to_mt9v032(subdev); | |
0a466b60 | 864 | unsigned int i; |
80b44ef2 | 865 | u32 version; |
0f2ce168 DC |
866 | int ret; |
867 | ||
868 | dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", | |
869 | client->addr); | |
870 | ||
871 | ret = mt9v032_power_on(mt9v032); | |
872 | if (ret < 0) { | |
873 | dev_err(&client->dev, "MT9V032 power up failed\n"); | |
874 | return ret; | |
875 | } | |
876 | ||
877 | /* Read and check the sensor version */ | |
80b44ef2 PZ |
878 | ret = regmap_read(mt9v032->regmap, MT9V032_CHIP_VERSION, &version); |
879 | if (ret < 0) { | |
0a466b60 | 880 | dev_err(&client->dev, "Failed reading chip version\n"); |
80b44ef2 | 881 | return ret; |
0a466b60 LP |
882 | } |
883 | ||
884 | for (i = 0; i < ARRAY_SIZE(mt9v032_versions); ++i) { | |
885 | if (mt9v032_versions[i].version == version) { | |
886 | mt9v032->version = &mt9v032_versions[i]; | |
887 | break; | |
888 | } | |
889 | } | |
890 | ||
891 | if (mt9v032->version == NULL) { | |
892 | dev_err(&client->dev, "Unsupported chip version 0x%04x\n", | |
893 | version); | |
0f2ce168 DC |
894 | return -ENODEV; |
895 | } | |
896 | ||
897 | mt9v032_power_off(mt9v032); | |
898 | ||
0a466b60 LP |
899 | dev_info(&client->dev, "%s detected at address 0x%02x\n", |
900 | mt9v032->version->name, client->addr); | |
0f2ce168 | 901 | |
637f005e | 902 | mt9v032_configure_pixel_rate(mt9v032); |
41a33a00 | 903 | |
0f2ce168 DC |
904 | return ret; |
905 | } | |
906 | ||
907 | static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) | |
908 | { | |
220ddc7f | 909 | struct mt9v032 *mt9v032 = to_mt9v032(subdev); |
0f2ce168 DC |
910 | struct v4l2_mbus_framefmt *format; |
911 | struct v4l2_rect *crop; | |
912 | ||
f7234138 | 913 | crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0); |
0f2ce168 DC |
914 | crop->left = MT9V032_COLUMN_START_DEF; |
915 | crop->top = MT9V032_ROW_START_DEF; | |
916 | crop->width = MT9V032_WINDOW_WIDTH_DEF; | |
917 | crop->height = MT9V032_WINDOW_HEIGHT_DEF; | |
918 | ||
f7234138 | 919 | format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); |
220ddc7f LP |
920 | |
921 | if (mt9v032->model->color) | |
f5fe58fd | 922 | format->code = MEDIA_BUS_FMT_SGRBG10_1X10; |
220ddc7f | 923 | else |
f5fe58fd | 924 | format->code = MEDIA_BUS_FMT_Y10_1X10; |
220ddc7f | 925 | |
0f2ce168 DC |
926 | format->width = MT9V032_WINDOW_WIDTH_DEF; |
927 | format->height = MT9V032_WINDOW_HEIGHT_DEF; | |
928 | format->field = V4L2_FIELD_NONE; | |
929 | format->colorspace = V4L2_COLORSPACE_SRGB; | |
930 | ||
931 | return mt9v032_set_power(subdev, 1); | |
932 | } | |
933 | ||
934 | static int mt9v032_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) | |
935 | { | |
936 | return mt9v032_set_power(subdev, 0); | |
937 | } | |
938 | ||
7c137c60 | 939 | static const struct v4l2_subdev_core_ops mt9v032_subdev_core_ops = { |
0f2ce168 DC |
940 | .s_power = mt9v032_set_power, |
941 | }; | |
942 | ||
7c137c60 | 943 | static const struct v4l2_subdev_video_ops mt9v032_subdev_video_ops = { |
0f2ce168 DC |
944 | .s_stream = mt9v032_s_stream, |
945 | }; | |
946 | ||
7c137c60 | 947 | static const struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = { |
0f2ce168 DC |
948 | .enum_mbus_code = mt9v032_enum_mbus_code, |
949 | .enum_frame_size = mt9v032_enum_frame_size, | |
950 | .get_fmt = mt9v032_get_format, | |
951 | .set_fmt = mt9v032_set_format, | |
1a023feb HV |
952 | .get_selection = mt9v032_get_selection, |
953 | .set_selection = mt9v032_set_selection, | |
0f2ce168 DC |
954 | }; |
955 | ||
7c137c60 | 956 | static const struct v4l2_subdev_ops mt9v032_subdev_ops = { |
0f2ce168 DC |
957 | .core = &mt9v032_subdev_core_ops, |
958 | .video = &mt9v032_subdev_video_ops, | |
959 | .pad = &mt9v032_subdev_pad_ops, | |
960 | }; | |
961 | ||
962 | static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = { | |
963 | .registered = mt9v032_registered, | |
964 | .open = mt9v032_open, | |
965 | .close = mt9v032_close, | |
966 | }; | |
967 | ||
80b44ef2 PZ |
968 | static const struct regmap_config mt9v032_regmap_config = { |
969 | .reg_bits = 8, | |
970 | .val_bits = 16, | |
971 | .max_register = 0xff, | |
972 | .cache_type = REGCACHE_RBTREE, | |
973 | }; | |
974 | ||
0f2ce168 DC |
975 | /* ----------------------------------------------------------------------------- |
976 | * Driver initialization and probing | |
977 | */ | |
978 | ||
f2272e13 LP |
979 | static struct mt9v032_platform_data * |
980 | mt9v032_get_pdata(struct i2c_client *client) | |
981 | { | |
8e8a6b23 | 982 | struct mt9v032_platform_data *pdata = NULL; |
859969b3 | 983 | struct v4l2_fwnode_endpoint endpoint; |
f2272e13 LP |
984 | struct device_node *np; |
985 | struct property *prop; | |
986 | ||
987 | if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) | |
988 | return client->dev.platform_data; | |
989 | ||
990 | np = of_graph_get_next_endpoint(client->dev.of_node, NULL); | |
991 | if (!np) | |
992 | return NULL; | |
993 | ||
859969b3 | 994 | if (v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &endpoint) < 0) |
f2272e13 LP |
995 | goto done; |
996 | ||
997 | pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); | |
998 | if (!pdata) | |
999 | goto done; | |
1000 | ||
1001 | prop = of_find_property(np, "link-frequencies", NULL); | |
1002 | if (prop) { | |
1003 | u64 *link_freqs; | |
1004 | size_t size = prop->length / sizeof(*link_freqs); | |
1005 | ||
1006 | link_freqs = devm_kcalloc(&client->dev, size, | |
1007 | sizeof(*link_freqs), GFP_KERNEL); | |
1008 | if (!link_freqs) | |
1009 | goto done; | |
1010 | ||
1011 | if (of_property_read_u64_array(np, "link-frequencies", | |
1012 | link_freqs, size) < 0) | |
1013 | goto done; | |
1014 | ||
1015 | pdata->link_freqs = link_freqs; | |
1016 | pdata->link_def_freq = link_freqs[0]; | |
1017 | } | |
1018 | ||
1019 | pdata->clk_pol = !!(endpoint.bus.parallel.flags & | |
1020 | V4L2_MBUS_PCLK_SAMPLE_RISING); | |
1021 | ||
1022 | done: | |
1023 | of_node_put(np); | |
1024 | return pdata; | |
1025 | } | |
1026 | ||
0f2ce168 DC |
1027 | static int mt9v032_probe(struct i2c_client *client, |
1028 | const struct i2c_device_id *did) | |
1029 | { | |
f2272e13 | 1030 | struct mt9v032_platform_data *pdata = mt9v032_get_pdata(client); |
0f2ce168 DC |
1031 | struct mt9v032 *mt9v032; |
1032 | unsigned int i; | |
1033 | int ret; | |
1034 | ||
c02b211d | 1035 | mt9v032 = devm_kzalloc(&client->dev, sizeof(*mt9v032), GFP_KERNEL); |
0f2ce168 DC |
1036 | if (!mt9v032) |
1037 | return -ENOMEM; | |
1038 | ||
80b44ef2 PZ |
1039 | mt9v032->regmap = devm_regmap_init_i2c(client, &mt9v032_regmap_config); |
1040 | if (IS_ERR(mt9v032->regmap)) | |
1041 | return PTR_ERR(mt9v032->regmap); | |
1042 | ||
3300a8fd LP |
1043 | mt9v032->clk = devm_clk_get(&client->dev, NULL); |
1044 | if (IS_ERR(mt9v032->clk)) | |
1045 | return PTR_ERR(mt9v032->clk); | |
1046 | ||
28d5bdbe MP |
1047 | mt9v032->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", |
1048 | GPIOD_OUT_HIGH); | |
1049 | if (IS_ERR(mt9v032->reset_gpio)) | |
1050 | return PTR_ERR(mt9v032->reset_gpio); | |
1051 | ||
1052 | mt9v032->standby_gpio = devm_gpiod_get_optional(&client->dev, "standby", | |
1053 | GPIOD_OUT_LOW); | |
1054 | if (IS_ERR(mt9v032->standby_gpio)) | |
1055 | return PTR_ERR(mt9v032->standby_gpio); | |
1056 | ||
0f2ce168 | 1057 | mutex_init(&mt9v032->power_lock); |
e9a50e4c | 1058 | mt9v032->pdata = pdata; |
220ddc7f | 1059 | mt9v032->model = (const void *)did->driver_data; |
0f2ce168 | 1060 | |
81ea4829 MP |
1061 | v4l2_ctrl_handler_init(&mt9v032->ctrls, 11 + |
1062 | ARRAY_SIZE(mt9v032_aegc_controls)); | |
0f2ce168 DC |
1063 | |
1064 | v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, | |
1065 | V4L2_CID_AUTOGAIN, 0, 1, 1, 1); | |
1066 | v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, | |
1067 | V4L2_CID_GAIN, MT9V032_ANALOG_GAIN_MIN, | |
1068 | MT9V032_ANALOG_GAIN_MAX, 1, MT9V032_ANALOG_GAIN_DEF); | |
1069 | v4l2_ctrl_new_std_menu(&mt9v032->ctrls, &mt9v032_ctrl_ops, | |
1070 | V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, | |
1071 | V4L2_EXPOSURE_AUTO); | |
1072 | v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, | |
0a466b60 LP |
1073 | V4L2_CID_EXPOSURE, mt9v032->model->data->min_shutter, |
1074 | mt9v032->model->data->max_shutter, 1, | |
0f2ce168 | 1075 | MT9V032_TOTAL_SHUTTER_WIDTH_DEF); |
9ec670e2 | 1076 | v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, |
0a466b60 | 1077 | V4L2_CID_HBLANK, mt9v032->model->data->min_hblank, |
9ec670e2 LP |
1078 | MT9V032_HORIZONTAL_BLANKING_MAX, 1, |
1079 | MT9V032_HORIZONTAL_BLANKING_DEF); | |
1080 | v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, | |
0a466b60 LP |
1081 | V4L2_CID_VBLANK, mt9v032->model->data->min_vblank, |
1082 | mt9v032->model->data->max_vblank, 1, | |
9ec670e2 | 1083 | MT9V032_VERTICAL_BLANKING_DEF); |
b28d7017 LP |
1084 | mt9v032->test_pattern = v4l2_ctrl_new_std_menu_items(&mt9v032->ctrls, |
1085 | &mt9v032_ctrl_ops, V4L2_CID_TEST_PATTERN, | |
1086 | ARRAY_SIZE(mt9v032_test_pattern_menu) - 1, 0, 0, | |
1087 | mt9v032_test_pattern_menu); | |
1088 | mt9v032->test_pattern_color = v4l2_ctrl_new_custom(&mt9v032->ctrls, | |
1089 | &mt9v032_test_pattern_color, NULL); | |
1090 | ||
81ea4829 MP |
1091 | v4l2_ctrl_new_custom(&mt9v032->ctrls, |
1092 | mt9v032->model->data->aec_max_shutter_v4l2_ctrl, | |
1093 | NULL); | |
1094 | for (i = 0; i < ARRAY_SIZE(mt9v032_aegc_controls); ++i) | |
1095 | v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_aegc_controls[i], | |
1096 | NULL); | |
1097 | ||
b28d7017 | 1098 | v4l2_ctrl_cluster(2, &mt9v032->test_pattern); |
e9a50e4c | 1099 | |
41a33a00 SA |
1100 | mt9v032->pixel_rate = |
1101 | v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, | |
0ba2aeb6 | 1102 | V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); |
0f2ce168 | 1103 | |
e9a50e4c LP |
1104 | if (pdata && pdata->link_freqs) { |
1105 | unsigned int def = 0; | |
1106 | ||
1107 | for (i = 0; pdata->link_freqs[i]; ++i) { | |
1108 | if (pdata->link_freqs[i] == pdata->link_def_freq) | |
1109 | def = i; | |
1110 | } | |
1111 | ||
1112 | mt9v032->link_freq = | |
1113 | v4l2_ctrl_new_int_menu(&mt9v032->ctrls, | |
1114 | &mt9v032_ctrl_ops, | |
1115 | V4L2_CID_LINK_FREQ, i - 1, def, | |
1116 | pdata->link_freqs); | |
1117 | v4l2_ctrl_cluster(2, &mt9v032->link_freq); | |
1118 | } | |
1119 | ||
0f2ce168 DC |
1120 | |
1121 | mt9v032->subdev.ctrl_handler = &mt9v032->ctrls; | |
1122 | ||
2d01209f LP |
1123 | if (mt9v032->ctrls.error) { |
1124 | dev_err(&client->dev, "control initialization error %d\n", | |
1125 | mt9v032->ctrls.error); | |
1126 | ret = mt9v032->ctrls.error; | |
1127 | goto err; | |
1128 | } | |
0f2ce168 DC |
1129 | |
1130 | mt9v032->crop.left = MT9V032_COLUMN_START_DEF; | |
1131 | mt9v032->crop.top = MT9V032_ROW_START_DEF; | |
1132 | mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF; | |
1133 | mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF; | |
1134 | ||
220ddc7f | 1135 | if (mt9v032->model->color) |
f5fe58fd | 1136 | mt9v032->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; |
220ddc7f | 1137 | else |
f5fe58fd | 1138 | mt9v032->format.code = MEDIA_BUS_FMT_Y10_1X10; |
220ddc7f | 1139 | |
0f2ce168 DC |
1140 | mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF; |
1141 | mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF; | |
1142 | mt9v032->format.field = V4L2_FIELD_NONE; | |
1143 | mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB; | |
1144 | ||
637f005e LP |
1145 | mt9v032->hratio = 1; |
1146 | mt9v032->vratio = 1; | |
1147 | ||
0f2ce168 | 1148 | mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE; |
9ec670e2 | 1149 | mt9v032->hblank = MT9V032_HORIZONTAL_BLANKING_DEF; |
e9a50e4c | 1150 | mt9v032->sysclk = MT9V032_SYSCLK_FREQ_DEF; |
0f2ce168 DC |
1151 | |
1152 | v4l2_i2c_subdev_init(&mt9v032->subdev, client, &mt9v032_subdev_ops); | |
1153 | mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops; | |
1154 | mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | |
1155 | ||
1156 | mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; | |
ab22e77c | 1157 | ret = media_entity_pads_init(&mt9v032->subdev.entity, 1, &mt9v032->pad); |
94b76ce8 PZ |
1158 | if (ret < 0) |
1159 | goto err; | |
9462550f | 1160 | |
94b76ce8 PZ |
1161 | mt9v032->subdev.dev = &client->dev; |
1162 | ret = v4l2_async_register_subdev(&mt9v032->subdev); | |
c02b211d | 1163 | if (ret < 0) |
94b76ce8 PZ |
1164 | goto err; |
1165 | ||
1166 | return 0; | |
0f2ce168 | 1167 | |
94b76ce8 PZ |
1168 | err: |
1169 | media_entity_cleanup(&mt9v032->subdev.entity); | |
1170 | v4l2_ctrl_handler_free(&mt9v032->ctrls); | |
0f2ce168 DC |
1171 | return ret; |
1172 | } | |
1173 | ||
1174 | static int mt9v032_remove(struct i2c_client *client) | |
1175 | { | |
1176 | struct v4l2_subdev *subdev = i2c_get_clientdata(client); | |
1177 | struct mt9v032 *mt9v032 = to_mt9v032(subdev); | |
1178 | ||
94b76ce8 | 1179 | v4l2_async_unregister_subdev(subdev); |
9462550f | 1180 | v4l2_ctrl_handler_free(&mt9v032->ctrls); |
0f2ce168 | 1181 | media_entity_cleanup(&subdev->entity); |
9462550f | 1182 | |
0f2ce168 DC |
1183 | return 0; |
1184 | } | |
1185 | ||
81ea4829 MP |
1186 | static const struct mt9v032_model_data mt9v032_model_data[] = { |
1187 | { | |
1188 | /* MT9V022, MT9V032 revisions 1/2/3 */ | |
1189 | .min_row_time = 660, | |
1190 | .min_hblank = MT9V032_HORIZONTAL_BLANKING_MIN, | |
1191 | .min_vblank = MT9V032_VERTICAL_BLANKING_MIN, | |
1192 | .max_vblank = MT9V032_VERTICAL_BLANKING_MAX, | |
1193 | .min_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MIN, | |
1194 | .max_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MAX, | |
1195 | .pclk_reg = MT9V032_PIXEL_CLOCK, | |
1196 | .aec_max_shutter_reg = MT9V032_AEC_MAX_SHUTTER_WIDTH, | |
1197 | .aec_max_shutter_v4l2_ctrl = &mt9v032_aec_max_shutter_width, | |
1198 | }, { | |
1199 | /* MT9V024, MT9V034 */ | |
1200 | .min_row_time = 690, | |
1201 | .min_hblank = MT9V034_HORIZONTAL_BLANKING_MIN, | |
1202 | .min_vblank = MT9V034_VERTICAL_BLANKING_MIN, | |
1203 | .max_vblank = MT9V034_VERTICAL_BLANKING_MAX, | |
1204 | .min_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MIN, | |
1205 | .max_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MAX, | |
1206 | .pclk_reg = MT9V034_PIXEL_CLOCK, | |
1207 | .aec_max_shutter_reg = MT9V034_AEC_MAX_SHUTTER_WIDTH, | |
1208 | .aec_max_shutter_v4l2_ctrl = &mt9v034_aec_max_shutter_width, | |
1209 | }, | |
1210 | }; | |
1211 | ||
1212 | static const struct mt9v032_model_info mt9v032_models[] = { | |
1213 | [MT9V032_MODEL_V022_COLOR] = { | |
1214 | .data = &mt9v032_model_data[0], | |
1215 | .color = true, | |
1216 | }, | |
1217 | [MT9V032_MODEL_V022_MONO] = { | |
1218 | .data = &mt9v032_model_data[0], | |
1219 | .color = false, | |
1220 | }, | |
1221 | [MT9V032_MODEL_V024_COLOR] = { | |
1222 | .data = &mt9v032_model_data[1], | |
1223 | .color = true, | |
1224 | }, | |
1225 | [MT9V032_MODEL_V024_MONO] = { | |
1226 | .data = &mt9v032_model_data[1], | |
1227 | .color = false, | |
1228 | }, | |
1229 | [MT9V032_MODEL_V032_COLOR] = { | |
1230 | .data = &mt9v032_model_data[0], | |
1231 | .color = true, | |
1232 | }, | |
1233 | [MT9V032_MODEL_V032_MONO] = { | |
1234 | .data = &mt9v032_model_data[0], | |
1235 | .color = false, | |
1236 | }, | |
1237 | [MT9V032_MODEL_V034_COLOR] = { | |
1238 | .data = &mt9v032_model_data[1], | |
1239 | .color = true, | |
1240 | }, | |
1241 | [MT9V032_MODEL_V034_MONO] = { | |
1242 | .data = &mt9v032_model_data[1], | |
1243 | .color = false, | |
1244 | }, | |
1245 | }; | |
1246 | ||
0f2ce168 | 1247 | static const struct i2c_device_id mt9v032_id[] = { |
d8dde6c8 PZ |
1248 | { "mt9v022", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V022_COLOR] }, |
1249 | { "mt9v022m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V022_MONO] }, | |
1250 | { "mt9v024", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V024_COLOR] }, | |
1251 | { "mt9v024m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V024_MONO] }, | |
daecfebc LP |
1252 | { "mt9v032", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_COLOR] }, |
1253 | { "mt9v032m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_MONO] }, | |
1254 | { "mt9v034", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_COLOR] }, | |
1255 | { "mt9v034m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_MONO] }, | |
0f2ce168 DC |
1256 | { } |
1257 | }; | |
1258 | MODULE_DEVICE_TABLE(i2c, mt9v032_id); | |
1259 | ||
f2272e13 LP |
1260 | #if IS_ENABLED(CONFIG_OF) |
1261 | static const struct of_device_id mt9v032_of_match[] = { | |
1262 | { .compatible = "aptina,mt9v022" }, | |
1263 | { .compatible = "aptina,mt9v022m" }, | |
1264 | { .compatible = "aptina,mt9v024" }, | |
1265 | { .compatible = "aptina,mt9v024m" }, | |
1266 | { .compatible = "aptina,mt9v032" }, | |
1267 | { .compatible = "aptina,mt9v032m" }, | |
1268 | { .compatible = "aptina,mt9v034" }, | |
1269 | { .compatible = "aptina,mt9v034m" }, | |
1270 | { /* Sentinel */ } | |
1271 | }; | |
1272 | MODULE_DEVICE_TABLE(of, mt9v032_of_match); | |
1273 | #endif | |
1274 | ||
0f2ce168 DC |
1275 | static struct i2c_driver mt9v032_driver = { |
1276 | .driver = { | |
1277 | .name = "mt9v032", | |
f2272e13 | 1278 | .of_match_table = of_match_ptr(mt9v032_of_match), |
0f2ce168 DC |
1279 | }, |
1280 | .probe = mt9v032_probe, | |
1281 | .remove = mt9v032_remove, | |
1282 | .id_table = mt9v032_id, | |
1283 | }; | |
1284 | ||
c6e8d86f | 1285 | module_i2c_driver(mt9v032_driver); |
0f2ce168 DC |
1286 | |
1287 | MODULE_DESCRIPTION("Aptina MT9V032 Camera driver"); | |
1288 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); | |
1289 | MODULE_LICENSE("GPL"); |