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.c165
1 files changed, 110 insertions, 55 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 71ae0cd6d576..b28b95014fe6 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -1616,6 +1616,37 @@ static bool drm_edid_is_zero(const u8 *in_edid, int length)
}
/**
+ * drm_edid_are_equal - compare two edid blobs.
+ * @edid1: pointer to first blob
+ * @edid2: pointer to second blob
+ * This helper can be used during probing to determine if
+ * edid had changed.
+ */
+bool drm_edid_are_equal(const struct edid *edid1, const struct edid *edid2)
+{
+ int edid1_len, edid2_len;
+ bool edid1_present = edid1 != NULL;
+ bool edid2_present = edid2 != NULL;
+
+ if (edid1_present != edid2_present)
+ return false;
+
+ if (edid1) {
+ edid1_len = EDID_LENGTH * (1 + edid1->extensions);
+ edid2_len = EDID_LENGTH * (1 + edid2->extensions);
+
+ if (edid1_len != edid2_len)
+ return false;
+
+ if (memcmp(edid1, edid2, edid1_len))
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL(drm_edid_are_equal);
+
+/**
* drm_edid_block_valid - Sanity check the EDID block (base or extension)
* @raw_edid: pointer to raw EDID block
* @block: type of block to validate (0 for base, extension otherwise)
@@ -1641,6 +1672,7 @@ bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid,
if (block == 0) {
int score = drm_edid_header_is_valid(raw_edid);
+
if (score == 8) {
if (edid_corrupt)
*edid_corrupt = false;
@@ -2017,13 +2049,17 @@ EXPORT_SYMBOL(drm_probe_ddc);
struct edid *drm_get_edid(struct drm_connector *connector,
struct i2c_adapter *adapter)
{
+ struct edid *edid;
+
if (connector->force == DRM_FORCE_OFF)
return NULL;
if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter))
return NULL;
- return drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter);
+ edid = drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter);
+ drm_connector_update_edid_property(connector, edid);
+ return edid;
}
EXPORT_SYMBOL(drm_get_edid);
@@ -2188,6 +2224,7 @@ struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) {
const struct drm_display_mode *ptr = &drm_dmt_modes[i];
+
if (hsize != ptr->hdisplay)
continue;
if (vsize != ptr->vdisplay)
@@ -2259,6 +2296,7 @@ drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure)
for (i = 1; i <= raw_edid[0x7e]; i++) {
u8 *ext = raw_edid + (i * EDID_LENGTH);
+
switch (*ext) {
case CEA_EXT:
cea_for_each_detailed_block(ext, cb, closure);
@@ -2290,6 +2328,7 @@ drm_monitor_supports_rb(struct edid *edid)
{
if (edid->revision >= 4) {
bool ret = false;
+
drm_for_each_detailed_block((u8 *)edid, is_rb, &ret);
return ret;
}
@@ -2314,6 +2353,7 @@ static int
drm_gtf2_hbreak(struct edid *edid)
{
u8 *r = NULL;
+
drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
return r ? (r[12] * 2) : 0;
}
@@ -2322,6 +2362,7 @@ static int
drm_gtf2_2c(struct edid *edid)
{
u8 *r = NULL;
+
drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
return r ? r[13] : 0;
}
@@ -2330,6 +2371,7 @@ static int
drm_gtf2_m(struct edid *edid)
{
u8 *r = NULL;
+
drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
return r ? (r[15] << 8) + r[14] : 0;
}
@@ -2338,6 +2380,7 @@ static int
drm_gtf2_k(struct edid *edid)
{
u8 *r = NULL;
+
drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
return r ? r[16] : 0;
}
@@ -2346,6 +2389,7 @@ static int
drm_gtf2_2j(struct edid *edid)
{
u8 *r = NULL;
+
drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
return r ? r[17] : 0;
}
@@ -2797,6 +2841,7 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
for (i = 0; i < ARRAY_SIZE(extra_modes); i++) {
const struct minimode *m = &extra_modes[i];
+
newmode = drm_gtf_mode(dev, m->w, m->h, m->r, 0, 0);
if (!newmode)
return modes;
@@ -2826,6 +2871,7 @@ drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid,
for (i = 0; i < ARRAY_SIZE(extra_modes); i++) {
const struct minimode *m = &extra_modes[i];
+
newmode = drm_cvt_mode(dev, m->w, m->h, m->r, rb, 0, 0);
if (!newmode)
return modes;
@@ -2961,6 +3007,7 @@ add_established_modes(struct drm_connector *connector, struct edid *edid)
for (i = 0; i <= EDID_EST_TIMINGS; i++) {
if (est_bits & (1<<i)) {
struct drm_display_mode *newmode;
+
newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
if (newmode) {
drm_mode_probed_add(connector, newmode);
@@ -3049,6 +3096,7 @@ static int drm_cvt_modes(struct drm_connector *connector,
for (i = 0; i < 4; i++) {
int uninitialized_var(width), height;
+
cvt = &(timing->data.other_data.data.cvt[i]);
if (!memcmp(cvt->code, empty, 3))
@@ -3188,7 +3236,8 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
/*
* Search EDID for CEA extension block.
*/
-static u8 *drm_find_edid_extension(const struct edid *edid, int ext_id)
+static u8 *drm_find_edid_extension(const struct edid *edid,
+ int ext_id, int *ext_index)
{
u8 *edid_ext = NULL;
int i;
@@ -3198,23 +3247,26 @@ static u8 *drm_find_edid_extension(const struct edid *edid, int ext_id)
return NULL;
/* Find CEA extension */
- for (i = 0; i < edid->extensions; i++) {
+ for (i = *ext_index; i < edid->extensions; i++) {
edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
if (edid_ext[0] == ext_id)
break;
}
- if (i == edid->extensions)
+ if (i >= edid->extensions)
return NULL;
+ *ext_index = i + 1;
+
return edid_ext;
}
static u8 *drm_find_displayid_extension(const struct edid *edid,
- int *length, int *idx)
+ int *length, int *idx,
+ int *ext_index)
{
- u8 *displayid = drm_find_edid_extension(edid, DISPLAYID_EXT);
+ u8 *displayid = drm_find_edid_extension(edid, DISPLAYID_EXT, ext_index);
struct displayid_hdr *base;
int ret;
@@ -3241,26 +3293,31 @@ static u8 *drm_find_cea_extension(const struct edid *edid)
struct displayid_block *block;
u8 *cea;
u8 *displayid;
+ int ext_index;
/* Look for a top level CEA extension block */
- cea = drm_find_edid_extension(edid, CEA_EXT);
+ /* FIXME: make callers iterate through multiple CEA ext blocks? */
+ ext_index = 0;
+ cea = drm_find_edid_extension(edid, CEA_EXT, &ext_index);
if (cea)
return cea;
/* CEA blocks can also be found embedded in a DisplayID block */
- displayid = drm_find_displayid_extension(edid, &length, &idx);
- if (!displayid)
- return NULL;
+ ext_index = 0;
+ for (;;) {
+ displayid = drm_find_displayid_extension(edid, &length, &idx,
+ &ext_index);
+ if (!displayid)
+ return NULL;
- idx += sizeof(struct displayid_hdr);
- for_each_displayid_db(displayid, block, idx, length) {
- if (block->tag == DATA_BLOCK_CTA) {
- cea = (u8 *)block;
- break;
+ idx += sizeof(struct displayid_hdr);
+ for_each_displayid_db(displayid, block, idx, length) {
+ if (block->tag == DATA_BLOCK_CTA)
+ return (u8 *)block;
}
}
- return cea;
+ return NULL;
}
static __always_inline const struct drm_display_mode *cea_mode_for_vic(u8 vic)
@@ -3691,6 +3748,7 @@ do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len)
for (i = 0; i < len; i++) {
struct drm_display_mode *mode;
+
mode = drm_display_mode_from_vic_index(connector, db, len, i);
if (mode) {
/*
@@ -4532,6 +4590,7 @@ int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads)
if (cea_db_tag(db) == AUDIO_BLOCK) {
int j;
+
dbl = cea_db_payload_len(db);
count = dbl / 3; /* SAD is 3B */
@@ -5135,6 +5194,7 @@ static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *d
unsigned vsync_width = (timings->vsw[0] | timings->vsw[1] << 8) + 1;
bool hsync_positive = (timings->hsync[1] >> 7) & 0x1;
bool vsync_positive = (timings->vsync[1] >> 7) & 0x1;
+
mode = drm_mode_create(dev);
if (!mode)
return NULL;
@@ -5195,19 +5255,24 @@ static int add_displayid_detailed_modes(struct drm_connector *connector,
int length, idx;
struct displayid_block *block;
int num_modes = 0;
+ int ext_index = 0;
- displayid = drm_find_displayid_extension(edid, &length, &idx);
- if (!displayid)
- return 0;
-
- idx += sizeof(struct displayid_hdr);
- for_each_displayid_db(displayid, block, idx, length) {
- switch (block->tag) {
- case DATA_BLOCK_TYPE_1_DETAILED_TIMING:
- num_modes += add_displayid_detailed_1_modes(connector, block);
+ for (;;) {
+ displayid = drm_find_displayid_extension(edid, &length, &idx,
+ &ext_index);
+ if (!displayid)
break;
+
+ idx += sizeof(struct displayid_hdr);
+ for_each_displayid_db(displayid, block, idx, length) {
+ switch (block->tag) {
+ case DATA_BLOCK_TYPE_1_DETAILED_TIMING:
+ num_modes += add_displayid_detailed_1_modes(connector, block);
+ break;
+ }
}
}
+
return num_modes;
}
@@ -5316,6 +5381,7 @@ int drm_add_modes_noedid(struct drm_connector *connector,
for (i = 0; i < count; i++) {
const struct drm_display_mode *ptr = &drm_dmt_modes[i];
+
if (hdisplay && vdisplay) {
/*
* Only when two are valid, they will be used to check
@@ -5787,8 +5853,8 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
}
EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode);
-static int drm_parse_tiled_block(struct drm_connector *connector,
- const struct displayid_block *block)
+static void drm_parse_tiled_block(struct drm_connector *connector,
+ const struct displayid_block *block)
{
const struct displayid_tiled_block *tile = (struct displayid_tiled_block *)block;
u16 w, h;
@@ -5822,30 +5888,27 @@ static int drm_parse_tiled_block(struct drm_connector *connector,
DRM_DEBUG_KMS("vend %c%c%c\n", tile->topology_id[0], tile->topology_id[1], tile->topology_id[2]);
tg = drm_mode_get_tile_group(connector->dev, tile->topology_id);
- if (!tg) {
+ if (!tg)
tg = drm_mode_create_tile_group(connector->dev, tile->topology_id);
- }
if (!tg)
- return -ENOMEM;
+ return;
if (connector->tile_group != tg) {
/* if we haven't got a pointer,
take the reference, drop ref to old tile group */
- if (connector->tile_group) {
+ if (connector->tile_group)
drm_mode_put_tile_group(connector->dev, connector->tile_group);
- }
connector->tile_group = tg;
- } else
+ } else {
/* if same tile group, then release the ref we just took. */
drm_mode_put_tile_group(connector->dev, tg);
- return 0;
+ }
}
-static int drm_displayid_parse_tiled(struct drm_connector *connector,
- const u8 *displayid, int length, int idx)
+static void drm_displayid_parse_tiled(struct drm_connector *connector,
+ const u8 *displayid, int length, int idx)
{
const struct displayid_block *block;
- int ret;
idx += sizeof(struct displayid_hdr);
for_each_displayid_db(displayid, block, idx, length) {
@@ -5854,42 +5917,34 @@ static int drm_displayid_parse_tiled(struct drm_connector *connector,
switch (block->tag) {
case DATA_BLOCK_TILED_DISPLAY:
- ret = drm_parse_tiled_block(connector, block);
- if (ret)
- return ret;
+ drm_parse_tiled_block(connector, block);
break;
default:
DRM_DEBUG_KMS("found DisplayID tag 0x%x, unhandled\n", block->tag);
break;
}
}
- return 0;
}
void drm_update_tile_info(struct drm_connector *connector,
const struct edid *edid)
{
const void *displayid = NULL;
+ int ext_index = 0;
int length, idx;
- int ret;
connector->has_tile = false;
- displayid = drm_find_displayid_extension(edid, &length, &idx);
- if (!displayid) {
- /* drop reference to any tile group we had */
- goto out_drop_ref;
+ for (;;) {
+ displayid = drm_find_displayid_extension(edid, &length, &idx,
+ &ext_index);
+ if (!displayid)
+ break;
+
+ drm_displayid_parse_tiled(connector, displayid, length, idx);
}
- ret = drm_displayid_parse_tiled(connector, displayid, length, idx);
- if (ret < 0)
- goto out_drop_ref;
- if (!connector->has_tile)
- goto out_drop_ref;
- return;
-out_drop_ref:
- if (connector->tile_group) {
+ if (!connector->has_tile && connector->tile_group) {
drm_mode_put_tile_group(connector->dev, connector->tile_group);
connector->tile_group = NULL;
}
- return;
}