diff options
author | Jonathan Corbet <corbet@lwn.net> | 2006-11-19 17:04:55 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2006-12-10 05:51:31 -0500 |
commit | f9a7615686a854cb94b5252e66b836a0a539ad9e (patch) | |
tree | 4ddcab4e5c8d2b712b3c637f3b8d10992acbae9d /drivers/media/video/ov7670.c | |
parent | 96389bf5153d19151edc565b397363e122cab4a7 (diff) |
V4L/DVB (4842): Updated camera driver
A couple of Cafe driver fixes, and support for the hue and saturation
controls.
Signed-off-by: Jonathan Corbet <corbet@lwn.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/ov7670.c')
-rw-r--r-- | drivers/media/video/ov7670.c | 366 |
1 files changed, 325 insertions, 41 deletions
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index 382aa83b73c..b7d824ee03e 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c | |||
@@ -138,6 +138,20 @@ MODULE_LICENSE("GPL"); | |||
138 | #define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ | 138 | #define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ |
139 | #define COM17_CBAR 0x08 /* DSP Color bar */ | 139 | #define COM17_CBAR 0x08 /* DSP Color bar */ |
140 | 140 | ||
141 | /* | ||
142 | * This matrix defines how the colors are generated, must be | ||
143 | * tweaked to adjust hue and saturation. | ||
144 | * | ||
145 | * Order: v-red, v-green, v-blue, u-red, u-green, u-blue | ||
146 | * | ||
147 | * They are nine-bit signed quantities, with the sign bit | ||
148 | * stored in 0x58. Sign for v-red is bit 0, and up from there. | ||
149 | */ | ||
150 | #define REG_CMATRIX_BASE 0x4f | ||
151 | #define CMATRIX_LEN 6 | ||
152 | #define REG_CMATRIX_SIGN 0x58 | ||
153 | |||
154 | |||
141 | #define REG_BRIGHT 0x55 /* Brightness */ | 155 | #define REG_BRIGHT 0x55 /* Brightness */ |
142 | #define REG_CONTRAS 0x56 /* Contrast control */ | 156 | #define REG_CONTRAS 0x56 /* Contrast control */ |
143 | 157 | ||
@@ -160,6 +174,19 @@ MODULE_LICENSE("GPL"); | |||
160 | 174 | ||
161 | 175 | ||
162 | /* | 176 | /* |
177 | * Information we maintain about a known sensor. | ||
178 | */ | ||
179 | struct ov7670_format_struct; /* coming later */ | ||
180 | struct ov7670_info { | ||
181 | struct ov7670_format_struct *fmt; /* Current format */ | ||
182 | unsigned char sat; /* Saturation value */ | ||
183 | int hue; /* Hue value */ | ||
184 | }; | ||
185 | |||
186 | |||
187 | |||
188 | |||
189 | /* | ||
163 | * The default register settings, as obtained from OmniVision. There | 190 | * The default register settings, as obtained from OmniVision. There |
164 | * is really no making sense of most of these - lots of "reserved" values | 191 | * is really no making sense of most of these - lots of "reserved" values |
165 | * and such. | 192 | * and such. |
@@ -179,7 +206,7 @@ static struct regval_list ov7670_default_regs[] = { | |||
179 | * 2 = 20fps | 206 | * 2 = 20fps |
180 | * 1 = 30fps | 207 | * 1 = 30fps |
181 | */ | 208 | */ |
182 | { REG_CLKRC, 0x1 }, /* OV: clock scale (15 fps) */ | 209 | { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */ |
183 | { REG_TSLB, 0x04 }, /* OV */ | 210 | { REG_TSLB, 0x04 }, /* OV */ |
184 | { REG_COM7, 0 }, /* VGA */ | 211 | { REG_COM7, 0 }, /* VGA */ |
185 | /* | 212 | /* |
@@ -286,10 +313,6 @@ static struct regval_list ov7670_default_regs[] = { | |||
286 | { 0x79, 0x05 }, { 0xc8, 0x30 }, | 313 | { 0x79, 0x05 }, { 0xc8, 0x30 }, |
287 | { 0x79, 0x26 }, | 314 | { 0x79, 0x26 }, |
288 | 315 | ||
289 | /* Not sure if these should be here */ | ||
290 | { 0xf1, 0x10 }, { 0x0f, 0x1d }, | ||
291 | { 0x0f, 0x1f }, | ||
292 | |||
293 | { 0xff, 0xff }, /* END MARKER */ | 316 | { 0xff, 0xff }, /* END MARKER */ |
294 | }; | 317 | }; |
295 | 318 | ||
@@ -312,6 +335,7 @@ static struct regval_list ov7670_fmt_yuv422[] = { | |||
312 | { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */ | 335 | { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */ |
313 | { 0x4f, 0x80 }, /* "matrix coefficient 1" */ | 336 | { 0x4f, 0x80 }, /* "matrix coefficient 1" */ |
314 | { 0x50, 0x80 }, /* "matrix coefficient 2" */ | 337 | { 0x50, 0x80 }, /* "matrix coefficient 2" */ |
338 | { 0x51, 0 }, /* vb */ | ||
315 | { 0x52, 0x22 }, /* "matrix coefficient 4" */ | 339 | { 0x52, 0x22 }, /* "matrix coefficient 4" */ |
316 | { 0x53, 0x5e }, /* "matrix coefficient 5" */ | 340 | { 0x53, 0x5e }, /* "matrix coefficient 5" */ |
317 | { 0x54, 0x80 }, /* "matrix coefficient 6" */ | 341 | { 0x54, 0x80 }, /* "matrix coefficient 6" */ |
@@ -327,6 +351,7 @@ static struct regval_list ov7670_fmt_rgb565[] = { | |||
327 | { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ | 351 | { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ |
328 | { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ | 352 | { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ |
329 | { 0x50, 0xb3 }, /* "matrix coefficient 2" */ | 353 | { 0x50, 0xb3 }, /* "matrix coefficient 2" */ |
354 | { 0x51, 0 }, /* vb */ | ||
330 | { 0x52, 0x3d }, /* "matrix coefficient 4" */ | 355 | { 0x52, 0x3d }, /* "matrix coefficient 4" */ |
331 | { 0x53, 0xa7 }, /* "matrix coefficient 5" */ | 356 | { 0x53, 0xa7 }, /* "matrix coefficient 5" */ |
332 | { 0x54, 0xe4 }, /* "matrix coefficient 6" */ | 357 | { 0x54, 0xe4 }, /* "matrix coefficient 6" */ |
@@ -342,6 +367,7 @@ static struct regval_list ov7670_fmt_rgb444[] = { | |||
342 | { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ | 367 | { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ |
343 | { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ | 368 | { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ |
344 | { 0x50, 0xb3 }, /* "matrix coefficient 2" */ | 369 | { 0x50, 0xb3 }, /* "matrix coefficient 2" */ |
370 | { 0x51, 0 }, /* vb */ | ||
345 | { 0x52, 0x3d }, /* "matrix coefficient 4" */ | 371 | { 0x52, 0x3d }, /* "matrix coefficient 4" */ |
346 | { 0x53, 0xa7 }, /* "matrix coefficient 5" */ | 372 | { 0x53, 0xa7 }, /* "matrix coefficient 5" */ |
347 | { 0x54, 0xe4 }, /* "matrix coefficient 6" */ | 373 | { 0x54, 0xe4 }, /* "matrix coefficient 6" */ |
@@ -362,7 +388,7 @@ static int ov7670_read(struct i2c_client *c, unsigned char reg, | |||
362 | int ret; | 388 | int ret; |
363 | 389 | ||
364 | ret = i2c_smbus_read_byte_data(c, reg); | 390 | ret = i2c_smbus_read_byte_data(c, reg); |
365 | if (ret > 0) | 391 | if (ret >= 0) |
366 | *value = (unsigned char) ret; | 392 | *value = (unsigned char) ret; |
367 | return ret; | 393 | return ret; |
368 | } | 394 | } |
@@ -442,28 +468,34 @@ static int ov7670_detect(struct i2c_client *client) | |||
442 | } | 468 | } |
443 | 469 | ||
444 | 470 | ||
445 | 471 | /* | |
446 | 472 | * Store information about the video data format. The color matrix | |
447 | 473 | * is deeply tied into the format, so keep the relevant values here. | |
474 | * The magic matrix nubmers come from OmniVision. | ||
475 | */ | ||
448 | static struct ov7670_format_struct { | 476 | static struct ov7670_format_struct { |
449 | __u8 *desc; | 477 | __u8 *desc; |
450 | __u32 pixelformat; | 478 | __u32 pixelformat; |
451 | struct regval_list *regs; | 479 | struct regval_list *regs; |
480 | int cmatrix[CMATRIX_LEN]; | ||
452 | } ov7670_formats[] = { | 481 | } ov7670_formats[] = { |
453 | { | 482 | { |
454 | .desc = "YUYV 4:2:2", | 483 | .desc = "YUYV 4:2:2", |
455 | .pixelformat = V4L2_PIX_FMT_YUYV, | 484 | .pixelformat = V4L2_PIX_FMT_YUYV, |
456 | .regs = ov7670_fmt_yuv422, | 485 | .regs = ov7670_fmt_yuv422, |
486 | .cmatrix = { 128, -128, 0, -34, -94, 128 }, | ||
457 | }, | 487 | }, |
458 | { | 488 | { |
459 | .desc = "RGB 444", | 489 | .desc = "RGB 444", |
460 | .pixelformat = V4L2_PIX_FMT_RGB444, | 490 | .pixelformat = V4L2_PIX_FMT_RGB444, |
461 | .regs = ov7670_fmt_rgb444, | 491 | .regs = ov7670_fmt_rgb444, |
492 | .cmatrix = { 179, -179, 0, -61, -176, 228 }, | ||
462 | }, | 493 | }, |
463 | { | 494 | { |
464 | .desc = "RGB 565", | 495 | .desc = "RGB 565", |
465 | .pixelformat = V4L2_PIX_FMT_RGB565, | 496 | .pixelformat = V4L2_PIX_FMT_RGB565, |
466 | .regs = ov7670_fmt_rgb565, | 497 | .regs = ov7670_fmt_rgb565, |
498 | .cmatrix = { 179, -179, 0, -61, -176, 228 }, | ||
467 | }, | 499 | }, |
468 | /* | 500 | /* |
469 | * Pretend we do RGB32. This is here on the assumption that the | 501 | * Pretend we do RGB32. This is here on the assumption that the |
@@ -476,6 +508,7 @@ static struct ov7670_format_struct { | |||
476 | .desc = "RGB32 (faked)", | 508 | .desc = "RGB32 (faked)", |
477 | .pixelformat = V4L2_PIX_FMT_RGB32, | 509 | .pixelformat = V4L2_PIX_FMT_RGB32, |
478 | .regs = ov7670_fmt_rgb444, | 510 | .regs = ov7670_fmt_rgb444, |
511 | .cmatrix = { 179, -179, 0, -61, -176, 228 }, | ||
479 | }, | 512 | }, |
480 | }; | 513 | }; |
481 | #define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0])) | 514 | #define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0])) |
@@ -488,6 +521,32 @@ static struct ov7670_format_struct { | |||
488 | /* | 521 | /* |
489 | * Then there is the issue of window sizes. Try to capture the info here. | 522 | * Then there is the issue of window sizes. Try to capture the info here. |
490 | */ | 523 | */ |
524 | |||
525 | /* | ||
526 | * QCIF mode is done (by OV) in a very strange way - it actually looks like | ||
527 | * VGA with weird scaling options - they do *not* use the canned QCIF mode | ||
528 | * which is allegedly provided by the sensor. So here's the weird register | ||
529 | * settings. | ||
530 | */ | ||
531 | static struct regval_list ov7670_qcif_regs[] = { | ||
532 | { REG_COM3, COM3_SCALEEN|COM3_DCWEN }, | ||
533 | { REG_COM3, COM3_DCWEN }, | ||
534 | { REG_COM14, COM14_DCWEN | 0x01}, | ||
535 | { 0x73, 0xf1 }, | ||
536 | { 0xa2, 0x52 }, | ||
537 | { 0x7b, 0x1c }, | ||
538 | { 0x7c, 0x28 }, | ||
539 | { 0x7d, 0x3c }, | ||
540 | { 0x7f, 0x69 }, | ||
541 | { REG_COM9, 0x38 }, | ||
542 | { 0xa1, 0x0b }, | ||
543 | { 0x74, 0x19 }, | ||
544 | { 0x9a, 0x80 }, | ||
545 | { 0x43, 0x14 }, | ||
546 | { REG_COM13, 0xc0 }, | ||
547 | { 0xff, 0xff }, | ||
548 | }; | ||
549 | |||
491 | static struct ov7670_win_size { | 550 | static struct ov7670_win_size { |
492 | int width; | 551 | int width; |
493 | int height; | 552 | int height; |
@@ -496,6 +555,7 @@ static struct ov7670_win_size { | |||
496 | int hstop; /* that they do not always make complete */ | 555 | int hstop; /* that they do not always make complete */ |
497 | int vstart; /* sense to humans, but evidently the sensor */ | 556 | int vstart; /* sense to humans, but evidently the sensor */ |
498 | int vstop; /* will do the right thing... */ | 557 | int vstop; /* will do the right thing... */ |
558 | struct regval_list *regs; /* Regs to tweak */ | ||
499 | /* h/vref stuff */ | 559 | /* h/vref stuff */ |
500 | } ov7670_win_sizes[] = { | 560 | } ov7670_win_sizes[] = { |
501 | /* VGA */ | 561 | /* VGA */ |
@@ -507,6 +567,7 @@ static struct ov7670_win_size { | |||
507 | .hstop = 14, /* Omnivision */ | 567 | .hstop = 14, /* Omnivision */ |
508 | .vstart = 10, | 568 | .vstart = 10, |
509 | .vstop = 490, | 569 | .vstop = 490, |
570 | .regs = NULL, | ||
510 | }, | 571 | }, |
511 | /* CIF */ | 572 | /* CIF */ |
512 | { | 573 | { |
@@ -517,6 +578,7 @@ static struct ov7670_win_size { | |||
517 | .hstop = 90, | 578 | .hstop = 90, |
518 | .vstart = 14, | 579 | .vstart = 14, |
519 | .vstop = 494, | 580 | .vstop = 494, |
581 | .regs = NULL, | ||
520 | }, | 582 | }, |
521 | /* QVGA */ | 583 | /* QVGA */ |
522 | { | 584 | { |
@@ -527,6 +589,18 @@ static struct ov7670_win_size { | |||
527 | .hstop = 20, | 589 | .hstop = 20, |
528 | .vstart = 14, | 590 | .vstart = 14, |
529 | .vstop = 494, | 591 | .vstop = 494, |
592 | .regs = NULL, | ||
593 | }, | ||
594 | /* QCIF */ | ||
595 | { | ||
596 | .width = QCIF_WIDTH, | ||
597 | .height = QCIF_HEIGHT, | ||
598 | .com7_bit = COM7_FMT_VGA, /* see comment above */ | ||
599 | .hstart = 456, /* Empirically determined */ | ||
600 | .hstop = 24, | ||
601 | .vstart = 14, | ||
602 | .vstop = 494, | ||
603 | .regs = ov7670_qcif_regs, | ||
530 | }, | 604 | }, |
531 | }; | 605 | }; |
532 | 606 | ||
@@ -610,7 +684,7 @@ static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, | |||
610 | wsize++) | 684 | wsize++) |
611 | if (pix->width >= wsize->width && pix->height >= wsize->height) | 685 | if (pix->width >= wsize->width && pix->height >= wsize->height) |
612 | break; | 686 | break; |
613 | if (wsize > ov7670_win_sizes + N_WIN_SIZES) | 687 | if (wsize >= ov7670_win_sizes + N_WIN_SIZES) |
614 | wsize--; /* Take the smallest one */ | 688 | wsize--; /* Take the smallest one */ |
615 | if (ret_wsize != NULL) | 689 | if (ret_wsize != NULL) |
616 | *ret_wsize = wsize; | 690 | *ret_wsize = wsize; |
@@ -624,7 +698,6 @@ static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, | |||
624 | pix->bytesperline *= 2; | 698 | pix->bytesperline *= 2; |
625 | pix->sizeimage = pix->height*pix->bytesperline; | 699 | pix->sizeimage = pix->height*pix->bytesperline; |
626 | return 0; | 700 | return 0; |
627 | |||
628 | } | 701 | } |
629 | 702 | ||
630 | /* | 703 | /* |
@@ -635,6 +708,7 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) | |||
635 | int ret; | 708 | int ret; |
636 | struct ov7670_format_struct *ovfmt; | 709 | struct ov7670_format_struct *ovfmt; |
637 | struct ov7670_win_size *wsize; | 710 | struct ov7670_win_size *wsize; |
711 | struct ov7670_info *info = i2c_get_clientdata(c); | ||
638 | unsigned char com7; | 712 | unsigned char com7; |
639 | 713 | ||
640 | ret = ov7670_try_fmt(c, fmt, &ovfmt, &wsize); | 714 | ret = ov7670_try_fmt(c, fmt, &ovfmt, &wsize); |
@@ -655,6 +729,10 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) | |||
655 | ov7670_write_array(c, ovfmt->regs + 1); | 729 | ov7670_write_array(c, ovfmt->regs + 1); |
656 | ov7670_set_hw(c, wsize->hstart, wsize->hstop, wsize->vstart, | 730 | ov7670_set_hw(c, wsize->hstart, wsize->hstop, wsize->vstart, |
657 | wsize->vstop); | 731 | wsize->vstop); |
732 | ret = 0; | ||
733 | if (wsize->regs) | ||
734 | ret = ov7670_write_array(c, wsize->regs); | ||
735 | info->fmt = ovfmt; | ||
658 | return 0; | 736 | return 0; |
659 | } | 737 | } |
660 | 738 | ||
@@ -662,6 +740,168 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) | |||
662 | * Code for dealing with controls. | 740 | * Code for dealing with controls. |
663 | */ | 741 | */ |
664 | 742 | ||
743 | |||
744 | |||
745 | |||
746 | |||
747 | static int ov7670_store_cmatrix(struct i2c_client *client, | ||
748 | int matrix[CMATRIX_LEN]) | ||
749 | { | ||
750 | int i, ret; | ||
751 | unsigned char signbits; | ||
752 | |||
753 | /* | ||
754 | * Weird crap seems to exist in the upper part of | ||
755 | * the sign bits register, so let's preserve it. | ||
756 | */ | ||
757 | ret = ov7670_read(client, REG_CMATRIX_SIGN, &signbits); | ||
758 | signbits &= 0xc0; | ||
759 | |||
760 | for (i = 0; i < CMATRIX_LEN; i++) { | ||
761 | unsigned char raw; | ||
762 | |||
763 | if (matrix[i] < 0) { | ||
764 | signbits |= (1 << i); | ||
765 | if (matrix[i] < -255) | ||
766 | raw = 0xff; | ||
767 | else | ||
768 | raw = (-1 * matrix[i]) & 0xff; | ||
769 | } | ||
770 | else { | ||
771 | if (matrix[i] > 255) | ||
772 | raw = 0xff; | ||
773 | else | ||
774 | raw = matrix[i] & 0xff; | ||
775 | } | ||
776 | ret += ov7670_write(client, REG_CMATRIX_BASE + i, raw); | ||
777 | } | ||
778 | ret += ov7670_write(client, REG_CMATRIX_SIGN, signbits); | ||
779 | return ret; | ||
780 | } | ||
781 | |||
782 | |||
783 | /* | ||
784 | * Hue also requires messing with the color matrix. It also requires | ||
785 | * trig functions, which tend not to be well supported in the kernel. | ||
786 | * So here is a simple table of sine values, 0-90 degrees, in steps | ||
787 | * of five degrees. Values are multiplied by 1000. | ||
788 | * | ||
789 | * The following naive approximate trig functions require an argument | ||
790 | * carefully limited to -180 <= theta <= 180. | ||
791 | */ | ||
792 | #define SIN_STEP 5 | ||
793 | static const int ov7670_sin_table[] = { | ||
794 | 0, 87, 173, 258, 342, 422, | ||
795 | 499, 573, 642, 707, 766, 819, | ||
796 | 866, 906, 939, 965, 984, 996, | ||
797 | 1000 | ||
798 | }; | ||
799 | |||
800 | static int ov7670_sine(int theta) | ||
801 | { | ||
802 | int chs = 1; | ||
803 | int sine; | ||
804 | |||
805 | if (theta < 0) { | ||
806 | theta = -theta; | ||
807 | chs = -1; | ||
808 | } | ||
809 | if (theta <= 90) | ||
810 | sine = ov7670_sin_table[theta/SIN_STEP]; | ||
811 | else { | ||
812 | theta -= 90; | ||
813 | sine = 1000 - ov7670_sin_table[theta/SIN_STEP]; | ||
814 | } | ||
815 | return sine*chs; | ||
816 | } | ||
817 | |||
818 | static int ov7670_cosine(int theta) | ||
819 | { | ||
820 | theta = 90 - theta; | ||
821 | if (theta > 180) | ||
822 | theta -= 360; | ||
823 | else if (theta < -180) | ||
824 | theta += 360; | ||
825 | return ov7670_sine(theta); | ||
826 | } | ||
827 | |||
828 | |||
829 | |||
830 | |||
831 | static void ov7670_calc_cmatrix(struct ov7670_info *info, | ||
832 | int matrix[CMATRIX_LEN]) | ||
833 | { | ||
834 | int i; | ||
835 | /* | ||
836 | * Apply the current saturation setting first. | ||
837 | */ | ||
838 | for (i = 0; i < CMATRIX_LEN; i++) | ||
839 | matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; | ||
840 | /* | ||
841 | * Then, if need be, rotate the hue value. | ||
842 | */ | ||
843 | if (info->hue != 0) { | ||
844 | int sinth, costh, tmpmatrix[CMATRIX_LEN]; | ||
845 | |||
846 | memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); | ||
847 | sinth = ov7670_sine(info->hue); | ||
848 | costh = ov7670_cosine(info->hue); | ||
849 | |||
850 | matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; | ||
851 | matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; | ||
852 | matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; | ||
853 | matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; | ||
854 | matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; | ||
855 | matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; | ||
856 | } | ||
857 | } | ||
858 | |||
859 | |||
860 | |||
861 | static int ov7670_t_sat(struct i2c_client *client, int value) | ||
862 | { | ||
863 | struct ov7670_info *info = i2c_get_clientdata(client); | ||
864 | int matrix[CMATRIX_LEN]; | ||
865 | int ret; | ||
866 | |||
867 | info->sat = value; | ||
868 | ov7670_calc_cmatrix(info, matrix); | ||
869 | ret = ov7670_store_cmatrix(client, matrix); | ||
870 | return ret; | ||
871 | } | ||
872 | |||
873 | static int ov7670_q_sat(struct i2c_client *client, __s32 *value) | ||
874 | { | ||
875 | struct ov7670_info *info = i2c_get_clientdata(client); | ||
876 | |||
877 | *value = info->sat; | ||
878 | return 0; | ||
879 | } | ||
880 | |||
881 | static int ov7670_t_hue(struct i2c_client *client, int value) | ||
882 | { | ||
883 | struct ov7670_info *info = i2c_get_clientdata(client); | ||
884 | int matrix[CMATRIX_LEN]; | ||
885 | int ret; | ||
886 | |||
887 | if (value < -180 || value > 180) | ||
888 | return -EINVAL; | ||
889 | info->hue = value; | ||
890 | ov7670_calc_cmatrix(info, matrix); | ||
891 | ret = ov7670_store_cmatrix(client, matrix); | ||
892 | return ret; | ||
893 | } | ||
894 | |||
895 | |||
896 | static int ov7670_q_hue(struct i2c_client *client, __s32 *value) | ||
897 | { | ||
898 | struct ov7670_info *info = i2c_get_clientdata(client); | ||
899 | |||
900 | *value = info->hue; | ||
901 | return 0; | ||
902 | } | ||
903 | |||
904 | |||
665 | /* | 905 | /* |
666 | * Some weird registers seem to store values in a sign/magnitude format! | 906 | * Some weird registers seem to store values in a sign/magnitude format! |
667 | */ | 907 | */ |
@@ -682,38 +922,43 @@ static unsigned char ov7670_abs_to_sm(unsigned char v) | |||
682 | return (128 - v) | 0x80; | 922 | return (128 - v) | 0x80; |
683 | } | 923 | } |
684 | 924 | ||
685 | static int ov7670_t_brightness(struct i2c_client *client, unsigned char value) | 925 | static int ov7670_t_brightness(struct i2c_client *client, int value) |
686 | { | 926 | { |
687 | unsigned char com8; | 927 | unsigned char com8, v; |
688 | int ret; | 928 | int ret; |
689 | 929 | ||
690 | ov7670_read(client, REG_COM8, &com8); | 930 | ov7670_read(client, REG_COM8, &com8); |
691 | com8 &= ~COM8_AEC; | 931 | com8 &= ~COM8_AEC; |
692 | ov7670_write(client, REG_COM8, com8); | 932 | ov7670_write(client, REG_COM8, com8); |
693 | value = ov7670_abs_to_sm(value); | 933 | v = ov7670_abs_to_sm(value); |
694 | ret = ov7670_write(client, REG_BRIGHT, value); | 934 | ret = ov7670_write(client, REG_BRIGHT, v); |
695 | return ret; | 935 | return ret; |
696 | } | 936 | } |
697 | 937 | ||
698 | static int ov7670_q_brightness(struct i2c_client *client, unsigned char *value) | 938 | static int ov7670_q_brightness(struct i2c_client *client, __s32 *value) |
699 | { | 939 | { |
700 | int ret; | 940 | unsigned char v; |
701 | ret = ov7670_read(client, REG_BRIGHT, value); | 941 | int ret = ov7670_read(client, REG_BRIGHT, &v); |
702 | *value = ov7670_sm_to_abs(*value); | 942 | |
943 | *value = ov7670_sm_to_abs(v); | ||
703 | return ret; | 944 | return ret; |
704 | } | 945 | } |
705 | 946 | ||
706 | static int ov7670_t_contrast(struct i2c_client *client, unsigned char value) | 947 | static int ov7670_t_contrast(struct i2c_client *client, int value) |
707 | { | 948 | { |
708 | return ov7670_write(client, REG_CONTRAS, value); | 949 | return ov7670_write(client, REG_CONTRAS, (unsigned char) value); |
709 | } | 950 | } |
710 | 951 | ||
711 | static int ov7670_q_contrast(struct i2c_client *client, unsigned char *value) | 952 | static int ov7670_q_contrast(struct i2c_client *client, __s32 *value) |
712 | { | 953 | { |
713 | return ov7670_read(client, REG_CONTRAS, value); | 954 | unsigned char v; |
955 | int ret = ov7670_read(client, REG_CONTRAS, &v); | ||
956 | |||
957 | *value = v; | ||
958 | return ret; | ||
714 | } | 959 | } |
715 | 960 | ||
716 | static int ov7670_q_hflip(struct i2c_client *client, unsigned char *value) | 961 | static int ov7670_q_hflip(struct i2c_client *client, __s32 *value) |
717 | { | 962 | { |
718 | int ret; | 963 | int ret; |
719 | unsigned char v; | 964 | unsigned char v; |
@@ -724,7 +969,7 @@ static int ov7670_q_hflip(struct i2c_client *client, unsigned char *value) | |||
724 | } | 969 | } |
725 | 970 | ||
726 | 971 | ||
727 | static int ov7670_t_hflip(struct i2c_client *client, unsigned char value) | 972 | static int ov7670_t_hflip(struct i2c_client *client, int value) |
728 | { | 973 | { |
729 | unsigned char v; | 974 | unsigned char v; |
730 | int ret; | 975 | int ret; |
@@ -741,7 +986,7 @@ static int ov7670_t_hflip(struct i2c_client *client, unsigned char value) | |||
741 | 986 | ||
742 | 987 | ||
743 | 988 | ||
744 | static int ov7670_q_vflip(struct i2c_client *client, unsigned char *value) | 989 | static int ov7670_q_vflip(struct i2c_client *client, __s32 *value) |
745 | { | 990 | { |
746 | int ret; | 991 | int ret; |
747 | unsigned char v; | 992 | unsigned char v; |
@@ -752,7 +997,7 @@ static int ov7670_q_vflip(struct i2c_client *client, unsigned char *value) | |||
752 | } | 997 | } |
753 | 998 | ||
754 | 999 | ||
755 | static int ov7670_t_vflip(struct i2c_client *client, unsigned char value) | 1000 | static int ov7670_t_vflip(struct i2c_client *client, int value) |
756 | { | 1001 | { |
757 | unsigned char v; | 1002 | unsigned char v; |
758 | int ret; | 1003 | int ret; |
@@ -770,8 +1015,8 @@ static int ov7670_t_vflip(struct i2c_client *client, unsigned char value) | |||
770 | 1015 | ||
771 | static struct ov7670_control { | 1016 | static struct ov7670_control { |
772 | struct v4l2_queryctrl qc; | 1017 | struct v4l2_queryctrl qc; |
773 | int (*query)(struct i2c_client *c, unsigned char *value); | 1018 | int (*query)(struct i2c_client *c, __s32 *value); |
774 | int (*tweak)(struct i2c_client *c, unsigned char value); | 1019 | int (*tweak)(struct i2c_client *c, int value); |
775 | } ov7670_controls[] = | 1020 | } ov7670_controls[] = |
776 | { | 1021 | { |
777 | { | 1022 | { |
@@ -804,6 +1049,34 @@ static struct ov7670_control { | |||
804 | }, | 1049 | }, |
805 | { | 1050 | { |
806 | .qc = { | 1051 | .qc = { |
1052 | .id = V4L2_CID_SATURATION, | ||
1053 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
1054 | .name = "Saturation", | ||
1055 | .minimum = 0, | ||
1056 | .maximum = 256, | ||
1057 | .step = 1, | ||
1058 | .default_value = 0x80, | ||
1059 | .flags = V4L2_CTRL_FLAG_SLIDER | ||
1060 | }, | ||
1061 | .tweak = ov7670_t_sat, | ||
1062 | .query = ov7670_q_sat, | ||
1063 | }, | ||
1064 | { | ||
1065 | .qc = { | ||
1066 | .id = V4L2_CID_HUE, | ||
1067 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
1068 | .name = "HUE", | ||
1069 | .minimum = -180, | ||
1070 | .maximum = 180, | ||
1071 | .step = 5, | ||
1072 | .default_value = 0, | ||
1073 | .flags = V4L2_CTRL_FLAG_SLIDER | ||
1074 | }, | ||
1075 | .tweak = ov7670_t_hue, | ||
1076 | .query = ov7670_q_hue, | ||
1077 | }, | ||
1078 | { | ||
1079 | .qc = { | ||
807 | .id = V4L2_CID_VFLIP, | 1080 | .id = V4L2_CID_VFLIP, |
808 | .type = V4L2_CTRL_TYPE_BOOLEAN, | 1081 | .type = V4L2_CTRL_TYPE_BOOLEAN, |
809 | .name = "Vertical flip", | 1082 | .name = "Vertical flip", |
@@ -857,25 +1130,26 @@ static int ov7670_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) | |||
857 | { | 1130 | { |
858 | struct ov7670_control *octrl = ov7670_find_control(ctrl->id); | 1131 | struct ov7670_control *octrl = ov7670_find_control(ctrl->id); |
859 | int ret; | 1132 | int ret; |
860 | unsigned char v; | ||
861 | 1133 | ||
862 | if (octrl == NULL) | 1134 | if (octrl == NULL) |
863 | return -EINVAL; | 1135 | return -EINVAL; |
864 | ret = octrl->query(client, &v); | 1136 | ret = octrl->query(client, &ctrl->value); |
865 | if (ret >= 0) { | 1137 | if (ret >= 0) |
866 | ctrl->value = v; | ||
867 | return 0; | 1138 | return 0; |
868 | } | ||
869 | return ret; | 1139 | return ret; |
870 | } | 1140 | } |
871 | 1141 | ||
872 | static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) | 1142 | static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) |
873 | { | 1143 | { |
874 | struct ov7670_control *octrl = ov7670_find_control(ctrl->id); | 1144 | struct ov7670_control *octrl = ov7670_find_control(ctrl->id); |
1145 | int ret; | ||
875 | 1146 | ||
876 | if (octrl == NULL) | 1147 | if (octrl == NULL) |
877 | return -EINVAL; | 1148 | return -EINVAL; |
878 | return octrl->tweak(client, ctrl->value); | 1149 | ret = octrl->tweak(client, ctrl->value); |
1150 | if (ret >= 0) | ||
1151 | return 0; | ||
1152 | return ret; | ||
879 | } | 1153 | } |
880 | 1154 | ||
881 | 1155 | ||
@@ -883,7 +1157,6 @@ static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) | |||
883 | 1157 | ||
884 | 1158 | ||
885 | 1159 | ||
886 | |||
887 | /* | 1160 | /* |
888 | * Basic i2c stuff. | 1161 | * Basic i2c stuff. |
889 | */ | 1162 | */ |
@@ -893,15 +1166,14 @@ static int ov7670_attach(struct i2c_adapter *adapter) | |||
893 | { | 1166 | { |
894 | int ret; | 1167 | int ret; |
895 | struct i2c_client *client; | 1168 | struct i2c_client *client; |
1169 | struct ov7670_info *info; | ||
896 | 1170 | ||
897 | printk(KERN_ERR "ov7670 attach, id = %d\n", adapter->id); | ||
898 | /* | 1171 | /* |
899 | * For now: only deal with adapters we recognize. | 1172 | * For now: only deal with adapters we recognize. |
900 | */ | 1173 | */ |
901 | if (adapter->id != I2C_HW_SMBUS_CAFE) | 1174 | if (adapter->id != I2C_HW_SMBUS_CAFE) |
902 | return -ENODEV; | 1175 | return -ENODEV; |
903 | 1176 | ||
904 | printk(KERN_ERR "ov7670 accepting\n"); | ||
905 | client = kzalloc(sizeof (struct i2c_client), GFP_KERNEL); | 1177 | client = kzalloc(sizeof (struct i2c_client), GFP_KERNEL); |
906 | if (! client) | 1178 | if (! client) |
907 | return -ENOMEM; | 1179 | return -ENOMEM; |
@@ -909,18 +1181,29 @@ static int ov7670_attach(struct i2c_adapter *adapter) | |||
909 | client->addr = OV7670_I2C_ADDR; | 1181 | client->addr = OV7670_I2C_ADDR; |
910 | client->driver = &ov7670_driver, | 1182 | client->driver = &ov7670_driver, |
911 | strcpy(client->name, "OV7670"); | 1183 | strcpy(client->name, "OV7670"); |
912 | /* Do we need clientdata? */ | 1184 | /* |
1185 | * Set up our info structure. | ||
1186 | */ | ||
1187 | info = kzalloc(sizeof (struct ov7670_info), GFP_KERNEL); | ||
1188 | if (! info) { | ||
1189 | ret = -ENOMEM; | ||
1190 | goto out_free; | ||
1191 | } | ||
1192 | info->fmt = &ov7670_formats[0]; | ||
1193 | info->sat = 128; /* Review this */ | ||
1194 | i2c_set_clientdata(client, info); | ||
913 | 1195 | ||
914 | /* | 1196 | /* |
915 | * Make sure it's an ov7670 | 1197 | * Make sure it's an ov7670 |
916 | */ | 1198 | */ |
917 | ret = ov7670_detect(client); | 1199 | ret = ov7670_detect(client); |
918 | printk(KERN_ERR "detect result is %d\n", ret); | ||
919 | if (ret) | 1200 | if (ret) |
920 | goto out_free; | 1201 | goto out_free_info; |
921 | i2c_attach_client(client); | 1202 | i2c_attach_client(client); |
922 | return 0; | 1203 | return 0; |
923 | 1204 | ||
1205 | out_free_info: | ||
1206 | kfree(info); | ||
924 | out_free: | 1207 | out_free: |
925 | kfree(client); | 1208 | kfree(client); |
926 | return ret; | 1209 | return ret; |
@@ -930,6 +1213,7 @@ static int ov7670_attach(struct i2c_adapter *adapter) | |||
930 | static int ov7670_detach(struct i2c_client *client) | 1213 | static int ov7670_detach(struct i2c_client *client) |
931 | { | 1214 | { |
932 | i2c_detach_client(client); | 1215 | i2c_detach_client(client); |
1216 | kfree(i2c_get_clientdata(client)); | ||
933 | kfree(client); | 1217 | kfree(client); |
934 | return 0; | 1218 | return 0; |
935 | } | 1219 | } |