diff --git a/core-symbols.txt b/core-symbols.txt index ed0d8035..8e725132 100644 --- a/core-symbols.txt +++ b/core-symbols.txt @@ -110,6 +110,7 @@ drmModeCrtcSetGamma drmModeDestroyPropertyBlob drmModeDetachMode drmModeDirtyFB +drmModeFormatModifierBlobIterNext drmModeFreeConnector drmModeFreeCrtc drmModeFreeEncoder diff --git a/tests/modetest/modetest.c b/tests/modetest/modetest.c index 5746357f..5fd22f79 100644 --- a/tests/modetest/modetest.c +++ b/tests/modetest/modetest.c @@ -300,11 +300,9 @@ static const char *modifier_to_string(uint64_t modifier) static void dump_in_formats(struct device *dev, uint32_t blob_id) { - uint32_t i, j; + drmModeFormatModifierIterator iter = {0}; drmModePropertyBlobPtr blob; - struct drm_format_modifier_blob *header; - uint32_t *formats; - struct drm_format_modifier *modifiers; + uint32_t fmt = 0; printf("\t\tin_formats blob decoded:\n"); blob = drmModeGetPropertyBlob(dev->fd, blob_id); @@ -313,23 +311,19 @@ static void dump_in_formats(struct device *dev, uint32_t blob_id) return; } - header = blob->data; - formats = (uint32_t *) ((char *) header + header->formats_offset); - modifiers = (struct drm_format_modifier *) - ((char *) header + header->modifiers_offset); - - for (i = 0; i < header->count_formats; i++) { - printf("\t\t\t"); - dump_fourcc(formats[i]); - printf(": "); - for (j = 0; j < header->count_modifiers; j++) { - uint64_t mask = 1ULL << i; - if (modifiers[j].formats & mask) - printf(" %s", modifier_to_string(modifiers[j].modifier)); + while (drmModeFormatModifierBlobIterNext(blob, &iter)) { + if (!fmt || fmt != iter.fmt) { + printf("%s\t\t\t", !fmt ? "" : "\n"); + fmt = iter.fmt; + dump_fourcc(fmt); + printf(": "); } - printf("\n"); + + printf(" %s", modifier_to_string(iter.mod)); } + printf("\n"); + drmModeFreePropertyBlob(blob); } diff --git a/xf86drmMode.c b/xf86drmMode.c index 0106954b..84d3c771 100644 --- a/xf86drmMode.c +++ b/xf86drmMode.c @@ -33,6 +33,7 @@ * */ +#include #include #include #include @@ -50,6 +51,7 @@ #include "xf86drmMode.h" #include "xf86drm.h" #include +#include #include #include #include @@ -727,6 +729,112 @@ err_allocs: return r; } +static inline const uint32_t * +get_formats_ptr(const struct drm_format_modifier_blob *blob) +{ + return (const uint32_t *)(((uint8_t *)blob) + blob->formats_offset); +} + +static inline const struct drm_format_modifier * +get_modifiers_ptr(const struct drm_format_modifier_blob *blob) +{ + return (const struct drm_format_modifier *)(((uint8_t *)blob) + + blob->modifiers_offset); +} + +static bool _drmModeFormatModifierGetNext(const drmModePropertyBlobRes *blob, + drmModeFormatModifierIterator *iter) +{ + const struct drm_format_modifier *blob_modifiers, *mod; + const struct drm_format_modifier_blob *fmt_mod_blob; + const uint32_t *blob_formats; + + assert(blob && iter); + + fmt_mod_blob = blob->data; + blob_modifiers = get_modifiers_ptr(fmt_mod_blob); + blob_formats = get_formats_ptr(fmt_mod_blob); + + /* fmt_idx and mod_idx designate the number of processed formats + * and modifiers. + */ + if (iter->fmt_idx >= fmt_mod_blob->count_formats || + iter->mod_idx >= fmt_mod_blob->count_modifiers) + return false; + + iter->fmt = blob_formats[iter->fmt_idx]; + iter->mod = DRM_FORMAT_MOD_INVALID; + + /* From the latest valid found, get the next valid modifier */ + while (iter->mod_idx < fmt_mod_blob->count_modifiers) { + mod = &blob_modifiers[iter->mod_idx++]; + + /* Check if the format that fmt_idx designates, belongs to + * this modifier 64-bit window selected via mod->offset. + */ + if (iter->fmt_idx < mod->offset || + iter->fmt_idx >= mod->offset + 64) + continue; + if (!(mod->formats & (1 << (iter->fmt_idx - mod->offset)))) + continue; + + iter->mod = mod->modifier; + break; + } + + if (iter->mod_idx == fmt_mod_blob->count_modifiers) { + iter->mod_idx = 0; + iter->fmt_idx++; + } + + /* Since mod_idx reset, in order for the caller to iterate over + * the last modifier of the last format, always return true here + * and early return from the next call. + */ + return true; +} + +/** + * Iterate over formats first and then over modifiers. On each call, iter->fmt + * is retained until all associated modifiers are returned. Then, either update + * iter->fmt with the next format, or exit if there aren't any left. + * + * NOTE: clients should not make any assumption on mod_idx and fmt_idx values + * + * @blob: valid kernel blob holding formats and modifiers + * @iter: input and output iterator data. Iter data must be initialised to zero + * @return: false, on error or there aren't any further formats or modifiers left. + * true, on success and there are more formats or modifiers. + */ +drm_public bool drmModeFormatModifierBlobIterNext(const drmModePropertyBlobRes *blob, + drmModeFormatModifierIterator *iter) +{ + drmModeFormatModifierIterator tmp; + bool has_fmt; + + if (!blob || !iter) + return false; + + tmp.fmt_idx = iter->fmt_idx; + tmp.mod_idx = iter->mod_idx; + + /* With the current state of things, DRM/KMS drivers are allowed to + * construct blobs having formats and no modifiers. Userspace can't + * legitimately abort in such cases. + * + * While waiting for the kernel to perhaps disallow formats with no + * modifiers in IN_FORMATS blobs, skip the format altogether. + */ + do { + has_fmt = _drmModeFormatModifierGetNext(blob, &tmp); + if (has_fmt && tmp.mod != DRM_FORMAT_MOD_INVALID) + *iter = tmp; + + } while (has_fmt && tmp.mod == DRM_FORMAT_MOD_INVALID); + + return has_fmt; +} + drm_public void drmModeFreePropertyBlob(drmModePropertyBlobPtr ptr) { if (!ptr) diff --git a/xf86drmMode.h b/xf86drmMode.h index de0e2fdb..19bf91dd 100644 --- a/xf86drmMode.h +++ b/xf86drmMode.h @@ -42,6 +42,7 @@ extern "C" { #include #include +#include #include #include @@ -231,6 +232,12 @@ typedef struct _drmModeObjectProperties { uint64_t *prop_values; } drmModeObjectProperties, *drmModeObjectPropertiesPtr; +typedef struct _drmModeFormatModifierIterator { + uint32_t fmt_idx, mod_idx; + uint32_t fmt; + uint64_t mod; +} drmModeFormatModifierIterator; + typedef struct _drmModePlane { uint32_t count_formats; uint32_t *formats; @@ -388,6 +395,8 @@ extern drmModePropertyPtr drmModeGetProperty(int fd, uint32_t propertyId); extern void drmModeFreeProperty(drmModePropertyPtr ptr); extern drmModePropertyBlobPtr drmModeGetPropertyBlob(int fd, uint32_t blob_id); +extern bool drmModeFormatModifierBlobIterNext(const drmModePropertyBlobRes *blob, + drmModeFormatModifierIterator *iter); extern void drmModeFreePropertyBlob(drmModePropertyBlobPtr ptr); extern int drmModeConnectorSetProperty(int fd, uint32_t connector_id, uint32_t property_id, uint64_t value);