diff options
Diffstat (limited to 'drivers/gpu/drm/i915/gvt/handlers.c')
| -rw-r--r-- | drivers/gpu/drm/i915/gvt/handlers.c | 261 | 
1 files changed, 255 insertions, 6 deletions
| diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 6eeaeecb7f85..477badfcb258 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -39,6 +39,7 @@  #include "i915_drv.h"  #include "gvt.h"  #include "i915_pvinfo.h" +#include "display/intel_display_types.h"  /* XXX FIXME i915 has changed PP_XXX definition */  #define PCH_PP_STATUS  _MMIO(0xc7200) @@ -443,6 +444,254 @@ static int dpy_reg_mmio_read(struct intel_vgpu *vgpu, unsigned int offset,  	return 0;  } +/* + * Only PIPE_A is enabled in current vGPU display and PIPE_A is tied to + *   TRANSCODER_A in HW. DDI/PORT could be PORT_x depends on + *   setup_virtual_dp_monitor(). + * emulate_monitor_status_change() set up PLL for PORT_x as the initial enabled + *   DPLL. Later guest driver may setup a different DPLLx when setting mode. + * So the correct sequence to find DP stream clock is: + *   Check TRANS_DDI_FUNC_CTL on TRANSCODER_A to get PORT_x. + *   Check correct PLLx for PORT_x to get PLL frequency and DP bitrate. + * Then Refresh rate then can be calculated based on follow equations: + *   Pixel clock = h_total * v_total * refresh_rate + *   stream clock = Pixel clock + *   ls_clk = DP bitrate + *   Link M/N = strm_clk / ls_clk + */ + +static u32 bdw_vgpu_get_dp_bitrate(struct intel_vgpu *vgpu, enum port port) +{ +	u32 dp_br = 0; +	u32 ddi_pll_sel = vgpu_vreg_t(vgpu, PORT_CLK_SEL(port)); + +	switch (ddi_pll_sel) { +	case PORT_CLK_SEL_LCPLL_2700: +		dp_br = 270000 * 2; +		break; +	case PORT_CLK_SEL_LCPLL_1350: +		dp_br = 135000 * 2; +		break; +	case PORT_CLK_SEL_LCPLL_810: +		dp_br = 81000 * 2; +		break; +	case PORT_CLK_SEL_SPLL: +	{ +		switch (vgpu_vreg_t(vgpu, SPLL_CTL) & SPLL_FREQ_MASK) { +		case SPLL_FREQ_810MHz: +			dp_br = 81000 * 2; +			break; +		case SPLL_FREQ_1350MHz: +			dp_br = 135000 * 2; +			break; +		case SPLL_FREQ_2700MHz: +			dp_br = 270000 * 2; +			break; +		default: +			gvt_dbg_dpy("vgpu-%d PORT_%c can't get freq from SPLL 0x%08x\n", +				    vgpu->id, port_name(port), vgpu_vreg_t(vgpu, SPLL_CTL)); +			break; +		} +		break; +	} +	case PORT_CLK_SEL_WRPLL1: +	case PORT_CLK_SEL_WRPLL2: +	{ +		u32 wrpll_ctl; +		int refclk, n, p, r; + +		if (ddi_pll_sel == PORT_CLK_SEL_WRPLL1) +			wrpll_ctl = vgpu_vreg_t(vgpu, WRPLL_CTL(DPLL_ID_WRPLL1)); +		else +			wrpll_ctl = vgpu_vreg_t(vgpu, WRPLL_CTL(DPLL_ID_WRPLL2)); + +		switch (wrpll_ctl & WRPLL_REF_MASK) { +		case WRPLL_REF_PCH_SSC: +			refclk = vgpu->gvt->gt->i915->dpll.ref_clks.ssc; +			break; +		case WRPLL_REF_LCPLL: +			refclk = 2700000; +			break; +		default: +			gvt_dbg_dpy("vgpu-%d PORT_%c WRPLL can't get refclk 0x%08x\n", +				    vgpu->id, port_name(port), wrpll_ctl); +			goto out; +		} + +		r = wrpll_ctl & WRPLL_DIVIDER_REF_MASK; +		p = (wrpll_ctl & WRPLL_DIVIDER_POST_MASK) >> WRPLL_DIVIDER_POST_SHIFT; +		n = (wrpll_ctl & WRPLL_DIVIDER_FB_MASK) >> WRPLL_DIVIDER_FB_SHIFT; + +		dp_br = (refclk * n / 10) / (p * r) * 2; +		break; +	} +	default: +		gvt_dbg_dpy("vgpu-%d PORT_%c has invalid clock select 0x%08x\n", +			    vgpu->id, port_name(port), vgpu_vreg_t(vgpu, PORT_CLK_SEL(port))); +		break; +	} + +out: +	return dp_br; +} + +static u32 bxt_vgpu_get_dp_bitrate(struct intel_vgpu *vgpu, enum port port) +{ +	u32 dp_br = 0; +	int refclk = vgpu->gvt->gt->i915->dpll.ref_clks.nssc; +	enum dpio_phy phy = DPIO_PHY0; +	enum dpio_channel ch = DPIO_CH0; +	struct dpll clock = {0}; +	u32 temp; + +	/* Port to PHY mapping is fixed, see bxt_ddi_phy_info{} */ +	switch (port) { +	case PORT_A: +		phy = DPIO_PHY1; +		ch = DPIO_CH0; +		break; +	case PORT_B: +		phy = DPIO_PHY0; +		ch = DPIO_CH0; +		break; +	case PORT_C: +		phy = DPIO_PHY0; +		ch = DPIO_CH1; +		break; +	default: +		gvt_dbg_dpy("vgpu-%d no PHY for PORT_%c\n", vgpu->id, port_name(port)); +		goto out; +	} + +	temp = vgpu_vreg_t(vgpu, BXT_PORT_PLL_ENABLE(port)); +	if (!(temp & PORT_PLL_ENABLE) || !(temp & PORT_PLL_LOCK)) { +		gvt_dbg_dpy("vgpu-%d PORT_%c PLL_ENABLE 0x%08x isn't enabled or locked\n", +			    vgpu->id, port_name(port), temp); +		goto out; +	} + +	clock.m1 = 2; +	clock.m2 = (vgpu_vreg_t(vgpu, BXT_PORT_PLL(phy, ch, 0)) & PORT_PLL_M2_MASK) << 22; +	if (vgpu_vreg_t(vgpu, BXT_PORT_PLL(phy, ch, 3)) & PORT_PLL_M2_FRAC_ENABLE) +		clock.m2 |= vgpu_vreg_t(vgpu, BXT_PORT_PLL(phy, ch, 2)) & PORT_PLL_M2_FRAC_MASK; +	clock.n = (vgpu_vreg_t(vgpu, BXT_PORT_PLL(phy, ch, 1)) & PORT_PLL_N_MASK) >> PORT_PLL_N_SHIFT; +	clock.p1 = (vgpu_vreg_t(vgpu, BXT_PORT_PLL_EBB_0(phy, ch)) & PORT_PLL_P1_MASK) >> PORT_PLL_P1_SHIFT; +	clock.p2 = (vgpu_vreg_t(vgpu, BXT_PORT_PLL_EBB_0(phy, ch)) & PORT_PLL_P2_MASK) >> PORT_PLL_P2_SHIFT; +	clock.m = clock.m1 * clock.m2; +	clock.p = clock.p1 * clock.p2; + +	if (clock.n == 0 || clock.p == 0) { +		gvt_dbg_dpy("vgpu-%d PORT_%c PLL has invalid divider\n", vgpu->id, port_name(port)); +		goto out; +	} + +	clock.vco = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(refclk, clock.m), clock.n << 22); +	clock.dot = DIV_ROUND_CLOSEST(clock.vco, clock.p); + +	dp_br = clock.dot / 5; + +out: +	return dp_br; +} + +static u32 skl_vgpu_get_dp_bitrate(struct intel_vgpu *vgpu, enum port port) +{ +	u32 dp_br = 0; +	enum intel_dpll_id dpll_id = DPLL_ID_SKL_DPLL0; + +	/* Find the enabled DPLL for the DDI/PORT */ +	if (!(vgpu_vreg_t(vgpu, DPLL_CTRL2) & DPLL_CTRL2_DDI_CLK_OFF(port)) && +	    (vgpu_vreg_t(vgpu, DPLL_CTRL2) & DPLL_CTRL2_DDI_SEL_OVERRIDE(port))) { +		dpll_id += (vgpu_vreg_t(vgpu, DPLL_CTRL2) & +			DPLL_CTRL2_DDI_CLK_SEL_MASK(port)) >> +			DPLL_CTRL2_DDI_CLK_SEL_SHIFT(port); +	} else { +		gvt_dbg_dpy("vgpu-%d DPLL for PORT_%c isn't turned on\n", +			    vgpu->id, port_name(port)); +		return dp_br; +	} + +	/* Find PLL output frequency from correct DPLL, and get bir rate */ +	switch ((vgpu_vreg_t(vgpu, DPLL_CTRL1) & +		DPLL_CTRL1_LINK_RATE_MASK(dpll_id)) >> +		DPLL_CTRL1_LINK_RATE_SHIFT(dpll_id)) { +		case DPLL_CTRL1_LINK_RATE_810: +			dp_br = 81000 * 2; +			break; +		case DPLL_CTRL1_LINK_RATE_1080: +			dp_br = 108000 * 2; +			break; +		case DPLL_CTRL1_LINK_RATE_1350: +			dp_br = 135000 * 2; +			break; +		case DPLL_CTRL1_LINK_RATE_1620: +			dp_br = 162000 * 2; +			break; +		case DPLL_CTRL1_LINK_RATE_2160: +			dp_br = 216000 * 2; +			break; +		case DPLL_CTRL1_LINK_RATE_2700: +			dp_br = 270000 * 2; +			break; +		default: +			dp_br = 0; +			gvt_dbg_dpy("vgpu-%d PORT_%c fail to get DPLL-%d freq\n", +				    vgpu->id, port_name(port), dpll_id); +	} + +	return dp_br; +} + +static void vgpu_update_refresh_rate(struct intel_vgpu *vgpu) +{ +	struct drm_i915_private *dev_priv = vgpu->gvt->gt->i915; +	enum port port; +	u32 dp_br, link_m, link_n, htotal, vtotal; + +	/* Find DDI/PORT assigned to TRANSCODER_A, expect B or D */ +	port = (vgpu_vreg_t(vgpu, TRANS_DDI_FUNC_CTL(TRANSCODER_A)) & +		TRANS_DDI_PORT_MASK) >> TRANS_DDI_PORT_SHIFT; +	if (port != PORT_B && port != PORT_D) { +		gvt_dbg_dpy("vgpu-%d unsupported PORT_%c\n", vgpu->id, port_name(port)); +		return; +	} + +	/* Calculate DP bitrate from PLL */ +	if (IS_BROADWELL(dev_priv)) +		dp_br = bdw_vgpu_get_dp_bitrate(vgpu, port); +	else if (IS_BROXTON(dev_priv)) +		dp_br = bxt_vgpu_get_dp_bitrate(vgpu, port); +	else +		dp_br = skl_vgpu_get_dp_bitrate(vgpu, port); + +	/* Get DP link symbol clock M/N */ +	link_m = vgpu_vreg_t(vgpu, PIPE_LINK_M1(TRANSCODER_A)); +	link_n = vgpu_vreg_t(vgpu, PIPE_LINK_N1(TRANSCODER_A)); + +	/* Get H/V total from transcoder timing */ +	htotal = (vgpu_vreg_t(vgpu, HTOTAL(TRANSCODER_A)) >> TRANS_HTOTAL_SHIFT) + 1; +	vtotal = (vgpu_vreg_t(vgpu, VTOTAL(TRANSCODER_A)) >> TRANS_VTOTAL_SHIFT) + 1; + +	if (dp_br && link_n && htotal && vtotal) { +		u64 pixel_clk = 0; +		u32 new_rate = 0; +		u32 *old_rate = &(intel_vgpu_port(vgpu, vgpu->display.port_num)->vrefresh_k); + +		/* Calcuate pixel clock by (ls_clk * M / N) */ +		pixel_clk = div_u64(mul_u32_u32(link_m, dp_br), link_n); +		pixel_clk *= MSEC_PER_SEC; + +		/* Calcuate refresh rate by (pixel_clk / (h_total * v_total)) */ +		new_rate = DIV64_U64_ROUND_CLOSEST(pixel_clk, div64_u64(mul_u32_u32(htotal, vtotal), MSEC_PER_SEC)); + +		if (*old_rate != new_rate) +			*old_rate = new_rate; + +		gvt_dbg_dpy("vgpu-%d PIPE_%c refresh rate updated to %d\n", +			    vgpu->id, pipe_name(PIPE_A), new_rate); +	} +} +  static int pipeconf_mmio_write(struct intel_vgpu *vgpu, unsigned int offset,  		void *p_data, unsigned int bytes)  { @@ -451,14 +700,14 @@ static int pipeconf_mmio_write(struct intel_vgpu *vgpu, unsigned int offset,  	write_vreg(vgpu, offset, p_data, bytes);  	data = vgpu_vreg(vgpu, offset); -	if (data & PIPECONF_ENABLE) +	if (data & PIPECONF_ENABLE) {  		vgpu_vreg(vgpu, offset) |= I965_PIPECONF_ACTIVE; -	else +		vgpu_update_refresh_rate(vgpu); +		vgpu_update_vblank_emulation(vgpu, true); +	} else {  		vgpu_vreg(vgpu, offset) &= ~I965_PIPECONF_ACTIVE; -	/* vgpu_lock already hold by emulate mmio r/w */ -	mutex_unlock(&vgpu->vgpu_lock); -	intel_gvt_check_vblank_emulation(vgpu->gvt); -	mutex_lock(&vgpu->vgpu_lock); +		vgpu_update_vblank_emulation(vgpu, false); +	}  	return 0;  } | 
