linux/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/* exynos_drm_fbdev.c
   3 *
   4 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
   5 * Authors:
   6 *      Inki Dae <inki.dae@samsung.com>
   7 *      Joonyoung Shim <jy0922.shim@samsung.com>
   8 *      Seung-Woo Kim <sw0312.kim@samsung.com>
   9 */
  10
  11#include <linux/console.h>
  12#include <linux/dma-mapping.h>
  13#include <linux/vmalloc.h>
  14
  15#include <drm/drm_crtc.h>
  16#include <drm/drm_fb_helper.h>
  17#include <drm/drm_fourcc.h>
  18#include <drm/drm_probe_helper.h>
  19#include <drm/exynos_drm.h>
  20
  21#include "exynos_drm_drv.h"
  22#include "exynos_drm_fb.h"
  23#include "exynos_drm_fbdev.h"
  24
  25#define MAX_CONNECTOR           4
  26#define PREFERRED_BPP           32
  27
  28#define to_exynos_fbdev(x)      container_of(x, struct exynos_drm_fbdev,\
  29                                drm_fb_helper)
  30
  31struct exynos_drm_fbdev {
  32        struct drm_fb_helper    drm_fb_helper;
  33        struct exynos_drm_gem   *exynos_gem;
  34};
  35
  36static int exynos_drm_fb_mmap(struct fb_info *info,
  37                        struct vm_area_struct *vma)
  38{
  39        struct drm_fb_helper *helper = info->par;
  40        struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(helper);
  41        struct exynos_drm_gem *exynos_gem = exynos_fbd->exynos_gem;
  42        unsigned long vm_size;
  43        int ret;
  44
  45        vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
  46
  47        vm_size = vma->vm_end - vma->vm_start;
  48
  49        if (vm_size > exynos_gem->size)
  50                return -EINVAL;
  51
  52        ret = dma_mmap_attrs(to_dma_dev(helper->dev), vma, exynos_gem->cookie,
  53                             exynos_gem->dma_addr, exynos_gem->size,
  54                             exynos_gem->dma_attrs);
  55        if (ret < 0) {
  56                DRM_DEV_ERROR(to_dma_dev(helper->dev), "failed to mmap.\n");
  57                return ret;
  58        }
  59
  60        return 0;
  61}
  62
  63static const struct fb_ops exynos_drm_fb_ops = {
  64        .owner          = THIS_MODULE,
  65        DRM_FB_HELPER_DEFAULT_OPS,
  66        .fb_mmap        = exynos_drm_fb_mmap,
  67        .fb_fillrect    = drm_fb_helper_cfb_fillrect,
  68        .fb_copyarea    = drm_fb_helper_cfb_copyarea,
  69        .fb_imageblit   = drm_fb_helper_cfb_imageblit,
  70};
  71
  72static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
  73                                   struct drm_fb_helper_surface_size *sizes,
  74                                   struct exynos_drm_gem *exynos_gem)
  75{
  76        struct fb_info *fbi;
  77        struct drm_framebuffer *fb = helper->fb;
  78        unsigned int size = fb->width * fb->height * fb->format->cpp[0];
  79        unsigned long offset;
  80
  81        fbi = drm_fb_helper_alloc_fbi(helper);
  82        if (IS_ERR(fbi)) {
  83                DRM_DEV_ERROR(to_dma_dev(helper->dev),
  84                              "failed to allocate fb info.\n");
  85                return PTR_ERR(fbi);
  86        }
  87
  88        fbi->fbops = &exynos_drm_fb_ops;
  89
  90        drm_fb_helper_fill_info(fbi, helper, sizes);
  91
  92        offset = fbi->var.xoffset * fb->format->cpp[0];
  93        offset += fbi->var.yoffset * fb->pitches[0];
  94
  95        fbi->screen_buffer = exynos_gem->kvaddr + offset;
  96        fbi->screen_size = size;
  97        fbi->fix.smem_len = size;
  98
  99        return 0;
 100}
 101
 102static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
 103                                    struct drm_fb_helper_surface_size *sizes)
 104{
 105        struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
 106        struct exynos_drm_gem *exynos_gem;
 107        struct drm_device *dev = helper->dev;
 108        struct drm_mode_fb_cmd2 mode_cmd = { 0 };
 109        unsigned long size;
 110        int ret;
 111
 112        DRM_DEV_DEBUG_KMS(dev->dev,
 113                          "surface width(%d), height(%d) and bpp(%d\n",
 114                          sizes->surface_width, sizes->surface_height,
 115                          sizes->surface_bpp);
 116
 117        mode_cmd.width = sizes->surface_width;
 118        mode_cmd.height = sizes->surface_height;
 119        mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
 120        mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
 121                                                          sizes->surface_depth);
 122
 123        size = mode_cmd.pitches[0] * mode_cmd.height;
 124
 125        exynos_gem = exynos_drm_gem_create(dev, EXYNOS_BO_WC, size, true);
 126        if (IS_ERR(exynos_gem))
 127                return PTR_ERR(exynos_gem);
 128
 129        exynos_fbdev->exynos_gem = exynos_gem;
 130
 131        helper->fb =
 132                exynos_drm_framebuffer_init(dev, &mode_cmd, &exynos_gem, 1);
 133        if (IS_ERR(helper->fb)) {
 134                DRM_DEV_ERROR(dev->dev, "failed to create drm framebuffer.\n");
 135                ret = PTR_ERR(helper->fb);
 136                goto err_destroy_gem;
 137        }
 138
 139        ret = exynos_drm_fbdev_update(helper, sizes, exynos_gem);
 140        if (ret < 0)
 141                goto err_destroy_framebuffer;
 142
 143        return ret;
 144
 145err_destroy_framebuffer:
 146        drm_framebuffer_cleanup(helper->fb);
 147err_destroy_gem:
 148        exynos_drm_gem_destroy(exynos_gem);
 149
 150        /*
 151         * if failed, all resources allocated above would be released by
 152         * drm_mode_config_cleanup() when drm_load() had been called prior
 153         * to any specific driver such as fimd or hdmi driver.
 154         */
 155
 156        return ret;
 157}
 158
 159static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
 160        .fb_probe =     exynos_drm_fbdev_create,
 161};
 162
 163int exynos_drm_fbdev_init(struct drm_device *dev)
 164{
 165        struct exynos_drm_fbdev *fbdev;
 166        struct exynos_drm_private *private = dev->dev_private;
 167        struct drm_fb_helper *helper;
 168        int ret;
 169
 170        if (!dev->mode_config.num_crtc)
 171                return 0;
 172
 173        fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
 174        if (!fbdev)
 175                return -ENOMEM;
 176
 177        private->fb_helper = helper = &fbdev->drm_fb_helper;
 178
 179        drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
 180
 181        ret = drm_fb_helper_init(dev, helper);
 182        if (ret < 0) {
 183                DRM_DEV_ERROR(dev->dev,
 184                              "failed to initialize drm fb helper.\n");
 185                goto err_init;
 186        }
 187
 188        ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
 189        if (ret < 0) {
 190                DRM_DEV_ERROR(dev->dev,
 191                              "failed to set up hw configuration.\n");
 192                goto err_setup;
 193        }
 194
 195        return 0;
 196
 197err_setup:
 198        drm_fb_helper_fini(helper);
 199
 200err_init:
 201        private->fb_helper = NULL;
 202        kfree(fbdev);
 203
 204        return ret;
 205}
 206
 207static void exynos_drm_fbdev_destroy(struct drm_device *dev,
 208                                      struct drm_fb_helper *fb_helper)
 209{
 210        struct drm_framebuffer *fb;
 211
 212        /* release drm framebuffer and real buffer */
 213        if (fb_helper->fb && fb_helper->fb->funcs) {
 214                fb = fb_helper->fb;
 215                if (fb)
 216                        drm_framebuffer_remove(fb);
 217        }
 218
 219        drm_fb_helper_unregister_fbi(fb_helper);
 220
 221        drm_fb_helper_fini(fb_helper);
 222}
 223
 224void exynos_drm_fbdev_fini(struct drm_device *dev)
 225{
 226        struct exynos_drm_private *private = dev->dev_private;
 227        struct exynos_drm_fbdev *fbdev;
 228
 229        if (!private || !private->fb_helper)
 230                return;
 231
 232        fbdev = to_exynos_fbdev(private->fb_helper);
 233
 234        exynos_drm_fbdev_destroy(dev, private->fb_helper);
 235        kfree(fbdev);
 236        private->fb_helper = NULL;
 237}
 238
 239