diff options
Diffstat (limited to 'drivers/media/video/mt9v011.c')
-rw-r--r-- | drivers/media/video/mt9v011.c | 156 |
1 files changed, 147 insertions, 9 deletions
diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c index b2260de645f0..cc85f77a5706 100644 --- a/drivers/media/video/mt9v011.c +++ b/drivers/media/video/mt9v011.c | |||
@@ -52,13 +52,34 @@ static struct v4l2_queryctrl mt9v011_qctrl[] = { | |||
52 | .step = 1, | 52 | .step = 1, |
53 | .default_value = 0, | 53 | .default_value = 0, |
54 | .flags = 0, | 54 | .flags = 0, |
55 | }, | 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 | } | ||
56 | }; | 75 | }; |
57 | 76 | ||
58 | struct mt9v011 { | 77 | struct mt9v011 { |
59 | struct v4l2_subdev sd; | 78 | struct v4l2_subdev sd; |
60 | unsigned width, height; | 79 | unsigned width, height; |
61 | unsigned xtal; | 80 | unsigned xtal; |
81 | unsigned hflip:1; | ||
82 | unsigned vflip:1; | ||
62 | 83 | ||
63 | u16 global_gain, red_bal, blue_bal; | 84 | u16 global_gain, red_bal, blue_bal; |
64 | }; | 85 | }; |
@@ -131,7 +152,6 @@ static const struct i2c_reg_value mt9v011_init_default[] = { | |||
131 | 152 | ||
132 | { R0A_MT9V011_CLK_SPEED, 0x0000 }, | 153 | { R0A_MT9V011_CLK_SPEED, 0x0000 }, |
133 | { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, | 154 | { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, |
134 | { R20_MT9V011_READ_MODE, 0x1000 }, | ||
135 | 155 | ||
136 | { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ | 156 | { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ |
137 | }; | 157 | }; |
@@ -156,7 +176,7 @@ static void set_balance(struct v4l2_subdev *sd) | |||
156 | mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); | 176 | mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); |
157 | } | 177 | } |
158 | 178 | ||
159 | static void calc_fps(struct v4l2_subdev *sd) | 179 | static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator) |
160 | { | 180 | { |
161 | struct mt9v011 *core = to_mt9v011(sd); | 181 | struct mt9v011 *core = to_mt9v011(sd); |
162 | unsigned height, width, hblank, vblank, speed; | 182 | unsigned height, width, hblank, vblank, speed; |
@@ -179,6 +199,51 @@ static void calc_fps(struct v4l2_subdev *sd) | |||
179 | 199 | ||
180 | v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", | 200 | v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", |
181 | tmp / 1000, tmp % 1000, t_time); | 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; | ||
182 | } | 247 | } |
183 | 248 | ||
184 | static void set_res(struct v4l2_subdev *sd) | 249 | static void set_res(struct v4l2_subdev *sd) |
@@ -207,9 +272,23 @@ static void set_res(struct v4l2_subdev *sd) | |||
207 | mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); | 272 | mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); |
208 | mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); | 273 | mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); |
209 | 274 | ||
210 | calc_fps(sd); | 275 | calc_fps(sd, NULL, NULL); |
211 | }; | 276 | }; |
212 | 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 | |||
213 | static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) | 292 | static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) |
214 | { | 293 | { |
215 | int i; | 294 | int i; |
@@ -220,6 +299,7 @@ static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) | |||
220 | 299 | ||
221 | set_balance(sd); | 300 | set_balance(sd); |
222 | set_res(sd); | 301 | set_res(sd); |
302 | set_read_mode(sd); | ||
223 | 303 | ||
224 | return 0; | 304 | return 0; |
225 | }; | 305 | }; |
@@ -240,6 +320,12 @@ static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | |||
240 | case V4L2_CID_BLUE_BALANCE: | 320 | case V4L2_CID_BLUE_BALANCE: |
241 | ctrl->value = core->blue_bal; | 321 | ctrl->value = core->blue_bal; |
242 | 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; | ||
243 | } | 329 | } |
244 | return -EINVAL; | 330 | return -EINVAL; |
245 | } | 331 | } |
@@ -288,6 +374,14 @@ static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | |||
288 | case V4L2_CID_BLUE_BALANCE: | 374 | case V4L2_CID_BLUE_BALANCE: |
289 | core->blue_bal = ctrl->value; | 375 | core->blue_bal = ctrl->value; |
290 | 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; | ||
291 | default: | 385 | default: |
292 | return -EINVAL; | 386 | return -EINVAL; |
293 | } | 387 | } |
@@ -322,6 +416,44 @@ static int mt9v011_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) | |||
322 | return 0; | 416 | return 0; |
323 | } | 417 | } |
324 | 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 | |||
325 | 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) |
326 | { | 458 | { |
327 | struct v4l2_pix_format *pix = &fmt->fmt.pix; | 459 | struct v4l2_pix_format *pix = &fmt->fmt.pix; |
@@ -393,10 +525,13 @@ static int mt9v011_s_register(struct v4l2_subdev *sd, | |||
393 | static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, | 525 | static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, |
394 | struct v4l2_dbg_chip_ident *chip) | 526 | struct v4l2_dbg_chip_ident *chip) |
395 | { | 527 | { |
528 | u16 version; | ||
396 | struct i2c_client *client = v4l2_get_subdevdata(sd); | 529 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
397 | 530 | ||
531 | version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); | ||
532 | |||
398 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011, | 533 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011, |
399 | MT9V011_VERSION); | 534 | version); |
400 | } | 535 | } |
401 | 536 | ||
402 | static const struct v4l2_subdev_core_ops mt9v011_core_ops = { | 537 | static const struct v4l2_subdev_core_ops mt9v011_core_ops = { |
@@ -416,6 +551,8 @@ static const struct v4l2_subdev_video_ops mt9v011_video_ops = { | |||
416 | .enum_fmt = mt9v011_enum_fmt, | 551 | .enum_fmt = mt9v011_enum_fmt, |
417 | .try_fmt = mt9v011_try_fmt, | 552 | .try_fmt = mt9v011_try_fmt, |
418 | .s_fmt = mt9v011_s_fmt, | 553 | .s_fmt = mt9v011_s_fmt, |
554 | .g_parm = mt9v011_g_parm, | ||
555 | .s_parm = mt9v011_s_parm, | ||
419 | }; | 556 | }; |
420 | 557 | ||
421 | static const struct v4l2_subdev_ops mt9v011_ops = { | 558 | static const struct v4l2_subdev_ops mt9v011_ops = { |
@@ -449,8 +586,9 @@ static int mt9v011_probe(struct i2c_client *c, | |||
449 | 586 | ||
450 | /* Check if the sensor is really a MT9V011 */ | 587 | /* Check if the sensor is really a MT9V011 */ |
451 | version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); | 588 | version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); |
452 | if (version != MT9V011_VERSION) { | 589 | if ((version != MT9V011_VERSION) && |
453 | 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", | ||
454 | version); | 592 | version); |
455 | kfree(core); | 593 | kfree(core); |
456 | return -EINVAL; | 594 | return -EINVAL; |
@@ -461,8 +599,8 @@ static int mt9v011_probe(struct i2c_client *c, | |||
461 | core->height = 480; | 599 | core->height = 480; |
462 | core->xtal = 27000000; /* Hz */ | 600 | core->xtal = 27000000; /* Hz */ |
463 | 601 | ||
464 | v4l_info(c, "chip found @ 0x%02x (%s)\n", | 602 | v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n", |
465 | c->addr << 1, c->adapter->name); | 603 | c->addr << 1, c->adapter->name, version); |
466 | 604 | ||
467 | return 0; | 605 | return 0; |
468 | } | 606 | } |