diff options
author | Lars-Peter Clausen <lars@metafoo.de> | 2015-01-23 10:52:31 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@osg.samsung.com> | 2015-02-02 08:49:20 -0500 |
commit | b37135e395c37a8d63defafcb567d55220a672f0 (patch) | |
tree | 176a03b4d1ffe3c668f9800510e38b5def0d4aef /drivers/media/i2c | |
parent | bf7dcb8067ed5c3b40768b071d93bd7676e36620 (diff) |
[media] adv7180: Add support for the adv7280-m/adv7281-m/adv7281-ma/adv7282-m
This patch adds support for the adv7280-m/adv2781-m/adv7281-ma/adv7282-m
devices to the adv7180 driver. They are very similar to the
adv7280/adv7281/adv7282 but instead of parallel video out they feature a
MIPI CSI2 transmitter.
The CSI2 transmitter is configured via a separate I2C address, so we need to
register a dummy device for it.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Acked-by: Federico Vaga <federico.vaga@gmail.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Diffstat (limited to 'drivers/media/i2c')
-rw-r--r-- | drivers/media/i2c/adv7180.c | 170 |
1 files changed, 154 insertions, 16 deletions
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index f55364ff2582..195d28f48c30 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c | |||
@@ -124,6 +124,11 @@ | |||
124 | #define ADV7180_REG_NTSC_V_BIT_END 0x00E6 | 124 | #define ADV7180_REG_NTSC_V_BIT_END 0x00E6 |
125 | #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F | 125 | #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F |
126 | 126 | ||
127 | #define ADV7180_REG_CSI_SLAVE_ADDR 0xFE | ||
128 | |||
129 | #define ADV7180_CSI_REG_PWRDN 0x00 | ||
130 | #define ADV7180_CSI_PWRDN 0x80 | ||
131 | |||
127 | #define ADV7180_INPUT_CVBS_AIN1 0x00 | 132 | #define ADV7180_INPUT_CVBS_AIN1 0x00 |
128 | #define ADV7180_INPUT_CVBS_AIN2 0x01 | 133 | #define ADV7180_INPUT_CVBS_AIN2 0x01 |
129 | #define ADV7180_INPUT_CVBS_AIN3 0x02 | 134 | #define ADV7180_INPUT_CVBS_AIN3 0x02 |
@@ -155,10 +160,13 @@ | |||
155 | #define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10 | 160 | #define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10 |
156 | #define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11 | 161 | #define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11 |
157 | 162 | ||
163 | #define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44 | ||
164 | |||
158 | struct adv7180_state; | 165 | struct adv7180_state; |
159 | 166 | ||
160 | #define ADV7180_FLAG_RESET_POWERED BIT(0) | 167 | #define ADV7180_FLAG_RESET_POWERED BIT(0) |
161 | #define ADV7180_FLAG_V2 BIT(1) | 168 | #define ADV7180_FLAG_V2 BIT(1) |
169 | #define ADV7180_FLAG_MIPI_CSI2 BIT(2) | ||
162 | 170 | ||
163 | struct adv7180_chip_info { | 171 | struct adv7180_chip_info { |
164 | unsigned int flags; | 172 | unsigned int flags; |
@@ -181,6 +189,7 @@ struct adv7180_state { | |||
181 | 189 | ||
182 | struct i2c_client *client; | 190 | struct i2c_client *client; |
183 | unsigned int register_page; | 191 | unsigned int register_page; |
192 | struct i2c_client *csi_client; | ||
184 | const struct adv7180_chip_info *chip_info; | 193 | const struct adv7180_chip_info *chip_info; |
185 | }; | 194 | }; |
186 | #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \ | 195 | #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \ |
@@ -213,6 +222,12 @@ static int adv7180_read(struct adv7180_state *state, unsigned int reg) | |||
213 | return i2c_smbus_read_byte_data(state->client, reg & 0xff); | 222 | return i2c_smbus_read_byte_data(state->client, reg & 0xff); |
214 | } | 223 | } |
215 | 224 | ||
225 | static int adv7180_csi_write(struct adv7180_state *state, unsigned int reg, | ||
226 | unsigned int value) | ||
227 | { | ||
228 | return i2c_smbus_write_byte_data(state->csi_client, reg, value); | ||
229 | } | ||
230 | |||
216 | static int adv7180_set_video_standard(struct adv7180_state *state, | 231 | static int adv7180_set_video_standard(struct adv7180_state *state, |
217 | unsigned int std) | 232 | unsigned int std) |
218 | { | 233 | { |
@@ -405,13 +420,31 @@ out: | |||
405 | static int adv7180_set_power(struct adv7180_state *state, bool on) | 420 | static int adv7180_set_power(struct adv7180_state *state, bool on) |
406 | { | 421 | { |
407 | u8 val; | 422 | u8 val; |
423 | int ret; | ||
408 | 424 | ||
409 | if (on) | 425 | if (on) |
410 | val = ADV7180_PWR_MAN_ON; | 426 | val = ADV7180_PWR_MAN_ON; |
411 | else | 427 | else |
412 | val = ADV7180_PWR_MAN_OFF; | 428 | val = ADV7180_PWR_MAN_OFF; |
413 | 429 | ||
414 | return adv7180_write(state, ADV7180_REG_PWR_MAN, val); | 430 | ret = adv7180_write(state, ADV7180_REG_PWR_MAN, val); |
431 | if (ret) | ||
432 | return ret; | ||
433 | |||
434 | if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { | ||
435 | if (on) { | ||
436 | adv7180_csi_write(state, 0xDE, 0x02); | ||
437 | adv7180_csi_write(state, 0xD2, 0xF7); | ||
438 | adv7180_csi_write(state, 0xD8, 0x65); | ||
439 | adv7180_csi_write(state, 0xE0, 0x09); | ||
440 | adv7180_csi_write(state, 0x2C, 0x00); | ||
441 | adv7180_csi_write(state, 0x00, 0x00); | ||
442 | } else { | ||
443 | adv7180_csi_write(state, 0x00, 0x80); | ||
444 | } | ||
445 | } | ||
446 | |||
447 | return 0; | ||
415 | } | 448 | } |
416 | 449 | ||
417 | static int adv7180_s_power(struct v4l2_subdev *sd, int on) | 450 | static int adv7180_s_power(struct v4l2_subdev *sd, int on) |
@@ -549,13 +582,22 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, | |||
549 | static int adv7180_g_mbus_config(struct v4l2_subdev *sd, | 582 | static int adv7180_g_mbus_config(struct v4l2_subdev *sd, |
550 | struct v4l2_mbus_config *cfg) | 583 | struct v4l2_mbus_config *cfg) |
551 | { | 584 | { |
552 | /* | 585 | struct adv7180_state *state = to_state(sd); |
553 | * The ADV7180 sensor supports BT.601/656 output modes. | 586 | |
554 | * The BT.656 is default and not yet configurable by s/w. | 587 | if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { |
555 | */ | 588 | cfg->type = V4L2_MBUS_CSI2; |
556 | cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | | 589 | cfg->flags = V4L2_MBUS_CSI2_1_LANE | |
557 | V4L2_MBUS_DATA_ACTIVE_HIGH; | 590 | V4L2_MBUS_CSI2_CHANNEL_0 | |
558 | cfg->type = V4L2_MBUS_BT656; | 591 | V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; |
592 | } else { | ||
593 | /* | ||
594 | * The ADV7180 sensor supports BT.601/656 output modes. | ||
595 | * The BT.656 is default and not yet configurable by s/w. | ||
596 | */ | ||
597 | cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | | ||
598 | V4L2_MBUS_DATA_ACTIVE_HIGH; | ||
599 | cfg->type = V4L2_MBUS_BT656; | ||
600 | } | ||
559 | 601 | ||
560 | return 0; | 602 | return 0; |
561 | } | 603 | } |
@@ -638,20 +680,32 @@ static int adv7180_select_input(struct adv7180_state *state, unsigned int input) | |||
638 | 680 | ||
639 | static int adv7182_init(struct adv7180_state *state) | 681 | static int adv7182_init(struct adv7180_state *state) |
640 | { | 682 | { |
683 | if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) | ||
684 | adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR, | ||
685 | ADV7180_DEFAULT_CSI_I2C_ADDR << 1); | ||
686 | |||
641 | if (state->chip_info->flags & ADV7180_FLAG_V2) { | 687 | if (state->chip_info->flags & ADV7180_FLAG_V2) { |
642 | /* ADI recommended writes for improved video quality */ | 688 | /* ADI recommended writes for improved video quality */ |
643 | adv7180_write(state, 0x0080, 0x51); | 689 | adv7180_write(state, 0x0080, 0x51); |
644 | adv7180_write(state, 0x0081, 0x51); | 690 | adv7180_write(state, 0x0081, 0x51); |
645 | adv7180_write(state, 0x0082, 0x68); | 691 | adv7180_write(state, 0x0082, 0x68); |
646 | adv7180_write(state, 0x0004, 0x17); | ||
647 | } else { | ||
648 | adv7180_write(state, 0x0004, 0x07); | ||
649 | } | 692 | } |
650 | 693 | ||
651 | /* ADI required writes */ | 694 | /* ADI required writes */ |
652 | adv7180_write(state, 0x0003, 0x0c); | 695 | if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { |
696 | adv7180_write(state, 0x0003, 0x4e); | ||
697 | adv7180_write(state, 0x0004, 0x57); | ||
698 | adv7180_write(state, 0x001d, 0xc0); | ||
699 | } else { | ||
700 | if (state->chip_info->flags & ADV7180_FLAG_V2) | ||
701 | adv7180_write(state, 0x0004, 0x17); | ||
702 | else | ||
703 | adv7180_write(state, 0x0004, 0x07); | ||
704 | adv7180_write(state, 0x0003, 0x0c); | ||
705 | adv7180_write(state, 0x001d, 0x40); | ||
706 | } | ||
707 | |||
653 | adv7180_write(state, 0x0013, 0x00); | 708 | adv7180_write(state, 0x0013, 0x00); |
654 | adv7180_write(state, 0x001d, 0x40); | ||
655 | 709 | ||
656 | return 0; | 710 | return 0; |
657 | } | 711 | } |
@@ -817,15 +871,81 @@ static const struct adv7180_chip_info adv7280_info = { | |||
817 | .select_input = adv7182_select_input, | 871 | .select_input = adv7182_select_input, |
818 | }; | 872 | }; |
819 | 873 | ||
874 | static const struct adv7180_chip_info adv7280_m_info = { | ||
875 | .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, | ||
876 | .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | | ||
877 | BIT(ADV7182_INPUT_CVBS_AIN2) | | ||
878 | BIT(ADV7182_INPUT_CVBS_AIN3) | | ||
879 | BIT(ADV7182_INPUT_CVBS_AIN4) | | ||
880 | BIT(ADV7182_INPUT_CVBS_AIN5) | | ||
881 | BIT(ADV7182_INPUT_CVBS_AIN6) | | ||
882 | BIT(ADV7182_INPUT_CVBS_AIN7) | | ||
883 | BIT(ADV7182_INPUT_CVBS_AIN8) | | ||
884 | BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | | ||
885 | BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | | ||
886 | BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) | | ||
887 | BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | | ||
888 | BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) | | ||
889 | BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6), | ||
890 | .init = adv7182_init, | ||
891 | .set_std = adv7182_set_std, | ||
892 | .select_input = adv7182_select_input, | ||
893 | }; | ||
894 | |||
820 | static const struct adv7180_chip_info adv7281_info = { | 895 | static const struct adv7180_chip_info adv7281_info = { |
821 | .flags = ADV7180_FLAG_V2, | 896 | .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, |
897 | .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | | ||
898 | BIT(ADV7182_INPUT_CVBS_AIN2) | | ||
899 | BIT(ADV7182_INPUT_CVBS_AIN7) | | ||
900 | BIT(ADV7182_INPUT_CVBS_AIN8) | | ||
901 | BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | | ||
902 | BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | | ||
903 | BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | | ||
904 | BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8), | ||
905 | .init = adv7182_init, | ||
906 | .set_std = adv7182_set_std, | ||
907 | .select_input = adv7182_select_input, | ||
908 | }; | ||
909 | |||
910 | static const struct adv7180_chip_info adv7281_m_info = { | ||
911 | .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, | ||
912 | .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | | ||
913 | BIT(ADV7182_INPUT_CVBS_AIN2) | | ||
914 | BIT(ADV7182_INPUT_CVBS_AIN3) | | ||
915 | BIT(ADV7182_INPUT_CVBS_AIN4) | | ||
916 | BIT(ADV7182_INPUT_CVBS_AIN7) | | ||
917 | BIT(ADV7182_INPUT_CVBS_AIN8) | | ||
918 | BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | | ||
919 | BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | | ||
920 | BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | | ||
921 | BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) | | ||
922 | BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | | ||
923 | BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) | | ||
924 | BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8), | ||
925 | .init = adv7182_init, | ||
926 | .set_std = adv7182_set_std, | ||
927 | .select_input = adv7182_select_input, | ||
928 | }; | ||
929 | |||
930 | static const struct adv7180_chip_info adv7281_ma_info = { | ||
931 | .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, | ||
822 | .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | | 932 | .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | |
823 | BIT(ADV7182_INPUT_CVBS_AIN2) | | 933 | BIT(ADV7182_INPUT_CVBS_AIN2) | |
934 | BIT(ADV7182_INPUT_CVBS_AIN3) | | ||
935 | BIT(ADV7182_INPUT_CVBS_AIN4) | | ||
936 | BIT(ADV7182_INPUT_CVBS_AIN5) | | ||
937 | BIT(ADV7182_INPUT_CVBS_AIN6) | | ||
824 | BIT(ADV7182_INPUT_CVBS_AIN7) | | 938 | BIT(ADV7182_INPUT_CVBS_AIN7) | |
825 | BIT(ADV7182_INPUT_CVBS_AIN8) | | 939 | BIT(ADV7182_INPUT_CVBS_AIN8) | |
826 | BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | | 940 | BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | |
941 | BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | | ||
942 | BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) | | ||
827 | BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | | 943 | BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | |
944 | BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) | | ||
945 | BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6) | | ||
828 | BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | | 946 | BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | |
947 | BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) | | ||
948 | BIT(ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6) | | ||
829 | BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8), | 949 | BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8), |
830 | .init = adv7182_init, | 950 | .init = adv7182_init, |
831 | .set_std = adv7182_set_std, | 951 | .set_std = adv7182_set_std, |
@@ -904,6 +1024,13 @@ static int adv7180_probe(struct i2c_client *client, | |||
904 | state->client = client; | 1024 | state->client = client; |
905 | state->chip_info = (struct adv7180_chip_info *)id->driver_data; | 1025 | state->chip_info = (struct adv7180_chip_info *)id->driver_data; |
906 | 1026 | ||
1027 | if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { | ||
1028 | state->csi_client = i2c_new_dummy(client->adapter, | ||
1029 | ADV7180_DEFAULT_CSI_I2C_ADDR); | ||
1030 | if (!state->csi_client) | ||
1031 | return -ENOMEM; | ||
1032 | } | ||
1033 | |||
907 | state->irq = client->irq; | 1034 | state->irq = client->irq; |
908 | mutex_init(&state->mutex); | 1035 | mutex_init(&state->mutex); |
909 | state->autodetect = true; | 1036 | state->autodetect = true; |
@@ -918,7 +1045,7 @@ static int adv7180_probe(struct i2c_client *client, | |||
918 | 1045 | ||
919 | ret = adv7180_init_controls(state); | 1046 | ret = adv7180_init_controls(state); |
920 | if (ret) | 1047 | if (ret) |
921 | goto err_unreg_subdev; | 1048 | goto err_unregister_csi_client; |
922 | 1049 | ||
923 | state->pad.flags = MEDIA_PAD_FL_SOURCE; | 1050 | state->pad.flags = MEDIA_PAD_FL_SOURCE; |
924 | sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER; | 1051 | sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER; |
@@ -951,7 +1078,9 @@ err_media_entity_cleanup: | |||
951 | media_entity_cleanup(&sd->entity); | 1078 | media_entity_cleanup(&sd->entity); |
952 | err_free_ctrl: | 1079 | err_free_ctrl: |
953 | adv7180_exit_controls(state); | 1080 | adv7180_exit_controls(state); |
954 | err_unreg_subdev: | 1081 | err_unregister_csi_client: |
1082 | if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) | ||
1083 | i2c_unregister_device(state->csi_client); | ||
955 | mutex_destroy(&state->mutex); | 1084 | mutex_destroy(&state->mutex); |
956 | return ret; | 1085 | return ret; |
957 | } | 1086 | } |
@@ -968,7 +1097,12 @@ static int adv7180_remove(struct i2c_client *client) | |||
968 | 1097 | ||
969 | media_entity_cleanup(&sd->entity); | 1098 | media_entity_cleanup(&sd->entity); |
970 | adv7180_exit_controls(state); | 1099 | adv7180_exit_controls(state); |
1100 | |||
1101 | if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) | ||
1102 | i2c_unregister_device(state->csi_client); | ||
1103 | |||
971 | mutex_destroy(&state->mutex); | 1104 | mutex_destroy(&state->mutex); |
1105 | |||
972 | return 0; | 1106 | return 0; |
973 | } | 1107 | } |
974 | 1108 | ||
@@ -976,8 +1110,12 @@ static const struct i2c_device_id adv7180_id[] = { | |||
976 | { "adv7180", (kernel_ulong_t)&adv7180_info }, | 1110 | { "adv7180", (kernel_ulong_t)&adv7180_info }, |
977 | { "adv7182", (kernel_ulong_t)&adv7182_info }, | 1111 | { "adv7182", (kernel_ulong_t)&adv7182_info }, |
978 | { "adv7280", (kernel_ulong_t)&adv7280_info }, | 1112 | { "adv7280", (kernel_ulong_t)&adv7280_info }, |
1113 | { "adv7280-m", (kernel_ulong_t)&adv7280_m_info }, | ||
979 | { "adv7281", (kernel_ulong_t)&adv7281_info }, | 1114 | { "adv7281", (kernel_ulong_t)&adv7281_info }, |
1115 | { "adv7281-m", (kernel_ulong_t)&adv7281_m_info }, | ||
1116 | { "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info }, | ||
980 | { "adv7282", (kernel_ulong_t)&adv7281_info }, | 1117 | { "adv7282", (kernel_ulong_t)&adv7281_info }, |
1118 | { "adv7282-m", (kernel_ulong_t)&adv7281_m_info }, | ||
981 | {}, | 1119 | {}, |
982 | }; | 1120 | }; |
983 | MODULE_DEVICE_TABLE(i2c, adv7180_id); | 1121 | MODULE_DEVICE_TABLE(i2c, adv7180_id); |