diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_hdmi.c')
| -rw-r--r-- | drivers/gpu/drm/i915/intel_hdmi.c | 204 | 
1 files changed, 159 insertions, 45 deletions
| diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index ec0779a52d53..e8abea7594ec 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -459,22 +459,31 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder,  	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);  	const struct drm_display_mode *adjusted_mode =  		&crtc_state->base.adjusted_mode; +	struct drm_connector *connector = &intel_hdmi->attached_connector->base; +	bool is_hdmi2_sink = connector->display_info.hdmi.scdc.supported;  	union hdmi_infoframe frame;  	int ret;  	ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, -						       adjusted_mode); +						       adjusted_mode, +						       is_hdmi2_sink);  	if (ret < 0) {  		DRM_ERROR("couldn't fill AVI infoframe\n");  		return;  	} +	if (crtc_state->ycbcr420) +		frame.avi.colorspace = HDMI_COLORSPACE_YUV420; +	else +		frame.avi.colorspace = HDMI_COLORSPACE_RGB; +  	drm_hdmi_avi_infoframe_quant_range(&frame.avi, adjusted_mode,  					   crtc_state->limited_color_range ?  					   HDMI_QUANTIZATION_RANGE_LIMITED :  					   HDMI_QUANTIZATION_RANGE_FULL,  					   intel_hdmi->rgb_quant_range_selectable); +	/* TODO: handle pixel repetition for YCBCR420 outputs */  	intel_write_infoframe(encoder, crtc_state, &frame);  } @@ -1292,6 +1301,9 @@ intel_hdmi_mode_valid(struct drm_connector *connector,  	if (mode->flags & DRM_MODE_FLAG_DBLCLK)  		clock *= 2; +	if (drm_mode_is_420_only(&connector->display_info, mode)) +		clock /= 2; +  	/* check if we can do 8bpc */  	status = hdmi_port_clock_valid(hdmi, clock, true, force_dvi); @@ -1321,14 +1333,21 @@ static bool hdmi_12bpc_possible(struct intel_crtc_state *crtc_state)  	if (crtc_state->output_types != 1 << INTEL_OUTPUT_HDMI)  		return false; -	for_each_connector_in_state(state, connector, connector_state, i) { +	for_each_new_connector_in_state(state, connector, connector_state, i) {  		const struct drm_display_info *info = &connector->display_info;  		if (connector_state->crtc != crtc_state->base.crtc)  			continue; -		if ((info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_36) == 0) -			return false; +		if (crtc_state->ycbcr420) { +			const struct drm_hdmi_info *hdmi = &info->hdmi; + +			if (!(hdmi->y420_dc_modes & DRM_EDID_YCBCR420_DC_36)) +				return false; +		} else { +			if (!(info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_36)) +				return false; +		}  	}  	/* Display Wa #1139 */ @@ -1339,6 +1358,36 @@ static bool hdmi_12bpc_possible(struct intel_crtc_state *crtc_state)  	return true;  } +static bool +intel_hdmi_ycbcr420_config(struct drm_connector *connector, +			   struct intel_crtc_state *config, +			   int *clock_12bpc, int *clock_8bpc) +{ +	struct intel_crtc *intel_crtc = to_intel_crtc(config->base.crtc); + +	if (!connector->ycbcr_420_allowed) { +		DRM_ERROR("Platform doesn't support YCBCR420 output\n"); +		return false; +	} + +	/* YCBCR420 TMDS rate requirement is half the pixel clock */ +	config->port_clock /= 2; +	*clock_12bpc /= 2; +	*clock_8bpc /= 2; +	config->ycbcr420 = true; + +	/* YCBCR 420 output conversion needs a scaler */ +	if (skl_update_scaler_crtc(config)) { +		DRM_DEBUG_KMS("Scaler allocation for output failed\n"); +		return false; +	} + +	intel_pch_panel_fitting(intel_crtc, config, +				DRM_MODE_SCALE_FULLSCREEN); + +	return true; +} +  bool intel_hdmi_compute_config(struct intel_encoder *encoder,  			       struct intel_crtc_state *pipe_config,  			       struct drm_connector_state *conn_state) @@ -1346,7 +1395,8 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,  	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);  	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);  	struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; -	struct drm_scdc *scdc = &conn_state->connector->display_info.hdmi.scdc; +	struct drm_connector *connector = conn_state->connector; +	struct drm_scdc *scdc = &connector->display_info.hdmi.scdc;  	struct intel_digital_connector_state *intel_conn_state =  		to_intel_digital_connector_state(conn_state);  	int clock_8bpc = pipe_config->base.adjusted_mode.crtc_clock; @@ -1376,6 +1426,14 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,  		clock_12bpc *= 2;  	} +	if (drm_mode_is_420_only(&connector->display_info, adjusted_mode)) { +		if (!intel_hdmi_ycbcr420_config(connector, pipe_config, +						&clock_12bpc, &clock_8bpc)) { +			DRM_ERROR("Can't support YCBCR420 output\n"); +			return false; +		} +	} +  	if (HAS_PCH_SPLIT(dev_priv) && !HAS_DDI(dev_priv))  		pipe_config->has_pch_encoder = true; @@ -1703,11 +1761,9 @@ static void intel_hdmi_destroy(struct drm_connector *connector)  }  static const struct drm_connector_funcs intel_hdmi_connector_funcs = { -	.dpms = drm_atomic_helper_connector_dpms,  	.detect = intel_hdmi_detect,  	.force = intel_hdmi_force,  	.fill_modes = drm_helper_probe_single_connector_modes, -	.set_property = drm_atomic_helper_connector_set_property,  	.atomic_get_property = intel_digital_connector_atomic_get_property,  	.atomic_set_property = intel_digital_connector_atomic_set_property,  	.late_register = intel_connector_register, @@ -1787,45 +1843,114 @@ void intel_hdmi_handle_sink_scrambling(struct intel_encoder *encoder,  	DRM_DEBUG_KMS("sink scrambling handled\n");  } -static u8 intel_hdmi_ddc_pin(struct drm_i915_private *dev_priv, -			     enum port port) +static u8 chv_port_to_ddc_pin(struct drm_i915_private *dev_priv, enum port port)  { -	const struct ddi_vbt_port_info *info = -		&dev_priv->vbt.ddi_port_info[port];  	u8 ddc_pin; -	if (info->alternate_ddc_pin) { -		DRM_DEBUG_KMS("Using DDC pin 0x%x for port %c (VBT)\n", -			      info->alternate_ddc_pin, port_name(port)); -		return info->alternate_ddc_pin; +	switch (port) { +	case PORT_B: +		ddc_pin = GMBUS_PIN_DPB; +		break; +	case PORT_C: +		ddc_pin = GMBUS_PIN_DPC; +		break; +	case PORT_D: +		ddc_pin = GMBUS_PIN_DPD_CHV; +		break; +	default: +		MISSING_CASE(port); +		ddc_pin = GMBUS_PIN_DPB; +		break;  	} +	return ddc_pin; +} + +static u8 bxt_port_to_ddc_pin(struct drm_i915_private *dev_priv, enum port port) +{ +	u8 ddc_pin;  	switch (port) {  	case PORT_B: -		if (IS_GEN9_LP(dev_priv) || HAS_PCH_CNP(dev_priv)) -			ddc_pin = GMBUS_PIN_1_BXT; -		else -			ddc_pin = GMBUS_PIN_DPB; +		ddc_pin = GMBUS_PIN_1_BXT;  		break;  	case PORT_C: -		if (IS_GEN9_LP(dev_priv) || HAS_PCH_CNP(dev_priv)) -			ddc_pin = GMBUS_PIN_2_BXT; -		else -			ddc_pin = GMBUS_PIN_DPC; +		ddc_pin = GMBUS_PIN_2_BXT; +		break; +	default: +		MISSING_CASE(port); +		ddc_pin = GMBUS_PIN_1_BXT; +		break; +	} +	return ddc_pin; +} + +static u8 cnp_port_to_ddc_pin(struct drm_i915_private *dev_priv, +			      enum port port) +{ +	u8 ddc_pin; + +	switch (port) { +	case PORT_B: +		ddc_pin = GMBUS_PIN_1_BXT; +		break; +	case PORT_C: +		ddc_pin = GMBUS_PIN_2_BXT;  		break;  	case PORT_D: -		if (HAS_PCH_CNP(dev_priv)) -			ddc_pin = GMBUS_PIN_4_CNP; -		else if (IS_CHERRYVIEW(dev_priv)) -			ddc_pin = GMBUS_PIN_DPD_CHV; -		else -			ddc_pin = GMBUS_PIN_DPD; +		ddc_pin = GMBUS_PIN_4_CNP; +		break; +	default: +		MISSING_CASE(port); +		ddc_pin = GMBUS_PIN_1_BXT; +		break; +	} +	return ddc_pin; +} + +static u8 g4x_port_to_ddc_pin(struct drm_i915_private *dev_priv, +			      enum port port) +{ +	u8 ddc_pin; + +	switch (port) { +	case PORT_B: +		ddc_pin = GMBUS_PIN_DPB; +		break; +	case PORT_C: +		ddc_pin = GMBUS_PIN_DPC; +		break; +	case PORT_D: +		ddc_pin = GMBUS_PIN_DPD;  		break;  	default:  		MISSING_CASE(port);  		ddc_pin = GMBUS_PIN_DPB;  		break;  	} +	return ddc_pin; +} + +static u8 intel_hdmi_ddc_pin(struct drm_i915_private *dev_priv, +			     enum port port) +{ +	const struct ddi_vbt_port_info *info = +		&dev_priv->vbt.ddi_port_info[port]; +	u8 ddc_pin; + +	if (info->alternate_ddc_pin) { +		DRM_DEBUG_KMS("Using DDC pin 0x%x for port %c (VBT)\n", +			      info->alternate_ddc_pin, port_name(port)); +		return info->alternate_ddc_pin; +	} + +	if (IS_CHERRYVIEW(dev_priv)) +		ddc_pin = chv_port_to_ddc_pin(dev_priv, port); +	else if (IS_GEN9_LP(dev_priv)) +		ddc_pin = bxt_port_to_ddc_pin(dev_priv, port); +	else if (HAS_PCH_CNP(dev_priv)) +		ddc_pin = cnp_port_to_ddc_pin(dev_priv, port); +	else +		ddc_pin = g4x_port_to_ddc_pin(dev_priv, port);  	DRM_DEBUG_KMS("Using DDC pin 0x%x for port %c (platform default)\n",  		      ddc_pin, port_name(port)); @@ -1859,25 +1984,14 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,  	connector->doublescan_allowed = 0;  	connector->stereo_allowed = 1; +	if (IS_GEMINILAKE(dev_priv)) +		connector->ycbcr_420_allowed = true; +  	intel_hdmi->ddc_bus = intel_hdmi_ddc_pin(dev_priv, port); -	switch (port) { -	case PORT_B: -		intel_encoder->hpd_pin = HPD_PORT_B; -		break; -	case PORT_C: -		intel_encoder->hpd_pin = HPD_PORT_C; -		break; -	case PORT_D: -		intel_encoder->hpd_pin = HPD_PORT_D; -		break; -	case PORT_E: -		intel_encoder->hpd_pin = HPD_PORT_E; -		break; -	default: -		MISSING_CASE(port); +	if (WARN_ON(port == PORT_A))  		return; -	} +	intel_encoder->hpd_pin = intel_hpd_pin(port);  	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {  		intel_hdmi->write_infoframe = vlv_write_infoframe; |