Commit | Line | Data |
---|---|---|
7dfba00d MCC |
1 | /* |
2 | * mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor | |
3 | * | |
4 | * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com) | |
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> |
7dfba00d | 15 | #include <media/v4l2-chip-ident.h> |
3c7c9370 | 16 | #include <media/mt9v011.h> |
7dfba00d MCC |
17 | |
18 | MODULE_DESCRIPTION("Micron mt9v011 sensor driver"); | |
19 | MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); | |
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 | /* supported controls */ |
52 | static struct v4l2_queryctrl mt9v011_qctrl[] = { | |
53 | { | |
54 | .id = V4L2_CID_GAIN, | |
55 | .type = V4L2_CTRL_TYPE_INTEGER, | |
56 | .name = "Gain", | |
57 | .minimum = 0, | |
32127363 | 58 | .maximum = (1 << 12) - 1 - 0x0020, |
7dfba00d MCC |
59 | .step = 1, |
60 | .default_value = 0x0020, | |
61 | .flags = 0, | |
590929f3 JO |
62 | }, { |
63 | .id = V4L2_CID_EXPOSURE, | |
64 | .type = V4L2_CTRL_TYPE_INTEGER, | |
65 | .name = "Exposure", | |
66 | .minimum = 0, | |
67 | .maximum = 2047, | |
68 | .step = 1, | |
69 | .default_value = 0x01fc, | |
70 | .flags = 0, | |
7dfba00d MCC |
71 | }, { |
72 | .id = V4L2_CID_RED_BALANCE, | |
73 | .type = V4L2_CTRL_TYPE_INTEGER, | |
74 | .name = "Red Balance", | |
75 | .minimum = -1 << 9, | |
76 | .maximum = (1 << 9) - 1, | |
77 | .step = 1, | |
78 | .default_value = 0, | |
79 | .flags = 0, | |
80 | }, { | |
81 | .id = V4L2_CID_BLUE_BALANCE, | |
82 | .type = V4L2_CTRL_TYPE_INTEGER, | |
83 | .name = "Blue Balance", | |
84 | .minimum = -1 << 9, | |
85 | .maximum = (1 << 9) - 1, | |
86 | .step = 1, | |
87 | .default_value = 0, | |
88 | .flags = 0, | |
2526ea6e MCC |
89 | }, { |
90 | .id = V4L2_CID_HFLIP, | |
91 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
92 | .name = "Mirror", | |
93 | .minimum = 0, | |
94 | .maximum = 1, | |
95 | .step = 1, | |
96 | .default_value = 0, | |
97 | .flags = 0, | |
98 | }, { | |
99 | .id = V4L2_CID_VFLIP, | |
100 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
101 | .name = "Vflip", | |
102 | .minimum = 0, | |
103 | .maximum = 1, | |
104 | .step = 1, | |
105 | .default_value = 0, | |
106 | .flags = 0, | |
107 | }, { | |
108 | } | |
7dfba00d MCC |
109 | }; |
110 | ||
111 | struct mt9v011 { | |
112 | struct v4l2_subdev sd; | |
27fe4a30 | 113 | unsigned width, height; |
e11206e6 | 114 | unsigned xtal; |
2526ea6e MCC |
115 | unsigned hflip:1; |
116 | unsigned vflip:1; | |
7dfba00d | 117 | |
32127363 JO |
118 | u16 global_gain, exposure; |
119 | s16 red_bal, blue_bal; | |
7dfba00d MCC |
120 | }; |
121 | ||
122 | static inline struct mt9v011 *to_mt9v011(struct v4l2_subdev *sd) | |
123 | { | |
124 | return container_of(sd, struct mt9v011, sd); | |
125 | } | |
126 | ||
127 | static int mt9v011_read(struct v4l2_subdev *sd, unsigned char addr) | |
128 | { | |
129 | struct i2c_client *c = v4l2_get_subdevdata(sd); | |
130 | __be16 buffer; | |
131 | int rc, val; | |
132 | ||
fbe2800c MCC |
133 | rc = i2c_master_send(c, &addr, 1); |
134 | if (rc != 1) | |
7dfba00d MCC |
135 | v4l2_dbg(0, debug, sd, |
136 | "i2c i/o error: rc == %d (should be 1)\n", rc); | |
137 | ||
138 | msleep(10); | |
139 | ||
fbe2800c MCC |
140 | rc = i2c_master_recv(c, (char *)&buffer, 2); |
141 | if (rc != 2) | |
7dfba00d | 142 | v4l2_dbg(0, debug, sd, |
fbe2800c | 143 | "i2c i/o error: rc == %d (should be 2)\n", rc); |
7dfba00d MCC |
144 | |
145 | val = be16_to_cpu(buffer); | |
146 | ||
147 | v4l2_dbg(2, debug, sd, "mt9v011: read 0x%02x = 0x%04x\n", addr, val); | |
148 | ||
149 | return val; | |
150 | } | |
151 | ||
152 | static void mt9v011_write(struct v4l2_subdev *sd, unsigned char addr, | |
153 | u16 value) | |
154 | { | |
155 | struct i2c_client *c = v4l2_get_subdevdata(sd); | |
156 | unsigned char buffer[3]; | |
157 | int rc; | |
158 | ||
159 | buffer[0] = addr; | |
160 | buffer[1] = value >> 8; | |
161 | buffer[2] = value & 0xff; | |
162 | ||
163 | v4l2_dbg(2, debug, sd, | |
164 | "mt9v011: writing 0x%02x 0x%04x\n", buffer[0], value); | |
27fe4a30 | 165 | rc = i2c_master_send(c, buffer, 3); |
fbe2800c | 166 | if (rc != 3) |
7dfba00d MCC |
167 | v4l2_dbg(0, debug, sd, |
168 | "i2c i/o error: rc == %d (should be 3)\n", rc); | |
169 | } | |
170 | ||
171 | ||
172 | struct i2c_reg_value { | |
173 | unsigned char reg; | |
174 | u16 value; | |
175 | }; | |
176 | ||
177 | /* | |
178 | * Values used at the original driver | |
179 | * Some values are marked as Reserved at the datasheet | |
180 | */ | |
181 | static const struct i2c_reg_value mt9v011_init_default[] = { | |
7dfba00d MCC |
182 | { R0D_MT9V011_RESET, 0x0001 }, |
183 | { R0D_MT9V011_RESET, 0x0000 }, | |
afe09f82 | 184 | |
afe09f82 | 185 | { R0C_MT9V011_SHUTTER_DELAY, 0x0000 }, |
6934e6ff MCC |
186 | { R09_MT9V011_SHUTTER_WIDTH, 0x1fc }, |
187 | ||
188 | { R0A_MT9V011_CLK_SPEED, 0x0000 }, | |
afe09f82 | 189 | { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, |
afe09f82 | 190 | |
e11206e6 | 191 | { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ |
7dfba00d MCC |
192 | }; |
193 | ||
32127363 JO |
194 | |
195 | static u16 calc_mt9v011_gain(s16 lineargain) | |
196 | { | |
197 | ||
198 | u16 digitalgain = 0; | |
199 | u16 analogmult = 0; | |
200 | u16 analoginit = 0; | |
201 | ||
202 | if (lineargain < 0) | |
203 | lineargain = 0; | |
204 | ||
205 | /* recommended minimum */ | |
206 | lineargain += 0x0020; | |
207 | ||
208 | if (lineargain > 2047) | |
209 | lineargain = 2047; | |
210 | ||
211 | if (lineargain > 1023) { | |
212 | digitalgain = 3; | |
213 | analogmult = 3; | |
214 | analoginit = lineargain / 16; | |
215 | } else if (lineargain > 511) { | |
216 | digitalgain = 1; | |
217 | analogmult = 3; | |
218 | analoginit = lineargain / 8; | |
219 | } else if (lineargain > 255) { | |
220 | analogmult = 3; | |
221 | analoginit = lineargain / 4; | |
222 | } else if (lineargain > 127) { | |
223 | analogmult = 1; | |
224 | analoginit = lineargain / 2; | |
225 | } else | |
226 | analoginit = lineargain; | |
227 | ||
228 | return analoginit + (analogmult << 7) + (digitalgain << 9); | |
229 | ||
230 | } | |
231 | ||
7dfba00d MCC |
232 | static void set_balance(struct v4l2_subdev *sd) |
233 | { | |
234 | struct mt9v011 *core = to_mt9v011(sd); | |
32127363 | 235 | u16 green_gain, blue_gain, red_gain; |
590929f3 | 236 | u16 exposure; |
32127363 | 237 | s16 bal; |
590929f3 JO |
238 | |
239 | exposure = core->exposure; | |
7dfba00d | 240 | |
32127363 | 241 | green_gain = calc_mt9v011_gain(core->global_gain); |
7dfba00d | 242 | |
32127363 JO |
243 | bal = core->global_gain; |
244 | bal += (core->blue_bal * core->global_gain / (1 << 7)); | |
245 | blue_gain = calc_mt9v011_gain(bal); | |
7dfba00d | 246 | |
32127363 JO |
247 | bal = core->global_gain; |
248 | bal += (core->red_bal * core->global_gain / (1 << 7)); | |
249 | red_gain = calc_mt9v011_gain(bal); | |
7dfba00d | 250 | |
32127363 JO |
251 | mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green_gain); |
252 | mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green_gain); | |
7dfba00d MCC |
253 | mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain); |
254 | mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); | |
590929f3 | 255 | mt9v011_write(sd, R09_MT9V011_SHUTTER_WIDTH, exposure); |
7dfba00d MCC |
256 | } |
257 | ||
83053f7f | 258 | static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator) |
e11206e6 MCC |
259 | { |
260 | struct mt9v011 *core = to_mt9v011(sd); | |
261 | unsigned height, width, hblank, vblank, speed; | |
262 | unsigned row_time, t_time; | |
263 | u64 frames_per_ms; | |
264 | unsigned tmp; | |
265 | ||
266 | height = mt9v011_read(sd, R03_MT9V011_HEIGHT); | |
267 | width = mt9v011_read(sd, R04_MT9V011_WIDTH); | |
268 | hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); | |
269 | vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); | |
270 | speed = mt9v011_read(sd, R0A_MT9V011_CLK_SPEED); | |
271 | ||
272 | row_time = (width + 113 + hblank) * (speed + 2); | |
273 | t_time = row_time * (height + vblank + 1); | |
274 | ||
275 | frames_per_ms = core->xtal * 1000l; | |
276 | do_div(frames_per_ms, t_time); | |
277 | tmp = frames_per_ms; | |
278 | ||
279 | v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", | |
280 | tmp / 1000, tmp % 1000, t_time); | |
83053f7f MCC |
281 | |
282 | if (numerator && denominator) { | |
283 | *numerator = 1000; | |
284 | *denominator = (u32)frames_per_ms; | |
285 | } | |
286 | } | |
287 | ||
288 | static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator) | |
289 | { | |
290 | struct mt9v011 *core = to_mt9v011(sd); | |
291 | unsigned height, width, hblank, vblank; | |
292 | unsigned row_time, line_time; | |
293 | u64 t_time, speed; | |
294 | ||
295 | /* Avoid bogus calculus */ | |
296 | if (!numerator || !denominator) | |
297 | return 0; | |
298 | ||
299 | height = mt9v011_read(sd, R03_MT9V011_HEIGHT); | |
300 | width = mt9v011_read(sd, R04_MT9V011_WIDTH); | |
301 | hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); | |
302 | vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); | |
303 | ||
304 | row_time = width + 113 + hblank; | |
305 | line_time = height + vblank + 1; | |
306 | ||
307 | t_time = core->xtal * ((u64)numerator); | |
308 | /* round to the closest value */ | |
309 | t_time += denominator / 2; | |
310 | do_div(t_time, denominator); | |
311 | ||
312 | speed = t_time; | |
313 | do_div(speed, row_time * line_time); | |
314 | ||
315 | /* Avoid having a negative value for speed */ | |
316 | if (speed < 2) | |
317 | speed = 0; | |
318 | else | |
319 | speed -= 2; | |
320 | ||
321 | /* Avoid speed overflow */ | |
322 | if (speed > 15) | |
323 | return 15; | |
324 | ||
325 | return (u16)speed; | |
e11206e6 MCC |
326 | } |
327 | ||
27fe4a30 MCC |
328 | static void set_res(struct v4l2_subdev *sd) |
329 | { | |
330 | struct mt9v011 *core = to_mt9v011(sd); | |
331 | unsigned vstart, hstart; | |
332 | ||
333 | /* | |
334 | * The mt9v011 doesn't have scaling. So, in order to select the desired | |
335 | * resolution, we're cropping at the middle of the sensor. | |
336 | * hblank and vblank should be adjusted, in order to warrant that | |
337 | * we'll preserve the line timings for 30 fps, no matter what resolution | |
338 | * is selected. | |
6934e6ff MCC |
339 | * NOTE: datasheet says that width (and height) should be filled with |
340 | * width-1. However, this doesn't work, since one pixel per line will | |
341 | * be missing. | |
27fe4a30 MCC |
342 | */ |
343 | ||
1048af2f | 344 | hstart = 20 + (640 - core->width) / 2; |
27fe4a30 MCC |
345 | mt9v011_write(sd, R02_MT9V011_COLSTART, hstart); |
346 | mt9v011_write(sd, R04_MT9V011_WIDTH, core->width); | |
347 | mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width); | |
348 | ||
c180604a | 349 | vstart = 8 + (480 - core->height) / 2; |
27fe4a30 MCC |
350 | mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); |
351 | mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); | |
352 | mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); | |
e11206e6 | 353 | |
83053f7f | 354 | calc_fps(sd, NULL, NULL); |
27fe4a30 MCC |
355 | }; |
356 | ||
2526ea6e MCC |
357 | static void set_read_mode(struct v4l2_subdev *sd) |
358 | { | |
359 | struct mt9v011 *core = to_mt9v011(sd); | |
360 | unsigned mode = 0x1000; | |
361 | ||
362 | if (core->hflip) | |
363 | mode |= 0x4000; | |
364 | ||
365 | if (core->vflip) | |
366 | mode |= 0x8000; | |
367 | ||
368 | mt9v011_write(sd, R20_MT9V011_READ_MODE, mode); | |
369 | } | |
370 | ||
7dfba00d MCC |
371 | static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) |
372 | { | |
7dfba00d MCC |
373 | int i; |
374 | ||
7dfba00d MCC |
375 | for (i = 0; i < ARRAY_SIZE(mt9v011_init_default); i++) |
376 | mt9v011_write(sd, mt9v011_init_default[i].reg, | |
377 | mt9v011_init_default[i].value); | |
378 | ||
379 | set_balance(sd); | |
27fe4a30 | 380 | set_res(sd); |
2526ea6e | 381 | set_read_mode(sd); |
7dfba00d MCC |
382 | |
383 | return 0; | |
384 | }; | |
385 | ||
386 | static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | |
387 | { | |
388 | struct mt9v011 *core = to_mt9v011(sd); | |
389 | ||
390 | v4l2_dbg(1, debug, sd, "g_ctrl called\n"); | |
391 | ||
392 | switch (ctrl->id) { | |
393 | case V4L2_CID_GAIN: | |
394 | ctrl->value = core->global_gain; | |
395 | return 0; | |
590929f3 JO |
396 | case V4L2_CID_EXPOSURE: |
397 | ctrl->value = core->exposure; | |
398 | return 0; | |
7dfba00d MCC |
399 | case V4L2_CID_RED_BALANCE: |
400 | ctrl->value = core->red_bal; | |
401 | return 0; | |
402 | case V4L2_CID_BLUE_BALANCE: | |
403 | ctrl->value = core->blue_bal; | |
404 | return 0; | |
2526ea6e MCC |
405 | case V4L2_CID_HFLIP: |
406 | ctrl->value = core->hflip ? 1 : 0; | |
407 | return 0; | |
408 | case V4L2_CID_VFLIP: | |
409 | ctrl->value = core->vflip ? 1 : 0; | |
410 | return 0; | |
7dfba00d MCC |
411 | } |
412 | return -EINVAL; | |
413 | } | |
414 | ||
9873740b MCC |
415 | static int mt9v011_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) |
416 | { | |
417 | int i; | |
418 | ||
419 | v4l2_dbg(1, debug, sd, "queryctrl called\n"); | |
420 | ||
421 | for (i = 0; i < ARRAY_SIZE(mt9v011_qctrl); i++) | |
422 | if (qc->id && qc->id == mt9v011_qctrl[i].id) { | |
423 | memcpy(qc, &(mt9v011_qctrl[i]), | |
424 | sizeof(*qc)); | |
425 | return 0; | |
426 | } | |
427 | ||
428 | return -EINVAL; | |
429 | } | |
430 | ||
431 | ||
7dfba00d MCC |
432 | static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) |
433 | { | |
434 | struct mt9v011 *core = to_mt9v011(sd); | |
435 | u8 i, n; | |
436 | n = ARRAY_SIZE(mt9v011_qctrl); | |
437 | ||
438 | for (i = 0; i < n; i++) { | |
439 | if (ctrl->id != mt9v011_qctrl[i].id) | |
440 | continue; | |
441 | if (ctrl->value < mt9v011_qctrl[i].minimum || | |
442 | ctrl->value > mt9v011_qctrl[i].maximum) | |
443 | return -ERANGE; | |
444 | v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n", | |
445 | ctrl->id, ctrl->value); | |
446 | break; | |
447 | } | |
448 | ||
449 | switch (ctrl->id) { | |
450 | case V4L2_CID_GAIN: | |
451 | core->global_gain = ctrl->value; | |
452 | break; | |
590929f3 JO |
453 | case V4L2_CID_EXPOSURE: |
454 | core->exposure = ctrl->value; | |
455 | break; | |
7dfba00d MCC |
456 | case V4L2_CID_RED_BALANCE: |
457 | core->red_bal = ctrl->value; | |
458 | break; | |
459 | case V4L2_CID_BLUE_BALANCE: | |
460 | core->blue_bal = ctrl->value; | |
461 | break; | |
2526ea6e MCC |
462 | case V4L2_CID_HFLIP: |
463 | core->hflip = ctrl->value; | |
464 | set_read_mode(sd); | |
465 | return 0; | |
466 | case V4L2_CID_VFLIP: | |
467 | core->vflip = ctrl->value; | |
468 | set_read_mode(sd); | |
469 | return 0; | |
7dfba00d MCC |
470 | default: |
471 | return -EINVAL; | |
472 | } | |
473 | ||
474 | set_balance(sd); | |
475 | ||
476 | return 0; | |
477 | } | |
478 | ||
ea01b11a HV |
479 | static int mt9v011_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, |
480 | enum v4l2_mbus_pixelcode *code) | |
27fe4a30 | 481 | { |
ea01b11a | 482 | if (index > 0) |
27fe4a30 MCC |
483 | return -EINVAL; |
484 | ||
ea01b11a | 485 | *code = V4L2_MBUS_FMT_SGRBG8_1X8; |
27fe4a30 MCC |
486 | return 0; |
487 | } | |
488 | ||
ea01b11a | 489 | static int mt9v011_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) |
27fe4a30 | 490 | { |
ea01b11a | 491 | if (fmt->code != V4L2_MBUS_FMT_SGRBG8_1X8) |
27fe4a30 MCC |
492 | return -EINVAL; |
493 | ||
ea01b11a HV |
494 | v4l_bound_align_image(&fmt->width, 48, 639, 1, |
495 | &fmt->height, 32, 480, 1, 0); | |
496 | fmt->field = V4L2_FIELD_NONE; | |
497 | fmt->colorspace = V4L2_COLORSPACE_SRGB; | |
27fe4a30 MCC |
498 | |
499 | return 0; | |
500 | } | |
501 | ||
83053f7f MCC |
502 | static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) |
503 | { | |
504 | struct v4l2_captureparm *cp = &parms->parm.capture; | |
505 | ||
506 | if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
507 | return -EINVAL; | |
508 | ||
509 | memset(cp, 0, sizeof(struct v4l2_captureparm)); | |
510 | cp->capability = V4L2_CAP_TIMEPERFRAME; | |
511 | calc_fps(sd, | |
512 | &cp->timeperframe.numerator, | |
513 | &cp->timeperframe.denominator); | |
514 | ||
515 | return 0; | |
516 | } | |
517 | ||
518 | static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) | |
519 | { | |
520 | struct v4l2_captureparm *cp = &parms->parm.capture; | |
521 | struct v4l2_fract *tpf = &cp->timeperframe; | |
522 | u16 speed; | |
523 | ||
524 | if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
525 | return -EINVAL; | |
526 | if (cp->extendedmode != 0) | |
527 | return -EINVAL; | |
528 | ||
529 | speed = calc_speed(sd, tpf->numerator, tpf->denominator); | |
530 | ||
531 | mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed); | |
532 | v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed); | |
533 | ||
534 | /* Recalculate and update fps info */ | |
535 | calc_fps(sd, &tpf->numerator, &tpf->denominator); | |
536 | ||
537 | return 0; | |
538 | } | |
539 | ||
ea01b11a | 540 | static int mt9v011_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) |
27fe4a30 | 541 | { |
27fe4a30 MCC |
542 | struct mt9v011 *core = to_mt9v011(sd); |
543 | int rc; | |
544 | ||
ea01b11a | 545 | rc = mt9v011_try_mbus_fmt(sd, fmt); |
27fe4a30 MCC |
546 | if (rc < 0) |
547 | return -EINVAL; | |
548 | ||
ea01b11a HV |
549 | core->width = fmt->width; |
550 | core->height = fmt->height; | |
27fe4a30 MCC |
551 | |
552 | set_res(sd); | |
553 | ||
554 | return 0; | |
555 | } | |
556 | ||
7dfba00d MCC |
557 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
558 | static int mt9v011_g_register(struct v4l2_subdev *sd, | |
559 | struct v4l2_dbg_register *reg) | |
560 | { | |
561 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
562 | ||
563 | if (!v4l2_chip_match_i2c_client(client, ®->match)) | |
564 | return -EINVAL; | |
565 | if (!capable(CAP_SYS_ADMIN)) | |
566 | return -EPERM; | |
567 | ||
568 | reg->val = mt9v011_read(sd, reg->reg & 0xff); | |
569 | reg->size = 2; | |
570 | ||
571 | return 0; | |
572 | } | |
573 | ||
574 | static int mt9v011_s_register(struct v4l2_subdev *sd, | |
575 | struct v4l2_dbg_register *reg) | |
576 | { | |
577 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
578 | ||
579 | if (!v4l2_chip_match_i2c_client(client, ®->match)) | |
580 | return -EINVAL; | |
581 | if (!capable(CAP_SYS_ADMIN)) | |
582 | return -EPERM; | |
583 | ||
584 | mt9v011_write(sd, reg->reg & 0xff, reg->val & 0xffff); | |
585 | ||
586 | return 0; | |
587 | } | |
588 | #endif | |
589 | ||
590 | static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, | |
591 | struct v4l2_dbg_chip_ident *chip) | |
592 | { | |
296544e1 | 593 | u16 version; |
7dfba00d MCC |
594 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
595 | ||
296544e1 MCC |
596 | version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); |
597 | ||
7dfba00d | 598 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011, |
296544e1 | 599 | version); |
7dfba00d MCC |
600 | } |
601 | ||
602 | static const struct v4l2_subdev_core_ops mt9v011_core_ops = { | |
9873740b | 603 | .queryctrl = mt9v011_queryctrl, |
7dfba00d MCC |
604 | .g_ctrl = mt9v011_g_ctrl, |
605 | .s_ctrl = mt9v011_s_ctrl, | |
606 | .reset = mt9v011_reset, | |
607 | .g_chip_ident = mt9v011_g_chip_ident, | |
608 | #ifdef CONFIG_VIDEO_ADV_DEBUG | |
609 | .g_register = mt9v011_g_register, | |
610 | .s_register = mt9v011_s_register, | |
611 | #endif | |
612 | }; | |
613 | ||
27fe4a30 | 614 | static const struct v4l2_subdev_video_ops mt9v011_video_ops = { |
ea01b11a HV |
615 | .enum_mbus_fmt = mt9v011_enum_mbus_fmt, |
616 | .try_mbus_fmt = mt9v011_try_mbus_fmt, | |
617 | .s_mbus_fmt = mt9v011_s_mbus_fmt, | |
83053f7f MCC |
618 | .g_parm = mt9v011_g_parm, |
619 | .s_parm = mt9v011_s_parm, | |
27fe4a30 MCC |
620 | }; |
621 | ||
7dfba00d | 622 | static const struct v4l2_subdev_ops mt9v011_ops = { |
27fe4a30 MCC |
623 | .core = &mt9v011_core_ops, |
624 | .video = &mt9v011_video_ops, | |
7dfba00d MCC |
625 | }; |
626 | ||
627 | ||
628 | /**************************************************************************** | |
629 | I2C Client & Driver | |
630 | ****************************************************************************/ | |
631 | ||
632 | static int mt9v011_probe(struct i2c_client *c, | |
633 | const struct i2c_device_id *id) | |
634 | { | |
27fe4a30 | 635 | u16 version; |
7dfba00d MCC |
636 | struct mt9v011 *core; |
637 | struct v4l2_subdev *sd; | |
638 | ||
639 | /* Check if the adapter supports the needed features */ | |
640 | if (!i2c_check_functionality(c->adapter, | |
641 | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) | |
642 | return -EIO; | |
643 | ||
644 | core = kzalloc(sizeof(struct mt9v011), GFP_KERNEL); | |
645 | if (!core) | |
646 | return -ENOMEM; | |
647 | ||
7dfba00d MCC |
648 | sd = &core->sd; |
649 | v4l2_i2c_subdev_init(sd, c, &mt9v011_ops); | |
27fe4a30 MCC |
650 | |
651 | /* Check if the sensor is really a MT9V011 */ | |
652 | version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); | |
296544e1 MCC |
653 | if ((version != MT9V011_VERSION) && |
654 | (version != MT9V011_REV_B_VERSION)) { | |
655 | v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n", | |
27fe4a30 MCC |
656 | version); |
657 | kfree(core); | |
658 | return -EINVAL; | |
659 | } | |
660 | ||
661 | core->global_gain = 0x0024; | |
590929f3 | 662 | core->exposure = 0x01fc; |
27fe4a30 MCC |
663 | core->width = 640; |
664 | core->height = 480; | |
e11206e6 | 665 | core->xtal = 27000000; /* Hz */ |
27fe4a30 | 666 | |
3c7c9370 HV |
667 | if (c->dev.platform_data) { |
668 | struct mt9v011_platform_data *pdata = c->dev.platform_data; | |
669 | ||
670 | core->xtal = pdata->xtal; | |
671 | v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n", | |
672 | core->xtal / 1000000, (core->xtal / 1000) % 1000); | |
673 | } | |
674 | ||
296544e1 MCC |
675 | v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n", |
676 | c->addr << 1, c->adapter->name, version); | |
7dfba00d MCC |
677 | |
678 | return 0; | |
679 | } | |
680 | ||
681 | static int mt9v011_remove(struct i2c_client *c) | |
682 | { | |
683 | struct v4l2_subdev *sd = i2c_get_clientdata(c); | |
684 | ||
685 | v4l2_dbg(1, debug, sd, | |
686 | "mt9v011.c: removing mt9v011 adapter on address 0x%x\n", | |
687 | c->addr << 1); | |
688 | ||
689 | v4l2_device_unregister_subdev(sd); | |
690 | kfree(to_mt9v011(sd)); | |
691 | return 0; | |
692 | } | |
693 | ||
694 | /* ----------------------------------------------------------------------- */ | |
695 | ||
696 | static const struct i2c_device_id mt9v011_id[] = { | |
697 | { "mt9v011", 0 }, | |
698 | { } | |
699 | }; | |
700 | MODULE_DEVICE_TABLE(i2c, mt9v011_id); | |
701 | ||
6ce58bea HV |
702 | static struct i2c_driver mt9v011_driver = { |
703 | .driver = { | |
704 | .owner = THIS_MODULE, | |
705 | .name = "mt9v011", | |
706 | }, | |
707 | .probe = mt9v011_probe, | |
708 | .remove = mt9v011_remove, | |
709 | .id_table = mt9v011_id, | |
7dfba00d | 710 | }; |
6ce58bea HV |
711 | |
712 | static __init int init_mt9v011(void) | |
713 | { | |
714 | return i2c_add_driver(&mt9v011_driver); | |
715 | } | |
716 | ||
717 | static __exit void exit_mt9v011(void) | |
718 | { | |
719 | i2c_del_driver(&mt9v011_driver); | |
720 | } | |
721 | ||
722 | module_init(init_mt9v011); | |
723 | module_exit(exit_mt9v011); |