drm/i915: use the HDMI DDI buffer translations from VBT
authorPaulo Zanoni <paulo.r.zanoni@intel.com>
Thu, 12 Sep 2013 20:06:24 +0000 (17:06 -0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Tue, 1 Oct 2013 05:45:04 +0000 (07:45 +0200)
We currently use the recommended values from BSpec, but the VBT
specifies the correct value to use for the hardware we have, so use
it. We also fall back to the recommended value in case we can't find
the VBT.

In addition, this code also provides some infrastructure to parse more
information about the DDI ports. There's a lot more information we
could extract and use in the future.

v2: - Move some code to init_vbt_defaults.
v3: - Rebase
    - Clarify the "DVO Port" matching code
v4: - Use I915_MAX_PORTS
    - Change the HAS_DDI checks
    - Replace DRM_ERROR with DRM_DEBUG_KMS

Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/intel_bios.c
drivers/gpu/drm/i915/intel_bios.h
drivers/gpu/drm/i915/intel_ddi.c

index 9868a66ef59c8c0b7dfd92bbbed7eb119e19e5bc..2eca286b57ffce1c38fedfbe97539901fa210efb 100644 (file)
@@ -1057,6 +1057,10 @@ enum modeset_restore {
        MODESET_SUSPENDED,
 };
 
+struct ddi_vbt_port_info {
+       uint8_t hdmi_level_shift;
+};
+
 struct intel_vbt_data {
        struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
        struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
@@ -1091,6 +1095,8 @@ struct intel_vbt_data {
 
        int child_dev_num;
        union child_device_config *child_dev;
+
+       struct ddi_vbt_port_info ddi_port_info[I915_MAX_PORTS];
 };
 
 enum intel_ddb_partitioning {
index 33003b97f1f027134bf79ce30e5160fb755cdd3f..2f434297246e53534a0b453022e1927a581d99af 100644 (file)
@@ -583,6 +583,76 @@ 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;
+       /* 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;
+
+       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 +722,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 +741,11 @@ 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++) {
+               /* Recommended BSpec default: 800mV 0dB. */
+               dev_priv->vbt.ddi_port_info[port].hdmi_level_shift = 6;
+       }
 }
 
 static int __init intel_no_opregion_vbt_callback(const struct dmi_system_id *id)
@@ -761,6 +837,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);
index 1da2bf208299727000116adef78d7802de4e2a5e..287cc5a21c2ef636dee0b4198f8c59665bd1fd5e 100644 (file)
@@ -648,6 +648,19 @@ int intel_parse_bios(struct drm_device *dev);
 #define                PORT_IDPC       8
 #define                PORT_IDPD       9
 
+/* Possible values for the "DVO Port" field for versions >= 155: */
+#define DVO_PORT_HDMIA 0
+#define DVO_PORT_HDMIB 1
+#define DVO_PORT_HDMIC 2
+#define DVO_PORT_HDMID 3
+#define DVO_PORT_LVDS  4
+#define DVO_PORT_TV    5
+#define DVO_PORT_CRT   6
+#define DVO_PORT_DPB   7
+#define DVO_PORT_DPC   8
+#define DVO_PORT_DPD   9
+#define DVO_PORT_DPA   10
+
 /* MIPI DSI panel info */
 struct bdb_mipi {
        u16 panel_id;
index 351e21a0be0720c2fb6af8620304ba1337cc5d50..963245a104e4ed732b39556d2f27f35152288347 100644 (file)
@@ -42,7 +42,6 @@ static const u32 hsw_ddi_translations_dp[] = {
        0x80C30FFF, 0x000B0000,
        0x00FFFFFF, 0x00040006,
        0x80D75FFF, 0x000B0000,
-       0x00FFFFFF, 0x00040006          /* HDMI parameters */
 };
 
 static const u32 hsw_ddi_translations_fdi[] = {
@@ -55,7 +54,22 @@ static const u32 hsw_ddi_translations_fdi[] = {
        0x00C30FFF, 0x001E0000,
        0x00FFFFFF, 0x00060006,
        0x00D75FFF, 0x001E0000,
-       0x00FFFFFF, 0x00040006          /* HDMI parameters */
+};
+
+static const u32 hsw_ddi_translations_hdmi[] = {
+                               /* Idx  NT mV diff      T mV diff       db  */
+       0x00FFFFFF, 0x0006000E, /* 0:   400             400             0   */
+       0x00E79FFF, 0x000E000C, /* 1:   400             500             2   */
+       0x00D75FFF, 0x0005000A, /* 2:   400             600             3.5 */
+       0x00FFFFFF, 0x0005000A, /* 3:   600             600             0   */
+       0x00E79FFF, 0x001D0007, /* 4:   600             750             2   */
+       0x00D75FFF, 0x000C0004, /* 5:   600             900             3.5 */
+       0x00FFFFFF, 0x00040006, /* 6:   800             800             0   */
+       0x80E79FFF, 0x00030002, /* 7:   800             1000            2   */
+       0x00FFFFFF, 0x00140005, /* 8:   850             850             0   */
+       0x00FFFFFF, 0x000C0004, /* 9:   900             900             0   */
+       0x00FFFFFF, 0x001C0003, /* 10:  950             950             0   */
+       0x80FFFFFF, 0x00030002, /* 11:  1000            1000            0   */
 };
 
 enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
@@ -92,12 +106,18 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port)
        const u32 *ddi_translations = (port == PORT_E) ?
                hsw_ddi_translations_fdi :
                hsw_ddi_translations_dp;
+       int hdmi_level = dev_priv->vbt.ddi_port_info[port].hdmi_level_shift;
 
        for (i = 0, reg = DDI_BUF_TRANS(port);
             i < ARRAY_SIZE(hsw_ddi_translations_fdi); i++) {
                I915_WRITE(reg, ddi_translations[i]);
                reg += 4;
        }
+       /* Entry 9 is for HDMI: */
+       for (i = 0; i < 2; i++) {
+               I915_WRITE(reg, hsw_ddi_translations_hdmi[hdmi_level * 2 + i]);
+               reg += 4;
+       }
 }
 
 /* Program DDI buffers translations for DP. By default, program ports A-D in DP