diff mbox

[09/15] drm/i915/glk: Implement Geminilake DDI init sequence

Message ID 1478791400-21756-10-git-send-email-ander.conselvan.de.oliveira@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ander Conselvan de Oliveira Nov. 10, 2016, 3:23 p.m. UTC
Implement the DDI initsequence and add information about the different
phys in GLK.

v2: Rebase on the move of phys to be power wells.

v3: Rebase on addition of struct bxt_ddi_phy_info.

Signed-off-by: Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h         |   5 +-
 drivers/gpu/drm/i915/i915_reg.h         |  17 +++--
 drivers/gpu/drm/i915/intel_dpio_phy.c   | 114 +++++++++++++++++++++++++++-----
 drivers/gpu/drm/i915/intel_dpll_mgr.c   |   4 +-
 drivers/gpu/drm/i915/intel_runtime_pm.c |  39 +++++++++++
 5 files changed, 155 insertions(+), 24 deletions(-)

Comments

Rodrigo Vivi Nov. 11, 2016, 1:27 a.m. UTC | #1
Reviewed-by: Rodrigo Vivi <rodrigo.vivi@intel.com>

On Thu, Nov 10, 2016 at 05:23:14PM +0200, Ander Conselvan de Oliveira wrote:
> Implement the DDI initsequence and add information about the different
> phys in GLK.
> 
> v2: Rebase on the move of phys to be power wells.
> 
> v3: Rebase on addition of struct bxt_ddi_phy_info.
> 
> Signed-off-by: Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
> ---
>  drivers/gpu/drm/i915/i915_drv.h         |   5 +-
>  drivers/gpu/drm/i915/i915_reg.h         |  17 +++--
>  drivers/gpu/drm/i915/intel_dpio_phy.c   | 114 +++++++++++++++++++++++++++-----
>  drivers/gpu/drm/i915/intel_dpll_mgr.c   |   4 +-
>  drivers/gpu/drm/i915/intel_runtime_pm.c |  39 +++++++++++
>  5 files changed, 155 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 4bb745a..f497396 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -207,7 +207,8 @@ enum dpio_channel {
>  
>  enum dpio_phy {
>  	DPIO_PHY0,
> -	DPIO_PHY1
> +	DPIO_PHY1,
> +	DPIO_PHY2,
>  };
>  
>  enum intel_display_power_domain {
> @@ -3888,7 +3889,7 @@ u32 vlv_flisdsi_read(struct drm_i915_private *dev_priv, u32 reg);
>  void vlv_flisdsi_write(struct drm_i915_private *dev_priv, u32 reg, u32 val);
>  
>  /* intel_dpio_phy.c */
> -void bxt_port_to_phy_channel(enum port port,
> +void bxt_port_to_phy_channel(struct drm_i915_private *dev_priv, enum port port,
>  			     enum dpio_phy *phy, enum dpio_channel *ch);
>  void bxt_ddi_phy_set_signal_level(struct drm_i915_private *dev_priv,
>  				  enum port port, u32 margin, u32 scale,
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 78a3347..d78a3e6 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -62,6 +62,9 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
>  #define _PORT3(port, a, b, c) ((port) == PORT_A ? (a) : \
>  			       (port) == PORT_B ? (b) : (c))
>  #define _MMIO_PORT3(pipe, a, b, c) _MMIO(_PORT3(pipe, a, b, c))
> +#define _PHY3(phy, a, b, c) ((phy) == DPIO_PHY0 ? (a) : \
> +			     (phy) == DPIO_PHY1 ? (b) : (c))
> +#define _MMIO_PHY3(phy, a, b, c) _MMIO(_PHY3(phy, a, b, c))
>  
>  #define _MASKED_FIELD(mask, value) ({					   \
>  	if (__builtin_constant_p(mask))					   \
> @@ -726,6 +729,7 @@ enum skl_disp_power_wells {
>  
>  	BXT_DPIO_CMN_A,
>  	BXT_DPIO_CMN_BC,
> +	GLK_DPIO_CMN_C,
>  };
>  
>  #define SKL_POWER_WELL_STATE(pw) (1 << ((pw) * 2))
> @@ -1194,8 +1198,10 @@ enum skl_disp_power_wells {
>  /* BXT PHY registers */
>  #define _BXT_PHY0_BASE			0x6C000
>  #define _BXT_PHY1_BASE			0x162000
> -#define BXT_PHY_BASE(phy)		_PIPE((phy), _BXT_PHY0_BASE, \
> -						     _BXT_PHY1_BASE)
> +#define _BXT_PHY2_BASE			0x163000
> +#define BXT_PHY_BASE(phy)		_PHY3((phy), _BXT_PHY0_BASE, \
> +						     _BXT_PHY1_BASE, \
> +						     _BXT_PHY2_BASE)
>  
>  #define _BXT_PHY(phy, reg)						\
>  	_MMIO(BXT_PHY_BASE(phy) - _BXT_PHY0_BASE + (reg))
> @@ -1207,7 +1213,6 @@ enum skl_disp_power_wells {
>  	_MMIO(_BXT_PHY_CH(phy, ch, reg_ch0, reg_ch1))
>  
>  #define BXT_P_CR_GT_DISP_PWRON		_MMIO(0x138090)
> -#define   GT_DISPLAY_POWER_ON(phy)	(1 << (phy))
>  
>  #define _BXT_PHY_CTL_DDI_A		0x64C00
>  #define _BXT_PHY_CTL_DDI_B		0x64C10
> @@ -1220,9 +1225,11 @@ enum skl_disp_power_wells {
>  
>  #define _PHY_CTL_FAMILY_EDP		0x64C80
>  #define _PHY_CTL_FAMILY_DDI		0x64C90
> +#define _PHY_CTL_FAMILY_DDI_C		0x64CA0
>  #define   COMMON_RESET_DIS		(1 << 31)
> -#define BXT_PHY_CTL_FAMILY(phy)		_MMIO_PIPE((phy), _PHY_CTL_FAMILY_DDI, \
> -							  _PHY_CTL_FAMILY_EDP)
> +#define BXT_PHY_CTL_FAMILY(phy)		_MMIO_PHY3((phy), _PHY_CTL_FAMILY_DDI, \
> +							  _PHY_CTL_FAMILY_EDP, \
> +							  _PHY_CTL_FAMILY_DDI_C)
>  
>  /* BXT PHY PLL registers */
>  #define _PORT_PLL_A			0x46074
> diff --git a/drivers/gpu/drm/i915/intel_dpio_phy.c b/drivers/gpu/drm/i915/intel_dpio_phy.c
> index 7a8e82d..4ff601d 100644
> --- a/drivers/gpu/drm/i915/intel_dpio_phy.c
> +++ b/drivers/gpu/drm/i915/intel_dpio_phy.c
> @@ -131,6 +131,18 @@ struct bxt_ddi_phy_info {
>  	enum dpio_phy rcomp_phy;
>  
>  	/**
> +	 * @reset_delay: delay in us to wait before setting the common reset
> +	 * bit in BXT_PHY_CTL_FAMILY, which effectively enables the phy.
> +	 */
> +	int reset_delay;
> +
> +	/**
> +	 * @pwron_mask: Mask with the appropriate bit set that would cause the
> +	 * punit to power this phy if written to BXT_P_CR_GT_DISP_PWRON.
> +	 */
> +	u32 pwron_mask;
> +
> +	/**
>  	 * @channel: struct containing per channel information.
>  	 */
>  	struct {
> @@ -145,6 +157,7 @@ static const struct bxt_ddi_phy_info bxt_ddi_phy_info[] = {
>  	[DPIO_PHY0] = {
>  		.dual_channel = true,
>  		.rcomp_phy = DPIO_PHY1,
> +		.pwron_mask = BIT(0),
>  
>  		.channel = {
>  			[DPIO_CH0] = { .port = PORT_B },
> @@ -154,6 +167,7 @@ static const struct bxt_ddi_phy_info bxt_ddi_phy_info[] = {
>  	[DPIO_PHY1] = {
>  		.dual_channel = false,
>  		.rcomp_phy = -1,
> +		.pwron_mask = BIT(1),
>  
>  		.channel = {
>  			[DPIO_CH0] = { .port = PORT_A },
> @@ -161,20 +175,77 @@ static const struct bxt_ddi_phy_info bxt_ddi_phy_info[] = {
>  	},
>  };
>  
> +static const struct bxt_ddi_phy_info glk_ddi_phy_info[] = {
> +	[DPIO_PHY0] = {
> +		.dual_channel = false,
> +		.rcomp_phy = DPIO_PHY1,
> +		.pwron_mask = BIT(0),
> +		.reset_delay = 20,
> +
> +		.channel = {
> +			[DPIO_CH0] = { .port = PORT_B },
> +		}
> +	},
> +	[DPIO_PHY1] = {
> +		.dual_channel = false,
> +		.rcomp_phy = -1,
> +		.pwron_mask = BIT(3),
> +		.reset_delay = 20,
> +
> +		.channel = {
> +			[DPIO_CH0] = { .port = PORT_A },
> +		}
> +	},
> +	[DPIO_PHY2] = {
> +		.dual_channel = false,
> +		.rcomp_phy = DPIO_PHY1,
> +		.pwron_mask = BIT(1),
> +		.reset_delay = 20,
> +
> +		.channel = {
> +			[DPIO_CH0] = { .port = PORT_C },
> +		}
> +	},
> +};
> +
>  static u32 bxt_phy_port_mask(const struct bxt_ddi_phy_info *phy_info)
>  {
>  	return (phy_info->dual_channel * BIT(phy_info->channel[DPIO_CH1].port)) |
>  		BIT(phy_info->channel[DPIO_CH0].port);
>  }
>  
> -void bxt_port_to_phy_channel(enum port port,
> +static const struct bxt_ddi_phy_info *
> +bxt_get_phy_list(struct drm_i915_private *dev_priv, int *count)
> +{
> +	if (IS_GEMINILAKE(dev_priv)) {
> +		*count =  ARRAY_SIZE(glk_ddi_phy_info);
> +		return glk_ddi_phy_info;
> +	} else {
> +		*count =  ARRAY_SIZE(bxt_ddi_phy_info);
> +		return bxt_ddi_phy_info;
> +	}
> +}
> +
> +static const struct bxt_ddi_phy_info *
> +bxt_get_phy_info(struct drm_i915_private *dev_priv, enum dpio_phy phy)
> +{
> +	int count;
> +	const struct bxt_ddi_phy_info *phy_list =
> +		bxt_get_phy_list(dev_priv, &count);
> +
> +	return &phy_list[phy];
> +}
> +
> +void bxt_port_to_phy_channel(struct drm_i915_private *dev_priv, enum port port,
>  			     enum dpio_phy *phy, enum dpio_channel *ch)
>  {
> -	const struct bxt_ddi_phy_info *phy_info;
> -	int i;
> +	const struct bxt_ddi_phy_info *phy_info, *phys;
> +	int i, count;
> +
> +	phys = bxt_get_phy_list(dev_priv, &count);
>  
> -	for (i = 0; i < ARRAY_SIZE(bxt_ddi_phy_info); i++) {
> -		phy_info = &bxt_ddi_phy_info[i];
> +	for (i = 0; i < count; i++) {
> +		phy_info = &phys[i];
>  
>  		if (port == phy_info->channel[DPIO_CH0].port) {
>  			*phy = i;
> @@ -203,7 +274,7 @@ void bxt_ddi_phy_set_signal_level(struct drm_i915_private *dev_priv,
>  	enum dpio_phy phy;
>  	enum dpio_channel ch;
>  
> -	bxt_port_to_phy_channel(port, &phy, &ch);
> +	bxt_port_to_phy_channel(dev_priv, port, &phy, &ch);
>  
>  	/*
>  	 * While we write to the group register to program all lanes at once we
> @@ -241,10 +312,12 @@ void bxt_ddi_phy_set_signal_level(struct drm_i915_private *dev_priv,
>  bool bxt_ddi_phy_is_enabled(struct drm_i915_private *dev_priv,
>  			    enum dpio_phy phy)
>  {
> -	const struct bxt_ddi_phy_info *phy_info = &bxt_ddi_phy_info[phy];
> +	const struct bxt_ddi_phy_info *phy_info;
>  	enum port port;
>  
> -	if (!(I915_READ(BXT_P_CR_GT_DISP_PWRON) & GT_DISPLAY_POWER_ON(phy)))
> +	phy_info = bxt_get_phy_info(dev_priv, phy);
> +
> +	if (!(I915_READ(BXT_P_CR_GT_DISP_PWRON) & phy_info->pwron_mask))
>  		return false;
>  
>  	if ((I915_READ(BXT_PORT_CL1CM_DW0(phy)) &
> @@ -306,9 +379,11 @@ static void bxt_phy_wait_grc_done(struct drm_i915_private *dev_priv,
>  static void _bxt_ddi_phy_init(struct drm_i915_private *dev_priv,
>  			      enum dpio_phy phy)
>  {
> -	const struct bxt_ddi_phy_info *phy_info = &bxt_ddi_phy_info[phy];
> +	const struct bxt_ddi_phy_info *phy_info;
>  	u32 val;
>  
> +	phy_info = bxt_get_phy_info(dev_priv, phy);
> +
>  	if (bxt_ddi_phy_is_enabled(dev_priv, phy)) {
>  		/* Still read out the GRC value for state verification */
>  		if (phy_info->rcomp_phy != -1)
> @@ -326,7 +401,7 @@ static void _bxt_ddi_phy_init(struct drm_i915_private *dev_priv,
>  	}
>  
>  	val = I915_READ(BXT_P_CR_GT_DISP_PWRON);
> -	val |= GT_DISPLAY_POWER_ON(phy);
> +	val |= phy_info->pwron_mask;
>  	I915_WRITE(BXT_P_CR_GT_DISP_PWRON, val);
>  
>  	/*
> @@ -384,6 +459,9 @@ static void _bxt_ddi_phy_init(struct drm_i915_private *dev_priv,
>  		I915_WRITE(BXT_PORT_REF_DW8(phy), val);
>  	}
>  
> +	if (phy_info->reset_delay)
> +		udelay(phy_info->reset_delay);
> +
>  	val = I915_READ(BXT_PHY_CTL_FAMILY(phy));
>  	val |= COMMON_RESET_DIS;
>  	I915_WRITE(BXT_PHY_CTL_FAMILY(phy), val);
> @@ -395,20 +473,24 @@ static void _bxt_ddi_phy_init(struct drm_i915_private *dev_priv,
>  
>  void bxt_ddi_phy_uninit(struct drm_i915_private *dev_priv, enum dpio_phy phy)
>  {
> +	const struct bxt_ddi_phy_info *phy_info;
>  	uint32_t val;
>  
> +	phy_info = bxt_get_phy_info(dev_priv, phy);
> +
>  	val = I915_READ(BXT_PHY_CTL_FAMILY(phy));
>  	val &= ~COMMON_RESET_DIS;
>  	I915_WRITE(BXT_PHY_CTL_FAMILY(phy), val);
>  
>  	val = I915_READ(BXT_P_CR_GT_DISP_PWRON);
> -	val &= ~GT_DISPLAY_POWER_ON(phy);
> +	val &= ~phy_info->pwron_mask;
>  	I915_WRITE(BXT_P_CR_GT_DISP_PWRON, val);
>  }
>  
>  void bxt_ddi_phy_init(struct drm_i915_private *dev_priv, enum dpio_phy phy)
>  {
> -	const struct bxt_ddi_phy_info *phy_info = &bxt_ddi_phy_info[phy];
> +	const struct bxt_ddi_phy_info *phy_info =
> +		bxt_get_phy_info(dev_priv, phy);
>  	enum dpio_phy rcomp_phy = phy_info->rcomp_phy;
>  	bool was_enabled;
>  
> @@ -461,10 +543,12 @@ __phy_reg_verify_state(struct drm_i915_private *dev_priv, enum dpio_phy phy,
>  bool bxt_ddi_phy_verify_state(struct drm_i915_private *dev_priv,
>  			      enum dpio_phy phy)
>  {
> -	const struct bxt_ddi_phy_info *phy_info = &bxt_ddi_phy_info[phy];
> +	const struct bxt_ddi_phy_info *phy_info;
>  	uint32_t mask;
>  	bool ok;
>  
> +	phy_info = bxt_get_phy_info(dev_priv, phy);
> +
>  #define _CHK(reg, mask, exp, fmt, ...)					\
>  	__phy_reg_verify_state(dev_priv, phy, reg, mask, exp, fmt,	\
>  			       ## __VA_ARGS__)
> @@ -540,7 +624,7 @@ void bxt_ddi_phy_set_lane_optim_mask(struct intel_encoder *encoder,
>  	enum dpio_channel ch;
>  	int lane;
>  
> -	bxt_port_to_phy_channel(port, &phy, &ch);
> +	bxt_port_to_phy_channel(dev_priv, port, &phy, &ch);
>  
>  	for (lane = 0; lane < 4; lane++) {
>  		u32 val = I915_READ(BXT_PORT_TX_DW14_LN(phy, ch, lane));
> @@ -568,7 +652,7 @@ bxt_ddi_phy_get_lane_lat_optim_mask(struct intel_encoder *encoder)
>  	int lane;
>  	uint8_t mask;
>  
> -	bxt_port_to_phy_channel(port, &phy, &ch);
> +	bxt_port_to_phy_channel(dev_priv, port, &phy, &ch);
>  
>  	mask = 0;
>  	for (lane = 0; lane < 4; lane++) {
> diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.c b/drivers/gpu/drm/i915/intel_dpll_mgr.c
> index 8205c1c..0311fd4 100644
> --- a/drivers/gpu/drm/i915/intel_dpll_mgr.c
> +++ b/drivers/gpu/drm/i915/intel_dpll_mgr.c
> @@ -1374,7 +1374,7 @@ static void bxt_ddi_pll_enable(struct drm_i915_private *dev_priv,
>  	enum dpio_phy phy;
>  	enum dpio_channel ch;
>  
> -	bxt_port_to_phy_channel(port, &phy, &ch);
> +	bxt_port_to_phy_channel(dev_priv, port, &phy, &ch);
>  
>  	/* Non-SSC reference */
>  	temp = I915_READ(BXT_PORT_PLL_ENABLE(port));
> @@ -1492,7 +1492,7 @@ static bool bxt_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv,
>  	enum dpio_phy phy;
>  	enum dpio_channel ch;
>  
> -	bxt_port_to_phy_channel(port, &phy, &ch);
> +	bxt_port_to_phy_channel(dev_priv, port, &phy, &ch);
>  
>  	if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS))
>  		return false;
> diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
> index 683c15b..30d5112 100644
> --- a/drivers/gpu/drm/i915/intel_runtime_pm.c
> +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
> @@ -477,6 +477,18 @@ static void hsw_set_power_well(struct drm_i915_private *dev_priv,
>  #define GLK_DISPLAY_DDI_C_POWER_DOMAINS (		\
>  	BIT(POWER_DOMAIN_PORT_DDI_C_LANES) |		\
>  	BIT(POWER_DOMAIN_INIT))
> +#define GLK_DPIO_CMN_A_POWER_DOMAINS (			\
> +	BIT(POWER_DOMAIN_PORT_DDI_A_LANES) |		\
> +	BIT(POWER_DOMAIN_AUX_A) |			\
> +	BIT(POWER_DOMAIN_INIT))
> +#define GLK_DPIO_CMN_B_POWER_DOMAINS (			\
> +	BIT(POWER_DOMAIN_PORT_DDI_B_LANES) |		\
> +	BIT(POWER_DOMAIN_AUX_B) |			\
> +	BIT(POWER_DOMAIN_INIT))
> +#define GLK_DPIO_CMN_C_POWER_DOMAINS (			\
> +	BIT(POWER_DOMAIN_PORT_DDI_C_LANES) |		\
> +	BIT(POWER_DOMAIN_AUX_C) |			\
> +	BIT(POWER_DOMAIN_INIT))
>  #define GLK_DISPLAY_AUX_A_POWER_DOMAINS (		\
>  	BIT(POWER_DOMAIN_AUX_A) |		\
>  	BIT(POWER_DOMAIN_INIT))
> @@ -926,6 +938,12 @@ static void bxt_verify_ddi_phy_power_wells(struct drm_i915_private *dev_priv)
>  	power_well = lookup_power_well(dev_priv, BXT_DPIO_CMN_BC);
>  	if (power_well->count > 0)
>  		bxt_ddi_phy_verify_state(dev_priv, power_well->data);
> +
> +	if (IS_GEMINILAKE(dev_priv)) {
> +		power_well = lookup_power_well(dev_priv, GLK_DPIO_CMN_C);
> +		if (power_well->count > 0)
> +			bxt_ddi_phy_verify_state(dev_priv, power_well->data);
> +	}
>  }
>  
>  static bool gen9_dc_off_power_well_enabled(struct drm_i915_private *dev_priv,
> @@ -2219,6 +2237,27 @@ static struct i915_power_well glk_power_wells[] = {
>  		.id = SKL_DISP_PW_2,
>  	},
>  	{
> +		.name = "dpio-common-a",
> +		.domains = GLK_DPIO_CMN_A_POWER_DOMAINS,
> +		.ops = &bxt_dpio_cmn_power_well_ops,
> +		.id = BXT_DPIO_CMN_A,
> +		.data = DPIO_PHY1,
> +	},
> +	{
> +		.name = "dpio-common-b",
> +		.domains = GLK_DPIO_CMN_B_POWER_DOMAINS,
> +		.ops = &bxt_dpio_cmn_power_well_ops,
> +		.id = BXT_DPIO_CMN_BC,
> +		.data = DPIO_PHY0,
> +	},
> +	{
> +		.name = "dpio-common-c",
> +		.domains = GLK_DPIO_CMN_C_POWER_DOMAINS,
> +		.ops = &bxt_dpio_cmn_power_well_ops,
> +		.id = GLK_DPIO_CMN_C,
> +		.data = DPIO_PHY2,
> +	},
> +	{
>  		.name = "AUX A",
>  		.domains = GLK_DISPLAY_AUX_A_POWER_DOMAINS,
>  		.ops = &skl_power_well_ops,
> -- 
> 2.5.5
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 4bb745a..f497396 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -207,7 +207,8 @@  enum dpio_channel {
 
 enum dpio_phy {
 	DPIO_PHY0,
-	DPIO_PHY1
+	DPIO_PHY1,
+	DPIO_PHY2,
 };
 
 enum intel_display_power_domain {
@@ -3888,7 +3889,7 @@  u32 vlv_flisdsi_read(struct drm_i915_private *dev_priv, u32 reg);
 void vlv_flisdsi_write(struct drm_i915_private *dev_priv, u32 reg, u32 val);
 
 /* intel_dpio_phy.c */
-void bxt_port_to_phy_channel(enum port port,
+void bxt_port_to_phy_channel(struct drm_i915_private *dev_priv, enum port port,
 			     enum dpio_phy *phy, enum dpio_channel *ch);
 void bxt_ddi_phy_set_signal_level(struct drm_i915_private *dev_priv,
 				  enum port port, u32 margin, u32 scale,
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 78a3347..d78a3e6 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -62,6 +62,9 @@  static inline bool i915_mmio_reg_valid(i915_reg_t reg)
 #define _PORT3(port, a, b, c) ((port) == PORT_A ? (a) : \
 			       (port) == PORT_B ? (b) : (c))
 #define _MMIO_PORT3(pipe, a, b, c) _MMIO(_PORT3(pipe, a, b, c))
+#define _PHY3(phy, a, b, c) ((phy) == DPIO_PHY0 ? (a) : \
+			     (phy) == DPIO_PHY1 ? (b) : (c))
+#define _MMIO_PHY3(phy, a, b, c) _MMIO(_PHY3(phy, a, b, c))
 
 #define _MASKED_FIELD(mask, value) ({					   \
 	if (__builtin_constant_p(mask))					   \
@@ -726,6 +729,7 @@  enum skl_disp_power_wells {
 
 	BXT_DPIO_CMN_A,
 	BXT_DPIO_CMN_BC,
+	GLK_DPIO_CMN_C,
 };
 
 #define SKL_POWER_WELL_STATE(pw) (1 << ((pw) * 2))
@@ -1194,8 +1198,10 @@  enum skl_disp_power_wells {
 /* BXT PHY registers */
 #define _BXT_PHY0_BASE			0x6C000
 #define _BXT_PHY1_BASE			0x162000
-#define BXT_PHY_BASE(phy)		_PIPE((phy), _BXT_PHY0_BASE, \
-						     _BXT_PHY1_BASE)
+#define _BXT_PHY2_BASE			0x163000
+#define BXT_PHY_BASE(phy)		_PHY3((phy), _BXT_PHY0_BASE, \
+						     _BXT_PHY1_BASE, \
+						     _BXT_PHY2_BASE)
 
 #define _BXT_PHY(phy, reg)						\
 	_MMIO(BXT_PHY_BASE(phy) - _BXT_PHY0_BASE + (reg))
@@ -1207,7 +1213,6 @@  enum skl_disp_power_wells {
 	_MMIO(_BXT_PHY_CH(phy, ch, reg_ch0, reg_ch1))
 
 #define BXT_P_CR_GT_DISP_PWRON		_MMIO(0x138090)
-#define   GT_DISPLAY_POWER_ON(phy)	(1 << (phy))
 
 #define _BXT_PHY_CTL_DDI_A		0x64C00
 #define _BXT_PHY_CTL_DDI_B		0x64C10
@@ -1220,9 +1225,11 @@  enum skl_disp_power_wells {
 
 #define _PHY_CTL_FAMILY_EDP		0x64C80
 #define _PHY_CTL_FAMILY_DDI		0x64C90
+#define _PHY_CTL_FAMILY_DDI_C		0x64CA0
 #define   COMMON_RESET_DIS		(1 << 31)
-#define BXT_PHY_CTL_FAMILY(phy)		_MMIO_PIPE((phy), _PHY_CTL_FAMILY_DDI, \
-							  _PHY_CTL_FAMILY_EDP)
+#define BXT_PHY_CTL_FAMILY(phy)		_MMIO_PHY3((phy), _PHY_CTL_FAMILY_DDI, \
+							  _PHY_CTL_FAMILY_EDP, \
+							  _PHY_CTL_FAMILY_DDI_C)
 
 /* BXT PHY PLL registers */
 #define _PORT_PLL_A			0x46074
diff --git a/drivers/gpu/drm/i915/intel_dpio_phy.c b/drivers/gpu/drm/i915/intel_dpio_phy.c
index 7a8e82d..4ff601d 100644
--- a/drivers/gpu/drm/i915/intel_dpio_phy.c
+++ b/drivers/gpu/drm/i915/intel_dpio_phy.c
@@ -131,6 +131,18 @@  struct bxt_ddi_phy_info {
 	enum dpio_phy rcomp_phy;
 
 	/**
+	 * @reset_delay: delay in us to wait before setting the common reset
+	 * bit in BXT_PHY_CTL_FAMILY, which effectively enables the phy.
+	 */
+	int reset_delay;
+
+	/**
+	 * @pwron_mask: Mask with the appropriate bit set that would cause the
+	 * punit to power this phy if written to BXT_P_CR_GT_DISP_PWRON.
+	 */
+	u32 pwron_mask;
+
+	/**
 	 * @channel: struct containing per channel information.
 	 */
 	struct {
@@ -145,6 +157,7 @@  static const struct bxt_ddi_phy_info bxt_ddi_phy_info[] = {
 	[DPIO_PHY0] = {
 		.dual_channel = true,
 		.rcomp_phy = DPIO_PHY1,
+		.pwron_mask = BIT(0),
 
 		.channel = {
 			[DPIO_CH0] = { .port = PORT_B },
@@ -154,6 +167,7 @@  static const struct bxt_ddi_phy_info bxt_ddi_phy_info[] = {
 	[DPIO_PHY1] = {
 		.dual_channel = false,
 		.rcomp_phy = -1,
+		.pwron_mask = BIT(1),
 
 		.channel = {
 			[DPIO_CH0] = { .port = PORT_A },
@@ -161,20 +175,77 @@  static const struct bxt_ddi_phy_info bxt_ddi_phy_info[] = {
 	},
 };
 
+static const struct bxt_ddi_phy_info glk_ddi_phy_info[] = {
+	[DPIO_PHY0] = {
+		.dual_channel = false,
+		.rcomp_phy = DPIO_PHY1,
+		.pwron_mask = BIT(0),
+		.reset_delay = 20,
+
+		.channel = {
+			[DPIO_CH0] = { .port = PORT_B },
+		}
+	},
+	[DPIO_PHY1] = {
+		.dual_channel = false,
+		.rcomp_phy = -1,
+		.pwron_mask = BIT(3),
+		.reset_delay = 20,
+
+		.channel = {
+			[DPIO_CH0] = { .port = PORT_A },
+		}
+	},
+	[DPIO_PHY2] = {
+		.dual_channel = false,
+		.rcomp_phy = DPIO_PHY1,
+		.pwron_mask = BIT(1),
+		.reset_delay = 20,
+
+		.channel = {
+			[DPIO_CH0] = { .port = PORT_C },
+		}
+	},
+};
+
 static u32 bxt_phy_port_mask(const struct bxt_ddi_phy_info *phy_info)
 {
 	return (phy_info->dual_channel * BIT(phy_info->channel[DPIO_CH1].port)) |
 		BIT(phy_info->channel[DPIO_CH0].port);
 }
 
-void bxt_port_to_phy_channel(enum port port,
+static const struct bxt_ddi_phy_info *
+bxt_get_phy_list(struct drm_i915_private *dev_priv, int *count)
+{
+	if (IS_GEMINILAKE(dev_priv)) {
+		*count =  ARRAY_SIZE(glk_ddi_phy_info);
+		return glk_ddi_phy_info;
+	} else {
+		*count =  ARRAY_SIZE(bxt_ddi_phy_info);
+		return bxt_ddi_phy_info;
+	}
+}
+
+static const struct bxt_ddi_phy_info *
+bxt_get_phy_info(struct drm_i915_private *dev_priv, enum dpio_phy phy)
+{
+	int count;
+	const struct bxt_ddi_phy_info *phy_list =
+		bxt_get_phy_list(dev_priv, &count);
+
+	return &phy_list[phy];
+}
+
+void bxt_port_to_phy_channel(struct drm_i915_private *dev_priv, enum port port,
 			     enum dpio_phy *phy, enum dpio_channel *ch)
 {
-	const struct bxt_ddi_phy_info *phy_info;
-	int i;
+	const struct bxt_ddi_phy_info *phy_info, *phys;
+	int i, count;
+
+	phys = bxt_get_phy_list(dev_priv, &count);
 
-	for (i = 0; i < ARRAY_SIZE(bxt_ddi_phy_info); i++) {
-		phy_info = &bxt_ddi_phy_info[i];
+	for (i = 0; i < count; i++) {
+		phy_info = &phys[i];
 
 		if (port == phy_info->channel[DPIO_CH0].port) {
 			*phy = i;
@@ -203,7 +274,7 @@  void bxt_ddi_phy_set_signal_level(struct drm_i915_private *dev_priv,
 	enum dpio_phy phy;
 	enum dpio_channel ch;
 
-	bxt_port_to_phy_channel(port, &phy, &ch);
+	bxt_port_to_phy_channel(dev_priv, port, &phy, &ch);
 
 	/*
 	 * While we write to the group register to program all lanes at once we
@@ -241,10 +312,12 @@  void bxt_ddi_phy_set_signal_level(struct drm_i915_private *dev_priv,
 bool bxt_ddi_phy_is_enabled(struct drm_i915_private *dev_priv,
 			    enum dpio_phy phy)
 {
-	const struct bxt_ddi_phy_info *phy_info = &bxt_ddi_phy_info[phy];
+	const struct bxt_ddi_phy_info *phy_info;
 	enum port port;
 
-	if (!(I915_READ(BXT_P_CR_GT_DISP_PWRON) & GT_DISPLAY_POWER_ON(phy)))
+	phy_info = bxt_get_phy_info(dev_priv, phy);
+
+	if (!(I915_READ(BXT_P_CR_GT_DISP_PWRON) & phy_info->pwron_mask))
 		return false;
 
 	if ((I915_READ(BXT_PORT_CL1CM_DW0(phy)) &
@@ -306,9 +379,11 @@  static void bxt_phy_wait_grc_done(struct drm_i915_private *dev_priv,
 static void _bxt_ddi_phy_init(struct drm_i915_private *dev_priv,
 			      enum dpio_phy phy)
 {
-	const struct bxt_ddi_phy_info *phy_info = &bxt_ddi_phy_info[phy];
+	const struct bxt_ddi_phy_info *phy_info;
 	u32 val;
 
+	phy_info = bxt_get_phy_info(dev_priv, phy);
+
 	if (bxt_ddi_phy_is_enabled(dev_priv, phy)) {
 		/* Still read out the GRC value for state verification */
 		if (phy_info->rcomp_phy != -1)
@@ -326,7 +401,7 @@  static void _bxt_ddi_phy_init(struct drm_i915_private *dev_priv,
 	}
 
 	val = I915_READ(BXT_P_CR_GT_DISP_PWRON);
-	val |= GT_DISPLAY_POWER_ON(phy);
+	val |= phy_info->pwron_mask;
 	I915_WRITE(BXT_P_CR_GT_DISP_PWRON, val);
 
 	/*
@@ -384,6 +459,9 @@  static void _bxt_ddi_phy_init(struct drm_i915_private *dev_priv,
 		I915_WRITE(BXT_PORT_REF_DW8(phy), val);
 	}
 
+	if (phy_info->reset_delay)
+		udelay(phy_info->reset_delay);
+
 	val = I915_READ(BXT_PHY_CTL_FAMILY(phy));
 	val |= COMMON_RESET_DIS;
 	I915_WRITE(BXT_PHY_CTL_FAMILY(phy), val);
@@ -395,20 +473,24 @@  static void _bxt_ddi_phy_init(struct drm_i915_private *dev_priv,
 
 void bxt_ddi_phy_uninit(struct drm_i915_private *dev_priv, enum dpio_phy phy)
 {
+	const struct bxt_ddi_phy_info *phy_info;
 	uint32_t val;
 
+	phy_info = bxt_get_phy_info(dev_priv, phy);
+
 	val = I915_READ(BXT_PHY_CTL_FAMILY(phy));
 	val &= ~COMMON_RESET_DIS;
 	I915_WRITE(BXT_PHY_CTL_FAMILY(phy), val);
 
 	val = I915_READ(BXT_P_CR_GT_DISP_PWRON);
-	val &= ~GT_DISPLAY_POWER_ON(phy);
+	val &= ~phy_info->pwron_mask;
 	I915_WRITE(BXT_P_CR_GT_DISP_PWRON, val);
 }
 
 void bxt_ddi_phy_init(struct drm_i915_private *dev_priv, enum dpio_phy phy)
 {
-	const struct bxt_ddi_phy_info *phy_info = &bxt_ddi_phy_info[phy];
+	const struct bxt_ddi_phy_info *phy_info =
+		bxt_get_phy_info(dev_priv, phy);
 	enum dpio_phy rcomp_phy = phy_info->rcomp_phy;
 	bool was_enabled;
 
@@ -461,10 +543,12 @@  __phy_reg_verify_state(struct drm_i915_private *dev_priv, enum dpio_phy phy,
 bool bxt_ddi_phy_verify_state(struct drm_i915_private *dev_priv,
 			      enum dpio_phy phy)
 {
-	const struct bxt_ddi_phy_info *phy_info = &bxt_ddi_phy_info[phy];
+	const struct bxt_ddi_phy_info *phy_info;
 	uint32_t mask;
 	bool ok;
 
+	phy_info = bxt_get_phy_info(dev_priv, phy);
+
 #define _CHK(reg, mask, exp, fmt, ...)					\
 	__phy_reg_verify_state(dev_priv, phy, reg, mask, exp, fmt,	\
 			       ## __VA_ARGS__)
@@ -540,7 +624,7 @@  void bxt_ddi_phy_set_lane_optim_mask(struct intel_encoder *encoder,
 	enum dpio_channel ch;
 	int lane;
 
-	bxt_port_to_phy_channel(port, &phy, &ch);
+	bxt_port_to_phy_channel(dev_priv, port, &phy, &ch);
 
 	for (lane = 0; lane < 4; lane++) {
 		u32 val = I915_READ(BXT_PORT_TX_DW14_LN(phy, ch, lane));
@@ -568,7 +652,7 @@  bxt_ddi_phy_get_lane_lat_optim_mask(struct intel_encoder *encoder)
 	int lane;
 	uint8_t mask;
 
-	bxt_port_to_phy_channel(port, &phy, &ch);
+	bxt_port_to_phy_channel(dev_priv, port, &phy, &ch);
 
 	mask = 0;
 	for (lane = 0; lane < 4; lane++) {
diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.c b/drivers/gpu/drm/i915/intel_dpll_mgr.c
index 8205c1c..0311fd4 100644
--- a/drivers/gpu/drm/i915/intel_dpll_mgr.c
+++ b/drivers/gpu/drm/i915/intel_dpll_mgr.c
@@ -1374,7 +1374,7 @@  static void bxt_ddi_pll_enable(struct drm_i915_private *dev_priv,
 	enum dpio_phy phy;
 	enum dpio_channel ch;
 
-	bxt_port_to_phy_channel(port, &phy, &ch);
+	bxt_port_to_phy_channel(dev_priv, port, &phy, &ch);
 
 	/* Non-SSC reference */
 	temp = I915_READ(BXT_PORT_PLL_ENABLE(port));
@@ -1492,7 +1492,7 @@  static bool bxt_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv,
 	enum dpio_phy phy;
 	enum dpio_channel ch;
 
-	bxt_port_to_phy_channel(port, &phy, &ch);
+	bxt_port_to_phy_channel(dev_priv, port, &phy, &ch);
 
 	if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS))
 		return false;
diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
index 683c15b..30d5112 100644
--- a/drivers/gpu/drm/i915/intel_runtime_pm.c
+++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
@@ -477,6 +477,18 @@  static void hsw_set_power_well(struct drm_i915_private *dev_priv,
 #define GLK_DISPLAY_DDI_C_POWER_DOMAINS (		\
 	BIT(POWER_DOMAIN_PORT_DDI_C_LANES) |		\
 	BIT(POWER_DOMAIN_INIT))
+#define GLK_DPIO_CMN_A_POWER_DOMAINS (			\
+	BIT(POWER_DOMAIN_PORT_DDI_A_LANES) |		\
+	BIT(POWER_DOMAIN_AUX_A) |			\
+	BIT(POWER_DOMAIN_INIT))
+#define GLK_DPIO_CMN_B_POWER_DOMAINS (			\
+	BIT(POWER_DOMAIN_PORT_DDI_B_LANES) |		\
+	BIT(POWER_DOMAIN_AUX_B) |			\
+	BIT(POWER_DOMAIN_INIT))
+#define GLK_DPIO_CMN_C_POWER_DOMAINS (			\
+	BIT(POWER_DOMAIN_PORT_DDI_C_LANES) |		\
+	BIT(POWER_DOMAIN_AUX_C) |			\
+	BIT(POWER_DOMAIN_INIT))
 #define GLK_DISPLAY_AUX_A_POWER_DOMAINS (		\
 	BIT(POWER_DOMAIN_AUX_A) |		\
 	BIT(POWER_DOMAIN_INIT))
@@ -926,6 +938,12 @@  static void bxt_verify_ddi_phy_power_wells(struct drm_i915_private *dev_priv)
 	power_well = lookup_power_well(dev_priv, BXT_DPIO_CMN_BC);
 	if (power_well->count > 0)
 		bxt_ddi_phy_verify_state(dev_priv, power_well->data);
+
+	if (IS_GEMINILAKE(dev_priv)) {
+		power_well = lookup_power_well(dev_priv, GLK_DPIO_CMN_C);
+		if (power_well->count > 0)
+			bxt_ddi_phy_verify_state(dev_priv, power_well->data);
+	}
 }
 
 static bool gen9_dc_off_power_well_enabled(struct drm_i915_private *dev_priv,
@@ -2219,6 +2237,27 @@  static struct i915_power_well glk_power_wells[] = {
 		.id = SKL_DISP_PW_2,
 	},
 	{
+		.name = "dpio-common-a",
+		.domains = GLK_DPIO_CMN_A_POWER_DOMAINS,
+		.ops = &bxt_dpio_cmn_power_well_ops,
+		.id = BXT_DPIO_CMN_A,
+		.data = DPIO_PHY1,
+	},
+	{
+		.name = "dpio-common-b",
+		.domains = GLK_DPIO_CMN_B_POWER_DOMAINS,
+		.ops = &bxt_dpio_cmn_power_well_ops,
+		.id = BXT_DPIO_CMN_BC,
+		.data = DPIO_PHY0,
+	},
+	{
+		.name = "dpio-common-c",
+		.domains = GLK_DPIO_CMN_C_POWER_DOMAINS,
+		.ops = &bxt_dpio_cmn_power_well_ops,
+		.id = GLK_DPIO_CMN_C,
+		.data = DPIO_PHY2,
+	},
+	{
 		.name = "AUX A",
 		.domains = GLK_DISPLAY_AUX_A_POWER_DOMAINS,
 		.ops = &skl_power_well_ops,