summaryrefslogtreecommitdiffstats
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.c530
1 files changed, 298 insertions, 232 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 3841aba17abd..261a62e15934 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -96,7 +96,6 @@ struct detailed_mode_closure {
struct drm_connector *connector;
const struct drm_edid *drm_edid;
bool preferred;
- u32 quirks;
int modes;
};
@@ -2797,7 +2796,7 @@ u32 drm_edid_get_panel_id(struct i2c_adapter *adapter)
* the EDID then we'll just return 0.
*/
- base_block = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ base_block = kzalloc(EDID_LENGTH, GFP_KERNEL);
if (!base_block)
return 0;
@@ -2887,9 +2886,9 @@ static u32 edid_get_quirks(const struct drm_edid *drm_edid)
* Walk the mode list for connector, clearing the preferred status on existing
* modes and setting it anew for the right mode ala quirks.
*/
-static void edid_fixup_preferred(struct drm_connector *connector,
- u32 quirks)
+static void edid_fixup_preferred(struct drm_connector *connector)
{
+ const struct drm_display_info *info = &connector->display_info;
struct drm_display_mode *t, *cur_mode, *preferred_mode;
int target_refresh = 0;
int cur_vrefresh, preferred_vrefresh;
@@ -2897,9 +2896,9 @@ static void edid_fixup_preferred(struct drm_connector *connector,
if (list_empty(&connector->probed_modes))
return;
- if (quirks & EDID_QUIRK_PREFER_LARGE_60)
+ if (info->quirks & EDID_QUIRK_PREFER_LARGE_60)
target_refresh = 60;
- if (quirks & EDID_QUIRK_PREFER_LARGE_75)
+ if (info->quirks & EDID_QUIRK_PREFER_LARGE_75)
target_refresh = 75;
preferred_mode = list_first_entry(&connector->probed_modes,
@@ -3401,9 +3400,9 @@ drm_mode_do_interlace_quirk(struct drm_display_mode *mode,
*/
static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connector,
const struct drm_edid *drm_edid,
- const struct detailed_timing *timing,
- u32 quirks)
+ const struct detailed_timing *timing)
{
+ const struct drm_display_info *info = &connector->display_info;
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode;
const struct detailed_pixel_timing *pt = &timing->data.pixel_data;
@@ -3437,7 +3436,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connecto
return NULL;
}
- if (quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) {
+ if (info->quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) {
mode = drm_cvt_mode(dev, hactive, vactive, 60, true, false, false);
if (!mode)
return NULL;
@@ -3449,7 +3448,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connecto
if (!mode)
return NULL;
- if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH)
+ if (info->quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH)
mode->clock = 1088 * 10;
else
mode->clock = le16_to_cpu(timing->pixel_clock) * 10;
@@ -3472,7 +3471,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connecto
drm_mode_do_interlace_quirk(mode, pt);
- if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
+ if (info->quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC;
} else {
mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ?
@@ -3485,12 +3484,12 @@ set_size:
mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4;
mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8;
- if (quirks & EDID_QUIRK_DETAILED_IN_CM) {
+ if (info->quirks & EDID_QUIRK_DETAILED_IN_CM) {
mode->width_mm *= 10;
mode->height_mm *= 10;
}
- if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
+ if (info->quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
mode->width_mm = drm_edid->edid->width_cm * 10;
mode->height_mm = drm_edid->edid->height_cm * 10;
}
@@ -4003,8 +4002,7 @@ do_detailed_mode(const struct detailed_timing *timing, void *c)
return;
newmode = drm_mode_detailed(closure->connector,
- closure->drm_edid, timing,
- closure->quirks);
+ closure->drm_edid, timing);
if (!newmode)
return;
@@ -4027,15 +4025,13 @@ do_detailed_mode(const struct detailed_timing *timing, void *c)
* add_detailed_modes - Add modes from detailed timings
* @connector: attached connector
* @drm_edid: EDID block to scan
- * @quirks: quirks to apply
*/
static int add_detailed_modes(struct drm_connector *connector,
- const struct drm_edid *drm_edid, u32 quirks)
+ const struct drm_edid *drm_edid)
{
struct detailed_mode_closure closure = {
.connector = connector,
.drm_edid = drm_edid,
- .quirks = quirks,
};
if (drm_edid->edid->revision >= 4)
@@ -4468,28 +4464,20 @@ static u8 svd_to_vic(u8 svd)
return svd;
}
+/*
+ * Return a display mode for the 0-based vic_index'th VIC across all CTA VDBs in
+ * the EDID, or NULL on errors.
+ */
static struct drm_display_mode *
-drm_display_mode_from_vic_index(struct drm_connector *connector,
- const u8 *video_db, u8 video_len,
- u8 video_index)
+drm_display_mode_from_vic_index(struct drm_connector *connector, int vic_index)
{
+ const struct drm_display_info *info = &connector->display_info;
struct drm_device *dev = connector->dev;
- struct drm_display_mode *newmode;
- u8 vic;
-
- if (video_db == NULL || video_index >= video_len)
- return NULL;
- /* CEA modes are numbered 1..127 */
- vic = svd_to_vic(video_db[video_index]);
- if (!drm_valid_cea_vic(vic))
- return NULL;
-
- newmode = drm_mode_duplicate(dev, cea_mode_for_vic(vic));
- if (!newmode)
+ if (!info->vics || vic_index >= info->vics_len || !info->vics[vic_index])
return NULL;
- return newmode;
+ return drm_display_mode_from_cea_vic(dev, info->vics[vic_index]);
}
/*
@@ -4505,10 +4493,8 @@ drm_display_mode_from_vic_index(struct drm_connector *connector,
static int do_y420vdb_modes(struct drm_connector *connector,
const u8 *svds, u8 svds_len)
{
- int modes = 0, i;
struct drm_device *dev = connector->dev;
- struct drm_display_info *info = &connector->display_info;
- struct drm_hdmi_info *hdmi = &info->hdmi;
+ int modes = 0, i;
for (i = 0; i < svds_len; i++) {
u8 vic = svd_to_vic(svds[i]);
@@ -4520,35 +4506,13 @@ static int do_y420vdb_modes(struct drm_connector *connector,
newmode = drm_mode_duplicate(dev, cea_mode_for_vic(vic));
if (!newmode)
break;
- bitmap_set(hdmi->y420_vdb_modes, vic, 1);
drm_mode_probed_add(connector, newmode);
modes++;
}
- if (modes > 0)
- info->color_formats |= DRM_COLOR_FORMAT_YCBCR420;
return modes;
}
-/*
- * drm_add_cmdb_modes - Add a YCBCR 420 mode into bitmap
- * @connector: connector corresponding to the HDMI sink
- * @vic: CEA vic for the video mode to be added in the map
- *
- * Makes an entry for a videomode in the YCBCR 420 bitmap
- */
-static void
-drm_add_cmdb_modes(struct drm_connector *connector, u8 svd)
-{
- u8 vic = svd_to_vic(svd);
- struct drm_hdmi_info *hdmi = &connector->display_info.hdmi;
-
- if (!drm_valid_cea_vic(vic))
- return;
-
- bitmap_set(hdmi->y420_cmdb_modes, vic, 1);
-}
-
/**
* drm_display_mode_from_cea_vic() - return a mode for CEA VIC
* @dev: DRM device
@@ -4577,29 +4541,20 @@ drm_display_mode_from_cea_vic(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_display_mode_from_cea_vic);
-static int
-do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len)
+/* Add modes based on VICs parsed in parse_cta_vdb() */
+static int add_cta_vdb_modes(struct drm_connector *connector)
{
+ const struct drm_display_info *info = &connector->display_info;
int i, modes = 0;
- struct drm_hdmi_info *hdmi = &connector->display_info.hdmi;
- for (i = 0; i < len; i++) {
+ if (!info->vics)
+ return 0;
+
+ for (i = 0; i < info->vics_len; i++) {
struct drm_display_mode *mode;
- mode = drm_display_mode_from_vic_index(connector, db, len, i);
+ mode = drm_display_mode_from_vic_index(connector, i);
if (mode) {
- /*
- * YCBCR420 capability block contains a bitmap which
- * gives the index of CEA modes from CEA VDB, which
- * can support YCBCR 420 sampling output also (apart
- * from RGB/YCBCR444 etc).
- * For example, if the bit 0 in bitmap is set,
- * first mode in VDB can support YCBCR420 output too.
- * Add YCBCR420 modes only if sink is HDMI 2.0 capable.
- */
- if (i < 64 && hdmi->y420_cmdb_map & (1ULL << i))
- drm_add_cmdb_modes(connector, db[i]);
-
drm_mode_probed_add(connector, mode);
modes++;
}
@@ -4693,15 +4648,13 @@ static int add_hdmi_mode(struct drm_connector *connector, u8 vic)
}
static int add_3d_struct_modes(struct drm_connector *connector, u16 structure,
- const u8 *video_db, u8 video_len, u8 video_index)
+ int vic_index)
{
struct drm_display_mode *newmode;
int modes = 0;
if (structure & (1 << 0)) {
- newmode = drm_display_mode_from_vic_index(connector, video_db,
- video_len,
- video_index);
+ newmode = drm_display_mode_from_vic_index(connector, vic_index);
if (newmode) {
newmode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING;
drm_mode_probed_add(connector, newmode);
@@ -4709,9 +4662,7 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure,
}
}
if (structure & (1 << 6)) {
- newmode = drm_display_mode_from_vic_index(connector, video_db,
- video_len,
- video_index);
+ newmode = drm_display_mode_from_vic_index(connector, vic_index);
if (newmode) {
newmode->flags |= DRM_MODE_FLAG_3D_TOP_AND_BOTTOM;
drm_mode_probed_add(connector, newmode);
@@ -4719,9 +4670,7 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure,
}
}
if (structure & (1 << 8)) {
- newmode = drm_display_mode_from_vic_index(connector, video_db,
- video_len,
- video_index);
+ newmode = drm_display_mode_from_vic_index(connector, vic_index);
if (newmode) {
newmode->flags |= DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF;
drm_mode_probed_add(connector, newmode);
@@ -4732,6 +4681,26 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure,
return modes;
}
+static bool hdmi_vsdb_latency_present(const u8 *db)
+{
+ return db[8] & BIT(7);
+}
+
+static bool hdmi_vsdb_i_latency_present(const u8 *db)
+{
+ return hdmi_vsdb_latency_present(db) && db[8] & BIT(6);
+}
+
+static int hdmi_vsdb_latency_length(const u8 *db)
+{
+ if (hdmi_vsdb_i_latency_present(db))
+ return 4;
+ else if (hdmi_vsdb_latency_present(db))
+ return 2;
+ else
+ return 0;
+}
+
/*
* do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block
* @connector: connector corresponding to the HDMI sink
@@ -4742,10 +4711,8 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure,
* also adds the stereo 3d modes when applicable.
*/
static int
-do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len,
- const u8 *video_db, u8 video_len)
+do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 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;
@@ -4758,13 +4725,7 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len,
if (!(db[8] & (1 << 5)))
goto out;
- /* Latency_Fields_Present */
- if (db[8] & (1 << 7))
- offset += 2;
-
- /* I_Latency_Fields_Present */
- if (db[8] & (1 << 6))
- offset += 2;
+ offset += hdmi_vsdb_latency_length(db);
/* the declared length is not long enough for the 2 first bytes
* of additional video format capabilities */
@@ -4818,9 +4779,7 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len,
for (i = 0; i < 16; i++) {
if (mask & (1 << i))
modes += add_3d_struct_modes(connector,
- structure_all,
- video_db,
- video_len, i);
+ structure_all, i);
}
}
@@ -4857,8 +4816,6 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len,
if (newflag != 0) {
newmode = drm_display_mode_from_vic_index(connector,
- video_db,
- video_len,
vic_index);
if (newmode) {
@@ -4873,8 +4830,6 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len,
}
out:
- if (modes > 0)
- info->has_hdmi_infoframe = true;
return modes;
}
@@ -5204,20 +5159,26 @@ static int edid_hfeeodb_extension_block_count(const struct edid *edid)
return cta[4 + 2];
}
-static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector,
- const u8 *db)
+/*
+ * CTA-861 YCbCr 4:2:0 Capability Map Data Block (CTA Y420CMDB)
+ *
+ * Y420CMDB contains a bitmap which gives the index of CTA modes from CTA VDB,
+ * which can support YCBCR 420 sampling output also (apart from RGB/YCBCR444
+ * etc). For example, if the bit 0 in bitmap is set, first mode in VDB can
+ * support YCBCR420 output too.
+ */
+static void parse_cta_y420cmdb(struct drm_connector *connector,
+ const struct cea_db *db, u64 *y420cmdb_map)
{
struct drm_display_info *info = &connector->display_info;
- struct drm_hdmi_info *hdmi = &info->hdmi;
- u8 map_len = cea_db_payload_len(db) - 1;
- u8 count;
+ int i, map_len = cea_db_payload_len(db) - 1;
+ const u8 *data = cea_db_data(db) + 1;
u64 map = 0;
if (map_len == 0) {
/* All CEA modes support ycbcr420 sampling also.*/
- hdmi->y420_cmdb_map = U64_MAX;
- info->color_formats |= DRM_COLOR_FORMAT_YCBCR420;
- return;
+ map = U64_MAX;
+ goto out;
}
/*
@@ -5235,13 +5196,14 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector,
if (WARN_ON_ONCE(map_len > 8))
map_len = 8;
- for (count = 0; count < map_len; count++)
- map |= (u64)db[2 + count] << (8 * count);
+ for (i = 0; i < map_len; i++)
+ map |= (u64)data[i] << (8 * i);
+out:
if (map)
info->color_formats |= DRM_COLOR_FORMAT_YCBCR420;
- hdmi->y420_cmdb_map = map;
+ *y420cmdb_map = map;
}
static int add_cea_modes(struct drm_connector *connector,
@@ -5249,21 +5211,16 @@ static int add_cea_modes(struct drm_connector *connector,
{
const struct cea_db *db;
struct cea_db_iter iter;
- int modes = 0;
+ int modes;
+
+ /* CTA VDB block VICs parsed earlier */
+ modes = add_cta_vdb_modes(connector);
cea_db_iter_edid_begin(drm_edid, &iter);
cea_db_iter_for_each(db, &iter) {
- const u8 *hdmi = NULL, *video = NULL;
- u8 hdmi_len = 0, video_len = 0;
-
- if (cea_db_tag(db) == CTA_DB_VIDEO) {
- video = cea_db_data(db);
- video_len = cea_db_payload_len(db);
- modes += do_cea_modes(connector, video, video_len);
- } else if (cea_db_is_hdmi_vsdb(db)) {
- /* FIXME: Switch to use cea_db_data() */
- hdmi = (const u8 *)db;
- hdmi_len = cea_db_payload_len(db);
+ if (cea_db_is_hdmi_vsdb(db)) {
+ modes += do_hdmi_vsdb_modes(connector, (const u8 *)db,
+ cea_db_payload_len(db));
} else if (cea_db_is_y420vdb(db)) {
const u8 *vdb420 = cea_db_data(db) + 1;
@@ -5271,15 +5228,6 @@ static int add_cea_modes(struct drm_connector *connector,
modes += do_y420vdb_modes(connector, vdb420,
cea_db_payload_len(db) - 1);
}
-
- /*
- * We parse the HDMI VSDB after having added the cea modes as we
- * will be patching their flags when the sink supports stereo
- * 3D.
- */
- if (hdmi)
- modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len,
- video, video_len);
}
cea_db_iter_end(&iter);
@@ -5416,6 +5364,7 @@ drm_parse_hdr_metadata_block(struct drm_connector *connector, const u8 *db)
}
}
+/* HDMI Vendor-Specific Data Block (HDMI VSDB, H14b-VSDB) */
static void
drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db)
{
@@ -5423,18 +5372,18 @@ drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db)
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;
- }
- if (len >= 9)
+
+ if (len >= 10 && hdmi_vsdb_latency_present(db)) {
+ connector->latency_present[0] = true;
connector->video_latency[0] = db[9];
- if (len >= 10)
connector->audio_latency[0] = db[10];
- if (len >= 11)
+ }
+
+ if (len >= 12 && hdmi_vsdb_i_latency_present(db)) {
+ connector->latency_present[1] = true;
connector->video_latency[1] = db[11];
- if (len >= 12)
connector->audio_latency[1] = db[12];
+ }
drm_dbg_kms(connector->dev,
"[CONNECTOR:%d:%s] HDMI: latency present %d %d, video latency %d %d, audio latency %d %d\n",
@@ -5533,8 +5482,6 @@ static void drm_edid_to_eld(struct drm_connector *connector,
int total_sad_count = 0;
int mnl;
- clear_eld(connector);
-
if (!drm_edid)
return;
@@ -5864,6 +5811,92 @@ drm_default_rgb_quant_range(const struct drm_display_mode *mode)
}
EXPORT_SYMBOL(drm_default_rgb_quant_range);
+/* CTA-861 Video Data Block (CTA VDB) */
+static void parse_cta_vdb(struct drm_connector *connector, const struct cea_db *db)
+{
+ struct drm_display_info *info = &connector->display_info;
+ int i, vic_index, len = cea_db_payload_len(db);
+ const u8 *svds = cea_db_data(db);
+ u8 *vics;
+
+ if (!len)
+ return;
+
+ /* Gracefully handle multiple VDBs, however unlikely that is */
+ vics = krealloc(info->vics, info->vics_len + len, GFP_KERNEL);
+ if (!vics)
+ return;
+
+ vic_index = info->vics_len;
+ info->vics_len += len;
+ info->vics = vics;
+
+ for (i = 0; i < len; i++) {
+ u8 vic = svd_to_vic(svds[i]);
+
+ if (!drm_valid_cea_vic(vic))
+ vic = 0;
+
+ info->vics[vic_index++] = vic;
+ }
+}
+
+/*
+ * Update y420_cmdb_modes based on previously parsed CTA VDB and Y420CMDB.
+ *
+ * Translate the y420cmdb_map based on VIC indexes to y420_cmdb_modes indexed
+ * using the VICs themselves.
+ */
+static void update_cta_y420cmdb(struct drm_connector *connector, u64 y420cmdb_map)
+{
+ struct drm_display_info *info = &connector->display_info;
+ struct drm_hdmi_info *hdmi = &info->hdmi;
+ int i, len = min_t(int, info->vics_len, BITS_PER_TYPE(y420cmdb_map));
+
+ for (i = 0; i < len; i++) {
+ u8 vic = info->vics[i];
+
+ if (vic && y420cmdb_map & BIT_ULL(i))
+ bitmap_set(hdmi->y420_cmdb_modes, vic, 1);
+ }
+}
+
+static bool cta_vdb_has_vic(const struct drm_connector *connector, u8 vic)
+{
+ const struct drm_display_info *info = &connector->display_info;
+ int i;
+
+ if (!vic || !info->vics)
+ return false;
+
+ for (i = 0; i < info->vics_len; i++) {
+ if (info->vics[i] == vic)
+ return true;
+ }
+
+ return false;
+}
+
+/* CTA-861-H YCbCr 4:2:0 Video Data Block (CTA Y420VDB) */
+static void parse_cta_y420vdb(struct drm_connector *connector,
+ const struct cea_db *db)
+{
+ struct drm_display_info *info = &connector->display_info;
+ struct drm_hdmi_info *hdmi = &info->hdmi;
+ const u8 *svds = cea_db_data(db) + 1;
+ int i;
+
+ for (i = 0; i < cea_db_payload_len(db) - 1; i++) {
+ u8 vic = svd_to_vic(svds[i]);
+
+ if (!drm_valid_cea_vic(vic))
+ continue;
+
+ bitmap_set(hdmi->y420_vdb_modes, vic, 1);
+ info->color_formats |= DRM_COLOR_FORMAT_YCBCR420;
+ }
+}
+
static void drm_parse_vcdb(struct drm_connector *connector, const u8 *db)
{
struct drm_display_info *info = &connector->display_info;
@@ -5995,14 +6028,14 @@ static void drm_parse_dsc_info(struct drm_hdmi_dsc_cap *hdmi_dsc,
static void drm_parse_hdmi_forum_scds(struct drm_connector *connector,
const u8 *hf_scds)
{
- struct drm_display_info *display = &connector->display_info;
- struct drm_hdmi_info *hdmi = &display->hdmi;
+ struct drm_display_info *info = &connector->display_info;
+ struct drm_hdmi_info *hdmi = &info->hdmi;
struct drm_hdmi_dsc_cap *hdmi_dsc = &hdmi->dsc_cap;
int max_tmds_clock = 0;
u8 max_frl_rate = 0;
bool dsc_support = false;
- display->has_hdmi_infoframe = true;
+ info->has_hdmi_infoframe = true;
if (hf_scds[6] & 0x80) {
hdmi->scdc.supported = true;
@@ -6026,7 +6059,7 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector,
max_tmds_clock = hf_scds[5] * 5000;
if (max_tmds_clock > 340000) {
- display->max_tmds_clock = max_tmds_clock;
+ info->max_tmds_clock = max_tmds_clock;
}
if (scdc->supported) {
@@ -6117,6 +6150,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
}
}
+/* HDMI Vendor-Specific Data Block (HDMI VSDB, H14b-VSDB) */
static void
drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db)
{
@@ -6130,6 +6164,15 @@ drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db)
if (len >= 7)
info->max_tmds_clock = db[7] * 5000;
+ /*
+ * Try to infer whether the sink supports HDMI infoframes.
+ *
+ * HDMI infoframe support was first added in HDMI 1.4. Assume the sink
+ * supports infoframes if HDMI_Video_present is set.
+ */
+ if (len >= 8 && db[8] & BIT(5))
+ info->has_hdmi_infoframe = true;
+
drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI: DVI dual %d, max TMDS clock %d kHz\n",
connector->base.id, connector->name,
info->dvi_dual, info->max_tmds_clock);
@@ -6165,6 +6208,7 @@ static void drm_parse_cea_ext(struct drm_connector *connector,
const struct cea_db *db;
struct cea_db_iter iter;
const u8 *edid_ext;
+ u64 y420cmdb_map = 0;
drm_edid_iter_begin(drm_edid, &edid_iter);
drm_edid_iter_for_each(edid_ext, &edid_iter) {
@@ -6202,13 +6246,20 @@ static void drm_parse_cea_ext(struct drm_connector *connector,
else if (cea_db_is_microsoft_vsdb(db))
drm_parse_microsoft_vsdb(connector, data);
else if (cea_db_is_y420cmdb(db))
- drm_parse_y420cmdb_bitmap(connector, data);
+ parse_cta_y420cmdb(connector, db, &y420cmdb_map);
+ else if (cea_db_is_y420vdb(db))
+ parse_cta_y420vdb(connector, db);
else if (cea_db_is_vcdb(db))
drm_parse_vcdb(connector, data);
else if (cea_db_is_hdmi_hdr_metadata_block(db))
drm_parse_hdr_metadata_block(connector, data);
+ else if (cea_db_tag(db) == CTA_DB_VIDEO)
+ parse_cta_vdb(connector, db);
}
cea_db_iter_end(&iter);
+
+ if (y420cmdb_map)
+ update_cta_y420cmdb(connector, y420cmdb_map);
}
static
@@ -6374,17 +6425,29 @@ static void drm_reset_display_info(struct drm_connector *connector)
info->mso_stream_count = 0;
info->mso_pixel_overlap = 0;
info->max_dsc_bpp = 0;
+
+ kfree(info->vics);
+ info->vics = NULL;
+ info->vics_len = 0;
+
+ info->quirks = 0;
}
-static u32 update_display_info(struct drm_connector *connector,
- const struct drm_edid *drm_edid)
+static void update_display_info(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
struct drm_display_info *info = &connector->display_info;
- const struct edid *edid = drm_edid->edid;
-
- u32 quirks = edid_get_quirks(drm_edid);
+ const struct edid *edid;
drm_reset_display_info(connector);
+ clear_eld(connector);
+
+ if (!drm_edid)
+ return;
+
+ edid = drm_edid->edid;
+
+ info->quirks = edid_get_quirks(drm_edid);
info->width_mm = edid->width_cm * 10;
info->height_mm = edid->height_cm * 10;
@@ -6456,17 +6519,30 @@ static u32 update_display_info(struct drm_connector *connector,
drm_update_mso(connector, drm_edid);
out:
- if (quirks & EDID_QUIRK_NON_DESKTOP) {
+ if (info->quirks & EDID_QUIRK_NON_DESKTOP) {
drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Non-desktop display%s\n",
connector->base.id, connector->name,
info->non_desktop ? " (redundant quirk)" : "");
info->non_desktop = true;
}
- if (quirks & EDID_QUIRK_CAP_DSC_15BPP)
+ if (info->quirks & EDID_QUIRK_CAP_DSC_15BPP)
info->max_dsc_bpp = 15;
- return quirks;
+ if (info->quirks & EDID_QUIRK_FORCE_6BPC)
+ info->bpc = 6;
+
+ if (info->quirks & EDID_QUIRK_FORCE_8BPC)
+ info->bpc = 8;
+
+ if (info->quirks & EDID_QUIRK_FORCE_10BPC)
+ info->bpc = 10;
+
+ if (info->quirks & EDID_QUIRK_FORCE_12BPC)
+ info->bpc = 12;
+
+ /* Depends on info->cea_rev set by drm_parse_cea_ext() above */
+ drm_edid_to_eld(connector, drm_edid);
}
static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev,
@@ -6561,27 +6637,14 @@ static int add_displayid_detailed_modes(struct drm_connector *connector,
return num_modes;
}
-static int _drm_edid_connector_update(struct drm_connector *connector,
- const struct drm_edid *drm_edid)
+static int _drm_edid_connector_add_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
+ const struct drm_display_info *info = &connector->display_info;
int num_modes = 0;
- u32 quirks;
- if (!drm_edid) {
- drm_reset_display_info(connector);
- clear_eld(connector);
+ if (!drm_edid)
return 0;
- }
-
- /*
- * CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks.
- * To avoid multiple parsing of same block, lets parse that map
- * from sink info, before parsing CEA modes.
- */
- quirks = update_display_info(connector, drm_edid);
-
- /* Depends on info->cea_rev set by update_display_info() above */
- drm_edid_to_eld(connector, drm_edid);
/*
* EDID spec says modes should be preferred in this order:
@@ -6597,7 +6660,7 @@ static int _drm_edid_connector_update(struct drm_connector *connector,
*
* XXX order for additional mode types in extension blocks?
*/
- num_modes += add_detailed_modes(connector, drm_edid, quirks);
+ num_modes += add_detailed_modes(connector, drm_edid);
num_modes += add_cvt_modes(connector, drm_edid);
num_modes += add_standard_modes(connector, drm_edid);
num_modes += add_established_modes(connector, drm_edid);
@@ -6607,20 +6670,8 @@ static int _drm_edid_connector_update(struct drm_connector *connector,
if (drm_edid->edid->features & DRM_EDID_FEATURE_CONTINUOUS_FREQ)
num_modes += add_inferred_modes(connector, drm_edid);
- if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
- edid_fixup_preferred(connector, quirks);
-
- if (quirks & EDID_QUIRK_FORCE_6BPC)
- connector->display_info.bpc = 6;
-
- if (quirks & EDID_QUIRK_FORCE_8BPC)
- connector->display_info.bpc = 8;
-
- if (quirks & EDID_QUIRK_FORCE_10BPC)
- connector->display_info.bpc = 10;
-
- if (quirks & EDID_QUIRK_FORCE_12BPC)
- connector->display_info.bpc = 12;
+ if (info->quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
+ edid_fixup_preferred(connector);
return num_modes;
}
@@ -6684,49 +6735,54 @@ out:
* @connector: Connector
* @drm_edid: EDID
*
- * Update the connector mode list, display info, ELD, HDR metadata, relevant
- * properties, etc. from the passed in EDID.
+ * Update the connector display info, ELD, HDR metadata, relevant properties,
+ * etc. from the passed in EDID.
*
* If EDID is NULL, reset the information.
*
- * Return: The number of modes added or 0 if we couldn't find any.
+ * Must be called before calling drm_edid_connector_add_modes().
+ *
+ * Return: 0 on success, negative error on errors.
*/
int drm_edid_connector_update(struct drm_connector *connector,
const struct drm_edid *drm_edid)
{
- int count;
-
- count = _drm_edid_connector_update(connector, drm_edid);
+ update_display_info(connector, drm_edid);
_drm_update_tile_info(connector, drm_edid);
- /* Note: Ignore errors for now. */
- _drm_edid_connector_property_update(connector, drm_edid);
-
- return count;
+ return _drm_edid_connector_property_update(connector, drm_edid);
}
EXPORT_SYMBOL(drm_edid_connector_update);
-static int _drm_connector_update_edid_property(struct drm_connector *connector,
- const struct drm_edid *drm_edid)
+/**
+ * drm_edid_connector_add_modes - Update probed modes from the EDID property
+ * @connector: Connector
+ *
+ * Add the modes from the previously updated EDID property to the connector
+ * probed modes list.
+ *
+ * drm_edid_connector_update() must have been called before this to update the
+ * EDID property.
+ *
+ * Return: The number of modes added, or 0 if we couldn't find any.
+ */
+int drm_edid_connector_add_modes(struct drm_connector *connector)
{
- /*
- * Set the display info, using edid if available, otherwise resetting
- * the values to defaults. This duplicates the work done in
- * drm_add_edid_modes, but that function is not consistently called
- * before this one in all drivers and the computation is cheap enough
- * that it seems better to duplicate it rather than attempt to ensure
- * some arbitrary ordering of calls.
- */
- if (drm_edid)
- update_display_info(connector, drm_edid);
- else
- drm_reset_display_info(connector);
+ const struct drm_edid *drm_edid = NULL;
+ int count;
- _drm_update_tile_info(connector, drm_edid);
+ if (connector->edid_blob_ptr)
+ drm_edid = drm_edid_alloc(connector->edid_blob_ptr->data,
+ connector->edid_blob_ptr->length);
- return _drm_edid_connector_property_update(connector, drm_edid);
+ count = _drm_edid_connector_add_modes(connector, drm_edid);
+
+ drm_edid_free(drm_edid);
+
+ return count;
}
+EXPORT_SYMBOL(drm_edid_connector_add_modes);
/**
* drm_connector_update_edid_property - update the edid property of a connector
@@ -6749,8 +6805,7 @@ int drm_connector_update_edid_property(struct drm_connector *connector,
{
struct drm_edid drm_edid;
- return _drm_connector_update_edid_property(connector,
- drm_edid_legacy_init(&drm_edid, edid));
+ return drm_edid_connector_update(connector, drm_edid_legacy_init(&drm_edid, edid));
}
EXPORT_SYMBOL(drm_connector_update_edid_property);
@@ -6763,13 +6818,14 @@ EXPORT_SYMBOL(drm_connector_update_edid_property);
* &drm_display_info structure and ELD in @connector with any information which
* can be derived from the edid.
*
- * This function is deprecated. Use drm_edid_connector_update() instead.
+ * This function is deprecated. Use drm_edid_connector_add_modes() instead.
*
* Return: The number of modes added or 0 if we couldn't find any.
*/
int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
{
- struct drm_edid drm_edid;
+ struct drm_edid _drm_edid;
+ const struct drm_edid *drm_edid;
if (edid && !drm_edid_is_valid(edid)) {
drm_warn(connector->dev, "[CONNECTOR:%d:%s] EDID invalid.\n",
@@ -6777,8 +6833,11 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
edid = NULL;
}
- return _drm_edid_connector_update(connector,
- drm_edid_legacy_init(&drm_edid, edid));
+ drm_edid = drm_edid_legacy_init(&_drm_edid, edid);
+
+ update_display_info(connector, drm_edid);
+
+ return _drm_edid_connector_add_modes(connector, drm_edid);
}
EXPORT_SYMBOL(drm_add_edid_modes);
@@ -6885,8 +6944,6 @@ static u8 drm_mode_hdmi_vic(const struct drm_connector *connector,
static u8 drm_mode_cea_vic(const struct drm_connector *connector,
const struct drm_display_mode *mode)
{
- u8 vic;
-
/*
* HDMI spec says if a mode is found in HDMI 1.4b 4K modes
* we should send its VIC in vendor infoframes, else send the
@@ -6896,14 +6953,23 @@ static u8 drm_mode_cea_vic(const struct drm_connector *connector,
if (drm_mode_hdmi_vic(connector, mode))
return 0;
- vic = drm_match_cea_mode(mode);
+ return drm_match_cea_mode(mode);
+}
- /*
- * HDMI 1.4 VIC range: 1 <= VIC <= 64 (CEA-861-D) but
- * HDMI 2.0 VIC range: 1 <= VIC <= 107 (CEA-861-F). So we
- * have to make sure we dont break HDMI 1.4 sinks.
- */
- if (!is_hdmi2_sink(connector) && vic > 64)
+/*
+ * Avoid sending VICs defined in HDMI 2.0 in AVI infoframes to sinks that
+ * conform to HDMI 1.4.
+ *
+ * HDMI 1.4 (CTA-861-D) VIC range: [1..64]
+ * HDMI 2.0 (CTA-861-F) VIC range: [1..107]
+ *
+ * If the sink lists the VIC in CTA VDB, assume it's fine, regardless of HDMI
+ * version.
+ */
+static u8 vic_for_avi_infoframe(const struct drm_connector *connector, u8 vic)
+{
+ if (!is_hdmi2_sink(connector) && vic > 64 &&
+ !cta_vdb_has_vic(connector, vic))
return 0;
return vic;
@@ -6978,7 +7044,7 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
picture_aspect = HDMI_PICTURE_ASPECT_NONE;
}
- frame->video_code = vic;
+ frame->video_code = vic_for_avi_infoframe(connector, vic);
frame->picture_aspect = picture_aspect;
frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN;