diff options
Diffstat (limited to 'drivers/gpu/drm/drm_edid.c')
| -rw-r--r-- | drivers/gpu/drm/drm_edid.c | 110 | 
1 files changed, 76 insertions, 34 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index cb487148359a..ddd537914575 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3398,6 +3398,7 @@ static int  do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len,  		   const u8 *video_db, u8 video_len)  { +	struct drm_display_info *info = &connector->display_info;  	int modes = 0, offset = 0, i, multi_present = 0, multi_len;  	u8 vic_len, hdmi_3d_len = 0;  	u16 mask; @@ -3525,6 +3526,8 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len,  	}  out: +	if (modes > 0) +		info->has_hdmi_infoframe = true;  	return modes;  } @@ -3761,8 +3764,8 @@ drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db)  {  	u8 len = cea_db_payload_len(db); -	if (len >= 6) -		connector->eld[5] |= (db[6] >> 7) << 1;  /* Supports_AI */ +	if (len >= 6 && (db[6] & (1 << 7))) +		connector->eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_SUPPORTS_AI;  	if (len >= 8) {  		connector->latency_present[0] = db[8] >> 7;  		connector->latency_present[1] = (db[8] >> 6) & 1; @@ -3834,16 +3837,27 @@ void drm_edid_get_monitor_name(struct edid *edid, char *name, int bufsize)  }  EXPORT_SYMBOL(drm_edid_get_monitor_name); -/** +static void clear_eld(struct drm_connector *connector) +{ +	memset(connector->eld, 0, sizeof(connector->eld)); + +	connector->latency_present[0] = false; +	connector->latency_present[1] = false; +	connector->video_latency[0] = 0; +	connector->audio_latency[0] = 0; +	connector->video_latency[1] = 0; +	connector->audio_latency[1] = 0; +} + +/*   * drm_edid_to_eld - build ELD from EDID   * @connector: connector corresponding to the HDMI/DP sink   * @edid: EDID to parse   *   * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The - * Conn_Type, HDCP and Port_ID ELD fields are left for the graphics driver to - * fill in. + * HDCP and Port_ID ELD fields are left for the graphics driver to fill in.   */ -void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) +static void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)  {  	uint8_t *eld = connector->eld;  	u8 *cea; @@ -3852,14 +3866,7 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)  	int mnl;  	int dbl; -	memset(eld, 0, sizeof(connector->eld)); - -	connector->latency_present[0] = false; -	connector->latency_present[1] = false; -	connector->video_latency[0] = 0; -	connector->audio_latency[0] = 0; -	connector->video_latency[1] = 0; -	connector->audio_latency[1] = 0; +	clear_eld(connector);  	if (!edid)  		return; @@ -3870,17 +3877,18 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)  		return;  	} -	mnl = get_monitor_name(edid, eld + 20); +	mnl = get_monitor_name(edid, &eld[DRM_ELD_MONITOR_NAME_STRING]); +	DRM_DEBUG_KMS("ELD monitor %s\n", &eld[DRM_ELD_MONITOR_NAME_STRING]); -	eld[4] = (cea[1] << 5) | mnl; -	DRM_DEBUG_KMS("ELD monitor %s\n", eld + 20); +	eld[DRM_ELD_CEA_EDID_VER_MNL] = cea[1] << DRM_ELD_CEA_EDID_VER_SHIFT; +	eld[DRM_ELD_CEA_EDID_VER_MNL] |= mnl; -	eld[0] = 2 << 3;		/* ELD version: 2 */ +	eld[DRM_ELD_VER] = DRM_ELD_VER_CEA861D; -	eld[16] = edid->mfg_id[0]; -	eld[17] = edid->mfg_id[1]; -	eld[18] = edid->prod_code[0]; -	eld[19] = edid->prod_code[1]; +	eld[DRM_ELD_MANUFACTURER_NAME0] = edid->mfg_id[0]; +	eld[DRM_ELD_MANUFACTURER_NAME1] = edid->mfg_id[1]; +	eld[DRM_ELD_PRODUCT_CODE0] = edid->prod_code[0]; +	eld[DRM_ELD_PRODUCT_CODE1] = edid->prod_code[1];  	if (cea_revision(cea) >= 3) {  		int i, start, end; @@ -3901,14 +3909,14 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)  				/* Audio Data Block, contains SADs */  				sad_count = min(dbl / 3, 15 - total_sad_count);  				if (sad_count >= 1) -					memcpy(eld + 20 + mnl + total_sad_count * 3, +					memcpy(&eld[DRM_ELD_CEA_SAD(mnl, total_sad_count)],  					       &db[1], sad_count * 3);  				total_sad_count += sad_count;  				break;  			case SPEAKER_BLOCK:  				/* Speaker Allocation Data Block */  				if (dbl >= 1) -					eld[7] = db[1]; +					eld[DRM_ELD_SPEAKER] = db[1];  				break;  			case VENDOR_BLOCK:  				/* HDMI Vendor-Specific Data Block */ @@ -3920,7 +3928,13 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)  			}  		}  	} -	eld[5] |= total_sad_count << 4; +	eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= total_sad_count << DRM_ELD_SAD_COUNT_SHIFT; + +	if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort || +	    connector->connector_type == DRM_MODE_CONNECTOR_eDP) +		eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_DP; +	else +		eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_HDMI;  	eld[DRM_ELD_BASELINE_ELD_LEN] =  		DIV_ROUND_UP(drm_eld_calc_baseline_block_size(eld), 4); @@ -3928,7 +3942,6 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)  	DRM_DEBUG_KMS("ELD size %d, SAD count %d\n",  		      drm_eld_size(eld), total_sad_count);  } -EXPORT_SYMBOL(drm_edid_to_eld);  /**   * drm_edid_to_sad - extracts SADs from EDID @@ -4238,6 +4251,8 @@ static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,  	struct drm_display_info *display = &connector->display_info;  	struct drm_hdmi_info *hdmi = &display->hdmi; +	display->has_hdmi_infoframe = true; +  	if (hf_vsdb[6] & 0x80) {  		hdmi->scdc.supported = true;  		if (hf_vsdb[6] & 0x40) @@ -4413,6 +4428,7 @@ drm_reset_display_info(struct drm_connector *connector)  	info->cea_rev = 0;  	info->max_tmds_clock = 0;  	info->dvi_dual = false; +	info->has_hdmi_infoframe = false;  	info->non_desktop = 0;  } @@ -4433,6 +4449,7 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi  	info->cea_rev = 0;  	info->max_tmds_clock = 0;  	info->dvi_dual = false; +	info->has_hdmi_infoframe = false;  	info->non_desktop = !!(quirks & EDID_QUIRK_NON_DESKTOP); @@ -4634,8 +4651,8 @@ static int add_displayid_detailed_modes(struct drm_connector *connector,   * @edid: EDID data   *   * Add the specified modes to the connector's mode list. Also fills out the - * &drm_display_info structure in @connector with any information which can be - * derived from the edid. + * &drm_display_info structure and ELD in @connector with any information which + * can be derived from the edid.   *   * Return: The number of modes added or 0 if we couldn't find any.   */ @@ -4645,14 +4662,18 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)  	u32 quirks;  	if (edid == NULL) { +		clear_eld(connector);  		return 0;  	}  	if (!drm_edid_is_valid(edid)) { +		clear_eld(connector);  		dev_warn(connector->dev->dev, "%s: EDID invalid.\n",  			 connector->name);  		return 0;  	} +	drm_edid_to_eld(connector, edid); +  	/*  	 * CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks.  	 * To avoid multiple parsing of same block, lets parse that map @@ -4850,6 +4871,11 @@ EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode);   * @mode: DRM display mode   * @rgb_quant_range: RGB quantization range (Q)   * @rgb_quant_range_selectable: Sink support selectable RGB quantization range (QS) + * @is_hdmi2_sink: HDMI 2.0 sink, which has different default recommendations + * + * Note that @is_hdmi2_sink can be derived by looking at the + * &drm_scdc.supported flag stored in &drm_hdmi_info.scdc, + * &drm_display_info.hdmi, which can be found in &drm_connector.display_info.   */  void  drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame, @@ -4928,6 +4954,7 @@ s3d_structure_from_display_mode(const struct drm_display_mode *mode)   * drm_hdmi_vendor_infoframe_from_display_mode() - fill an HDMI infoframe with   * data from a DRM display mode   * @frame: HDMI vendor infoframe + * @connector: the connector   * @mode: DRM display mode   *   * Note that there's is a need to send HDMI vendor infoframes only when using a @@ -4938,8 +4965,15 @@ s3d_structure_from_display_mode(const struct drm_display_mode *mode)   */  int  drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame, +					    struct drm_connector *connector,  					    const struct drm_display_mode *mode)  { +	/* +	 * FIXME: sil-sii8620 doesn't have a connector around when +	 * we need one, so we have to be prepared for a NULL connector. +	 */ +	bool has_hdmi_infoframe = connector ? +		connector->display_info.has_hdmi_infoframe : false;  	int err;  	u32 s3d_flags;  	u8 vic; @@ -4947,11 +4981,21 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,  	if (!frame || !mode)  		return -EINVAL; +	if (!has_hdmi_infoframe) +		return -EINVAL; +  	vic = drm_match_hdmi_mode(mode);  	s3d_flags = mode->flags & DRM_MODE_FLAG_3D_MASK; -	if (!vic && !s3d_flags) -		return -EINVAL; +	/* +	 * Even if it's not absolutely necessary to send the infoframe +	 * (ie.vic==0 and s3d_struct==0) we will still send it if we +	 * know that the sink can handle it. This is based on a +	 * suggestion in HDMI 2.0 Appendix F. Apparently some sinks +	 * have trouble realizing that they shuld switch from 3D to 2D +	 * mode if the source simply stops sending the infoframe when +	 * it wants to switch from 3D to 2D. +	 */  	if (vic && s3d_flags)  		return -EINVAL; @@ -4960,10 +5004,8 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,  	if (err < 0)  		return err; -	if (vic) -		frame->vic = vic; -	else -		frame->s3d_struct = s3d_structure_from_display_mode(mode); +	frame->vic = vic; +	frame->s3d_struct = s3d_structure_from_display_mode(mode);  	return 0;  }  |