aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/mt9v011.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/mt9v011.c')
-rw-r--r--drivers/media/video/mt9v011.c221
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
57struct mt9v011 { 77struct 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
137static void set_balance(struct v4l2_subdev *sd) 159static 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
179static 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
209static 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
157static void set_res(struct v4l2_subdev *sd) 249static 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
278static 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
184static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) 292static 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
333static 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
218static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) 350static 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
419static 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
435static 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
279static int mt9v011_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) 457static 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
475static 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
299static int mt9v011_g_register(struct v4l2_subdev *sd, 493static int mt9v011_g_register(struct v4l2_subdev *sd,
@@ -331,16 +525,21 @@ static int mt9v011_s_register(struct v4l2_subdev *sd,
331static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, 525static 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
340static const struct v4l2_subdev_core_ops mt9v011_core_ops = { 537static 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
357static const struct v4l2_subdev_ops mt9v011_ops = { 558static 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}