linux/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
<<
>>
Prefs
   1/*
   2 * rcar_du_vsp.h  --  R-Car Display Unit VSP-Based Compositor
   3 *
   4 * Copyright (C) 2015 Renesas Electronics Corporation
   5 *
   6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <drm/drmP.h>
  15#include <drm/drm_atomic_helper.h>
  16#include <drm/drm_crtc.h>
  17#include <drm/drm_crtc_helper.h>
  18#include <drm/drm_fb_cma_helper.h>
  19#include <drm/drm_gem_cma_helper.h>
  20#include <drm/drm_gem_framebuffer_helper.h>
  21#include <drm/drm_plane_helper.h>
  22
  23#include <linux/bitops.h>
  24#include <linux/dma-mapping.h>
  25#include <linux/of_platform.h>
  26#include <linux/scatterlist.h>
  27#include <linux/videodev2.h>
  28
  29#include <media/vsp1.h>
  30
  31#include "rcar_du_drv.h"
  32#include "rcar_du_kms.h"
  33#include "rcar_du_vsp.h"
  34
  35static void rcar_du_vsp_complete(void *private, bool completed, u32 crc)
  36{
  37        struct rcar_du_crtc *crtc = private;
  38
  39        if (crtc->vblank_enable)
  40                drm_crtc_handle_vblank(&crtc->crtc);
  41
  42        if (completed)
  43                rcar_du_crtc_finish_page_flip(crtc);
  44
  45        drm_crtc_add_crc_entry(&crtc->crtc, false, 0, &crc);
  46}
  47
  48void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
  49{
  50        const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode;
  51        struct rcar_du_device *rcdu = crtc->group->dev;
  52        struct vsp1_du_lif_config cfg = {
  53                .width = mode->hdisplay,
  54                .height = mode->vdisplay,
  55                .callback = rcar_du_vsp_complete,
  56                .callback_data = crtc,
  57        };
  58        struct rcar_du_plane_state state = {
  59                .state = {
  60                        .alpha = DRM_BLEND_ALPHA_OPAQUE,
  61                        .crtc = &crtc->crtc,
  62                        .dst.x1 = 0,
  63                        .dst.y1 = 0,
  64                        .dst.x2 = mode->hdisplay,
  65                        .dst.y2 = mode->vdisplay,
  66                        .src.x1 = 0,
  67                        .src.y1 = 0,
  68                        .src.x2 = mode->hdisplay << 16,
  69                        .src.y2 = mode->vdisplay << 16,
  70                        .zpos = 0,
  71                },
  72                .format = rcar_du_format_info(DRM_FORMAT_ARGB8888),
  73                .source = RCAR_DU_PLANE_VSPD1,
  74                .colorkey = 0,
  75        };
  76
  77        if (rcdu->info->gen >= 3)
  78                state.hwindex = (crtc->index % 2) ? 2 : 0;
  79        else
  80                state.hwindex = crtc->index % 2;
  81
  82        __rcar_du_plane_setup(crtc->group, &state);
  83
  84        /*
  85         * Ensure that the plane source configuration takes effect by requesting
  86         * a restart of the group. See rcar_du_plane_atomic_update() for a more
  87         * detailed explanation.
  88         *
  89         * TODO: Check whether this is still needed on Gen3.
  90         */
  91        crtc->group->need_restart = true;
  92
  93        vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
  94}
  95
  96void rcar_du_vsp_disable(struct rcar_du_crtc *crtc)
  97{
  98        vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL);
  99}
 100
 101void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc)
 102{
 103        vsp1_du_atomic_begin(crtc->vsp->vsp, crtc->vsp_pipe);
 104}
 105
 106void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc)
 107{
 108        struct vsp1_du_atomic_pipe_config cfg = { { 0, } };
 109        struct rcar_du_crtc_state *state;
 110
 111        state = to_rcar_crtc_state(crtc->crtc.state);
 112        cfg.crc = state->crc;
 113
 114        vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
 115}
 116
 117/* Keep the two tables in sync. */
 118static const u32 formats_kms[] = {
 119        DRM_FORMAT_RGB332,
 120        DRM_FORMAT_ARGB4444,
 121        DRM_FORMAT_XRGB4444,
 122        DRM_FORMAT_ARGB1555,
 123        DRM_FORMAT_XRGB1555,
 124        DRM_FORMAT_RGB565,
 125        DRM_FORMAT_BGR888,
 126        DRM_FORMAT_RGB888,
 127        DRM_FORMAT_BGRA8888,
 128        DRM_FORMAT_BGRX8888,
 129        DRM_FORMAT_ARGB8888,
 130        DRM_FORMAT_XRGB8888,
 131        DRM_FORMAT_UYVY,
 132        DRM_FORMAT_VYUY,
 133        DRM_FORMAT_YUYV,
 134        DRM_FORMAT_YVYU,
 135        DRM_FORMAT_NV12,
 136        DRM_FORMAT_NV21,
 137        DRM_FORMAT_NV16,
 138        DRM_FORMAT_NV61,
 139        DRM_FORMAT_YUV420,
 140        DRM_FORMAT_YVU420,
 141        DRM_FORMAT_YUV422,
 142        DRM_FORMAT_YVU422,
 143        DRM_FORMAT_YUV444,
 144        DRM_FORMAT_YVU444,
 145};
 146
 147static const u32 formats_v4l2[] = {
 148        V4L2_PIX_FMT_RGB332,
 149        V4L2_PIX_FMT_ARGB444,
 150        V4L2_PIX_FMT_XRGB444,
 151        V4L2_PIX_FMT_ARGB555,
 152        V4L2_PIX_FMT_XRGB555,
 153        V4L2_PIX_FMT_RGB565,
 154        V4L2_PIX_FMT_RGB24,
 155        V4L2_PIX_FMT_BGR24,
 156        V4L2_PIX_FMT_ARGB32,
 157        V4L2_PIX_FMT_XRGB32,
 158        V4L2_PIX_FMT_ABGR32,
 159        V4L2_PIX_FMT_XBGR32,
 160        V4L2_PIX_FMT_UYVY,
 161        V4L2_PIX_FMT_VYUY,
 162        V4L2_PIX_FMT_YUYV,
 163        V4L2_PIX_FMT_YVYU,
 164        V4L2_PIX_FMT_NV12M,
 165        V4L2_PIX_FMT_NV21M,
 166        V4L2_PIX_FMT_NV16M,
 167        V4L2_PIX_FMT_NV61M,
 168        V4L2_PIX_FMT_YUV420M,
 169        V4L2_PIX_FMT_YVU420M,
 170        V4L2_PIX_FMT_YUV422M,
 171        V4L2_PIX_FMT_YVU422M,
 172        V4L2_PIX_FMT_YUV444M,
 173        V4L2_PIX_FMT_YVU444M,
 174};
 175
 176static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
 177{
 178        struct rcar_du_vsp_plane_state *state =
 179                to_rcar_vsp_plane_state(plane->plane.state);
 180        struct rcar_du_crtc *crtc = to_rcar_crtc(state->state.crtc);
 181        struct drm_framebuffer *fb = plane->plane.state->fb;
 182        struct vsp1_du_atomic_config cfg = {
 183                .pixelformat = 0,
 184                .pitch = fb->pitches[0],
 185                .alpha = state->state.alpha >> 8,
 186                .zpos = state->state.zpos,
 187        };
 188        unsigned int i;
 189
 190        cfg.src.left = state->state.src.x1 >> 16;
 191        cfg.src.top = state->state.src.y1 >> 16;
 192        cfg.src.width = drm_rect_width(&state->state.src) >> 16;
 193        cfg.src.height = drm_rect_height(&state->state.src) >> 16;
 194
 195        cfg.dst.left = state->state.dst.x1;
 196        cfg.dst.top = state->state.dst.y1;
 197        cfg.dst.width = drm_rect_width(&state->state.dst);
 198        cfg.dst.height = drm_rect_height(&state->state.dst);
 199
 200        for (i = 0; i < state->format->planes; ++i)
 201                cfg.mem[i] = sg_dma_address(state->sg_tables[i].sgl)
 202                           + fb->offsets[i];
 203
 204        for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) {
 205                if (formats_kms[i] == state->format->fourcc) {
 206                        cfg.pixelformat = formats_v4l2[i];
 207                        break;
 208                }
 209        }
 210
 211        vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe,
 212                              plane->index, &cfg);
 213}
 214
 215static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
 216                                        struct drm_plane_state *state)
 217{
 218        struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
 219        struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
 220        struct rcar_du_device *rcdu = vsp->dev;
 221        unsigned int i;
 222        int ret;
 223
 224        /*
 225         * There's no need to prepare (and unprepare) the framebuffer when the
 226         * plane is not visible, as it will not be displayed.
 227         */
 228        if (!state->visible)
 229                return 0;
 230
 231        for (i = 0; i < rstate->format->planes; ++i) {
 232                struct drm_gem_cma_object *gem =
 233                        drm_fb_cma_get_gem_obj(state->fb, i);
 234                struct sg_table *sgt = &rstate->sg_tables[i];
 235
 236                ret = dma_get_sgtable(rcdu->dev, sgt, gem->vaddr, gem->paddr,
 237                                      gem->base.size);
 238                if (ret)
 239                        goto fail;
 240
 241                ret = vsp1_du_map_sg(vsp->vsp, sgt);
 242                if (!ret) {
 243                        sg_free_table(sgt);
 244                        ret = -ENOMEM;
 245                        goto fail;
 246                }
 247        }
 248
 249        ret = drm_gem_fb_prepare_fb(plane, state);
 250        if (ret)
 251                goto fail;
 252
 253        return 0;
 254
 255fail:
 256        while (i--) {
 257                struct sg_table *sgt = &rstate->sg_tables[i];
 258
 259                vsp1_du_unmap_sg(vsp->vsp, sgt);
 260                sg_free_table(sgt);
 261        }
 262
 263        return ret;
 264}
 265
 266static void rcar_du_vsp_plane_cleanup_fb(struct drm_plane *plane,
 267                                         struct drm_plane_state *state)
 268{
 269        struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
 270        struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
 271        unsigned int i;
 272
 273        if (!state->visible)
 274                return;
 275
 276        for (i = 0; i < rstate->format->planes; ++i) {
 277                struct sg_table *sgt = &rstate->sg_tables[i];
 278
 279                vsp1_du_unmap_sg(vsp->vsp, sgt);
 280                sg_free_table(sgt);
 281        }
 282}
 283
 284static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
 285                                          struct drm_plane_state *state)
 286{
 287        struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
 288
 289        return __rcar_du_plane_atomic_check(plane, state, &rstate->format);
 290}
 291
 292static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
 293                                        struct drm_plane_state *old_state)
 294{
 295        struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
 296        struct rcar_du_crtc *crtc = to_rcar_crtc(old_state->crtc);
 297
 298        if (plane->state->visible)
 299                rcar_du_vsp_plane_setup(rplane);
 300        else
 301                vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe,
 302                                      rplane->index, NULL);
 303}
 304
 305static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
 306        .prepare_fb = rcar_du_vsp_plane_prepare_fb,
 307        .cleanup_fb = rcar_du_vsp_plane_cleanup_fb,
 308        .atomic_check = rcar_du_vsp_plane_atomic_check,
 309        .atomic_update = rcar_du_vsp_plane_atomic_update,
 310};
 311
 312static struct drm_plane_state *
 313rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane)
 314{
 315        struct rcar_du_vsp_plane_state *copy;
 316
 317        if (WARN_ON(!plane->state))
 318                return NULL;
 319
 320        copy = kzalloc(sizeof(*copy), GFP_KERNEL);
 321        if (copy == NULL)
 322                return NULL;
 323
 324        __drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
 325
 326        return &copy->state;
 327}
 328
 329static void rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane,
 330                                                   struct drm_plane_state *state)
 331{
 332        __drm_atomic_helper_plane_destroy_state(state);
 333        kfree(to_rcar_vsp_plane_state(state));
 334}
 335
 336static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
 337{
 338        struct rcar_du_vsp_plane_state *state;
 339
 340        if (plane->state) {
 341                rcar_du_vsp_plane_atomic_destroy_state(plane, plane->state);
 342                plane->state = NULL;
 343        }
 344
 345        state = kzalloc(sizeof(*state), GFP_KERNEL);
 346        if (state == NULL)
 347                return;
 348
 349        state->state.alpha = DRM_BLEND_ALPHA_OPAQUE;
 350        state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
 351
 352        plane->state = &state->state;
 353        plane->state->plane = plane;
 354}
 355
 356static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
 357        .update_plane = drm_atomic_helper_update_plane,
 358        .disable_plane = drm_atomic_helper_disable_plane,
 359        .reset = rcar_du_vsp_plane_reset,
 360        .destroy = drm_plane_cleanup,
 361        .atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state,
 362        .atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
 363};
 364
 365int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
 366                     unsigned int crtcs)
 367{
 368        struct rcar_du_device *rcdu = vsp->dev;
 369        struct platform_device *pdev;
 370        unsigned int num_crtcs = hweight32(crtcs);
 371        unsigned int i;
 372        int ret;
 373
 374        /* Find the VSP device and initialize it. */
 375        pdev = of_find_device_by_node(np);
 376        if (!pdev)
 377                return -ENXIO;
 378
 379        vsp->vsp = &pdev->dev;
 380
 381        ret = vsp1_du_init(vsp->vsp);
 382        if (ret < 0)
 383                return ret;
 384
 385         /*
 386          * The VSP2D (Gen3) has 5 RPFs, but the VSP1D (Gen2) is limited to
 387          * 4 RPFs.
 388          */
 389        vsp->num_planes = rcdu->info->gen >= 3 ? 5 : 4;
 390
 391        vsp->planes = devm_kcalloc(rcdu->dev, vsp->num_planes,
 392                                   sizeof(*vsp->planes), GFP_KERNEL);
 393        if (!vsp->planes)
 394                return -ENOMEM;
 395
 396        for (i = 0; i < vsp->num_planes; ++i) {
 397                enum drm_plane_type type = i < num_crtcs
 398                                         ? DRM_PLANE_TYPE_PRIMARY
 399                                         : DRM_PLANE_TYPE_OVERLAY;
 400                struct rcar_du_vsp_plane *plane = &vsp->planes[i];
 401
 402                plane->vsp = vsp;
 403                plane->index = i;
 404
 405                ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
 406                                               &rcar_du_vsp_plane_funcs,
 407                                               formats_kms,
 408                                               ARRAY_SIZE(formats_kms),
 409                                               NULL, type, NULL);
 410                if (ret < 0)
 411                        return ret;
 412
 413                drm_plane_helper_add(&plane->plane,
 414                                     &rcar_du_vsp_plane_helper_funcs);
 415
 416                if (type == DRM_PLANE_TYPE_PRIMARY)
 417                        continue;
 418
 419                drm_plane_create_alpha_property(&plane->plane);
 420                drm_plane_create_zpos_property(&plane->plane, 1, 1,
 421                                               vsp->num_planes - 1);
 422        }
 423
 424        return 0;
 425}
 426