linux/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2014 The Linux Foundation. All rights reserved.
   3 * Copyright (C) 2013 Red Hat
   4 * Author: Rob Clark <robdclark@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms of the GNU General Public License version 2 as published by
   8 * the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14 *
  15 * You should have received a copy of the GNU General Public License along with
  16 * this program.  If not, see <http://www.gnu.org/licenses/>.
  17 */
  18
  19#include "mdp5_kms.h"
  20
  21#define MAX_PLANE       4
  22
  23struct mdp5_plane {
  24        struct drm_plane base;
  25        const char *name;
  26
  27        enum mdp5_pipe pipe;
  28
  29        spinlock_t pipe_lock;   /* protect REG_MDP5_PIPE_* registers */
  30        uint32_t reg_offset;
  31
  32        uint32_t flush_mask;    /* used to commit pipe registers */
  33
  34        uint32_t nformats;
  35        uint32_t formats[32];
  36
  37        bool enabled;
  38};
  39#define to_mdp5_plane(x) container_of(x, struct mdp5_plane, base)
  40
  41static int mdp5_plane_mode_set(struct drm_plane *plane,
  42                struct drm_crtc *crtc, struct drm_framebuffer *fb,
  43                int crtc_x, int crtc_y,
  44                unsigned int crtc_w, unsigned int crtc_h,
  45                uint32_t src_x, uint32_t src_y,
  46                uint32_t src_w, uint32_t src_h);
  47static void set_scanout_locked(struct drm_plane *plane,
  48                struct drm_framebuffer *fb);
  49
  50static struct mdp5_kms *get_kms(struct drm_plane *plane)
  51{
  52        struct msm_drm_private *priv = plane->dev->dev_private;
  53        return to_mdp5_kms(to_mdp_kms(priv->kms));
  54}
  55
  56static bool plane_enabled(struct drm_plane_state *state)
  57{
  58        return state->fb && state->crtc;
  59}
  60
  61static int mdp5_plane_disable(struct drm_plane *plane)
  62{
  63        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
  64        struct mdp5_kms *mdp5_kms = get_kms(plane);
  65        enum mdp5_pipe pipe = mdp5_plane->pipe;
  66
  67        DBG("%s: disable", mdp5_plane->name);
  68
  69        if (mdp5_kms) {
  70                /* Release the memory we requested earlier from the SMP: */
  71                mdp5_smp_release(mdp5_kms->smp, pipe);
  72        }
  73
  74        return 0;
  75}
  76
  77static void mdp5_plane_destroy(struct drm_plane *plane)
  78{
  79        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
  80
  81        drm_plane_helper_disable(plane);
  82        drm_plane_cleanup(plane);
  83
  84        kfree(mdp5_plane);
  85}
  86
  87/* helper to install properties which are common to planes and crtcs */
  88void mdp5_plane_install_properties(struct drm_plane *plane,
  89                struct drm_mode_object *obj)
  90{
  91        // XXX
  92}
  93
  94int mdp5_plane_set_property(struct drm_plane *plane,
  95                struct drm_property *property, uint64_t val)
  96{
  97        // XXX
  98        return -EINVAL;
  99}
 100
 101static void mdp5_plane_reset(struct drm_plane *plane)
 102{
 103        struct mdp5_plane_state *mdp5_state;
 104
 105        if (plane->state && plane->state->fb)
 106                drm_framebuffer_unreference(plane->state->fb);
 107
 108        kfree(to_mdp5_plane_state(plane->state));
 109        mdp5_state = kzalloc(sizeof(*mdp5_state), GFP_KERNEL);
 110
 111        if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
 112                mdp5_state->zpos = 0;
 113        } else {
 114                mdp5_state->zpos = 1 + drm_plane_index(plane);
 115        }
 116
 117        plane->state = &mdp5_state->base;
 118}
 119
 120static struct drm_plane_state *
 121mdp5_plane_duplicate_state(struct drm_plane *plane)
 122{
 123        struct mdp5_plane_state *mdp5_state;
 124
 125        if (WARN_ON(!plane->state))
 126                return NULL;
 127
 128        mdp5_state = kmemdup(to_mdp5_plane_state(plane->state),
 129                        sizeof(*mdp5_state), GFP_KERNEL);
 130
 131        if (mdp5_state && mdp5_state->base.fb)
 132                drm_framebuffer_reference(mdp5_state->base.fb);
 133
 134        mdp5_state->mode_changed = false;
 135        mdp5_state->pending = false;
 136
 137        return &mdp5_state->base;
 138}
 139
 140static void mdp5_plane_destroy_state(struct drm_plane *plane,
 141                struct drm_plane_state *state)
 142{
 143        if (state->fb)
 144                drm_framebuffer_unreference(state->fb);
 145
 146        kfree(to_mdp5_plane_state(state));
 147}
 148
 149static const struct drm_plane_funcs mdp5_plane_funcs = {
 150                .update_plane = drm_atomic_helper_update_plane,
 151                .disable_plane = drm_atomic_helper_disable_plane,
 152                .destroy = mdp5_plane_destroy,
 153                .set_property = mdp5_plane_set_property,
 154                .reset = mdp5_plane_reset,
 155                .atomic_duplicate_state = mdp5_plane_duplicate_state,
 156                .atomic_destroy_state = mdp5_plane_destroy_state,
 157};
 158
 159static int mdp5_plane_prepare_fb(struct drm_plane *plane,
 160                struct drm_framebuffer *fb)
 161{
 162        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
 163        struct mdp5_kms *mdp5_kms = get_kms(plane);
 164
 165        DBG("%s: prepare: FB[%u]", mdp5_plane->name, fb->base.id);
 166        return msm_framebuffer_prepare(fb, mdp5_kms->id);
 167}
 168
 169static void mdp5_plane_cleanup_fb(struct drm_plane *plane,
 170                struct drm_framebuffer *fb)
 171{
 172        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
 173        struct mdp5_kms *mdp5_kms = get_kms(plane);
 174
 175        DBG("%s: cleanup: FB[%u]", mdp5_plane->name, fb->base.id);
 176        msm_framebuffer_cleanup(fb, mdp5_kms->id);
 177}
 178
 179static int mdp5_plane_atomic_check(struct drm_plane *plane,
 180                struct drm_plane_state *state)
 181{
 182        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
 183        struct drm_plane_state *old_state = plane->state;
 184
 185        DBG("%s: check (%d -> %d)", mdp5_plane->name,
 186                        plane_enabled(old_state), plane_enabled(state));
 187
 188        if (plane_enabled(state) && plane_enabled(old_state)) {
 189                /* we cannot change SMP block configuration during scanout: */
 190                bool full_modeset = false;
 191                if (state->fb->pixel_format != old_state->fb->pixel_format) {
 192                        DBG("%s: pixel_format change!", mdp5_plane->name);
 193                        full_modeset = true;
 194                }
 195                if (state->src_w != old_state->src_w) {
 196                        DBG("%s: src_w change!", mdp5_plane->name);
 197                        full_modeset = true;
 198                }
 199                if (to_mdp5_plane_state(old_state)->pending) {
 200                        DBG("%s: still pending!", mdp5_plane->name);
 201                        full_modeset = true;
 202                }
 203                if (full_modeset) {
 204                        struct drm_crtc_state *crtc_state =
 205                                        drm_atomic_get_crtc_state(state->state, state->crtc);
 206                        crtc_state->mode_changed = true;
 207                        to_mdp5_plane_state(state)->mode_changed = true;
 208                }
 209        } else {
 210                to_mdp5_plane_state(state)->mode_changed = true;
 211        }
 212
 213        return 0;
 214}
 215
 216static void mdp5_plane_atomic_update(struct drm_plane *plane,
 217                                     struct drm_plane_state *old_state)
 218{
 219        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
 220        struct drm_plane_state *state = plane->state;
 221
 222        DBG("%s: update", mdp5_plane->name);
 223
 224        if (!plane_enabled(state)) {
 225                to_mdp5_plane_state(state)->pending = true;
 226                mdp5_plane_disable(plane);
 227        } else if (to_mdp5_plane_state(state)->mode_changed) {
 228                int ret;
 229                to_mdp5_plane_state(state)->pending = true;
 230                ret = mdp5_plane_mode_set(plane,
 231                                state->crtc, state->fb,
 232                                state->crtc_x, state->crtc_y,
 233                                state->crtc_w, state->crtc_h,
 234                                state->src_x,  state->src_y,
 235                                state->src_w, state->src_h);
 236                /* atomic_check should have ensured that this doesn't fail */
 237                WARN_ON(ret < 0);
 238        } else {
 239                unsigned long flags;
 240                spin_lock_irqsave(&mdp5_plane->pipe_lock, flags);
 241                set_scanout_locked(plane, state->fb);
 242                spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags);
 243        }
 244}
 245
 246static const struct drm_plane_helper_funcs mdp5_plane_helper_funcs = {
 247                .prepare_fb = mdp5_plane_prepare_fb,
 248                .cleanup_fb = mdp5_plane_cleanup_fb,
 249                .atomic_check = mdp5_plane_atomic_check,
 250                .atomic_update = mdp5_plane_atomic_update,
 251};
 252
 253static void set_scanout_locked(struct drm_plane *plane,
 254                struct drm_framebuffer *fb)
 255{
 256        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
 257        struct mdp5_kms *mdp5_kms = get_kms(plane);
 258        enum mdp5_pipe pipe = mdp5_plane->pipe;
 259
 260        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe),
 261                        MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) |
 262                        MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1]));
 263
 264        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe),
 265                        MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) |
 266                        MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3]));
 267
 268        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe),
 269                        msm_framebuffer_iova(fb, mdp5_kms->id, 0));
 270        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe),
 271                        msm_framebuffer_iova(fb, mdp5_kms->id, 1));
 272        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe),
 273                        msm_framebuffer_iova(fb, mdp5_kms->id, 2));
 274        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe),
 275                        msm_framebuffer_iova(fb, mdp5_kms->id, 4));
 276
 277        plane->fb = fb;
 278}
 279
 280static int mdp5_plane_mode_set(struct drm_plane *plane,
 281                struct drm_crtc *crtc, struct drm_framebuffer *fb,
 282                int crtc_x, int crtc_y,
 283                unsigned int crtc_w, unsigned int crtc_h,
 284                uint32_t src_x, uint32_t src_y,
 285                uint32_t src_w, uint32_t src_h)
 286{
 287        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
 288        struct mdp5_kms *mdp5_kms = get_kms(plane);
 289        enum mdp5_pipe pipe = mdp5_plane->pipe;
 290        const struct mdp_format *format;
 291        uint32_t nplanes, config = 0;
 292        uint32_t phasex_step = 0, phasey_step = 0;
 293        uint32_t hdecm = 0, vdecm = 0;
 294        unsigned long flags;
 295        int ret;
 296
 297        nplanes = drm_format_num_planes(fb->pixel_format);
 298
 299        /* bad formats should already be rejected: */
 300        if (WARN_ON(nplanes > pipe2nclients(pipe)))
 301                return -EINVAL;
 302
 303        /* src values are in Q16 fixed point, convert to integer: */
 304        src_x = src_x >> 16;
 305        src_y = src_y >> 16;
 306        src_w = src_w >> 16;
 307        src_h = src_h >> 16;
 308
 309        DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", mdp5_plane->name,
 310                        fb->base.id, src_x, src_y, src_w, src_h,
 311                        crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h);
 312
 313        /* Request some memory from the SMP: */
 314        ret = mdp5_smp_request(mdp5_kms->smp,
 315                        mdp5_plane->pipe, fb->pixel_format, src_w);
 316        if (ret)
 317                return ret;
 318
 319        /*
 320         * Currently we update the hw for allocations/requests immediately,
 321         * but once atomic modeset/pageflip is in place, the allocation
 322         * would move into atomic->check_plane_state(), while updating the
 323         * hw would remain here:
 324         */
 325        mdp5_smp_configure(mdp5_kms->smp, pipe);
 326
 327        if (src_w != crtc_w) {
 328                config |= MDP5_PIPE_SCALE_CONFIG_SCALEX_EN;
 329                /* TODO calc phasex_step, hdecm */
 330        }
 331
 332        if (src_h != crtc_h) {
 333                config |= MDP5_PIPE_SCALE_CONFIG_SCALEY_EN;
 334                /* TODO calc phasey_step, vdecm */
 335        }
 336
 337        spin_lock_irqsave(&mdp5_plane->pipe_lock, flags);
 338
 339        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe),
 340                        MDP5_PIPE_SRC_IMG_SIZE_WIDTH(src_w) |
 341                        MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(src_h));
 342
 343        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_SIZE(pipe),
 344                        MDP5_PIPE_SRC_SIZE_WIDTH(src_w) |
 345                        MDP5_PIPE_SRC_SIZE_HEIGHT(src_h));
 346
 347        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_XY(pipe),
 348                        MDP5_PIPE_SRC_XY_X(src_x) |
 349                        MDP5_PIPE_SRC_XY_Y(src_y));
 350
 351        mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_SIZE(pipe),
 352                        MDP5_PIPE_OUT_SIZE_WIDTH(crtc_w) |
 353                        MDP5_PIPE_OUT_SIZE_HEIGHT(crtc_h));
 354
 355        mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_XY(pipe),
 356                        MDP5_PIPE_OUT_XY_X(crtc_x) |
 357                        MDP5_PIPE_OUT_XY_Y(crtc_y));
 358
 359        format = to_mdp_format(msm_framebuffer_format(fb));
 360
 361        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe),
 362                        MDP5_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) |
 363                        MDP5_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) |
 364                        MDP5_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) |
 365                        MDP5_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) |
 366                        COND(format->alpha_enable, MDP5_PIPE_SRC_FORMAT_ALPHA_ENABLE) |
 367                        MDP5_PIPE_SRC_FORMAT_CPP(format->cpp - 1) |
 368                        MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) |
 369                        COND(format->unpack_tight, MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT) |
 370                        MDP5_PIPE_SRC_FORMAT_NUM_PLANES(nplanes - 1) |
 371                        MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(CHROMA_RGB));
 372
 373        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe),
 374                        MDP5_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) |
 375                        MDP5_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) |
 376                        MDP5_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) |
 377                        MDP5_PIPE_SRC_UNPACK_ELEM3(format->unpack[3]));
 378
 379        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe),
 380                        MDP5_PIPE_SRC_OP_MODE_BWC(BWC_LOSSLESS));
 381
 382        /* not using secure mode: */
 383        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0);
 384
 385        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe), phasex_step);
 386        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe), phasey_step);
 387        mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe),
 388                        MDP5_PIPE_DECIMATION_VERT(vdecm) |
 389                        MDP5_PIPE_DECIMATION_HORZ(hdecm));
 390        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe),
 391                        MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER(SCALE_FILTER_NEAREST) |
 392                        MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER(SCALE_FILTER_NEAREST) |
 393                        MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER(SCALE_FILTER_NEAREST) |
 394                        MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER(SCALE_FILTER_NEAREST) |
 395                        MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER(SCALE_FILTER_NEAREST) |
 396                        MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER(SCALE_FILTER_NEAREST));
 397
 398        set_scanout_locked(plane, fb);
 399
 400        spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags);
 401
 402        return ret;
 403}
 404
 405void mdp5_plane_complete_flip(struct drm_plane *plane)
 406{
 407        struct mdp5_kms *mdp5_kms = get_kms(plane);
 408        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
 409        enum mdp5_pipe pipe = mdp5_plane->pipe;
 410
 411        DBG("%s: complete flip", mdp5_plane->name);
 412
 413        mdp5_smp_commit(mdp5_kms->smp, pipe);
 414
 415        to_mdp5_plane_state(plane->state)->pending = false;
 416}
 417
 418enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane)
 419{
 420        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
 421        return mdp5_plane->pipe;
 422}
 423
 424uint32_t mdp5_plane_get_flush(struct drm_plane *plane)
 425{
 426        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
 427
 428        return mdp5_plane->flush_mask;
 429}
 430
 431/* initialize plane */
 432struct drm_plane *mdp5_plane_init(struct drm_device *dev,
 433                enum mdp5_pipe pipe, bool private_plane, uint32_t reg_offset)
 434{
 435        struct drm_plane *plane = NULL;
 436        struct mdp5_plane *mdp5_plane;
 437        int ret;
 438        enum drm_plane_type type;
 439
 440        mdp5_plane = kzalloc(sizeof(*mdp5_plane), GFP_KERNEL);
 441        if (!mdp5_plane) {
 442                ret = -ENOMEM;
 443                goto fail;
 444        }
 445
 446        plane = &mdp5_plane->base;
 447
 448        mdp5_plane->pipe = pipe;
 449        mdp5_plane->name = pipe2name(pipe);
 450
 451        mdp5_plane->nformats = mdp5_get_formats(pipe, mdp5_plane->formats,
 452                        ARRAY_SIZE(mdp5_plane->formats));
 453
 454        mdp5_plane->flush_mask = mdp_ctl_flush_mask_pipe(pipe);
 455        mdp5_plane->reg_offset = reg_offset;
 456        spin_lock_init(&mdp5_plane->pipe_lock);
 457
 458        type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
 459        ret = drm_universal_plane_init(dev, plane, 0xff, &mdp5_plane_funcs,
 460                                 mdp5_plane->formats, mdp5_plane->nformats,
 461                                 type);
 462        if (ret)
 463                goto fail;
 464
 465        drm_plane_helper_add(plane, &mdp5_plane_helper_funcs);
 466
 467        mdp5_plane_install_properties(plane, &plane->base);
 468
 469        return plane;
 470
 471fail:
 472        if (plane)
 473                mdp5_plane_destroy(plane);
 474
 475        return ERR_PTR(ret);
 476}
 477