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 |
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 --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)
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(-)