diff options
Diffstat (limited to 'drivers/media/video')
-rw-r--r-- | drivers/media/video/gspca/sonixb.c | 170 |
1 files changed, 122 insertions, 48 deletions
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index dc2d88cdd4a2..66fffa345e7b 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c | |||
@@ -176,7 +176,7 @@ static const struct ctrl sd_ctrls[] = { | |||
176 | .maximum = 255, | 176 | .maximum = 255, |
177 | .step = 1, | 177 | .step = 1, |
178 | #define GAIN_DEF 127 | 178 | #define GAIN_DEF 127 |
179 | #define GAIN_KNEE 200 | 179 | #define GAIN_KNEE 230 |
180 | .default_value = GAIN_DEF, | 180 | .default_value = GAIN_DEF, |
181 | }, | 181 | }, |
182 | .set = sd_setgain, | 182 | .set = sd_setgain, |
@@ -188,10 +188,10 @@ static const struct ctrl sd_ctrls[] = { | |||
188 | .id = V4L2_CID_EXPOSURE, | 188 | .id = V4L2_CID_EXPOSURE, |
189 | .type = V4L2_CTRL_TYPE_INTEGER, | 189 | .type = V4L2_CTRL_TYPE_INTEGER, |
190 | .name = "Exposure", | 190 | .name = "Exposure", |
191 | #define EXPOSURE_DEF 33 /* 33 ms / 30 fps */ | 191 | #define EXPOSURE_DEF 66 /* 33 ms / 30 fps (except on PAS202) */ |
192 | #define EXPOSURE_KNEE 100 /* 100 ms / 10 fps */ | 192 | #define EXPOSURE_KNEE 200 /* 100 ms / 10 fps (except on PAS202) */ |
193 | .minimum = 0, | 193 | .minimum = 0, |
194 | .maximum = 511, | 194 | .maximum = 1023, |
195 | .step = 1, | 195 | .step = 1, |
196 | .default_value = EXPOSURE_DEF, | 196 | .default_value = EXPOSURE_DEF, |
197 | .flags = 0, | 197 | .flags = 0, |
@@ -454,31 +454,41 @@ static const __u8 initPas202[] = { | |||
454 | 0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, | 454 | 0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, |
455 | 0x00, 0x00, | 455 | 0x00, 0x00, |
456 | 0x00, 0x00, 0x00, 0x06, 0x03, 0x0a, | 456 | 0x00, 0x00, 0x00, 0x06, 0x03, 0x0a, |
457 | 0x28, 0x1e, 0x28, 0x89, 0x20, | 457 | 0x28, 0x1e, 0x20, 0x89, 0x20, |
458 | 0x00, 0x00, 0x02, 0x03, 0x0f, 0x0c | 458 | 0x00, 0x00, 0x02, 0x03, 0x0f, 0x0c |
459 | }; | 459 | }; |
460 | |||
461 | /* "Known" PAS202BCB registers: | ||
462 | 0x02 clock divider | ||
463 | 0x04 Variable framerate bits 6-11 (*) | ||
464 | 0x05 Var framerate bits 0-5, one must leave the 2 msb's at 0 !! | ||
465 | 0x07 Blue Gain | ||
466 | 0x08 Green Gain | ||
467 | 0x09 Red Gain | ||
468 | 0x0b offset sign bit (bit0 1 > negative offset) | ||
469 | 0x0c offset | ||
470 | 0x0e Unknown image is slightly brighter when bit 0 is 0, if reg0f is 0 too, | ||
471 | leave at 1 otherwise we get a jump in our exposure control | ||
472 | 0x0f Exposure 0-255, 0 = use full frame time, 255 = no exposure at all | ||
473 | 0x10 Master gain 0 - 31 | ||
474 | 0x11 write 1 to apply changes | ||
475 | (*) The variable framerate control must never be set lower then 500 | ||
476 | which sets the framerate at 30 / reg02, otherwise vsync is lost. | ||
477 | */ | ||
460 | static const __u8 pas202_sensor_init[][8] = { | 478 | static const __u8 pas202_sensor_init[][8] = { |
461 | {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10}, | 479 | /* Set the clock divider to 4 -> 30 / 4 = 7.5 fps, we would like |
480 | to set it lower, but for some reason the bridge starts missing | ||
481 | vsync's then */ | ||
482 | {0xa0, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x10}, | ||
462 | {0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10}, | 483 | {0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10}, |
463 | {0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10}, | 484 | {0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10}, |
464 | {0xd0, 0x40, 0x0C, 0x00, 0x0C, 0x00, 0x32, 0x10}, | 485 | {0xd0, 0x40, 0x0C, 0x00, 0x0C, 0x01, 0x32, 0x10}, |
465 | {0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10}, | 486 | {0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10}, |
466 | {0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10}, | 487 | {0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10}, |
467 | {0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10}, | 488 | {0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10}, |
468 | {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, | 489 | {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, |
469 | {0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10}, | 490 | {0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10}, |
470 | {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, | 491 | {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, |
471 | {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x10}, | ||
472 | {0xb0, 0x40, 0x0e, 0x00, 0x3d, 0x00, 0x63, 0x10}, | ||
473 | |||
474 | {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16}, | ||
475 | {0xa0, 0x40, 0x10, 0x08, 0x3d, 0x00, 0x63, 0x15}, | ||
476 | {0xa0, 0x40, 0x02, 0x04, 0x3d, 0x00, 0x63, 0x16}, | ||
477 | {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16}, | ||
478 | {0xb0, 0x40, 0x0e, 0x00, 0x31, 0x00, 0x63, 0x16}, | ||
479 | {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}, | ||
480 | {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15}, | ||
481 | {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}, | ||
482 | }; | 492 | }; |
483 | 493 | ||
484 | static const __u8 initTas5110c[] = { | 494 | static const __u8 initTas5110c[] = { |
@@ -524,8 +534,8 @@ SENS(initOv7630, initOv7630_3, ov7630_sensor_init, NULL, ov7630_sensor_init_3, | |||
524 | F_GAIN, 0, 0x21), | 534 | F_GAIN, 0, 0x21), |
525 | SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_SIF, NO_EXPO|NO_FREQ, | 535 | SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_SIF, NO_EXPO|NO_FREQ, |
526 | 0), | 536 | 0), |
527 | SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, 0, | 537 | SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, F_GAIN, |
528 | NO_EXPO|NO_FREQ, 0), | 538 | NO_FREQ, 0), |
529 | SENS(initTas5110c, NULL, tas5110_sensor_init, NULL, NULL, | 539 | SENS(initTas5110c, NULL, tas5110_sensor_init, NULL, NULL, |
530 | F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0), | 540 | F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0), |
531 | SENS(initTas5110d, NULL, tas5110_sensor_init, NULL, NULL, | 541 | SENS(initTas5110d, NULL, tas5110_sensor_init, NULL, NULL, |
@@ -633,27 +643,20 @@ static void setbrightness(struct gspca_dev *gspca_dev) | |||
633 | break; | 643 | break; |
634 | } | 644 | } |
635 | case SENSOR_PAS202: { | 645 | case SENSOR_PAS202: { |
636 | /* __u8 i2cpexpo1[] = | 646 | __u8 i2cpbright[] = |
637 | {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x16}; */ | 647 | {0xb0, 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x16}; |
638 | __u8 i2cpexpo[] = | 648 | const __u8 i2cpdoit[] = |
639 | {0xb0, 0x40, 0x0e, 0x01, 0xab, 0x00, 0x63, 0x16}; | 649 | {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; |
640 | __u8 i2cp202[] = | 650 | |
641 | {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15}; | 651 | if (sd->brightness < 127) { |
642 | static __u8 i2cpdoit[] = | 652 | /* change reg 0x0b, signreg */ |
643 | {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}; | 653 | i2cpbright[3] = 0x01; |
644 | 654 | /* set reg 0x0c, offset */ | |
645 | /* change reg 0x10 */ | 655 | i2cpbright[4] = 127 - sd->brightness; |
646 | i2cpexpo[4] = 0xff - sd->brightness; | 656 | } else |
647 | /* if(i2c_w(gspca_dev,i2cpexpo1) < 0) | 657 | i2cpbright[4] = sd->brightness - 127; |
648 | goto err; */ | 658 | |
649 | /* if(i2c_w(gspca_dev,i2cpdoit) < 0) | 659 | if (i2c_w(gspca_dev, i2cpbright) < 0) |
650 | goto err; */ | ||
651 | if (i2c_w(gspca_dev, i2cpexpo) < 0) | ||
652 | goto err; | ||
653 | if (i2c_w(gspca_dev, i2cpdoit) < 0) | ||
654 | goto err; | ||
655 | i2cp202[3] = sd->brightness >> 3; | ||
656 | if (i2c_w(gspca_dev, i2cp202) < 0) | ||
657 | goto err; | 660 | goto err; |
658 | if (i2c_w(gspca_dev, i2cpdoit) < 0) | 661 | if (i2c_w(gspca_dev, i2cpdoit) < 0) |
659 | goto err; | 662 | goto err; |
@@ -706,6 +709,27 @@ static void setsensorgain(struct gspca_dev *gspca_dev) | |||
706 | goto err; | 709 | goto err; |
707 | break; | 710 | break; |
708 | } | 711 | } |
712 | case SENSOR_PAS202: { | ||
713 | __u8 i2cpgain[] = | ||
714 | {0xa0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x63, 0x15}; | ||
715 | __u8 i2cpcolorgain[] = | ||
716 | {0xc0, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x15}; | ||
717 | const __u8 i2cpdoit[] = | ||
718 | {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x63, 0x16}; | ||
719 | |||
720 | i2cpgain[3] = sd->gain >> 3; | ||
721 | i2cpcolorgain[3] = sd->gain >> 4; | ||
722 | i2cpcolorgain[4] = sd->gain >> 4; | ||
723 | i2cpcolorgain[5] = sd->gain >> 4; | ||
724 | |||
725 | if (i2c_w(gspca_dev, i2cpgain) < 0) | ||
726 | goto err; | ||
727 | if (i2c_w(gspca_dev, i2cpcolorgain) < 0) | ||
728 | goto err; | ||
729 | if (i2c_w(gspca_dev, i2cpdoit) < 0) | ||
730 | goto err; | ||
731 | break; | ||
732 | } | ||
709 | } | 733 | } |
710 | return; | 734 | return; |
711 | err: | 735 | err: |
@@ -779,7 +803,7 @@ static void setexposure(struct gspca_dev *gspca_dev) | |||
779 | } else | 803 | } else |
780 | reg10_max = 0x41; | 804 | reg10_max = 0x41; |
781 | 805 | ||
782 | reg11 = (30 * sd->exposure + 999) / 1000; | 806 | reg11 = (15 * sd->exposure + 999) / 1000; |
783 | if (reg11 < 1) | 807 | if (reg11 < 1) |
784 | reg11 = 1; | 808 | reg11 = 1; |
785 | else if (reg11 > 16) | 809 | else if (reg11 > 16) |
@@ -791,8 +815,8 @@ static void setexposure(struct gspca_dev *gspca_dev) | |||
791 | reg11 = 3; | 815 | reg11 = 3; |
792 | 816 | ||
793 | /* frame exposure time in ms = 1000 * reg11 / 30 -> | 817 | /* frame exposure time in ms = 1000 * reg11 / 30 -> |
794 | reg10 = sd->exposure * reg10_max / (1000 * reg11 / 30) */ | 818 | reg10 = (sd->exposure / 2) * reg10_max / (1000 * reg11 / 30) */ |
795 | reg10 = (sd->exposure * 30 * reg10_max) / (1000 * reg11); | 819 | reg10 = (sd->exposure * 15 * reg10_max) / (1000 * reg11); |
796 | 820 | ||
797 | /* Don't allow this to get below 10 when using autogain, the | 821 | /* Don't allow this to get below 10 when using autogain, the |
798 | steps become very large (relatively) when below 10 causing | 822 | steps become very large (relatively) when below 10 causing |
@@ -815,10 +839,53 @@ static void setexposure(struct gspca_dev *gspca_dev) | |||
815 | if (i2c_w(gspca_dev, i2c) == 0) | 839 | if (i2c_w(gspca_dev, i2c) == 0) |
816 | sd->reg11 = reg11; | 840 | sd->reg11 = reg11; |
817 | else | 841 | else |
818 | PDEBUG(D_ERR, "i2c error exposure"); | 842 | goto err; |
843 | break; | ||
844 | } | ||
845 | case SENSOR_PAS202: { | ||
846 | __u8 i2cpframerate[] = | ||
847 | {0xb0, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x16}; | ||
848 | __u8 i2cpexpo[] = | ||
849 | {0xa0, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x16}; | ||
850 | const __u8 i2cpdoit[] = | ||
851 | {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; | ||
852 | int framerate_ctrl; | ||
853 | |||
854 | /* The exposure knee for the autogain algorithm is 200 | ||
855 | (100 ms / 10 fps on other sensors), for values below this | ||
856 | use the control for setting the partial frame expose time, | ||
857 | above that use variable framerate. This way we run at max | ||
858 | framerate (640x480@7.5 fps, 320x240@10fps) until the knee | ||
859 | is reached. Using the variable framerate control above 200 | ||
860 | is better then playing around with both clockdiv + partial | ||
861 | frame exposure times (like we are doing with the ov chips), | ||
862 | as that sometimes leads to jumps in the exposure control, | ||
863 | which are bad for auto exposure. */ | ||
864 | if (sd->exposure < 200) { | ||
865 | i2cpexpo[3] = 255 - (sd->exposure * 255) / 200; | ||
866 | framerate_ctrl = 500; | ||
867 | } else { | ||
868 | /* The PAS202's exposure control goes from 0 - 4095, | ||
869 | but anything below 500 causes vsync issues, so scale | ||
870 | our 200-1023 to 500-4095 */ | ||
871 | framerate_ctrl = (sd->exposure - 200) * 1000 / 229 + | ||
872 | 500; | ||
873 | } | ||
874 | |||
875 | i2cpframerate[3] = framerate_ctrl >> 6; | ||
876 | i2cpframerate[4] = framerate_ctrl & 0x3f; | ||
877 | if (i2c_w(gspca_dev, i2cpframerate) < 0) | ||
878 | goto err; | ||
879 | if (i2c_w(gspca_dev, i2cpexpo) < 0) | ||
880 | goto err; | ||
881 | if (i2c_w(gspca_dev, i2cpdoit) < 0) | ||
882 | goto err; | ||
819 | break; | 883 | break; |
820 | } | 884 | } |
821 | } | 885 | } |
886 | return; | ||
887 | err: | ||
888 | PDEBUG(D_ERR, "i2c error exposure"); | ||
822 | } | 889 | } |
823 | 890 | ||
824 | static void setfreq(struct gspca_dev *gspca_dev) | 891 | static void setfreq(struct gspca_dev *gspca_dev) |
@@ -965,9 +1032,6 @@ static int sd_start(struct gspca_dev *gspca_dev) | |||
965 | reg12_19[6] = sn9c10x[0x18 - 1] | (mode << 4); | 1032 | reg12_19[6] = sn9c10x[0x18 - 1] | (mode << 4); |
966 | /* Special cases where reg 17 and or 19 value depends on mode */ | 1033 | /* Special cases where reg 17 and or 19 value depends on mode */ |
967 | switch (sd->sensor) { | 1034 | switch (sd->sensor) { |
968 | case SENSOR_PAS202: | ||
969 | reg12_19[5] = mode ? 0x24 : 0x20; | ||
970 | break; | ||
971 | case SENSOR_TAS5130CXX: | 1035 | case SENSOR_TAS5130CXX: |
972 | /* probably not mode specific at all most likely the upper | 1036 | /* probably not mode specific at all most likely the upper |
973 | nibble of 0x19 is exposure (clock divider) just as with | 1037 | nibble of 0x19 is exposure (clock divider) just as with |
@@ -1003,6 +1067,16 @@ static int sd_start(struct gspca_dev *gspca_dev) | |||
1003 | sensor_data[sd->sensor].sensor_bridge_init_size[ | 1067 | sensor_data[sd->sensor].sensor_bridge_init_size[ |
1004 | sd->bridge]); | 1068 | sd->bridge]); |
1005 | 1069 | ||
1070 | /* Mode specific sensor setup */ | ||
1071 | switch (sd->sensor) { | ||
1072 | case SENSOR_PAS202: { | ||
1073 | const __u8 i2cpclockdiv[] = | ||
1074 | {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10}; | ||
1075 | /* clockdiv from 4 to 3 (7.5 -> 10 fps) when in low res mode */ | ||
1076 | if (mode) | ||
1077 | i2c_w(gspca_dev, i2cpclockdiv); | ||
1078 | } | ||
1079 | } | ||
1006 | /* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */ | 1080 | /* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */ |
1007 | reg_w(gspca_dev, 0x15, ®12_19[3], 2); | 1081 | reg_w(gspca_dev, 0x15, ®12_19[3], 2); |
1008 | /* compression register */ | 1082 | /* compression register */ |