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.c1272
1 files changed, 743 insertions, 529 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index bc43e1b32092..929fc0e46751 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -45,10 +45,6 @@
#include "drm_crtc_internal.h"
-#define version_greater(edid, maj, min) \
- (((edid)->version > (maj)) || \
- ((edid)->version == (maj) && (edid)->revision > (min)))
-
static int oui(u8 first, u8 second, u8 third)
{
return (first << 16) | (second << 8) | third;
@@ -96,7 +92,7 @@ static int oui(u8 first, u8 second, u8 third)
struct detailed_mode_closure {
struct drm_connector *connector;
- const struct edid *edid;
+ const struct drm_edid *drm_edid;
bool preferred;
u32 quirks;
int modes;
@@ -1567,6 +1563,24 @@ static const struct drm_display_mode edid_4k_modes[] = {
/*** DDC fetch and block validation ***/
+/*
+ * The opaque EDID type, internal to drm_edid.c.
+ */
+struct drm_edid {
+ /* Size allocated for edid */
+ size_t size;
+ const struct edid *edid;
+};
+
+static bool version_greater(const struct drm_edid *drm_edid,
+ u8 version, u8 revision)
+{
+ const struct edid *edid = drm_edid->edid;
+
+ return edid->version > version ||
+ (edid->version == version && edid->revision > revision);
+}
+
static int edid_extension_block_count(const struct edid *edid)
{
return edid->extensions;
@@ -1599,6 +1613,72 @@ static const void *edid_extension_block_data(const struct edid *edid, int index)
return edid_block_data(edid, index + 1);
}
+/*
+ * Initializer helper for legacy interfaces, where we have no choice but to
+ * trust edid size. Not for general purpose use.
+ */
+static const struct drm_edid *drm_edid_legacy_init(struct drm_edid *drm_edid,
+ const struct edid *edid)
+{
+ if (!edid)
+ return NULL;
+
+ memset(drm_edid, 0, sizeof(*drm_edid));
+
+ drm_edid->edid = edid;
+ drm_edid->size = edid_size(edid);
+
+ return drm_edid;
+}
+
+/*
+ * EDID base and extension block iterator.
+ *
+ * struct drm_edid_iter iter;
+ * const u8 *block;
+ *
+ * drm_edid_iter_begin(drm_edid, &iter);
+ * drm_edid_iter_for_each(block, &iter) {
+ * // do stuff with block
+ * }
+ * drm_edid_iter_end(&iter);
+ */
+struct drm_edid_iter {
+ const struct drm_edid *drm_edid;
+
+ /* Current block index. */
+ int index;
+};
+
+static void drm_edid_iter_begin(const struct drm_edid *drm_edid,
+ struct drm_edid_iter *iter)
+{
+ memset(iter, 0, sizeof(*iter));
+
+ iter->drm_edid = drm_edid;
+}
+
+static const void *__drm_edid_iter_next(struct drm_edid_iter *iter)
+{
+ const void *block = NULL;
+
+ if (!iter->drm_edid)
+ return NULL;
+
+ if (iter->index < edid_block_count(iter->drm_edid->edid))
+ block = edid_block_data(iter->drm_edid->edid, iter->index++);
+
+ return block;
+}
+
+#define drm_edid_iter_for_each(__block, __iter) \
+ while (((__block) = __drm_edid_iter_next(__iter)))
+
+static void drm_edid_iter_end(struct drm_edid_iter *iter)
+{
+ memset(iter, 0, sizeof(*iter));
+}
+
static const u8 edid_header[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
};
@@ -2362,13 +2442,13 @@ EXPORT_SYMBOL(drm_edid_duplicate);
/**
* edid_get_quirks - return quirk flags for a given EDID
- * @edid: EDID to process
+ * @drm_edid: EDID to process
*
* This tells subsequent routines what fixes they need to apply.
*/
-static u32 edid_get_quirks(const struct edid *edid)
+static u32 edid_get_quirks(const struct drm_edid *drm_edid)
{
- u32 panel_id = edid_extract_panel_id(edid);
+ u32 panel_id = edid_extract_panel_id(drm_edid->edid);
const struct edid_quirk *quirk;
int i;
@@ -2523,20 +2603,21 @@ vtb_for_each_detailed_block(const u8 *ext, detailed_cb *cb, void *closure)
cb((const struct detailed_timing *)(det_base + 18 * i), closure);
}
-static void
-drm_for_each_detailed_block(const struct edid *edid, detailed_cb *cb, void *closure)
+static void drm_for_each_detailed_block(const struct drm_edid *drm_edid,
+ detailed_cb *cb, void *closure)
{
+ struct drm_edid_iter edid_iter;
+ const u8 *ext;
int i;
- if (edid == NULL)
+ if (!drm_edid)
return;
for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
- cb(&(edid->detailed_timings[i]), closure);
-
- for (i = 0; i < edid_extension_block_count(edid); i++) {
- const u8 *ext = edid_extension_block_data(edid, i);
+ cb(&drm_edid->edid->detailed_timings[i], closure);
+ drm_edid_iter_begin(drm_edid, &edid_iter);
+ drm_edid_iter_for_each(ext, &edid_iter) {
switch (*ext) {
case CEA_EXT:
cea_for_each_detailed_block(ext, cb, closure);
@@ -2548,6 +2629,7 @@ drm_for_each_detailed_block(const struct edid *edid, detailed_cb *cb, void *clos
break;
}
}
+ drm_edid_iter_end(&edid_iter);
}
static void
@@ -2568,16 +2650,16 @@ is_rb(const struct detailed_timing *descriptor, void *data)
/* EDID 1.4 defines this explicitly. For EDID 1.3, we guess, badly. */
static bool
-drm_monitor_supports_rb(const struct edid *edid)
+drm_monitor_supports_rb(const struct drm_edid *drm_edid)
{
- if (edid->revision >= 4) {
+ if (drm_edid->edid->revision >= 4) {
bool ret = false;
- drm_for_each_detailed_block(edid, is_rb, &ret);
+ drm_for_each_detailed_block(drm_edid, is_rb, &ret);
return ret;
}
- return ((edid->input & DRM_EDID_INPUT_DIGITAL) != 0);
+ return ((drm_edid->edid->input & DRM_EDID_INPUT_DIGITAL) != 0);
}
static void
@@ -2596,11 +2678,11 @@ find_gtf2(const struct detailed_timing *descriptor, void *data)
/* Secondary GTF curve kicks in above some break frequency */
static int
-drm_gtf2_hbreak(const struct edid *edid)
+drm_gtf2_hbreak(const struct drm_edid *drm_edid)
{
const struct detailed_timing *descriptor = NULL;
- drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
+ drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.hfreq_start_khz) != 12);
@@ -2608,11 +2690,11 @@ drm_gtf2_hbreak(const struct edid *edid)
}
static int
-drm_gtf2_2c(const struct edid *edid)
+drm_gtf2_2c(const struct drm_edid *drm_edid)
{
const struct detailed_timing *descriptor = NULL;
- drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
+ drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.c) != 13);
@@ -2620,11 +2702,11 @@ drm_gtf2_2c(const struct edid *edid)
}
static int
-drm_gtf2_m(const struct edid *edid)
+drm_gtf2_m(const struct drm_edid *drm_edid)
{
const struct detailed_timing *descriptor = NULL;
- drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
+ drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.m) != 14);
@@ -2632,11 +2714,11 @@ drm_gtf2_m(const struct edid *edid)
}
static int
-drm_gtf2_k(const struct edid *edid)
+drm_gtf2_k(const struct drm_edid *drm_edid)
{
const struct detailed_timing *descriptor = NULL;
- drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
+ drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.k) != 16);
@@ -2644,11 +2726,11 @@ drm_gtf2_k(const struct edid *edid)
}
static int
-drm_gtf2_2j(const struct edid *edid)
+drm_gtf2_2j(const struct drm_edid *drm_edid)
{
const struct detailed_timing *descriptor = NULL;
- drm_for_each_detailed_block(edid, find_gtf2, &descriptor);
+ drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor);
BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.j) != 17);
@@ -2656,12 +2738,14 @@ drm_gtf2_2j(const struct edid *edid)
}
/* Get standard timing level (CVT/GTF/DMT). */
-static int standard_timing_level(const struct edid *edid)
+static int standard_timing_level(const struct drm_edid *drm_edid)
{
+ const struct edid *edid = drm_edid->edid;
+
if (edid->revision >= 2) {
if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF))
return LEVEL_CVT;
- if (drm_gtf2_hbreak(edid))
+ if (drm_gtf2_hbreak(drm_edid))
return LEVEL_GTF2;
if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
return LEVEL_GTF;
@@ -2693,9 +2777,9 @@ static int drm_mode_hsync(const struct drm_display_mode *mode)
* Take the standard timing params (in this case width, aspect, and refresh)
* and convert them into a real mode using CVT/GTF/DMT.
*/
-static struct drm_display_mode *
-drm_mode_std(struct drm_connector *connector, const struct edid *edid,
- const struct std_timing *t)
+static struct drm_display_mode *drm_mode_std(struct drm_connector *connector,
+ const struct drm_edid *drm_edid,
+ const struct std_timing *t)
{
struct drm_device *dev = connector->dev;
struct drm_display_mode *m, *mode = NULL;
@@ -2705,7 +2789,7 @@ drm_mode_std(struct drm_connector *connector, const struct edid *edid,
>> EDID_TIMING_ASPECT_SHIFT;
unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK)
>> EDID_TIMING_VFREQ_SHIFT;
- int timing_level = standard_timing_level(edid);
+ int timing_level = standard_timing_level(drm_edid);
if (bad_std_timing(t->hsize, t->vfreq_aspect))
return NULL;
@@ -2716,7 +2800,7 @@ drm_mode_std(struct drm_connector *connector, const struct edid *edid,
vrefresh_rate = vfreq + 60;
/* the vdisplay is calculated based on the aspect ratio */
if (aspect_ratio == 0) {
- if (edid->revision < 3)
+ if (drm_edid->edid->revision < 3)
vsize = hsize;
else
vsize = (hsize * 10) / 16;
@@ -2759,7 +2843,7 @@ drm_mode_std(struct drm_connector *connector, const struct edid *edid,
}
/* check whether it can be found in default mode table */
- if (drm_monitor_supports_rb(edid)) {
+ if (drm_monitor_supports_rb(drm_edid)) {
mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate,
true);
if (mode)
@@ -2785,14 +2869,14 @@ drm_mode_std(struct drm_connector *connector, const struct edid *edid,
mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
if (!mode)
return NULL;
- if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) {
+ if (drm_mode_hsync(mode) > drm_gtf2_hbreak(drm_edid)) {
drm_mode_destroy(dev, mode);
mode = drm_gtf_mode_complex(dev, hsize, vsize,
vrefresh_rate, 0, 0,
- drm_gtf2_m(edid),
- drm_gtf2_2c(edid),
- drm_gtf2_k(edid),
- drm_gtf2_2j(edid));
+ drm_gtf2_m(drm_edid),
+ drm_gtf2_2c(drm_edid),
+ drm_gtf2_k(drm_edid),
+ drm_gtf2_2j(drm_edid));
}
break;
case LEVEL_CVT:
@@ -2851,7 +2935,7 @@ drm_mode_do_interlace_quirk(struct drm_display_mode *mode,
* drm_display_mode.
*/
static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
- const struct edid *edid,
+ const struct drm_edid *drm_edid,
const struct detailed_timing *timing,
u32 quirks)
{
@@ -2939,8 +3023,8 @@ set_size:
}
if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
- mode->width_mm = edid->width_cm * 10;
- mode->height_mm = edid->height_cm * 10;
+ mode->width_mm = drm_edid->edid->width_cm * 10;
+ mode->height_mm = drm_edid->edid->height_cm * 10;
}
mode->type = DRM_MODE_TYPE_DRIVER;
@@ -2998,10 +3082,11 @@ range_pixel_clock(const struct edid *edid, const u8 *t)
return t[9] * 10000 + 5001;
}
-static bool
-mode_in_range(const struct drm_display_mode *mode, const struct edid *edid,
- const struct detailed_timing *timing)
+static bool mode_in_range(const struct drm_display_mode *mode,
+ const struct drm_edid *drm_edid,
+ const struct detailed_timing *timing)
{
+ const struct edid *edid = drm_edid->edid;
u32 max_clock;
const u8 *t = (const u8 *)timing;
@@ -3020,7 +3105,7 @@ mode_in_range(const struct drm_display_mode *mode, const struct edid *edid,
if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3))))
return false;
- if (mode_is_rb(mode) && !drm_monitor_supports_rb(edid))
+ if (mode_is_rb(mode) && !drm_monitor_supports_rb(drm_edid))
return false;
return true;
@@ -3044,16 +3129,16 @@ static bool valid_inferred_mode(const struct drm_connector *connector,
return ok;
}
-static int
-drm_dmt_modes_for_range(struct drm_connector *connector, const struct edid *edid,
- const struct detailed_timing *timing)
+static int drm_dmt_modes_for_range(struct drm_connector *connector,
+ const struct drm_edid *drm_edid,
+ const struct detailed_timing *timing)
{
int i, modes = 0;
struct drm_display_mode *newmode;
struct drm_device *dev = connector->dev;
for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) {
- if (mode_in_range(drm_dmt_modes + i, edid, timing) &&
+ if (mode_in_range(drm_dmt_modes + i, drm_edid, timing) &&
valid_inferred_mode(connector, drm_dmt_modes + i)) {
newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]);
if (newmode) {
@@ -3079,9 +3164,9 @@ void drm_mode_fixup_1366x768(struct drm_display_mode *mode)
}
}
-static int
-drm_gtf_modes_for_range(struct drm_connector *connector, const struct edid *edid,
- const struct detailed_timing *timing)
+static int drm_gtf_modes_for_range(struct drm_connector *connector,
+ const struct drm_edid *drm_edid,
+ const struct detailed_timing *timing)
{
int i, modes = 0;
struct drm_display_mode *newmode;
@@ -3095,7 +3180,7 @@ drm_gtf_modes_for_range(struct drm_connector *connector, const struct edid *edid
return modes;
drm_mode_fixup_1366x768(newmode);
- if (!mode_in_range(newmode, edid, timing) ||
+ if (!mode_in_range(newmode, drm_edid, timing) ||
!valid_inferred_mode(connector, newmode)) {
drm_mode_destroy(dev, newmode);
continue;
@@ -3108,14 +3193,14 @@ drm_gtf_modes_for_range(struct drm_connector *connector, const struct edid *edid
return modes;
}
-static int
-drm_cvt_modes_for_range(struct drm_connector *connector, const struct edid *edid,
- const struct detailed_timing *timing)
+static int drm_cvt_modes_for_range(struct drm_connector *connector,
+ const struct drm_edid *drm_edid,
+ const struct detailed_timing *timing)
{
int i, modes = 0;
struct drm_display_mode *newmode;
struct drm_device *dev = connector->dev;
- bool rb = drm_monitor_supports_rb(edid);
+ bool rb = drm_monitor_supports_rb(drm_edid);
for (i = 0; i < ARRAY_SIZE(extra_modes); i++) {
const struct minimode *m = &extra_modes[i];
@@ -3125,7 +3210,7 @@ drm_cvt_modes_for_range(struct drm_connector *connector, const struct edid *edid
return modes;
drm_mode_fixup_1366x768(newmode);
- if (!mode_in_range(newmode, edid, timing) ||
+ if (!mode_in_range(newmode, drm_edid, timing) ||
!valid_inferred_mode(connector, newmode)) {
drm_mode_destroy(dev, newmode);
continue;
@@ -3149,25 +3234,25 @@ do_inferred_modes(const struct detailed_timing *timing, void *c)
return;
closure->modes += drm_dmt_modes_for_range(closure->connector,
- closure->edid,
+ closure->drm_edid,
timing);
- if (!version_greater(closure->edid, 1, 1))
+ if (!version_greater(closure->drm_edid, 1, 1))
return; /* GTF not defined yet */
switch (range->flags) {
case 0x02: /* secondary gtf, XXX could do more */
case 0x00: /* default gtf */
closure->modes += drm_gtf_modes_for_range(closure->connector,
- closure->edid,
+ closure->drm_edid,
timing);
break;
case 0x04: /* cvt, only in 1.4+ */
- if (!version_greater(closure->edid, 1, 3))
+ if (!version_greater(closure->drm_edid, 1, 3))
break;
closure->modes += drm_cvt_modes_for_range(closure->connector,
- closure->edid,
+ closure->drm_edid,
timing);
break;
case 0x01: /* just the ranges, no formula */
@@ -3176,16 +3261,16 @@ do_inferred_modes(const struct detailed_timing *timing, void *c)
}
}
-static int
-add_inferred_modes(struct drm_connector *connector, const struct edid *edid)
+static int add_inferred_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
struct detailed_mode_closure closure = {
.connector = connector,
- .edid = edid,
+ .drm_edid = drm_edid,
};
- if (version_greater(edid, 1, 0))
- drm_for_each_detailed_block(edid, do_inferred_modes, &closure);
+ if (version_greater(drm_edid, 1, 0))
+ drm_for_each_detailed_block(drm_edid, do_inferred_modes, &closure);
return closure.modes;
}
@@ -3235,17 +3320,18 @@ do_established_modes(const struct detailed_timing *timing, void *c)
* bitmap of the supported "established modes" list (defined above). Tease them
* out and add them to the global modes list.
*/
-static int
-add_established_modes(struct drm_connector *connector, const struct edid *edid)
+static int add_established_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
struct drm_device *dev = connector->dev;
+ const struct edid *edid = drm_edid->edid;
unsigned long est_bits = edid->established_timings.t1 |
(edid->established_timings.t2 << 8) |
((edid->established_timings.mfg_rsvd & 0x80) << 9);
int i, modes = 0;
struct detailed_mode_closure closure = {
.connector = connector,
- .edid = edid,
+ .drm_edid = drm_edid,
};
for (i = 0; i <= EDID_EST_TIMINGS; i++) {
@@ -3260,8 +3346,8 @@ add_established_modes(struct drm_connector *connector, const struct edid *edid)
}
}
- if (version_greater(edid, 1, 0))
- drm_for_each_detailed_block(edid, do_established_modes,
+ if (version_greater(drm_edid, 1, 0))
+ drm_for_each_detailed_block(drm_edid, do_established_modes,
&closure);
return modes + closure.modes;
@@ -3273,7 +3359,6 @@ do_standard_modes(const struct detailed_timing *timing, void *c)
struct detailed_mode_closure *closure = c;
const struct detailed_non_pixel *data = &timing->data.other_data;
struct drm_connector *connector = closure->connector;
- const struct edid *edid = closure->edid;
int i;
if (!is_display_descriptor(timing, EDID_DETAIL_STD_MODES))
@@ -3283,7 +3368,7 @@ do_standard_modes(const struct detailed_timing *timing, void *c)
const struct std_timing *std = &data->data.timings[i];
struct drm_display_mode *newmode;
- newmode = drm_mode_std(connector, edid, std);
+ newmode = drm_mode_std(connector, closure->drm_edid, std);
if (newmode) {
drm_mode_probed_add(connector, newmode);
closure->modes++;
@@ -3296,28 +3381,28 @@ do_standard_modes(const struct detailed_timing *timing, void *c)
* using the appropriate standard (DMT, GTF, or CVT). Grab them from EDID and
* add them to the list.
*/
-static int
-add_standard_modes(struct drm_connector *connector, const struct edid *edid)
+static int add_standard_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
int i, modes = 0;
struct detailed_mode_closure closure = {
.connector = connector,
- .edid = edid,
+ .drm_edid = drm_edid,
};
for (i = 0; i < EDID_STD_TIMINGS; i++) {
struct drm_display_mode *newmode;
- newmode = drm_mode_std(connector, edid,
- &edid->standard_timings[i]);
+ newmode = drm_mode_std(connector, drm_edid,
+ &drm_edid->edid->standard_timings[i]);
if (newmode) {
drm_mode_probed_add(connector, newmode);
modes++;
}
}
- if (version_greater(edid, 1, 0))
- drm_for_each_detailed_block(edid, do_standard_modes,
+ if (version_greater(drm_edid, 1, 0))
+ drm_for_each_detailed_block(drm_edid, do_standard_modes,
&closure);
/* XXX should also look for standard codes in VTB blocks */
@@ -3389,15 +3474,15 @@ do_cvt_mode(const struct detailed_timing *timing, void *c)
}
static int
-add_cvt_modes(struct drm_connector *connector, const struct edid *edid)
+add_cvt_modes(struct drm_connector *connector, const struct drm_edid *drm_edid)
{
struct detailed_mode_closure closure = {
.connector = connector,
- .edid = edid,
+ .drm_edid = drm_edid,
};
- if (version_greater(edid, 1, 2))
- drm_for_each_detailed_block(edid, do_cvt_mode, &closure);
+ if (version_greater(drm_edid, 1, 2))
+ drm_for_each_detailed_block(drm_edid, do_cvt_mode, &closure);
/* XXX should also look for CVT codes in VTB blocks */
@@ -3416,7 +3501,7 @@ do_detailed_mode(const struct detailed_timing *timing, void *c)
return;
newmode = drm_mode_detailed(closure->connector->dev,
- closure->edid, timing,
+ closure->drm_edid, timing,
closure->quirks);
if (!newmode)
return;
@@ -3439,38 +3524,43 @@ do_detailed_mode(const struct detailed_timing *timing, void *c)
/*
* add_detailed_modes - Add modes from detailed timings
* @connector: attached connector
- * @edid: EDID block to scan
+ * @drm_edid: EDID block to scan
* @quirks: quirks to apply
*/
-static int
-add_detailed_modes(struct drm_connector *connector, const struct edid *edid,
- u32 quirks)
+static int add_detailed_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid, u32 quirks)
{
struct detailed_mode_closure closure = {
.connector = connector,
- .edid = edid,
+ .drm_edid = drm_edid,
.preferred = true,
.quirks = quirks,
};
- if (closure.preferred && !version_greater(edid, 1, 3))
+ if (closure.preferred && !version_greater(drm_edid, 1, 3))
closure.preferred =
- (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
+ (drm_edid->edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
- drm_for_each_detailed_block(edid, do_detailed_mode, &closure);
+ drm_for_each_detailed_block(drm_edid, do_detailed_mode, &closure);
return closure.modes;
}
-#define AUDIO_BLOCK 0x01
-#define VIDEO_BLOCK 0x02
-#define VENDOR_BLOCK 0x03
-#define SPEAKER_BLOCK 0x04
-#define HDR_STATIC_METADATA_BLOCK 0x6
-#define USE_EXTENDED_TAG 0x07
-#define EXT_VIDEO_CAPABILITY_BLOCK 0x00
-#define EXT_VIDEO_DATA_BLOCK_420 0x0E
-#define EXT_VIDEO_CAP_BLOCK_Y420CMDB 0x0F
+/* CTA-861-H Table 60 - CTA Tag Codes */
+#define CTA_DB_AUDIO 1
+#define CTA_DB_VIDEO 2
+#define CTA_DB_VENDOR 3
+#define CTA_DB_SPEAKER 4
+#define CTA_DB_EXTENDED_TAG 7
+
+/* CTA-861-H Table 62 - CTA Extended Tag Codes */
+#define CTA_EXT_DB_VIDEO_CAP 0
+#define CTA_EXT_DB_VENDOR 1
+#define CTA_EXT_DB_HDR_STATIC_METADATA 6
+#define CTA_EXT_DB_420_VIDEO_DATA 14
+#define CTA_EXT_DB_420_VIDEO_CAP_MAP 15
+#define CTA_EXT_DB_HF_SCDB 0x79
+
#define EDID_BASIC_AUDIO (1 << 6)
#define EDID_CEA_YCRCB444 (1 << 5)
#define EDID_CEA_YCRCB422 (1 << 4)
@@ -3478,10 +3568,13 @@ add_detailed_modes(struct drm_connector *connector, const struct edid *edid,
/*
* Search EDID for CEA extension block.
+ *
+ * FIXME: Prefer not returning pointers to raw EDID data.
*/
-const u8 *drm_find_edid_extension(const struct edid *edid,
+const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid,
int ext_id, int *ext_index)
{
+ const struct edid *edid = drm_edid ? drm_edid->edid : NULL;
const u8 *edid_ext = NULL;
int i;
@@ -3504,30 +3597,29 @@ const u8 *drm_find_edid_extension(const struct edid *edid,
return edid_ext;
}
-static const u8 *drm_find_cea_extension(const struct edid *edid)
+/* Return true if the EDID has a CTA extension or a DisplayID CTA data block */
+static bool drm_edid_has_cta_extension(const struct drm_edid *drm_edid)
{
const struct displayid_block *block;
struct displayid_iter iter;
- const u8 *cea;
int ext_index = 0;
+ bool found = false;
/* Look for a top level CEA extension block */
- /* FIXME: make callers iterate through multiple CEA ext blocks? */
- cea = drm_find_edid_extension(edid, CEA_EXT, &ext_index);
- if (cea)
- return cea;
+ if (drm_find_edid_extension(drm_edid, CEA_EXT, &ext_index))
+ return true;
/* CEA blocks can also be found embedded in a DisplayID block */
- displayid_iter_edid_begin(edid, &iter);
+ displayid_iter_edid_begin(drm_edid, &iter);
displayid_iter_for_each(block, &iter) {
if (block->tag == DATA_BLOCK_CTA) {
- cea = (const u8 *)block;
+ found = true;
break;
}
}
displayid_iter_end(&iter);
- return cea;
+ return found;
}
static __always_inline const struct drm_display_mode *cea_mode_for_vic(u8 vic)
@@ -3792,16 +3884,16 @@ static bool drm_valid_hdmi_vic(u8 vic)
return vic > 0 && vic < ARRAY_SIZE(edid_4k_modes);
}
-static int
-add_alternate_cea_modes(struct drm_connector *connector, const struct edid *edid)
+static int add_alternate_cea_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode, *tmp;
LIST_HEAD(list);
int modes = 0;
- /* Don't add CEA modes if the CEA extension block is missing */
- if (!drm_find_cea_extension(edid))
+ /* Don't add CTA modes if the CTA extension block is missing */
+ if (!drm_edid_has_cta_extension(drm_edid))
return 0;
/*
@@ -4283,24 +4375,6 @@ out:
}
static int
-cea_db_payload_len(const u8 *db)
-{
- return db[0] & 0x1f;
-}
-
-static int
-cea_db_extended_tag(const u8 *db)
-{
- return db[1];
-}
-
-static int
-cea_db_tag(const u8 *db)
-{
- return db[0] >> 5;
-}
-
-static int
cea_revision(const u8 *cea)
{
/*
@@ -4313,125 +4387,255 @@ cea_revision(const u8 *cea)
return cea[1];
}
-static int
-cea_db_offsets(const u8 *cea, int *start, int *end)
+/*
+ * CTA Data Block iterator.
+ *
+ * Iterate through all CTA Data Blocks in both EDID CTA Extensions and DisplayID
+ * CTA Data Blocks.
+ *
+ * struct cea_db *db:
+ * struct cea_db_iter iter;
+ *
+ * cea_db_iter_edid_begin(edid, &iter);
+ * cea_db_iter_for_each(db, &iter) {
+ * // do stuff with db
+ * }
+ * cea_db_iter_end(&iter);
+ */
+struct cea_db_iter {
+ struct drm_edid_iter edid_iter;
+ struct displayid_iter displayid_iter;
+
+ /* Current Data Block Collection. */
+ const u8 *collection;
+
+ /* Current Data Block index in current collection. */
+ int index;
+
+ /* End index in current collection. */
+ int end;
+};
+
+/* CTA-861-H section 7.4 CTA Data BLock Collection */
+struct cea_db {
+ u8 tag_length;
+ u8 data[];
+} __packed;
+
+static int cea_db_tag(const struct cea_db *db)
{
- /* DisplayID CTA extension blocks and top-level CEA EDID
- * block header definitions differ in the following bytes:
- * 1) Byte 2 of the header specifies length differently,
- * 2) Byte 3 is only present in the CEA top level block.
- *
- * The different definitions for byte 2 follow.
- *
- * DisplayID CTA extension block defines byte 2 as:
- * Number of payload bytes
- *
- * CEA EDID block defines byte 2 as:
- * Byte number (decimal) within this block where the 18-byte
- * DTDs begin. If no non-DTD data is present in this extension
- * block, the value should be set to 04h (the byte after next).
- * If set to 00h, there are no DTDs present in this block and
- * no non-DTD data.
- */
- if (cea[0] == DATA_BLOCK_CTA) {
- /*
- * for_each_displayid_db() has already verified
- * that these stay within expected bounds.
- */
- *start = 3;
- *end = *start + cea[2];
- } else if (cea[0] == CEA_EXT) {
- /* Data block offset in CEA extension block */
- *start = 4;
- *end = cea[2];
- if (*end == 0)
- *end = 127;
- if (*end < 4 || *end > 127)
- return -ERANGE;
- } else {
- return -EOPNOTSUPP;
- }
+ return db->tag_length >> 5;
+}
- return 0;
+static int cea_db_payload_len(const void *_db)
+{
+ /* FIXME: Transition to passing struct cea_db * everywhere. */
+ const struct cea_db *db = _db;
+
+ return db->tag_length & 0x1f;
}
-static bool cea_db_is_hdmi_vsdb(const u8 *db)
+static const void *cea_db_data(const struct cea_db *db)
{
- if (cea_db_tag(db) != VENDOR_BLOCK)
- return false;
+ return db->data;
+}
- if (cea_db_payload_len(db) < 5)
- return false;
+static bool cea_db_is_extended_tag(const struct cea_db *db, int tag)
+{
+ return cea_db_tag(db) == CTA_DB_EXTENDED_TAG &&
+ cea_db_payload_len(db) >= 1 &&
+ db->data[0] == tag;
+}
- return oui(db[3], db[2], db[1]) == HDMI_IEEE_OUI;
+static bool cea_db_is_vendor(const struct cea_db *db, int vendor_oui)
+{
+ const u8 *data = cea_db_data(db);
+
+ return cea_db_tag(db) == CTA_DB_VENDOR &&
+ cea_db_payload_len(db) >= 3 &&
+ oui(data[2], data[1], data[0]) == vendor_oui;
}
-static bool cea_db_is_hdmi_forum_vsdb(const u8 *db)
+static void cea_db_iter_edid_begin(const struct drm_edid *drm_edid,
+ struct cea_db_iter *iter)
{
- if (cea_db_tag(db) != VENDOR_BLOCK)
- return false;
+ memset(iter, 0, sizeof(*iter));
- if (cea_db_payload_len(db) < 7)
- return false;
+ drm_edid_iter_begin(drm_edid, &iter->edid_iter);
+ displayid_iter_edid_begin(drm_edid, &iter->displayid_iter);
+}
+
+static const struct cea_db *
+__cea_db_iter_current_block(const struct cea_db_iter *iter)
+{
+ const struct cea_db *db;
+
+ if (!iter->collection)
+ return NULL;
- return oui(db[3], db[2], db[1]) == HDMI_FORUM_IEEE_OUI;
+ db = (const struct cea_db *)&iter->collection[iter->index];
+
+ if (iter->index + sizeof(*db) <= iter->end &&
+ iter->index + sizeof(*db) + cea_db_payload_len(db) <= iter->end)
+ return db;
+
+ return NULL;
}
-static bool cea_db_is_microsoft_vsdb(const u8 *db)
+/*
+ * References:
+ * - VESA E-EDID v1.4
+ * - CTA-861-H section 7.3.3 CTA Extension Version 3
+ */
+static const void *__cea_db_iter_edid_next(struct cea_db_iter *iter)
{
- if (cea_db_tag(db) != VENDOR_BLOCK)
- return false;
+ const u8 *ext;
- if (cea_db_payload_len(db) != 21)
- return false;
+ drm_edid_iter_for_each(ext, &iter->edid_iter) {
+ /* Only support CTA Extension revision 3+ */
+ if (ext[0] != CEA_EXT || cea_revision(ext) < 3)
+ continue;
+
+ iter->index = 4;
+ iter->end = ext[2];
+ if (iter->end == 0)
+ iter->end = 127;
+ if (iter->end < 4 || iter->end > 127)
+ continue;
+
+ return ext;
+ }
- return oui(db[3], db[2], db[1]) == MICROSOFT_IEEE_OUI;
+ return NULL;
}
-static bool cea_db_is_vcdb(const u8 *db)
+/*
+ * References:
+ * - DisplayID v1.3 Appendix C: CEA Data Block within a DisplayID Data Block
+ * - DisplayID v2.0 section 4.10 CTA DisplayID Data Block
+ *
+ * Note that the above do not specify any connection between DisplayID Data
+ * Block revision and CTA Extension versions.
+ */
+static const void *__cea_db_iter_displayid_next(struct cea_db_iter *iter)
{
- if (cea_db_tag(db) != USE_EXTENDED_TAG)
- return false;
+ const struct displayid_block *block;
- if (cea_db_payload_len(db) != 2)
- return false;
+ displayid_iter_for_each(block, &iter->displayid_iter) {
+ if (block->tag != DATA_BLOCK_CTA)
+ continue;
- if (cea_db_extended_tag(db) != EXT_VIDEO_CAPABILITY_BLOCK)
- return false;
+ /*
+ * The displayid iterator has already verified the block bounds
+ * in displayid_iter_block().
+ */
+ iter->index = sizeof(*block);
+ iter->end = iter->index + block->num_bytes;
- return true;
+ return block;
+ }
+
+ return NULL;
}
-static bool cea_db_is_y420cmdb(const u8 *db)
+static const struct cea_db *__cea_db_iter_next(struct cea_db_iter *iter)
{
- if (cea_db_tag(db) != USE_EXTENDED_TAG)
- return false;
+ const struct cea_db *db;
- if (!cea_db_payload_len(db))
- return false;
+ if (iter->collection) {
+ /* Current collection should always be valid. */
+ db = __cea_db_iter_current_block(iter);
+ if (WARN_ON(!db)) {
+ iter->collection = NULL;
+ return NULL;
+ }
- if (cea_db_extended_tag(db) != EXT_VIDEO_CAP_BLOCK_Y420CMDB)
- return false;
+ /* Next block in CTA Data Block Collection */
+ iter->index += sizeof(*db) + cea_db_payload_len(db);
- return true;
+ db = __cea_db_iter_current_block(iter);
+ if (db)
+ return db;
+ }
+
+ for (;;) {
+ /*
+ * Find the next CTA Data Block Collection. First iterate all
+ * the EDID CTA Extensions, then all the DisplayID CTA blocks.
+ *
+ * Per DisplayID v1.3 Appendix B: DisplayID as an EDID
+ * Extension, it's recommended that DisplayID extensions are
+ * exposed after all of the CTA Extensions.
+ */
+ iter->collection = __cea_db_iter_edid_next(iter);
+ if (!iter->collection)
+ iter->collection = __cea_db_iter_displayid_next(iter);
+
+ if (!iter->collection)
+ return NULL;
+
+ db = __cea_db_iter_current_block(iter);
+ if (db)
+ return db;
+ }
}
-static bool cea_db_is_y420vdb(const u8 *db)
+#define cea_db_iter_for_each(__db, __iter) \
+ while (((__db) = __cea_db_iter_next(__iter)))
+
+static void cea_db_iter_end(struct cea_db_iter *iter)
{
- if (cea_db_tag(db) != USE_EXTENDED_TAG)
- return false;
+ displayid_iter_end(&iter->displayid_iter);
+ drm_edid_iter_end(&iter->edid_iter);
- if (!cea_db_payload_len(db))
- return false;
+ memset(iter, 0, sizeof(*iter));
+}
- if (cea_db_extended_tag(db) != EXT_VIDEO_DATA_BLOCK_420)
- return false;
+static bool cea_db_is_hdmi_vsdb(const struct cea_db *db)
+{
+ return cea_db_is_vendor(db, HDMI_IEEE_OUI) &&
+ cea_db_payload_len(db) >= 5;
+}
- return true;
+static bool cea_db_is_hdmi_forum_vsdb(const struct cea_db *db)
+{
+ return cea_db_is_vendor(db, HDMI_FORUM_IEEE_OUI) &&
+ cea_db_payload_len(db) >= 7;
+}
+
+static bool cea_db_is_microsoft_vsdb(const struct cea_db *db)
+{
+ return cea_db_is_vendor(db, MICROSOFT_IEEE_OUI) &&
+ cea_db_payload_len(db) == 21;
+}
+
+static bool cea_db_is_vcdb(const struct cea_db *db)
+{
+ return cea_db_is_extended_tag(db, CTA_EXT_DB_VIDEO_CAP) &&
+ cea_db_payload_len(db) == 2;
+}
+
+static bool cea_db_is_hdmi_forum_scdb(const struct cea_db *db)
+{
+ return cea_db_is_extended_tag(db, CTA_EXT_DB_HF_SCDB) &&
+ cea_db_payload_len(db) >= 7;
}
-#define for_each_cea_db(cea, i, start, end) \
- for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
+static bool cea_db_is_y420cmdb(const struct cea_db *db)
+{
+ return cea_db_is_extended_tag(db, CTA_EXT_DB_420_VIDEO_CAP_MAP);
+}
+
+static bool cea_db_is_y420vdb(const struct cea_db *db)
+{
+ return cea_db_is_extended_tag(db, CTA_EXT_DB_420_VIDEO_DATA);
+}
+
+static bool cea_db_is_hdmi_hdr_metadata_block(const struct cea_db *db)
+{
+ return cea_db_is_extended_tag(db, CTA_EXT_DB_HDR_STATIC_METADATA) &&
+ cea_db_payload_len(db) >= 3;
+}
static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector,
const u8 *db)
@@ -4473,49 +4677,44 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector,
hdmi->y420_cmdb_map = map;
}
-static int
-add_cea_modes(struct drm_connector *connector, const struct edid *edid)
+static int add_cea_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
- const u8 *cea = drm_find_cea_extension(edid);
- const u8 *db, *hdmi = NULL, *video = NULL;
- u8 dbl, hdmi_len, video_len = 0;
+ const struct cea_db *db;
+ struct cea_db_iter iter;
int modes = 0;
- if (cea && cea_revision(cea) >= 3) {
- int i, start, end;
-
- if (cea_db_offsets(cea, &start, &end))
- return 0;
-
- for_each_cea_db(cea, i, start, end) {
- db = &cea[i];
- dbl = cea_db_payload_len(db);
-
- if (cea_db_tag(db) == VIDEO_BLOCK) {
- video = db + 1;
- video_len = dbl;
- modes += do_cea_modes(connector, video, dbl);
- } else if (cea_db_is_hdmi_vsdb(db)) {
- hdmi = db;
- hdmi_len = dbl;
- } else if (cea_db_is_y420vdb(db)) {
- const u8 *vdb420 = &db[2];
-
- /* Add 4:2:0(only) modes present in EDID */
- modes += do_y420vdb_modes(connector,
- vdb420,
- dbl - 1);
- }
+ 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);
+ } else if (cea_db_is_y420vdb(db)) {
+ const u8 *vdb420 = cea_db_data(db) + 1;
+
+ /* Add 4:2:0(only) modes present in EDID */
+ 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);
+ /*
+ * 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);
return modes;
}
@@ -4563,20 +4762,6 @@ static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode)
mode->clock = clock;
}
-static bool cea_db_is_hdmi_hdr_metadata_block(const u8 *db)
-{
- if (cea_db_tag(db) != USE_EXTENDED_TAG)
- return false;
-
- if (db[1] != HDR_STATIC_METADATA_BLOCK)
- return false;
-
- if (cea_db_payload_len(db) < 3)
- return false;
-
- return true;
-}
-
static uint8_t eotf_supported(const u8 *edid_ext)
{
return edid_ext[2] &
@@ -4654,15 +4839,15 @@ monitor_name(const struct detailed_timing *timing, void *data)
*res = timing->data.other_data.data.str.str;
}
-static int get_monitor_name(const struct edid *edid, char name[13])
+static int get_monitor_name(const struct drm_edid *drm_edid, char name[13])
{
const char *edid_name = NULL;
int mnl;
- if (!edid || !name)
+ if (!drm_edid || !name)
return 0;
- drm_for_each_detailed_block(edid, monitor_name, &edid_name);
+ drm_for_each_detailed_block(drm_edid, monitor_name, &edid_name);
for (mnl = 0; edid_name && mnl < 13; mnl++) {
if (edid_name[mnl] == 0x0a)
break;
@@ -4682,14 +4867,22 @@ static int get_monitor_name(const struct edid *edid, char name[13])
*/
void drm_edid_get_monitor_name(const struct edid *edid, char *name, int bufsize)
{
- int name_length;
- char buf[13];
+ int name_length = 0;
if (bufsize <= 0)
return;
- name_length = min(get_monitor_name(edid, buf), bufsize - 1);
- memcpy(name, buf, name_length);
+ if (edid) {
+ char buf[13];
+ struct drm_edid drm_edid = {
+ .edid = edid,
+ .size = edid_size(edid),
+ };
+
+ name_length = min(get_monitor_name(&drm_edid, buf), bufsize - 1);
+ memcpy(name, buf, name_length);
+ }
+
name[name_length] = '\0';
}
EXPORT_SYMBOL(drm_edid_get_monitor_name);
@@ -4709,82 +4902,70 @@ static void clear_eld(struct drm_connector *connector)
/*
* drm_edid_to_eld - build ELD from EDID
* @connector: connector corresponding to the HDMI/DP sink
- * @edid: EDID to parse
+ * @drm_edid: EDID to parse
*
* Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The
* HDCP and Port_ID ELD fields are left for the graphics driver to fill in.
*/
static void drm_edid_to_eld(struct drm_connector *connector,
- const struct edid *edid)
+ const struct drm_edid *drm_edid)
{
+ const struct drm_display_info *info = &connector->display_info;
+ const struct cea_db *db;
+ struct cea_db_iter iter;
uint8_t *eld = connector->eld;
- const u8 *cea;
- const u8 *db;
int total_sad_count = 0;
int mnl;
- int dbl;
clear_eld(connector);
- if (!edid)
+ if (!drm_edid)
return;
- cea = drm_find_cea_extension(edid);
- if (!cea) {
- DRM_DEBUG_KMS("ELD: no CEA Extension found\n");
- return;
- }
-
- mnl = get_monitor_name(edid, &eld[DRM_ELD_MONITOR_NAME_STRING]);
+ mnl = get_monitor_name(drm_edid, &eld[DRM_ELD_MONITOR_NAME_STRING]);
DRM_DEBUG_KMS("ELD monitor %s\n", &eld[DRM_ELD_MONITOR_NAME_STRING]);
- eld[DRM_ELD_CEA_EDID_VER_MNL] = cea[1] << DRM_ELD_CEA_EDID_VER_SHIFT;
+ eld[DRM_ELD_CEA_EDID_VER_MNL] = info->cea_rev << DRM_ELD_CEA_EDID_VER_SHIFT;
eld[DRM_ELD_CEA_EDID_VER_MNL] |= mnl;
eld[DRM_ELD_VER] = DRM_ELD_VER_CEA861D;
- 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];
+ eld[DRM_ELD_MANUFACTURER_NAME0] = drm_edid->edid->mfg_id[0];
+ eld[DRM_ELD_MANUFACTURER_NAME1] = drm_edid->edid->mfg_id[1];
+ eld[DRM_ELD_PRODUCT_CODE0] = drm_edid->edid->prod_code[0];
+ eld[DRM_ELD_PRODUCT_CODE1] = drm_edid->edid->prod_code[1];
- if (cea_revision(cea) >= 3) {
- int i, start, end;
+ cea_db_iter_edid_begin(drm_edid, &iter);
+ cea_db_iter_for_each(db, &iter) {
+ const u8 *data = cea_db_data(db);
+ int len = cea_db_payload_len(db);
int sad_count;
- if (cea_db_offsets(cea, &start, &end)) {
- start = 0;
- end = 0;
- }
-
- for_each_cea_db(cea, i, start, end) {
- db = &cea[i];
- dbl = cea_db_payload_len(db);
-
- switch (cea_db_tag(db)) {
- case AUDIO_BLOCK:
- /* Audio Data Block, contains SADs */
- sad_count = min(dbl / 3, 15 - total_sad_count);
- if (sad_count >= 1)
- 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[DRM_ELD_SPEAKER] = db[1];
- break;
- case VENDOR_BLOCK:
- /* HDMI Vendor-Specific Data Block */
- if (cea_db_is_hdmi_vsdb(db))
- drm_parse_hdmi_vsdb_audio(connector, db);
- break;
- default:
- break;
- }
+ switch (cea_db_tag(db)) {
+ case CTA_DB_AUDIO:
+ /* Audio Data Block, contains SADs */
+ sad_count = min(len / 3, 15 - total_sad_count);
+ if (sad_count >= 1)
+ memcpy(&eld[DRM_ELD_CEA_SAD(mnl, total_sad_count)],
+ data, sad_count * 3);
+ total_sad_count += sad_count;
+ break;
+ case CTA_DB_SPEAKER:
+ /* Speaker Allocation Data Block */
+ if (len >= 1)
+ eld[DRM_ELD_SPEAKER] = data[0];
+ break;
+ case CTA_DB_VENDOR:
+ /* HDMI Vendor-Specific Data Block */
+ if (cea_db_is_hdmi_vsdb(db))
+ drm_parse_hdmi_vsdb_audio(connector, (const u8 *)db);
+ break;
+ default:
+ break;
}
}
+ cea_db_iter_end(&iter);
+
eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= total_sad_count << DRM_ELD_SAD_COUNT_SHIFT;
if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
@@ -4800,6 +4981,40 @@ static void drm_edid_to_eld(struct drm_connector *connector,
drm_eld_size(eld), total_sad_count);
}
+static int _drm_edid_to_sad(const struct drm_edid *drm_edid,
+ struct cea_sad **sads)
+{
+ const struct cea_db *db;
+ struct cea_db_iter iter;
+ int count = 0;
+
+ cea_db_iter_edid_begin(drm_edid, &iter);
+ cea_db_iter_for_each(db, &iter) {
+ if (cea_db_tag(db) == CTA_DB_AUDIO) {
+ int j;
+
+ count = cea_db_payload_len(db) / 3; /* SAD is 3B */
+ *sads = kcalloc(count, sizeof(**sads), GFP_KERNEL);
+ if (!*sads)
+ return -ENOMEM;
+ for (j = 0; j < count; j++) {
+ const u8 *sad = &db->data[j * 3];
+
+ (*sads)[j].format = (sad[0] & 0x78) >> 3;
+ (*sads)[j].channels = sad[0] & 0x7;
+ (*sads)[j].freq = sad[1] & 0x7F;
+ (*sads)[j].byte2 = sad[2];
+ }
+ break;
+ }
+ }
+ cea_db_iter_end(&iter);
+
+ DRM_DEBUG_KMS("Found %d Short Audio Descriptors\n", count);
+
+ return count;
+}
+
/**
* drm_edid_to_sad - extracts SADs from EDID
* @edid: EDID to parse
@@ -4813,53 +5028,37 @@ static void drm_edid_to_eld(struct drm_connector *connector,
*/
int drm_edid_to_sad(const struct edid *edid, struct cea_sad **sads)
{
- int count = 0;
- int i, start, end, dbl;
- const u8 *cea;
-
- cea = drm_find_cea_extension(edid);
- if (!cea) {
- DRM_DEBUG_KMS("SAD: no CEA Extension found\n");
- return 0;
- }
-
- if (cea_revision(cea) < 3) {
- DRM_DEBUG_KMS("SAD: wrong CEA revision\n");
- return 0;
- }
-
- if (cea_db_offsets(cea, &start, &end)) {
- DRM_DEBUG_KMS("SAD: invalid data block offsets\n");
- return -EPROTO;
- }
-
- for_each_cea_db(cea, i, start, end) {
- const u8 *db = &cea[i];
+ struct drm_edid drm_edid;
- if (cea_db_tag(db) == AUDIO_BLOCK) {
- int j;
+ return _drm_edid_to_sad(drm_edid_legacy_init(&drm_edid, edid), sads);
+}
+EXPORT_SYMBOL(drm_edid_to_sad);
- dbl = cea_db_payload_len(db);
+static int _drm_edid_to_speaker_allocation(const struct drm_edid *drm_edid,
+ u8 **sadb)
+{
+ const struct cea_db *db;
+ struct cea_db_iter iter;
+ int count = 0;
- count = dbl / 3; /* SAD is 3B */
- *sads = kcalloc(count, sizeof(**sads), GFP_KERNEL);
- if (!*sads)
+ cea_db_iter_edid_begin(drm_edid, &iter);
+ cea_db_iter_for_each(db, &iter) {
+ if (cea_db_tag(db) == CTA_DB_SPEAKER &&
+ cea_db_payload_len(db) == 3) {
+ *sadb = kmemdup(db->data, cea_db_payload_len(db),
+ GFP_KERNEL);
+ if (!*sadb)
return -ENOMEM;
- for (j = 0; j < count; j++) {
- const u8 *sad = &db[1 + j * 3];
-
- (*sads)[j].format = (sad[0] & 0x78) >> 3;
- (*sads)[j].channels = sad[0] & 0x7;
- (*sads)[j].freq = sad[1] & 0x7F;
- (*sads)[j].byte2 = sad[2];
- }
+ count = cea_db_payload_len(db);
break;
}
}
+ cea_db_iter_end(&iter);
+
+ DRM_DEBUG_KMS("Found %d Speaker Allocation Data Blocks\n", count);
return count;
}
-EXPORT_SYMBOL(drm_edid_to_sad);
/**
* drm_edid_to_speaker_allocation - extracts Speaker Allocation Data Blocks from EDID
@@ -4875,44 +5074,10 @@ EXPORT_SYMBOL(drm_edid_to_sad);
*/
int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb)
{
- int count = 0;
- int i, start, end, dbl;
- const u8 *cea;
-
- cea = drm_find_cea_extension(edid);
- if (!cea) {
- DRM_DEBUG_KMS("SAD: no CEA Extension found\n");
- return 0;
- }
-
- if (cea_revision(cea) < 3) {
- DRM_DEBUG_KMS("SAD: wrong CEA revision\n");
- return 0;
- }
+ struct drm_edid drm_edid;
- if (cea_db_offsets(cea, &start, &end)) {
- DRM_DEBUG_KMS("SAD: invalid data block offsets\n");
- return -EPROTO;
- }
-
- for_each_cea_db(cea, i, start, end) {
- const u8 *db = &cea[i];
-
- if (cea_db_tag(db) == SPEAKER_BLOCK) {
- dbl = cea_db_payload_len(db);
-
- /* Speaker Allocation Data Block */
- if (dbl == 3) {
- *sadb = kmemdup(&db[1], dbl, GFP_KERNEL);
- if (!*sadb)
- return -ENOMEM;
- count = dbl;
- break;
- }
- }
- }
-
- return count;
+ return _drm_edid_to_speaker_allocation(drm_edid_legacy_init(&drm_edid, edid),
+ sadb);
}
EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
@@ -4957,6 +5122,28 @@ int drm_av_sync_delay(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_av_sync_delay);
+static bool _drm_detect_hdmi_monitor(const struct drm_edid *drm_edid)
+{
+ const struct cea_db *db;
+ struct cea_db_iter iter;
+ bool hdmi = false;
+
+ /*
+ * Because HDMI identifier is in Vendor Specific Block,
+ * search it from all data blocks of CEA extension.
+ */
+ cea_db_iter_edid_begin(drm_edid, &iter);
+ cea_db_iter_for_each(db, &iter) {
+ if (cea_db_is_hdmi_vsdb(db)) {
+ hdmi = true;
+ break;
+ }
+ }
+ cea_db_iter_end(&iter);
+
+ return hdmi;
+}
+
/**
* drm_detect_hdmi_monitor - detect whether monitor is HDMI
* @edid: monitor EDID information
@@ -4970,29 +5157,53 @@ EXPORT_SYMBOL(drm_av_sync_delay);
*/
bool drm_detect_hdmi_monitor(const struct edid *edid)
{
+ struct drm_edid drm_edid;
+
+ return _drm_detect_hdmi_monitor(drm_edid_legacy_init(&drm_edid, edid));
+}
+EXPORT_SYMBOL(drm_detect_hdmi_monitor);
+
+static bool _drm_detect_monitor_audio(const struct drm_edid *drm_edid)
+{
+ struct drm_edid_iter edid_iter;
+ const struct cea_db *db;
+ struct cea_db_iter iter;
const u8 *edid_ext;
- int i;
- int start_offset, end_offset;
+ bool has_audio = false;
- edid_ext = drm_find_cea_extension(edid);
- if (!edid_ext)
- return false;
+ drm_edid_iter_begin(drm_edid, &edid_iter);
+ drm_edid_iter_for_each(edid_ext, &edid_iter) {
+ if (edid_ext[0] == CEA_EXT) {
+ has_audio = edid_ext[3] & EDID_BASIC_AUDIO;
+ if (has_audio)
+ break;
+ }
+ }
+ drm_edid_iter_end(&edid_iter);
- if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
- return false;
+ if (has_audio) {
+ DRM_DEBUG_KMS("Monitor has basic audio support\n");
+ goto end;
+ }
- /*
- * Because HDMI identifier is in Vendor Specific Block,
- * search it from all data blocks of CEA extension.
- */
- for_each_cea_db(edid_ext, i, start_offset, end_offset) {
- if (cea_db_is_hdmi_vsdb(&edid_ext[i]))
- return true;
+ cea_db_iter_edid_begin(drm_edid, &iter);
+ cea_db_iter_for_each(db, &iter) {
+ if (cea_db_tag(db) == CTA_DB_AUDIO) {
+ const u8 *data = cea_db_data(db);
+ int i;
+
+ for (i = 0; i < cea_db_payload_len(db); i += 3)
+ DRM_DEBUG_KMS("CEA audio format %d\n",
+ (data[i] >> 3) & 0xf);
+ has_audio = true;
+ break;
+ }
}
+ cea_db_iter_end(&iter);
- return false;
+end:
+ return has_audio;
}
-EXPORT_SYMBOL(drm_detect_hdmi_monitor);
/**
* drm_detect_monitor_audio - check monitor audio capability
@@ -5008,37 +5219,9 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor);
*/
bool drm_detect_monitor_audio(const struct edid *edid)
{
- const u8 *edid_ext;
- int i, j;
- bool has_audio = false;
- int start_offset, end_offset;
-
- edid_ext = drm_find_cea_extension(edid);
- if (!edid_ext)
- goto end;
-
- has_audio = (edid_ext[0] == CEA_EXT &&
- (edid_ext[3] & EDID_BASIC_AUDIO) != 0);
+ struct drm_edid drm_edid;
- if (has_audio) {
- DRM_DEBUG_KMS("Monitor has basic audio support\n");
- goto end;
- }
-
- if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
- goto end;
-
- for_each_cea_db(edid_ext, i, start_offset, end_offset) {
- if (cea_db_tag(&edid_ext[i]) == AUDIO_BLOCK) {
- has_audio = true;
- for (j = 1; j < cea_db_payload_len(&edid_ext[i]) + 1; j += 3)
- DRM_DEBUG_KMS("CEA audio format %d\n",
- (edid_ext[i + j] >> 3) & 0xf);
- goto end;
- }
- }
-end:
- return has_audio;
+ return _drm_detect_monitor_audio(drm_edid_legacy_init(&drm_edid, edid));
}
EXPORT_SYMBOL(drm_detect_monitor_audio);
@@ -5117,17 +5300,18 @@ static void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector,
hdmi->y420_dc_modes = dc_mask;
}
-static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,
- const u8 *hf_vsdb)
+/* Sink Capability Data Structure */
+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;
display->has_hdmi_infoframe = true;
- if (hf_vsdb[6] & 0x80) {
+ if (hf_scds[6] & 0x80) {
hdmi->scdc.supported = true;
- if (hf_vsdb[6] & 0x40)
+ if (hf_scds[6] & 0x40)
hdmi->scdc.read_request = true;
}
@@ -5140,9 +5324,9 @@ static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,
* Lets check it out.
*/
- if (hf_vsdb[5]) {
+ if (hf_scds[5]) {
/* max clock is 5000 KHz times block value */
- u32 max_tmds_clock = hf_vsdb[5] * 5000;
+ u32 max_tmds_clock = hf_scds[5] * 5000;
struct drm_scdc *scdc = &hdmi->scdc;
if (max_tmds_clock > 340000) {
@@ -5155,42 +5339,42 @@ static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,
scdc->scrambling.supported = true;
/* Few sinks support scrambling for clocks < 340M */
- if ((hf_vsdb[6] & 0x8))
+ if ((hf_scds[6] & 0x8))
scdc->scrambling.low_rates = true;
}
}
- if (hf_vsdb[7]) {
+ if (hf_scds[7]) {
u8 max_frl_rate;
u8 dsc_max_frl_rate;
u8 dsc_max_slices;
struct drm_hdmi_dsc_cap *hdmi_dsc = &hdmi->dsc_cap;
DRM_DEBUG_KMS("hdmi_21 sink detected. parsing edid\n");
- max_frl_rate = (hf_vsdb[7] & DRM_EDID_MAX_FRL_RATE_MASK) >> 4;
+ max_frl_rate = (hf_scds[7] & DRM_EDID_MAX_FRL_RATE_MASK) >> 4;
drm_get_max_frl_rate(max_frl_rate, &hdmi->max_lanes,
&hdmi->max_frl_rate_per_lane);
- hdmi_dsc->v_1p2 = hf_vsdb[11] & DRM_EDID_DSC_1P2;
+ hdmi_dsc->v_1p2 = hf_scds[11] & DRM_EDID_DSC_1P2;
if (hdmi_dsc->v_1p2) {
- hdmi_dsc->native_420 = hf_vsdb[11] & DRM_EDID_DSC_NATIVE_420;
- hdmi_dsc->all_bpp = hf_vsdb[11] & DRM_EDID_DSC_ALL_BPP;
+ hdmi_dsc->native_420 = hf_scds[11] & DRM_EDID_DSC_NATIVE_420;
+ hdmi_dsc->all_bpp = hf_scds[11] & DRM_EDID_DSC_ALL_BPP;
- if (hf_vsdb[11] & DRM_EDID_DSC_16BPC)
+ if (hf_scds[11] & DRM_EDID_DSC_16BPC)
hdmi_dsc->bpc_supported = 16;
- else if (hf_vsdb[11] & DRM_EDID_DSC_12BPC)
+ else if (hf_scds[11] & DRM_EDID_DSC_12BPC)
hdmi_dsc->bpc_supported = 12;
- else if (hf_vsdb[11] & DRM_EDID_DSC_10BPC)
+ else if (hf_scds[11] & DRM_EDID_DSC_10BPC)
hdmi_dsc->bpc_supported = 10;
else
hdmi_dsc->bpc_supported = 0;
- dsc_max_frl_rate = (hf_vsdb[12] & DRM_EDID_DSC_MAX_FRL_RATE_MASK) >> 4;
+ dsc_max_frl_rate = (hf_scds[12] & DRM_EDID_DSC_MAX_FRL_RATE_MASK) >> 4;
drm_get_max_frl_rate(dsc_max_frl_rate, &hdmi_dsc->max_lanes,
&hdmi_dsc->max_frl_rate_per_lane);
- hdmi_dsc->total_chunk_kbytes = hf_vsdb[13] & DRM_EDID_DSC_TOTAL_CHUNK_KBYTES;
+ hdmi_dsc->total_chunk_kbytes = hf_scds[13] & DRM_EDID_DSC_TOTAL_CHUNK_KBYTES;
- dsc_max_slices = hf_vsdb[12] & DRM_EDID_DSC_MAX_SLICES;
+ dsc_max_slices = hf_scds[12] & DRM_EDID_DSC_MAX_SLICES;
switch (dsc_max_slices) {
case 1:
hdmi_dsc->max_slices = 1;
@@ -5228,7 +5412,7 @@ static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,
}
}
- drm_parse_ycbcr420_deep_color_info(connector, hf_vsdb);
+ drm_parse_ycbcr420_deep_color_info(connector, hf_scds);
}
static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
@@ -5332,48 +5516,55 @@ static void drm_parse_microsoft_vsdb(struct drm_connector *connector,
}
static void drm_parse_cea_ext(struct drm_connector *connector,
- const struct edid *edid)
+ const struct drm_edid *drm_edid)
{
struct drm_display_info *info = &connector->display_info;
+ struct drm_edid_iter edid_iter;
+ const struct cea_db *db;
+ struct cea_db_iter iter;
const u8 *edid_ext;
- int i, start, end;
- edid_ext = drm_find_cea_extension(edid);
- if (!edid_ext)
- return;
+ drm_edid_iter_begin(drm_edid, &edid_iter);
+ drm_edid_iter_for_each(edid_ext, &edid_iter) {
+ if (edid_ext[0] != CEA_EXT)
+ continue;
- info->cea_rev = edid_ext[1];
+ if (!info->cea_rev)
+ info->cea_rev = edid_ext[1];
- /* The existence of a CEA block should imply RGB support */
- info->color_formats = DRM_COLOR_FORMAT_RGB444;
+ if (info->cea_rev != edid_ext[1])
+ DRM_DEBUG_KMS("CEA extension version mismatch %u != %u\n",
+ info->cea_rev, edid_ext[1]);
- /* CTA DisplayID Data Block does not have byte #3 */
- if (edid_ext[0] == CEA_EXT) {
+ /* The existence of a CTA extension should imply RGB support */
+ info->color_formats = DRM_COLOR_FORMAT_RGB444;
if (edid_ext[3] & EDID_CEA_YCRCB444)
info->color_formats |= DRM_COLOR_FORMAT_YCBCR444;
if (edid_ext[3] & EDID_CEA_YCRCB422)
info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
}
+ drm_edid_iter_end(&edid_iter);
- if (cea_db_offsets(edid_ext, &start, &end))
- return;
-
- for_each_cea_db(edid_ext, i, start, end) {
- const u8 *db = &edid_ext[i];
+ cea_db_iter_edid_begin(drm_edid, &iter);
+ cea_db_iter_for_each(db, &iter) {
+ /* FIXME: convert parsers to use struct cea_db */
+ const u8 *data = (const u8 *)db;
if (cea_db_is_hdmi_vsdb(db))
- drm_parse_hdmi_vsdb_video(connector, db);
- if (cea_db_is_hdmi_forum_vsdb(db))
- drm_parse_hdmi_forum_vsdb(connector, db);
- if (cea_db_is_microsoft_vsdb(db))
- drm_parse_microsoft_vsdb(connector, db);
- if (cea_db_is_y420cmdb(db))
- drm_parse_y420cmdb_bitmap(connector, db);
- if (cea_db_is_vcdb(db))
- drm_parse_vcdb(connector, db);
- if (cea_db_is_hdmi_hdr_metadata_block(db))
- drm_parse_hdr_metadata_block(connector, db);
+ drm_parse_hdmi_vsdb_video(connector, data);
+ else if (cea_db_is_hdmi_forum_vsdb(db) ||
+ cea_db_is_hdmi_forum_scdb(db))
+ drm_parse_hdmi_forum_scds(connector, data);
+ 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);
+ 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);
}
+ cea_db_iter_end(&iter);
}
static
@@ -5400,16 +5591,15 @@ void get_monitor_range(const struct detailed_timing *timing,
monitor_range->max_vfreq = range->max_vfreq;
}
-static
-void drm_get_monitor_range(struct drm_connector *connector,
- const struct edid *edid)
+static void drm_get_monitor_range(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
struct drm_display_info *info = &connector->display_info;
- if (!version_greater(edid, 1, 1))
+ if (!version_greater(drm_edid, 1, 1))
return;
- drm_for_each_detailed_block(edid, get_monitor_range,
+ drm_for_each_detailed_block(drm_edid, get_monitor_range,
&info->monitor_range);
DRM_DEBUG_KMS("Supported Monitor Refresh rate range is %d Hz - %d Hz\n",
@@ -5469,12 +5659,13 @@ static void drm_parse_vesa_mso_data(struct drm_connector *connector,
info->mso_stream_count, info->mso_pixel_overlap);
}
-static void drm_update_mso(struct drm_connector *connector, const struct edid *edid)
+static void drm_update_mso(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
const struct displayid_block *block;
struct displayid_iter iter;
- displayid_iter_edid_begin(edid, &iter);
+ displayid_iter_edid_begin(drm_edid, &iter);
displayid_iter_for_each(block, &iter) {
if (block->tag == DATA_BLOCK_2_VENDOR_SPECIFIC)
drm_parse_vesa_mso_data(connector, block);
@@ -5513,18 +5704,20 @@ drm_reset_display_info(struct drm_connector *connector)
info->mso_pixel_overlap = 0;
}
-u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid)
+static u32 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(edid);
+ u32 quirks = edid_get_quirks(drm_edid);
drm_reset_display_info(connector);
info->width_mm = edid->width_cm * 10;
info->height_mm = edid->height_cm * 10;
- drm_get_monitor_range(connector, edid);
+ drm_get_monitor_range(connector, drm_edid);
if (edid->revision < 3)
goto out;
@@ -5533,7 +5726,7 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi
goto out;
info->color_formats |= DRM_COLOR_FORMAT_RGB444;
- drm_parse_cea_ext(connector, edid);
+ drm_parse_cea_ext(connector, drm_edid);
/*
* Digital sink with "DFP 1.x compliant TMDS" according to EDID 1.3?
@@ -5586,7 +5779,7 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi
if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
- drm_update_mso(connector, edid);
+ drm_update_mso(connector, drm_edid);
out:
if (quirks & EDID_QUIRK_NON_DESKTOP) {
@@ -5598,6 +5791,14 @@ out:
return quirks;
}
+u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid)
+{
+ struct drm_edid drm_edid;
+
+ return update_display_info(connector,
+ drm_edid_legacy_init(&drm_edid, edid));
+}
+
static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev,
struct displayid_detailed_timings_1 *timings,
bool type_7)
@@ -5673,13 +5874,13 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector,
}
static int add_displayid_detailed_modes(struct drm_connector *connector,
- const struct edid *edid)
+ const struct drm_edid *drm_edid)
{
const struct displayid_block *block;
struct displayid_iter iter;
int num_modes = 0;
- displayid_iter_edid_begin(edid, &iter);
+ displayid_iter_edid_begin(drm_edid, &iter);
displayid_iter_for_each(block, &iter) {
if (block->tag == DATA_BLOCK_TYPE_1_DETAILED_TIMING ||
block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING)
@@ -5691,24 +5892,26 @@ static int add_displayid_detailed_modes(struct drm_connector *connector,
}
static int drm_edid_connector_update(struct drm_connector *connector,
- const struct edid *edid)
+ const struct drm_edid *drm_edid)
{
int num_modes = 0;
u32 quirks;
- if (edid == NULL) {
+ if (!drm_edid) {
+ drm_reset_display_info(connector);
clear_eld(connector);
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
* from sink info, before parsing CEA modes.
*/
- quirks = drm_add_display_info(connector, edid);
+ 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:
@@ -5724,15 +5927,15 @@ 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, edid, quirks);
- num_modes += add_cvt_modes(connector, edid);
- num_modes += add_standard_modes(connector, edid);
- num_modes += add_established_modes(connector, edid);
- num_modes += add_cea_modes(connector, edid);
- num_modes += add_alternate_cea_modes(connector, edid);
- num_modes += add_displayid_detailed_modes(connector, edid);
- if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
- num_modes += add_inferred_modes(connector, edid);
+ num_modes += add_detailed_modes(connector, drm_edid, quirks);
+ num_modes += add_cvt_modes(connector, drm_edid);
+ num_modes += add_standard_modes(connector, drm_edid);
+ num_modes += add_established_modes(connector, drm_edid);
+ num_modes += add_cea_modes(connector, drm_edid);
+ num_modes += add_alternate_cea_modes(connector, drm_edid);
+ num_modes += add_displayid_detailed_modes(connector, drm_edid);
+ if (drm_edid->edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
+ 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);
@@ -5765,13 +5968,16 @@ static int drm_edid_connector_update(struct drm_connector *connector,
*/
int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
{
+ struct drm_edid drm_edid;
+
if (edid && !drm_edid_is_valid(edid)) {
drm_warn(connector->dev, "%s: EDID invalid.\n",
connector->name);
edid = NULL;
}
- return drm_edid_connector_update(connector, edid);
+ return drm_edid_connector_update(connector,
+ drm_edid_legacy_init(&drm_edid, edid));
}
EXPORT_SYMBOL(drm_add_edid_modes);
@@ -6166,15 +6372,15 @@ static void drm_parse_tiled_block(struct drm_connector *connector,
}
}
-void drm_update_tile_info(struct drm_connector *connector,
- const struct edid *edid)
+static void _drm_update_tile_info(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
{
const struct displayid_block *block;
struct displayid_iter iter;
connector->has_tile = false;
- displayid_iter_edid_begin(edid, &iter);
+ displayid_iter_edid_begin(drm_edid, &iter);
displayid_iter_for_each(block, &iter) {
if (block->tag == DATA_BLOCK_TILED_DISPLAY)
drm_parse_tiled_block(connector, block);
@@ -6186,3 +6392,11 @@ void drm_update_tile_info(struct drm_connector *connector,
connector->tile_group = NULL;
}
}
+
+void drm_update_tile_info(struct drm_connector *connector,
+ const struct edid *edid)
+{
+ struct drm_edid drm_edid;
+
+ _drm_update_tile_info(connector, drm_edid_legacy_init(&drm_edid, edid));
+}