From patchwork Mon Jun 22 16:26:02 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Daniel Stone X-Patchwork-Id: 6657191 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id C757D9F399 for ; Mon, 22 Jun 2015 16:26:12 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 861C8205FA for ; Mon, 22 Jun 2015 16:26:11 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id EFC0F203B8 for ; Mon, 22 Jun 2015 16:26:09 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id D745F6E831; Mon, 22 Jun 2015 09:26:08 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [93.93.135.160]) by gabe.freedesktop.org (Postfix) with ESMTP id B7FD86E831 for ; Mon, 22 Jun 2015 09:26:07 -0700 (PDT) Received: from [127.0.0.1] (localhost [127.0.0.1]) (Authenticated sender: daniels) with ESMTPSA id 6786B518802F From: Daniel Stone To: dri-devel@lists.freedesktop.org Subject: [PATCH v2 libdrm 1/2] Support atomic modesetting ioctl Date: Mon, 22 Jun 2015 17:26:02 +0100 Message-Id: <1434990363-5052-1-git-send-email-daniels@collabora.com> X-Mailer: git-send-email 2.4.3 MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Spam-Status: No, score=-5.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Ville Syrjälä 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ä Signed-off-by: Rob Clark Signed-off-by: Daniel Stone --- include/drm/drm.h | 9 ++ include/drm/drm_mode.h | 16 ++++ xf86drmMode.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++ xf86drmMode.h | 19 ++++ 4 files changed, 284 insertions(+) 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 #include +#include #include #include +#include #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