summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_edid.c
diff options
context:
space:
mode:
authorJani Nikula <jani.nikula@intel.com>2022-05-03 12:23:51 +0300
committerJani Nikula <jani.nikula@intel.com>2022-05-05 20:16:48 +0300
commitaba5825439d7691343aa562dfeaf6616455bcb01 (patch)
tree16d8b8653e85e1d1a69abe6246e94b51ac5e81cc /drivers/gpu/drm/drm_edid.c
parent94afc5382669c3a201c5817c47b57a879b6dabce (diff)
downloadlinux-aba5825439d7691343aa562dfeaf6616455bcb01.tar.bz2
drm/edid: add iterator for CTA data blocks
Add an iterator for CTA Data Blocks across EDID CTA Extensions and DisplayID CTA Data Blocks. v2: Update references, note why we can trust displayid ranges (Ville) Cc: Ville Syrjälä <ville.syrjala@linux.intel.com> Signed-off-by: Jani Nikula <jani.nikula@intel.com> Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/37fdd2d9eabc73aaa9f95c56246dc47aea0e8e4e.1651569697.git.jani.nikula@intel.com
Diffstat (limited to 'drivers/gpu/drm/drm_edid.c')
-rw-r--r--drivers/gpu/drm/drm_edid.c202
1 files changed, 190 insertions, 12 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 6d71b2b77639..da8f455b725e 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -4337,24 +4337,12 @@ 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)
{
/*
@@ -4409,6 +4397,196 @@ cea_db_offsets(const u8 *cea, int *start, int *end)
return 0;
}
+/*
+ * 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 void *_db)
+{
+ /* FIXME: Transition to passing struct cea_db * everywhere. */
+ const struct cea_db *db = _db;
+
+ return db->tag_length >> 5;
+}
+
+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 const void *cea_db_data(const struct cea_db *db)
+{
+ return db->data;
+}
+
+static void cea_db_iter_edid_begin(const struct edid *edid, struct cea_db_iter *iter)
+{
+ memset(iter, 0, sizeof(*iter));
+
+ drm_edid_iter_begin(edid, &iter->edid_iter);
+ displayid_iter_edid_begin(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;
+
+ 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;
+}
+
+/*
+ * 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)
+{
+ const u8 *ext;
+
+ 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 NULL;
+}
+
+/*
+ * 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)
+{
+ const struct displayid_block *block;
+
+ displayid_iter_for_each(block, &iter->displayid_iter) {
+ if (block->tag != DATA_BLOCK_CTA)
+ continue;
+
+ /*
+ * 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 block;
+ }
+
+ return NULL;
+}
+
+static const struct cea_db *__cea_db_iter_next(struct cea_db_iter *iter)
+{
+ const struct cea_db *db;
+
+ 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;
+ }
+
+ /* Next block in CTA Data Block Collection */
+ iter->index += sizeof(*db) + cea_db_payload_len(db);
+
+ 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;
+ }
+}
+
+#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)
+{
+ displayid_iter_end(&iter->displayid_iter);
+ drm_edid_iter_end(&iter->edid_iter);
+
+ memset(iter, 0, sizeof(*iter));
+}
+
static bool cea_db_is_hdmi_vsdb(const u8 *db)
{
if (cea_db_tag(db) != CTA_DB_VENDOR)