diff mbox series

[RFC,05/19] drm/edid: add iterator for CEA data blocks

Message ID 6da67bfcb883daf80910a49dbc1bd112ca51345d.1647985054.git.jani.nikula@intel.com (mailing list archive)
State New, archived
Headers show
Series drm/edid: overhaul CEA data block iteration | expand

Commit Message

Jani Nikula March 22, 2022, 9:40 p.m. UTC
Add an iterator for CEA Data Blocks across CEA extensions and CTA
DisplayID Data Blocks.

Signed-off-by: Jani Nikula <jani.nikula@intel.com>
---
 drivers/gpu/drm/drm_edid.c | 198 ++++++++++++++++++++++++++++++++++---
 1 file changed, 186 insertions(+), 12 deletions(-)

Comments

Ville Syrjälä March 23, 2022, 4:05 p.m. UTC | #1
On Tue, Mar 22, 2022 at 11:40:34PM +0200, Jani Nikula wrote:
> Add an iterator for CEA Data Blocks across CEA extensions and CTA
> DisplayID Data Blocks.
> 
> Signed-off-by: Jani Nikula <jani.nikula@intel.com>
> ---
>  drivers/gpu/drm/drm_edid.c | 198 ++++++++++++++++++++++++++++++++++---
>  1 file changed, 186 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 31d132fcd0ca..c12c3cbab274 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -4196,24 +4196,12 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len,
>  	return modes;
>  }
>  
> -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)
>  {
> @@ -4269,6 +4257,192 @@ cea_db_offsets(const u8 *cea, int *start, int *end)
>  	return 0;
>  }
>  
> +/*
> + * CEA Data Block iterator.
> + *
> + * Iterate through all CEA Data Blocks in both EDID CEA extensions and CTA Data
> + * Blocks in DisplayID extension 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;
> +};
> +
> +/* CEA-861-F section 7.5 CEA Extension Version 3 and Table 43 */
> +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
> + * - CEA-861-F section 7.5 CEA 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 CEA 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 CEA 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;
> +
> +		iter->index = sizeof(*block);
> +		iter->end = iter->index + block->num_bytes;

I'd like to keep the comment from cea_db_offsets() reminding
us why we  can trust this thing.

Overall looks pretty nice to my eyes.
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>

> +
> +		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 CEA 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 CEA Data Block Collection. First iterate all
> +		 * the EDID CEA 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 CEA 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) != CEA_DB_VENDOR)
> -- 
> 2.30.2
diff mbox series

Patch

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 31d132fcd0ca..c12c3cbab274 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -4196,24 +4196,12 @@  do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len,
 	return modes;
 }
 
-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)
 {
@@ -4269,6 +4257,192 @@  cea_db_offsets(const u8 *cea, int *start, int *end)
 	return 0;
 }
 
+/*
+ * CEA Data Block iterator.
+ *
+ * Iterate through all CEA Data Blocks in both EDID CEA extensions and CTA Data
+ * Blocks in DisplayID extension 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;
+};
+
+/* CEA-861-F section 7.5 CEA Extension Version 3 and Table 43 */
+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
+ * - CEA-861-F section 7.5 CEA 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 CEA 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 CEA 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;
+
+		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 CEA 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 CEA Data Block Collection. First iterate all
+		 * the EDID CEA 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 CEA 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) != CEA_DB_VENDOR)