Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target...
[GitHub/MotorolaMobilityLLC/kernel-slsi.git] / drivers / gpu / drm / i915 / intel_bios.c
index 33003b97f1f027134bf79ce30e5160fb755cdd3f..6dd622d733b930643f03f40f6fb7f37a35b05df8 100644 (file)
@@ -477,15 +477,13 @@ static void
 parse_driver_features(struct drm_i915_private *dev_priv,
                       struct bdb_header *bdb)
 {
-       struct drm_device *dev = dev_priv->dev;
        struct bdb_driver_features *driver;
 
        driver = find_section(bdb, BDB_DRIVER_FEATURES);
        if (!driver)
                return;
 
-       if (SUPPORTS_EDP(dev) &&
-           driver->lvds_config == BDB_DRIVER_FEATURE_EDP)
+       if (driver->lvds_config == BDB_DRIVER_FEATURE_EDP)
                dev_priv->vbt.edp_support = 1;
 
        if (driver->dual_frequency)
@@ -501,7 +499,7 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
 
        edp = find_section(bdb, BDB_EDP);
        if (!edp) {
-               if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->vbt.edp_support)
+               if (dev_priv->vbt.edp_support)
                        DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported.\n");
                return;
        }
@@ -583,6 +581,129 @@ parse_mipi(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
        dev_priv->vbt.dsi.panel_id = mipi->panel_id;
 }
 
+static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,
+                          struct bdb_header *bdb)
+{
+       union child_device_config *it, *child = NULL;
+       struct ddi_vbt_port_info *info = &dev_priv->vbt.ddi_port_info[port];
+       uint8_t hdmi_level_shift;
+       int i, j;
+       bool is_dvi, is_hdmi, is_dp, is_edp, is_crt;
+       uint8_t aux_channel;
+       /* Each DDI port can have more than one value on the "DVO Port" field,
+        * so look for all the possible values for each port and abort if more
+        * than one is found. */
+       int dvo_ports[][2] = {
+               {DVO_PORT_HDMIA, DVO_PORT_DPA},
+               {DVO_PORT_HDMIB, DVO_PORT_DPB},
+               {DVO_PORT_HDMIC, DVO_PORT_DPC},
+               {DVO_PORT_HDMID, DVO_PORT_DPD},
+               {DVO_PORT_CRT, -1 /* Port E can only be DVO_PORT_CRT */ },
+       };
+
+       /* Find the child device to use, abort if more than one found. */
+       for (i = 0; i < dev_priv->vbt.child_dev_num; i++) {
+               it = dev_priv->vbt.child_dev + i;
+
+               for (j = 0; j < 2; j++) {
+                       if (dvo_ports[port][j] == -1)
+                               break;
+
+                       if (it->common.dvo_port == dvo_ports[port][j]) {
+                               if (child) {
+                                       DRM_DEBUG_KMS("More than one child device for port %c in VBT.\n",
+                                                     port_name(port));
+                                       return;
+                               }
+                               child = it;
+                       }
+               }
+       }
+       if (!child)
+               return;
+
+       aux_channel = child->raw[25];
+
+       is_dvi = child->common.device_type & DEVICE_TYPE_TMDS_DVI_SIGNALING;
+       is_dp = child->common.device_type & DEVICE_TYPE_DISPLAYPORT_OUTPUT;
+       is_crt = child->common.device_type & DEVICE_TYPE_ANALOG_OUTPUT;
+       is_hdmi = is_dvi && (child->common.device_type & DEVICE_TYPE_NOT_HDMI_OUTPUT) == 0;
+       is_edp = is_dp && (child->common.device_type & DEVICE_TYPE_INTERNAL_CONNECTOR);
+
+       info->supports_dvi = is_dvi;
+       info->supports_hdmi = is_hdmi;
+       info->supports_dp = is_dp;
+
+       DRM_DEBUG_KMS("Port %c VBT info: DP:%d HDMI:%d DVI:%d EDP:%d CRT:%d\n",
+                     port_name(port), is_dp, is_hdmi, is_dvi, is_edp, is_crt);
+
+       if (is_edp && is_dvi)
+               DRM_DEBUG_KMS("Internal DP port %c is TMDS compatible\n",
+                             port_name(port));
+       if (is_crt && port != PORT_E)
+               DRM_DEBUG_KMS("Port %c is analog\n", port_name(port));
+       if (is_crt && (is_dvi || is_dp))
+               DRM_DEBUG_KMS("Analog port %c is also DP or TMDS compatible\n",
+                             port_name(port));
+       if (is_dvi && (port == PORT_A || port == PORT_E))
+               DRM_DEBUG_KMS("Port %c is TMDS compabile\n", port_name(port));
+       if (!is_dvi && !is_dp && !is_crt)
+               DRM_DEBUG_KMS("Port %c is not DP/TMDS/CRT compatible\n",
+                             port_name(port));
+       if (is_edp && (port == PORT_B || port == PORT_C || port == PORT_E))
+               DRM_DEBUG_KMS("Port %c is internal DP\n", port_name(port));
+
+       if (is_dvi) {
+               if (child->common.ddc_pin == 0x05 && port != PORT_B)
+                       DRM_DEBUG_KMS("Unexpected DDC pin for port B\n");
+               if (child->common.ddc_pin == 0x04 && port != PORT_C)
+                       DRM_DEBUG_KMS("Unexpected DDC pin for port C\n");
+               if (child->common.ddc_pin == 0x06 && port != PORT_D)
+                       DRM_DEBUG_KMS("Unexpected DDC pin for port D\n");
+       }
+
+       if (is_dp) {
+               if (aux_channel == 0x40 && port != PORT_A)
+                       DRM_DEBUG_KMS("Unexpected AUX channel for port A\n");
+               if (aux_channel == 0x10 && port != PORT_B)
+                       DRM_DEBUG_KMS("Unexpected AUX channel for port B\n");
+               if (aux_channel == 0x20 && port != PORT_C)
+                       DRM_DEBUG_KMS("Unexpected AUX channel for port C\n");
+               if (aux_channel == 0x30 && port != PORT_D)
+                       DRM_DEBUG_KMS("Unexpected AUX channel for port D\n");
+       }
+
+       if (bdb->version >= 158) {
+               /* The VBT HDMI level shift values match the table we have. */
+               hdmi_level_shift = child->raw[7] & 0xF;
+               if (hdmi_level_shift < 0xC) {
+                       DRM_DEBUG_KMS("VBT HDMI level shift for port %c: %d\n",
+                                     port_name(port),
+                                     hdmi_level_shift);
+                       info->hdmi_level_shift = hdmi_level_shift;
+               }
+       }
+}
+
+static void parse_ddi_ports(struct drm_i915_private *dev_priv,
+                           struct bdb_header *bdb)
+{
+       struct drm_device *dev = dev_priv->dev;
+       enum port port;
+
+       if (!HAS_DDI(dev))
+               return;
+
+       if (!dev_priv->vbt.child_dev_num)
+               return;
+
+       if (bdb->version < 155)
+               return;
+
+       for (port = PORT_A; port < I915_MAX_PORTS; port++)
+               parse_ddi_port(dev_priv, port, bdb);
+}
+
 static void
 parse_device_mapping(struct drm_i915_private *dev_priv,
                       struct bdb_header *bdb)
@@ -652,6 +773,7 @@ static void
 init_vbt_defaults(struct drm_i915_private *dev_priv)
 {
        struct drm_device *dev = dev_priv->dev;
+       enum port port;
 
        dev_priv->vbt.crt_ddc_pin = GMBUS_PORT_VGADDC;
 
@@ -670,6 +792,18 @@ init_vbt_defaults(struct drm_i915_private *dev_priv)
        dev_priv->vbt.lvds_use_ssc = 1;
        dev_priv->vbt.lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1);
        DRM_DEBUG_KMS("Set default to SSC at %dMHz\n", dev_priv->vbt.lvds_ssc_freq);
+
+       for (port = PORT_A; port < I915_MAX_PORTS; port++) {
+               struct ddi_vbt_port_info *info =
+                       &dev_priv->vbt.ddi_port_info[port];
+
+               /* Recommended BSpec default: 800mV 0dB. */
+               info->hdmi_level_shift = 6;
+
+               info->supports_dvi = (port != PORT_A && port != PORT_E);
+               info->supports_hdmi = info->supports_dvi;
+               info->supports_dp = (port != PORT_E);
+       }
 }
 
 static int __init intel_no_opregion_vbt_callback(const struct dmi_system_id *id)
@@ -761,6 +895,7 @@ intel_parse_bios(struct drm_device *dev)
        parse_driver_features(dev_priv, bdb);
        parse_edp(dev_priv, bdb);
        parse_mipi(dev_priv, bdb);
+       parse_ddi_ports(dev_priv, bdb);
 
        if (bios)
                pci_unmap_rom(pdev, bios);