diff options
author | Mats Randgaard <matrandg@cisco.com> | 2013-12-05 08:39:04 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-01-07 03:10:00 -0500 |
commit | 5c6c634916cab638f474dca20b2fba942cfd6ffc (patch) | |
tree | 221f9b1eb793fec3aa850ad4532f69fe0f8d3120 /drivers/media/i2c/adv7604.c | |
parent | 1e0b9156d52ac748c92d5aa7ae9dc602168b0b62 (diff) |
[media] adv7604: adjust gain and offset for DVI-D signals
If the input signal is DVI-D and quantization range is RGB full range,
gain and offset must be adjusted to get the right range on the output.
Signed-off-by: Mats Randgaard <matrandg@cisco.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
Diffstat (limited to 'drivers/media/i2c/adv7604.c')
-rw-r--r-- | drivers/media/i2c/adv7604.c | 98 |
1 files changed, 89 insertions, 9 deletions
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 85caf2451614..d5355d7ba0a0 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c | |||
@@ -885,12 +885,72 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, | |||
885 | cp_write(sd, 0xac, (height & 0x0f) << 4); | 885 | cp_write(sd, 0xac, (height & 0x0f) << 4); |
886 | } | 886 | } |
887 | 887 | ||
888 | static void adv7604_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 offset_a, u16 offset_b, u16 offset_c) | ||
889 | { | ||
890 | struct adv7604_state *state = to_state(sd); | ||
891 | u8 offset_buf[4]; | ||
892 | |||
893 | if (auto_offset) { | ||
894 | offset_a = 0x3ff; | ||
895 | offset_b = 0x3ff; | ||
896 | offset_c = 0x3ff; | ||
897 | } | ||
898 | |||
899 | v4l2_dbg(2, debug, sd, "%s: %s offset: a = 0x%x, b = 0x%x, c = 0x%x\n", | ||
900 | __func__, auto_offset ? "Auto" : "Manual", | ||
901 | offset_a, offset_b, offset_c); | ||
902 | |||
903 | offset_buf[0] = (cp_read(sd, 0x77) & 0xc0) | ((offset_a & 0x3f0) >> 4); | ||
904 | offset_buf[1] = ((offset_a & 0x00f) << 4) | ((offset_b & 0x3c0) >> 6); | ||
905 | offset_buf[2] = ((offset_b & 0x03f) << 2) | ((offset_c & 0x300) >> 8); | ||
906 | offset_buf[3] = offset_c & 0x0ff; | ||
907 | |||
908 | /* Registers must be written in this order with no i2c access in between */ | ||
909 | if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x77, 4, offset_buf)) | ||
910 | v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__); | ||
911 | } | ||
912 | |||
913 | static void adv7604_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, u16 gain_b, u16 gain_c) | ||
914 | { | ||
915 | struct adv7604_state *state = to_state(sd); | ||
916 | u8 gain_buf[4]; | ||
917 | u8 gain_man = 1; | ||
918 | u8 agc_mode_man = 1; | ||
919 | |||
920 | if (auto_gain) { | ||
921 | gain_man = 0; | ||
922 | agc_mode_man = 0; | ||
923 | gain_a = 0x100; | ||
924 | gain_b = 0x100; | ||
925 | gain_c = 0x100; | ||
926 | } | ||
927 | |||
928 | v4l2_dbg(2, debug, sd, "%s: %s gain: a = 0x%x, b = 0x%x, c = 0x%x\n", | ||
929 | __func__, auto_gain ? "Auto" : "Manual", | ||
930 | gain_a, gain_b, gain_c); | ||
931 | |||
932 | gain_buf[0] = ((gain_man << 7) | (agc_mode_man << 6) | ((gain_a & 0x3f0) >> 4)); | ||
933 | gain_buf[1] = (((gain_a & 0x00f) << 4) | ((gain_b & 0x3c0) >> 6)); | ||
934 | gain_buf[2] = (((gain_b & 0x03f) << 2) | ((gain_c & 0x300) >> 8)); | ||
935 | gain_buf[3] = ((gain_c & 0x0ff)); | ||
936 | |||
937 | /* Registers must be written in this order with no i2c access in between */ | ||
938 | if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x73, 4, gain_buf)) | ||
939 | v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__); | ||
940 | } | ||
941 | |||
888 | static void set_rgb_quantization_range(struct v4l2_subdev *sd) | 942 | static void set_rgb_quantization_range(struct v4l2_subdev *sd) |
889 | { | 943 | { |
890 | struct adv7604_state *state = to_state(sd); | 944 | struct adv7604_state *state = to_state(sd); |
945 | bool rgb_output = io_read(sd, 0x02) & 0x02; | ||
946 | bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80; | ||
947 | |||
948 | v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n", | ||
949 | __func__, state->rgb_quantization_range, | ||
950 | rgb_output, hdmi_signal); | ||
891 | 951 | ||
892 | v4l2_dbg(2, debug, sd, "%s: rgb_quantization_range = %d\n", | 952 | adv7604_set_gain(sd, true, 0x0, 0x0, 0x0); |
893 | __func__, state->rgb_quantization_range); | 953 | adv7604_set_offset(sd, true, 0x0, 0x0, 0x0); |
894 | 954 | ||
895 | switch (state->rgb_quantization_range) { | 955 | switch (state->rgb_quantization_range) { |
896 | case V4L2_DV_RGB_RANGE_AUTO: | 956 | case V4L2_DV_RGB_RANGE_AUTO: |
@@ -908,7 +968,7 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) | |||
908 | break; | 968 | break; |
909 | } | 969 | } |
910 | 970 | ||
911 | if (hdmi_read(sd, 0x05) & 0x80) { | 971 | if (hdmi_signal) { |
912 | /* Receiving HDMI signal | 972 | /* Receiving HDMI signal |
913 | * Set automode */ | 973 | * Set automode */ |
914 | io_write_and_or(sd, 0x02, 0x0f, 0xf0); | 974 | io_write_and_or(sd, 0x02, 0x0f, 0xf0); |
@@ -924,24 +984,45 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) | |||
924 | } else { | 984 | } else { |
925 | /* RGB full range (0-255) */ | 985 | /* RGB full range (0-255) */ |
926 | io_write_and_or(sd, 0x02, 0x0f, 0x10); | 986 | io_write_and_or(sd, 0x02, 0x0f, 0x10); |
987 | |||
988 | if (is_digital_input(sd) && rgb_output) { | ||
989 | adv7604_set_offset(sd, false, 0x40, 0x40, 0x40); | ||
990 | } else { | ||
991 | adv7604_set_gain(sd, false, 0xe0, 0xe0, 0xe0); | ||
992 | adv7604_set_offset(sd, false, 0x70, 0x70, 0x70); | ||
993 | } | ||
927 | } | 994 | } |
928 | break; | 995 | break; |
929 | case V4L2_DV_RGB_RANGE_LIMITED: | 996 | case V4L2_DV_RGB_RANGE_LIMITED: |
930 | if (state->selected_input == ADV7604_INPUT_VGA_COMP) { | 997 | if (state->selected_input == ADV7604_INPUT_VGA_COMP) { |
931 | /* YCrCb limited range (16-235) */ | 998 | /* YCrCb limited range (16-235) */ |
932 | io_write_and_or(sd, 0x02, 0x0f, 0x20); | 999 | io_write_and_or(sd, 0x02, 0x0f, 0x20); |
933 | } else { | 1000 | break; |
934 | /* RGB limited range (16-235) */ | ||
935 | io_write_and_or(sd, 0x02, 0x0f, 0x00); | ||
936 | } | 1001 | } |
1002 | |||
1003 | /* RGB limited range (16-235) */ | ||
1004 | io_write_and_or(sd, 0x02, 0x0f, 0x00); | ||
1005 | |||
937 | break; | 1006 | break; |
938 | case V4L2_DV_RGB_RANGE_FULL: | 1007 | case V4L2_DV_RGB_RANGE_FULL: |
939 | if (state->selected_input == ADV7604_INPUT_VGA_COMP) { | 1008 | if (state->selected_input == ADV7604_INPUT_VGA_COMP) { |
940 | /* YCrCb full range (0-255) */ | 1009 | /* YCrCb full range (0-255) */ |
941 | io_write_and_or(sd, 0x02, 0x0f, 0x60); | 1010 | io_write_and_or(sd, 0x02, 0x0f, 0x60); |
1011 | break; | ||
1012 | } | ||
1013 | |||
1014 | /* RGB full range (0-255) */ | ||
1015 | io_write_and_or(sd, 0x02, 0x0f, 0x10); | ||
1016 | |||
1017 | if (is_analog_input(sd) || hdmi_signal) | ||
1018 | break; | ||
1019 | |||
1020 | /* Adjust gain/offset for DVI-D signals only */ | ||
1021 | if (rgb_output) { | ||
1022 | adv7604_set_offset(sd, false, 0x40, 0x40, 0x40); | ||
942 | } else { | 1023 | } else { |
943 | /* RGB full range (0-255) */ | 1024 | adv7604_set_gain(sd, false, 0xe0, 0xe0, 0xe0); |
944 | io_write_and_or(sd, 0x02, 0x0f, 0x10); | 1025 | adv7604_set_offset(sd, false, 0x70, 0x70, 0x70); |
945 | } | 1026 | } |
946 | break; | 1027 | break; |
947 | } | 1028 | } |
@@ -1367,7 +1448,6 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd, | |||
1367 | 1448 | ||
1368 | set_rgb_quantization_range(sd); | 1449 | set_rgb_quantization_range(sd); |
1369 | 1450 | ||
1370 | |||
1371 | if (debug > 1) | 1451 | if (debug > 1) |
1372 | v4l2_print_dv_timings(sd->name, "adv7604_s_dv_timings: ", | 1452 | v4l2_print_dv_timings(sd->name, "adv7604_s_dv_timings: ", |
1373 | timings, true); | 1453 | timings, true); |