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@xxxxxxxxx> > --- > 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@xxxxxxxxxxxxxxx> > + > + 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 -- Ville Syrjälä Intel