linux/drivers/gpu/drm/omapdrm/omap_plane.c
<<
>>
Prefs
   1/*
   2 * drivers/gpu/drm/omapdrm/omap_plane.c
   3 *
   4 * Copyright (C) 2011 Texas Instruments
   5 * Author: Rob Clark <rob.clark@linaro.org>
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License version 2 as published by
   9 * the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful, but WITHOUT
  12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  14 * more details.
  15 *
  16 * You should have received a copy of the GNU General Public License along with
  17 * this program.  If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "drm_flip_work.h"
  21
  22#include "omap_drv.h"
  23#include "omap_dmm_tiler.h"
  24
  25/* some hackery because omapdss has an 'enum omap_plane' (which would be
  26 * better named omap_plane_id).. and compiler seems unhappy about having
  27 * both a 'struct omap_plane' and 'enum omap_plane'
  28 */
  29#define omap_plane _omap_plane
  30
  31/*
  32 * plane funcs
  33 */
  34
  35struct callback {
  36        void (*fxn)(void *);
  37        void *arg;
  38};
  39
  40#define to_omap_plane(x) container_of(x, struct omap_plane, base)
  41
  42struct omap_plane {
  43        struct drm_plane base;
  44        int id;  /* TODO rename omap_plane -> omap_plane_id in omapdss so I can use the enum */
  45        const char *name;
  46        struct omap_overlay_info info;
  47        struct omap_drm_apply apply;
  48
  49        /* position/orientation of scanout within the fb: */
  50        struct omap_drm_window win;
  51        bool enabled;
  52
  53        /* last fb that we pinned: */
  54        struct drm_framebuffer *pinned_fb;
  55
  56        uint32_t nformats;
  57        uint32_t formats[32];
  58
  59        struct omap_drm_irq error_irq;
  60
  61        /* for deferring bo unpin's until next post_apply(): */
  62        struct drm_flip_work unpin_work;
  63
  64        // XXX maybe get rid of this and handle vblank in crtc too?
  65        struct callback apply_done_cb;
  66};
  67
  68static void omap_plane_unpin_worker(struct drm_flip_work *work, void *val)
  69{
  70        struct omap_plane *omap_plane =
  71                        container_of(work, struct omap_plane, unpin_work);
  72        struct drm_device *dev = omap_plane->base.dev;
  73
  74        /*
  75         * omap_framebuffer_pin/unpin are always called from priv->wq,
  76         * so there's no need for locking here.
  77         */
  78        omap_framebuffer_unpin(val);
  79        mutex_lock(&dev->mode_config.mutex);
  80        drm_framebuffer_unreference(val);
  81        mutex_unlock(&dev->mode_config.mutex);
  82}
  83
  84/* update which fb (if any) is pinned for scanout */
  85static int omap_plane_update_pin(struct drm_plane *plane,
  86                                 struct drm_framebuffer *fb)
  87{
  88        struct omap_plane *omap_plane = to_omap_plane(plane);
  89        struct drm_framebuffer *pinned_fb = omap_plane->pinned_fb;
  90
  91        if (pinned_fb != fb) {
  92                int ret = 0;
  93
  94                DBG("%p -> %p", pinned_fb, fb);
  95
  96                if (fb) {
  97                        drm_framebuffer_reference(fb);
  98                        ret = omap_framebuffer_pin(fb);
  99                }
 100
 101                if (pinned_fb)
 102                        drm_flip_work_queue(&omap_plane->unpin_work, pinned_fb);
 103
 104                if (ret) {
 105                        dev_err(plane->dev->dev, "could not swap %p -> %p\n",
 106                                        omap_plane->pinned_fb, fb);
 107                        drm_framebuffer_unreference(fb);
 108                        omap_plane->pinned_fb = NULL;
 109                        return ret;
 110                }
 111
 112                omap_plane->pinned_fb = fb;
 113        }
 114
 115        return 0;
 116}
 117
 118static void omap_plane_pre_apply(struct omap_drm_apply *apply)
 119{
 120        struct omap_plane *omap_plane =
 121                        container_of(apply, struct omap_plane, apply);
 122        struct omap_drm_window *win = &omap_plane->win;
 123        struct drm_plane *plane = &omap_plane->base;
 124        struct drm_device *dev = plane->dev;
 125        struct omap_overlay_info *info = &omap_plane->info;
 126        struct drm_crtc *crtc = plane->crtc;
 127        enum omap_channel channel;
 128        bool enabled = omap_plane->enabled && crtc;
 129        int ret;
 130
 131        DBG("%s, enabled=%d", omap_plane->name, enabled);
 132
 133        /* if fb has changed, pin new fb: */
 134        omap_plane_update_pin(plane, enabled ? plane->fb : NULL);
 135
 136        if (!enabled) {
 137                dispc_ovl_enable(omap_plane->id, false);
 138                return;
 139        }
 140
 141        channel = omap_crtc_channel(crtc);
 142
 143        /* update scanout: */
 144        omap_framebuffer_update_scanout(plane->fb, win, info);
 145
 146        DBG("%dx%d -> %dx%d (%d)", info->width, info->height,
 147                        info->out_width, info->out_height,
 148                        info->screen_width);
 149        DBG("%d,%d %pad %pad", info->pos_x, info->pos_y,
 150                        &info->paddr, &info->p_uv_addr);
 151
 152        dispc_ovl_set_channel_out(omap_plane->id, channel);
 153
 154        /* and finally, update omapdss: */
 155        ret = dispc_ovl_setup(omap_plane->id, info, false,
 156                              omap_crtc_timings(crtc), false);
 157        if (ret) {
 158                dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret);
 159                return;
 160        }
 161
 162        dispc_ovl_enable(omap_plane->id, true);
 163}
 164
 165static void omap_plane_post_apply(struct omap_drm_apply *apply)
 166{
 167        struct omap_plane *omap_plane =
 168                        container_of(apply, struct omap_plane, apply);
 169        struct drm_plane *plane = &omap_plane->base;
 170        struct omap_drm_private *priv = plane->dev->dev_private;
 171        struct callback cb;
 172
 173        cb = omap_plane->apply_done_cb;
 174        omap_plane->apply_done_cb.fxn = NULL;
 175
 176        drm_flip_work_commit(&omap_plane->unpin_work, priv->wq);
 177
 178        if (cb.fxn)
 179                cb.fxn(cb.arg);
 180}
 181
 182static int omap_plane_apply(struct drm_plane *plane)
 183{
 184        if (plane->crtc) {
 185                struct omap_plane *omap_plane = to_omap_plane(plane);
 186                return omap_crtc_apply(plane->crtc, &omap_plane->apply);
 187        }
 188        return 0;
 189}
 190
 191int omap_plane_mode_set(struct drm_plane *plane,
 192                        struct drm_crtc *crtc, struct drm_framebuffer *fb,
 193                        int crtc_x, int crtc_y,
 194                        unsigned int crtc_w, unsigned int crtc_h,
 195                        unsigned int src_x, unsigned int src_y,
 196                        unsigned int src_w, unsigned int src_h,
 197                        void (*fxn)(void *), void *arg)
 198{
 199        struct omap_plane *omap_plane = to_omap_plane(plane);
 200        struct omap_drm_window *win = &omap_plane->win;
 201
 202        win->crtc_x = crtc_x;
 203        win->crtc_y = crtc_y;
 204        win->crtc_w = crtc_w;
 205        win->crtc_h = crtc_h;
 206
 207        win->src_x = src_x;
 208        win->src_y = src_y;
 209        win->src_w = src_w;
 210        win->src_h = src_h;
 211
 212        if (fxn) {
 213                /* omap_crtc should ensure that a new page flip
 214                 * isn't permitted while there is one pending:
 215                 */
 216                BUG_ON(omap_plane->apply_done_cb.fxn);
 217
 218                omap_plane->apply_done_cb.fxn = fxn;
 219                omap_plane->apply_done_cb.arg = arg;
 220        }
 221
 222        return omap_plane_apply(plane);
 223}
 224
 225static int omap_plane_update(struct drm_plane *plane,
 226                struct drm_crtc *crtc, struct drm_framebuffer *fb,
 227                int crtc_x, int crtc_y,
 228                unsigned int crtc_w, unsigned int crtc_h,
 229                uint32_t src_x, uint32_t src_y,
 230                uint32_t src_w, uint32_t src_h)
 231{
 232        struct omap_plane *omap_plane = to_omap_plane(plane);
 233        omap_plane->enabled = true;
 234
 235        /* omap_plane_mode_set() takes adjusted src */
 236        switch (omap_plane->win.rotation & 0xf) {
 237        case BIT(DRM_ROTATE_90):
 238        case BIT(DRM_ROTATE_270):
 239                swap(src_w, src_h);
 240                break;
 241        }
 242
 243        /*
 244         * We don't need to take a reference to the framebuffer as the DRM core
 245         * has already done so for the purpose of setting plane->fb.
 246         */
 247        plane->fb = fb;
 248        plane->crtc = crtc;
 249
 250        /* src values are in Q16 fixed point, convert to integer: */
 251        return omap_plane_mode_set(plane, crtc, fb,
 252                        crtc_x, crtc_y, crtc_w, crtc_h,
 253                        src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16,
 254                        NULL, NULL);
 255}
 256
 257static int omap_plane_disable(struct drm_plane *plane)
 258{
 259        struct omap_plane *omap_plane = to_omap_plane(plane);
 260
 261        omap_plane->win.rotation = BIT(DRM_ROTATE_0);
 262        omap_plane->info.zorder = plane->type == DRM_PLANE_TYPE_PRIMARY
 263                                ? 0 : omap_plane->id;
 264
 265        return omap_plane_set_enable(plane, false);
 266}
 267
 268static void omap_plane_destroy(struct drm_plane *plane)
 269{
 270        struct omap_plane *omap_plane = to_omap_plane(plane);
 271
 272        DBG("%s", omap_plane->name);
 273
 274        omap_irq_unregister(plane->dev, &omap_plane->error_irq);
 275
 276        drm_plane_cleanup(plane);
 277
 278        drm_flip_work_cleanup(&omap_plane->unpin_work);
 279
 280        kfree(omap_plane);
 281}
 282
 283int omap_plane_set_enable(struct drm_plane *plane, bool enable)
 284{
 285        struct omap_plane *omap_plane = to_omap_plane(plane);
 286
 287        if (enable == omap_plane->enabled)
 288                return 0;
 289
 290        omap_plane->enabled = enable;
 291        return omap_plane_apply(plane);
 292}
 293
 294/* helper to install properties which are common to planes and crtcs */
 295void omap_plane_install_properties(struct drm_plane *plane,
 296                struct drm_mode_object *obj)
 297{
 298        struct drm_device *dev = plane->dev;
 299        struct omap_drm_private *priv = dev->dev_private;
 300        struct drm_property *prop;
 301
 302        if (priv->has_dmm) {
 303                prop = priv->rotation_prop;
 304                if (!prop) {
 305                        prop = drm_mode_create_rotation_property(dev,
 306                                                                 BIT(DRM_ROTATE_0) |
 307                                                                 BIT(DRM_ROTATE_90) |
 308                                                                 BIT(DRM_ROTATE_180) |
 309                                                                 BIT(DRM_ROTATE_270) |
 310                                                                 BIT(DRM_REFLECT_X) |
 311                                                                 BIT(DRM_REFLECT_Y));
 312                        if (prop == NULL)
 313                                return;
 314                        priv->rotation_prop = prop;
 315                }
 316                drm_object_attach_property(obj, prop, 0);
 317        }
 318
 319        prop = priv->zorder_prop;
 320        if (!prop) {
 321                prop = drm_property_create_range(dev, 0, "zorder", 0, 3);
 322                if (prop == NULL)
 323                        return;
 324                priv->zorder_prop = prop;
 325        }
 326        drm_object_attach_property(obj, prop, 0);
 327}
 328
 329int omap_plane_set_property(struct drm_plane *plane,
 330                struct drm_property *property, uint64_t val)
 331{
 332        struct omap_plane *omap_plane = to_omap_plane(plane);
 333        struct omap_drm_private *priv = plane->dev->dev_private;
 334        int ret = -EINVAL;
 335
 336        if (property == priv->rotation_prop) {
 337                DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
 338                omap_plane->win.rotation = val;
 339                ret = omap_plane_apply(plane);
 340        } else if (property == priv->zorder_prop) {
 341                DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
 342                omap_plane->info.zorder = val;
 343                ret = omap_plane_apply(plane);
 344        }
 345
 346        return ret;
 347}
 348
 349static const struct drm_plane_funcs omap_plane_funcs = {
 350        .update_plane = omap_plane_update,
 351        .disable_plane = omap_plane_disable,
 352        .destroy = omap_plane_destroy,
 353        .set_property = omap_plane_set_property,
 354};
 355
 356static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
 357{
 358        struct omap_plane *omap_plane =
 359                        container_of(irq, struct omap_plane, error_irq);
 360        DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_plane->name,
 361                irqstatus);
 362}
 363
 364static const char *plane_names[] = {
 365        [OMAP_DSS_GFX] = "gfx",
 366        [OMAP_DSS_VIDEO1] = "vid1",
 367        [OMAP_DSS_VIDEO2] = "vid2",
 368        [OMAP_DSS_VIDEO3] = "vid3",
 369};
 370
 371static const uint32_t error_irqs[] = {
 372        [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
 373        [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
 374        [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
 375        [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
 376};
 377
 378/* initialize plane */
 379struct drm_plane *omap_plane_init(struct drm_device *dev,
 380                int id, enum drm_plane_type type)
 381{
 382        struct omap_drm_private *priv = dev->dev_private;
 383        struct drm_plane *plane;
 384        struct omap_plane *omap_plane;
 385        struct omap_overlay_info *info;
 386        int ret;
 387
 388        DBG("%s: type=%d", plane_names[id], type);
 389
 390        omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
 391        if (!omap_plane)
 392                return ERR_PTR(-ENOMEM);
 393
 394        drm_flip_work_init(&omap_plane->unpin_work,
 395                        "unpin", omap_plane_unpin_worker);
 396
 397        omap_plane->nformats = omap_framebuffer_get_formats(
 398                        omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
 399                        dss_feat_get_supported_color_modes(id));
 400        omap_plane->id = id;
 401        omap_plane->name = plane_names[id];
 402
 403        plane = &omap_plane->base;
 404
 405        omap_plane->apply.pre_apply  = omap_plane_pre_apply;
 406        omap_plane->apply.post_apply = omap_plane_post_apply;
 407
 408        omap_plane->error_irq.irqmask = error_irqs[id];
 409        omap_plane->error_irq.irq = omap_plane_error_irq;
 410        omap_irq_register(dev, &omap_plane->error_irq);
 411
 412        ret = drm_universal_plane_init(dev, plane, (1 << priv->num_crtcs) - 1,
 413                                       &omap_plane_funcs, omap_plane->formats,
 414                                       omap_plane->nformats, type);
 415        if (ret < 0)
 416                goto error;
 417
 418        omap_plane_install_properties(plane, &plane->base);
 419
 420        /* get our starting configuration, set defaults for parameters
 421         * we don't currently use, etc:
 422         */
 423        info = &omap_plane->info;
 424        info->rotation_type = OMAP_DSS_ROT_DMA;
 425        info->rotation = OMAP_DSS_ROT_0;
 426        info->global_alpha = 0xff;
 427        info->mirror = 0;
 428
 429        /* Set defaults depending on whether we are a CRTC or overlay
 430         * layer.
 431         * TODO add ioctl to give userspace an API to change this.. this
 432         * will come in a subsequent patch.
 433         */
 434        if (type == DRM_PLANE_TYPE_PRIMARY)
 435                omap_plane->info.zorder = 0;
 436        else
 437                omap_plane->info.zorder = id;
 438
 439        return plane;
 440
 441error:
 442        omap_irq_unregister(plane->dev, &omap_plane->error_irq);
 443        kfree(omap_plane);
 444        return NULL;
 445}
 446