linux/drivers/gpu/drm/tegra/fb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2012-2013 Avionic Design GmbH
   4 * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
   5 *
   6 * Based on the KMS/FB CMA helpers
   7 *   Copyright (C) 2012 Analog Device Inc.
   8 */
   9
  10#include <linux/console.h>
  11
  12#include "drm.h"
  13#include "gem.h"
  14#include <drm/drm_gem_framebuffer_helper.h>
  15#include <drm/drm_modeset_helper.h>
  16
  17#ifdef CONFIG_DRM_FBDEV_EMULATION
  18static inline struct tegra_fbdev *to_tegra_fbdev(struct drm_fb_helper *helper)
  19{
  20        return container_of(helper, struct tegra_fbdev, base);
  21}
  22#endif
  23
  24struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
  25                                    unsigned int index)
  26{
  27        return to_tegra_bo(drm_gem_fb_get_obj(framebuffer, index));
  28}
  29
  30bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer)
  31{
  32        struct tegra_bo *bo = tegra_fb_get_plane(framebuffer, 0);
  33
  34        if (bo->flags & TEGRA_BO_BOTTOM_UP)
  35                return true;
  36
  37        return false;
  38}
  39
  40int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
  41                        struct tegra_bo_tiling *tiling)
  42{
  43        uint64_t modifier = framebuffer->modifier;
  44
  45        switch (modifier) {
  46        case DRM_FORMAT_MOD_LINEAR:
  47                tiling->mode = TEGRA_BO_TILING_MODE_PITCH;
  48                tiling->value = 0;
  49                break;
  50
  51        case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED:
  52                tiling->mode = TEGRA_BO_TILING_MODE_TILED;
  53                tiling->value = 0;
  54                break;
  55
  56        case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0):
  57                tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
  58                tiling->value = 0;
  59                break;
  60
  61        case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1):
  62                tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
  63                tiling->value = 1;
  64                break;
  65
  66        case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2):
  67                tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
  68                tiling->value = 2;
  69                break;
  70
  71        case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3):
  72                tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
  73                tiling->value = 3;
  74                break;
  75
  76        case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4):
  77                tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
  78                tiling->value = 4;
  79                break;
  80
  81        case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5):
  82                tiling->mode = TEGRA_BO_TILING_MODE_BLOCK;
  83                tiling->value = 5;
  84                break;
  85
  86        default:
  87                return -EINVAL;
  88        }
  89
  90        return 0;
  91}
  92
  93static const struct drm_framebuffer_funcs tegra_fb_funcs = {
  94        .destroy = drm_gem_fb_destroy,
  95        .create_handle = drm_gem_fb_create_handle,
  96};
  97
  98static struct drm_framebuffer *tegra_fb_alloc(struct drm_device *drm,
  99                                              const struct drm_mode_fb_cmd2 *mode_cmd,
 100                                              struct tegra_bo **planes,
 101                                              unsigned int num_planes)
 102{
 103        struct drm_framebuffer *fb;
 104        unsigned int i;
 105        int err;
 106
 107        fb = kzalloc(sizeof(*fb), GFP_KERNEL);
 108        if (!fb)
 109                return ERR_PTR(-ENOMEM);
 110
 111        drm_helper_mode_fill_fb_struct(drm, fb, mode_cmd);
 112
 113        for (i = 0; i < fb->format->num_planes; i++)
 114                fb->obj[i] = &planes[i]->gem;
 115
 116        err = drm_framebuffer_init(drm, fb, &tegra_fb_funcs);
 117        if (err < 0) {
 118                dev_err(drm->dev, "failed to initialize framebuffer: %d\n",
 119                        err);
 120                kfree(fb);
 121                return ERR_PTR(err);
 122        }
 123
 124        return fb;
 125}
 126
 127struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
 128                                        struct drm_file *file,
 129                                        const struct drm_mode_fb_cmd2 *cmd)
 130{
 131        const struct drm_format_info *info = drm_get_format_info(drm, cmd);
 132        struct tegra_bo *planes[4];
 133        struct drm_gem_object *gem;
 134        struct drm_framebuffer *fb;
 135        unsigned int i;
 136        int err;
 137
 138        for (i = 0; i < info->num_planes; i++) {
 139                unsigned int width = cmd->width / (i ? info->hsub : 1);
 140                unsigned int height = cmd->height / (i ? info->vsub : 1);
 141                unsigned int size, bpp;
 142
 143                gem = drm_gem_object_lookup(file, cmd->handles[i]);
 144                if (!gem) {
 145                        err = -ENXIO;
 146                        goto unreference;
 147                }
 148
 149                bpp = info->cpp[i];
 150
 151                size = (height - 1) * cmd->pitches[i] +
 152                       width * bpp + cmd->offsets[i];
 153
 154                if (gem->size < size) {
 155                        err = -EINVAL;
 156                        goto unreference;
 157                }
 158
 159                planes[i] = to_tegra_bo(gem);
 160        }
 161
 162        fb = tegra_fb_alloc(drm, cmd, planes, i);
 163        if (IS_ERR(fb)) {
 164                err = PTR_ERR(fb);
 165                goto unreference;
 166        }
 167
 168        return fb;
 169
 170unreference:
 171        while (i--)
 172                drm_gem_object_put_unlocked(&planes[i]->gem);
 173
 174        return ERR_PTR(err);
 175}
 176
 177#ifdef CONFIG_DRM_FBDEV_EMULATION
 178static int tegra_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 179{
 180        struct drm_fb_helper *helper = info->par;
 181        struct tegra_bo *bo;
 182        int err;
 183
 184        bo = tegra_fb_get_plane(helper->fb, 0);
 185
 186        err = drm_gem_mmap_obj(&bo->gem, bo->gem.size, vma);
 187        if (err < 0)
 188                return err;
 189
 190        return __tegra_gem_mmap(&bo->gem, vma);
 191}
 192
 193static struct fb_ops tegra_fb_ops = {
 194        .owner = THIS_MODULE,
 195        DRM_FB_HELPER_DEFAULT_OPS,
 196        .fb_fillrect = drm_fb_helper_sys_fillrect,
 197        .fb_copyarea = drm_fb_helper_sys_copyarea,
 198        .fb_imageblit = drm_fb_helper_sys_imageblit,
 199        .fb_mmap = tegra_fb_mmap,
 200};
 201
 202static int tegra_fbdev_probe(struct drm_fb_helper *helper,
 203                             struct drm_fb_helper_surface_size *sizes)
 204{
 205        struct tegra_fbdev *fbdev = to_tegra_fbdev(helper);
 206        struct tegra_drm *tegra = helper->dev->dev_private;
 207        struct drm_device *drm = helper->dev;
 208        struct drm_mode_fb_cmd2 cmd = { 0 };
 209        unsigned int bytes_per_pixel;
 210        struct drm_framebuffer *fb;
 211        unsigned long offset;
 212        struct fb_info *info;
 213        struct tegra_bo *bo;
 214        size_t size;
 215        int err;
 216
 217        bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
 218
 219        cmd.width = sizes->surface_width;
 220        cmd.height = sizes->surface_height;
 221        cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel,
 222                                  tegra->pitch_align);
 223
 224        cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
 225                                                     sizes->surface_depth);
 226
 227        size = cmd.pitches[0] * cmd.height;
 228
 229        bo = tegra_bo_create(drm, size, 0);
 230        if (IS_ERR(bo))
 231                return PTR_ERR(bo);
 232
 233        info = drm_fb_helper_alloc_fbi(helper);
 234        if (IS_ERR(info)) {
 235                dev_err(drm->dev, "failed to allocate framebuffer info\n");
 236                drm_gem_object_put_unlocked(&bo->gem);
 237                return PTR_ERR(info);
 238        }
 239
 240        fbdev->fb = tegra_fb_alloc(drm, &cmd, &bo, 1);
 241        if (IS_ERR(fbdev->fb)) {
 242                err = PTR_ERR(fbdev->fb);
 243                dev_err(drm->dev, "failed to allocate DRM framebuffer: %d\n",
 244                        err);
 245                drm_gem_object_put_unlocked(&bo->gem);
 246                return PTR_ERR(fbdev->fb);
 247        }
 248
 249        fb = fbdev->fb;
 250        helper->fb = fb;
 251        helper->fbdev = info;
 252
 253        info->fbops = &tegra_fb_ops;
 254
 255        drm_fb_helper_fill_info(info, helper, sizes);
 256
 257        offset = info->var.xoffset * bytes_per_pixel +
 258                 info->var.yoffset * fb->pitches[0];
 259
 260        if (bo->pages) {
 261                bo->vaddr = vmap(bo->pages, bo->num_pages, VM_MAP,
 262                                 pgprot_writecombine(PAGE_KERNEL));
 263                if (!bo->vaddr) {
 264                        dev_err(drm->dev, "failed to vmap() framebuffer\n");
 265                        err = -ENOMEM;
 266                        goto destroy;
 267                }
 268        }
 269
 270        drm->mode_config.fb_base = (resource_size_t)bo->paddr;
 271        info->screen_base = (void __iomem *)bo->vaddr + offset;
 272        info->screen_size = size;
 273        info->fix.smem_start = (unsigned long)(bo->paddr + offset);
 274        info->fix.smem_len = size;
 275
 276        return 0;
 277
 278destroy:
 279        drm_framebuffer_remove(fb);
 280        return err;
 281}
 282
 283static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
 284        .fb_probe = tegra_fbdev_probe,
 285};
 286
 287static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm)
 288{
 289        struct tegra_fbdev *fbdev;
 290
 291        fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
 292        if (!fbdev) {
 293                dev_err(drm->dev, "failed to allocate DRM fbdev\n");
 294                return ERR_PTR(-ENOMEM);
 295        }
 296
 297        drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs);
 298
 299        return fbdev;
 300}
 301
 302static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
 303{
 304        kfree(fbdev);
 305}
 306
 307static int tegra_fbdev_init(struct tegra_fbdev *fbdev,
 308                            unsigned int preferred_bpp,
 309                            unsigned int num_crtc,
 310                            unsigned int max_connectors)
 311{
 312        struct drm_device *drm = fbdev->base.dev;
 313        int err;
 314
 315        err = drm_fb_helper_init(drm, &fbdev->base, max_connectors);
 316        if (err < 0) {
 317                dev_err(drm->dev, "failed to initialize DRM FB helper: %d\n",
 318                        err);
 319                return err;
 320        }
 321
 322        err = drm_fb_helper_single_add_all_connectors(&fbdev->base);
 323        if (err < 0) {
 324                dev_err(drm->dev, "failed to add connectors: %d\n", err);
 325                goto fini;
 326        }
 327
 328        err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp);
 329        if (err < 0) {
 330                dev_err(drm->dev, "failed to set initial configuration: %d\n",
 331                        err);
 332                goto fini;
 333        }
 334
 335        return 0;
 336
 337fini:
 338        drm_fb_helper_fini(&fbdev->base);
 339        return err;
 340}
 341
 342static void tegra_fbdev_exit(struct tegra_fbdev *fbdev)
 343{
 344        drm_fb_helper_unregister_fbi(&fbdev->base);
 345
 346        if (fbdev->fb) {
 347                struct tegra_bo *bo = tegra_fb_get_plane(fbdev->fb, 0);
 348
 349                /* Undo the special mapping we made in fbdev probe. */
 350                if (bo && bo->pages) {
 351                        vunmap(bo->vaddr);
 352                        bo->vaddr = NULL;
 353                }
 354
 355                drm_framebuffer_remove(fbdev->fb);
 356        }
 357
 358        drm_fb_helper_fini(&fbdev->base);
 359        tegra_fbdev_free(fbdev);
 360}
 361#endif
 362
 363int tegra_drm_fb_prepare(struct drm_device *drm)
 364{
 365#ifdef CONFIG_DRM_FBDEV_EMULATION
 366        struct tegra_drm *tegra = drm->dev_private;
 367
 368        tegra->fbdev = tegra_fbdev_create(drm);
 369        if (IS_ERR(tegra->fbdev))
 370                return PTR_ERR(tegra->fbdev);
 371#endif
 372
 373        return 0;
 374}
 375
 376void tegra_drm_fb_free(struct drm_device *drm)
 377{
 378#ifdef CONFIG_DRM_FBDEV_EMULATION
 379        struct tegra_drm *tegra = drm->dev_private;
 380
 381        tegra_fbdev_free(tegra->fbdev);
 382#endif
 383}
 384
 385int tegra_drm_fb_init(struct drm_device *drm)
 386{
 387#ifdef CONFIG_DRM_FBDEV_EMULATION
 388        struct tegra_drm *tegra = drm->dev_private;
 389        int err;
 390
 391        err = tegra_fbdev_init(tegra->fbdev, 32, drm->mode_config.num_crtc,
 392                               drm->mode_config.num_connector);
 393        if (err < 0)
 394                return err;
 395#endif
 396
 397        return 0;
 398}
 399
 400void tegra_drm_fb_exit(struct drm_device *drm)
 401{
 402#ifdef CONFIG_DRM_FBDEV_EMULATION
 403        struct tegra_drm *tegra = drm->dev_private;
 404
 405        tegra_fbdev_exit(tegra->fbdev);
 406#endif
 407}
 408