diff options
Diffstat (limited to 'drivers/gpu/drm/tegra/rgb.c')
| -rw-r--r-- | drivers/gpu/drm/tegra/rgb.c | 53 | 
1 files changed, 47 insertions, 6 deletions
| diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index 606c78a2b988..ff8fce36d2aa 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -17,6 +17,8 @@ struct tegra_rgb {  	struct tegra_output output;  	struct tegra_dc *dc; +	struct clk *pll_d_out0; +	struct clk *pll_d2_out0;  	struct clk *clk_parent;  	struct clk *clk;  }; @@ -116,13 +118,21 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)  		DISP_ORDER_RED_BLUE;  	tegra_dc_writel(rgb->dc, value, DC_DISP_DISP_INTERFACE_CONTROL); -	/* XXX: parameterize? */ -	value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE; -	tegra_dc_writel(rgb->dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS); -  	tegra_dc_commit(rgb->dc);  } +static bool tegra_rgb_pll_rate_change_allowed(struct tegra_rgb *rgb) +{ +	if (!rgb->pll_d2_out0) +		return false; + +	if (!clk_is_match(rgb->clk_parent, rgb->pll_d_out0) && +	    !clk_is_match(rgb->clk_parent, rgb->pll_d2_out0)) +		return false; + +	return true; +} +  static int  tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder,  			       struct drm_crtc_state *crtc_state, @@ -151,8 +161,17 @@ tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder,  	 * and hope that the desired frequency can be matched (or at least  	 * matched sufficiently close that the panel will still work).  	 */ -	div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2; -	pclk = 0; +	if (tegra_rgb_pll_rate_change_allowed(rgb)) { +		/* +		 * Set display controller clock to x2 of PCLK in order to +		 * produce higher resolution pulse positions. +		 */ +		div = 2; +		pclk *= 2; +	} else { +		div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2; +		pclk = 0; +	}  	err = tegra_dc_state_setup_clock(dc, crtc_state, rgb->clk_parent,  					 pclk, div); @@ -210,6 +229,22 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)  		return err;  	} +	rgb->pll_d_out0 = clk_get_sys(NULL, "pll_d_out0"); +	if (IS_ERR(rgb->pll_d_out0)) { +		err = PTR_ERR(rgb->pll_d_out0); +		dev_err(dc->dev, "failed to get pll_d_out0: %d\n", err); +		return err; +	} + +	if (dc->soc->has_pll_d2_out0) { +		rgb->pll_d2_out0 = clk_get_sys(NULL, "pll_d2_out0"); +		if (IS_ERR(rgb->pll_d2_out0)) { +			err = PTR_ERR(rgb->pll_d2_out0); +			dev_err(dc->dev, "failed to get pll_d2_out0: %d\n", err); +			return err; +		} +	} +  	dc->rgb = &rgb->output;  	return 0; @@ -217,9 +252,15 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)  int tegra_dc_rgb_remove(struct tegra_dc *dc)  { +	struct tegra_rgb *rgb; +  	if (!dc->rgb)  		return 0; +	rgb = to_rgb(dc->rgb); +	clk_put(rgb->pll_d2_out0); +	clk_put(rgb->pll_d_out0); +  	tegra_output_remove(dc->rgb);  	dc->rgb = NULL; |