diff options
Diffstat (limited to 'drivers/media/video/mt9v011.c')
-rw-r--r-- | drivers/media/video/mt9v011.c | 221 |
1 files changed, 212 insertions, 9 deletions
diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c index 1fe8fc9183a7..cc85f77a5706 100644 --- a/drivers/media/video/mt9v011.c +++ b/drivers/media/video/mt9v011.c | |||
@@ -8,6 +8,7 @@ | |||
8 | #include <linux/i2c.h> | 8 | #include <linux/i2c.h> |
9 | #include <linux/videodev2.h> | 9 | #include <linux/videodev2.h> |
10 | #include <linux/delay.h> | 10 | #include <linux/delay.h> |
11 | #include <asm/div64.h> | ||
11 | #include <media/v4l2-device.h> | 12 | #include <media/v4l2-device.h> |
12 | #include "mt9v011.h" | 13 | #include "mt9v011.h" |
13 | #include <media/v4l2-i2c-drv.h> | 14 | #include <media/v4l2-i2c-drv.h> |
@@ -51,12 +52,34 @@ static struct v4l2_queryctrl mt9v011_qctrl[] = { | |||
51 | .step = 1, | 52 | .step = 1, |
52 | .default_value = 0, | 53 | .default_value = 0, |
53 | .flags = 0, | 54 | .flags = 0, |
54 | }, | 55 | }, { |
56 | .id = V4L2_CID_HFLIP, | ||
57 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
58 | .name = "Mirror", | ||
59 | .minimum = 0, | ||
60 | .maximum = 1, | ||
61 | .step = 1, | ||
62 | .default_value = 0, | ||
63 | .flags = 0, | ||
64 | }, { | ||
65 | .id = V4L2_CID_VFLIP, | ||
66 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
67 | .name = "Vflip", | ||
68 | .minimum = 0, | ||
69 | .maximum = 1, | ||
70 | .step = 1, | ||
71 | .default_value = 0, | ||
72 | .flags = 0, | ||
73 | }, { | ||
74 | } | ||
55 | }; | 75 | }; |
56 | 76 | ||
57 | struct mt9v011 { | 77 | struct mt9v011 { |
58 | struct v4l2_subdev sd; | 78 | struct v4l2_subdev sd; |
59 | unsigned width, height; | 79 | unsigned width, height; |
80 | unsigned xtal; | ||
81 | unsigned hflip:1; | ||
82 | unsigned vflip:1; | ||
60 | 83 | ||
61 | u16 global_gain, red_bal, blue_bal; | 84 | u16 global_gain, red_bal, blue_bal; |
62 | }; | 85 | }; |
@@ -129,9 +152,8 @@ static const struct i2c_reg_value mt9v011_init_default[] = { | |||
129 | 152 | ||
130 | { R0A_MT9V011_CLK_SPEED, 0x0000 }, | 153 | { R0A_MT9V011_CLK_SPEED, 0x0000 }, |
131 | { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, | 154 | { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, |
132 | { R20_MT9V011_READ_MODE, 0x1000 }, | ||
133 | 155 | ||
134 | { R07_MT9V011_OUT_CTRL, 0x000a }, /* chip enable */ | 156 | { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ |
135 | }; | 157 | }; |
136 | 158 | ||
137 | static void set_balance(struct v4l2_subdev *sd) | 159 | static void set_balance(struct v4l2_subdev *sd) |
@@ -154,6 +176,76 @@ static void set_balance(struct v4l2_subdev *sd) | |||
154 | mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); | 176 | mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); |
155 | } | 177 | } |
156 | 178 | ||
179 | static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator) | ||
180 | { | ||
181 | struct mt9v011 *core = to_mt9v011(sd); | ||
182 | unsigned height, width, hblank, vblank, speed; | ||
183 | unsigned row_time, t_time; | ||
184 | u64 frames_per_ms; | ||
185 | unsigned tmp; | ||
186 | |||
187 | height = mt9v011_read(sd, R03_MT9V011_HEIGHT); | ||
188 | width = mt9v011_read(sd, R04_MT9V011_WIDTH); | ||
189 | hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); | ||
190 | vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); | ||
191 | speed = mt9v011_read(sd, R0A_MT9V011_CLK_SPEED); | ||
192 | |||
193 | row_time = (width + 113 + hblank) * (speed + 2); | ||
194 | t_time = row_time * (height + vblank + 1); | ||
195 | |||
196 | frames_per_ms = core->xtal * 1000l; | ||
197 | do_div(frames_per_ms, t_time); | ||
198 | tmp = frames_per_ms; | ||
199 | |||
200 | v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", | ||
201 | tmp / 1000, tmp % 1000, t_time); | ||
202 | |||
203 | if (numerator && denominator) { | ||
204 | *numerator = 1000; | ||
205 | *denominator = (u32)frames_per_ms; | ||
206 | } | ||
207 | } | ||
208 | |||
209 | static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator) | ||
210 | { | ||
211 | struct mt9v011 *core = to_mt9v011(sd); | ||
212 | unsigned height, width, hblank, vblank; | ||
213 | unsigned row_time, line_time; | ||
214 | u64 t_time, speed; | ||
215 | |||
216 | /* Avoid bogus calculus */ | ||
217 | if (!numerator || !denominator) | ||
218 | return 0; | ||
219 | |||
220 | height = mt9v011_read(sd, R03_MT9V011_HEIGHT); | ||
221 | width = mt9v011_read(sd, R04_MT9V011_WIDTH); | ||
222 | hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); | ||
223 | vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); | ||
224 | |||
225 | row_time = width + 113 + hblank; | ||
226 | line_time = height + vblank + 1; | ||
227 | |||
228 | t_time = core->xtal * ((u64)numerator); | ||
229 | /* round to the closest value */ | ||
230 | t_time += denominator / 2; | ||
231 | do_div(t_time, denominator); | ||
232 | |||
233 | speed = t_time; | ||
234 | do_div(speed, row_time * line_time); | ||
235 | |||
236 | /* Avoid having a negative value for speed */ | ||
237 | if (speed < 2) | ||
238 | speed = 0; | ||
239 | else | ||
240 | speed -= 2; | ||
241 | |||
242 | /* Avoid speed overflow */ | ||
243 | if (speed > 15) | ||
244 | return 15; | ||
245 | |||
246 | return (u16)speed; | ||
247 | } | ||
248 | |||
157 | static void set_res(struct v4l2_subdev *sd) | 249 | static void set_res(struct v4l2_subdev *sd) |
158 | { | 250 | { |
159 | struct mt9v011 *core = to_mt9v011(sd); | 251 | struct mt9v011 *core = to_mt9v011(sd); |
@@ -175,12 +267,28 @@ static void set_res(struct v4l2_subdev *sd) | |||
175 | mt9v011_write(sd, R04_MT9V011_WIDTH, core->width); | 267 | mt9v011_write(sd, R04_MT9V011_WIDTH, core->width); |
176 | mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width); | 268 | mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width); |
177 | 269 | ||
178 | vstart = 8 + (640 - core->height) / 2; | 270 | vstart = 8 + (480 - core->height) / 2; |
179 | mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); | 271 | mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); |
180 | mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); | 272 | mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); |
181 | mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); | 273 | mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); |
274 | |||
275 | calc_fps(sd, NULL, NULL); | ||
182 | }; | 276 | }; |
183 | 277 | ||
278 | static void set_read_mode(struct v4l2_subdev *sd) | ||
279 | { | ||
280 | struct mt9v011 *core = to_mt9v011(sd); | ||
281 | unsigned mode = 0x1000; | ||
282 | |||
283 | if (core->hflip) | ||
284 | mode |= 0x4000; | ||
285 | |||
286 | if (core->vflip) | ||
287 | mode |= 0x8000; | ||
288 | |||
289 | mt9v011_write(sd, R20_MT9V011_READ_MODE, mode); | ||
290 | } | ||
291 | |||
184 | static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) | 292 | static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) |
185 | { | 293 | { |
186 | int i; | 294 | int i; |
@@ -191,6 +299,7 @@ static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) | |||
191 | 299 | ||
192 | set_balance(sd); | 300 | set_balance(sd); |
193 | set_res(sd); | 301 | set_res(sd); |
302 | set_read_mode(sd); | ||
194 | 303 | ||
195 | return 0; | 304 | return 0; |
196 | }; | 305 | }; |
@@ -211,10 +320,33 @@ static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | |||
211 | case V4L2_CID_BLUE_BALANCE: | 320 | case V4L2_CID_BLUE_BALANCE: |
212 | ctrl->value = core->blue_bal; | 321 | ctrl->value = core->blue_bal; |
213 | return 0; | 322 | return 0; |
323 | case V4L2_CID_HFLIP: | ||
324 | ctrl->value = core->hflip ? 1 : 0; | ||
325 | return 0; | ||
326 | case V4L2_CID_VFLIP: | ||
327 | ctrl->value = core->vflip ? 1 : 0; | ||
328 | return 0; | ||
214 | } | 329 | } |
215 | return -EINVAL; | 330 | return -EINVAL; |
216 | } | 331 | } |
217 | 332 | ||
333 | static int mt9v011_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) | ||
334 | { | ||
335 | int i; | ||
336 | |||
337 | v4l2_dbg(1, debug, sd, "queryctrl called\n"); | ||
338 | |||
339 | for (i = 0; i < ARRAY_SIZE(mt9v011_qctrl); i++) | ||
340 | if (qc->id && qc->id == mt9v011_qctrl[i].id) { | ||
341 | memcpy(qc, &(mt9v011_qctrl[i]), | ||
342 | sizeof(*qc)); | ||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | return -EINVAL; | ||
347 | } | ||
348 | |||
349 | |||
218 | static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | 350 | static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) |
219 | { | 351 | { |
220 | struct mt9v011 *core = to_mt9v011(sd); | 352 | struct mt9v011 *core = to_mt9v011(sd); |
@@ -242,6 +374,14 @@ static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | |||
242 | case V4L2_CID_BLUE_BALANCE: | 374 | case V4L2_CID_BLUE_BALANCE: |
243 | core->blue_bal = ctrl->value; | 375 | core->blue_bal = ctrl->value; |
244 | break; | 376 | break; |
377 | case V4L2_CID_HFLIP: | ||
378 | core->hflip = ctrl->value; | ||
379 | set_read_mode(sd); | ||
380 | return 0; | ||
381 | case V4L2_CID_VFLIP: | ||
382 | core->vflip = ctrl->value; | ||
383 | set_read_mode(sd); | ||
384 | return 0; | ||
245 | default: | 385 | default: |
246 | return -EINVAL; | 386 | return -EINVAL; |
247 | } | 387 | } |
@@ -276,6 +416,44 @@ static int mt9v011_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) | |||
276 | return 0; | 416 | return 0; |
277 | } | 417 | } |
278 | 418 | ||
419 | static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) | ||
420 | { | ||
421 | struct v4l2_captureparm *cp = &parms->parm.capture; | ||
422 | |||
423 | if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
424 | return -EINVAL; | ||
425 | |||
426 | memset(cp, 0, sizeof(struct v4l2_captureparm)); | ||
427 | cp->capability = V4L2_CAP_TIMEPERFRAME; | ||
428 | calc_fps(sd, | ||
429 | &cp->timeperframe.numerator, | ||
430 | &cp->timeperframe.denominator); | ||
431 | |||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) | ||
436 | { | ||
437 | struct v4l2_captureparm *cp = &parms->parm.capture; | ||
438 | struct v4l2_fract *tpf = &cp->timeperframe; | ||
439 | u16 speed; | ||
440 | |||
441 | if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
442 | return -EINVAL; | ||
443 | if (cp->extendedmode != 0) | ||
444 | return -EINVAL; | ||
445 | |||
446 | speed = calc_speed(sd, tpf->numerator, tpf->denominator); | ||
447 | |||
448 | mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed); | ||
449 | v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed); | ||
450 | |||
451 | /* Recalculate and update fps info */ | ||
452 | calc_fps(sd, &tpf->numerator, &tpf->denominator); | ||
453 | |||
454 | return 0; | ||
455 | } | ||
456 | |||
279 | static int mt9v011_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) | 457 | static int mt9v011_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) |
280 | { | 458 | { |
281 | struct v4l2_pix_format *pix = &fmt->fmt.pix; | 459 | struct v4l2_pix_format *pix = &fmt->fmt.pix; |
@@ -294,6 +472,22 @@ static int mt9v011_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) | |||
294 | return 0; | 472 | return 0; |
295 | } | 473 | } |
296 | 474 | ||
475 | static int mt9v011_s_config(struct v4l2_subdev *sd, int dumb, void *data) | ||
476 | { | ||
477 | struct mt9v011 *core = to_mt9v011(sd); | ||
478 | unsigned *xtal = data; | ||
479 | |||
480 | v4l2_dbg(1, debug, sd, "s_config called\n"); | ||
481 | |||
482 | if (xtal) { | ||
483 | core->xtal = *xtal; | ||
484 | v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n", | ||
485 | *xtal / 1000000, (*xtal / 1000) % 1000); | ||
486 | } | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | |||
297 | 491 | ||
298 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 492 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
299 | static int mt9v011_g_register(struct v4l2_subdev *sd, | 493 | static int mt9v011_g_register(struct v4l2_subdev *sd, |
@@ -331,16 +525,21 @@ static int mt9v011_s_register(struct v4l2_subdev *sd, | |||
331 | static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, | 525 | static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, |
332 | struct v4l2_dbg_chip_ident *chip) | 526 | struct v4l2_dbg_chip_ident *chip) |
333 | { | 527 | { |
528 | u16 version; | ||
334 | struct i2c_client *client = v4l2_get_subdevdata(sd); | 529 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
335 | 530 | ||
531 | version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); | ||
532 | |||
336 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011, | 533 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011, |
337 | MT9V011_VERSION); | 534 | version); |
338 | } | 535 | } |
339 | 536 | ||
340 | static const struct v4l2_subdev_core_ops mt9v011_core_ops = { | 537 | static const struct v4l2_subdev_core_ops mt9v011_core_ops = { |
538 | .queryctrl = mt9v011_queryctrl, | ||
341 | .g_ctrl = mt9v011_g_ctrl, | 539 | .g_ctrl = mt9v011_g_ctrl, |
342 | .s_ctrl = mt9v011_s_ctrl, | 540 | .s_ctrl = mt9v011_s_ctrl, |
343 | .reset = mt9v011_reset, | 541 | .reset = mt9v011_reset, |
542 | .s_config = mt9v011_s_config, | ||
344 | .g_chip_ident = mt9v011_g_chip_ident, | 543 | .g_chip_ident = mt9v011_g_chip_ident, |
345 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 544 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
346 | .g_register = mt9v011_g_register, | 545 | .g_register = mt9v011_g_register, |
@@ -352,6 +551,8 @@ static const struct v4l2_subdev_video_ops mt9v011_video_ops = { | |||
352 | .enum_fmt = mt9v011_enum_fmt, | 551 | .enum_fmt = mt9v011_enum_fmt, |
353 | .try_fmt = mt9v011_try_fmt, | 552 | .try_fmt = mt9v011_try_fmt, |
354 | .s_fmt = mt9v011_s_fmt, | 553 | .s_fmt = mt9v011_s_fmt, |
554 | .g_parm = mt9v011_g_parm, | ||
555 | .s_parm = mt9v011_s_parm, | ||
355 | }; | 556 | }; |
356 | 557 | ||
357 | static const struct v4l2_subdev_ops mt9v011_ops = { | 558 | static const struct v4l2_subdev_ops mt9v011_ops = { |
@@ -385,8 +586,9 @@ static int mt9v011_probe(struct i2c_client *c, | |||
385 | 586 | ||
386 | /* Check if the sensor is really a MT9V011 */ | 587 | /* Check if the sensor is really a MT9V011 */ |
387 | version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); | 588 | version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); |
388 | if (version != MT9V011_VERSION) { | 589 | if ((version != MT9V011_VERSION) && |
389 | v4l2_info(sd, "*** unknown micron chip detected (0x%04x.\n", | 590 | (version != MT9V011_REV_B_VERSION)) { |
591 | v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n", | ||
390 | version); | 592 | version); |
391 | kfree(core); | 593 | kfree(core); |
392 | return -EINVAL; | 594 | return -EINVAL; |
@@ -395,9 +597,10 @@ static int mt9v011_probe(struct i2c_client *c, | |||
395 | core->global_gain = 0x0024; | 597 | core->global_gain = 0x0024; |
396 | core->width = 640; | 598 | core->width = 640; |
397 | core->height = 480; | 599 | core->height = 480; |
600 | core->xtal = 27000000; /* Hz */ | ||
398 | 601 | ||
399 | v4l_info(c, "chip found @ 0x%02x (%s)\n", | 602 | v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n", |
400 | c->addr << 1, c->adapter->name); | 603 | c->addr << 1, c->adapter->name, version); |
401 | 604 | ||
402 | return 0; | 605 | return 0; |
403 | } | 606 | } |