summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/drm_edid.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_edid.c')
-rw-r--r--drivers/gpu/drm/drm_edid.c128
1 files changed, 94 insertions, 34 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 16fb76ba6509..4f751a9d71a3 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -165,6 +165,24 @@ static const struct edid_quirk {
/* HTC Vive VR Headset */
{ "HVR", 0xaa01, EDID_QUIRK_NON_DESKTOP },
+
+ /* Oculus Rift DK1, DK2, and CV1 VR Headsets */
+ { "OVR", 0x0001, EDID_QUIRK_NON_DESKTOP },
+ { "OVR", 0x0003, EDID_QUIRK_NON_DESKTOP },
+ { "OVR", 0x0004, EDID_QUIRK_NON_DESKTOP },
+
+ /* Windows Mixed Reality Headsets */
+ { "ACR", 0x7fce, EDID_QUIRK_NON_DESKTOP },
+ { "HPN", 0x3515, EDID_QUIRK_NON_DESKTOP },
+ { "LEN", 0x0408, EDID_QUIRK_NON_DESKTOP },
+ { "LEN", 0xb800, EDID_QUIRK_NON_DESKTOP },
+ { "FUJ", 0x1970, EDID_QUIRK_NON_DESKTOP },
+ { "DEL", 0x7fce, EDID_QUIRK_NON_DESKTOP },
+ { "SEC", 0x144a, EDID_QUIRK_NON_DESKTOP },
+ { "AUS", 0xc102, EDID_QUIRK_NON_DESKTOP },
+
+ /* Sony PlayStation VR Headset */
+ { "SNY", 0x0704, EDID_QUIRK_NON_DESKTOP },
};
/*
@@ -3401,6 +3419,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;
@@ -3528,6 +3547,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;
}
@@ -3764,8 +3785,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;
@@ -3837,16 +3858,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;
@@ -3855,14 +3887,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;
@@ -3873,17 +3898,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;
@@ -3904,14 +3930,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 */
@@ -3923,7 +3949,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);
@@ -3931,7 +3963,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
@@ -4241,6 +4272,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)
@@ -4416,6 +4449,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;
}
@@ -4436,6 +4470,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);
@@ -4637,8 +4672,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.
*/
@@ -4648,14 +4683,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
@@ -4853,6 +4892,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,
@@ -4931,6 +4975,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
@@ -4941,8 +4986,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;
@@ -4950,11 +5002,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;
@@ -4963,10 +5025,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;
}