| From 12d2a77de36195027194674b657edda53ce3cce4 Mon Sep 17 00:00:00 2001 |
| From: Paulo Zanoni <paulo.r.zanoni@intel.com> |
| Date: Thu, 12 Sep 2013 17:06:24 -0300 |
| Subject: 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 <paulo.r.zanoni@intel.com> |
| Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> |
| (cherry picked from commit 6acab15a7b0d2722924c5d671cb29974791beece) |
| Signed-off-by: Darren Hart <dvhart@linux.intel.com> |
| --- |
| drivers/gpu/drm/i915/i915_drv.h | 6 ++ |
| drivers/gpu/drm/i915/intel_bios.c | 77 ++++++++++++++++++++++++++++++++++++++ |
| drivers/gpu/drm/i915/intel_bios.h | 13 ++++++ |
| drivers/gpu/drm/i915/intel_ddi.c | 24 ++++++++++- |
| 4 files changed, 118 insertions(+), 2 deletions(-) |
| |
| --- 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 { |
| --- 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_ |
| 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_privat |
| 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 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); |
| --- 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 * |
| #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; |
| --- 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_fd |
| 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(st |
| 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 |