From patchwork Sat May 24 18:30:20 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rob Clark X-Patchwork-Id: 4237921 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 EF27CBF90B for ; Sat, 24 May 2014 18:31:14 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 7C8142020F for ; Sat, 24 May 2014 18:31:12 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id CBB17201C8 for ; Sat, 24 May 2014 18:31:09 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id EE2466E43F; Sat, 24 May 2014 11:31:08 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-qc0-f177.google.com (mail-qc0-f177.google.com [209.85.216.177]) by gabe.freedesktop.org (Postfix) with ESMTP id AEFAA6E45E for ; Sat, 24 May 2014 11:31:07 -0700 (PDT) Received: by mail-qc0-f177.google.com with SMTP id i17so9841142qcy.8 for ; Sat, 24 May 2014 11:31:07 -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; bh=mak0KAxeeEUvReBRtDOr+CvVqLu24bkZL6KIr+olVSE=; b=rQjnbp6BTowKdPX0/N6L5ztcvgFyuBNFB4S/oy+XPY9qDCsbHqIPode1vV7Hk6FPHU 0C7RdfxZiJayeQOZPN9UE0JCrfPGzQnsgk4A2Dl6qUjrouiARtkO9mwnowtA8xk+ASvY 8+pDh1PTYtmGblcNgZWYCy27W9H4MX1/xORxdzDljkm0du2APVpiXHl8sGbgcWkRhfWZ tR03vpWv4nNRwD8Dkc+xeXGAuBvBxOWpN99iotL9ohhzkeiLMlAn0kOykGCoPOTIkew8 08Qo6LESXLFXUD5+11uWDOlbLLzHEolDGjaGkVJkkCCX9Q/7PU+dqE7Z9H0jfhBUD6Fo 67pw== X-Received: by 10.229.125.130 with SMTP id y2mr18469196qcr.22.1400956267268; Sat, 24 May 2014 11:31:07 -0700 (PDT) Received: from localhost (pool-108-20-252-175.bstnma.east.verizon.net. [108.20.252.175]) by mx.google.com with ESMTPSA id r14sm4466324qga.4.2014.05.24.11.31.05 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 24 May 2014 11:31:06 -0700 (PDT) From: Rob Clark To: dri-devel@lists.freedesktop.org Subject: [PATCH 11/17] drm: convert plane to properties/state Date: Sat, 24 May 2014 14:30:20 -0400 Message-Id: <1400956226-28053-12-git-send-email-robdclark@gmail.com> X-Mailer: git-send-email 1.9.0 In-Reply-To: <1400956226-28053-1-git-send-email-robdclark@gmail.com> References: <1400956226-28053-1-git-send-email-robdclark@gmail.com> X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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 Break the mutable state of a plane out into a separate structure and use atomic properties mechanism to set plane attributes. This makes it easier to have some helpers for plane->set_property() and for checking for invalid params. The idea is that individual drivers can wrap the state struct in their own struct which adds driver specific parameters, for easy build-up of state across multiple set_property() calls and for easy atomic commit or roll- back. The same should be done for CRTC, encoder, and connector, but this patch only includes the first part (plane). Signed-off-by: Rob Clark --- drivers/gpu/drm/armada/armada_overlay.c | 11 +- drivers/gpu/drm/drm_atomic.c | 225 ++++++++++++++- drivers/gpu/drm/drm_crtc.c | 433 ++++++++++++++++++++--------- drivers/gpu/drm/drm_fb_helper.c | 17 +- drivers/gpu/drm/drm_plane_helper.c | 2 + drivers/gpu/drm/exynos/exynos_drm_plane.c | 7 +- drivers/gpu/drm/i915/intel_sprite.c | 1 + drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c | 6 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 6 +- drivers/gpu/drm/nouveau/dispnv04/overlay.c | 8 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/omapdrm/omap_plane.c | 7 + drivers/gpu/drm/rcar-du/rcar_du_plane.c | 8 +- drivers/gpu/drm/shmobile/shmob_drm_plane.c | 2 + include/drm/drm_atomic.h | 29 +- include/drm/drm_crtc.h | 114 +++++++- 16 files changed, 725 insertions(+), 153 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c index 601ba9a..041ea89 100644 --- a/drivers/gpu/drm/armada/armada_overlay.c +++ b/drivers/gpu/drm/armada/armada_overlay.c @@ -7,6 +7,7 @@ * published by the Free Software Foundation. */ #include +#include #include "armada_crtc.h" #include "armada_drm.h" #include "armada_fb.h" @@ -288,7 +289,12 @@ static int armada_plane_set_property(struct drm_plane *plane, { struct armada_private *priv = plane->dev->dev_private; struct armada_plane *dplane = drm_to_armada_plane(plane); + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); bool update_attr = false; + int ret = 0; + + if (IS_ERR(pstate)) + return PTR_ERR(pstate); if (property == priv->colorkey_prop) { #define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8) @@ -342,13 +348,16 @@ static int armada_plane_set_property(struct drm_plane *plane, } else if (property == priv->saturation_prop) { dplane->prop.saturation = val; update_attr = true; + } else { + ret = drm_plane_set_property(plane, pstate, property, + val, blob_data); } if (update_attr && dplane->base.crtc) armada_ovl_update_attr(&dplane->prop, drm_to_armada_crtc(dplane->base.crtc)); - return 0; + return ret; } static const struct drm_plane_funcs armada_plane_funcs = { diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 45df5e5..403ffc5 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -24,6 +24,7 @@ #include #include +#include /** * drm_atomic_begin - start a sequence of atomic updates @@ -46,10 +47,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, uint32_t flags) { struct drm_atomic_state *state; + int nplanes = dev->mode_config.num_total_plane; int sz; void *ptr; sz = sizeof(*state); + sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes; ptr = kzalloc(sz, GFP_KERNEL); @@ -65,6 +68,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev, state->dev = dev; state->flags = flags; + state->planes = ptr; + ptr = &state->planes[nplanes]; + + state->pstates = ptr; + ptr = &state->pstates[nplanes]; + return state; } EXPORT_SYMBOL(drm_atomic_begin); @@ -101,8 +110,20 @@ EXPORT_SYMBOL(drm_atomic_set_event); int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_atomic_state *a = state; + int nplanes = dev->mode_config.num_total_plane; + int i, ret = 0; + + for (i = 0; i < nplanes; i++) { + if (a->planes[i]) { + ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]); + if (ret) + break; + } + } + a->acquire_ctx.frozen = true; - return 0; /* for now */ + + return ret; } EXPORT_SYMBOL(drm_atomic_check); @@ -180,6 +201,18 @@ fail: static void commit_locks(struct drm_atomic_state *a, struct ww_acquire_ctx *ww_ctx) { + struct drm_device *dev = a->dev; + int nplanes = dev->mode_config.num_total_plane; + int i; + + for (i = 0; i < nplanes; i++) { + struct drm_plane *plane = a->planes[i]; + if (plane) { + plane->state->state = NULL; + drm_plane_destroy_state(plane, a->pstates[i]); + } + } + /* and properly release them (clear in_atomic, remove from list): */ drm_modeset_drop_locks(&a->acquire_ctx); ww_acquire_fini(ww_ctx); @@ -189,7 +222,17 @@ static void commit_locks(struct drm_atomic_state *a, static int atomic_commit(struct drm_atomic_state *a, struct ww_acquire_ctx *ww_ctx) { - int ret = 0; + int nplanes = a->dev->mode_config.num_total_plane; + int i, ret = 0; + + for (i = 0; i < nplanes; i++) { + struct drm_plane *plane = a->planes[i]; + if (plane) { + ret = drm_atomic_commit_plane_state(plane, a->pstates[i]); + if (ret) + break; + } + } commit_locks(a, ww_ctx); @@ -264,7 +307,185 @@ void _drm_atomic_state_free(struct kref *kref) } EXPORT_SYMBOL(_drm_atomic_state_free); +int drm_atomic_plane_set_property(struct drm_plane *plane, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data) +{ + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); + if (IS_ERR(pstate)) + return PTR_ERR(pstate); + return drm_plane_set_property(plane, pstate, property, val, blob_data); +} +EXPORT_SYMBOL(drm_atomic_plane_set_property); + +static void init_plane_state(struct drm_plane *plane, + struct drm_plane_state *pstate, struct drm_atomic_state *state) +{ + /* snapshot current state: */ + *pstate = *plane->state; + pstate->state = state; + if (pstate->fb) + drm_framebuffer_reference(pstate->fb); +} + +struct drm_plane_state * +drm_atomic_get_plane_state(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_atomic_state *a = state; + struct drm_plane_state *pstate; + int ret; + + pstate = a->pstates[plane->id]; + + if (!pstate) { + struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx; + + /* grab lock of current crtc.. if crtc is NULL then grab all: */ + if (plane->state->crtc) + ret = drm_modeset_lock(&plane->state->crtc->mutex, ctx); + else + ret = drm_modeset_lock_all_crtcs(plane->dev, ctx); + if (ret) + return ERR_PTR(ret); + + pstate = drm_plane_create_state(plane); + if (!pstate) + return ERR_PTR(-ENOMEM); + init_plane_state(plane, pstate, state); + a->planes[plane->id] = plane; + a->pstates[plane->id] = pstate; + } + + return pstate; +} +EXPORT_SYMBOL(drm_atomic_get_plane_state); + +static void +swap_plane_state(struct drm_plane *plane, struct drm_atomic_state *a) +{ + struct drm_plane_state *pstate = a->pstates[plane->id]; + + /* clear transient state (only valid during atomic update): */ + pstate->update_plane = false; + pstate->new_fb = false; + + swap(plane->state, a->pstates[plane->id]); + plane->base.propvals = &plane->state->propvals; +} + +/* For primary plane, if the driver implements ->page_flip(), then + * we can use that. But drivers can now choose not to bother with + * implementing page_flip(). + */ +static bool can_flip(struct drm_plane *plane, struct drm_plane_state *pstate) +{ + struct drm_crtc *crtc = pstate->crtc; + return (plane == crtc->primary) && crtc->funcs->page_flip && + !pstate->update_plane; +} + +/* clear crtc/fb, ie. after disable_plane(). But takes care to keep + * the property state in sync. Once we get rid of plane->crtc/fb ptrs + * and just use state, we can get rid of this fxn: + */ +static void +reset_plane(struct drm_plane *plane, struct drm_plane_state *pstate) +{ + struct drm_mode_config *config = &plane->dev->mode_config; + drm_plane_set_property(plane, pstate, config->prop_fb_id, 0, NULL); + drm_plane_set_property(plane, pstate, config->prop_crtc_id, 0, NULL); + plane->crtc = NULL; + plane->fb = NULL; +} + +static int +commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate) +{ + struct drm_atomic_state *a = pstate->state; + struct drm_framebuffer *old_fb = plane->fb; + struct drm_framebuffer *fb = pstate->fb; + bool enabled = pstate->crtc && fb; + int ret = 0; + + if (fb) + drm_framebuffer_reference(fb); + + if (!enabled) { + if ((plane->type == DRM_PLANE_TYPE_PRIMARY) && + (plane->funcs == &drm_primary_helper_funcs)) { + /* primary plane helpers don't like ->disable_plane().. + * so this hack for now until someone comes up with + * something better: + */ + ret = 0; + } else { + ret = plane->funcs->disable_plane(plane); + reset_plane(plane, pstate); + } + } else { + struct drm_crtc *crtc = pstate->crtc; + if (pstate->update_plane || + (pstate->new_fb && !can_flip(plane, pstate))) { + ret = plane->funcs->update_plane(plane, crtc, pstate->fb, + pstate->crtc_x, pstate->crtc_y, + pstate->crtc_w, pstate->crtc_h, + pstate->src_x, pstate->src_y, + pstate->src_w, pstate->src_h); + if (ret == 0) { + /* + * For page_flip(), the driver does this, but for + * update_plane() it doesn't.. hurray \o/ + */ + plane->crtc = crtc; + plane->fb = fb; + fb = NULL; /* don't unref */ + } + + } else if (pstate->new_fb) { + ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags); + if (ret == 0) { + /* + * Warn if the driver hasn't properly updated the plane->fb + * field to reflect that the new framebuffer is now used. + * Failing to do so will screw with the reference counting + * on framebuffers. + */ + WARN_ON(plane->fb != fb); + fb = NULL; /* don't unref */ + } + } else { + old_fb = NULL; + ret = 0; + } + } + + if (ret) { + /* Keep the old fb, don't unref it. */ + old_fb = NULL; + } else { + /* on success, update state and fb refcnting: */ + /* NOTE: if we ensure no driver sets plane->state->fb = NULL + * on disable, we can move this up a level and not duplicate + * nearly the same thing for both update_plane and disable_plane + * cases.. I leave it like this for now to be paranoid due to + * the slightly different ordering in the two cases in the + * original code. + */ + swap_plane_state(plane, pstate->state); + } + + + if (fb) + drm_framebuffer_unreference(fb); + if (old_fb) + drm_framebuffer_unreference(old_fb); + + return ret; +} const struct drm_atomic_funcs drm_atomic_funcs = { + .check_plane_state = drm_plane_check_state, + .commit_plane_state = commit_plane_state, }; EXPORT_SYMBOL(drm_atomic_funcs); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 48555724..b556a31 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -712,6 +712,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) * in this manner. */ if (atomic_read(&fb->refcount.refcount) > 1) { + void *state; + + state = dev->driver->atomic_begin(dev, 0); + if (IS_ERR(state)) { + DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n"); + return; + } + + /* TODO once CRTC is converted to state/properties, we can push the + * locking down into drm_atomic_commit(), since that is where + * the actual changes take place.. + */ drm_modeset_lock_all(dev); /* remove from any CRTC */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -728,8 +740,17 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) list_for_each_entry(plane, &dev->mode_config.plane_list, head) { if (plane->fb == fb) - drm_plane_force_disable(plane); + drm_plane_force_disable(plane, state); } + + /* just disabling stuff shouldn't fail, hopefully: */ + if(dev->driver->atomic_check(dev, state)) + DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n"); + else + dev->driver->atomic_commit(dev, state); + + dev->driver->atomic_end(dev, state); + drm_modeset_unlock_all(dev); } @@ -1090,18 +1111,23 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, const uint32_t *formats, uint32_t format_count, enum drm_plane_type type) { + struct drm_mode_config *config = &dev->mode_config; int ret; + /* this is now required: */ + WARN_ON(!funcs->set_property); + drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); if (ret) goto out; + plane->funcs = funcs; + plane->state = drm_plane_create_state(plane); plane->base.properties = &plane->properties; - plane->base.propvals = &plane->propvals; + plane->base.propvals = &plane->state->propvals; plane->dev = dev; - plane->funcs = funcs; plane->format_types = kmalloc(sizeof(uint32_t) * format_count, GFP_KERNEL); if (!plane->format_types) { @@ -1116,15 +1142,27 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, plane->possible_crtcs = possible_crtcs; plane->type = type; - list_add_tail(&plane->head, &dev->mode_config.plane_list); - dev->mode_config.num_total_plane++; + list_add_tail(&plane->head, &config->plane_list); + plane->id = config->num_total_plane; + config->num_total_plane++; if (plane->type == DRM_PLANE_TYPE_OVERLAY) - dev->mode_config.num_overlay_plane++; + config->num_overlay_plane++; drm_object_attach_property(&plane->base, - dev->mode_config.plane_type_property, + config->plane_type_property, plane->type); + drm_object_attach_property(&plane->base, config->prop_fb_id, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_id, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_x, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_y, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_w, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_h, 0); + drm_object_attach_property(&plane->base, config->prop_src_x, 0); + drm_object_attach_property(&plane->base, config->prop_src_y, 0); + drm_object_attach_property(&plane->base, config->prop_src_w, 0); + drm_object_attach_property(&plane->base, config->prop_src_h, 0); + out: drm_modeset_unlock_all(dev); @@ -1185,10 +1223,151 @@ void drm_plane_cleanup(struct drm_plane *plane) dev->mode_config.num_total_plane--; if (plane->type == DRM_PLANE_TYPE_OVERLAY) dev->mode_config.num_overlay_plane--; + drm_plane_destroy_state(plane, plane->state); drm_modeset_unlock_all(dev); } EXPORT_SYMBOL(drm_plane_cleanup); +int drm_plane_check_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + unsigned int fb_width, fb_height; + struct drm_framebuffer *fb = state->fb; + int i; + + /* disabling the plane is allowed: */ + if (!fb) + return 0; + + fb_width = fb->width << 16; + fb_height = fb->height << 16; + + /* Check whether this plane supports the fb pixel format. */ + for (i = 0; i < plane->format_count; i++) + if (fb->pixel_format == plane->format_types[i]) + break; + if (i == plane->format_count) { + DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format); + return -EINVAL; + } + + /* Make sure source coordinates are inside the fb. */ + if (state->src_w > fb_width || + state->src_x > fb_width - state->src_w || + state->src_h > fb_height || + state->src_y > fb_height - state->src_h) { + DRM_DEBUG_KMS("Invalid source coordinates " + "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", + state->src_w >> 16, + ((state->src_w & 0xffff) * 15625) >> 10, + state->src_h >> 16, + ((state->src_h & 0xffff) * 15625) >> 10, + state->src_x >> 16, + ((state->src_x & 0xffff) * 15625) >> 10, + state->src_y >> 16, + ((state->src_y & 0xffff) * 15625) >> 10); + return -ENOSPC; + } + + /* Give drivers some help against integer overflows */ + if (state->crtc_w > INT_MAX || + state->crtc_x > INT_MAX - (int32_t) state->crtc_w || + state->crtc_h > INT_MAX || + state->crtc_y > INT_MAX - (int32_t) state->crtc_h) { + DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", + state->crtc_w, state->crtc_h, + state->crtc_x, state->crtc_y); + return -ERANGE; + } + + return 0; +} +EXPORT_SYMBOL(drm_plane_check_state); + +void drm_plane_commit_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + plane->state = state; + plane->base.propvals = &state->propvals; +} +EXPORT_SYMBOL(drm_plane_commit_state); + +int drm_plane_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, + uint64_t value, void *blob_data) +{ + struct drm_device *dev = plane->dev; + struct drm_mode_config *config = &dev->mode_config; + + drm_object_property_set_value(&plane->base, + &state->propvals, property, value, blob_data); + + if (property == config->prop_fb_id) { + struct drm_framebuffer *old_fb = state->fb; + /* + * NOTE: the ref to the fb could have been lost between + * drm_property_change_is_valid() and now. The upshot + * is that drm_framebuffer_lookup() could return NULL + * and we'd disable the plane. + * + * We *could* return an error in that case. But if (for + * example) _setcrtc() raced with _rmfb() and _rmfb() + * came after, it would disable what was enabled in the + * _setcrtc(). Which is the same end result that we get + * here, just skipping briefly setting the mode. + */ + state->fb = drm_framebuffer_lookup(dev, value); + if (old_fb) + drm_framebuffer_unreference(old_fb); + state->new_fb = true; + } else if (property == config->prop_crtc_id) { + struct drm_mode_object *obj = drm_property_get_obj(property, value); + struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL; + /* take the lock of the incoming crtc as well, moving + * plane between crtcs is synchronized on both incoming + * and outgoing crtc. + */ + if (crtc) { + struct drm_atomic_state *a = state->state; + int ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx); + if (ret) + return ret; + } + state->crtc = crtc; + state->update_plane = true; + } else if (property == config->prop_crtc_x) { + state->crtc_x = U642I64(value); + state->update_plane = true; + } else if (property == config->prop_crtc_y) { + state->crtc_y = U642I64(value); + state->update_plane = true; + } else if (property == config->prop_crtc_w) { + state->crtc_w = value; + state->update_plane = true; + } else if (property == config->prop_crtc_h) { + state->crtc_h = value; + state->update_plane = true; + } else if (property == config->prop_src_x) { + state->src_x = value; + state->update_plane = true; + } else if (property == config->prop_src_y) { + state->src_y = value; + state->update_plane = true; + } else if (property == config->prop_src_w) { + state->src_w = value; + state->update_plane = true; + } else if (property == config->prop_src_h) { + state->src_h = value; + state->update_plane = true; + } else { + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(drm_plane_set_property); + /** * drm_plane_force_disable - Forcibly disable a plane * @plane: plane to disable @@ -1198,43 +1377,93 @@ EXPORT_SYMBOL(drm_plane_cleanup); * Used when the plane's current framebuffer is destroyed, * and when restoring fbdev mode. */ -void drm_plane_force_disable(struct drm_plane *plane) +void drm_plane_force_disable(struct drm_plane *plane, + struct drm_atomic_state *state) { - struct drm_framebuffer *old_fb = plane->fb; - int ret; + struct drm_mode_config *config = &plane->dev->mode_config; - if (!old_fb) - return; - - ret = plane->funcs->disable_plane(plane); - if (ret) { - DRM_ERROR("failed to disable plane with busy fb\n"); - return; - } - /* disconnect the plane from the fb and crtc: */ - __drm_framebuffer_unreference(old_fb); - plane->fb = NULL; - plane->crtc = NULL; + /* should turn off the crtc */ + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_id, 0, NULL); + drm_mode_plane_set_obj_prop(plane, state, + config->prop_fb_id, 0, NULL); } EXPORT_SYMBOL(drm_plane_force_disable); static int drm_mode_create_standard_connector_properties(struct drm_device *dev) { - struct drm_property *edid; - struct drm_property *dpms; + struct drm_property *prop; /* * Standard properties (apply to all connectors) */ - edid = drm_property_create(dev, DRM_MODE_PROP_BLOB | + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE, "EDID", 0); - dev->mode_config.edid_property = edid; + if (!prop) + return -ENOMEM; + dev->mode_config.edid_property = prop; - dpms = drm_property_create_enum(dev, 0, + prop = drm_property_create_enum(dev, 0, "DPMS", drm_dpms_enum_list, ARRAY_SIZE(drm_dpms_enum_list)); - dev->mode_config.dpms_property = dpms; + if (!prop) + return -ENOMEM; + dev->mode_config.dpms_property = prop; + + + prop = drm_property_create_range(dev, 0, "SRC_X", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_x = prop; + + prop = drm_property_create_range(dev, 0, "SRC_Y", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_y = prop; + + prop = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_w = prop; + + prop = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_h = prop; + + prop = drm_property_create_signed_range(dev, 0, "CRTC_X", + INT_MIN, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_x = prop; + + prop = drm_property_create_signed_range(dev, 0, "CRTC_Y", + INT_MIN, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_y = prop; + + prop = drm_property_create_range(dev, 0, "CRTC_W", 0, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_w = prop; + + prop = drm_property_create_range(dev, 0, "CRTC_H", 0, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_h = prop; + + prop = drm_property_create_object(dev, 0, "FB_ID", DRM_MODE_OBJECT_FB); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_fb_id = prop; + + prop = drm_property_create_object(dev, 0, + "CRTC_ID", DRM_MODE_OBJECT_CRTC); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_id = prop; return 0; } @@ -2140,20 +2369,19 @@ int drm_mode_setplane(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_set_plane *plane_req = data; + struct drm_mode_config *config = &dev->mode_config; struct drm_plane *plane; - struct drm_crtc *crtc; - struct drm_framebuffer *fb = NULL, *old_fb = NULL; + struct drm_atomic_state *state; int ret = 0; - unsigned int fb_width, fb_height; - int i; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - /* - * First, find the plane, crtc, and fb objects. If not available, - * we don't bother to call the driver. - */ +retry: + state = dev->driver->atomic_begin(dev, 0); + if (IS_ERR(state)) + return PTR_ERR(state); + plane = drm_plane_find(dev, plane_req->plane_id); if (!plane) { DRM_DEBUG_KMS("Unknown plane ID %d\n", @@ -2161,104 +2389,37 @@ int drm_mode_setplane(struct drm_device *dev, void *data, return -ENOENT; } - /* No fb means shut it down */ - if (!plane_req->fb_id) { - drm_modeset_lock_all(dev); - old_fb = plane->fb; - ret = plane->funcs->disable_plane(plane); - if (!ret) { - plane->crtc = NULL; - plane->fb = NULL; - } else { - old_fb = NULL; - } - drm_modeset_unlock_all(dev); - goto out; - } - - crtc = drm_crtc_find(dev, plane_req->crtc_id); - if (!crtc) { - DRM_DEBUG_KMS("Unknown crtc ID %d\n", - plane_req->crtc_id); - ret = -ENOENT; - goto out; - } - - fb = drm_framebuffer_lookup(dev, plane_req->fb_id); - if (!fb) { - DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", - plane_req->fb_id); - ret = -ENOENT; - goto out; - } - - /* Check whether this plane supports the fb pixel format. */ - for (i = 0; i < plane->format_count; i++) - if (fb->pixel_format == plane->format_types[i]) - break; - if (i == plane->format_count) { - DRM_DEBUG_KMS("Invalid pixel format %s\n", - drm_get_format_name(fb->pixel_format)); - ret = -EINVAL; - goto out; - } - - fb_width = fb->width << 16; - fb_height = fb->height << 16; - - /* Make sure source coordinates are inside the fb. */ - if (plane_req->src_w > fb_width || - plane_req->src_x > fb_width - plane_req->src_w || - plane_req->src_h > fb_height || - plane_req->src_y > fb_height - plane_req->src_h) { - DRM_DEBUG_KMS("Invalid source coordinates " - "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", - plane_req->src_w >> 16, - ((plane_req->src_w & 0xffff) * 15625) >> 10, - plane_req->src_h >> 16, - ((plane_req->src_h & 0xffff) * 15625) >> 10, - plane_req->src_x >> 16, - ((plane_req->src_x & 0xffff) * 15625) >> 10, - plane_req->src_y >> 16, - ((plane_req->src_y & 0xffff) * 15625) >> 10); - ret = -ENOSPC; - goto out; - } - - /* Give drivers some help against integer overflows */ - if (plane_req->crtc_w > INT_MAX || - plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || - plane_req->crtc_h > INT_MAX || - plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { - DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", - plane_req->crtc_w, plane_req->crtc_h, - plane_req->crtc_x, plane_req->crtc_y); - ret = -ERANGE; + ret = + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_id, plane_req->crtc_id, NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_fb_id, plane_req->fb_id, NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_w, plane_req->crtc_w, NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_h, plane_req->crtc_h, NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_src_w, plane_req->src_w, NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_src_h, plane_req->src_h, NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_src_x, plane_req->src_x, NULL) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_src_y, plane_req->src_y, NULL) || + dev->driver->atomic_check(dev, state); + if (ret) goto out; - } - drm_modeset_lock_all(dev); - old_fb = plane->fb; - ret = plane->funcs->update_plane(plane, crtc, fb, - plane_req->crtc_x, plane_req->crtc_y, - plane_req->crtc_w, plane_req->crtc_h, - plane_req->src_x, plane_req->src_y, - plane_req->src_w, plane_req->src_h); - if (!ret) { - plane->crtc = crtc; - plane->fb = fb; - fb = NULL; - } else { - old_fb = NULL; - } - drm_modeset_unlock_all(dev); + ret = dev->driver->atomic_commit(dev, state); out: - if (fb) - drm_framebuffer_unreference(fb); - if (old_fb) - drm_framebuffer_unreference(old_fb); - + dev->driver->atomic_end(dev, state); + if (ret == -EDEADLK) + goto retry; return ret; } @@ -3833,7 +3994,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); } -static int drm_mode_connector_set_obj_prop(struct drm_connector *connector, +int drm_mode_connector_set_obj_prop(struct drm_connector *connector, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3856,8 +4017,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector, return ret; } +EXPORT_SYMBOL(drm_mode_connector_set_obj_prop); -static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3872,8 +4034,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, return ret; } +EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop); -static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, +int drm_mode_plane_set_obj_prop(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3882,12 +4045,10 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, if (plane->funcs->set_property) ret = plane->funcs->set_property(plane, state, property, value, blob_data); - if (!ret) - drm_object_property_set_value(&plane->base, &plane->propvals, - property, value, NULL); return ret; } +EXPORT_SYMBOL(drm_mode_plane_set_obj_prop); static int drm_mode_set_obj_prop(struct drm_mode_object *obj, struct drm_atomic_state *state, struct drm_property *property, diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 97b0d84..b73d3b0 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -286,13 +286,28 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; struct drm_plane *plane; bool error = false; + void *state; int i; drm_warn_on_modeset_not_all_locked(dev); + state = dev->driver->atomic_begin(dev, 0); + if (IS_ERR(state)) { + DRM_ERROR("failed to restore fbdev mode\n"); + return true; + } + list_for_each_entry(plane, &dev->mode_config.plane_list, head) if (plane->type != DRM_PLANE_TYPE_PRIMARY) - drm_plane_force_disable(plane); + drm_plane_force_disable(plane, state); + + /* just disabling stuff shouldn't fail, hopefully: */ + if(dev->driver->atomic_check(dev, state)) + DRM_ERROR("failed to restore fbdev mode\n"); + else + dev->driver->atomic_commit(dev, state); + + dev->driver->atomic_end(dev, state); for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index d966afa..7a32383 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -26,6 +26,7 @@ #include #include #include +#include #define SUBPIXEL_MASK 0xffff @@ -234,6 +235,7 @@ EXPORT_SYMBOL(drm_primary_helper_destroy); const struct drm_plane_funcs drm_primary_helper_funcs = { .update_plane = drm_primary_helper_update, .disable_plane = drm_primary_helper_disable, + .set_property = drm_atomic_plane_set_property, .destroy = drm_primary_helper_destroy, }; EXPORT_SYMBOL(drm_primary_helper_funcs); diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 9da0935..8cf7442 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -10,6 +10,7 @@ */ #include +#include #include #include "exynos_drm_drv.h" @@ -220,13 +221,17 @@ static int exynos_plane_set_property(struct drm_plane *plane, struct drm_device *dev = plane->dev; struct exynos_plane *exynos_plane = to_exynos_plane(plane); struct exynos_drm_private *dev_priv = dev->dev_private; + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); + + if (IS_ERR(pstate)) + return PTR_ERR(pstate); if (property == dev_priv->plane_zpos_property) { exynos_plane->overlay.zpos = val; return 0; } - return -EINVAL; + return drm_plane_set_property(plane, pstate, property, val, blob_data); } static struct drm_plane_funcs exynos_plane_funcs = { diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index c235546..3f742f5 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1190,6 +1190,7 @@ static const struct drm_plane_funcs intel_plane_funcs = { .update_plane = intel_update_plane, .disable_plane = intel_disable_plane, .destroy = intel_destroy_plane, + .set_property = drm_atomic_plane_set_property, }; static uint32_t ilk_plane_formats[] = { diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 8c064dc..4c92985 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -88,8 +88,10 @@ int mdp4_plane_set_property(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) { - // XXX - return -EINVAL; + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); + if (IS_ERR(pstate)) + return PTR_ERR(pstate); + return drm_plane_set_property(plane, pstate, property, val, blob_data); } static const struct drm_plane_funcs mdp4_plane_funcs = { diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 5cbf226..53cc8c6 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -103,8 +103,10 @@ int mdp5_plane_set_property(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data) { - // XXX - return -EINVAL; + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); + if (IS_ERR(pstate)) + return PTR_ERR(pstate); + return drm_plane_set_property(plane, pstate, property, val, blob_data); } static const struct drm_plane_funcs mdp5_plane_funcs = { diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c index 577e6aa..97b48b5 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c +++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c @@ -24,6 +24,7 @@ */ #include +#include #include #include @@ -226,6 +227,10 @@ nv_set_property(struct drm_plane *plane, uint64_t value, void *blob_data) { struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane; + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); + + if (IS_ERR(pstate)) + return PTR_ERR(pstate); if (property == nv_plane->props.colorkey) nv_plane->colorkey = value; @@ -240,7 +245,8 @@ nv_set_property(struct drm_plane *plane, else if (property == nv_plane->props.iturbt_709) nv_plane->iturbt_709 = value; else - return -EINVAL; + return drm_plane_set_property(plane, pstate, + property, value, blob_data); if (nv_plane->set_params) nv_plane->set_params(nv_plane); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 3dca538..da80bdc 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -585,7 +585,7 @@ static void dev_lastclose(struct drm_device *dev) for (i = 0; i < priv->num_planes; i++) { drm_object_property_set_value(&priv->planes[i]->base, - &priv->planes[i]->propvals, + &priv->planes[i]->state->propvals, priv->rotation_prop, 0, NULL); } } diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 2d3c975..a9acc58 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -341,8 +341,12 @@ int omap_plane_set_property(struct drm_plane *plane, { struct omap_plane *omap_plane = to_omap_plane(plane); struct omap_drm_private *priv = plane->dev->dev_private; + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); int ret = -EINVAL; + if (IS_ERR(pstate)) + return PTR_ERR(pstate); + if (property == priv->rotation_prop) { DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val); omap_plane->win.rotation = val; @@ -351,6 +355,9 @@ int omap_plane_set_property(struct drm_plane *plane, DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val); omap_plane->info.zorder = val; ret = apply(plane); + } else { + ret = drm_plane_set_property(plane, pstate, property, + val, blob_data); } return ret; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 3a5d843..015c76a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -404,6 +405,10 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, { struct rcar_du_plane *rplane = to_rcar_plane(plane); struct rcar_du_group *rgrp = rplane->group; + struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state); + + if (IS_ERR(pstate)) + return PTR_ERR(pstate); if (property == rgrp->planes.alpha) rcar_du_plane_set_alpha(rplane, value); @@ -412,7 +417,8 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, else if (property == rgrp->planes.zpos) rcar_du_plane_set_zpos(rplane, value); else - return -EINVAL; + return drm_plane_set_property(plane, pstate, + property, value, blob_data); return 0; } diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c index 060ae03..ccf03ea 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -228,6 +229,7 @@ static void shmob_drm_plane_destroy(struct drm_plane *plane) static const struct drm_plane_funcs shmob_drm_plane_funcs = { .update_plane = shmob_drm_plane_update, .disable_plane = shmob_drm_plane_disable, + .set_property = drm_atomic_plane_set_property, .destroy = shmob_drm_plane_destroy, }; diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index ff72b81..78e93ec 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -68,7 +68,8 @@ * struct drm_atomic_funcs - helper funcs used by the atomic helpers */ struct drm_atomic_funcs { - int dummy; /* for now */ + int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate); + int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate); }; const extern struct drm_atomic_funcs drm_atomic_funcs; @@ -84,6 +85,30 @@ int drm_atomic_commit_unlocked(struct drm_device *dev, struct drm_atomic_state *state); void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state); +int drm_atomic_plane_set_property(struct drm_plane *plane, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t val, void *blob_data); +struct drm_plane_state *drm_atomic_get_plane_state(struct drm_plane *plane, + struct drm_atomic_state *state); + +static inline int +drm_atomic_check_plane_state(struct drm_plane *plane, + struct drm_plane_state *pstate) +{ + const struct drm_atomic_funcs *funcs = + plane->dev->driver->atomic_funcs; + return funcs->check_plane_state(plane, pstate); +} + +static inline int +drm_atomic_commit_plane_state(struct drm_plane *plane, + struct drm_plane_state *pstate) +{ + const struct drm_atomic_funcs *funcs = + plane->dev->driver->atomic_funcs; + return funcs->commit_plane_state(plane, pstate); +} + /** * struct drm_atomic_state - the state object used by atomic helpers */ @@ -91,6 +116,8 @@ struct drm_atomic_state { struct kref refcount; struct drm_device *dev; uint32_t flags; + struct drm_plane **planes; + struct drm_plane_state **pstates; bool committed; bool checked; /* just for debugging */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 547b75a..58309cc 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -569,7 +569,10 @@ struct drm_plane_funcs { int (*disable_plane)(struct drm_plane *plane); void (*destroy)(struct drm_plane *plane); - int (*set_property)(struct drm_plane *plane, + struct drm_plane_state *(*create_state)(struct drm_plane *plane); + void (*destroy_state)(struct drm_plane *plane, + struct drm_plane_state *pstate); + int (*set_property)(struct drm_plane *plane, struct drm_atomic_state *state, struct drm_property *property, uint64_t val, void *blob_data); @@ -582,6 +585,48 @@ enum drm_plane_type { }; /** + * drm_plane_state - mutable plane state + * @update_plane: if full update_plane() is needed (vs pageflip) + * @new_fb: has the fb been changed + * @crtc: currently bound CRTC + * @fb: currently bound fb + * @crtc_x: left position of visible portion of plane on crtc + * @crtc_y: upper position of visible portion of plane on crtc + * @crtc_w: width of visible portion of plane on crtc + * @crtc_h: height of visible portion of plane on crtc + * @src_x: left position of visible portion of plane within + * plane (in 16.16) + * @src_y: upper position of visible portion of plane within + * plane (in 16.16) + * @src_w: width of visible portion of plane (in 16.16) + * @src_h: height of visible portion of plane (in 16.16) + * @propvals: property values + * @state: current global/toplevel state object (for atomic) while an + * update is in progress, NULL otherwise. + */ +struct drm_plane_state { + bool update_plane : 1; + bool new_fb : 1; + + struct drm_crtc *crtc; + struct drm_framebuffer *fb; + + /* Signed dest location allows it to be partially off screen */ + int32_t crtc_x, crtc_y; + uint32_t crtc_w, crtc_h; + + /* Source values are 16.16 fixed point */ + uint32_t src_x, src_y; + uint32_t src_h, src_w; + + bool enabled; + + struct drm_object_property_values propvals; + + struct drm_atomic_state *state; +}; + +/** * drm_plane - central DRM plane control structure * @dev: DRM device this plane belongs to * @head: for list management @@ -591,6 +636,8 @@ enum drm_plane_type { * @format_count: number of formats supported * @crtc: currently bound CRTC * @fb: currently bound fb + * @id: plane number, 0..n + * @state: the mutable state * @funcs: helper functions * @properties: property tracking for this plane * @type: type of plane (overlay, primary, cursor) @@ -608,10 +655,17 @@ struct drm_plane { struct drm_crtc *crtc; struct drm_framebuffer *fb; + int id; + + /* + * State that can be updated from userspace, and atomically + * commited or rolled back: + */ + struct drm_plane_state *state; + const struct drm_plane_funcs *funcs; struct drm_object_properties properties; - struct drm_object_property_values propvals; enum drm_plane_type type; }; @@ -807,8 +861,20 @@ struct drm_mode_config { bool poll_running; struct delayed_work output_poll_work; - /* pointers to standard properties */ + /* just so blob properties can always be in a list: */ struct list_head property_blob_list; + + /* pointers to standard properties */ + struct drm_property *prop_src_x; + struct drm_property *prop_src_y; + struct drm_property *prop_src_w; + struct drm_property *prop_src_h; + struct drm_property *prop_crtc_x; + struct drm_property *prop_crtc_y; + struct drm_property *prop_crtc_w; + struct drm_property *prop_crtc_h; + struct drm_property *prop_fb_id; + struct drm_property *prop_crtc_id; struct drm_property *edid_property; struct drm_property *dpms_property; struct drm_property *plane_type_property; @@ -930,11 +996,20 @@ extern int drm_plane_init(struct drm_device *dev, const uint32_t *formats, uint32_t format_count, bool is_primary); extern void drm_plane_cleanup(struct drm_plane *plane); -extern void drm_plane_force_disable(struct drm_plane *plane); +extern void drm_plane_force_disable(struct drm_plane *plane, + struct drm_atomic_state *state); extern int drm_crtc_check_viewport(const struct drm_crtc *crtc, int x, int y, const struct drm_display_mode *mode, const struct drm_framebuffer *fb); +extern int drm_plane_check_state(struct drm_plane *plane, + struct drm_plane_state *state); +extern void drm_plane_commit_state(struct drm_plane *plane, + struct drm_plane_state *state); +extern int drm_plane_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, + uint64_t value, void *blob_data); extern void drm_encoder_cleanup(struct drm_encoder *encoder); @@ -984,6 +1059,17 @@ extern int drm_object_property_set_value(struct drm_mode_object *obj, extern int drm_object_property_get_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t *value); + +int drm_mode_connector_set_obj_prop(struct drm_connector *connector, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t value, void *blob_data); +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t value, void *blob_data); +int drm_mode_plane_set_obj_prop(struct drm_plane *plane, + struct drm_atomic_state *state, struct drm_property *property, + uint64_t value, void *blob_data); + extern int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, const struct drm_framebuffer_funcs *funcs); @@ -1165,6 +1251,26 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id) return mo ? obj_to_blob(mo) : NULL; } +static inline struct drm_plane_state * +drm_plane_create_state(struct drm_plane *plane) +{ + if (plane->funcs->create_state) + return plane->funcs->create_state(plane); + return kzalloc(sizeof(struct drm_plane_state), GFP_KERNEL); +} + +static inline void +drm_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *pstate) +{ + if (pstate->fb) + drm_framebuffer_unreference(pstate->fb); + if (plane->funcs->destroy_state) + plane->funcs->destroy_state(plane, pstate); + else + kfree(pstate); +} + /* Plane list iterator for legacy (overlay only) planes. */ #define drm_for_each_legacy_plane(plane, planelist) \ list_for_each_entry(plane, planelist, head) \