drm/i915: Per-DDI I_boost override
authorAntti Koskipaa <antti.koskipaa@linux.intel.com>
Fri, 10 Jul 2015 11:10:55 +0000 (14:10 +0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Fri, 14 Aug 2015 16:13:09 +0000 (18:13 +0200)
An OEM may request increased I_boost beyond the recommended values
by specifying an I_boost value to be applied to all swing entries for
a port. These override values are specified in VBT.

v2: rebase and remove unused iboost_bit variable

Issue: VIZ-5676
Signed-off-by: Antti Koskipaa <antti.koskipaa@linux.intel.com>
Reviewed-by: David Weinehall <david.weinehall@linux.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 01abe13e98e010066576c9173e8cd200ebde0268..4273c281ce77a22f7324d22099b4919823a38ceb 100644 (file)
@@ -1429,6 +1429,9 @@ struct ddi_vbt_port_info {
        uint8_t supports_dp:1;
 
        uint8_t alternate_aux_channel;
+
+       uint8_t dp_boost_level;
+       uint8_t hdmi_boost_level;
 };
 
 enum psr_lines_to_wait {
index 990acc20771a6027ad2144edf864011e668604e6..8e46149bafdd2380c6d075ca30cbc967d1c2a1e6 100644 (file)
@@ -886,6 +886,17 @@ err:
        memset(dev_priv->vbt.dsi.sequence, 0, sizeof(dev_priv->vbt.dsi.sequence));
 }
 
+static u8 translate_iboost(u8 val)
+{
+       static const u8 mapping[] = { 1, 3, 7 }; /* See VBT spec */
+
+       if (val >= ARRAY_SIZE(mapping)) {
+               DRM_DEBUG_KMS("Unsupported I_boost value found in VBT (%d), display may not work properly\n", val);
+               return 0;
+       }
+       return mapping[val];
+}
+
 static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,
                           const struct bdb_header *bdb)
 {
@@ -1001,6 +1012,16 @@ static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,
                              hdmi_level_shift);
                info->hdmi_level_shift = hdmi_level_shift;
        }
+
+       /* Parse the I_boost config for SKL and above */
+       if (bdb->version >= 196 && (child->common.flags_1 & IBOOST_ENABLE)) {
+               info->dp_boost_level = translate_iboost(child->common.iboost_level & 0xF);
+               DRM_DEBUG_KMS("VBT (e)DP boost level for port %c: %d\n",
+                             port_name(port), info->dp_boost_level);
+               info->hdmi_boost_level = translate_iboost(child->common.iboost_level >> 4);
+               DRM_DEBUG_KMS("VBT HDMI boost level for port %c: %d\n",
+                             port_name(port), info->hdmi_boost_level);
+       }
 }
 
 static void parse_ddi_ports(struct drm_i915_private *dev_priv,
index f7ad6a585129a311ec003392e5de46c887f303a8..6d909efbf43f3a52655551f71ab91f683ecb16bf 100644 (file)
@@ -231,6 +231,10 @@ struct old_child_dev_config {
 /* This one contains field offsets that are known to be common for all BDB
  * versions. Notice that the meaning of the contents contents may still change,
  * but at least the offsets are consistent. */
+
+/* Definitions for flags_1 */
+#define IBOOST_ENABLE (1<<3)
+
 struct common_child_dev_config {
        u16 handle;
        u16 device_type;
@@ -239,8 +243,13 @@ struct common_child_dev_config {
        u8 not_common2[2];
        u8 ddc_pin;
        u16 edid_ptr;
+       u8 obsolete;
+       u8 flags_1;
+       u8 not_common3[13];
+       u8 iboost_level;
 } __packed;
 
+
 /* This field changes depending on the BDB version, so the most reliable way to
  * read it is by checking the BDB version and reading the raw pointer. */
 union child_device_config {
index 110d5469c86c72005534a508e65cbf4897cb6b02..6cfe65d6a8cf96358f7858494d8232a2cc0b7ba6 100644 (file)
@@ -440,6 +440,7 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port,
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 reg;
+       u32 iboost_bit = 0;
        int i, n_hdmi_entries, n_dp_entries, n_edp_entries, hdmi_default_entry,
            size;
        int hdmi_level = dev_priv->vbt.ddi_port_info[port].hdmi_level_shift;
@@ -466,6 +467,10 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port,
                ddi_translations_hdmi =
                                skl_get_buf_trans_hdmi(dev, &n_hdmi_entries);
                hdmi_default_entry = 8;
+               /* If we're boosting the current, set bit 31 of trans1 */
+               if (dev_priv->vbt.ddi_port_info[port].hdmi_boost_level ||
+                   dev_priv->vbt.ddi_port_info[port].dp_boost_level)
+                       iboost_bit = 1<<31;
        } else if (IS_BROADWELL(dev)) {
                ddi_translations_fdi = bdw_ddi_translations_fdi;
                ddi_translations_dp = bdw_ddi_translations_dp;
@@ -526,7 +531,7 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port,
        }
 
        for (i = 0, reg = DDI_BUF_TRANS(port); i < size; i++) {
-               I915_WRITE(reg, ddi_translations[i].trans1);
+               I915_WRITE(reg, ddi_translations[i].trans1 | iboost_bit);
                reg += 4;
                I915_WRITE(reg, ddi_translations[i].trans2);
                reg += 4;
@@ -541,7 +546,7 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port,
                hdmi_level = hdmi_default_entry;
 
        /* Entry 9 is for HDMI: */
-       I915_WRITE(reg, ddi_translations_hdmi[hdmi_level].trans1);
+       I915_WRITE(reg, ddi_translations_hdmi[hdmi_level].trans1 | iboost_bit);
        reg += 4;
        I915_WRITE(reg, ddi_translations_hdmi[hdmi_level].trans2);
        reg += 4;
@@ -2078,18 +2083,35 @@ static void skl_ddi_set_iboost(struct drm_device *dev, u32 level,
        struct drm_i915_private *dev_priv = dev->dev_private;
        const struct ddi_buf_trans *ddi_translations;
        uint8_t iboost;
+       uint8_t dp_iboost, hdmi_iboost;
        int n_entries;
        u32 reg;
 
+       /* VBT may override standard boost values */
+       dp_iboost = dev_priv->vbt.ddi_port_info[port].dp_boost_level;
+       hdmi_iboost = dev_priv->vbt.ddi_port_info[port].hdmi_boost_level;
+
        if (type == INTEL_OUTPUT_DISPLAYPORT) {
-               ddi_translations = skl_get_buf_trans_dp(dev, &n_entries);
-               iboost = ddi_translations[port].i_boost;
+               if (dp_iboost) {
+                       iboost = dp_iboost;
+               } else {
+                       ddi_translations = skl_get_buf_trans_dp(dev, &n_entries);
+                       iboost = ddi_translations[port].i_boost;
+               }
        } else if (type == INTEL_OUTPUT_EDP) {
-               ddi_translations = skl_get_buf_trans_edp(dev, &n_entries);
-               iboost = ddi_translations[port].i_boost;
+               if (dp_iboost) {
+                       iboost = dp_iboost;
+               } else {
+                       ddi_translations = skl_get_buf_trans_edp(dev, &n_entries);
+                       iboost = ddi_translations[port].i_boost;
+               }
        } else if (type == INTEL_OUTPUT_HDMI) {
-               ddi_translations = skl_get_buf_trans_hdmi(dev, &n_entries);
-               iboost = ddi_translations[port].i_boost;
+               if (hdmi_iboost) {
+                       iboost = hdmi_iboost;
+               } else {
+                       ddi_translations = skl_get_buf_trans_hdmi(dev, &n_entries);
+                       iboost = ddi_translations[port].i_boost;
+               }
        } else {
                return;
        }