From patchwork Sat Oct 13 00:49:12 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rob Clark X-Patchwork-Id: 12206 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 889B923EFB for ; Sat, 13 Oct 2012 00:49:47 +0000 (UTC) Received: from mail-ie0-f180.google.com (mail-ie0-f180.google.com [209.85.223.180]) by fiordland.canonical.com (Postfix) with ESMTP id E1F0CA18327 for ; Sat, 13 Oct 2012 00:49:46 +0000 (UTC) Received: by mail-ie0-f180.google.com with SMTP id e10so5509072iej.11 for ; Fri, 12 Oct 2012 17:49:46 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-forwarded-to:x-forwarded-for:delivered-to:received-spf :dkim-signature:sender:from:to:cc:subject:date:message-id:x-mailer :in-reply-to:references:x-gm-message-state; bh=PpYFhrH1CZdBsMWleu8CQNawQjv0z68s54ZD/24CZqQ=; b=bdOTDDQV8V9iABXAKgKQ+HL9yDJsHAVezPNCCmUmRJPLpaV3VlFnMzPHllNLk2CeII tktbGPpgdH8NZhwVs7c5auTXdW1u/T75be8W6tPQJi64gayOl+ALnPG1aEym9BXCtQAp Gkdatg3vhLKgxwpT0yqEpwpEiV7Vvz+1HoTp5XwBc7bk4St57KwJFgqExR+EFmGBDyEi 10OJI4utRFASKpZTAkRjWnZ8XlBa2VmOAGo79Mv1eVXFGgyVoD0JFj6s1lJiPsVVGKCA HZV0RIBQ4iTAMVFnGQsxku1x5gK1nzcMweI1gTL2hZVSnEeGNmJYbKldtdfFNXqO5Vye Nf2g== Received: by 10.43.7.132 with SMTP id oo4mr4700433icb.6.1350089386481; Fri, 12 Oct 2012 17:49:46 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.50.67.148 with SMTP id n20csp274670igt; Fri, 12 Oct 2012 17:49:45 -0700 (PDT) Received: by 10.182.192.74 with SMTP id he10mr4813213obc.87.1350089385546; Fri, 12 Oct 2012 17:49:45 -0700 (PDT) Received: from mail-oa0-f50.google.com (mail-oa0-f50.google.com [209.85.219.50]) by mx.google.com with ESMTPS id d7si101453oea.52.2012.10.12.17.49.45 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 12 Oct 2012 17:49:45 -0700 (PDT) Received-SPF: pass (google.com: domain of robdclark@gmail.com designates 209.85.219.50 as permitted sender) client-ip=209.85.219.50; Authentication-Results: mx.google.com; spf=pass (google.com: domain of robdclark@gmail.com designates 209.85.219.50 as permitted sender) smtp.mail=robdclark@gmail.com; dkim=pass header.i=@gmail.com Received: by mail-oa0-f50.google.com with SMTP id n16so4214146oag.37 for ; Fri, 12 Oct 2012 17:49:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references; bh=PpYFhrH1CZdBsMWleu8CQNawQjv0z68s54ZD/24CZqQ=; b=0k/ARqTpeSxQBVa7qzaN0TEB+lEf1waHdUC93VCBDuOj2MLCR5JSIWIBMI1ehFyNKl X7osKl3X/C2RIGPI3ianHLIM9gC5NmUnFzoy3cKq/TN/Tj3gGGZDJKZ8vE8pLt+XnEqa Kx/JDDAyGmbBFix9uJdFvqsYOXimbpeXuEt+KY8YF2uKsClCsFuzO+EkSdy3LZYoz+8e LSBhtPR8/xXwv/StdsgdbvxtPZYgb0HjcQY6gEdtNxTQ4i8HWFKJ9ng1fDUXjWytN7ji +5JzHmJZWIPkHmViI55ZI2jddLVeqr8wj8RLdWBnKRJfORQR0HY92SPc1X1UWgsIf2yk 0OLQ== Received: by 10.182.95.142 with SMTP id dk14mr4905247obb.2.1350089385347; Fri, 12 Oct 2012 17:49:45 -0700 (PDT) Received: from localhost (ppp-70-129-143-201.dsl.rcsntx.swbell.net. [70.129.143.201]) by mx.google.com with ESMTPS id h2sm8245107obn.11.2012.10.12.17.49.43 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 12 Oct 2012 17:49:44 -0700 (PDT) Sender: Rob Clark From: Rob Clark To: dri-devel@lists.freedesktop.org Cc: patches@linaro.org, ville.syrjala@linux.intel.com, jbarnes@virtuousgeek.org, daniel.vetter@ffwll.ch, krh@bitplanet.net, Rob Clark Subject: [RFC 11/11] drm/omap: update for atomic age Date: Fri, 12 Oct 2012 19:49:12 -0500 Message-Id: <1350089352-18162-12-git-send-email-rob.clark@linaro.org> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1350089352-18162-1-git-send-email-rob.clark@linaro.org> References: <1350089352-18162-1-git-send-email-rob.clark@linaro.org> X-Gm-Message-State: ALoCoQkRX0KI3eu1i7mPrSfArUPKH9avVigoinc3f4dBcmPrqL8Ulpd1qecp7Bfq7kZAnla7NHzr From: Rob Clark --- drivers/staging/omapdrm/Makefile | 1 + drivers/staging/omapdrm/omap_atomic.c | 347 +++++++++++++++++++++++++++++++++ drivers/staging/omapdrm/omap_atomic.h | 52 +++++ drivers/staging/omapdrm/omap_crtc.c | 231 +++++++++++----------- drivers/staging/omapdrm/omap_drv.c | 27 ++- drivers/staging/omapdrm/omap_drv.h | 43 ++-- drivers/staging/omapdrm/omap_fb.c | 34 ++-- drivers/staging/omapdrm/omap_plane.c | 291 ++++++++++++++------------- 8 files changed, 737 insertions(+), 289 deletions(-) create mode 100644 drivers/staging/omapdrm/omap_atomic.c create mode 100644 drivers/staging/omapdrm/omap_atomic.h diff --git a/drivers/staging/omapdrm/Makefile b/drivers/staging/omapdrm/Makefile index d85e058..7d45e4d 100644 --- a/drivers/staging/omapdrm/Makefile +++ b/drivers/staging/omapdrm/Makefile @@ -13,6 +13,7 @@ omapdrm-y := omap_drv.o \ omap_connector.o \ omap_fb.o \ omap_fbdev.o \ + omap_atomic.o \ omap_gem.o \ omap_gem_dmabuf.o \ omap_dmm_tiler.o \ diff --git a/drivers/staging/omapdrm/omap_atomic.c b/drivers/staging/omapdrm/omap_atomic.c new file mode 100644 index 0000000..fbf03f1 --- /dev/null +++ b/drivers/staging/omapdrm/omap_atomic.c @@ -0,0 +1,347 @@ +/* + * drivers/staging/omapdrm/omap_atomic.c + * + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "omap_drv.h" +#include "omap_atomic.h" + +struct omap_atomic_state { + struct drm_device *dev; + + /* for page-flips, this is the CRTC involved: */ + struct drm_crtc *crtc; + int pipe; + + int num_dirty_planes, num_dirty_crtcs; + struct omap_plane_state *plane_state[8]; + struct omap_crtc_state *crtc_state[8]; + + int num_pending_fbs; + atomic_t num_ready_fbs; + struct drm_framebuffer *pending_fbs[8]; + + /* for handling page flips without caring about what + * the callback is called from. Possibly we should just + * make omap_gem always call the cb from the worker so + * we don't have to care about this.. + */ + struct work_struct commit_work; +}; + +static void commit_worker(struct work_struct *work); + +static int crtc2pipe(struct drm_device *dev, struct drm_crtc *crtc) +{ + struct omap_drm_private *priv = dev->dev_private; + int i; + + for (i = 0; i < ARRAY_SIZE(priv->crtcs); i++) + if (priv->crtcs[i] == crtc) + return i; + + BUG(); /* bogus CRTC ptr */ + return -1; +} + +static void set_atomic(struct omap_atomic_state *omap_state, bool atomic) +{ + struct omap_drm_private *priv = omap_state->dev->dev_private; + if (omap_state->crtc) { + int pipe = omap_state->pipe; + priv->crtc_atomic[pipe] = atomic; + } else { + priv->global_atomic = atomic; + } +} + +void *omap_atomic_begin(struct drm_device *dev, struct drm_crtc *crtc) +{ + struct omap_drm_private *priv = dev->dev_private; + struct omap_atomic_state *omap_state; + int pipe = 0; + + if (crtc) { + pipe = crtc2pipe(dev, crtc); + if (priv->event[pipe]) { + dev_err(dev->dev, "pending page-flip!\n"); + return ERR_PTR(-EBUSY); + } + WARN_ON(priv->crtc_atomic[pipe]); + } else { + WARN_ON(priv->global_atomic); + } + + omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL); + if (!omap_state) { + dev_err(dev->dev, "failed to allocate state\n"); + return ERR_PTR(-ENOMEM); + } + + omap_state->dev = dev; + omap_state->crtc = crtc; + omap_state->pipe = pipe; + + INIT_WORK(&omap_state->commit_work, commit_worker); + + set_atomic(omap_state, true); + + DBG("state=%p, crtc=%p", omap_state, crtc); + + return omap_state; +} + +static void release_state(struct omap_atomic_state *omap_state) +{ + int i; + + DBG("state=%p", omap_state); + + for (i = 0; i < omap_state->num_pending_fbs; i++) + drm_framebuffer_unreference(omap_state->pending_fbs[i]); + + /* + * omap_plane_commit()/omap_crtc_commit() have taken ownership + * of their respective state objects, so don't need to kfree() + * 'em here + */ + + kfree(omap_state); +} + +int omap_atomic_check(struct drm_device *dev, void *state) +{ + struct omap_atomic_state *omap_state = state; + struct omap_drm_private *priv = dev->dev_private; + int i, ret = 0; + + for (i = 0; (i < ARRAY_SIZE(omap_state->plane_state)) && !ret; i++) + if (omap_state->plane_state[i]) + ret = omap_plane_check_state(priv->planes[i], + omap_state->plane_state[i]); + + for (i = 0; (i < ARRAY_SIZE(omap_state->crtc_state)) && !ret; i++) + if (omap_state->crtc_state[i]) + ret = omap_crtc_check_state(priv->crtcs[i], + omap_state->crtc_state[i]); + + DBG("state=%p, ret=%d", omap_state, ret); + + if (ret) { + set_atomic(omap_state, false); + release_state(omap_state); + } + + return ret; +} + +static void commit_state(struct omap_atomic_state *omap_state) +{ + struct drm_device *dev = omap_state->dev; + struct omap_drm_private *priv = dev->dev_private; + int i; + + DBG("state=%p", omap_state); + + for (i = 0; i < ARRAY_SIZE(omap_state->plane_state); i++) { + struct omap_plane_state *plane_state = + omap_state->plane_state[i]; + if (plane_state) + omap_plane_commit_state(priv->planes[i], plane_state); + } + + set_atomic(omap_state, false); + + for (i = 0; i < ARRAY_SIZE(omap_state->crtc_state); i++) { + struct omap_crtc_state *crtc_state = + omap_state->crtc_state[i]; + if (crtc_state) + omap_crtc_commit_state(priv->crtcs[i], crtc_state); + } + + release_state(omap_state); +} + +static void commit_worker(struct work_struct *work) +{ + struct omap_atomic_state *omap_state = + container_of(work, struct omap_atomic_state, commit_work); + struct drm_device *dev = omap_state->dev; + + mutex_lock(&dev->mode_config.mutex); + DBG("state=%p", omap_state); + commit_state(omap_state); + mutex_unlock(&dev->mode_config.mutex); +} + +static void commit_cb(void *state) +{ + struct omap_atomic_state *omap_state = state; + struct omap_drm_private *priv = omap_state->dev->dev_private; + int num_ready_fbs = atomic_inc_return(&omap_state->num_ready_fbs); + + if (num_ready_fbs == omap_state->num_pending_fbs) + queue_work(priv->wq, &omap_state->commit_work); +} + +static void commit_async(struct drm_device *dev, void *state, + struct drm_pending_vblank_event *event) +{ + struct omap_atomic_state *omap_state = state; + struct omap_drm_private *priv = omap_state->dev->dev_private; + int i; + + if (event) { + int pipe = omap_state->pipe; + WARN_ON(priv->event[pipe]); + priv->event[pipe] = event; + } + + if (!omap_state->num_pending_fbs) { + commit_state(omap_state); + return; + } + + for (i = 0; i < omap_state->num_pending_fbs; i++) { + struct drm_gem_object *bo; + bo = omap_framebuffer_bo(omap_state->pending_fbs[i], 0); + omap_gem_op_async(bo, OMAP_GEM_READ, commit_cb, omap_state); + } +} + +int omap_atomic_commit(struct drm_device *dev, void *state, + struct drm_pending_vblank_event *event) +{ + struct omap_atomic_state *omap_state = state; + + DBG("state=%p, event=%p", omap_state, event); + + if (omap_state->crtc) { + /* async page-flip */ + commit_async(dev, state, event); + } else { + /* sync mode-set, etc */ + WARN_ON(event); /* this should not happen */ + commit_state(omap_state); + } + + return 0; +} + +void omap_atomic_end(struct drm_device *dev, void *state) +{ + /* + * State is freed either if atomic_check() fails or + * when async pageflip completes, so we don't need + * to do anything here. + */ +} + +struct omap_plane_state *omap_atomic_plane_state(void *state, int id) +{ + struct omap_atomic_state *omap_state = state; + struct omap_drm_private *priv = omap_state->dev->dev_private; + struct omap_plane_state *plane_state = omap_state->plane_state[id]; + int i; + + if (!plane_state) { + struct drm_plane *plane = priv->planes[id]; + + plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL); + + /* snapshot current state: */ + *plane_state = *to_omap_plane_state(plane->state); + + omap_state->plane_state[id] = plane_state; + } + + /* updating a plane implicitly dirties the crtc: */ + for (i = 0; i < priv->num_crtcs; i++) { + if (priv->crtcs[i] == plane_state->base.crtc) { + omap_atomic_crtc_state(state, i); + break; + } + } + + return plane_state; +} + +struct omap_crtc_state *omap_atomic_crtc_state(void *state, int id) +{ + struct omap_atomic_state *omap_state = state; + struct omap_drm_private *priv = omap_state->dev->dev_private; + struct omap_crtc_state *crtc_state = omap_state->crtc_state[id]; + + if (!crtc_state) { + struct drm_crtc *crtc = priv->crtcs[id]; + + crtc_state = kzalloc(sizeof(*crtc_state), GFP_KERNEL); + + /* snapshot current state: */ + *crtc_state = *to_omap_crtc_state(crtc->state); + + omap_state->crtc_state[id] = crtc_state; + } + + return crtc_state; +} + +/* when fb is changed, that gets recorded in the state, so that pageflips + * can defer until all fb's are ready + */ +void omap_atomic_add_fb(void *state, struct drm_framebuffer *fb) +{ + struct omap_atomic_state *omap_state = state; + drm_framebuffer_reference(fb); + omap_state->pending_fbs[omap_state->num_pending_fbs++] = fb; +} + +/* possibly this could be in drm core? */ +static void send_page_flip_event(struct drm_device *dev, int crtc, + struct drm_pending_vblank_event *event) +{ + unsigned long flags; + struct timeval now; + + DBG("%p", event); + + spin_lock_irqsave(&dev->event_lock, flags); + event->event.sequence = drm_vblank_count_and_time(dev, crtc, &now); + event->event.tv_sec = now.tv_sec; + event->event.tv_usec = now.tv_usec; + list_add_tail(&event->base.link, + &event->base.file_priv->event_list); + wake_up_interruptible(&event->base.file_priv->event_wait); + spin_unlock_irqrestore(&dev->event_lock, flags); +} + +/* called when plane is updated.. so we can keep track of when to send + * page-flip events + */ +void omap_atomic_plane_update(struct drm_device *dev, int id) +{ + struct omap_drm_private *priv = dev->dev_private; + int pipe = crtc2pipe(dev, priv->planes[id]->state->crtc); + + DBG("id=%d", id); + + if (priv->event[pipe]) { + /* wakeup userspace */ + send_page_flip_event(dev, pipe, priv->event[pipe]); + priv->event[pipe] = NULL; + } +} diff --git a/drivers/staging/omapdrm/omap_atomic.h b/drivers/staging/omapdrm/omap_atomic.h new file mode 100644 index 0000000..6ba596a --- /dev/null +++ b/drivers/staging/omapdrm/omap_atomic.h @@ -0,0 +1,52 @@ +/* + * drivers/staging/omapdrm/omap_atomic.h + * + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef __OMAP_ATOMIC_H__ +#define __OMAP_ATOMIC_H__ + +#include "drm_mode.h" +#include "drm_crtc.h" + +struct omap_plane_state { + struct drm_plane_state base; + uint8_t rotation; + uint8_t zorder; + uint8_t enabled; +}; +#define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base) + +struct omap_crtc_state { + struct drm_crtc_state base; +}; +#define to_omap_crtc_state(x) container_of(x, struct omap_crtc_state, base) + +void *omap_atomic_begin(struct drm_device *dev, struct drm_crtc *crtc); +int omap_atomic_check(struct drm_device *dev, void *state); +int omap_atomic_commit(struct drm_device *dev, void *state, + struct drm_pending_vblank_event *event); +void omap_atomic_end(struct drm_device *dev, void *state); + +struct omap_plane_state *omap_atomic_plane_state(void *state, int id); +struct omap_crtc_state *omap_atomic_crtc_state(void *state, int id); + +void omap_atomic_add_fb(void *state, struct drm_framebuffer *fb); + +void omap_atomic_plane_update(struct drm_device *dev, int id); + +#endif /* __OMAP_ATOMIC_H__ */ diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c index b903c63..f9af31e 100644 --- a/drivers/staging/omapdrm/omap_crtc.c +++ b/drivers/staging/omapdrm/omap_crtc.c @@ -32,7 +32,6 @@ struct omap_crtc { const char *name; int pipe; enum omap_channel channel; - struct omap_overlay_manager_info info; struct dss_lcd_mgr_config lcd_config; @@ -50,21 +49,10 @@ struct omap_crtc { /* list of queued apply's: */ struct list_head queued_applies; + bool in_apply; /* for debug */ + /* for handling queued and in-progress applies: */ struct work_struct apply_work; - - /* if there is a pending flip, these will be non-null: */ - struct drm_pending_vblank_event *event; - struct drm_framebuffer *old_fb; - - /* for handling page flips without caring about what - * the callback is called from. Possibly we should just - * make omap_gem always call the cb from the worker so - * we don't have to care about this.. - * - * XXX maybe fold into apply_work?? - */ - struct work_struct page_flip_work; }; /* @@ -132,6 +120,9 @@ static const struct dss_mgr_ops mgr_ops = { * CRTC funcs: */ +static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb); + static void omap_crtc_destroy(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); @@ -146,6 +137,7 @@ static void omap_crtc_destroy(struct drm_crtc *crtc) channel2crtc[omap_crtc->channel] = NULL; + kfree(crtc->state); kfree(omap_crtc); } @@ -206,11 +198,7 @@ static int omap_crtc_mode_set(struct drm_crtc *crtc, } } - return omap_plane_mode_set(omap_crtc->plane, crtc, crtc->state->fb, - 0, 0, mode->hdisplay, mode->vdisplay, - x << 16, y << 16, - mode->hdisplay << 16, mode->vdisplay << 16, - NULL, NULL); + return omap_crtc_mode_set_base(crtc, x, y, old_fb); } static void omap_crtc_prepare(struct drm_crtc *crtc) @@ -232,99 +220,61 @@ static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct drm_plane *plane = omap_crtc->plane; - struct drm_display_mode *mode = &crtc->mode; - - return omap_plane_mode_set(plane, crtc, crtc->state->fb, - 0, 0, mode->hdisplay, mode->vdisplay, - x << 16, y << 16, - mode->hdisplay << 16, mode->vdisplay << 16, - NULL, NULL); -} - -static void omap_crtc_load_lut(struct drm_crtc *crtc) -{ -} - -static void vblank_cb(void *arg) -{ - struct drm_crtc *crtc = arg; - struct drm_device *dev = crtc->dev; - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - - /* wakeup userspace */ - if (omap_crtc->event) - drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc->event); - - omap_crtc->event = NULL; - omap_crtc->old_fb = NULL; - - spin_unlock_irqrestore(&dev->event_lock, flags); -} - -static void page_flip_worker(struct work_struct *work) -{ - struct omap_crtc *omap_crtc = - container_of(work, struct omap_crtc, page_flip_work); - struct drm_crtc *crtc = &omap_crtc->base; struct drm_device *dev = crtc->dev; + struct drm_mode_config *config = &dev->mode_config; struct drm_display_mode *mode = &crtc->mode; - struct drm_gem_object *bo; + void *state; + int w, h, ret; - mutex_lock(&dev->mode_config.mutex); - omap_plane_mode_set(omap_crtc->plane, crtc, crtc->state->fb, - 0, 0, mode->hdisplay, mode->vdisplay, - crtc->state->x << 16, crtc->state->y << 16, - mode->hdisplay << 16, mode->vdisplay << 16, - vblank_cb, crtc); - mutex_unlock(&dev->mode_config.mutex); + if (WARN_ON(!crtc->state->fb)) + return -EINVAL; - bo = omap_framebuffer_bo(crtc->state->fb, 0); - drm_gem_object_unreference_unlocked(bo); -} + w = mode->hdisplay; + h = mode->vdisplay; -static void page_flip_cb(void *arg) -{ - struct drm_crtc *crtc = arg; - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct omap_drm_private *priv = crtc->dev->dev_private; + if (crtc->state->invert_dimensions) { + swap(w, h); + swap(x, y); + } - /* avoid assumptions about what ctxt we are called from: */ - queue_work(priv->wq, &omap_crtc->page_flip_work); + /* for now, until property based atomic mode-set: */ + state = omap_atomic_begin(dev, NULL); + if (IS_ERR(state)) + return PTR_ERR(state); + + ret = + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_id, crtc->base.id) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_fb_id, crtc->state->fb->base.id) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_x, 0) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_y, 0) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_w, mode->hdisplay) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_h, mode->vdisplay) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_src_w, w << 16) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_src_h, h << 16) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_src_x, x << 16) || + drm_mode_plane_set_obj_prop(plane, state, + config->prop_src_y, y << 16) || + dev->driver->atomic_check(dev, state); + + if (!ret) + ret = omap_atomic_commit(dev, state, NULL); + + omap_atomic_end(dev, state); + + return ret; } -static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event) +static void omap_crtc_load_lut(struct drm_crtc *crtc) { - struct drm_device *dev = crtc->dev; - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct drm_gem_object *bo; - - DBG("%d -> %d (event=%p)", crtc->state->fb ? crtc->state->fb->base.id : -1, - fb->base.id, event); - - if (omap_crtc->old_fb) { - dev_err(dev->dev, "already a pending flip\n"); - return -EINVAL; - } - - omap_crtc->event = event; - crtc->state->fb = fb; - - /* - * Hold a reference temporarily until the crtc is updated - * and takes the reference to the bo. This avoids it - * getting freed from under us: - */ - bo = omap_framebuffer_bo(fb, 0); - drm_gem_object_reference(bo); - - omap_gem_op_async(bo, OMAP_GEM_READ, page_flip_cb, crtc); - - return 0; } static int omap_crtc_set_property(struct drm_crtc *crtc, void *state, @@ -332,9 +282,23 @@ static int omap_crtc_set_property(struct drm_crtc *crtc, void *state, { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct omap_drm_private *priv = crtc->dev->dev_private; + struct omap_crtc_state *crtc_state = + omap_atomic_crtc_state(state, omap_crtc->pipe); + int ret; + + DBG("%s: %s = %llx", omap_crtc->name, property->name, val); + + ret = drm_crtc_set_property(crtc, &crtc_state->base, property, val); + if (!ret) { + /* we need to set fb property on our private plane too: + */ + struct drm_mode_config *config = &crtc->dev->mode_config; + if (property != config->prop_fb_id) + return ret; + } if (property == priv->rotation_prop) { - crtc->state->invert_dimensions = + crtc_state->base.invert_dimensions = !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270))); } @@ -344,7 +308,6 @@ static int omap_crtc_set_property(struct drm_crtc *crtc, void *state, static const struct drm_crtc_funcs omap_crtc_funcs = { .set_config = drm_crtc_helper_set_config, .destroy = omap_crtc_destroy, - .page_flip = omap_crtc_page_flip_locked, .set_property = omap_crtc_set_property, }; @@ -370,6 +333,24 @@ enum omap_channel omap_crtc_channel(struct drm_crtc *crtc) return omap_crtc->channel; } +int omap_crtc_check_state(struct drm_crtc *crtc, + struct omap_crtc_state *crtc_state) +{ + return drm_crtc_check_state(crtc, &crtc_state->base); +} + +void omap_crtc_commit_state(struct drm_crtc *crtc, + struct omap_crtc_state *crtc_state) +{ + + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct omap_crtc_state *old_state = to_omap_crtc_state(crtc->state); + DBG("%s", omap_crtc->name); + drm_crtc_commit_state(crtc, &crtc_state->base); + kfree(old_state); + omap_crtc_apply(crtc, &omap_crtc->apply); +} + static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) { struct omap_crtc *omap_crtc = @@ -407,6 +388,7 @@ static void apply_worker(struct work_struct *work) * with respect to modesetting ioctls from userspace. */ mutex_lock(&dev->mode_config.mutex); + omap_crtc->in_apply = true; dispc_runtime_get(); /* @@ -423,6 +405,9 @@ static void apply_worker(struct work_struct *work) list_del(&apply->pending_node); } + if (pipe_in_atomic(dev, omap_crtc->pipe)) + goto out; + need_apply = !list_empty(&omap_crtc->queued_applies); /* then handle the next round of of queued apply's: */ @@ -449,6 +434,7 @@ static void apply_worker(struct work_struct *work) out: dispc_runtime_put(); + omap_crtc->in_apply = false; mutex_unlock(&dev->mode_config.mutex); } @@ -459,13 +445,17 @@ int omap_crtc_apply(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + WARN_ON(omap_crtc->in_apply); /* no need to queue it again if it is already queued: */ - if (apply->queued) - return 0; + if (! apply->queued) { + apply->queued = true; + list_add_tail(&apply->queued_node, + &omap_crtc->queued_applies); + } - apply->queued = true; - list_add_tail(&apply->queued_node, &omap_crtc->queued_applies); + if (pipe_in_atomic(dev, omap_crtc->pipe)) + return 0; /* * If there are no currently pending updates, then go ahead and @@ -539,6 +529,7 @@ static void omap_crtc_pre_apply(struct omap_drm_apply *apply) struct omap_crtc *omap_crtc = container_of(apply, struct omap_crtc, apply); enum omap_channel channel = omap_crtc->channel; + struct omap_overlay_manager_info info = {0}; DBG("%s: enabled=%d, timings_valid=%d", omap_crtc->name, omap_crtc->enabled, @@ -549,9 +540,14 @@ static void omap_crtc_pre_apply(struct omap_drm_apply *apply) return; } + info.default_color = 0x00000000; + info.trans_key = 0x00000000; + info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + info.trans_enabled = false; + if (dss_mgr_is_lcd(channel)) dispc_mgr_set_lcd_config(channel, &omap_crtc->lcd_config); - dispc_mgr_setup(channel, &omap_crtc->info); + dispc_mgr_setup(channel, &info); dispc_mgr_set_timings(channel, &omap_crtc->timings); set_enabled(&omap_crtc->base, true); } @@ -573,7 +569,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, { struct drm_crtc *crtc = NULL; struct omap_crtc *omap_crtc; - struct omap_overlay_manager_info *info; DBG("%s", channel_names[channel]); @@ -586,7 +581,13 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, crtc = &omap_crtc->base; - INIT_WORK(&omap_crtc->page_flip_work, page_flip_worker); + crtc->state = kzalloc(sizeof(struct omap_crtc_state), GFP_KERNEL); + + if (!crtc->state) { + dev_err(dev->dev, "could not allocate CRTC state\n"); + goto fail; + } + INIT_WORK(&omap_crtc->apply_work, apply_worker); INIT_LIST_HEAD(&omap_crtc->pending_applies); @@ -612,12 +613,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, channel2crtc[omap_crtc->channel] = crtc; dss_install_mgr_ops(&mgr_ops); - /* TODO: fix hard-coded setup.. add properties! */ - info->default_color = 0x00000000; - info->trans_key = 0x00000000; - info->trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST; - info->trans_enabled = false; - drm_crtc_init(dev, crtc, &omap_crtc_funcs); drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c index 7321510..0b180b7 100644 --- a/drivers/staging/omapdrm/omap_drv.c +++ b/drivers/staging/omapdrm/omap_drv.c @@ -18,6 +18,7 @@ */ #include "omap_drv.h" +#include "omap_atomic.h" #include "drm_crtc_helper.h" #include "drm_fb_helper.h" @@ -510,6 +511,7 @@ static int dev_firstopen(struct drm_device *dev) */ static void dev_lastclose(struct drm_device *dev) { + void *state; int i; /* we don't support vga-switcheroo.. so just make sure the fbdev @@ -525,11 +527,16 @@ static void dev_lastclose(struct drm_device *dev) * a flag that properties should automatically be restored to * default state on lastclose? */ + state = dev->driver->atomic_begin(dev, NULL); for (i = 0; i < priv->num_planes; i++) { struct drm_plane *plane = priv->planes[i]; - drm_object_property_set_value(&plane->base, - &plane->state->propvals, priv->rotation_prop, 0); + drm_mode_plane_set_obj_prop(plane, state, + priv->rotation_prop, 0); } + /* disabling rotation won't ever fail: */ + dev->driver->atomic_check(dev, state); + dev->driver->atomic_commit(dev, state, NULL); + dev->driver->atomic_end(dev, state); mutex_lock(&dev->mode_config.mutex); ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev); @@ -540,7 +547,19 @@ static void dev_lastclose(struct drm_device *dev) static void dev_preclose(struct drm_device *dev, struct drm_file *file) { + struct omap_drm_private *priv = dev->dev_private; + int i; + DBG("preclose: dev=%p", dev); + + /* + * Clear out pending events before they get destroyed in + * drm_events_release(), so that if we later get a vblank + * we don't deref a bogus ptr + */ + for (i = 0; i < ARRAY_SIZE(priv->event); i++) + if (priv->event[i] && priv->event[i]->base.file_priv == file) + priv->event[i] = NULL; } static void dev_postclose(struct drm_device *dev, struct drm_file *file) @@ -597,6 +616,10 @@ static struct drm_driver omap_drm_driver = { .dumb_create = omap_gem_dumb_create, .dumb_map_offset = omap_gem_dumb_map_offset, .dumb_destroy = omap_gem_dumb_destroy, + .atomic_begin = omap_atomic_begin, + .atomic_check = omap_atomic_check, + .atomic_commit = omap_atomic_commit, + .atomic_end = omap_atomic_end, .ioctls = ioctls, .num_ioctls = DRM_OMAP_NUM_IOCTLS, .fops = &omapdriver_fops, diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h index c20ed7e..131f0a8 100644 --- a/drivers/staging/omapdrm/omap_drv.h +++ b/drivers/staging/omapdrm/omap_drv.h @@ -27,6 +27,7 @@ #include #include #include "omap_drm.h" +#include "omap_atomic.h" /* APIs we need from dispc.. TODO omapdss should export these */ #include "../../video/omap2/dss/dss.h" @@ -47,15 +48,6 @@ void hdmi_dump_regs(struct seq_file *s); */ #define MAX_MAPPERS 2 -/* parameters which describe (unrotated) coordinates of scanout within a fb: */ -struct omap_drm_window { - uint32_t rotation; - int32_t crtc_x, crtc_y; /* signed because can be offscreen */ - uint32_t crtc_w, crtc_h; - uint32_t src_x, src_y; - uint32_t src_w, src_h; -}; - /* Once GO bit is set, we can't make further updates to shadowed registers * until the GO bit is cleared. So various parts in the kms code that need * to update shadowed registers queue up a pair of callbacks, pre_apply @@ -119,9 +111,16 @@ struct omap_drm_private { struct drm_property *zorder_prop; /* irq handling: */ - struct list_head irq_list; /* list of omap_drm_irq */ + struct list_head irq_list; /* list of omap_drm_irq */ uint32_t vblank_mask; /* irq bits set for userspace vblank */ struct omap_drm_irq error_handler; + + /* atomic: */ + bool global_atomic; /* in global atomic update (ie. modeset) */ + bool crtc_atomic[8]; /* in per-crtc atomic update (ie. pageflip) */ + + /* pending vblank event per CRTC: */ + struct drm_pending_vblank_event *event[8]; }; /* this should probably be in drm-core to standardize amongst drivers */ @@ -154,6 +153,10 @@ void omap_fbdev_free(struct drm_device *dev); const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc); enum omap_channel omap_crtc_channel(struct drm_crtc *crtc); +int omap_crtc_check_state(struct drm_crtc *crtc, + struct omap_crtc_state *crtc_state); +void omap_crtc_commit_state(struct drm_crtc *crtc, + struct omap_crtc_state *crtc_state); int omap_crtc_apply(struct drm_crtc *crtc, struct omap_drm_apply *apply); struct drm_crtc *omap_crtc_init(struct drm_device *dev, @@ -162,17 +165,14 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, struct drm_plane *omap_plane_init(struct drm_device *dev, int plane_id, bool private_plane); int omap_plane_dpms(struct drm_plane *plane, int mode); -int omap_plane_mode_set(struct drm_plane *plane, - struct drm_crtc *crtc, struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h, - void (*fxn)(void *), void *arg); void omap_plane_install_properties(struct drm_plane *plane, struct drm_mode_object *obj); int omap_plane_set_property(struct drm_plane *plane, void *state, struct drm_property *property, uint64_t val); +int omap_plane_check_state(struct drm_plane *plane, + struct omap_plane_state *plane_state); +void omap_plane_commit_state(struct drm_plane *plane, + struct omap_plane_state *plane_state); struct drm_encoder *omap_encoder_init(struct drm_device *dev); struct drm_encoder *omap_connector_attached_encoder( @@ -198,7 +198,7 @@ int omap_framebuffer_replace(struct drm_framebuffer *a, struct drm_framebuffer *b, void *arg, void (*unpin)(void *arg, struct drm_gem_object *bo)); void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, - struct omap_drm_window *win, struct omap_overlay_info *info); + struct drm_plane_state *state, struct omap_overlay_info *info); struct drm_connector *omap_framebuffer_get_next_connector( struct drm_framebuffer *fb, struct drm_connector *from); void omap_framebuffer_flush(struct drm_framebuffer *fb, @@ -284,6 +284,13 @@ static inline uint32_t pipe2vbl(int crtc) return dispc_mgr_get_vsync_irq(channel); } +/* is the specified pipe/crtc blocked for atomic update? */ +static inline bool pipe_in_atomic(struct drm_device *dev, int pipe) +{ + struct omap_drm_private *priv = dev->dev_private; + return priv->global_atomic || priv->crtc_atomic[pipe]; +} + /* should these be made into common util helpers? */ diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/staging/omapdrm/omap_fb.c index 66c11f9..3d6350d 100644 --- a/drivers/staging/omapdrm/omap_fb.c +++ b/drivers/staging/omapdrm/omap_fb.c @@ -154,33 +154,34 @@ static uint32_t get_linear_addr(struct plane *plane, /* update ovl info for scanout, handles cases of multi-planar fb's, etc. */ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, - struct omap_drm_window *win, struct omap_overlay_info *info) + struct drm_plane_state *state, struct omap_overlay_info *info) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); + struct omap_plane_state *plane_state = to_omap_plane_state(state); const struct format *format = omap_fb->format; struct plane *plane = &omap_fb->planes[0]; uint32_t x, y, orient = 0; info->color_mode = format->dss_format; - info->pos_x = win->crtc_x; - info->pos_y = win->crtc_y; - info->out_width = win->crtc_w; - info->out_height = win->crtc_h; - info->width = win->src_w; - info->height = win->src_h; + info->pos_x = state->crtc_x; + info->pos_y = state->crtc_y; + info->out_width = state->crtc_w; + info->out_height = state->crtc_h; + info->width = state->src_w >> 16; + info->height = state->src_h >> 16; - x = win->src_x; - y = win->src_y; + x = state->src_x >> 16; + y = state->src_y >> 16; if (omap_gem_flags(plane->bo) & OMAP_BO_TILED) { - uint32_t w = win->src_w; - uint32_t h = win->src_h; + uint32_t w = state->src_w >> 16; + uint32_t h = state->src_h >> 16; - switch (win->rotation & 0xf) { + switch (plane_state->rotation & 0xf) { default: dev_err(fb->dev->dev, "invalid rotation: %02x", - (uint32_t)win->rotation); + plane_state->rotation); /* fallthru to default to no rotation */ case 0: case BIT(DRM_ROTATE_0): @@ -197,10 +198,10 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, break; } - if (win->rotation & BIT(DRM_REFLECT_X)) + if (plane_state->rotation & BIT(DRM_REFLECT_X)) orient ^= MASK_X_INVERT; - if (win->rotation & BIT(DRM_REFLECT_Y)) + if (plane_state->rotation & BIT(DRM_REFLECT_Y)) orient ^= MASK_Y_INVERT; /* adjust x,y offset for flip/invert: */ @@ -220,6 +221,9 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, info->screen_width = plane->pitch; } + /* always do the rotation in tiler (or none at all): */ + info->rotation = OMAP_DSS_ROT_0; + /* convert to pixels: */ info->screen_width /= format->planes[0].stride_bpp; diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c index cb5d427..6881532 100644 --- a/drivers/staging/omapdrm/omap_plane.c +++ b/drivers/staging/omapdrm/omap_plane.c @@ -32,24 +32,14 @@ * plane funcs */ -struct callback { - void (*fxn)(void *); - void *arg; -}; - #define to_omap_plane(x) container_of(x, struct omap_plane, base) struct omap_plane { struct drm_plane base; int id; /* TODO rename omap_plane -> omap_plane_id in omapdss so I can use the enum */ const char *name; - struct omap_overlay_info info; struct omap_drm_apply apply; - /* position/orientation of scanout within the fb: */ - struct omap_drm_window win; - bool enabled; - /* last fb that we pinned: */ struct drm_framebuffer *pinned_fb; @@ -60,9 +50,6 @@ struct omap_plane { /* set of bo's pending unpin until next post_apply() */ DECLARE_KFIFO_PTR(unpin_fifo, struct drm_gem_object *); - - // XXX maybe get rid of this and handle vblank in crtc too? - struct callback apply_done_cb; }; static void unpin(void *arg, struct drm_gem_object *bo) @@ -114,24 +101,53 @@ static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb) return 0; } +static inline bool is_enabled(struct drm_plane_state *state) +{ + return to_omap_plane_state(state)->enabled && + state->crtc && state->fb; +} + +/* TODO get rid of this and convert dispc code to use drm state + * structs directly.. + */ +static void state2info(struct omap_plane_state *plane_state, + struct omap_overlay_info *info) +{ + + memset(info, 0, sizeof(*info)); + + info->global_alpha = 0xff; + /* TODO: we should calculate valid zorder from all the planes: */ + info->zorder = plane_state->zorder; + + /* update scanout: */ + omap_framebuffer_update_scanout(plane_state->base.fb, + &plane_state->base, info); + + DBG("%dx%d -> %dx%d (%d)", info->width, info->height, + info->out_width, info->out_height, info->screen_width); + DBG("%d,%d %08x %08x", info->pos_x, info->pos_y, + info->paddr, info->p_uv_addr); +} + static void omap_plane_pre_apply(struct omap_drm_apply *apply) { struct omap_plane *omap_plane = container_of(apply, struct omap_plane, apply); - struct omap_drm_window *win = &omap_plane->win; struct drm_plane *plane = &omap_plane->base; struct drm_device *dev = plane->dev; - struct omap_overlay_info *info = &omap_plane->info; + struct omap_overlay_info info; struct drm_crtc *crtc = plane->state->crtc; + struct drm_framebuffer *fb = plane->state->fb; enum omap_channel channel; - bool enabled = omap_plane->enabled && crtc; - bool ilace, replication; + bool enabled = is_enabled(plane->state); + bool replication; int ret; DBG("%s, enabled=%d", omap_plane->name, enabled); /* if fb has changed, pin new fb: */ - update_pin(plane, enabled ? plane->state->fb : NULL); + update_pin(plane, enabled ? fb : NULL); if (!enabled) { dispc_ovl_enable(omap_plane->id, false); @@ -140,21 +156,14 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply) channel = omap_crtc_channel(crtc); - /* update scanout: */ - omap_framebuffer_update_scanout(plane->state->fb, win, info); - - DBG("%dx%d -> %dx%d (%d)", info->width, info->height, - info->out_width, info->out_height, - info->screen_width); - DBG("%d,%d %08x %08x", info->pos_x, info->pos_y, - info->paddr, info->p_uv_addr); + state2info(to_omap_plane_state(plane->state), &info); /* TODO: */ - ilace = false; replication = false; /* and finally, update omapdss: */ - ret = dispc_ovl_setup(omap_plane->id, info, + dispc_ovl_set_channel_out(omap_plane->id, channel); + ret = dispc_ovl_setup(omap_plane->id, &info, replication, omap_crtc_timings(crtc), false); if (ret) { dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret); @@ -162,7 +171,6 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply) } dispc_ovl_enable(omap_plane->id, true); - dispc_ovl_set_channel_out(omap_plane->id, channel); } static void omap_plane_post_apply(struct omap_drm_apply *apply) @@ -170,94 +178,21 @@ static void omap_plane_post_apply(struct omap_drm_apply *apply) struct omap_plane *omap_plane = container_of(apply, struct omap_plane, apply); struct drm_plane *plane = &omap_plane->base; - struct omap_overlay_info *info = &omap_plane->info; struct drm_gem_object *bo = NULL; - struct callback cb; - - cb = omap_plane->apply_done_cb; - omap_plane->apply_done_cb.fxn = NULL; while (kfifo_get(&omap_plane->unpin_fifo, &bo)) { omap_gem_put_paddr(bo); drm_gem_object_unreference_unlocked(bo); } - if (cb.fxn) - cb.fxn(cb.arg); - - if (omap_plane->enabled) { - omap_framebuffer_flush(plane->state->fb, info->pos_x, info->pos_y, - info->out_width, info->out_height); - } -} - -static int apply(struct drm_plane *plane) -{ - if (plane->state->crtc) { - struct omap_plane *omap_plane = to_omap_plane(plane); - return omap_crtc_apply(plane->state->crtc, &omap_plane->apply); - } - return 0; -} - -int omap_plane_mode_set(struct drm_plane *plane, - struct drm_crtc *crtc, struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h, - void (*fxn)(void *), void *arg) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_drm_window *win = &omap_plane->win; - - win->crtc_x = crtc_x; - win->crtc_y = crtc_y; - win->crtc_w = crtc_w; - win->crtc_h = crtc_h; - - /* src values are in Q16 fixed point, convert to integer: */ - win->src_x = src_x >> 16; - win->src_y = src_y >> 16; - win->src_w = src_w >> 16; - win->src_h = src_h >> 16; - - if (fxn) { - /* omap_crtc should ensure that a new page flip - * isn't permitted while there is one pending: - */ - BUG_ON(omap_plane->apply_done_cb.fxn); + omap_atomic_plane_update(plane->dev, omap_plane->id); - omap_plane->apply_done_cb.fxn = fxn; - omap_plane->apply_done_cb.arg = arg; + if (is_enabled(plane->state)) { + struct drm_plane_state *state = plane->state; + omap_framebuffer_flush(plane->state->fb, + state->crtc_x, state->crtc_y, + state->crtc_w, state->crtc_h); } - - plane->state->fb = fb; - plane->state->crtc = crtc; - - return apply(plane); -} - -static int omap_plane_update(struct drm_plane *plane, - struct drm_crtc *crtc, struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - omap_plane->enabled = true; - return omap_plane_mode_set(plane, crtc, fb, - crtc_x, crtc_y, crtc_w, crtc_h, - src_x, src_y, src_w, src_h, - NULL, NULL); -} - -static int omap_plane_disable(struct drm_plane *plane) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - omap_plane->win.rotation = BIT(DRM_ROTATE_0); - return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); } static void omap_plane_destroy(struct drm_plane *plane) @@ -268,24 +203,29 @@ static void omap_plane_destroy(struct drm_plane *plane) omap_irq_unregister(plane->dev, &omap_plane->error_irq); - omap_plane_disable(plane); + omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); drm_plane_cleanup(plane); WARN_ON(!kfifo_is_empty(&omap_plane->unpin_fifo)); kfifo_free(&omap_plane->unpin_fifo); + kfree(plane->state); kfree(omap_plane); } int omap_plane_dpms(struct drm_plane *plane, int mode) { struct omap_plane *omap_plane = to_omap_plane(plane); + struct drm_plane_state *state = plane->state; bool enabled = (mode == DRM_MODE_DPMS_ON); int ret = 0; - if (enabled != omap_plane->enabled) { - omap_plane->enabled = enabled; - ret = apply(plane); + DBG("%s: mode=%d", omap_plane->name, mode); + + if (enabled != is_enabled(state)) { + to_omap_plane_state(state)->enabled = enabled; + if (state->crtc) + ret = omap_crtc_apply(state->crtc, &omap_plane->apply); } return ret; @@ -332,24 +272,106 @@ int omap_plane_set_property(struct drm_plane *plane, void *state, { struct omap_plane *omap_plane = to_omap_plane(plane); struct omap_drm_private *priv = plane->dev->dev_private; - int ret = -EINVAL; + struct omap_plane_state *plane_state = + omap_atomic_plane_state(state, omap_plane->id); + int ret; + + DBG("%s: %s = %llx", omap_plane->name, property->name, val); + + ret = drm_plane_set_property(plane, &plane_state->base, property, val); + if (!ret) { + /* if this property is handled by base, we are nearly done.. + * we just need to register an fb property w/ atomic so that + * commit can be deferred until the fb is ready + */ + struct drm_mode_config *config = &plane->dev->mode_config; + if ((property == config->prop_fb_id) && val) { + struct drm_mode_object *obj = + drm_property_get_obj(property, val); + omap_atomic_add_fb(state, obj_to_fb(obj)); + } + return ret; + } + + /* if it is not a base plane property, see if it is one of ours: */ if (property == priv->rotation_prop) { DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val); - omap_plane->win.rotation = val; - ret = apply(plane); + plane_state->rotation = val; } else if (property == priv->zorder_prop) { DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val); - omap_plane->info.zorder = val; - ret = apply(plane); + plane_state->zorder = val; + } else { + return -EINVAL; } - return ret; + return 0; +} + +int omap_plane_check_state(struct drm_plane *plane, + struct omap_plane_state *plane_state) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + struct drm_crtc *crtc = plane_state->base.crtc; + struct omap_overlay_info info; + int ret, x_predecim = 0, y_predecim = 0; + + if (!is_enabled(&plane_state->base)) + return 0; + + ret = drm_plane_check_state(plane, &plane_state->base); + if (ret) + return ret; + + state2info(plane_state, &info); + + ret = dispc_ovl_check(omap_plane->id, + omap_crtc_channel(crtc), + &info, omap_crtc_timings(crtc), + &x_predecim, &y_predecim); + if (ret) { + DBG("%s: dispc_ovl_check failed: %d", omap_plane->name, ret); + return ret; + } + + /* TODO add some properties to set max pre-decimation.. but + * until then, we'd rather fallback to GPU than decimate: + */ + if ((x_predecim > 1) || (y_predecim > 1)) { + DBG("%s: x_predecim=%d, y_predecim=%d", omap_plane->name, + x_predecim, y_predecim); + return -EINVAL; + } + + return 0; +} + +void omap_plane_commit_state(struct drm_plane *plane, + struct omap_plane_state *plane_state) +{ + struct omap_plane *omap_plane = to_omap_plane(plane); + struct omap_plane_state *old_state = to_omap_plane_state(plane->state); + struct drm_crtc *crtc; + + DBG("%s", omap_plane->name); + + crtc = plane_state->base.crtc; + + /* TODO we need to handle crtc switch.. we should reject that + * at the check() stage if we are still waiting for GO to clear + * on the outgoing crtc.. + */ + if (!crtc) + crtc = plane->state->crtc; + + drm_plane_commit_state(plane, &plane_state->base); + kfree(old_state); + + if (crtc) + omap_crtc_apply(crtc, &omap_plane->apply); } static const struct drm_plane_funcs omap_plane_funcs = { - .update_plane = omap_plane_update, - .disable_plane = omap_plane_disable, .destroy = omap_plane_destroy, .set_property = omap_plane_set_property, }; @@ -382,7 +404,6 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, struct omap_drm_private *priv = dev->dev_private; struct drm_plane *plane = NULL; struct omap_plane *omap_plane; - struct omap_overlay_info *info; int ret; DBG("%s: priv=%d", plane_names[id], private_plane); @@ -407,6 +428,13 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, plane = &omap_plane->base; + plane->state = kzalloc(sizeof(struct omap_plane_state), GFP_KERNEL); + + if (!plane->state) { + dev_err(dev->dev, "could not allocate CRTC state\n"); + goto fail; + } + omap_plane->apply.pre_apply = omap_plane_pre_apply; omap_plane->apply.post_apply = omap_plane_post_apply; @@ -419,24 +447,15 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, omap_plane_install_properties(plane, &plane->base); - /* get our starting configuration, set defaults for parameters - * we don't currently use, etc: - */ - info = &omap_plane->info; - info->rotation_type = OMAP_DSS_ROT_DMA; - info->rotation = OMAP_DSS_ROT_0; - info->global_alpha = 0xff; - info->mirror = 0; - /* Set defaults depending on whether we are a CRTC or overlay * layer. - * TODO add ioctl to give userspace an API to change this.. this - * will come in a subsequent patch. */ - if (private_plane) - omap_plane->info.zorder = 0; - else - omap_plane->info.zorder = id; + if (!private_plane) { + struct omap_plane_state *plane_state = + to_omap_plane_state(plane->state); + plane_state->zorder = id; + plane_state->enabled = true; + } return plane;