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
This commit is contained in:
@@ -4336,24 +4336,12 @@ out:
|
||||
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)
|
||||
{
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user