xf86drm: add iterator API for DRM/KMS IN_FORMATS blobs
authorLuigi Santivetti <luigi.santivetti@imgtec.com>
Thu, 25 Mar 2021 22:42:38 +0000 (22:42 +0000)
committerSimon Ser <contact@emersion.fr>
Mon, 8 Nov 2021 16:20:04 +0000 (16:20 +0000)
Add support for parsing IN_FORMATS property blobs. Providing libdrm
with this functionality helps to standardise how user-space reads
kernel blobs and decreases duplication on the client side.

drmModeFormatModifierBlobIterNext() allows the caller to view
formats and associated modifiers given a valid property blob.
An example is available inside the libdrm unit test, modetest.c.

Signed-off-by: Luigi Santivetti <luigi.santivetti@imgtec.com>
Reviewed-by: Simon Ser <contact@emersion.fr>
core-symbols.txt
tests/modetest/modetest.c
xf86drmMode.c
xf86drmMode.h

index ed0d803..8e72513 100644 (file)
@@ -110,6 +110,7 @@ drmModeCrtcSetGamma
 drmModeDestroyPropertyBlob
 drmModeDetachMode
 drmModeDirtyFB
+drmModeFormatModifierBlobIterNext
 drmModeFreeConnector
 drmModeFreeCrtc
 drmModeFreeEncoder
index 5746357..5fd22f7 100644 (file)
@@ -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);
 }
 
index 0106954..84d3c77 100644 (file)
@@ -33,6 +33,7 @@
  *
  */
 
+#include <assert.h>
 #include <limits.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -50,6 +51,7 @@
 #include "xf86drmMode.h"
 #include "xf86drm.h"
 #include <drm.h>
+#include <drm_fourcc.h>
 #include <string.h>
 #include <dirent.h>
 #include <unistd.h>
@@ -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)
index de0e2fd..19bf91d 100644 (file)
@@ -42,6 +42,7 @@ extern "C" {
 
 #include <drm.h>
 #include <drm_mode.h>
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
 
@@ -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);