diff options
Diffstat (limited to 'drivers/media/video/mt9p031.c')
-rw-r--r-- | drivers/media/video/mt9p031.c | 118 |
1 files changed, 111 insertions, 7 deletions
diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c index 3a9363118e83..8f061d9ac443 100644 --- a/drivers/media/video/mt9p031.c +++ b/drivers/media/video/mt9p031.c | |||
@@ -91,7 +91,14 @@ | |||
91 | #define MT9P031_GLOBAL_GAIN_MAX 1024 | 91 | #define MT9P031_GLOBAL_GAIN_MAX 1024 |
92 | #define MT9P031_GLOBAL_GAIN_DEF 8 | 92 | #define MT9P031_GLOBAL_GAIN_DEF 8 |
93 | #define MT9P031_GLOBAL_GAIN_MULT (1 << 6) | 93 | #define MT9P031_GLOBAL_GAIN_MULT (1 << 6) |
94 | #define MT9P031_ROW_BLACK_TARGET 0x49 | ||
94 | #define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b | 95 | #define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b |
96 | #define MT9P031_GREEN1_OFFSET 0x60 | ||
97 | #define MT9P031_GREEN2_OFFSET 0x61 | ||
98 | #define MT9P031_BLACK_LEVEL_CALIBRATION 0x62 | ||
99 | #define MT9P031_BLC_MANUAL_BLC (1 << 0) | ||
100 | #define MT9P031_RED_OFFSET 0x63 | ||
101 | #define MT9P031_BLUE_OFFSET 0x64 | ||
95 | #define MT9P031_TEST_PATTERN 0xa0 | 102 | #define MT9P031_TEST_PATTERN 0xa0 |
96 | #define MT9P031_TEST_PATTERN_SHIFT 3 | 103 | #define MT9P031_TEST_PATTERN_SHIFT 3 |
97 | #define MT9P031_TEST_PATTERN_ENABLE (1 << 0) | 104 | #define MT9P031_TEST_PATTERN_ENABLE (1 << 0) |
@@ -110,7 +117,6 @@ struct mt9p031 { | |||
110 | struct media_pad pad; | 117 | struct media_pad pad; |
111 | struct v4l2_rect crop; /* Sensor window */ | 118 | struct v4l2_rect crop; /* Sensor window */ |
112 | struct v4l2_mbus_framefmt format; | 119 | struct v4l2_mbus_framefmt format; |
113 | struct v4l2_ctrl_handler ctrls; | ||
114 | struct mt9p031_platform_data *pdata; | 120 | struct mt9p031_platform_data *pdata; |
115 | struct mutex power_lock; /* lock to protect power_count */ | 121 | struct mutex power_lock; /* lock to protect power_count */ |
116 | int power_count; | 122 | int power_count; |
@@ -119,6 +125,10 @@ struct mt9p031 { | |||
119 | struct aptina_pll pll; | 125 | struct aptina_pll pll; |
120 | int reset; | 126 | int reset; |
121 | 127 | ||
128 | struct v4l2_ctrl_handler ctrls; | ||
129 | struct v4l2_ctrl *blc_auto; | ||
130 | struct v4l2_ctrl *blc_offset; | ||
131 | |||
122 | /* Registers cache */ | 132 | /* Registers cache */ |
123 | u16 output_control; | 133 | u16 output_control; |
124 | u16 mode2; | 134 | u16 mode2; |
@@ -565,6 +575,10 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev, | |||
565 | */ | 575 | */ |
566 | 576 | ||
567 | #define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) | 577 | #define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) |
578 | #define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002) | ||
579 | #define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003) | ||
580 | #define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004) | ||
581 | #define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005) | ||
568 | 582 | ||
569 | static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) | 583 | static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) |
570 | { | 584 | { |
@@ -629,11 +643,17 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) | |||
629 | 643 | ||
630 | case V4L2_CID_TEST_PATTERN: | 644 | case V4L2_CID_TEST_PATTERN: |
631 | if (!ctrl->val) { | 645 | if (!ctrl->val) { |
632 | ret = mt9p031_set_mode2(mt9p031, | 646 | /* Restore the black level compensation settings. */ |
633 | 0, MT9P031_READ_MODE_2_ROW_BLC); | 647 | if (mt9p031->blc_auto->cur.val != 0) { |
634 | if (ret < 0) | 648 | ret = mt9p031_s_ctrl(mt9p031->blc_auto); |
635 | return ret; | 649 | if (ret < 0) |
636 | 650 | return ret; | |
651 | } | ||
652 | if (mt9p031->blc_offset->cur.val != 0) { | ||
653 | ret = mt9p031_s_ctrl(mt9p031->blc_offset); | ||
654 | if (ret < 0) | ||
655 | return ret; | ||
656 | } | ||
637 | return mt9p031_write(client, MT9P031_TEST_PATTERN, | 657 | return mt9p031_write(client, MT9P031_TEST_PATTERN, |
638 | MT9P031_TEST_PATTERN_DISABLE); | 658 | MT9P031_TEST_PATTERN_DISABLE); |
639 | } | 659 | } |
@@ -648,10 +668,14 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) | |||
648 | if (ret < 0) | 668 | if (ret < 0) |
649 | return ret; | 669 | return ret; |
650 | 670 | ||
671 | /* Disable digital black level compensation when using a test | ||
672 | * pattern. | ||
673 | */ | ||
651 | ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, | 674 | ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, |
652 | 0); | 675 | 0); |
653 | if (ret < 0) | 676 | if (ret < 0) |
654 | return ret; | 677 | return ret; |
678 | |||
655 | ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0); | 679 | ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0); |
656 | if (ret < 0) | 680 | if (ret < 0) |
657 | return ret; | 681 | return ret; |
@@ -659,7 +683,40 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) | |||
659 | return mt9p031_write(client, MT9P031_TEST_PATTERN, | 683 | return mt9p031_write(client, MT9P031_TEST_PATTERN, |
660 | ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT) | 684 | ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT) |
661 | | MT9P031_TEST_PATTERN_ENABLE); | 685 | | MT9P031_TEST_PATTERN_ENABLE); |
686 | |||
687 | case V4L2_CID_BLC_AUTO: | ||
688 | ret = mt9p031_set_mode2(mt9p031, | ||
689 | ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC, | ||
690 | ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0); | ||
691 | if (ret < 0) | ||
692 | return ret; | ||
693 | |||
694 | return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION, | ||
695 | ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC); | ||
696 | |||
697 | case V4L2_CID_BLC_TARGET_LEVEL: | ||
698 | return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, | ||
699 | ctrl->val); | ||
700 | |||
701 | case V4L2_CID_BLC_ANALOG_OFFSET: | ||
702 | data = ctrl->val & ((1 << 9) - 1); | ||
703 | |||
704 | ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data); | ||
705 | if (ret < 0) | ||
706 | return ret; | ||
707 | ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data); | ||
708 | if (ret < 0) | ||
709 | return ret; | ||
710 | ret = mt9p031_write(client, MT9P031_RED_OFFSET, data); | ||
711 | if (ret < 0) | ||
712 | return ret; | ||
713 | return mt9p031_write(client, MT9P031_BLUE_OFFSET, data); | ||
714 | |||
715 | case V4L2_CID_BLC_DIGITAL_OFFSET: | ||
716 | return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, | ||
717 | ctrl->val & ((1 << 12) - 1)); | ||
662 | } | 718 | } |
719 | |||
663 | return 0; | 720 | return 0; |
664 | } | 721 | } |
665 | 722 | ||
@@ -693,6 +750,46 @@ static const struct v4l2_ctrl_config mt9p031_ctrls[] = { | |||
693 | .flags = 0, | 750 | .flags = 0, |
694 | .menu_skip_mask = 0, | 751 | .menu_skip_mask = 0, |
695 | .qmenu = mt9p031_test_pattern_menu, | 752 | .qmenu = mt9p031_test_pattern_menu, |
753 | }, { | ||
754 | .ops = &mt9p031_ctrl_ops, | ||
755 | .id = V4L2_CID_BLC_AUTO, | ||
756 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
757 | .name = "BLC, Auto", | ||
758 | .min = 0, | ||
759 | .max = 1, | ||
760 | .step = 1, | ||
761 | .def = 1, | ||
762 | .flags = 0, | ||
763 | }, { | ||
764 | .ops = &mt9p031_ctrl_ops, | ||
765 | .id = V4L2_CID_BLC_TARGET_LEVEL, | ||
766 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
767 | .name = "BLC Target Level", | ||
768 | .min = 0, | ||
769 | .max = 4095, | ||
770 | .step = 1, | ||
771 | .def = 168, | ||
772 | .flags = 0, | ||
773 | }, { | ||
774 | .ops = &mt9p031_ctrl_ops, | ||
775 | .id = V4L2_CID_BLC_ANALOG_OFFSET, | ||
776 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
777 | .name = "BLC Analog Offset", | ||
778 | .min = -255, | ||
779 | .max = 255, | ||
780 | .step = 1, | ||
781 | .def = 32, | ||
782 | .flags = 0, | ||
783 | }, { | ||
784 | .ops = &mt9p031_ctrl_ops, | ||
785 | .id = V4L2_CID_BLC_DIGITAL_OFFSET, | ||
786 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
787 | .name = "BLC Digital Offset", | ||
788 | .min = -2048, | ||
789 | .max = 2047, | ||
790 | .step = 1, | ||
791 | .def = 40, | ||
792 | .flags = 0, | ||
696 | } | 793 | } |
697 | }; | 794 | }; |
698 | 795 | ||
@@ -872,9 +969,16 @@ static int mt9p031_probe(struct i2c_client *client, | |||
872 | 969 | ||
873 | mt9p031->subdev.ctrl_handler = &mt9p031->ctrls; | 970 | mt9p031->subdev.ctrl_handler = &mt9p031->ctrls; |
874 | 971 | ||
875 | if (mt9p031->ctrls.error) | 972 | if (mt9p031->ctrls.error) { |
876 | printk(KERN_INFO "%s: control initialization error %d\n", | 973 | printk(KERN_INFO "%s: control initialization error %d\n", |
877 | __func__, mt9p031->ctrls.error); | 974 | __func__, mt9p031->ctrls.error); |
975 | ret = mt9p031->ctrls.error; | ||
976 | goto done; | ||
977 | } | ||
978 | |||
979 | mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO); | ||
980 | mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls, | ||
981 | V4L2_CID_BLC_DIGITAL_OFFSET); | ||
878 | 982 | ||
879 | mutex_init(&mt9p031->power_lock); | 983 | mutex_init(&mt9p031->power_lock); |
880 | v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); | 984 | v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); |