Commit | Line | Data |
---|---|---|
7dfba00d MCC |
1 | /* |
2 | * mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor | |
3 | * | |
37e59f87 | 4 | * Copyright (c) 2009 Mauro Carvalho Chehab |
7dfba00d MCC |
5 | * This code is placed under the terms of the GNU General Public License v2 |
6 | */ | |
7 | ||
8 | #include <linux/i2c.h> | |
5a0e3ad6 | 9 | #include <linux/slab.h> |
7dfba00d MCC |
10 | #include <linux/videodev2.h> |
11 | #include <linux/delay.h> | |
7a707b89 | 12 | #include <linux/module.h> |
e11206e6 | 13 | #include <asm/div64.h> |
7dfba00d | 14 | #include <media/v4l2-device.h> |
ea01a83d | 15 | #include <media/v4l2-ctrls.h> |
b5dcee22 | 16 | #include <media/i2c/mt9v011.h> |
7dfba00d MCC |
17 | |
18 | MODULE_DESCRIPTION("Micron mt9v011 sensor driver"); | |
37e59f87 | 19 | MODULE_AUTHOR("Mauro Carvalho Chehab"); |
7dfba00d MCC |
20 | MODULE_LICENSE("GPL"); |
21 | ||
7dfba00d MCC |
22 | static int debug; |
23 | module_param(debug, int, 0); | |
24 | MODULE_PARM_DESC(debug, "Debug level (0-2)"); | |
25 | ||
3c7c9370 HV |
26 | #define R00_MT9V011_CHIP_VERSION 0x00 |
27 | #define R01_MT9V011_ROWSTART 0x01 | |
28 | #define R02_MT9V011_COLSTART 0x02 | |
29 | #define R03_MT9V011_HEIGHT 0x03 | |
30 | #define R04_MT9V011_WIDTH 0x04 | |
31 | #define R05_MT9V011_HBLANK 0x05 | |
32 | #define R06_MT9V011_VBLANK 0x06 | |
33 | #define R07_MT9V011_OUT_CTRL 0x07 | |
34 | #define R09_MT9V011_SHUTTER_WIDTH 0x09 | |
35 | #define R0A_MT9V011_CLK_SPEED 0x0a | |
36 | #define R0B_MT9V011_RESTART 0x0b | |
37 | #define R0C_MT9V011_SHUTTER_DELAY 0x0c | |
38 | #define R0D_MT9V011_RESET 0x0d | |
39 | #define R1E_MT9V011_DIGITAL_ZOOM 0x1e | |
40 | #define R20_MT9V011_READ_MODE 0x20 | |
41 | #define R2B_MT9V011_GREEN_1_GAIN 0x2b | |
42 | #define R2C_MT9V011_BLUE_GAIN 0x2c | |
43 | #define R2D_MT9V011_RED_GAIN 0x2d | |
44 | #define R2E_MT9V011_GREEN_2_GAIN 0x2e | |
45 | #define R35_MT9V011_GLOBAL_GAIN 0x35 | |
46 | #define RF1_MT9V011_CHIP_ENABLE 0xf1 | |
47 | ||
48 | #define MT9V011_VERSION 0x8232 | |
49 | #define MT9V011_REV_B_VERSION 0x8243 | |
50 | ||
7dfba00d MCC |
51 | struct mt9v011 { |
52 | struct v4l2_subdev sd; | |
ea01a83d | 53 | struct v4l2_ctrl_handler ctrls; |
27fe4a30 | 54 | unsigned width, height; |
e11206e6 | 55 | unsigned xtal; |
2526ea6e MCC |
56 | unsigned hflip:1; |
57 | unsigned vflip:1; | |
7dfba00d | 58 | |
32127363 JO |
59 | u16 global_gain, exposure; |
60 | s16 red_bal, blue_bal; | |
7dfba00d MCC |
61 | }; |
62 | ||
63 | static inline struct mt9v011 *to_mt9v011(struct v4l2_subdev *sd) | |
64 | { | |
65 | return container_of(sd, struct mt9v011, sd); | |
66 | } | |
67 | ||
68 | static int mt9v011_read(struct v4l2_subdev *sd, unsigned char addr) | |
69 | { | |
70 | struct i2c_client *c = v4l2_get_subdevdata(sd); | |
71 | __be16 buffer; | |
72 | int rc, val; | |
73 | ||
fbe2800c MCC |
74 | rc = i2c_master_send(c, &addr, 1); |
75 | if (rc != 1) | |
7dfba00d MCC |
76 | v4l2_dbg(0, debug, sd, |
77 | "i2c i/o error: rc == %d (should be 1)\n", rc); | |
78 | ||
79 | msleep(10); | |
80 | ||
fbe2800c MCC |
81 | rc = i2c_master_recv(c, (char *)&buffer, 2); |
82 | if (rc != 2) | |
7dfba00d | 83 | v4l2_dbg(0, debug, sd, |
fbe2800c | 84 | "i2c i/o error: rc == %d (should be 2)\n", rc); |
7dfba00d MCC |
85 | |
86 | val = be16_to_cpu(buffer); | |
87 | ||
88 | v4l2_dbg(2, debug, sd, "mt9v011: read 0x%02x = 0x%04x\n", addr, val); | |
89 | ||
90 | return val; | |
91 | } | |
92 | ||
93 | static void mt9v011_write(struct v4l2_subdev *sd, unsigned char addr, | |
94 | u16 value) | |
95 | { | |
96 | struct i2c_client *c = v4l2_get_subdevdata(sd); | |
97 | unsigned char buffer[3]; | |
98 | int rc; | |
99 | ||
100 | buffer[0] = addr; | |
101 | buffer[1] = value >> 8; | |
102 | buffer[2] = value & 0xff; | |
103 | ||
104 | v4l2_dbg(2, debug, sd, | |
105 | "mt9v011: writing 0x%02x 0x%04x\n", buffer[0], value); | |
27fe4a30 | 106 | rc = i2c_master_send(c, buffer, 3); |
fbe2800c | 107 | if (rc != 3) |
7dfba00d MCC |
108 | v4l2_dbg(0, debug, sd, |
109 | "i2c i/o error: rc == %d (should be 3)\n", rc); | |
110 | } | |
111 | ||
112 | ||
113 | struct i2c_reg_value { | |
114 | unsigned char reg; | |
115 | u16 value; | |
116 | }; | |
117 | ||
118 | /* | |
119 | * Values used at the original driver | |
120 | * Some values are marked as Reserved at the datasheet | |
121 | */ | |
122 | static const struct i2c_reg_value mt9v011_init_default[] = { | |
7dfba00d MCC |
123 | { R0D_MT9V011_RESET, 0x0001 }, |
124 | { R0D_MT9V011_RESET, 0x0000 }, | |
afe09f82 | 125 | |
afe09f82 | 126 | { R0C_MT9V011_SHUTTER_DELAY, 0x0000 }, |
6934e6ff MCC |
127 | { R09_MT9V011_SHUTTER_WIDTH, 0x1fc }, |
128 | ||
129 | { R0A_MT9V011_CLK_SPEED, 0x0000 }, | |
afe09f82 | 130 | { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, |
afe09f82 | 131 | |
e11206e6 | 132 | { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ |
7dfba00d MCC |
133 | }; |
134 | ||
32127363 JO |
135 | |
136 | static u16 calc_mt9v011_gain(s16 lineargain) | |
137 | { | |
138 | ||
139 | u16 digitalgain = 0; | |
140 | u16 analogmult = 0; | |
141 | u16 analoginit = 0; | |
142 | ||
143 | if (lineargain < 0) | |
144 | lineargain = 0; | |
145 | ||
146 | /* recommended minimum */ | |
147 | lineargain += 0x0020; | |
148 | ||
149 | if (lineargain > 2047) | |
150 | lineargain = 2047; | |
151 | ||
152 | if (lineargain > 1023) { | |
153 | digitalgain = 3; | |
154 | analogmult = 3; | |
155 | analoginit = lineargain / 16; | |
156 | } else if (lineargain > 511) { | |
157 | digitalgain = 1; | |
158 | analogmult = 3; | |
159 | analoginit = lineargain / 8; | |
160 | } else if (lineargain > 255) { | |
161 | analogmult = 3; | |
162 | analoginit = lineargain / 4; | |
163 | } else if (lineargain > 127) { | |
164 | analogmult = 1; | |
165 | analoginit = lineargain / 2; | |
166 | } else | |
167 | analoginit = lineargain; | |
168 | ||
169 | return analoginit + (analogmult << 7) + (digitalgain << 9); | |
170 | ||
171 | } | |
172 | ||
7dfba00d MCC |
173 | static void set_balance(struct v4l2_subdev *sd) |
174 | { | |
175 | struct mt9v011 *core = to_mt9v011(sd); | |
32127363 | 176 | u16 green_gain, blue_gain, red_gain; |
590929f3 | 177 | u16 exposure; |
32127363 | 178 | s16 bal; |
590929f3 JO |
179 | |
180 | exposure = core->exposure; | |
7dfba00d | 181 | |
32127363 | 182 | green_gain = calc_mt9v011_gain(core->global_gain); |
7dfba00d | 183 | |
32127363 JO |
184 | bal = core->global_gain; |
185 | bal += (core->blue_bal * core->global_gain / (1 << 7)); | |
186 | blue_gain = calc_mt9v011_gain(bal); | |
7dfba00d | 187 | |
32127363 JO |
188 | bal = core->global_gain; |
189 | bal += (core->red_bal * core->global_gain / (1 << 7)); | |
190 | red_gain = calc_mt9v011_gain(bal); | |
7dfba00d | 191 | |
32127363 JO |
192 | mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green_gain); |
193 | mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green_gain); | |
7dfba00d MCC |
194 | mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain); |
195 | mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); | |
590929f3 | 196 | mt9v011_write(sd, R09_MT9V011_SHUTTER_WIDTH, exposure); |
7dfba00d MCC |
197 | } |
198 | ||
83053f7f | 199 | static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator) |
e11206e6 MCC |
200 | { |
201 | struct mt9v011 *core = to_mt9v011(sd); | |
202 | unsigned height, width, hblank, vblank, speed; | |
203 | unsigned row_time, t_time; | |
204 | u64 frames_per_ms; | |
205 | unsigned tmp; | |
206 | ||
207 | height = mt9v011_read(sd, R03_MT9V011_HEIGHT); | |
208 | width = mt9v011_read(sd, R04_MT9V011_WIDTH); | |
209 | hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); | |
210 | vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); | |
211 | speed = mt9v011_read(sd, R0A_MT9V011_CLK_SPEED); | |
212 | ||
213 | row_time = (width + 113 + hblank) * (speed + 2); | |
214 | t_time = row_time * (height + vblank + 1); | |
215 | ||
216 | frames_per_ms = core->xtal * 1000l; | |
217 | do_div(frames_per_ms, t_time); | |
218 | tmp = frames_per_ms; | |
219 | ||
220 | v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", | |
221 | tmp / 1000, tmp % 1000, t_time); | |
83053f7f MCC |
222 | |
223 | if (numerator && denominator) { | |
224 | *numerator = 1000; | |
225 | *denominator = (u32)frames_per_ms; | |
226 | } | |
227 | } | |
228 | ||
229 | static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator) | |
230 | { | |
231 | struct mt9v011 *core = to_mt9v011(sd); | |
232 | unsigned height, width, hblank, vblank; | |
233 | unsigned row_time, line_time; | |
234 | u64 t_time, speed; | |
235 | ||
236 | /* Avoid bogus calculus */ | |
237 | if (!numerator || !denominator) | |
238 | return 0; | |
239 | ||
240 | height = mt9v011_read(sd, R03_MT9V011_HEIGHT); | |
241 | width = mt9v011_read(sd, R04_MT9V011_WIDTH); | |
242 | hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); | |
243 | vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); | |
244 | ||
245 | row_time = width + 113 + hblank; | |
246 | line_time = height + vblank + 1; | |
247 | ||
248 | t_time = core->xtal * ((u64)numerator); | |
249 | /* round to the closest value */ | |
250 | t_time += denominator / 2; | |
251 | do_div(t_time, denominator); | |
252 | ||
253 | speed = t_time; | |
254 | do_div(speed, row_time * line_time); | |
255 | ||
256 | /* Avoid having a negative value for speed */ | |
257 | if (speed < 2) | |
258 | speed = 0; | |
259 | else | |
260 | speed -= 2; | |
261 | ||
262 | /* Avoid speed overflow */ | |
263 | if (speed > 15) | |
264 | return 15; | |
265 | ||
266 | return (u16)speed; | |
e11206e6 MCC |
267 | } |
268 | ||
27fe4a30 MCC |
269 | static void set_res(struct v4l2_subdev *sd) |
270 | { | |
271 | struct mt9v011 *core = to_mt9v011(sd); | |
272 | unsigned vstart, hstart; | |
273 | ||
274 | /* | |
275 | * The mt9v011 doesn't have scaling. So, in order to select the desired | |
276 | * resolution, we're cropping at the middle of the sensor. | |
277 | * hblank and vblank should be adjusted, in order to warrant that | |
278 | * we'll preserve the line timings for 30 fps, no matter what resolution | |
279 | * is selected. | |
6934e6ff MCC |
280 | * NOTE: datasheet says that width (and height) should be filled with |
281 | * width-1. However, this doesn't work, since one pixel per line will | |
282 | * be missing. | |
27fe4a30 MCC |
283 | */ |
284 | ||
1048af2f | 285 | hstart = 20 + (640 - core->width) / 2; |
27fe4a30 MCC |
286 | mt9v011_write(sd, R02_MT9V011_COLSTART, hstart); |
287 | mt9v011_write(sd, R04_MT9V011_WIDTH, core->width); | |
288 | mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width); | |
289 | ||
c180604a | 290 | vstart = 8 + (480 - core->height) / 2; |
27fe4a30 MCC |
291 | mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); |
292 | mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); | |
293 | mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); | |
e11206e6 | 294 | |
83053f7f | 295 | calc_fps(sd, NULL, NULL); |
27fe4a30 MCC |
296 | }; |
297 | ||
2526ea6e MCC |
298 | static void set_read_mode(struct v4l2_subdev *sd) |
299 | { | |
300 | struct mt9v011 *core = to_mt9v011(sd); | |
301 | unsigned mode = 0x1000; | |
302 | ||
303 | if (core->hflip) | |
304 | mode |= 0x4000; | |
305 | ||
306 | if (core->vflip) | |
307 | mode |= 0x8000; | |
308 | ||
309 | mt9v011_write(sd, R20_MT9V011_READ_MODE, mode); | |
310 | } | |
311 | ||
7dfba00d MCC |
312 | static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) |
313 | { | |
7dfba00d MCC |
314 | int i; |
315 | ||
7dfba00d MCC |
316 | for (i = 0; i < ARRAY_SIZE(mt9v011_init_default); i++) |
317 | mt9v011_write(sd, mt9v011_init_default[i].reg, | |
318 | mt9v011_init_default[i].value); | |
319 | ||
320 | set_balance(sd); | |
27fe4a30 | 321 | set_res(sd); |
2526ea6e | 322 | set_read_mode(sd); |
7dfba00d | 323 | |
7dfba00d MCC |
324 | return 0; |
325 | } | |
326 | ||
ebcff5fc HV |
327 | static int mt9v011_enum_mbus_code(struct v4l2_subdev *sd, |
328 | struct v4l2_subdev_pad_config *cfg, | |
329 | struct v4l2_subdev_mbus_code_enum *code) | |
27fe4a30 | 330 | { |
ebcff5fc | 331 | if (code->pad || code->index > 0) |
27fe4a30 MCC |
332 | return -EINVAL; |
333 | ||
ebcff5fc | 334 | code->code = MEDIA_BUS_FMT_SGRBG8_1X8; |
27fe4a30 MCC |
335 | return 0; |
336 | } | |
337 | ||
717fd5b4 HV |
338 | static int mt9v011_set_fmt(struct v4l2_subdev *sd, |
339 | struct v4l2_subdev_pad_config *cfg, | |
340 | struct v4l2_subdev_format *format) | |
27fe4a30 | 341 | { |
717fd5b4 HV |
342 | struct v4l2_mbus_framefmt *fmt = &format->format; |
343 | struct mt9v011 *core = to_mt9v011(sd); | |
344 | ||
345 | if (format->pad || fmt->code != MEDIA_BUS_FMT_SGRBG8_1X8) | |
27fe4a30 MCC |
346 | return -EINVAL; |
347 | ||
ea01b11a HV |
348 | v4l_bound_align_image(&fmt->width, 48, 639, 1, |
349 | &fmt->height, 32, 480, 1, 0); | |
350 | fmt->field = V4L2_FIELD_NONE; | |
351 | fmt->colorspace = V4L2_COLORSPACE_SRGB; | |
27fe4a30 | 352 | |
717fd5b4 HV |
353 | if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { |
354 | core->width = fmt->width; | |
355 | core->height = fmt->height; | |
356 | ||
357 | set_res(sd); | |
358 | } else { | |
359 | cfg->try_fmt = *fmt; | |
360 | } | |
361 | ||
27fe4a30 MCC |
362 | return 0; |
363 | } | |
364 | ||
83053f7f MCC |
365 | static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) |
366 | { | |
367 | struct v4l2_captureparm *cp = &parms->parm.capture; | |
368 | ||
369 | if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
370 | return -EINVAL; | |
371 | ||
372 | memset(cp, 0, sizeof(struct v4l2_captureparm)); | |
373 | cp->capability = V4L2_CAP_TIMEPERFRAME; | |
374 | calc_fps(sd, | |
375 | &cp->timeperframe.numerator, | |
376 | &cp->timeperframe.denominator); | |
377 | ||
378 | return 0; | |
379 | } | |
380 | ||
381 | static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) | |
382 | { | |
383 | struct v4l2_captureparm *cp = &parms->parm.capture; | |
384 | struct v4l2_fract *tpf = &cp->timeperframe; | |
385 | u16 speed; | |
386 | ||
387 | if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
388 | return -EINVAL; | |
389 | if (cp->extendedmode != 0) | |
390 | return -EINVAL; | |
391 | ||
392 | speed = calc_speed(sd, tpf->numerator, tpf->denominator); | |
393 | ||
394 | mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed); | |
395 | v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed); | |
396 | ||
397 | /* Recalculate and update fps info */ | |
398 | calc_fps(sd, &tpf->numerator, &tpf->denominator); | |
399 | ||
400 | return 0; | |
401 | } | |
402 | ||
7dfba00d MCC |
403 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
404 | static int mt9v011_g_register(struct v4l2_subdev *sd, | |
405 | struct v4l2_dbg_register *reg) | |
406 | { | |
7dfba00d MCC |
407 | reg->val = mt9v011_read(sd, reg->reg & 0xff); |
408 | reg->size = 2; | |
409 | ||
410 | return 0; | |
411 | } | |
412 | ||
413 | static int mt9v011_s_register(struct v4l2_subdev *sd, | |
977ba3b1 | 414 | const struct v4l2_dbg_register *reg) |
7dfba00d | 415 | { |
7dfba00d MCC |
416 | mt9v011_write(sd, reg->reg & 0xff, reg->val & 0xffff); |
417 | ||
418 | return 0; | |
419 | } | |
420 | #endif | |
421 | ||
ea01a83d HV |
422 | static int mt9v011_s_ctrl(struct v4l2_ctrl *ctrl) |
423 | { | |
424 | struct mt9v011 *core = | |
425 | container_of(ctrl->handler, struct mt9v011, ctrls); | |
426 | struct v4l2_subdev *sd = &core->sd; | |
427 | ||
428 | switch (ctrl->id) { | |
429 | case V4L2_CID_GAIN: | |
430 | core->global_gain = ctrl->val; | |
431 | break; | |
432 | case V4L2_CID_EXPOSURE: | |
433 | core->exposure = ctrl->val; | |
434 | break; | |
435 | case V4L2_CID_RED_BALANCE: | |
436 | core->red_bal = ctrl->val; | |
437 | break; | |
438 | case V4L2_CID_BLUE_BALANCE: | |
439 | core->blue_bal = ctrl->val; | |
440 | break; | |
441 | case V4L2_CID_HFLIP: | |
442 | core->hflip = ctrl->val; | |
443 | set_read_mode(sd); | |
444 | return 0; | |
445 | case V4L2_CID_VFLIP: | |
446 | core->vflip = ctrl->val; | |
447 | set_read_mode(sd); | |
448 | return 0; | |
449 | default: | |
450 | return -EINVAL; | |
451 | } | |
452 | ||
453 | set_balance(sd); | |
454 | return 0; | |
455 | } | |
456 | ||
217bdb07 | 457 | static const struct v4l2_ctrl_ops mt9v011_ctrl_ops = { |
7dfba00d | 458 | .s_ctrl = mt9v011_s_ctrl, |
ea01a83d HV |
459 | }; |
460 | ||
461 | static const struct v4l2_subdev_core_ops mt9v011_core_ops = { | |
7dfba00d | 462 | .reset = mt9v011_reset, |
7dfba00d MCC |
463 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
464 | .g_register = mt9v011_g_register, | |
465 | .s_register = mt9v011_s_register, | |
466 | #endif | |
467 | }; | |
468 | ||
27fe4a30 | 469 | static const struct v4l2_subdev_video_ops mt9v011_video_ops = { |
83053f7f MCC |
470 | .g_parm = mt9v011_g_parm, |
471 | .s_parm = mt9v011_s_parm, | |
27fe4a30 MCC |
472 | }; |
473 | ||
ebcff5fc HV |
474 | static const struct v4l2_subdev_pad_ops mt9v011_pad_ops = { |
475 | .enum_mbus_code = mt9v011_enum_mbus_code, | |
717fd5b4 | 476 | .set_fmt = mt9v011_set_fmt, |
ebcff5fc HV |
477 | }; |
478 | ||
7dfba00d | 479 | static const struct v4l2_subdev_ops mt9v011_ops = { |
27fe4a30 MCC |
480 | .core = &mt9v011_core_ops, |
481 | .video = &mt9v011_video_ops, | |
ebcff5fc | 482 | .pad = &mt9v011_pad_ops, |
7dfba00d MCC |
483 | }; |
484 | ||
485 | ||
486 | /**************************************************************************** | |
487 | I2C Client & Driver | |
488 | ****************************************************************************/ | |
489 | ||
490 | static int mt9v011_probe(struct i2c_client *c, | |
491 | const struct i2c_device_id *id) | |
492 | { | |
27fe4a30 | 493 | u16 version; |
7dfba00d MCC |
494 | struct mt9v011 *core; |
495 | struct v4l2_subdev *sd; | |
496 | ||
497 | /* Check if the adapter supports the needed features */ | |
498 | if (!i2c_check_functionality(c->adapter, | |
499 | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) | |
500 | return -EIO; | |
501 | ||
c02b211d | 502 | core = devm_kzalloc(&c->dev, sizeof(struct mt9v011), GFP_KERNEL); |
7dfba00d MCC |
503 | if (!core) |
504 | return -ENOMEM; | |
505 | ||
7dfba00d MCC |
506 | sd = &core->sd; |
507 | v4l2_i2c_subdev_init(sd, c, &mt9v011_ops); | |
27fe4a30 MCC |
508 | |
509 | /* Check if the sensor is really a MT9V011 */ | |
510 | version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); | |
296544e1 MCC |
511 | if ((version != MT9V011_VERSION) && |
512 | (version != MT9V011_REV_B_VERSION)) { | |
513 | v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n", | |
27fe4a30 | 514 | version); |
27fe4a30 MCC |
515 | return -EINVAL; |
516 | } | |
517 | ||
ea01a83d HV |
518 | v4l2_ctrl_handler_init(&core->ctrls, 5); |
519 | v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, | |
520 | V4L2_CID_GAIN, 0, (1 << 12) - 1 - 0x20, 1, 0x20); | |
521 | v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, | |
522 | V4L2_CID_EXPOSURE, 0, 2047, 1, 0x01fc); | |
523 | v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, | |
524 | V4L2_CID_RED_BALANCE, -(1 << 9), (1 << 9) - 1, 1, 0); | |
525 | v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, | |
526 | V4L2_CID_BLUE_BALANCE, -(1 << 9), (1 << 9) - 1, 1, 0); | |
527 | v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, | |
528 | V4L2_CID_HFLIP, 0, 1, 1, 0); | |
529 | v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, | |
530 | V4L2_CID_VFLIP, 0, 1, 1, 0); | |
531 | ||
532 | if (core->ctrls.error) { | |
533 | int ret = core->ctrls.error; | |
534 | ||
535 | v4l2_err(sd, "control initialization error %d\n", ret); | |
536 | v4l2_ctrl_handler_free(&core->ctrls); | |
ea01a83d HV |
537 | return ret; |
538 | } | |
539 | core->sd.ctrl_handler = &core->ctrls; | |
540 | ||
27fe4a30 | 541 | core->global_gain = 0x0024; |
590929f3 | 542 | core->exposure = 0x01fc; |
27fe4a30 MCC |
543 | core->width = 640; |
544 | core->height = 480; | |
e11206e6 | 545 | core->xtal = 27000000; /* Hz */ |
27fe4a30 | 546 | |
3c7c9370 HV |
547 | if (c->dev.platform_data) { |
548 | struct mt9v011_platform_data *pdata = c->dev.platform_data; | |
549 | ||
550 | core->xtal = pdata->xtal; | |
551 | v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n", | |
552 | core->xtal / 1000000, (core->xtal / 1000) % 1000); | |
553 | } | |
554 | ||
296544e1 MCC |
555 | v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n", |
556 | c->addr << 1, c->adapter->name, version); | |
7dfba00d MCC |
557 | |
558 | return 0; | |
559 | } | |
560 | ||
561 | static int mt9v011_remove(struct i2c_client *c) | |
562 | { | |
563 | struct v4l2_subdev *sd = i2c_get_clientdata(c); | |
ea01a83d | 564 | struct mt9v011 *core = to_mt9v011(sd); |
7dfba00d MCC |
565 | |
566 | v4l2_dbg(1, debug, sd, | |
567 | "mt9v011.c: removing mt9v011 adapter on address 0x%x\n", | |
568 | c->addr << 1); | |
569 | ||
570 | v4l2_device_unregister_subdev(sd); | |
ea01a83d | 571 | v4l2_ctrl_handler_free(&core->ctrls); |
c02b211d | 572 | |
7dfba00d MCC |
573 | return 0; |
574 | } | |
575 | ||
576 | /* ----------------------------------------------------------------------- */ | |
577 | ||
578 | static const struct i2c_device_id mt9v011_id[] = { | |
579 | { "mt9v011", 0 }, | |
580 | { } | |
581 | }; | |
582 | MODULE_DEVICE_TABLE(i2c, mt9v011_id); | |
583 | ||
6ce58bea HV |
584 | static struct i2c_driver mt9v011_driver = { |
585 | .driver = { | |
6ce58bea HV |
586 | .name = "mt9v011", |
587 | }, | |
588 | .probe = mt9v011_probe, | |
589 | .remove = mt9v011_remove, | |
590 | .id_table = mt9v011_id, | |
7dfba00d | 591 | }; |
6ce58bea | 592 | |
c6e8d86f | 593 | module_i2c_driver(mt9v011_driver); |