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 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        omap_framebuffer_unpin(val);
  75        mutex_lock(&dev->mode_config.mutex);
  76        drm_framebuffer_unreference(val);
  77        mutex_unlock(&dev->mode_config.mutex);
  78}
  79
  80/* update which fb (if any) is pinned for scanout */
  81static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
  82{
  83        struct omap_plane *omap_plane = to_omap_plane(plane);
  84        struct drm_framebuffer *pinned_fb = omap_plane->pinned_fb;
  85
  86        if (pinned_fb != fb) {
  87                int ret = 0;
  88
  89                DBG("%p -> %p", pinned_fb, fb);
  90
  91                if (fb) {
  92                        drm_framebuffer_reference(fb);
  93                        ret = omap_framebuffer_pin(fb);
  94                }
  95
  96                if (pinned_fb)
  97                        drm_flip_work_queue(&omap_plane->unpin_work, pinned_fb);
  98
  99                if (ret) {
 100                        dev_err(plane->dev->dev, "could not swap %p -> %p\n",
 101                                        omap_plane->pinned_fb, fb);
 102                        drm_framebuffer_unreference(fb);
 103                        omap_plane->pinned_fb = NULL;
 104                        return ret;
 105                }
 106
 107                omap_plane->pinned_fb = fb;
 108        }
 109
 110        return 0;
 111}
 112
 113static void omap_plane_pre_apply(struct omap_drm_apply *apply)
 114{
 115        struct omap_plane *omap_plane =
 116                        container_of(apply, struct omap_plane, apply);
 117        struct omap_drm_window *win = &omap_plane->win;
 118        struct drm_plane *plane = &omap_plane->base;
 119        struct drm_device *dev = plane->dev;
 120        struct omap_overlay_info *info = &omap_plane->info;
 121        struct drm_crtc *crtc = plane->crtc;
 122        enum omap_channel channel;
 123        bool enabled = omap_plane->enabled && crtc;
 124        bool ilace, replication;
 125        int ret;
 126
 127        DBG("%s, enabled=%d", omap_plane->name, enabled);
 128
 129        /* if fb has changed, pin new fb: */
 130        update_pin(plane, enabled ? plane->fb : NULL);
 131
 132        if (!enabled) {
 133                dispc_ovl_enable(omap_plane->id, false);
 134                return;
 135        }
 136
 137        channel = omap_crtc_channel(crtc);
 138
 139        /* update scanout: */
 140        omap_framebuffer_update_scanout(plane->fb, win, info);
 141
 142        DBG("%dx%d -> %dx%d (%d)", info->width, info->height,
 143                        info->out_width, info->out_height,
 144                        info->screen_width);
 145        DBG("%d,%d %08x %08x", info->pos_x, info->pos_y,
 146                        info->paddr, info->p_uv_addr);
 147
 148        /* TODO: */
 149        ilace = false;
 150        replication = false;
 151
 152        /* and finally, update omapdss: */
 153        ret = dispc_ovl_setup(omap_plane->id, info,
 154                        replication, omap_crtc_timings(crtc), false);
 155        if (ret) {
 156                dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret);
 157                return;
 158        }
 159
 160        dispc_ovl_enable(omap_plane->id, true);
 161        dispc_ovl_set_channel_out(omap_plane->id, channel);
 162}
 163
 164static void omap_plane_post_apply(struct omap_drm_apply *apply)
 165{
 166        struct omap_plane *omap_plane =
 167                        container_of(apply, struct omap_plane, apply);
 168        struct drm_plane *plane = &omap_plane->base;
 169        struct omap_drm_private *priv = plane->dev->dev_private;
 170        struct omap_overlay_info *info = &omap_plane->info;
 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        if (omap_plane->enabled) {
 182                omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
 183                                info->out_width, info->out_height);
 184        }
 185}
 186
 187static int apply(struct drm_plane *plane)
 188{
 189        if (plane->crtc) {
 190                struct omap_plane *omap_plane = to_omap_plane(plane);
 191                return omap_crtc_apply(plane->crtc, &omap_plane->apply);
 192        }
 193        return 0;
 194}
 195
 196int omap_plane_mode_set(struct drm_plane *plane,
 197                struct drm_crtc *crtc, struct drm_framebuffer *fb,
 198                int crtc_x, int crtc_y,
 199                unsigned int crtc_w, unsigned int crtc_h,
 200                uint32_t src_x, uint32_t src_y,
 201                uint32_t src_w, uint32_t src_h,
 202                void (*fxn)(void *), void *arg)
 203{
 204        struct omap_plane *omap_plane = to_omap_plane(plane);
 205        struct omap_drm_window *win = &omap_plane->win;
 206
 207        win->crtc_x = crtc_x;
 208        win->crtc_y = crtc_y;
 209        win->crtc_w = crtc_w;
 210        win->crtc_h = crtc_h;
 211
 212        /* src values are in Q16 fixed point, convert to integer: */
 213        win->src_x = src_x >> 16;
 214        win->src_y = src_y >> 16;
 215        win->src_w = src_w >> 16;
 216        win->src_h = src_h >> 16;
 217
 218        if (fxn) {
 219                /* omap_crtc should ensure that a new page flip
 220                 * isn't permitted while there is one pending:
 221                 */
 222                BUG_ON(omap_plane->apply_done_cb.fxn);
 223
 224                omap_plane->apply_done_cb.fxn = fxn;
 225                omap_plane->apply_done_cb.arg = arg;
 226        }
 227
 228        if (plane->fb)
 229                drm_framebuffer_unreference(plane->fb);
 230
 231        drm_framebuffer_reference(fb);
 232
 233        plane->fb = fb;
 234        plane->crtc = crtc;
 235
 236        return apply(plane);
 237}
 238
 239static int omap_plane_update(struct drm_plane *plane,
 240                struct drm_crtc *crtc, struct drm_framebuffer *fb,
 241                int crtc_x, int crtc_y,
 242                unsigned int crtc_w, unsigned int crtc_h,
 243                uint32_t src_x, uint32_t src_y,
 244                uint32_t src_w, uint32_t src_h)
 245{
 246        struct omap_plane *omap_plane = to_omap_plane(plane);
 247        omap_plane->enabled = true;
 248
 249        /* omap_plane_mode_set() takes adjusted src */
 250        switch (omap_plane->win.rotation & 0xf) {
 251        case BIT(DRM_ROTATE_90):
 252        case BIT(DRM_ROTATE_270):
 253                swap(src_w, src_h);
 254                break;
 255        }
 256
 257        return omap_plane_mode_set(plane, crtc, fb,
 258                        crtc_x, crtc_y, crtc_w, crtc_h,
 259                        src_x, src_y, src_w, src_h,
 260                        NULL, NULL);
 261}
 262
 263static int omap_plane_disable(struct drm_plane *plane)
 264{
 265        struct omap_plane *omap_plane = to_omap_plane(plane);
 266        omap_plane->win.rotation = BIT(DRM_ROTATE_0);
 267        return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
 268}
 269
 270static void omap_plane_destroy(struct drm_plane *plane)
 271{
 272        struct omap_plane *omap_plane = to_omap_plane(plane);
 273
 274        DBG("%s", omap_plane->name);
 275
 276        omap_irq_unregister(plane->dev, &omap_plane->error_irq);
 277
 278        omap_plane_disable(plane);
 279        drm_plane_cleanup(plane);
 280
 281        drm_flip_work_cleanup(&omap_plane->unpin_work);
 282
 283        kfree(omap_plane);
 284}
 285
 286int omap_plane_dpms(struct drm_plane *plane, int mode)
 287{
 288        struct omap_plane *omap_plane = to_omap_plane(plane);
 289        bool enabled = (mode == DRM_MODE_DPMS_ON);
 290        int ret = 0;
 291
 292        if (enabled != omap_plane->enabled) {
 293                omap_plane->enabled = enabled;
 294                ret = apply(plane);
 295        }
 296
 297        return ret;
 298}
 299
 300/* helper to install properties which are common to planes and crtcs */
 301void omap_plane_install_properties(struct drm_plane *plane,
 302                struct drm_mode_object *obj)
 303{
 304        struct drm_device *dev = plane->dev;
 305        struct omap_drm_private *priv = dev->dev_private;
 306        struct drm_property *prop;
 307
 308        if (priv->has_dmm) {
 309                prop = priv->rotation_prop;
 310                if (!prop) {
 311                        const struct drm_prop_enum_list props[] = {
 312                                        { DRM_ROTATE_0,   "rotate-0" },
 313                                        { DRM_ROTATE_90,  "rotate-90" },
 314                                        { DRM_ROTATE_180, "rotate-180" },
 315                                        { DRM_ROTATE_270, "rotate-270" },
 316                                        { DRM_REFLECT_X,  "reflect-x" },
 317                                        { DRM_REFLECT_Y,  "reflect-y" },
 318                        };
 319                        prop = drm_property_create_bitmask(dev, 0, "rotation",
 320                                        props, ARRAY_SIZE(props));
 321                        if (prop == NULL)
 322                                return;
 323                        priv->rotation_prop = prop;
 324                }
 325                drm_object_attach_property(obj, prop, 0);
 326        }
 327
 328        prop = priv->zorder_prop;
 329        if (!prop) {
 330                prop = drm_property_create_range(dev, 0, "zorder", 0, 3);
 331                if (prop == NULL)
 332                        return;
 333                priv->zorder_prop = prop;
 334        }
 335        drm_object_attach_property(obj, prop, 0);
 336}
 337
 338int omap_plane_set_property(struct drm_plane *plane,
 339                struct drm_property *property, uint64_t val)
 340{
 341        struct omap_plane *omap_plane = to_omap_plane(plane);
 342        struct omap_drm_private *priv = plane->dev->dev_private;
 343        int ret = -EINVAL;
 344
 345        if (property == priv->rotation_prop) {
 346                DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
 347                omap_plane->win.rotation = val;
 348                ret = apply(plane);
 349        } else if (property == priv->zorder_prop) {
 350                DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
 351                omap_plane->info.zorder = val;
 352                ret = apply(plane);
 353        }
 354
 355        return ret;
 356}
 357
 358static const struct drm_plane_funcs omap_plane_funcs = {
 359                .update_plane = omap_plane_update,
 360                .disable_plane = omap_plane_disable,
 361                .destroy = omap_plane_destroy,
 362                .set_property = omap_plane_set_property,
 363};
 364
 365static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
 366{
 367        struct omap_plane *omap_plane =
 368                        container_of(irq, struct omap_plane, error_irq);
 369        DRM_ERROR("%s: errors: %08x\n", omap_plane->name, irqstatus);
 370}
 371
 372static const char *plane_names[] = {
 373                [OMAP_DSS_GFX] = "gfx",
 374                [OMAP_DSS_VIDEO1] = "vid1",
 375                [OMAP_DSS_VIDEO2] = "vid2",
 376                [OMAP_DSS_VIDEO3] = "vid3",
 377};
 378
 379static const uint32_t error_irqs[] = {
 380                [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
 381                [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
 382                [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
 383                [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
 384};
 385
 386/* initialize plane */
 387struct drm_plane *omap_plane_init(struct drm_device *dev,
 388                int id, bool private_plane)
 389{
 390        struct omap_drm_private *priv = dev->dev_private;
 391        struct drm_plane *plane = NULL;
 392        struct omap_plane *omap_plane;
 393        struct omap_overlay_info *info;
 394        int ret;
 395
 396        DBG("%s: priv=%d", plane_names[id], private_plane);
 397
 398        omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
 399        if (!omap_plane)
 400                goto fail;
 401
 402        ret = drm_flip_work_init(&omap_plane->unpin_work, 16,
 403                        "unpin", unpin_worker);
 404        if (ret) {
 405                dev_err(dev->dev, "could not allocate unpin FIFO\n");
 406                goto fail;
 407        }
 408
 409        omap_plane->nformats = omap_framebuffer_get_formats(
 410                        omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
 411                        dss_feat_get_supported_color_modes(id));
 412        omap_plane->id = id;
 413        omap_plane->name = plane_names[id];
 414
 415        plane = &omap_plane->base;
 416
 417        omap_plane->apply.pre_apply  = omap_plane_pre_apply;
 418        omap_plane->apply.post_apply = omap_plane_post_apply;
 419
 420        omap_plane->error_irq.irqmask = error_irqs[id];
 421        omap_plane->error_irq.irq = omap_plane_error_irq;
 422        omap_irq_register(dev, &omap_plane->error_irq);
 423
 424        drm_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &omap_plane_funcs,
 425                        omap_plane->formats, omap_plane->nformats, private_plane);
 426
 427        omap_plane_install_properties(plane, &plane->base);
 428
 429        /* get our starting configuration, set defaults for parameters
 430         * we don't currently use, etc:
 431         */
 432        info = &omap_plane->info;
 433        info->rotation_type = OMAP_DSS_ROT_DMA;
 434        info->rotation = OMAP_DSS_ROT_0;
 435        info->global_alpha = 0xff;
 436        info->mirror = 0;
 437
 438        /* Set defaults depending on whether we are a CRTC or overlay
 439         * layer.
 440         * TODO add ioctl to give userspace an API to change this.. this
 441         * will come in a subsequent patch.
 442         */
 443        if (private_plane)
 444                omap_plane->info.zorder = 0;
 445        else
 446                omap_plane->info.zorder = id;
 447
 448        return plane;
 449
 450fail:
 451        if (plane)
 452                omap_plane_destroy(plane);
 453
 454        return NULL;
 455}
 456