diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_hdmi.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_hdmi.c | 168 |
1 files changed, 97 insertions, 71 deletions
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 5a9de21637b7..29ec1535992d 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c | |||
@@ -869,10 +869,15 @@ static enum drm_mode_status | |||
869 | intel_hdmi_mode_valid(struct drm_connector *connector, | 869 | intel_hdmi_mode_valid(struct drm_connector *connector, |
870 | struct drm_display_mode *mode) | 870 | struct drm_display_mode *mode) |
871 | { | 871 | { |
872 | if (mode->clock > hdmi_portclock_limit(intel_attached_hdmi(connector), | 872 | int clock = mode->clock; |
873 | true)) | 873 | |
874 | if (mode->flags & DRM_MODE_FLAG_DBLCLK) | ||
875 | clock *= 2; | ||
876 | |||
877 | if (clock > hdmi_portclock_limit(intel_attached_hdmi(connector), | ||
878 | true)) | ||
874 | return MODE_CLOCK_HIGH; | 879 | return MODE_CLOCK_HIGH; |
875 | if (mode->clock < 20000) | 880 | if (clock < 20000) |
876 | return MODE_CLOCK_LOW; | 881 | return MODE_CLOCK_LOW; |
877 | 882 | ||
878 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | 883 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) |
@@ -890,7 +895,7 @@ static bool hdmi_12bpc_possible(struct intel_crtc *crtc) | |||
890 | if (HAS_GMCH_DISPLAY(dev)) | 895 | if (HAS_GMCH_DISPLAY(dev)) |
891 | return false; | 896 | return false; |
892 | 897 | ||
893 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { | 898 | for_each_intel_encoder(dev, encoder) { |
894 | if (encoder->new_crtc != crtc) | 899 | if (encoder->new_crtc != crtc) |
895 | continue; | 900 | continue; |
896 | 901 | ||
@@ -926,6 +931,10 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, | |||
926 | intel_hdmi->color_range = 0; | 931 | intel_hdmi->color_range = 0; |
927 | } | 932 | } |
928 | 933 | ||
934 | if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) { | ||
935 | pipe_config->pixel_multiplier = 2; | ||
936 | } | ||
937 | |||
929 | if (intel_hdmi->color_range) | 938 | if (intel_hdmi->color_range) |
930 | pipe_config->limited_color_range = true; | 939 | pipe_config->limited_color_range = true; |
931 | 940 | ||
@@ -967,104 +976,117 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, | |||
967 | return true; | 976 | return true; |
968 | } | 977 | } |
969 | 978 | ||
970 | static enum drm_connector_status | 979 | static void |
971 | intel_hdmi_detect(struct drm_connector *connector, bool force) | 980 | intel_hdmi_unset_edid(struct drm_connector *connector) |
972 | { | 981 | { |
973 | struct drm_device *dev = connector->dev; | ||
974 | struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); | 982 | struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); |
975 | struct intel_digital_port *intel_dig_port = | ||
976 | hdmi_to_dig_port(intel_hdmi); | ||
977 | struct intel_encoder *intel_encoder = &intel_dig_port->base; | ||
978 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
979 | struct edid *edid; | ||
980 | enum intel_display_power_domain power_domain; | ||
981 | enum drm_connector_status status = connector_status_disconnected; | ||
982 | 983 | ||
983 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", | 984 | intel_hdmi->has_hdmi_sink = false; |
984 | connector->base.id, connector->name); | 985 | intel_hdmi->has_audio = false; |
986 | intel_hdmi->rgb_quant_range_selectable = false; | ||
987 | |||
988 | kfree(to_intel_connector(connector)->detect_edid); | ||
989 | to_intel_connector(connector)->detect_edid = NULL; | ||
990 | } | ||
991 | |||
992 | static bool | ||
993 | intel_hdmi_set_edid(struct drm_connector *connector) | ||
994 | { | ||
995 | struct drm_i915_private *dev_priv = to_i915(connector->dev); | ||
996 | struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); | ||
997 | struct intel_encoder *intel_encoder = | ||
998 | &hdmi_to_dig_port(intel_hdmi)->base; | ||
999 | enum intel_display_power_domain power_domain; | ||
1000 | struct edid *edid; | ||
1001 | bool connected = false; | ||
985 | 1002 | ||
986 | power_domain = intel_display_port_power_domain(intel_encoder); | 1003 | power_domain = intel_display_port_power_domain(intel_encoder); |
987 | intel_display_power_get(dev_priv, power_domain); | 1004 | intel_display_power_get(dev_priv, power_domain); |
988 | 1005 | ||
989 | intel_hdmi->has_hdmi_sink = false; | ||
990 | intel_hdmi->has_audio = false; | ||
991 | intel_hdmi->rgb_quant_range_selectable = false; | ||
992 | edid = drm_get_edid(connector, | 1006 | edid = drm_get_edid(connector, |
993 | intel_gmbus_get_adapter(dev_priv, | 1007 | intel_gmbus_get_adapter(dev_priv, |
994 | intel_hdmi->ddc_bus)); | 1008 | intel_hdmi->ddc_bus)); |
995 | 1009 | ||
996 | if (edid) { | 1010 | intel_display_power_put(dev_priv, power_domain); |
997 | if (edid->input & DRM_EDID_INPUT_DIGITAL) { | 1011 | |
998 | status = connector_status_connected; | 1012 | to_intel_connector(connector)->detect_edid = edid; |
999 | if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI) | 1013 | if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) { |
1000 | intel_hdmi->has_hdmi_sink = | 1014 | intel_hdmi->rgb_quant_range_selectable = |
1001 | drm_detect_hdmi_monitor(edid); | 1015 | drm_rgb_quant_range_selectable(edid); |
1002 | intel_hdmi->has_audio = drm_detect_monitor_audio(edid); | ||
1003 | intel_hdmi->rgb_quant_range_selectable = | ||
1004 | drm_rgb_quant_range_selectable(edid); | ||
1005 | } | ||
1006 | kfree(edid); | ||
1007 | } | ||
1008 | 1016 | ||
1009 | if (status == connector_status_connected) { | 1017 | intel_hdmi->has_audio = drm_detect_monitor_audio(edid); |
1010 | if (intel_hdmi->force_audio != HDMI_AUDIO_AUTO) | 1018 | if (intel_hdmi->force_audio != HDMI_AUDIO_AUTO) |
1011 | intel_hdmi->has_audio = | 1019 | intel_hdmi->has_audio = |
1012 | (intel_hdmi->force_audio == HDMI_AUDIO_ON); | 1020 | intel_hdmi->force_audio == HDMI_AUDIO_ON; |
1013 | intel_encoder->type = INTEL_OUTPUT_HDMI; | 1021 | |
1022 | if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI) | ||
1023 | intel_hdmi->has_hdmi_sink = | ||
1024 | drm_detect_hdmi_monitor(edid); | ||
1025 | |||
1026 | connected = true; | ||
1014 | } | 1027 | } |
1015 | 1028 | ||
1016 | intel_display_power_put(dev_priv, power_domain); | 1029 | return connected; |
1030 | } | ||
1031 | |||
1032 | static enum drm_connector_status | ||
1033 | intel_hdmi_detect(struct drm_connector *connector, bool force) | ||
1034 | { | ||
1035 | enum drm_connector_status status; | ||
1036 | |||
1037 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", | ||
1038 | connector->base.id, connector->name); | ||
1039 | |||
1040 | intel_hdmi_unset_edid(connector); | ||
1041 | |||
1042 | if (intel_hdmi_set_edid(connector)) { | ||
1043 | struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); | ||
1044 | |||
1045 | hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI; | ||
1046 | status = connector_status_connected; | ||
1047 | } else | ||
1048 | status = connector_status_disconnected; | ||
1017 | 1049 | ||
1018 | return status; | 1050 | return status; |
1019 | } | 1051 | } |
1020 | 1052 | ||
1021 | static int intel_hdmi_get_modes(struct drm_connector *connector) | 1053 | static void |
1054 | intel_hdmi_force(struct drm_connector *connector) | ||
1022 | { | 1055 | { |
1023 | struct intel_encoder *intel_encoder = intel_attached_encoder(connector); | 1056 | struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); |
1024 | struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base); | ||
1025 | struct drm_i915_private *dev_priv = connector->dev->dev_private; | ||
1026 | enum intel_display_power_domain power_domain; | ||
1027 | int ret; | ||
1028 | 1057 | ||
1029 | /* We should parse the EDID data and find out if it's an HDMI sink so | 1058 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", |
1030 | * we can send audio to it. | 1059 | connector->base.id, connector->name); |
1031 | */ | ||
1032 | 1060 | ||
1033 | power_domain = intel_display_port_power_domain(intel_encoder); | 1061 | intel_hdmi_unset_edid(connector); |
1034 | intel_display_power_get(dev_priv, power_domain); | ||
1035 | 1062 | ||
1036 | ret = intel_ddc_get_modes(connector, | 1063 | if (connector->status != connector_status_connected) |
1037 | intel_gmbus_get_adapter(dev_priv, | 1064 | return; |
1038 | intel_hdmi->ddc_bus)); | ||
1039 | 1065 | ||
1040 | intel_display_power_put(dev_priv, power_domain); | 1066 | intel_hdmi_set_edid(connector); |
1067 | hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI; | ||
1068 | } | ||
1041 | 1069 | ||
1042 | return ret; | 1070 | static int intel_hdmi_get_modes(struct drm_connector *connector) |
1071 | { | ||
1072 | struct edid *edid; | ||
1073 | |||
1074 | edid = to_intel_connector(connector)->detect_edid; | ||
1075 | if (edid == NULL) | ||
1076 | return 0; | ||
1077 | |||
1078 | return intel_connector_update_modes(connector, edid); | ||
1043 | } | 1079 | } |
1044 | 1080 | ||
1045 | static bool | 1081 | static bool |
1046 | intel_hdmi_detect_audio(struct drm_connector *connector) | 1082 | intel_hdmi_detect_audio(struct drm_connector *connector) |
1047 | { | 1083 | { |
1048 | struct intel_encoder *intel_encoder = intel_attached_encoder(connector); | ||
1049 | struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base); | ||
1050 | struct drm_i915_private *dev_priv = connector->dev->dev_private; | ||
1051 | enum intel_display_power_domain power_domain; | ||
1052 | struct edid *edid; | ||
1053 | bool has_audio = false; | 1084 | bool has_audio = false; |
1085 | struct edid *edid; | ||
1054 | 1086 | ||
1055 | power_domain = intel_display_port_power_domain(intel_encoder); | 1087 | edid = to_intel_connector(connector)->detect_edid; |
1056 | intel_display_power_get(dev_priv, power_domain); | 1088 | if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) |
1057 | 1089 | has_audio = drm_detect_monitor_audio(edid); | |
1058 | edid = drm_get_edid(connector, | ||
1059 | intel_gmbus_get_adapter(dev_priv, | ||
1060 | intel_hdmi->ddc_bus)); | ||
1061 | if (edid) { | ||
1062 | if (edid->input & DRM_EDID_INPUT_DIGITAL) | ||
1063 | has_audio = drm_detect_monitor_audio(edid); | ||
1064 | kfree(edid); | ||
1065 | } | ||
1066 | |||
1067 | intel_display_power_put(dev_priv, power_domain); | ||
1068 | 1090 | ||
1069 | return has_audio; | 1091 | return has_audio; |
1070 | } | 1092 | } |
@@ -1265,6 +1287,8 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) | |||
1265 | enum pipe pipe = intel_crtc->pipe; | 1287 | enum pipe pipe = intel_crtc->pipe; |
1266 | u32 val; | 1288 | u32 val; |
1267 | 1289 | ||
1290 | intel_hdmi_prepare(encoder); | ||
1291 | |||
1268 | mutex_lock(&dev_priv->dpio_lock); | 1292 | mutex_lock(&dev_priv->dpio_lock); |
1269 | 1293 | ||
1270 | /* program left/right clock distribution */ | 1294 | /* program left/right clock distribution */ |
@@ -1434,8 +1458,8 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) | |||
1434 | 1458 | ||
1435 | for (i = 0; i < 4; i++) { | 1459 | for (i = 0; i < 4; i++) { |
1436 | val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i)); | 1460 | val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i)); |
1437 | val &= ~DPIO_SWING_MARGIN_MASK; | 1461 | val &= ~DPIO_SWING_MARGIN000_MASK; |
1438 | val |= 102 << DPIO_SWING_MARGIN_SHIFT; | 1462 | val |= 102 << DPIO_SWING_MARGIN000_SHIFT; |
1439 | vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val); | 1463 | vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val); |
1440 | } | 1464 | } |
1441 | 1465 | ||
@@ -1482,6 +1506,7 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) | |||
1482 | 1506 | ||
1483 | static void intel_hdmi_destroy(struct drm_connector *connector) | 1507 | static void intel_hdmi_destroy(struct drm_connector *connector) |
1484 | { | 1508 | { |
1509 | kfree(to_intel_connector(connector)->detect_edid); | ||
1485 | drm_connector_cleanup(connector); | 1510 | drm_connector_cleanup(connector); |
1486 | kfree(connector); | 1511 | kfree(connector); |
1487 | } | 1512 | } |
@@ -1489,6 +1514,7 @@ static void intel_hdmi_destroy(struct drm_connector *connector) | |||
1489 | static const struct drm_connector_funcs intel_hdmi_connector_funcs = { | 1514 | static const struct drm_connector_funcs intel_hdmi_connector_funcs = { |
1490 | .dpms = intel_connector_dpms, | 1515 | .dpms = intel_connector_dpms, |
1491 | .detect = intel_hdmi_detect, | 1516 | .detect = intel_hdmi_detect, |
1517 | .force = intel_hdmi_force, | ||
1492 | .fill_modes = drm_helper_probe_single_connector_modes, | 1518 | .fill_modes = drm_helper_probe_single_connector_modes, |
1493 | .set_property = intel_hdmi_set_property, | 1519 | .set_property = intel_hdmi_set_property, |
1494 | .destroy = intel_hdmi_destroy, | 1520 | .destroy = intel_hdmi_destroy, |