From patchwork Sun Oct 6 00:45:49 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Rob Clark X-Patchwork-Id: 2992171 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id EDEA5BF924 for ; Sun, 6 Oct 2013 00:59:36 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id CACB320237 for ; Sun, 6 Oct 2013 00:59:35 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 9BD4C20225 for ; Sun, 6 Oct 2013 00:59:34 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 84AE9E62D5 for ; Sat, 5 Oct 2013 17:59:34 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-qa0-f44.google.com (mail-qa0-f44.google.com [209.85.216.44]) by gabe.freedesktop.org (Postfix) with ESMTP id F39AAE5EB4 for ; Sat, 5 Oct 2013 17:46:29 -0700 (PDT) Received: by mail-qa0-f44.google.com with SMTP id j7so2091224qaq.17 for ; Sat, 05 Oct 2013 17:46:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; bh=BXZFN4BYHecTXa28b6wvRCK3dFG5SjOe17hYDEUqF0s=; b=WckQmgaxLfe7Hq8gh99oPSe+gWKdaa9p+Rrjf4zfJjlCJ8/w7tL++3XjMiDqRqbRCU dv5FiUWOvVf5ywRc6oRUc/VVfpLa8UGua5uBdUYpUsvyioeK7bpWN+7P/nk3pdcFU/HJ PWZ0iHmP6pnFcqu6EOHIyyhjGKajV+1NkeqbWOu0Dj5/5ktZjVCUb/LWtFtGo8htq2Jx MCFZYQzz4Ri4LRDu/jdhmpZs6Cyn3h2CzjO3DiD/OAgxZaJ67WTS9cMi3py5NAstR8BB MrArHz0xa9sirQKMpdkMRMSc0+ZcL/pgOf0RnyvVbh2QmI1PYZT+b3hF1GQUKtg77C8+ PfWw== X-Received: by 10.49.129.65 with SMTP id nu1mr2163197qeb.50.1381020389482; Sat, 05 Oct 2013 17:46:29 -0700 (PDT) Received: from localhost (pool-108-20-247-120.bstnma.east.verizon.net. [108.20.247.120]) by mx.google.com with ESMTPSA id a7sm39304859qew.2.1969.12.31.16.00.00 (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Sat, 05 Oct 2013 17:46:28 -0700 (PDT) From: Rob Clark To: dri-devel@lists.freedesktop.org Subject: [RFCv1 11/12] drm: Atomic modeset ioctl Date: Sat, 5 Oct 2013 20:45:49 -0400 Message-Id: <1381020350-1125-12-git-send-email-robdclark@gmail.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1381020350-1125-1-git-send-email-robdclark@gmail.com> References: <1381020350-1125-1-git-send-email-robdclark@gmail.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org Errors-To: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org X-Spam-Status: No, score=-4.7 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, T_DKIM_INVALID, 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ä The atomic modeset ioctl cna be used to push any number of new values for object properties. The driver can then check the full device configuration as single unit, and try to apply the changes atomically. The ioctl simply takes a list of object IDs and property IDs and their values. For setting values to blob properties, the property value indicates the length of the data, and the actual data is passed via another blob pointer. The caller can demand non-blocking operation from the ioctl, and if the driver can't satisfy that requirement an error will be returned. The caller can also request to receive asynchronous completion events after the operation has reached the hardware. An event is sent for each object specified by the caller, whether or not the actual state of that object changed. Each event also carries a framebuffer ID, which indicates to user space that the specified object is no longer accessing that framebuffer. TODO: detailed error reporting? v1: original v2: rebase on uapi changes, and drm state structs.. -- Rob Clark v3: rebase, missing padding in drm_event_atomic.. -- Rob Clark Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/drm_crtc.c | 159 ++++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_drv.c | 1 + include/drm/drmP.h | 6 ++ include/drm/drm_crtc.h | 2 + include/uapi/drm/drm.h | 12 ++++ include/uapi/drm/drm_mode.h | 16 +++++ 6 files changed, 196 insertions(+) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 09396a9..910e5c6 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -4338,3 +4338,162 @@ void drm_mode_config_cleanup(struct drm_device *dev) idr_destroy(&dev->mode_config.crtc_idr); } EXPORT_SYMBOL(drm_mode_config_cleanup); + +int drm_mode_atomic_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_atomic *arg = data; + uint32_t __user *objs_ptr = (uint32_t __user *)(unsigned long)(arg->objs_ptr); + uint32_t __user *count_props_ptr = (uint32_t __user *)(unsigned long)(arg->count_props_ptr); + uint32_t __user *props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); + uint64_t __user *prop_values_ptr = (uint64_t __user *)(unsigned long)(arg->prop_values_ptr); + uint64_t __user *blob_values_ptr = (uint64_t __user *)(unsigned long)(arg->blob_values_ptr); + unsigned int copied_objs = 0; + unsigned int copied_props = 0; + unsigned int copied_blobs = 0; + void *state; + int ret = 0; + unsigned int i, j; + + if (arg->flags & ~(DRM_MODE_ATOMIC_TEST_ONLY | + DRM_MODE_ATOMIC_EVENT | DRM_MODE_ATOMIC_NONBLOCK)) + return -EINVAL; + + /* can't test and expect an event at the same time. */ + if ((arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) && + (arg->flags & DRM_MODE_ATOMIC_EVENT)) + return -EINVAL; + + mutex_lock(&dev->mode_config.mutex); + + state = dev->driver->atomic_begin(dev, arg->flags); + if (IS_ERR(state)) { + ret = PTR_ERR(state); + goto unlock; + } + + for (i = 0; i < arg->count_objs; i++) { + uint32_t obj_id, count_props; + struct drm_mode_object *obj; + + if (get_user(obj_id, objs_ptr + copied_objs)) { + ret = -EFAULT; + goto out; + } + + obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY); + if (!obj || !obj->properties) { + ret = -ENOENT; + goto out; + } + + if (arg->flags & DRM_MODE_ATOMIC_EVENT) { + // XXX create atomic event instead.. + struct drm_pending_vblank_event *e = + create_vblank_event(dev, file_priv, arg->user_data); + if (!e) { + ret = -ENOMEM; + goto out; + } + ret = dev->driver->atomic_set_event(dev, state, obj, e); + if (ret) { + destroy_vblank_event(dev, file_priv, e); + goto out; + } + } + + if (get_user(count_props, count_props_ptr + copied_objs)) { + ret = -EFAULT; + goto out; + } + + copied_objs++; + + for (j = 0; j < count_props; j++) { + uint32_t prop_id; + uint64_t prop_value; + struct drm_mode_object *prop_obj; + struct drm_property *prop; + void *blob_data = NULL; + + if (get_user(prop_id, props_ptr + copied_props)) { + ret = -EFAULT; + goto out; + } + + if (!object_has_prop(obj, prop_id)) { + ret = -EINVAL; + goto out; + } + + prop_obj = drm_mode_object_find(dev, prop_id, + DRM_MODE_OBJECT_PROPERTY); + if (!prop_obj) { + ret = -ENOENT; + goto out; + } + prop = obj_to_property(prop_obj); + + if (get_user(prop_value, prop_values_ptr + copied_props)) { + ret = -EFAULT; + goto out; + } + + if (!drm_property_change_is_valid(prop, prop_value)) { + ret = -EINVAL; + goto out; + } + + if ((prop->flags & DRM_MODE_PROP_BLOB) && prop_value) { + uint64_t blob_ptr; + + if (get_user(blob_ptr, blob_values_ptr + copied_blobs)) { + ret = -EFAULT; + goto out; + } + + blob_data = kmalloc(prop_value, GFP_KERNEL); + if (!blob_data) { + ret = -ENOMEM; + goto out; + } + + if (copy_from_user(blob_data, (void __user *)(unsigned long)blob_ptr, prop_value)) { + kfree(blob_data); + ret = -EFAULT; + goto out; + } + } + + /* User space sends the blob pointer even if we + * don't use it (length==0). + */ + if (prop->flags & DRM_MODE_PROP_BLOB) + copied_blobs++; + + /* The driver will be in charge of blob_data from now on. */ + ret = drm_mode_set_obj_prop(obj, state, prop, + prop_value, blob_data); + if (ret) + goto out; + + copied_props++; + } + } + + ret = dev->driver->atomic_check(dev, state); + if (ret) + goto out; + + if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) + goto out; + + ret = dev->driver->atomic_commit(dev, state); + + out: + dev->driver->atomic_end(dev, state); + unlock: + mutex_unlock(&dev->mode_config.mutex); + + return ret; +} diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index e572dd2..43e04ae 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -166,6 +166,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 4c3de22..f282733 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1158,6 +1158,12 @@ struct drm_pending_vblank_event { struct drm_event_vblank event; }; +struct drm_pending_atomic_event { + struct drm_pending_event base; + int pipe; + struct drm_event_atomic event; +}; + /** * DRM device structure. This structure represent a complete card that * may contain multiple heads. diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 86a5a00..fa3956e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1313,6 +1313,8 @@ extern int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int drm_mode_atomic_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); extern void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, int *bpp); diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index ece8678..efb1461 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -733,6 +733,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 @@ -774,6 +775,17 @@ struct drm_event_vblank { __u32 reserved; }; +struct drm_event_atomic { + struct drm_event base; + __u64 user_data; + __u32 tv_sec; + __u32 tv_usec; + __u32 sequence; + __u32 obj_id; + __u32 old_fb_id; + __u32 reserved; +}; + #define DRM_CAP_DUMB_BUFFER 0x1 #define DRM_CAP_VBLANK_HIGH_CRTC 0x2 #define DRM_CAP_DUMB_PREFERRED_DEPTH 0x3 diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 6d4f089..03a473c 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -489,4 +489,20 @@ struct drm_mode_destroy_dumb { uint32_t handle; }; +#define DRM_MODE_ATOMIC_TEST_ONLY (1<<0) +#define DRM_MODE_ATOMIC_EVENT (1<<1) +#define DRM_MODE_ATOMIC_NONBLOCK (1<<2) + +/* FIXME come up with some sane error reporting mechanism? */ +struct drm_mode_atomic { + __u32 flags; + __u32 count_objs; + __u64 objs_ptr; + __u64 count_props_ptr; + __u64 props_ptr; + __u64 prop_values_ptr; + __u64 blob_values_ptr; + __u64 user_data; +}; + #endif