From: Paulo Zanoni Date: Thu, 12 Sep 2013 20:06:24 +0000 (-0300) Subject: drm/i915: use the HDMI DDI buffer translations from VBT X-Git-Tag: next-20131017~58^2~1^2~86 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=6acab15a7b0d2722924c5d671cb29974791beece;p=karo-tx-linux.git drm/i915: use the HDMI DDI buffer translations from VBT 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 Signed-off-by: Daniel Vetter --- diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9868a66ef59c..2eca286b57ff 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -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 { diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 33003b97f1f0..2f434297246e 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -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); diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h index 1da2bf208299..287cc5a21c2e 100644 --- a/drivers/gpu/drm/i915/intel_bios.h +++ b/drivers/gpu/drm/i915/intel_bios.h @@ -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; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 351e21a0be07..963245a104e4 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -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