diff options
Diffstat (limited to 'extras/recipes-kernel/linux/linux-omap-2.6.39/camera/0002-v4l-Add-mt9v032-sensor-driver.patch')
-rw-r--r-- | extras/recipes-kernel/linux/linux-omap-2.6.39/camera/0002-v4l-Add-mt9v032-sensor-driver.patch | 853 |
1 files changed, 853 insertions, 0 deletions
diff --git a/extras/recipes-kernel/linux/linux-omap-2.6.39/camera/0002-v4l-Add-mt9v032-sensor-driver.patch b/extras/recipes-kernel/linux/linux-omap-2.6.39/camera/0002-v4l-Add-mt9v032-sensor-driver.patch new file mode 100644 index 00000000..fb7cd205 --- /dev/null +++ b/extras/recipes-kernel/linux/linux-omap-2.6.39/camera/0002-v4l-Add-mt9v032-sensor-driver.patch | |||
@@ -0,0 +1,853 @@ | |||
1 | From ba65e798c98e9c4d331deb2b51337964336d3f78 Mon Sep 17 00:00:00 2001 | ||
2 | From: Detlev Casanova <detlev.casanova@gmail.com> | ||
3 | Date: Sun, 28 Nov 2010 19:07:20 +0100 | ||
4 | Subject: [PATCH 2/3] v4l: Add mt9v032 sensor driver | ||
5 | |||
6 | The MT9V032 is a parallel wide VGA sensor from Aptina (formerly Micron) | ||
7 | controlled through I2C. | ||
8 | |||
9 | The driver creates a V4L2 subdevice. It currently supports binning and | ||
10 | cropping, and the gain, auto gain, exposure, auto exposure and test | ||
11 | pattern controls. | ||
12 | |||
13 | Signed-off-by: Detlev Casanova <detlev.casanova@gmail.com> | ||
14 | Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
15 | --- | ||
16 | drivers/media/video/Kconfig | 7 + | ||
17 | drivers/media/video/Makefile | 1 + | ||
18 | drivers/media/video/mt9v032.c | 773 +++++++++++++++++++++++++++++++++++++++++ | ||
19 | include/media/mt9v032.h | 12 + | ||
20 | 4 files changed, 793 insertions(+), 0 deletions(-) | ||
21 | create mode 100644 drivers/media/video/mt9v032.c | ||
22 | create mode 100644 include/media/mt9v032.h | ||
23 | |||
24 | diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig | ||
25 | index cb87e35..3a5bc57 100644 | ||
26 | --- a/drivers/media/video/Kconfig | ||
27 | +++ b/drivers/media/video/Kconfig | ||
28 | @@ -344,6 +344,13 @@ config VIDEO_MT9V011 | ||
29 | mt0v011 1.3 Mpixel camera. It currently only works with the | ||
30 | em28xx driver. | ||
31 | |||
32 | +config VIDEO_MT9V032 | ||
33 | + tristate "Micron MT9V032 sensor support" | ||
34 | + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API | ||
35 | + ---help--- | ||
36 | + This is a Video4Linux2 sensor-level driver for the Micron | ||
37 | + MT9V032 752x480 CMOS sensor. | ||
38 | + | ||
39 | config VIDEO_TCM825X | ||
40 | tristate "TCM825x camera sensor support" | ||
41 | depends on I2C && VIDEO_V4L2 | ||
42 | diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile | ||
43 | index 912b29b..6679c6a 100644 | ||
44 | --- a/drivers/media/video/Makefile | ||
45 | +++ b/drivers/media/video/Makefile | ||
46 | @@ -67,6 +67,7 @@ obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o | ||
47 | obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o | ||
48 | obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o | ||
49 | obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o | ||
50 | +obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o | ||
51 | obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o | ||
52 | obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o | ||
53 | |||
54 | diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c | ||
55 | new file mode 100644 | ||
56 | index 0000000..c64e1dc | ||
57 | --- /dev/null | ||
58 | +++ b/drivers/media/video/mt9v032.c | ||
59 | @@ -0,0 +1,773 @@ | ||
60 | +/* | ||
61 | + * Driver for MT9V032 CMOS Image Sensor from Micron | ||
62 | + * | ||
63 | + * Copyright (C) 2010, Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
64 | + * | ||
65 | + * Based on the MT9M001 driver, | ||
66 | + * | ||
67 | + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> | ||
68 | + * | ||
69 | + * This program is free software; you can redistribute it and/or modify | ||
70 | + * it under the terms of the GNU General Public License version 2 as | ||
71 | + * published by the Free Software Foundation. | ||
72 | + */ | ||
73 | + | ||
74 | +#include <linux/delay.h> | ||
75 | +#include <linux/i2c.h> | ||
76 | +#include <linux/log2.h> | ||
77 | +#include <linux/mutex.h> | ||
78 | +#include <linux/slab.h> | ||
79 | +#include <linux/videodev2.h> | ||
80 | +#include <linux/v4l2-mediabus.h> | ||
81 | + | ||
82 | +#include <media/mt9v032.h> | ||
83 | +#include <media/v4l2-ctrls.h> | ||
84 | +#include <media/v4l2-device.h> | ||
85 | +#include <media/v4l2-subdev.h> | ||
86 | + | ||
87 | +#define MT9V032_PIXEL_ARRAY_HEIGHT 492 | ||
88 | +#define MT9V032_PIXEL_ARRAY_WIDTH 782 | ||
89 | + | ||
90 | +#define MT9V032_CHIP_VERSION 0x00 | ||
91 | +#define MT9V032_CHIP_ID_REV1 0x1311 | ||
92 | +#define MT9V032_CHIP_ID_REV3 0x1313 | ||
93 | +#define MT9V032_COLUMN_START 0x01 | ||
94 | +#define MT9V032_COLUMN_START_MIN 1 | ||
95 | +#define MT9V032_COLUMN_START_DEF 1 | ||
96 | +#define MT9V032_COLUMN_START_MAX 752 | ||
97 | +#define MT9V032_ROW_START 0x02 | ||
98 | +#define MT9V032_ROW_START_MIN 4 | ||
99 | +#define MT9V032_ROW_START_DEF 5 | ||
100 | +#define MT9V032_ROW_START_MAX 482 | ||
101 | +#define MT9V032_WINDOW_HEIGHT 0x03 | ||
102 | +#define MT9V032_WINDOW_HEIGHT_MIN 1 | ||
103 | +#define MT9V032_WINDOW_HEIGHT_DEF 480 | ||
104 | +#define MT9V032_WINDOW_HEIGHT_MAX 480 | ||
105 | +#define MT9V032_WINDOW_WIDTH 0x04 | ||
106 | +#define MT9V032_WINDOW_WIDTH_MIN 1 | ||
107 | +#define MT9V032_WINDOW_WIDTH_DEF 752 | ||
108 | +#define MT9V032_WINDOW_WIDTH_MAX 752 | ||
109 | +#define MT9V032_HORIZONTAL_BLANKING 0x05 | ||
110 | +#define MT9V032_HORIZONTAL_BLANKING_MIN 43 | ||
111 | +#define MT9V032_HORIZONTAL_BLANKING_MAX 1023 | ||
112 | +#define MT9V032_VERTICAL_BLANKING 0x06 | ||
113 | +#define MT9V032_VERTICAL_BLANKING_MIN 4 | ||
114 | +#define MT9V032_VERTICAL_BLANKING_MAX 3000 | ||
115 | +#define MT9V032_CHIP_CONTROL 0x07 | ||
116 | +#define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3) | ||
117 | +#define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7) | ||
118 | +#define MT9V032_CHIP_CONTROL_SEQUENTIAL (1 << 8) | ||
119 | +#define MT9V032_SHUTTER_WIDTH1 0x08 | ||
120 | +#define MT9V032_SHUTTER_WIDTH2 0x09 | ||
121 | +#define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a | ||
122 | +#define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b | ||
123 | +#define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1 | ||
124 | +#define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480 | ||
125 | +#define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767 | ||
126 | +#define MT9V032_RESET 0x0c | ||
127 | +#define MT9V032_READ_MODE 0x0d | ||
128 | +#define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0) | ||
129 | +#define MT9V032_READ_MODE_ROW_BIN_SHIFT 0 | ||
130 | +#define MT9V032_READ_MODE_COLUMN_BIN_MASK (3 << 2) | ||
131 | +#define MT9V032_READ_MODE_COLUMN_BIN_SHIFT 2 | ||
132 | +#define MT9V032_READ_MODE_ROW_FLIP (1 << 4) | ||
133 | +#define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5) | ||
134 | +#define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6) | ||
135 | +#define MT9V032_READ_MODE_DARK_ROWS (1 << 7) | ||
136 | +#define MT9V032_PIXEL_OPERATION_MODE 0x0f | ||
137 | +#define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2) | ||
138 | +#define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6) | ||
139 | +#define MT9V032_ANALOG_GAIN 0x35 | ||
140 | +#define MT9V032_ANALOG_GAIN_MIN 16 | ||
141 | +#define MT9V032_ANALOG_GAIN_DEF 16 | ||
142 | +#define MT9V032_ANALOG_GAIN_MAX 64 | ||
143 | +#define MT9V032_MAX_ANALOG_GAIN 0x36 | ||
144 | +#define MT9V032_MAX_ANALOG_GAIN_MAX 127 | ||
145 | +#define MT9V032_FRAME_DARK_AVERAGE 0x42 | ||
146 | +#define MT9V032_DARK_AVG_THRESH 0x46 | ||
147 | +#define MT9V032_DARK_AVG_LOW_THRESH_MASK (255 << 0) | ||
148 | +#define MT9V032_DARK_AVG_LOW_THRESH_SHIFT 0 | ||
149 | +#define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8) | ||
150 | +#define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8 | ||
151 | +#define MT9V032_ROW_NOISE_CORR_CONTROL 0x70 | ||
152 | +#define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5) | ||
153 | +#define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7) | ||
154 | +#define MT9V032_PIXEL_CLOCK 0x74 | ||
155 | +#define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0) | ||
156 | +#define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1) | ||
157 | +#define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2) | ||
158 | +#define MT9V032_PIXEL_CLOCK_CONT_LINE (1 << 3) | ||
159 | +#define MT9V032_PIXEL_CLOCK_INV_PXL_CLK (1 << 4) | ||
160 | +#define MT9V032_TEST_PATTERN 0x7f | ||
161 | +#define MT9V032_TEST_PATTERN_DATA_MASK (1023 << 0) | ||
162 | +#define MT9V032_TEST_PATTERN_DATA_SHIFT 0 | ||
163 | +#define MT9V032_TEST_PATTERN_USE_DATA (1 << 10) | ||
164 | +#define MT9V032_TEST_PATTERN_GRAY_MASK (3 << 11) | ||
165 | +#define MT9V032_TEST_PATTERN_GRAY_NONE (0 << 11) | ||
166 | +#define MT9V032_TEST_PATTERN_GRAY_VERTICAL (1 << 11) | ||
167 | +#define MT9V032_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11) | ||
168 | +#define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11) | ||
169 | +#define MT9V032_TEST_PATTERN_ENABLE (1 << 13) | ||
170 | +#define MT9V032_TEST_PATTERN_FLIP (1 << 14) | ||
171 | +#define MT9V032_AEC_AGC_ENABLE 0xaf | ||
172 | +#define MT9V032_AEC_ENABLE (1 << 0) | ||
173 | +#define MT9V032_AGC_ENABLE (1 << 1) | ||
174 | +#define MT9V032_THERMAL_INFO 0xc1 | ||
175 | + | ||
176 | +struct mt9v032 { | ||
177 | + struct v4l2_subdev subdev; | ||
178 | + struct media_pad pad; | ||
179 | + | ||
180 | + struct v4l2_mbus_framefmt format; | ||
181 | + struct v4l2_rect crop; | ||
182 | + | ||
183 | + struct v4l2_ctrl_handler ctrls; | ||
184 | + | ||
185 | + struct mutex power_lock; | ||
186 | + int power_count; | ||
187 | + | ||
188 | + struct mt9v032_platform_data *pdata; | ||
189 | + u16 chip_control; | ||
190 | + u16 aec_agc; | ||
191 | +}; | ||
192 | + | ||
193 | +static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd) | ||
194 | +{ | ||
195 | + return container_of(sd, struct mt9v032, subdev); | ||
196 | +} | ||
197 | + | ||
198 | +static int mt9v032_read(struct i2c_client *client, const u8 reg) | ||
199 | +{ | ||
200 | + s32 data = i2c_smbus_read_word_data(client, reg); | ||
201 | + dev_dbg(&client->dev, "%s: read 0x%04x from 0x%02x\n", __func__, | ||
202 | + swab16(data), reg); | ||
203 | + return data < 0 ? data : swab16(data); | ||
204 | +} | ||
205 | + | ||
206 | +static int mt9v032_write(struct i2c_client *client, const u8 reg, | ||
207 | + const u16 data) | ||
208 | +{ | ||
209 | + dev_dbg(&client->dev, "%s: writing 0x%04x to 0x%02x\n", __func__, | ||
210 | + data, reg); | ||
211 | + return i2c_smbus_write_word_data(client, reg, swab16(data)); | ||
212 | +} | ||
213 | + | ||
214 | +static int mt9v032_set_chip_control(struct mt9v032 *mt9v032, u16 clear, u16 set) | ||
215 | +{ | ||
216 | + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); | ||
217 | + u16 value = (mt9v032->chip_control & ~clear) | set; | ||
218 | + int ret; | ||
219 | + | ||
220 | + ret = mt9v032_write(client, MT9V032_CHIP_CONTROL, value); | ||
221 | + if (ret < 0) | ||
222 | + return ret; | ||
223 | + | ||
224 | + mt9v032->chip_control = value; | ||
225 | + return 0; | ||
226 | +} | ||
227 | + | ||
228 | +static int | ||
229 | +mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable) | ||
230 | +{ | ||
231 | + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); | ||
232 | + u16 value = mt9v032->aec_agc; | ||
233 | + int ret; | ||
234 | + | ||
235 | + if (enable) | ||
236 | + value |= which; | ||
237 | + else | ||
238 | + value &= ~which; | ||
239 | + | ||
240 | + ret = mt9v032_write(client, MT9V032_AEC_AGC_ENABLE, value); | ||
241 | + if (ret < 0) | ||
242 | + return ret; | ||
243 | + | ||
244 | + mt9v032->aec_agc = value; | ||
245 | + return 0; | ||
246 | +} | ||
247 | + | ||
248 | +static int mt9v032_power_on(struct mt9v032 *mt9v032) | ||
249 | +{ | ||
250 | + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); | ||
251 | + int ret; | ||
252 | + | ||
253 | + if (mt9v032->pdata->set_clock) { | ||
254 | + mt9v032->pdata->set_clock(&mt9v032->subdev, 25000000); | ||
255 | + udelay(1); | ||
256 | + } | ||
257 | + | ||
258 | + /* Reset the chip and stop data read out */ | ||
259 | + ret = mt9v032_write(client, MT9V032_RESET, 1); | ||
260 | + if (ret < 0) | ||
261 | + return ret; | ||
262 | + | ||
263 | + ret = mt9v032_write(client, MT9V032_RESET, 0); | ||
264 | + if (ret < 0) | ||
265 | + return ret; | ||
266 | + | ||
267 | + return mt9v032_write(client, MT9V032_CHIP_CONTROL, 0); | ||
268 | +} | ||
269 | + | ||
270 | +static void mt9v032_power_off(struct mt9v032 *mt9v032) | ||
271 | +{ | ||
272 | + if (mt9v032->pdata->set_clock) | ||
273 | + mt9v032->pdata->set_clock(&mt9v032->subdev, 0); | ||
274 | +} | ||
275 | + | ||
276 | +static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) | ||
277 | +{ | ||
278 | + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); | ||
279 | + int ret; | ||
280 | + | ||
281 | + if (!on) { | ||
282 | + mt9v032_power_off(mt9v032); | ||
283 | + return 0; | ||
284 | + } | ||
285 | + | ||
286 | + ret = mt9v032_power_on(mt9v032); | ||
287 | + if (ret < 0) | ||
288 | + return ret; | ||
289 | + | ||
290 | + /* Configure the pixel clock polarity */ | ||
291 | + if (mt9v032->pdata && mt9v032->pdata->clk_pol) { | ||
292 | + ret = mt9v032_write(client, MT9V032_PIXEL_CLOCK, | ||
293 | + MT9V032_PIXEL_CLOCK_INV_PXL_CLK); | ||
294 | + if (ret < 0) | ||
295 | + return ret; | ||
296 | + } | ||
297 | + | ||
298 | + /* Disable the noise correction algorithm and restore the controls. */ | ||
299 | + ret = mt9v032_write(client, MT9V032_ROW_NOISE_CORR_CONTROL, 0); | ||
300 | + if (ret < 0) | ||
301 | + return ret; | ||
302 | + | ||
303 | + return v4l2_ctrl_handler_setup(&mt9v032->ctrls); | ||
304 | +} | ||
305 | + | ||
306 | +/* ----------------------------------------------------------------------------- | ||
307 | + * V4L2 subdev video operations | ||
308 | + */ | ||
309 | + | ||
310 | +static struct v4l2_mbus_framefmt * | ||
311 | +__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, | ||
312 | + unsigned int pad, enum v4l2_subdev_format_whence which) | ||
313 | +{ | ||
314 | + switch (which) { | ||
315 | + case V4L2_SUBDEV_FORMAT_TRY: | ||
316 | + return v4l2_subdev_get_try_format(fh, pad); | ||
317 | + case V4L2_SUBDEV_FORMAT_ACTIVE: | ||
318 | + return &mt9v032->format; | ||
319 | + default: | ||
320 | + return NULL; | ||
321 | + } | ||
322 | +} | ||
323 | + | ||
324 | +static struct v4l2_rect * | ||
325 | +__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, | ||
326 | + unsigned int pad, enum v4l2_subdev_format_whence which) | ||
327 | +{ | ||
328 | + switch (which) { | ||
329 | + case V4L2_SUBDEV_FORMAT_TRY: | ||
330 | + return v4l2_subdev_get_try_crop(fh, pad); | ||
331 | + case V4L2_SUBDEV_FORMAT_ACTIVE: | ||
332 | + return &mt9v032->crop; | ||
333 | + default: | ||
334 | + return NULL; | ||
335 | + } | ||
336 | +} | ||
337 | + | ||
338 | +static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) | ||
339 | +{ | ||
340 | + const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE | ||
341 | + | MT9V032_CHIP_CONTROL_DOUT_ENABLE | ||
342 | + | MT9V032_CHIP_CONTROL_SEQUENTIAL; | ||
343 | + struct i2c_client *client = v4l2_get_subdevdata(subdev); | ||
344 | + struct mt9v032 *mt9v032 = to_mt9v032(subdev); | ||
345 | + struct v4l2_mbus_framefmt *format = &mt9v032->format; | ||
346 | + struct v4l2_rect *crop = &mt9v032->crop; | ||
347 | + unsigned int hratio; | ||
348 | + unsigned int vratio; | ||
349 | + int ret; | ||
350 | + | ||
351 | + if (!enable) | ||
352 | + return mt9v032_set_chip_control(mt9v032, mode, 0); | ||
353 | + | ||
354 | + /* Configure the window size and row/column bin */ | ||
355 | + hratio = DIV_ROUND_CLOSEST(crop->width, format->width); | ||
356 | + vratio = DIV_ROUND_CLOSEST(crop->height, format->height); | ||
357 | + | ||
358 | + ret = mt9v032_write(client, MT9V032_READ_MODE, | ||
359 | + (hratio - 1) << MT9V032_READ_MODE_ROW_BIN_SHIFT | | ||
360 | + (vratio - 1) << MT9V032_READ_MODE_COLUMN_BIN_SHIFT); | ||
361 | + if (ret < 0) | ||
362 | + return ret; | ||
363 | + | ||
364 | + ret = mt9v032_write(client, MT9V032_COLUMN_START, crop->left); | ||
365 | + if (ret < 0) | ||
366 | + return ret; | ||
367 | + | ||
368 | + ret = mt9v032_write(client, MT9V032_ROW_START, crop->top); | ||
369 | + if (ret < 0) | ||
370 | + return ret; | ||
371 | + | ||
372 | + ret = mt9v032_write(client, MT9V032_WINDOW_WIDTH, crop->width); | ||
373 | + if (ret < 0) | ||
374 | + return ret; | ||
375 | + | ||
376 | + ret = mt9v032_write(client, MT9V032_WINDOW_HEIGHT, crop->height); | ||
377 | + if (ret < 0) | ||
378 | + return ret; | ||
379 | + | ||
380 | + ret = mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, | ||
381 | + max(43, 660 - crop->width)); | ||
382 | + if (ret < 0) | ||
383 | + return ret; | ||
384 | + | ||
385 | + /* Switch to master "normal" mode */ | ||
386 | + return mt9v032_set_chip_control(mt9v032, 0, mode); | ||
387 | +} | ||
388 | + | ||
389 | +static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, | ||
390 | + struct v4l2_subdev_fh *fh, | ||
391 | + struct v4l2_subdev_mbus_code_enum *code) | ||
392 | +{ | ||
393 | + if (code->index > 0) | ||
394 | + return -EINVAL; | ||
395 | + | ||
396 | + code->code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
397 | + return 0; | ||
398 | +} | ||
399 | + | ||
400 | +static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, | ||
401 | + struct v4l2_subdev_fh *fh, | ||
402 | + struct v4l2_subdev_frame_size_enum *fse) | ||
403 | +{ | ||
404 | + if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) | ||
405 | + return -EINVAL; | ||
406 | + | ||
407 | + fse->min_width = MT9V032_WINDOW_WIDTH_DEF / fse->index; | ||
408 | + fse->max_width = fse->min_width; | ||
409 | + fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / fse->index; | ||
410 | + fse->max_height = fse->min_height; | ||
411 | + | ||
412 | + return 0; | ||
413 | +} | ||
414 | + | ||
415 | +static int mt9v032_get_format(struct v4l2_subdev *subdev, | ||
416 | + struct v4l2_subdev_fh *fh, | ||
417 | + struct v4l2_subdev_format *format) | ||
418 | +{ | ||
419 | + struct mt9v032 *mt9v032 = to_mt9v032(subdev); | ||
420 | + | ||
421 | + format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad, | ||
422 | + format->which); | ||
423 | + return 0; | ||
424 | +} | ||
425 | + | ||
426 | +static int mt9v032_set_format(struct v4l2_subdev *subdev, | ||
427 | + struct v4l2_subdev_fh *fh, | ||
428 | + struct v4l2_subdev_format *format) | ||
429 | +{ | ||
430 | + struct mt9v032 *mt9v032 = to_mt9v032(subdev); | ||
431 | + struct v4l2_mbus_framefmt *__format; | ||
432 | + struct v4l2_rect *__crop; | ||
433 | + unsigned int width; | ||
434 | + unsigned int height; | ||
435 | + unsigned int hratio; | ||
436 | + unsigned int vratio; | ||
437 | + | ||
438 | + __crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad, | ||
439 | + format->which); | ||
440 | + | ||
441 | + /* Clamp the width and height to avoid dividing by zero. */ | ||
442 | + width = clamp_t(unsigned int, ALIGN(format->format.width, 2), | ||
443 | + max(__crop->width / 8, MT9V032_WINDOW_WIDTH_MIN), | ||
444 | + __crop->width); | ||
445 | + height = clamp_t(unsigned int, ALIGN(format->format.height, 2), | ||
446 | + max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN), | ||
447 | + __crop->height); | ||
448 | + | ||
449 | + hratio = DIV_ROUND_CLOSEST(__crop->width, width); | ||
450 | + vratio = DIV_ROUND_CLOSEST(__crop->height, height); | ||
451 | + | ||
452 | + __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad, | ||
453 | + format->which); | ||
454 | + __format->width = __crop->width / hratio; | ||
455 | + __format->height = __crop->height / vratio; | ||
456 | + | ||
457 | + format->format = *__format; | ||
458 | + | ||
459 | + return 0; | ||
460 | +} | ||
461 | + | ||
462 | +static int mt9v032_get_crop(struct v4l2_subdev *subdev, | ||
463 | + struct v4l2_subdev_fh *fh, | ||
464 | + struct v4l2_subdev_crop *crop) | ||
465 | +{ | ||
466 | + struct mt9v032 *mt9v032 = to_mt9v032(subdev); | ||
467 | + | ||
468 | + crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad, | ||
469 | + crop->which); | ||
470 | + return 0; | ||
471 | +} | ||
472 | + | ||
473 | +static int mt9v032_set_crop(struct v4l2_subdev *subdev, | ||
474 | + struct v4l2_subdev_fh *fh, | ||
475 | + struct v4l2_subdev_crop *crop) | ||
476 | +{ | ||
477 | + struct mt9v032 *mt9v032 = to_mt9v032(subdev); | ||
478 | + struct v4l2_mbus_framefmt *__format; | ||
479 | + struct v4l2_rect *__crop; | ||
480 | + struct v4l2_rect rect; | ||
481 | + | ||
482 | + /* Clamp the crop rectangle boundaries and align them to a non multiple | ||
483 | + * of 2 pixels to ensure a GRBG Bayer pattern. | ||
484 | + */ | ||
485 | + rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1, | ||
486 | + MT9V032_COLUMN_START_MIN, | ||
487 | + MT9V032_COLUMN_START_MAX); | ||
488 | + rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1, | ||
489 | + MT9V032_ROW_START_MIN, | ||
490 | + MT9V032_ROW_START_MAX); | ||
491 | + rect.width = clamp(ALIGN(crop->rect.width, 2), | ||
492 | + MT9V032_WINDOW_WIDTH_MIN, | ||
493 | + MT9V032_WINDOW_WIDTH_MAX); | ||
494 | + rect.height = clamp(ALIGN(crop->rect.height, 2), | ||
495 | + MT9V032_WINDOW_HEIGHT_MIN, | ||
496 | + MT9V032_WINDOW_HEIGHT_MAX); | ||
497 | + | ||
498 | + rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); | ||
499 | + rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); | ||
500 | + | ||
501 | + __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which); | ||
502 | + | ||
503 | + if (rect.width != __crop->width || rect.height != __crop->height) { | ||
504 | + /* Reset the output image size if the crop rectangle size has | ||
505 | + * been modified. | ||
506 | + */ | ||
507 | + __format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad, | ||
508 | + crop->which); | ||
509 | + __format->width = rect.width; | ||
510 | + __format->height = rect.height; | ||
511 | + } | ||
512 | + | ||
513 | + *__crop = rect; | ||
514 | + crop->rect = rect; | ||
515 | + | ||
516 | + return 0; | ||
517 | +} | ||
518 | + | ||
519 | +/* ----------------------------------------------------------------------------- | ||
520 | + * V4L2 subdev control operations | ||
521 | + */ | ||
522 | + | ||
523 | +#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) | ||
524 | + | ||
525 | +static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) | ||
526 | +{ | ||
527 | + struct mt9v032 *mt9v032 = | ||
528 | + container_of(ctrl->handler, struct mt9v032, ctrls); | ||
529 | + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); | ||
530 | + u16 data; | ||
531 | + | ||
532 | + switch (ctrl->id) { | ||
533 | + case V4L2_CID_AUTOGAIN: | ||
534 | + return mt9v032_update_aec_agc(mt9v032, MT9V032_AGC_ENABLE, | ||
535 | + ctrl->val); | ||
536 | + | ||
537 | + case V4L2_CID_GAIN: | ||
538 | + return mt9v032_write(client, MT9V032_ANALOG_GAIN, ctrl->val); | ||
539 | + | ||
540 | + case V4L2_CID_EXPOSURE_AUTO: | ||
541 | + return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, | ||
542 | + ctrl->val); | ||
543 | + | ||
544 | + case V4L2_CID_EXPOSURE: | ||
545 | + return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH, | ||
546 | + ctrl->val); | ||
547 | + | ||
548 | + case V4L2_CID_TEST_PATTERN: | ||
549 | + switch (ctrl->val) { | ||
550 | + case 0: | ||
551 | + data = 0; | ||
552 | + break; | ||
553 | + case 1: | ||
554 | + data = MT9V032_TEST_PATTERN_GRAY_VERTICAL | ||
555 | + | MT9V032_TEST_PATTERN_ENABLE; | ||
556 | + break; | ||
557 | + case 2: | ||
558 | + data = MT9V032_TEST_PATTERN_GRAY_HORIZONTAL | ||
559 | + | MT9V032_TEST_PATTERN_ENABLE; | ||
560 | + break; | ||
561 | + case 3: | ||
562 | + data = MT9V032_TEST_PATTERN_GRAY_DIAGONAL | ||
563 | + | MT9V032_TEST_PATTERN_ENABLE; | ||
564 | + break; | ||
565 | + default: | ||
566 | + data = (ctrl->val << MT9V032_TEST_PATTERN_DATA_SHIFT) | ||
567 | + | MT9V032_TEST_PATTERN_USE_DATA | ||
568 | + | MT9V032_TEST_PATTERN_ENABLE | ||
569 | + | MT9V032_TEST_PATTERN_FLIP; | ||
570 | + break; | ||
571 | + } | ||
572 | + | ||
573 | + return mt9v032_write(client, MT9V032_TEST_PATTERN, data); | ||
574 | + } | ||
575 | + | ||
576 | + return 0; | ||
577 | +} | ||
578 | + | ||
579 | +static struct v4l2_ctrl_ops mt9v032_ctrl_ops = { | ||
580 | + .s_ctrl = mt9v032_s_ctrl, | ||
581 | +}; | ||
582 | + | ||
583 | +static const struct v4l2_ctrl_config mt9v032_ctrls[] = { | ||
584 | + { | ||
585 | + .ops = &mt9v032_ctrl_ops, | ||
586 | + .id = V4L2_CID_TEST_PATTERN, | ||
587 | + .type = V4L2_CTRL_TYPE_INTEGER, | ||
588 | + .name = "Test pattern", | ||
589 | + .min = 0, | ||
590 | + .max = 1023, | ||
591 | + .step = 1, | ||
592 | + .def = 0, | ||
593 | + .flags = 0, | ||
594 | + } | ||
595 | +}; | ||
596 | + | ||
597 | +/* ----------------------------------------------------------------------------- | ||
598 | + * V4L2 subdev core operations | ||
599 | + */ | ||
600 | + | ||
601 | +static int mt9v032_set_power(struct v4l2_subdev *subdev, int on) | ||
602 | +{ | ||
603 | + struct mt9v032 *mt9v032 = to_mt9v032(subdev); | ||
604 | + int ret = 0; | ||
605 | + | ||
606 | + mutex_lock(&mt9v032->power_lock); | ||
607 | + | ||
608 | + /* If the power count is modified from 0 to != 0 or from != 0 to 0, | ||
609 | + * update the power state. | ||
610 | + */ | ||
611 | + if (mt9v032->power_count == !on) { | ||
612 | + ret = __mt9v032_set_power(mt9v032, !!on); | ||
613 | + if (ret < 0) | ||
614 | + goto done; | ||
615 | + } | ||
616 | + | ||
617 | + /* Update the power count. */ | ||
618 | + mt9v032->power_count += on ? 1 : -1; | ||
619 | + WARN_ON(mt9v032->power_count < 0); | ||
620 | + | ||
621 | +done: | ||
622 | + mutex_unlock(&mt9v032->power_lock); | ||
623 | + return ret; | ||
624 | +} | ||
625 | + | ||
626 | +/* ----------------------------------------------------------------------------- | ||
627 | + * V4L2 subdev internal operations | ||
628 | + */ | ||
629 | + | ||
630 | +static int mt9v032_registered(struct v4l2_subdev *subdev) | ||
631 | +{ | ||
632 | + struct i2c_client *client = v4l2_get_subdevdata(subdev); | ||
633 | + struct mt9v032 *mt9v032 = to_mt9v032(subdev); | ||
634 | + s32 data; | ||
635 | + int ret; | ||
636 | + | ||
637 | + dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", | ||
638 | + client->addr); | ||
639 | + | ||
640 | + ret = mt9v032_power_on(mt9v032); | ||
641 | + if (ret < 0) { | ||
642 | + dev_err(&client->dev, "MT9V032 power up failed\n"); | ||
643 | + return ret; | ||
644 | + } | ||
645 | + | ||
646 | + /* Read and check the sensor version */ | ||
647 | + data = mt9v032_read(client, MT9V032_CHIP_VERSION); | ||
648 | + if (data != MT9V032_CHIP_ID_REV1 && data != MT9V032_CHIP_ID_REV3) { | ||
649 | + dev_err(&client->dev, "MT9V032 not detected, wrong version " | ||
650 | + "0x%04x\n", data); | ||
651 | + return -ENODEV; | ||
652 | + } | ||
653 | + | ||
654 | + mt9v032_power_off(mt9v032); | ||
655 | + | ||
656 | + dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n", | ||
657 | + client->addr); | ||
658 | + | ||
659 | + return ret; | ||
660 | +} | ||
661 | + | ||
662 | +static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) | ||
663 | +{ | ||
664 | + struct v4l2_mbus_framefmt *format; | ||
665 | + struct v4l2_rect *crop; | ||
666 | + | ||
667 | + crop = v4l2_subdev_get_try_crop(fh, 0); | ||
668 | + crop->left = MT9V032_COLUMN_START_DEF; | ||
669 | + crop->top = MT9V032_ROW_START_DEF; | ||
670 | + crop->width = MT9V032_WINDOW_WIDTH_DEF; | ||
671 | + crop->height = MT9V032_WINDOW_HEIGHT_DEF; | ||
672 | + | ||
673 | + format = v4l2_subdev_get_try_format(fh, 0); | ||
674 | + format->code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
675 | + format->width = MT9V032_WINDOW_WIDTH_DEF; | ||
676 | + format->height = MT9V032_WINDOW_HEIGHT_DEF; | ||
677 | + format->field = V4L2_FIELD_NONE; | ||
678 | + format->colorspace = V4L2_COLORSPACE_SRGB; | ||
679 | + | ||
680 | + return mt9v032_set_power(subdev, 1); | ||
681 | +} | ||
682 | + | ||
683 | +static int mt9v032_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) | ||
684 | +{ | ||
685 | + return mt9v032_set_power(subdev, 0); | ||
686 | +} | ||
687 | + | ||
688 | +static struct v4l2_subdev_core_ops mt9v032_subdev_core_ops = { | ||
689 | + .s_power = mt9v032_set_power, | ||
690 | +}; | ||
691 | + | ||
692 | +static struct v4l2_subdev_video_ops mt9v032_subdev_video_ops = { | ||
693 | + .s_stream = mt9v032_s_stream, | ||
694 | +}; | ||
695 | + | ||
696 | +static struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = { | ||
697 | + .enum_mbus_code = mt9v032_enum_mbus_code, | ||
698 | + .enum_frame_size = mt9v032_enum_frame_size, | ||
699 | + .get_fmt = mt9v032_get_format, | ||
700 | + .set_fmt = mt9v032_set_format, | ||
701 | + .get_crop = mt9v032_get_crop, | ||
702 | + .set_crop = mt9v032_set_crop, | ||
703 | +}; | ||
704 | + | ||
705 | +static struct v4l2_subdev_ops mt9v032_subdev_ops = { | ||
706 | + .core = &mt9v032_subdev_core_ops, | ||
707 | + .video = &mt9v032_subdev_video_ops, | ||
708 | + .pad = &mt9v032_subdev_pad_ops, | ||
709 | +}; | ||
710 | + | ||
711 | +static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = { | ||
712 | + .registered = mt9v032_registered, | ||
713 | + .open = mt9v032_open, | ||
714 | + .close = mt9v032_close, | ||
715 | +}; | ||
716 | + | ||
717 | +/* ----------------------------------------------------------------------------- | ||
718 | + * Driver initialization and probing | ||
719 | + */ | ||
720 | + | ||
721 | +static int mt9v032_probe(struct i2c_client *client, | ||
722 | + const struct i2c_device_id *did) | ||
723 | +{ | ||
724 | + struct mt9v032 *mt9v032; | ||
725 | + unsigned int i; | ||
726 | + int ret; | ||
727 | + | ||
728 | + if (!i2c_check_functionality(client->adapter, | ||
729 | + I2C_FUNC_SMBUS_WORD_DATA)) { | ||
730 | + dev_warn(&client->adapter->dev, | ||
731 | + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); | ||
732 | + return -EIO; | ||
733 | + } | ||
734 | + | ||
735 | + mt9v032 = kzalloc(sizeof(*mt9v032), GFP_KERNEL); | ||
736 | + if (!mt9v032) | ||
737 | + return -ENOMEM; | ||
738 | + | ||
739 | + mutex_init(&mt9v032->power_lock); | ||
740 | + mt9v032->pdata = client->dev.platform_data; | ||
741 | + | ||
742 | + v4l2_ctrl_handler_init(&mt9v032->ctrls, ARRAY_SIZE(mt9v032_ctrls) + 4); | ||
743 | + | ||
744 | + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, | ||
745 | + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); | ||
746 | + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, | ||
747 | + V4L2_CID_GAIN, MT9V032_ANALOG_GAIN_MIN, | ||
748 | + MT9V032_ANALOG_GAIN_MAX, 1, MT9V032_ANALOG_GAIN_DEF); | ||
749 | + v4l2_ctrl_new_std_menu(&mt9v032->ctrls, &mt9v032_ctrl_ops, | ||
750 | + V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, | ||
751 | + V4L2_EXPOSURE_AUTO); | ||
752 | + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, | ||
753 | + V4L2_CID_EXPOSURE, MT9V032_TOTAL_SHUTTER_WIDTH_MIN, | ||
754 | + MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1, | ||
755 | + MT9V032_TOTAL_SHUTTER_WIDTH_DEF); | ||
756 | + | ||
757 | + for (i = 0; i < ARRAY_SIZE(mt9v032_ctrls); ++i) | ||
758 | + v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_ctrls[i], NULL); | ||
759 | + | ||
760 | + mt9v032->subdev.ctrl_handler = &mt9v032->ctrls; | ||
761 | + | ||
762 | + if (mt9v032->ctrls.error) | ||
763 | + printk(KERN_INFO "%s: control initialization error %d\n", | ||
764 | + __func__, mt9v032->ctrls.error); | ||
765 | + | ||
766 | + mt9v032->crop.left = MT9V032_COLUMN_START_DEF; | ||
767 | + mt9v032->crop.top = MT9V032_ROW_START_DEF; | ||
768 | + mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF; | ||
769 | + mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF; | ||
770 | + | ||
771 | + mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; | ||
772 | + mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF; | ||
773 | + mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF; | ||
774 | + mt9v032->format.field = V4L2_FIELD_NONE; | ||
775 | + mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB; | ||
776 | + | ||
777 | + mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE; | ||
778 | + | ||
779 | + v4l2_i2c_subdev_init(&mt9v032->subdev, client, &mt9v032_subdev_ops); | ||
780 | + mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops; | ||
781 | + mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
782 | + | ||
783 | + mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; | ||
784 | + ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0); | ||
785 | + if (ret < 0) | ||
786 | + kfree(mt9v032); | ||
787 | + | ||
788 | + return ret; | ||
789 | +} | ||
790 | + | ||
791 | +static int mt9v032_remove(struct i2c_client *client) | ||
792 | +{ | ||
793 | + struct v4l2_subdev *subdev = i2c_get_clientdata(client); | ||
794 | + struct mt9v032 *mt9v032 = to_mt9v032(subdev); | ||
795 | + | ||
796 | + v4l2_device_unregister_subdev(subdev); | ||
797 | + media_entity_cleanup(&subdev->entity); | ||
798 | + kfree(mt9v032); | ||
799 | + return 0; | ||
800 | +} | ||
801 | + | ||
802 | +static const struct i2c_device_id mt9v032_id[] = { | ||
803 | + { "mt9v032", 0 }, | ||
804 | + { } | ||
805 | +}; | ||
806 | +MODULE_DEVICE_TABLE(i2c, mt9v032_id); | ||
807 | + | ||
808 | +static struct i2c_driver mt9v032_driver = { | ||
809 | + .driver = { | ||
810 | + .name = "mt9v032", | ||
811 | + }, | ||
812 | + .probe = mt9v032_probe, | ||
813 | + .remove = mt9v032_remove, | ||
814 | + .id_table = mt9v032_id, | ||
815 | +}; | ||
816 | + | ||
817 | +static int __init mt9v032_init(void) | ||
818 | +{ | ||
819 | + return i2c_add_driver(&mt9v032_driver); | ||
820 | +} | ||
821 | + | ||
822 | +static void __exit mt9v032_exit(void) | ||
823 | +{ | ||
824 | + i2c_del_driver(&mt9v032_driver); | ||
825 | +} | ||
826 | + | ||
827 | +module_init(mt9v032_init); | ||
828 | +module_exit(mt9v032_exit); | ||
829 | + | ||
830 | +MODULE_DESCRIPTION("Aptina MT9V032 Camera driver"); | ||
831 | +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); | ||
832 | +MODULE_LICENSE("GPL"); | ||
833 | diff --git a/include/media/mt9v032.h b/include/media/mt9v032.h | ||
834 | new file mode 100644 | ||
835 | index 0000000..5e27f9b | ||
836 | --- /dev/null | ||
837 | +++ b/include/media/mt9v032.h | ||
838 | @@ -0,0 +1,12 @@ | ||
839 | +#ifndef _MEDIA_MT9V032_H | ||
840 | +#define _MEDIA_MT9V032_H | ||
841 | + | ||
842 | +struct v4l2_subdev; | ||
843 | + | ||
844 | +struct mt9v032_platform_data { | ||
845 | + unsigned int clk_pol:1; | ||
846 | + | ||
847 | + void (*set_clock)(struct v4l2_subdev *subdev, unsigned int rate); | ||
848 | +}; | ||
849 | + | ||
850 | +#endif | ||
851 | -- | ||
852 | 1.6.6.1 | ||
853 | |||