diff mbox

[v2,libdrm,1/2] Support atomic modesetting ioctl

Message ID 1434990363-5052-1-git-send-email-daniels@collabora.com (mailing list archive)
State New, archived
Headers show

Commit Message

Daniel Stone June 22, 2015, 4:26 p.m. UTC
From: Ville Syrjälä <ville.syrjala@linux.intel.com>

Add support for the atomic modesetting ioctl through a property-set API.

v1: Squashed intermediate patches from Ville, Rob and myself. Updated
    for current kernel interface (no blobs).
v2: Rewrite user-facing API to provide transactional/cursor interface.
    Use memclear to zero out ioctl.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Rob Clark <robclark@freedesktop.org>
Signed-off-by: Daniel Stone <daniels@collabora.com>
---
 include/drm/drm.h      |   9 ++
 include/drm/drm_mode.h |  16 ++++
 xf86drmMode.c          | 240 +++++++++++++++++++++++++++++++++++++++++++++++++
 xf86drmMode.h          |  19 ++++
 4 files changed, 284 insertions(+)
diff mbox

Patch

diff --git a/include/drm/drm.h b/include/drm/drm.h
index 229a29f..0b1d2ef 100644
--- a/include/drm/drm.h
+++ b/include/drm/drm.h
@@ -635,6 +635,13 @@  struct drm_get_cap {
  */
 #define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2
 
+/**
+ * DRM_CLIENT_CAP_ATOMIC
+ *
+ * If set to 1, the DRM core will allow atomic modesetting requests.
+ */
+#define DRM_CLIENT_CAP_ATOMIC		3
+
 /** DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */
 struct drm_set_client_cap {
 	__u64 capability;
@@ -758,6 +765,7 @@  struct drm_prime_handle {
 #define DRM_IOCTL_MODE_OBJ_GETPROPERTIES	DRM_IOWR(0xB9, struct drm_mode_obj_get_properties)
 #define DRM_IOCTL_MODE_OBJ_SETPROPERTY	DRM_IOWR(0xBA, struct drm_mode_obj_set_property)
 #define DRM_IOCTL_MODE_CURSOR2		DRM_IOWR(0xBB, struct drm_mode_cursor2)
+#define DRM_IOCTL_MODE_ATOMIC		DRM_IOWR(0xBC, struct drm_mode_atomic)
 
 /**
  * Device specific ioctls should only be in their respective headers
@@ -806,6 +814,7 @@  struct drm_event_vblank {
 #define DRM_CAP_PRIME 0x5
 #define DRM_CAP_TIMESTAMP_MONOTONIC 0x6
 #define DRM_CAP_ASYNC_PAGE_FLIP 0x7
+#define DRM_CAP_ATOMIC 0xa
 
 #define DRM_PRIME_CAP_IMPORT 0x1
 #define DRM_PRIME_CAP_EXPORT 0x2
diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h
index a2ab88a..66f856f 100644
--- a/include/drm/drm_mode.h
+++ b/include/drm/drm_mode.h
@@ -507,4 +507,20 @@  struct drm_mode_destroy_dumb {
 	__u32 handle;
 };
 
+/* page-flip flags are valid, plus: */
+#define DRM_MODE_ATOMIC_TEST_ONLY	0x0100
+#define DRM_MODE_ATOMIC_NONBLOCK	0x0200
+#define DRM_MODE_ATOMIC_ALLOW_MODESET	0x0400
+
+struct drm_mode_atomic {
+	__u32 flags;
+	__u32 count_objs;
+	__u64 objs_ptr;
+	__u64 count_props_ptr;
+	__u64 props_ptr;
+	__u64 prop_values_ptr;
+	__u64 reserved;
+	__u64 user_data;
+};
+
 #endif
diff --git a/xf86drmMode.c b/xf86drmMode.c
index 1333da4..a75eca3 100644
--- a/xf86drmMode.c
+++ b/xf86drmMode.c
@@ -37,9 +37,12 @@ 
  * TODO the types we are after are defined in diffrent headers on diffrent
  * platforms find which headers to include to get uint32_t
  */
+#include <limits.h>
 #include <stdint.h>
+#include <stdlib.h>
 #include <sys/ioctl.h>
 #include <stdio.h>
+#include <stdbool.h>
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -1147,3 +1150,240 @@  int drmModeObjectSetProperty(int fd, uint32_t object_id, uint32_t object_type,
 
 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_OBJ_SETPROPERTY, &prop);
 }
+
+typedef struct _drmModeAtomicReqItem drmModeAtomicReqItem, *drmModeAtomicReqItemPtr;
+
+struct _drmModeAtomicReqItem {
+	uint32_t object_id;
+	uint32_t property_id;
+	uint64_t value;
+};
+
+struct _drmModeAtomicReq {
+	uint32_t cursor;
+	uint32_t size_items;
+	drmModeAtomicReqItemPtr items;
+};
+
+drmModeAtomicReqPtr drmModeAtomicAlloc(void)
+{
+	drmModeAtomicReqPtr req;
+
+	req = drmMalloc(sizeof *req);
+	if (!req)
+		return NULL;
+
+	req->items = NULL;
+	req->cursor = 0;
+	req->size_items = 0;
+
+	return req;
+}
+
+drmModeAtomicReqPtr drmModeAtomicDuplicate(drmModeAtomicReqPtr old)
+{
+	drmModeAtomicReqPtr new;
+
+	new = drmMalloc(sizeof *new);
+	if (!new)
+		return NULL;
+
+	new->cursor = old->cursor;
+	new->size_items = old->size_items;
+
+	if (old->size_items) {
+		new->items = drmMalloc(old->size_items * sizeof(*new->items));
+		if (!new->items) {
+			free(new);
+			return NULL;
+		}
+		memcpy(new->items, old->items,
+		       old->size_items * sizeof(*new->items));
+	} else {
+		new->items = NULL;
+	}
+
+	return new;
+}
+
+int drmModeAtomicMerge(drmModeAtomicReqPtr base, drmModeAtomicReqPtr augment)
+{
+	if (!augment || augment->cursor == 0)
+		return 0;
+
+	if (base->cursor + augment->cursor >= base->size_items) {
+		drmModeAtomicReqItemPtr new;
+		int saved_size = base->size_items;
+
+		base->size_items = base->cursor + augment->cursor;
+		new = realloc(base->items,
+			      base->size_items * sizeof(*base->items));
+		if (!new) {
+			base->size_items = saved_size;
+			return -ENOMEM;
+		}
+		base->items = new;
+	}
+
+	memcpy(&base->items[base->cursor], augment->items,
+	       augment->cursor * sizeof(*augment->items));
+	base->cursor += augment->cursor;
+
+	return 0;
+}
+
+int drmModeAtomicGetCursor(drmModeAtomicReqPtr req)
+{
+	return req->cursor;
+}
+
+void drmModeAtomicSetCursor(drmModeAtomicReqPtr req, int cursor)
+{
+	req->cursor = cursor;
+}
+
+int drmModeAtomicAddProperty(drmModeAtomicReqPtr req,
+			     uint32_t object_id,
+			     uint32_t property_id,
+			     uint64_t value)
+{
+	if (req->cursor >= req->size_items) {
+		drmModeAtomicReqItemPtr new;
+
+		req->size_items += 16;
+		new = realloc(req->items, req->size_items * sizeof(*req->items));
+		if (!new) {
+			req->size_items -= 16;
+			return -ENOMEM;
+		}
+		req->items = new;
+	}
+
+	req->items[req->cursor].object_id = object_id;
+	req->items[req->cursor].property_id = property_id;
+	req->items[req->cursor].value = value;
+	req->cursor++;
+
+	return req->cursor;
+}
+
+void drmModeAtomicFree(drmModeAtomicReqPtr req)
+{
+	if (!req)
+		return;
+
+	if (req->items)
+		drmFree(req->items);
+	drmFree(req);
+}
+
+static int sort_req_list(const void *misc, const void *other)
+{
+	const drmModeAtomicReqItem *first = misc;
+	const drmModeAtomicReqItem *second = other;
+
+	if (first->object_id < second->object_id)
+		return -1;
+	else if (first->object_id > second->object_id)
+		return 1;
+	else
+		return second->property_id - first->property_id;
+}
+
+int drmModeAtomicCommit(int fd, drmModeAtomicReqPtr req, uint32_t flags,
+			void *user_data)
+{
+	drmModeAtomicReqPtr sorted = drmModeAtomicDuplicate(req);
+	struct drm_mode_atomic atomic;
+	uint32_t *objs_ptr = NULL;
+	uint32_t *count_props_ptr = NULL;
+	uint32_t *props_ptr = NULL;
+	uint64_t *prop_values_ptr = NULL;
+	uint32_t last_obj_id = 0;
+	uint32_t i;
+	int obj_idx = -1;
+	int ret = -1;
+
+	if (!sorted)
+		return -ENOMEM;
+
+	memclear(atomic);
+
+	/* Sort the list by object ID, then by property ID. */
+	qsort(sorted->items, sorted->cursor, sizeof(*sorted->items),
+	      sort_req_list);
+
+	/* Now the list is sorted, eliminate duplicate property sets. */
+	for (i = 0; i < sorted->cursor; i++) {
+		if (sorted->items[i].object_id != last_obj_id) {
+			atomic.count_objs++;
+			last_obj_id = sorted->items[i].object_id;
+		}
+
+		if (i == sorted->cursor - 1)
+			continue;
+
+		if (sorted->items[i].object_id != sorted->items[i + 1].object_id ||
+		    sorted->items[i].property_id != sorted->items[i + 1].property_id)
+			continue;
+
+		memmove(&sorted->items[i], &sorted->items[i + 1],
+			(sorted->cursor - i - 1) * sizeof(*sorted->items));
+		sorted->cursor--;
+	}
+
+	objs_ptr = drmMalloc(atomic.count_objs * sizeof objs_ptr[0]);
+	if (!objs_ptr) {
+		errno = ENOMEM;
+		goto out;
+	}
+
+	count_props_ptr = drmMalloc(atomic.count_objs * sizeof count_props_ptr[0]);
+	if (!count_props_ptr) {
+		errno = ENOMEM;
+		goto out;
+	}
+
+	props_ptr = drmMalloc(sorted->cursor * sizeof props_ptr[0]);
+	if (!props_ptr) {
+		errno = ENOMEM;
+		goto out;
+	}
+
+	prop_values_ptr = drmMalloc(sorted->cursor * sizeof prop_values_ptr[0]);
+	if (!prop_values_ptr) {
+		errno = ENOMEM;
+		goto out;
+	}
+
+	for (i = 0, last_obj_id = 0; i < sorted->cursor; i++) {
+		if (sorted->items[i].object_id != last_obj_id) {
+			obj_idx++;
+			objs_ptr[obj_idx] = sorted->items[i].object_id;
+			last_obj_id = objs_ptr[obj_idx];
+		}
+
+		count_props_ptr[obj_idx]++;
+		props_ptr[i] = sorted->items[i].property_id;
+		prop_values_ptr[i] = sorted->items[i].value;
+
+	}
+
+	atomic.flags = flags;
+	atomic.objs_ptr = VOID2U64(objs_ptr);
+	atomic.count_props_ptr = VOID2U64(count_props_ptr);
+	atomic.props_ptr = VOID2U64(props_ptr);
+	atomic.prop_values_ptr = VOID2U64(prop_values_ptr);
+	atomic.user_data = VOID2U64(user_data);
+
+	ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ATOMIC, &atomic);
+
+out:
+	drmFree(objs_ptr);
+	drmFree(count_props_ptr);
+	drmFree(props_ptr);
+	drmFree(prop_values_ptr);
+	drmModeAtomicFree(sorted);
+
+	return ret;
+}
diff --git a/xf86drmMode.h b/xf86drmMode.h
index 20c3f15..317ea23 100644
--- a/xf86drmMode.h
+++ b/xf86drmMode.h
@@ -484,6 +484,25 @@  extern int drmModeObjectSetProperty(int fd, uint32_t object_id,
 				    uint32_t object_type, uint32_t property_id,
 				    uint64_t value);
 
+
+typedef struct _drmModeAtomicReq drmModeAtomicReq, *drmModeAtomicReqPtr;
+
+extern drmModeAtomicReqPtr drmModeAtomicAlloc(void);
+extern drmModeAtomicReqPtr drmModeAtomicDuplicate(drmModeAtomicReqPtr req);
+extern int drmModeAtomicMerge(drmModeAtomicReqPtr base,
+			      drmModeAtomicReqPtr augment);
+extern void drmModeAtomicFree(drmModeAtomicReqPtr req);
+extern int drmModeAtomicGetCursor(drmModeAtomicReqPtr req);
+extern void drmModeAtomicSetCursor(drmModeAtomicReqPtr req, int cursor);
+extern int drmModeAtomicAddProperty(drmModeAtomicReqPtr req,
+				    uint32_t object_id,
+				    uint32_t property_id,
+				    uint64_t value);
+extern int drmModeAtomicCommit(int fd,
+			       drmModeAtomicReqPtr req,
+			       uint32_t flags,
+			       void *user_data);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif