drm/i915/skl: Buffer translation improvements
diff mbox

Message ID 20150618095033.GH14570@boom
State New
Headers show

Commit Message

David Weinehall June 18, 2015, 9:50 a.m. UTC
This patch adds support for 0.85V VccIO on Skylake Y,
separate buffer translation tables for Skylake U,
and support for I_boost for the entries that needs this.

Issue: VIZ-5677
Signed-off-by: David Weinehall <david.weinehall@linux.intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h  |   8 +
 drivers/gpu/drm/i915/i915_reg.h  |  12 ++
 drivers/gpu/drm/i915/intel_ddi.c | 433 +++++++++++++++++++++++++++++----------
 drivers/gpu/drm/i915/intel_dp.c  |  46 ++++-
 drivers/gpu/drm/i915/intel_drv.h |   2 +
 5 files changed, 379 insertions(+), 122 deletions(-)

Comments

Chris Wilson June 18, 2015, 10:10 a.m. UTC | #1
On Thu, Jun 18, 2015 at 12:50:33PM +0300, David Weinehall wrote:
> +static const struct ddi_buf_trans *skl_get_buf_trans_dp(struct drm_device *dev,

struct drm_i915_private not struct drm_device!

> +							int *n_entries)
> +{
> +	struct drm_i915_private *dev_priv = dev->dev_private;
> +	const struct ddi_buf_trans *ddi_translations;
> +	static int is_095v = -1;
> +
> +	if (is_095v == -1) {
> +		u32 spr1 = I915_READ(UAIMI_SPR1);
> +		is_095v = spr1 & SKL_VCCIO_MASK;
> +	}
> +
> +	if (IS_SKL_ULX(dev) && !is_095v) {
> +		ddi_translations = skl_y_085v_ddi_translations_dp;
> +		*n_entries = ARRAY_SIZE(skl_y_085v_ddi_translations_dp);
> +	} else if (IS_SKL_ULT(dev)) {
> +		ddi_translations = skl_u_ddi_translations_dp;
> +		*n_entries = ARRAY_SIZE(skl_u_ddi_translations_dp);
> +	} else {
> +		ddi_translations = skl_ddi_translations_dp;
> +		*n_entries = ARRAY_SIZE(skl_ddi_translations_dp);
> +	}

These are static routing, but called fairly often. (Often enough that
you care to only read the reigster once.) Any reason not to preserve
these routing tables in dev_priv or, slightly more preferrable, intel_dp?
-Chris
David Weinehall June 18, 2015, 10:47 a.m. UTC | #2
On Thu, Jun 18, 2015 at 11:10:13AM +0100, Chris Wilson wrote:
> On Thu, Jun 18, 2015 at 12:50:33PM +0300, David Weinehall wrote:
> > +static const struct ddi_buf_trans *skl_get_buf_trans_dp(struct drm_device *dev,
> 
> struct drm_i915_private not struct drm_device!

The device uses both dev and dev_priv; only passing in
drm_i915_private wouldn't provide access to dev,
or am I missing something?

> > +							int *n_entries)
> > +{
> > +	struct drm_i915_private *dev_priv = dev->dev_private;
> > +	const struct ddi_buf_trans *ddi_translations;
> > +	static int is_095v = -1;
> > +
> > +	if (is_095v == -1) {
> > +		u32 spr1 = I915_READ(UAIMI_SPR1);
> > +		is_095v = spr1 & SKL_VCCIO_MASK;
> > +	}
> > +
> > +	if (IS_SKL_ULX(dev) && !is_095v) {
> > +		ddi_translations = skl_y_085v_ddi_translations_dp;
> > +		*n_entries = ARRAY_SIZE(skl_y_085v_ddi_translations_dp);
> > +	} else if (IS_SKL_ULT(dev)) {
> > +		ddi_translations = skl_u_ddi_translations_dp;
> > +		*n_entries = ARRAY_SIZE(skl_u_ddi_translations_dp);
> > +	} else {
> > +		ddi_translations = skl_ddi_translations_dp;
> > +		*n_entries = ARRAY_SIZE(skl_ddi_translations_dp);
> > +	}
> 
> These are static routing, but called fairly often. (Often enough that
> you care to only read the reigster once.) Any reason not to preserve
> these routing tables in dev_priv or, slightly more preferrable, intel_dp?
> -Chris

No particularly good reason, so yes, it's a good proposal.
The reason I cached the register read was mostly because the less reads
from hardware the better (I was thinking of whether having the value
read more than once was too much already, but I figured that having
a global variable to hold a Skylake-specific value would be ugly).

I'll fix it on Monday (tomorrow is a public holiday here, so I'll
be traveling this weekend).


Kind regards, David
Chris Wilson June 18, 2015, 10:59 a.m. UTC | #3
On Thu, Jun 18, 2015 at 01:47:30PM +0300, David Weinehall wrote:
> On Thu, Jun 18, 2015 at 11:10:13AM +0100, Chris Wilson wrote:
> > On Thu, Jun 18, 2015 at 12:50:33PM +0300, David Weinehall wrote:
> > > +static const struct ddi_buf_trans *skl_get_buf_trans_dp(struct drm_device *dev,
> > 
> > struct drm_i915_private not struct drm_device!
> 
> The device uses both dev and dev_priv; only passing in
> drm_i915_private wouldn't provide access to dev,
> or am I missing something?

Hmm, Oh, you didn't set bits in intel_info for ult/ulx. Instead you have
large if chains hidden in macros.

So another task is to translate IS_*_UL? over to using a field in
intel_info to reduce the code size (at the expense of more intel_info
data).

#define IS_SKL_ULT(P) (IS_SKL(P) && INTEL_INFO(P)->is_ult)
#define IS_SKL_ULX(P) (IS_SKL(P) && INTEL_INFO(P)->is_ulx)
-Chris
Daniel Vetter June 18, 2015, 3:05 p.m. UTC | #4
On Thu, Jun 18, 2015 at 12:50:33PM +0300, David Weinehall wrote:
> @@ -3520,6 +3545,9 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
>  	} else if (HAS_DDI(dev)) {
>  		signal_levels = hsw_signal_levels(train_set);
>  		mask = DDI_BUF_EMP_MASK;
> +
> +		if (IS_SKYLAKE(dev))
> +			skl_set_iboost(intel_dp);

Imo this should be put into hsw_signal_levels and then hsw_signal_levels
be moved into intel_ddi.c - that way everything related to low-level ddi
DP signal level code in intel_ddi.c.
-Daniel
David Weinehall June 23, 2015, 10:28 a.m. UTC | #5
On Thu, Jun 18, 2015 at 11:59:34AM +0100, Chris Wilson wrote:
> On Thu, Jun 18, 2015 at 01:47:30PM +0300, David Weinehall wrote:
> > On Thu, Jun 18, 2015 at 11:10:13AM +0100, Chris Wilson wrote:
> > > On Thu, Jun 18, 2015 at 12:50:33PM +0300, David Weinehall wrote:
> > > > +static const struct ddi_buf_trans *skl_get_buf_trans_dp(struct drm_device *dev,
> > > 
> > > struct drm_i915_private not struct drm_device!
> > 
> > The device uses both dev and dev_priv; only passing in
> > drm_i915_private wouldn't provide access to dev,
> > or am I missing something?
> 
> Hmm, Oh, you didn't set bits in intel_info for ult/ulx. Instead you have
> large if chains hidden in macros.
> 
> So another task is to translate IS_*_UL? over to using a field in
> intel_info to reduce the code size (at the expense of more intel_info
> data).
> 
> #define IS_SKL_ULT(P) (IS_SKL(P) && INTEL_INFO(P)->is_ult)
> #define IS_SKL_ULX(P) (IS_SKL(P) && INTEL_INFO(P)->is_ulx)

I just followed the pattern of the existing IS_xxx_yyy macros.
Cleanup sounds sensible; this patch merely aims to add a missing
feature.


Kind regards, David
David Weinehall June 23, 2015, 10:47 a.m. UTC | #6
On Thu, Jun 18, 2015 at 05:05:21PM +0200, Daniel Vetter wrote:
> On Thu, Jun 18, 2015 at 12:50:33PM +0300, David Weinehall wrote:
> > @@ -3520,6 +3545,9 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
> >  	} else if (HAS_DDI(dev)) {
> >  		signal_levels = hsw_signal_levels(train_set);
> >  		mask = DDI_BUF_EMP_MASK;
> > +
> > +		if (IS_SKYLAKE(dev))
> > +			skl_set_iboost(intel_dp);
> 
> Imo this should be put into hsw_signal_levels and then hsw_signal_levels
> be moved into intel_ddi.c - that way everything related to low-level ddi
> DP signal level code in intel_ddi.c.

I'll have a go at it.


Regards, David
David Weinehall June 23, 2015, 11:12 a.m. UTC | #7
On Thu, Jun 18, 2015 at 05:05:21PM +0200, Daniel Vetter wrote:
> On Thu, Jun 18, 2015 at 12:50:33PM +0300, David Weinehall wrote:
> > @@ -3520,6 +3545,9 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
> >  	} else if (HAS_DDI(dev)) {
> >  		signal_levels = hsw_signal_levels(train_set);
> >  		mask = DDI_BUF_EMP_MASK;
> > +
> > +		if (IS_SKYLAKE(dev))
> > +			skl_set_iboost(intel_dp);
> 
> Imo this should be put into hsw_signal_levels and then hsw_signal_levels
> be moved into intel_ddi.c - that way everything related to low-level ddi
> DP signal level code in intel_ddi.c.

I'm guessing the BXT code should be moved there too
and preferably folded in under HAS_DDI(dev)?


Kind regards, David
Daniel Vetter June 23, 2015, 12:07 p.m. UTC | #8
On Tue, Jun 23, 2015 at 02:12:41PM +0300, David Weinehall wrote:
> On Thu, Jun 18, 2015 at 05:05:21PM +0200, Daniel Vetter wrote:
> > On Thu, Jun 18, 2015 at 12:50:33PM +0300, David Weinehall wrote:
> > > @@ -3520,6 +3545,9 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
> > >  	} else if (HAS_DDI(dev)) {
> > >  		signal_levels = hsw_signal_levels(train_set);
> > >  		mask = DDI_BUF_EMP_MASK;
> > > +
> > > +		if (IS_SKYLAKE(dev))
> > > +			skl_set_iboost(intel_dp);
> > 
> > Imo this should be put into hsw_signal_levels and then hsw_signal_levels
> > be moved into intel_ddi.c - that way everything related to low-level ddi
> > DP signal level code in intel_ddi.c.
> 
> I'm guessing the BXT code should be moved there too
> and preferably folded in under HAS_DDI(dev)?

Yeah that would make sense. And given the long list of vfuncs probably
also time to add a vfunc for this? But that's a bit ouf of scope now,
perhaps a follow-up.
-Daniel
sonika.jindal@intel.com June 24, 2015, 4:47 a.m. UTC | #9
On 6/23/2015 4:42 PM, David Weinehall wrote:
> On Thu, Jun 18, 2015 at 05:05:21PM +0200, Daniel Vetter wrote:
>> On Thu, Jun 18, 2015 at 12:50:33PM +0300, David Weinehall wrote:
>>> @@ -3520,6 +3545,9 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
>>>   	} else if (HAS_DDI(dev)) {
>>>   		signal_levels = hsw_signal_levels(train_set);
>>>   		mask = DDI_BUF_EMP_MASK;
>>> +
>>> +		if (IS_SKYLAKE(dev))
>>> +			skl_set_iboost(intel_dp);
>>
>> Imo this should be put into hsw_signal_levels and then hsw_signal_levels
>> be moved into intel_ddi.c - that way everything related to low-level ddi
>> DP signal level code in intel_ddi.c.
>
> I'm guessing the BXT code should be moved there too
> and preferably folded in under HAS_DDI(dev)?
No, it is not required. The Vswing programming for SKL and BXT is 
completely different. So, better keep it separate.

>
>
> Kind regards, David
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
>
David Weinehall June 25, 2015, 8:09 a.m. UTC | #10
On Thu, Jun 18, 2015 at 11:10:13AM +0100, Chris Wilson wrote:
> These are static routing, but called fairly often. (Often enough that
> you care to only read the register once.) Any reason not to preserve
> these routing tables in dev_priv or, slightly more preferrable, intel_dp?

Looking over this a bit, I realise that Broxton suffers from the same
problem, *but* uses a different struct.  I think it'd be best to leave
things the way they are for the time being and try to come up with a
good solution that would work for both platforms without introducing
separate struct pointers to dev_priv (intel_dp while logically the
better option, is trickier to use from intel_ddi, since the function
that would set this up doesn't use intel_dp currently).

Opinions?


Kind regards, David
David Weinehall June 25, 2015, 10:18 a.m. UTC | #11
On Wed, Jun 24, 2015 at 10:17:34AM +0530, Jindal, Sonika wrote:
> 
> 
> On 6/23/2015 4:42 PM, David Weinehall wrote:
> >On Thu, Jun 18, 2015 at 05:05:21PM +0200, Daniel Vetter wrote:
> >>On Thu, Jun 18, 2015 at 12:50:33PM +0300, David Weinehall wrote:
> >>>@@ -3520,6 +3545,9 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
> >>>  	} else if (HAS_DDI(dev)) {
> >>>  		signal_levels = hsw_signal_levels(train_set);
> >>>  		mask = DDI_BUF_EMP_MASK;
> >>>+
> >>>+		if (IS_SKYLAKE(dev))
> >>>+			skl_set_iboost(intel_dp);
> >>
> >>Imo this should be put into hsw_signal_levels and then hsw_signal_levels
> >>be moved into intel_ddi.c - that way everything related to low-level ddi
> >>DP signal level code in intel_ddi.c.
> >
> >I'm guessing the BXT code should be moved there too
> >and preferably folded in under HAS_DDI(dev)?
> No, it is not required. The Vswing programming for SKL and BXT is completely
> different. So, better keep it separate.

It's not a matter of whether it's required, but whether it makes sense
from a consistency perspective.  From a logical point of view it makes
sense, and keeping them in the same place also makes it possible to
reuse quite a lot of code.  Hence my v2 does so.


Kind regards, David

Patch
diff mbox

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 491ef0cfcb0b..09a57a584f5f 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2415,6 +2415,14 @@  struct drm_i915_cmd_table {
 /* ULX machines are also considered ULT. */
 #define IS_HSW_ULX(dev)		(INTEL_DEVID(dev) == 0x0A0E || \
 				 INTEL_DEVID(dev) == 0x0A1E)
+#define IS_SKL_ULT(dev)		(INTEL_DEVID(dev) == 0x1906 || \
+				 INTEL_DEVID(dev) == 0x1913 || \
+				 INTEL_DEVID(dev) == 0x1916 || \
+				 INTEL_DEVID(dev) == 0x1921 || \
+				 INTEL_DEVID(dev) == 0x1926)
+#define IS_SKL_ULX(dev)		(INTEL_DEVID(dev) == 0x190E || \
+				 INTEL_DEVID(dev) == 0x1915 || \
+				 INTEL_DEVID(dev) == 0x191E)
 #define IS_PRELIMINARY_HW(intel_info) ((intel_info)->is_preliminary)
 
 #define SKL_REVID_A0		(0x0)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 0b979ad16d41..fb63ead2b8eb 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1382,6 +1382,18 @@  enum skl_disp_power_wells {
 							_PORT_TX_DW14_LN0_C) + \
 					 _BXT_LANE_OFFSET(lane))
 
+/* UAIMI scratch pad register 1 */
+#define UAIMI_SPR1			0x4F074
+/* SKL VccIO mask */
+#define SKL_VCCIO_MASK			0x1
+/* SKL balance leg register */
+#define DISPIO_CR_TX_BMU_CR0		0x6C00C
+/* I_boost values */
+#define BALANCE_LEG_SHIFT(port)		(8+3*(port))
+#define BALANCE_LEG_MASK(port)		(7<<(8+3*(port)))
+/* Balance leg disable bits */
+#define BALANCE_LEG_DISABLE_SHIFT	23
+
 /*
  * Fence registers
  */
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index 31b29e8781ac..3abcb43bec8b 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -31,6 +31,7 @@ 
 struct ddi_buf_trans {
 	u32 trans1;	/* balance leg enable, de-emph level */
 	u32 trans2;	/* vref sel, vswing */
+	u8 i_boost;	/* SKL: I_boost; valid: 0x0, 0x1, 0x3, 0x7 */
 };
 
 /* HDMI/DVI modes ignore everything but the last 2 items. So we share
@@ -38,134 +39,213 @@  struct ddi_buf_trans {
  * automatically adapt to HDMI connections as well
  */
 static const struct ddi_buf_trans hsw_ddi_translations_dp[] = {
-	{ 0x00FFFFFF, 0x0006000E },
-	{ 0x00D75FFF, 0x0005000A },
-	{ 0x00C30FFF, 0x00040006 },
-	{ 0x80AAAFFF, 0x000B0000 },
-	{ 0x00FFFFFF, 0x0005000A },
-	{ 0x00D75FFF, 0x000C0004 },
-	{ 0x80C30FFF, 0x000B0000 },
-	{ 0x00FFFFFF, 0x00040006 },
-	{ 0x80D75FFF, 0x000B0000 },
+	{ 0x00FFFFFF, 0x0006000E, 0x0 },
+	{ 0x00D75FFF, 0x0005000A, 0x0 },
+	{ 0x00C30FFF, 0x00040006, 0x0 },
+	{ 0x80AAAFFF, 0x000B0000, 0x0 },
+	{ 0x00FFFFFF, 0x0005000A, 0x0 },
+	{ 0x00D75FFF, 0x000C0004, 0x0 },
+	{ 0x80C30FFF, 0x000B0000, 0x0 },
+	{ 0x00FFFFFF, 0x00040006, 0x0 },
+	{ 0x80D75FFF, 0x000B0000, 0x0 },
 };
 
 static const struct ddi_buf_trans hsw_ddi_translations_fdi[] = {
-	{ 0x00FFFFFF, 0x0007000E },
-	{ 0x00D75FFF, 0x000F000A },
-	{ 0x00C30FFF, 0x00060006 },
-	{ 0x00AAAFFF, 0x001E0000 },
-	{ 0x00FFFFFF, 0x000F000A },
-	{ 0x00D75FFF, 0x00160004 },
-	{ 0x00C30FFF, 0x001E0000 },
-	{ 0x00FFFFFF, 0x00060006 },
-	{ 0x00D75FFF, 0x001E0000 },
+	{ 0x00FFFFFF, 0x0007000E, 0x0 },
+	{ 0x00D75FFF, 0x000F000A, 0x0 },
+	{ 0x00C30FFF, 0x00060006, 0x0 },
+	{ 0x00AAAFFF, 0x001E0000, 0x0 },
+	{ 0x00FFFFFF, 0x000F000A, 0x0 },
+	{ 0x00D75FFF, 0x00160004, 0x0 },
+	{ 0x00C30FFF, 0x001E0000, 0x0 },
+	{ 0x00FFFFFF, 0x00060006, 0x0 },
+	{ 0x00D75FFF, 0x001E0000, 0x0 },
 };
 
 static const struct ddi_buf_trans hsw_ddi_translations_hdmi[] = {
 					/* Idx	NT mV d	T mV d	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	*/
+	{ 0x00FFFFFF, 0x0006000E, 0x0 },/* 0:	400	400	0	*/
+	{ 0x00E79FFF, 0x000E000C, 0x0 },/* 1:	400	500	2	*/
+	{ 0x00D75FFF, 0x0005000A, 0x0 },/* 2:	400	600	3.5	*/
+	{ 0x00FFFFFF, 0x0005000A, 0x0 },/* 3:	600	600	0	*/
+	{ 0x00E79FFF, 0x001D0007, 0x0 },/* 4:	600	750	2	*/
+	{ 0x00D75FFF, 0x000C0004, 0x0 },/* 5:	600	900	3.5	*/
+	{ 0x00FFFFFF, 0x00040006, 0x0 },/* 6:	800	800	0	*/
+	{ 0x80E79FFF, 0x00030002, 0x0 },/* 7:	800	1000	2	*/
+	{ 0x00FFFFFF, 0x00140005, 0x0 },/* 8:	850	850	0	*/
+	{ 0x00FFFFFF, 0x000C0004, 0x0 },/* 9:	900	900	0	*/
+	{ 0x00FFFFFF, 0x001C0003, 0x0 },/* 10:	950	950	0	*/
+	{ 0x80FFFFFF, 0x00030002, 0x0 },/* 11:	1000	1000	0	*/
 };
 
 static const struct ddi_buf_trans bdw_ddi_translations_edp[] = {
-	{ 0x00FFFFFF, 0x00000012 },
-	{ 0x00EBAFFF, 0x00020011 },
-	{ 0x00C71FFF, 0x0006000F },
-	{ 0x00AAAFFF, 0x000E000A },
-	{ 0x00FFFFFF, 0x00020011 },
-	{ 0x00DB6FFF, 0x0005000F },
-	{ 0x00BEEFFF, 0x000A000C },
-	{ 0x00FFFFFF, 0x0005000F },
-	{ 0x00DB6FFF, 0x000A000C },
+	{ 0x00FFFFFF, 0x00000012, 0x0 },
+	{ 0x00EBAFFF, 0x00020011, 0x0 },
+	{ 0x00C71FFF, 0x0006000F, 0x0 },
+	{ 0x00AAAFFF, 0x000E000A, 0x0 },
+	{ 0x00FFFFFF, 0x00020011, 0x0 },
+	{ 0x00DB6FFF, 0x0005000F, 0x0 },
+	{ 0x00BEEFFF, 0x000A000C, 0x0 },
+	{ 0x00FFFFFF, 0x0005000F, 0x0 },
+	{ 0x00DB6FFF, 0x000A000C, 0x0 },
 };
 
 static const struct ddi_buf_trans bdw_ddi_translations_dp[] = {
-	{ 0x00FFFFFF, 0x0007000E },
-	{ 0x00D75FFF, 0x000E000A },
-	{ 0x00BEFFFF, 0x00140006 },
-	{ 0x80B2CFFF, 0x001B0002 },
-	{ 0x00FFFFFF, 0x000E000A },
-	{ 0x00DB6FFF, 0x00160005 },
-	{ 0x80C71FFF, 0x001A0002 },
-	{ 0x00F7DFFF, 0x00180004 },
-	{ 0x80D75FFF, 0x001B0002 },
+	{ 0x00FFFFFF, 0x0007000E, 0x0 },
+	{ 0x00D75FFF, 0x000E000A, 0x0 },
+	{ 0x00BEFFFF, 0x00140006, 0x0 },
+	{ 0x80B2CFFF, 0x001B0002, 0x0 },
+	{ 0x00FFFFFF, 0x000E000A, 0x0 },
+	{ 0x00DB6FFF, 0x00160005, 0x0 },
+	{ 0x80C71FFF, 0x001A0002, 0x0 },
+	{ 0x00F7DFFF, 0x00180004, 0x0 },
+	{ 0x80D75FFF, 0x001B0002, 0x0 },
 };
 
 static const struct ddi_buf_trans bdw_ddi_translations_fdi[] = {
-	{ 0x00FFFFFF, 0x0001000E },
-	{ 0x00D75FFF, 0x0004000A },
-	{ 0x00C30FFF, 0x00070006 },
-	{ 0x00AAAFFF, 0x000C0000 },
-	{ 0x00FFFFFF, 0x0004000A },
-	{ 0x00D75FFF, 0x00090004 },
-	{ 0x00C30FFF, 0x000C0000 },
-	{ 0x00FFFFFF, 0x00070006 },
-	{ 0x00D75FFF, 0x000C0000 },
+	{ 0x00FFFFFF, 0x0001000E, 0x0 },
+	{ 0x00D75FFF, 0x0004000A, 0x0 },
+	{ 0x00C30FFF, 0x00070006, 0x0 },
+	{ 0x00AAAFFF, 0x000C0000, 0x0 },
+	{ 0x00FFFFFF, 0x0004000A, 0x0 },
+	{ 0x00D75FFF, 0x00090004, 0x0 },
+	{ 0x00C30FFF, 0x000C0000, 0x0 },
+	{ 0x00FFFFFF, 0x00070006, 0x0 },
+	{ 0x00D75FFF, 0x000C0000, 0x0 },
 };
 
 static const struct ddi_buf_trans bdw_ddi_translations_hdmi[] = {
 					/* Idx	NT mV d	T mV df	db	*/
-	{ 0x00FFFFFF, 0x0007000E },	/* 0:	400	400	0	*/
-	{ 0x00D75FFF, 0x000E000A },	/* 1:	400	600	3.5	*/
-	{ 0x00BEFFFF, 0x00140006 },	/* 2:	400	800	6	*/
-	{ 0x00FFFFFF, 0x0009000D },	/* 3:	450	450	0	*/
-	{ 0x00FFFFFF, 0x000E000A },	/* 4:	600	600	0	*/
-	{ 0x00D7FFFF, 0x00140006 },	/* 5:	600	800	2.5	*/
-	{ 0x80CB2FFF, 0x001B0002 },	/* 6:	600	1000	4.5	*/
-	{ 0x00FFFFFF, 0x00140006 },	/* 7:	800	800	0	*/
-	{ 0x80E79FFF, 0x001B0002 },	/* 8:	800	1000	2	*/
-	{ 0x80FFFFFF, 0x001B0002 },	/* 9:	1000	1000	0	*/
+	{ 0x00FFFFFF, 0x0007000E, 0x0 },/* 0:	400	400	0	*/
+	{ 0x00D75FFF, 0x000E000A, 0x0 },/* 1:	400	600	3.5	*/
+	{ 0x00BEFFFF, 0x00140006, 0x0 },/* 2:	400	800	6	*/
+	{ 0x00FFFFFF, 0x0009000D, 0x0 },/* 3:	450	450	0	*/
+	{ 0x00FFFFFF, 0x000E000A, 0x0 },/* 4:	600	600	0	*/
+	{ 0x00D7FFFF, 0x00140006, 0x0 },/* 5:	600	800	2.5	*/
+	{ 0x80CB2FFF, 0x001B0002, 0x0 },/* 6:	600	1000	4.5	*/
+	{ 0x00FFFFFF, 0x00140006, 0x0 },/* 7:	800	800	0	*/
+	{ 0x80E79FFF, 0x001B0002, 0x0 },/* 8:	800	1000	2	*/
+	{ 0x80FFFFFF, 0x001B0002, 0x0 },/* 9:	1000	1000	0	*/
 };
 
+/* Skylake H, S, and Skylake Y with 0.95V VccIO */
 static const struct ddi_buf_trans skl_ddi_translations_dp[] = {
-	{ 0x00000018, 0x000000a2 },
-	{ 0x00004014, 0x0000009B },
-	{ 0x00006012, 0x00000088 },
-	{ 0x00008010, 0x00000087 },
-	{ 0x00000018, 0x0000009B },
-	{ 0x00004014, 0x00000088 },
-	{ 0x00006012, 0x00000087 },
-	{ 0x00000018, 0x00000088 },
-	{ 0x00004014, 0x00000087 },
+	{ 0x00002016, 0x000000A0, 0x0 },
+	{ 0x00005012, 0x0000009B, 0x0 },
+	{ 0x00007011, 0x00000088, 0x0 },
+	{ 0x00009010, 0x000000C7, 0x0 },
+	{ 0x00002016, 0x0000009B, 0x0 },
+	{ 0x00005012, 0x00000088, 0x0 },
+	{ 0x00007011, 0x000000C7, 0x0 },
+	{ 0x00002016, 0x000000DF, 0x0 },
+	{ 0x00005012, 0x000000C7, 0x0 },
 };
 
-/* eDP 1.4 low vswing translation parameters */
+/* Skylake U */
+static const struct ddi_buf_trans skl_u_ddi_translations_dp[] = {
+	{ 0x00002016, 0x000000A2, 0x0 },
+	{ 0x00005012, 0x00000088, 0x0 },
+	{ 0x00007011, 0x00000087, 0x0 },
+	{ 0x80009010, 0x000000C7, 0x1 },	/* Uses I_boost */
+	{ 0x00002016, 0x0000009D, 0x0 },
+	{ 0x00005012, 0x000000C7, 0x0 },
+	{ 0x00007011, 0x000000C7, 0x0 },
+	{ 0x00002016, 0x00000088, 0x0 },
+	{ 0x00005012, 0x000000C7, 0x0 },
+};
+
+/* Skylake Y with 0.85V VccIO */
+static const struct ddi_buf_trans skl_y_085v_ddi_translations_dp[] = {
+	{ 0x00000018, 0x000000A2, 0x0 },
+	{ 0x00005012, 0x00000088, 0x0 },
+	{ 0x00007011, 0x00000087, 0x0 },
+	{ 0x80009010, 0x000000C7, 0x1 },	/* Uses I_boost */
+	{ 0x00000018, 0x0000009D, 0x0 },
+	{ 0x00005012, 0x000000C7, 0x0 },
+	{ 0x00007011, 0x000000C7, 0x0 },
+	{ 0x00000018, 0x00000088, 0x0 },
+	{ 0x00005012, 0x000000C7, 0x0 },
+};
+
+/*
+ * Skylake H and S, and Skylake Y with 0.95V VccIO
+ * eDP 1.4 low vswing translation parameters
+ */
 static const struct ddi_buf_trans skl_ddi_translations_edp[] = {
-	{ 0x00000018, 0x000000a8 },
-	{ 0x00002016, 0x000000ab },
-	{ 0x00006012, 0x000000a2 },
-	{ 0x00008010, 0x00000088 },
-	{ 0x00000018, 0x000000ab },
-	{ 0x00004014, 0x000000a2 },
-	{ 0x00006012, 0x000000a6 },
-	{ 0x00000018, 0x000000a2 },
-	{ 0x00005013, 0x0000009c },
-	{ 0x00000018, 0x00000088 },
+	{ 0x00000018, 0x000000A8, 0x0 },
+	{ 0x00004013, 0x000000A9, 0x0 },
+	{ 0x00007011, 0x000000A2, 0x0 },
+	{ 0x00009010, 0x0000009C, 0x0 },
+	{ 0x00000018, 0x000000A9, 0x0 },
+	{ 0x00006013, 0x000000A2, 0x0 },
+	{ 0x00007011, 0x000000A6, 0x0 },
+	{ 0x00000018, 0x000000AB, 0x0 },
+	{ 0x00007013, 0x0000009F, 0x0 },
+	{ 0x00000018, 0x000000DF, 0x0 },
 };
 
+/*
+ * Skylake U
+ * eDP 1.4 low vswing translation parameters
+ */
+static const struct ddi_buf_trans skl_u_ddi_translations_edp[] = {
+	{ 0x00000018, 0x000000A8, 0x0 },
+	{ 0x00004013, 0x000000A9, 0x0 },
+	{ 0x00007011, 0x000000A2, 0x0 },
+	{ 0x00009010, 0x0000009C, 0x0 },
+	{ 0x00000018, 0x000000A9, 0x0 },
+	{ 0x00006013, 0x000000A2, 0x0 },
+	{ 0x00007011, 0x000000A6, 0x0 },
+	{ 0x00002016, 0x000000AB, 0x0 },
+	{ 0x00005013, 0x0000009F, 0x0 },
+	{ 0x00000018, 0x000000DF, 0x0 },
+};
 
+/*
+ * Skylake Y with 0.95V VccIO
+ * eDP 1.4 low vswing translation parameters
+ */
+static const struct ddi_buf_trans skl_y_085v_ddi_translations_edp[] = {
+	{ 0x00000018, 0x000000A8, 0x0 },
+	{ 0x00004013, 0x000000AB, 0x0 },
+	{ 0x00007011, 0x000000A4, 0x0 },
+	{ 0x00009010, 0x000000DF, 0x0 },
+	{ 0x00000018, 0x000000AA, 0x0 },
+	{ 0x00006013, 0x000000A4, 0x0 },
+	{ 0x00007011, 0x0000009D, 0x0 },
+	{ 0x00000018, 0x000000A0, 0x0 },
+	{ 0x00006012, 0x000000DF, 0x0 },
+	{ 0x00000018, 0x0000008A, 0x0 },
+};
+
+/* Skylake H, S and U, and Skylake Y with 0.95V VccIO */
 static const struct ddi_buf_trans skl_ddi_translations_hdmi[] = {
-	{ 0x00000018, 0x000000ac },
-	{ 0x00005012, 0x0000009d },
-	{ 0x00007011, 0x00000088 },
-	{ 0x00000018, 0x000000a1 },
-	{ 0x00000018, 0x00000098 },
-	{ 0x00004013, 0x00000088 },
-	{ 0x00006012, 0x00000087 },
-	{ 0x00000018, 0x000000df },
-	{ 0x00003015, 0x00000087 },
-	{ 0x00003015, 0x000000c7 },
-	{ 0x00000018, 0x000000c7 },
+	{ 0x00000018, 0x000000AC, 0x0 },
+	{ 0x00005012, 0x0000009D, 0x0 },
+	{ 0x00007011, 0x00000088, 0x0 },
+	{ 0x00000018, 0x000000A1, 0x0 },
+	{ 0x00000018, 0x00000098, 0x0 },
+	{ 0x00004013, 0x00000088, 0x0 },
+	{ 0x00006012, 0x00000087, 0x0 },
+	{ 0x00000018, 0x000000DF, 0x0 },
+	{ 0x00003015, 0x00000087, 0x0 },	/* Default */
+	{ 0x00003015, 0x000000C7, 0x0 },
+	{ 0x00000018, 0x000000C7, 0x0 },
+};
+
+/* Skylake Y with 0.85V VccIO */
+static const struct ddi_buf_trans skl_y_085v_ddi_translations_hdmi[] = {
+	{ 0x00000018, 0x000000A1, 0x0 },
+	{ 0x00005012, 0x000000DF, 0x0 },
+	{ 0x00007011, 0x00000084, 0x0 },
+	{ 0x00000018, 0x000000A4, 0x0 },
+	{ 0x00000018, 0x0000009D, 0x0 },
+	{ 0x00004013, 0x00000080, 0x0 },
+	{ 0x00006013, 0x000000C7, 0x0 },
+	{ 0x00000018, 0x0000008A, 0x0 },
+	{ 0x00003015, 0x000000C7, 0x0 },	/* Default */
+	{ 0x80003015, 0x000000C7, 0x7 },	/* Uses I_boost */
+	{ 0x00000018, 0x000000C7, 0x0 },
 };
 
 struct bxt_ddi_buf_trans {
@@ -190,7 +270,7 @@  static const struct bxt_ddi_buf_trans bxt_ddi_translations_dp[] = {
 	{ 154, 0x9A, 0, 64,  false },	/* 6:	600		6   */
 	{ 102, 0x9A, 0, 128, false },	/* 7:	800		0   */
 	{ 154, 0x9A, 0, 85,  false },	/* 8:	800		3.5 */
-	{ 154, 0x9A, 1, 128, false },  /* 9:	1200		0   */
+	{ 154, 0x9A, 1, 128, false },	/* 9:	1200		0   */
 };
 
 /* BSpec has 2 recommended values - entries 0 and 8.
@@ -249,6 +329,99 @@  intel_dig_port_supports_hdmi(const struct intel_digital_port *intel_dig_port)
 	return intel_dig_port->hdmi.hdmi_reg;
 }
 
+static const struct ddi_buf_trans *skl_get_buf_trans_dp(struct drm_device *dev,
+							int *n_entries)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	const struct ddi_buf_trans *ddi_translations;
+	static int is_095v = -1;
+
+	if (is_095v == -1) {
+		u32 spr1 = I915_READ(UAIMI_SPR1);
+		is_095v = spr1 & SKL_VCCIO_MASK;
+	}
+
+	if (IS_SKL_ULX(dev) && !is_095v) {
+		ddi_translations = skl_y_085v_ddi_translations_dp;
+		*n_entries = ARRAY_SIZE(skl_y_085v_ddi_translations_dp);
+	} else if (IS_SKL_ULT(dev)) {
+		ddi_translations = skl_u_ddi_translations_dp;
+		*n_entries = ARRAY_SIZE(skl_u_ddi_translations_dp);
+	} else {
+		ddi_translations = skl_ddi_translations_dp;
+		*n_entries = ARRAY_SIZE(skl_ddi_translations_dp);
+	}
+
+	return ddi_translations;
+}
+
+static const struct ddi_buf_trans *skl_get_buf_trans_edp(struct drm_device *dev,
+							 int *n_entries)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	const struct ddi_buf_trans *ddi_translations;
+	static int is_095v = -1;
+
+	if (is_095v == -1) {
+		u32 spr1 = I915_READ(UAIMI_SPR1);
+		is_095v = spr1 & SKL_VCCIO_MASK;
+	}
+
+	if (IS_SKL_ULX(dev) && !is_095v) {
+		if (dev_priv->edp_low_vswing) {
+			ddi_translations = skl_y_085v_ddi_translations_edp;
+			*n_entries =
+				ARRAY_SIZE(skl_y_085v_ddi_translations_edp);
+		} else {
+			ddi_translations = skl_y_085v_ddi_translations_dp;
+			*n_entries =
+				ARRAY_SIZE(skl_y_085v_ddi_translations_dp);
+		}
+	} else if (IS_SKL_ULT(dev)) {
+		if (dev_priv->edp_low_vswing) {
+			ddi_translations = skl_u_ddi_translations_edp;
+			*n_entries = ARRAY_SIZE(skl_u_ddi_translations_edp);
+		} else {
+			ddi_translations = skl_u_ddi_translations_dp;
+			*n_entries = ARRAY_SIZE(skl_u_ddi_translations_dp);
+		}
+	} else {
+		if (dev_priv->edp_low_vswing) {
+			ddi_translations = skl_ddi_translations_edp;
+			*n_entries = ARRAY_SIZE(skl_ddi_translations_edp);
+		} else {
+			ddi_translations = skl_ddi_translations_dp;
+			*n_entries = ARRAY_SIZE(skl_ddi_translations_dp);
+		}
+	}
+
+	return ddi_translations;
+}
+
+static const struct ddi_buf_trans *
+skl_get_buf_trans_hdmi(struct drm_device *dev,
+		       int *n_entries)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	const struct ddi_buf_trans *ddi_translations;
+	static int is_095v = -1;
+
+	if (is_095v == -1) {
+		u32 spr1 = I915_READ(UAIMI_SPR1);
+		is_095v = spr1 & SKL_VCCIO_MASK;
+	}
+
+	if (IS_SKL_ULX(dev) && !is_095v) {
+		ddi_translations = skl_y_085v_ddi_translations_hdmi;
+		*n_entries = ARRAY_SIZE(skl_y_085v_ddi_translations_hdmi);
+	} else {
+		ddi_translations = skl_ddi_translations_hdmi;
+		*n_entries = ARRAY_SIZE(skl_ddi_translations_hdmi);
+	}
+
+	return ddi_translations;
+}
+
 /*
  * Starting with Haswell, DDI port buffers must be programmed with correct
  * values in advance. The buffer values are different for FDI and DP modes,
@@ -279,20 +452,13 @@  static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port,
 					INTEL_OUTPUT_HDMI);
 		return;
 	} else if (IS_SKYLAKE(dev)) {
-		ddi_translations_fdi = NULL;
-		ddi_translations_dp = skl_ddi_translations_dp;
-		n_dp_entries = ARRAY_SIZE(skl_ddi_translations_dp);
-		if (dev_priv->edp_low_vswing) {
-			ddi_translations_edp = skl_ddi_translations_edp;
-			n_edp_entries = ARRAY_SIZE(skl_ddi_translations_edp);
-		} else {
-			ddi_translations_edp = skl_ddi_translations_dp;
-			n_edp_entries = ARRAY_SIZE(skl_ddi_translations_dp);
-		}
-
-		ddi_translations_hdmi = skl_ddi_translations_hdmi;
-		n_hdmi_entries = ARRAY_SIZE(skl_ddi_translations_hdmi);
-		hdmi_default_entry = 7;
+		ddi_translations_dp =
+				skl_get_buf_trans_dp(dev, &n_dp_entries);
+		ddi_translations_edp =
+				skl_get_buf_trans_edp(dev, &n_edp_entries);
+		ddi_translations_hdmi =
+				skl_get_buf_trans_hdmi(dev, &n_hdmi_entries);
+		hdmi_default_entry = 8;
 	} else if (IS_BROADWELL(dev)) {
 		ddi_translations_fdi = bdw_ddi_translations_fdi;
 		ddi_translations_dp = bdw_ddi_translations_dp;
@@ -1806,6 +1972,47 @@  void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc)
 			   TRANS_CLK_SEL_DISABLED);
 }
 
+void skl_ddi_set_iboost(struct drm_device *dev, u32 level,
+			enum port port, int type)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	const struct ddi_buf_trans *ddi_translations;
+	uint8_t iboost;
+	int n_entries;
+	u32 reg;
+
+	if (type == INTEL_OUTPUT_DISPLAYPORT) {
+		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;
+	} else if (type == INTEL_OUTPUT_HDMI) {
+		ddi_translations = skl_get_buf_trans_hdmi(dev, &n_entries);
+		iboost = ddi_translations[port].i_boost;
+	} else {
+		return;
+	}
+
+	/* Make sure that the requested I_boost is valid */
+	if (iboost && iboost != 0x1 && iboost != 0x3 && iboost != 0x7) {
+		DRM_ERROR("Invalid I_boost value %u\n", iboost);
+		return;
+	}
+
+	reg = I915_READ(DISPIO_CR_TX_BMU_CR0);
+	reg &= ~BALANCE_LEG_MASK(port);
+	reg &= ~(1 << (BALANCE_LEG_DISABLE_SHIFT + port));
+
+	if (iboost) {
+		reg |= iboost << BALANCE_LEG_SHIFT(port);
+	} else {
+		reg |= 1 << (BALANCE_LEG_DISABLE_SHIFT + port);
+	}
+
+	I915_WRITE(DISPIO_CR_TX_BMU_CR0, reg);
+}
+
 void bxt_ddi_vswing_sequence(struct drm_device *dev, u32 level,
 			     enum port port, int type)
 {
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index f52eef138247..807c77d8dabc 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3454,17 +3454,10 @@  hsw_signal_levels(uint8_t train_set)
 	}
 }
 
-static void bxt_signal_levels(struct intel_dp *intel_dp)
+static uint32_t get_signal_level(int signal_levels)
 {
-	struct intel_digital_port *dport = dp_to_dig_port(intel_dp);
-	enum port port = dport->port;
-	struct drm_device *dev = dport->base.base.dev;
-	struct intel_encoder *encoder = &dport->base;
-	uint8_t train_set = intel_dp->train_set[0];
-	uint32_t level = 0;
+	uint32_t level;
 
-	int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
-					 DP_TRAIN_PRE_EMPHASIS_MASK);
 	switch (signal_levels) {
 	default:
 		DRM_DEBUG_KMS("Unsupported voltage swing/pre-emph level\n");
@@ -3500,6 +3493,38 @@  static void bxt_signal_levels(struct intel_dp *intel_dp)
 		break;
 	}
 
+	return level;
+}
+
+static void skl_set_iboost(struct intel_dp *intel_dp)
+{
+	struct intel_digital_port *dport = dp_to_dig_port(intel_dp);
+	enum port port = dport->port;
+	struct drm_device *dev = dport->base.base.dev;
+	struct intel_encoder *encoder = &dport->base;
+	uint8_t train_set = intel_dp->train_set[0];
+	uint32_t level;
+
+	int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
+					 DP_TRAIN_PRE_EMPHASIS_MASK);
+	level = get_signal_level(signal_levels);
+
+	skl_ddi_set_iboost(dev, level, port, encoder->type);
+}
+
+static void bxt_signal_levels(struct intel_dp *intel_dp)
+{
+	struct intel_digital_port *dport = dp_to_dig_port(intel_dp);
+	enum port port = dport->port;
+	struct drm_device *dev = dport->base.base.dev;
+	struct intel_encoder *encoder = &dport->base;
+	uint8_t train_set = intel_dp->train_set[0];
+	uint32_t level = 0;
+
+	int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
+					 DP_TRAIN_PRE_EMPHASIS_MASK);
+	level = get_signal_level(signal_levels);
+
 	bxt_ddi_vswing_sequence(dev, level, port, encoder->type);
 }
 
@@ -3520,6 +3545,9 @@  intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
 	} else if (HAS_DDI(dev)) {
 		signal_levels = hsw_signal_levels(train_set);
 		mask = DDI_BUF_EMP_MASK;
+
+		if (IS_SKYLAKE(dev))
+			skl_set_iboost(intel_dp);
 	} else if (IS_CHERRYVIEW(dev)) {
 		signal_levels = chv_signal_levels(intel_dp);
 		mask = 0;
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index bcafefcf048b..b2ebecef6dec 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -950,6 +950,8 @@  void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder);
 void intel_ddi_clock_get(struct intel_encoder *encoder,
 			 struct intel_crtc_state *pipe_config);
 void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state);
+void skl_ddi_set_iboost(struct drm_device *dev, u32 level,
+			enum port port, int type);
 void bxt_ddi_vswing_sequence(struct drm_device *dev, u32 level,
 				enum port port, int type);