Commit 6acab15a authored by Paulo Zanoni's avatar Paulo Zanoni Committed by Daniel Vetter

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: default avatarPaulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent 768f69c9
...@@ -1057,6 +1057,10 @@ enum modeset_restore { ...@@ -1057,6 +1057,10 @@ enum modeset_restore {
MODESET_SUSPENDED, MODESET_SUSPENDED,
}; };
struct ddi_vbt_port_info {
uint8_t hdmi_level_shift;
};
struct intel_vbt_data { struct intel_vbt_data {
struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */ struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */ struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
...@@ -1091,6 +1095,8 @@ struct intel_vbt_data { ...@@ -1091,6 +1095,8 @@ struct intel_vbt_data {
int child_dev_num; int child_dev_num;
union child_device_config *child_dev; union child_device_config *child_dev;
struct ddi_vbt_port_info ddi_port_info[I915_MAX_PORTS];
}; };
enum intel_ddb_partitioning { enum intel_ddb_partitioning {
......
...@@ -583,6 +583,76 @@ parse_mipi(struct drm_i915_private *dev_priv, struct bdb_header *bdb) ...@@ -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; 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 static void
parse_device_mapping(struct drm_i915_private *dev_priv, parse_device_mapping(struct drm_i915_private *dev_priv,
struct bdb_header *bdb) struct bdb_header *bdb)
...@@ -652,6 +722,7 @@ static void ...@@ -652,6 +722,7 @@ static void
init_vbt_defaults(struct drm_i915_private *dev_priv) init_vbt_defaults(struct drm_i915_private *dev_priv)
{ {
struct drm_device *dev = dev_priv->dev; struct drm_device *dev = dev_priv->dev;
enum port port;
dev_priv->vbt.crt_ddc_pin = GMBUS_PORT_VGADDC; dev_priv->vbt.crt_ddc_pin = GMBUS_PORT_VGADDC;
...@@ -670,6 +741,11 @@ init_vbt_defaults(struct drm_i915_private *dev_priv) ...@@ -670,6 +741,11 @@ init_vbt_defaults(struct drm_i915_private *dev_priv)
dev_priv->vbt.lvds_use_ssc = 1; dev_priv->vbt.lvds_use_ssc = 1;
dev_priv->vbt.lvds_ssc_freq = intel_bios_ssc_frequency(dev, 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); 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) 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) ...@@ -761,6 +837,7 @@ intel_parse_bios(struct drm_device *dev)
parse_driver_features(dev_priv, bdb); parse_driver_features(dev_priv, bdb);
parse_edp(dev_priv, bdb); parse_edp(dev_priv, bdb);
parse_mipi(dev_priv, bdb); parse_mipi(dev_priv, bdb);
parse_ddi_ports(dev_priv, bdb);
if (bios) if (bios)
pci_unmap_rom(pdev, bios); pci_unmap_rom(pdev, bios);
......
...@@ -648,6 +648,19 @@ int intel_parse_bios(struct drm_device *dev); ...@@ -648,6 +648,19 @@ int intel_parse_bios(struct drm_device *dev);
#define PORT_IDPC 8 #define PORT_IDPC 8
#define PORT_IDPD 9 #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 */ /* MIPI DSI panel info */
struct bdb_mipi { struct bdb_mipi {
u16 panel_id; u16 panel_id;
......
...@@ -42,7 +42,6 @@ static const u32 hsw_ddi_translations_dp[] = { ...@@ -42,7 +42,6 @@ static const u32 hsw_ddi_translations_dp[] = {
0x80C30FFF, 0x000B0000, 0x80C30FFF, 0x000B0000,
0x00FFFFFF, 0x00040006, 0x00FFFFFF, 0x00040006,
0x80D75FFF, 0x000B0000, 0x80D75FFF, 0x000B0000,
0x00FFFFFF, 0x00040006 /* HDMI parameters */
}; };
static const u32 hsw_ddi_translations_fdi[] = { static const u32 hsw_ddi_translations_fdi[] = {
...@@ -55,7 +54,22 @@ static const u32 hsw_ddi_translations_fdi[] = { ...@@ -55,7 +54,22 @@ static const u32 hsw_ddi_translations_fdi[] = {
0x00C30FFF, 0x001E0000, 0x00C30FFF, 0x001E0000,
0x00FFFFFF, 0x00060006, 0x00FFFFFF, 0x00060006,
0x00D75FFF, 0x001E0000, 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) 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) ...@@ -92,12 +106,18 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port)
const u32 *ddi_translations = (port == PORT_E) ? const u32 *ddi_translations = (port == PORT_E) ?
hsw_ddi_translations_fdi : hsw_ddi_translations_fdi :
hsw_ddi_translations_dp; 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); for (i = 0, reg = DDI_BUF_TRANS(port);
i < ARRAY_SIZE(hsw_ddi_translations_fdi); i++) { i < ARRAY_SIZE(hsw_ddi_translations_fdi); i++) {
I915_WRITE(reg, ddi_translations[i]); I915_WRITE(reg, ddi_translations[i]);
reg += 4; 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 /* Program DDI buffers translations for DP. By default, program ports A-D in DP
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment