diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_panel.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/intel_panel.c | 363 | 
1 files changed, 228 insertions, 135 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index a0c8e43db5eb..d1d1b59102d6 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -35,6 +35,7 @@  #include "intel_connector.h"  #include "intel_de.h"  #include "intel_display_types.h" +#include "intel_drrs.h"  #include "intel_panel.h"  bool intel_panel_use_ssc(struct drm_i915_private *i915) @@ -45,10 +46,87 @@ bool intel_panel_use_ssc(struct drm_i915_private *i915)  		&& !(i915->quirks & QUIRK_LVDS_SSC_DISABLE);  } +const struct drm_display_mode * +intel_panel_preferred_fixed_mode(struct intel_connector *connector) +{ +	return list_first_entry_or_null(&connector->panel.fixed_modes, +					struct drm_display_mode, head); +} + +const struct drm_display_mode * +intel_panel_fixed_mode(struct intel_connector *connector, +		       const struct drm_display_mode *mode) +{ +	const struct drm_display_mode *fixed_mode, *best_mode = NULL; +	int vrefresh = drm_mode_vrefresh(mode); + +	/* pick the fixed_mode that is closest in terms of vrefresh */ +	list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) { +		if (!best_mode || +		    abs(drm_mode_vrefresh(fixed_mode) - vrefresh) < +		    abs(drm_mode_vrefresh(best_mode) - vrefresh)) +			best_mode = fixed_mode; +	} + +	return best_mode; +} + +const struct drm_display_mode * +intel_panel_downclock_mode(struct intel_connector *connector, +			   const struct drm_display_mode *adjusted_mode) +{ +	struct drm_i915_private *i915 = to_i915(connector->base.dev); +	const struct drm_display_mode *fixed_mode, *best_mode = NULL; +	int min_vrefresh = i915->vbt.seamless_drrs_min_refresh_rate; +	int max_vrefresh = drm_mode_vrefresh(adjusted_mode); + +	/* pick the fixed_mode with the lowest refresh rate */ +	list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) { +		int vrefresh = drm_mode_vrefresh(fixed_mode); + +		if (vrefresh >= min_vrefresh && vrefresh < max_vrefresh) { +			max_vrefresh = vrefresh; +			best_mode = fixed_mode; +		} +	} + +	return best_mode; +} + +int intel_panel_get_modes(struct intel_connector *connector) +{ +	const struct drm_display_mode *fixed_mode; +	int num_modes = 0; + +	list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) { +		struct drm_display_mode *mode; + +		mode = drm_mode_duplicate(connector->base.dev, fixed_mode); +		if (mode) { +			drm_mode_probed_add(&connector->base, mode); +			num_modes++; +		} +	} + +	return num_modes; +} + +enum drrs_type intel_panel_drrs_type(struct intel_connector *connector) +{ +	struct drm_i915_private *i915 = to_i915(connector->base.dev); + +	if (list_empty(&connector->panel.fixed_modes) || +	    list_is_singular(&connector->panel.fixed_modes)) +		return DRRS_TYPE_NONE; + +	return i915->vbt.drrs_type; +} +  int intel_panel_compute_config(struct intel_connector *connector,  			       struct drm_display_mode *adjusted_mode)  { -	const struct drm_display_mode *fixed_mode = connector->panel.fixed_mode; +	const struct drm_display_mode *fixed_mode = +		intel_panel_fixed_mode(connector, adjusted_mode);  	if (!fixed_mode)  		return 0; @@ -75,128 +153,142 @@ int intel_panel_compute_config(struct intel_connector *connector,  	return 0;  } -static bool is_downclock_mode(const struct drm_display_mode *downclock_mode, -			      const struct drm_display_mode *fixed_mode) +static bool is_alt_fixed_mode(const struct drm_display_mode *mode, +			      const struct drm_display_mode *preferred_mode)  { -	return drm_mode_match(downclock_mode, fixed_mode, +	return drm_mode_match(mode, preferred_mode,  			      DRM_MODE_MATCH_TIMINGS |  			      DRM_MODE_MATCH_FLAGS |  			      DRM_MODE_MATCH_3D_FLAGS) && -		downclock_mode->clock < fixed_mode->clock; +		mode->clock != preferred_mode->clock;  } -struct drm_display_mode * -intel_panel_edid_downclock_mode(struct intel_connector *connector, -				const struct drm_display_mode *fixed_mode) +static void intel_panel_add_edid_alt_fixed_modes(struct intel_connector *connector)  {  	struct drm_i915_private *dev_priv = to_i915(connector->base.dev); -	const struct drm_display_mode *scan, *best_mode = NULL; -	struct drm_display_mode *downclock_mode; -	int best_clock = fixed_mode->clock; +	const struct drm_display_mode *preferred_mode = +		intel_panel_preferred_fixed_mode(connector); +	struct drm_display_mode *mode, *next; -	list_for_each_entry(scan, &connector->base.probed_modes, head) { -		/* -		 * If one mode has the same resolution with the fixed_panel -		 * mode while they have the different refresh rate, it means -		 * that the reduced downclock is found. In such -		 * case we can set the different FPx0/1 to dynamically select -		 * between low and high frequency. -		 */ -		if (is_downclock_mode(scan, fixed_mode) && -		    scan->clock < best_clock) { -			/* -			 * The downclock is already found. But we -			 * expect to find the lower downclock. -			 */ -			best_clock = scan->clock; -			best_mode = scan; -		} -	} - -	if (!best_mode) -		return NULL; - -	downclock_mode = drm_mode_duplicate(&dev_priv->drm, best_mode); -	if (!downclock_mode) -		return NULL; +	list_for_each_entry_safe(mode, next, &connector->base.probed_modes, head) { +		if (!is_alt_fixed_mode(mode, preferred_mode)) +			continue; -	drm_dbg_kms(&dev_priv->drm, -		    "[CONNECTOR:%d:%s] using downclock mode from EDID: ", -		    connector->base.base.id, connector->base.name); -	drm_mode_debug_printmodeline(downclock_mode); +		drm_dbg_kms(&dev_priv->drm, +			    "[CONNECTOR:%d:%s] using alternate EDID fixed mode: " DRM_MODE_FMT "\n", +			    connector->base.base.id, connector->base.name, +			    DRM_MODE_ARG(mode)); -	return downclock_mode; +		list_move_tail(&mode->head, &connector->panel.fixed_modes); +	}  } -struct drm_display_mode * -intel_panel_edid_fixed_mode(struct intel_connector *connector) +static void intel_panel_add_edid_preferred_mode(struct intel_connector *connector)  {  	struct drm_i915_private *dev_priv = to_i915(connector->base.dev); -	const struct drm_display_mode *scan; -	struct drm_display_mode *fixed_mode; +	struct drm_display_mode *scan, *fixed_mode = NULL;  	if (list_empty(&connector->base.probed_modes)) -		return NULL; +		return; -	/* prefer fixed mode from EDID if available */ +	/* make sure the preferred mode is first */  	list_for_each_entry(scan, &connector->base.probed_modes, head) { -		if ((scan->type & DRM_MODE_TYPE_PREFERRED) == 0) -			continue; +		if (scan->type & DRM_MODE_TYPE_PREFERRED) { +			fixed_mode = scan; +			break; +		} +	} -		fixed_mode = drm_mode_duplicate(&dev_priv->drm, scan); -		if (!fixed_mode) -			return NULL; +	if (!fixed_mode) +		fixed_mode = list_first_entry(&connector->base.probed_modes, +					      typeof(*fixed_mode), head); -		drm_dbg_kms(&dev_priv->drm, -			    "[CONNECTOR:%d:%s] using preferred mode from EDID: ", -			    connector->base.base.id, connector->base.name); -		drm_mode_debug_printmodeline(fixed_mode); +	drm_dbg_kms(&dev_priv->drm, +		    "[CONNECTOR:%d:%s] using %s EDID fixed mode: " DRM_MODE_FMT "\n", +		    connector->base.base.id, connector->base.name, +		    fixed_mode->type & DRM_MODE_TYPE_PREFERRED ? "preferred" : "first", +		    DRM_MODE_ARG(fixed_mode)); + +	fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; -		return fixed_mode; +	list_move_tail(&fixed_mode->head, &connector->panel.fixed_modes); +} + +static void intel_panel_destroy_probed_modes(struct intel_connector *connector) +{ +	struct drm_i915_private *i915 = to_i915(connector->base.dev); +	struct drm_display_mode *mode, *next; + +	list_for_each_entry_safe(mode, next, &connector->base.probed_modes, head) { +		list_del(&mode->head); +		drm_mode_destroy(&i915->drm, mode);  	} +} -	scan = list_first_entry(&connector->base.probed_modes, -				typeof(*scan), head); +void intel_panel_add_edid_fixed_modes(struct intel_connector *connector, bool has_drrs) +{ +	intel_panel_add_edid_preferred_mode(connector); +	if (intel_panel_preferred_fixed_mode(connector) && has_drrs) +		intel_panel_add_edid_alt_fixed_modes(connector); +	intel_panel_destroy_probed_modes(connector); +} + +static void intel_panel_add_fixed_mode(struct intel_connector *connector, +				       struct drm_display_mode *fixed_mode, +				       const char *type) +{ +	struct drm_i915_private *i915 = to_i915(connector->base.dev); +	struct drm_display_info *info = &connector->base.display_info; -	fixed_mode = drm_mode_duplicate(&dev_priv->drm, scan);  	if (!fixed_mode) -		return NULL; +		return; -	fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; +	fixed_mode->type |= DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; -	drm_dbg_kms(&dev_priv->drm, -		    "[CONNECTOR:%d:%s] using first mode from EDID: ", -		    connector->base.base.id, connector->base.name); -	drm_mode_debug_printmodeline(fixed_mode); +	info->width_mm = fixed_mode->width_mm; +	info->height_mm = fixed_mode->height_mm; -	return fixed_mode; +	drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] using %s fixed mode: " DRM_MODE_FMT "\n", +		    connector->base.base.id, connector->base.name, type, +		    DRM_MODE_ARG(fixed_mode)); + +	list_add_tail(&fixed_mode->head, &connector->panel.fixed_modes);  } -struct drm_display_mode * -intel_panel_vbt_fixed_mode(struct intel_connector *connector) +void intel_panel_add_vbt_lfp_fixed_mode(struct intel_connector *connector)  { -	struct drm_i915_private *dev_priv = to_i915(connector->base.dev); -	struct drm_display_info *info = &connector->base.display_info; -	struct drm_display_mode *fixed_mode; +	struct drm_i915_private *i915 = to_i915(connector->base.dev); +	const struct drm_display_mode *mode; -	if (!dev_priv->vbt.lfp_lvds_vbt_mode) -		return NULL; +	mode = i915->vbt.lfp_lvds_vbt_mode; +	if (!mode) +		return; -	fixed_mode = drm_mode_duplicate(&dev_priv->drm, -					dev_priv->vbt.lfp_lvds_vbt_mode); -	if (!fixed_mode) -		return NULL; +	intel_panel_add_fixed_mode(connector, +				   drm_mode_duplicate(&i915->drm, mode), +				   "VBT LFP"); +} -	fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; +void intel_panel_add_vbt_sdvo_fixed_mode(struct intel_connector *connector) +{ +	struct drm_i915_private *i915 = to_i915(connector->base.dev); +	const struct drm_display_mode *mode; -	drm_dbg_kms(&dev_priv->drm, "[CONNECTOR:%d:%s] using mode from VBT: ", -		    connector->base.base.id, connector->base.name); -	drm_mode_debug_printmodeline(fixed_mode); +	mode = i915->vbt.sdvo_lvds_vbt_mode; +	if (!mode) +		return; -	info->width_mm = fixed_mode->width_mm; -	info->height_mm = fixed_mode->height_mm; +	intel_panel_add_fixed_mode(connector, +				   drm_mode_duplicate(&i915->drm, mode), +				   "VBT SDVO"); +} -	return fixed_mode; +void intel_panel_add_encoder_fixed_mode(struct intel_connector *connector, +					struct intel_encoder *encoder) +{ +	intel_panel_add_fixed_mode(connector, +				   intel_encoder_current_mode(encoder), +				   "current (BIOS)");  }  /* adjusted_mode has been preset to be the panel's fixed mode */ @@ -205,18 +297,20 @@ static int pch_panel_fitting(struct intel_crtc_state *crtc_state,  {  	const struct drm_display_mode *adjusted_mode =  		&crtc_state->hw.adjusted_mode; +	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src); +	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);  	int x, y, width, height;  	/* Native modes don't need fitting */ -	if (adjusted_mode->crtc_hdisplay == crtc_state->pipe_src_w && -	    adjusted_mode->crtc_vdisplay == crtc_state->pipe_src_h && +	if (adjusted_mode->crtc_hdisplay == pipe_src_w && +	    adjusted_mode->crtc_vdisplay == pipe_src_h &&  	    crtc_state->output_format != INTEL_OUTPUT_FORMAT_YCBCR420)  		return 0;  	switch (conn_state->scaling_mode) {  	case DRM_MODE_SCALE_CENTER: -		width = crtc_state->pipe_src_w; -		height = crtc_state->pipe_src_h; +		width = pipe_src_w; +		height = pipe_src_h;  		x = (adjusted_mode->crtc_hdisplay - width + 1)/2;  		y = (adjusted_mode->crtc_vdisplay - height + 1)/2;  		break; @@ -224,19 +318,17 @@ static int pch_panel_fitting(struct intel_crtc_state *crtc_state,  	case DRM_MODE_SCALE_ASPECT:  		/* Scale but preserve the aspect ratio */  		{ -			u32 scaled_width = adjusted_mode->crtc_hdisplay -				* crtc_state->pipe_src_h; -			u32 scaled_height = crtc_state->pipe_src_w -				* adjusted_mode->crtc_vdisplay; +			u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h; +			u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;  			if (scaled_width > scaled_height) { /* pillar */ -				width = scaled_height / crtc_state->pipe_src_h; +				width = scaled_height / pipe_src_h;  				if (width & 1)  					width++;  				x = (adjusted_mode->crtc_hdisplay - width + 1) / 2;  				y = 0;  				height = adjusted_mode->crtc_vdisplay;  			} else if (scaled_width < scaled_height) { /* letter */ -				height = scaled_width / crtc_state->pipe_src_w; +				height = scaled_width / pipe_src_w;  				if (height & 1)  				    height++;  				y = (adjusted_mode->crtc_vdisplay - height + 1) / 2; @@ -251,8 +343,8 @@ static int pch_panel_fitting(struct intel_crtc_state *crtc_state,  		break;  	case DRM_MODE_SCALE_NONE: -		WARN_ON(adjusted_mode->crtc_hdisplay != crtc_state->pipe_src_w); -		WARN_ON(adjusted_mode->crtc_vdisplay != crtc_state->pipe_src_h); +		WARN_ON(adjusted_mode->crtc_hdisplay != pipe_src_w); +		WARN_ON(adjusted_mode->crtc_vdisplay != pipe_src_h);  		fallthrough;  	case DRM_MODE_SCALE_FULLSCREEN:  		x = y = 0; @@ -333,10 +425,10 @@ static void i965_scale_aspect(struct intel_crtc_state *crtc_state,  {  	const struct drm_display_mode *adjusted_mode =  		&crtc_state->hw.adjusted_mode; -	u32 scaled_width = adjusted_mode->crtc_hdisplay * -		crtc_state->pipe_src_h; -	u32 scaled_height = crtc_state->pipe_src_w * -		adjusted_mode->crtc_vdisplay; +	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src); +	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src); +	u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h; +	u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;  	/* 965+ is easy, it does everything in hw */  	if (scaled_width > scaled_height) @@ -345,7 +437,7 @@ static void i965_scale_aspect(struct intel_crtc_state *crtc_state,  	else if (scaled_width < scaled_height)  		*pfit_control |= PFIT_ENABLE |  			PFIT_SCALING_LETTER; -	else if (adjusted_mode->crtc_hdisplay != crtc_state->pipe_src_w) +	else if (adjusted_mode->crtc_hdisplay != pipe_src_w)  		*pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;  } @@ -354,10 +446,10 @@ static void i9xx_scale_aspect(struct intel_crtc_state *crtc_state,  			      u32 *border)  {  	struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; -	u32 scaled_width = adjusted_mode->crtc_hdisplay * -		crtc_state->pipe_src_h; -	u32 scaled_height = crtc_state->pipe_src_w * -		adjusted_mode->crtc_vdisplay; +	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src); +	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src); +	u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h; +	u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;  	u32 bits;  	/* @@ -367,12 +459,11 @@ static void i9xx_scale_aspect(struct intel_crtc_state *crtc_state,  	 */  	if (scaled_width > scaled_height) { /* pillar */  		centre_horizontally(adjusted_mode, -				    scaled_height / -				    crtc_state->pipe_src_h); +				    scaled_height / pipe_src_h);  		*border = LVDS_BORDER_ENABLE; -		if (crtc_state->pipe_src_h != adjusted_mode->crtc_vdisplay) { -			bits = panel_fitter_scaling(crtc_state->pipe_src_h, +		if (pipe_src_h != adjusted_mode->crtc_vdisplay) { +			bits = panel_fitter_scaling(pipe_src_h,  						    adjusted_mode->crtc_vdisplay);  			*pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | @@ -383,12 +474,11 @@ static void i9xx_scale_aspect(struct intel_crtc_state *crtc_state,  		}  	} else if (scaled_width < scaled_height) { /* letter */  		centre_vertically(adjusted_mode, -				  scaled_width / -				  crtc_state->pipe_src_w); +				  scaled_width / pipe_src_w);  		*border = LVDS_BORDER_ENABLE; -		if (crtc_state->pipe_src_w != adjusted_mode->crtc_hdisplay) { -			bits = panel_fitter_scaling(crtc_state->pipe_src_w, +		if (pipe_src_w != adjusted_mode->crtc_hdisplay) { +			bits = panel_fitter_scaling(pipe_src_w,  						    adjusted_mode->crtc_hdisplay);  			*pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | @@ -413,10 +503,12 @@ static int gmch_panel_fitting(struct intel_crtc_state *crtc_state,  	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);  	u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;  	struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; +	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src); +	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);  	/* Native modes don't need fitting */ -	if (adjusted_mode->crtc_hdisplay == crtc_state->pipe_src_w && -	    adjusted_mode->crtc_vdisplay == crtc_state->pipe_src_h) +	if (adjusted_mode->crtc_hdisplay == pipe_src_w && +	    adjusted_mode->crtc_vdisplay == pipe_src_h)  		goto out;  	switch (conn_state->scaling_mode) { @@ -425,8 +517,8 @@ static int gmch_panel_fitting(struct intel_crtc_state *crtc_state,  		 * For centered modes, we have to calculate border widths &  		 * heights and modify the values programmed into the CRTC.  		 */ -		centre_horizontally(adjusted_mode, crtc_state->pipe_src_w); -		centre_vertically(adjusted_mode, crtc_state->pipe_src_h); +		centre_horizontally(adjusted_mode, pipe_src_w); +		centre_vertically(adjusted_mode, pipe_src_h);  		border = LVDS_BORDER_ENABLE;  		break;  	case DRM_MODE_SCALE_ASPECT: @@ -442,8 +534,8 @@ static int gmch_panel_fitting(struct intel_crtc_state *crtc_state,  		 * Full scaling, even if it changes the aspect ratio.  		 * Fortunately this is all done for us in hw.  		 */ -		if (crtc_state->pipe_src_h != adjusted_mode->crtc_vdisplay || -		    crtc_state->pipe_src_w != adjusted_mode->crtc_hdisplay) { +		if (pipe_src_h != adjusted_mode->crtc_vdisplay || +		    pipe_src_w != adjusted_mode->crtc_hdisplay) {  			pfit_control |= PFIT_ENABLE;  			if (DISPLAY_VER(dev_priv) >= 4)  				pfit_control |= PFIT_SCALING_AUTO; @@ -508,7 +600,8 @@ enum drm_mode_status  intel_panel_mode_valid(struct intel_connector *connector,  		       const struct drm_display_mode *mode)  { -	const struct drm_display_mode *fixed_mode = connector->panel.fixed_mode; +	const struct drm_display_mode *fixed_mode = +		intel_panel_fixed_mode(connector, mode);  	if (!fixed_mode)  		return MODE_OK; @@ -525,29 +618,29 @@ intel_panel_mode_valid(struct intel_connector *connector,  	return MODE_OK;  } -int intel_panel_init(struct intel_panel *panel, -		     struct drm_display_mode *fixed_mode, -		     struct drm_display_mode *downclock_mode) +int intel_panel_init(struct intel_connector *connector)  { +	struct intel_panel *panel = &connector->panel; +  	intel_backlight_init_funcs(panel); -	panel->fixed_mode = fixed_mode; -	panel->downclock_mode = downclock_mode; +	drm_dbg_kms(connector->base.dev, +		    "[CONNECTOR:%d:%s] DRRS type: %s\n", +		    connector->base.base.id, connector->base.name, +		    intel_drrs_type_str(intel_panel_drrs_type(connector)));  	return 0;  } -void intel_panel_fini(struct intel_panel *panel) +void intel_panel_fini(struct intel_connector *connector)  { -	struct intel_connector *intel_connector = -		container_of(panel, struct intel_connector, panel); +	struct intel_panel *panel = &connector->panel; +	struct drm_display_mode *fixed_mode, *next;  	intel_backlight_destroy(panel); -	if (panel->fixed_mode) -		drm_mode_destroy(intel_connector->base.dev, panel->fixed_mode); - -	if (panel->downclock_mode) -		drm_mode_destroy(intel_connector->base.dev, -				panel->downclock_mode); +	list_for_each_entry_safe(fixed_mode, next, &panel->fixed_modes, head) { +		list_del(&fixed_mode->head); +		drm_mode_destroy(connector->base.dev, fixed_mode); +	}  }  |