linux/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
<<
>>
Prefs
   1/* exynos_drm_fbdev.c
   2 *
   3 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
   4 * Authors:
   5 *      Inki Dae <inki.dae@samsung.com>
   6 *      Joonyoung Shim <jy0922.shim@samsung.com>
   7 *      Seung-Woo Kim <sw0312.kim@samsung.com>
   8 *
   9 * This program is free software; you can redistribute  it and/or modify it
  10 * under  the terms of  the GNU General  Public License as published by the
  11 * Free Software Foundation;  either version 2 of the  License, or (at your
  12 * option) any later version.
  13 */
  14
  15#include <drm/drmP.h>
  16#include <drm/drm_crtc.h>
  17#include <drm/drm_fb_helper.h>
  18#include <drm/drm_crtc_helper.h>
  19#include <drm/exynos_drm.h>
  20
  21#include <linux/console.h>
  22
  23#include "exynos_drm_drv.h"
  24#include "exynos_drm_fb.h"
  25#include "exynos_drm_fbdev.h"
  26#include "exynos_drm_iommu.h"
  27
  28#define MAX_CONNECTOR           4
  29#define PREFERRED_BPP           32
  30
  31#define to_exynos_fbdev(x)      container_of(x, struct exynos_drm_fbdev,\
  32                                drm_fb_helper)
  33
  34struct exynos_drm_fbdev {
  35        struct drm_fb_helper    drm_fb_helper;
  36        struct exynos_drm_gem   *exynos_gem;
  37};
  38
  39static int exynos_drm_fb_mmap(struct fb_info *info,
  40                        struct vm_area_struct *vma)
  41{
  42        struct drm_fb_helper *helper = info->par;
  43        struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(helper);
  44        struct exynos_drm_gem *exynos_gem = exynos_fbd->exynos_gem;
  45        unsigned long vm_size;
  46        int ret;
  47
  48        vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
  49
  50        vm_size = vma->vm_end - vma->vm_start;
  51
  52        if (vm_size > exynos_gem->size)
  53                return -EINVAL;
  54
  55        ret = dma_mmap_attrs(to_dma_dev(helper->dev), vma, exynos_gem->cookie,
  56                             exynos_gem->dma_addr, exynos_gem->size,
  57                             exynos_gem->dma_attrs);
  58        if (ret < 0) {
  59                DRM_ERROR("failed to mmap.\n");
  60                return ret;
  61        }
  62
  63        return 0;
  64}
  65
  66static struct fb_ops exynos_drm_fb_ops = {
  67        .owner          = THIS_MODULE,
  68        DRM_FB_HELPER_DEFAULT_OPS,
  69        .fb_mmap        = exynos_drm_fb_mmap,
  70        .fb_fillrect    = drm_fb_helper_cfb_fillrect,
  71        .fb_copyarea    = drm_fb_helper_cfb_copyarea,
  72        .fb_imageblit   = drm_fb_helper_cfb_imageblit,
  73};
  74
  75static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
  76                                   struct drm_fb_helper_surface_size *sizes,
  77                                   struct exynos_drm_gem *exynos_gem)
  78{
  79        struct fb_info *fbi;
  80        struct drm_framebuffer *fb = helper->fb;
  81        unsigned int size = fb->width * fb->height * fb->format->cpp[0];
  82        unsigned int nr_pages;
  83        unsigned long offset;
  84
  85        fbi = drm_fb_helper_alloc_fbi(helper);
  86        if (IS_ERR(fbi)) {
  87                DRM_ERROR("failed to allocate fb info.\n");
  88                return PTR_ERR(fbi);
  89        }
  90
  91        fbi->par = helper;
  92        fbi->flags = FBINFO_FLAG_DEFAULT;
  93        fbi->fbops = &exynos_drm_fb_ops;
  94
  95        drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth);
  96        drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height);
  97
  98        nr_pages = exynos_gem->size >> PAGE_SHIFT;
  99
 100        exynos_gem->kvaddr = (void __iomem *) vmap(exynos_gem->pages, nr_pages,
 101                                VM_MAP, pgprot_writecombine(PAGE_KERNEL));
 102        if (!exynos_gem->kvaddr) {
 103                DRM_ERROR("failed to map pages to kernel space.\n");
 104                return -EIO;
 105        }
 106
 107        offset = fbi->var.xoffset * fb->format->cpp[0];
 108        offset += fbi->var.yoffset * fb->pitches[0];
 109
 110        fbi->screen_base = exynos_gem->kvaddr + offset;
 111        fbi->screen_size = size;
 112        fbi->fix.smem_len = size;
 113
 114        return 0;
 115}
 116
 117static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
 118                                    struct drm_fb_helper_surface_size *sizes)
 119{
 120        struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
 121        struct exynos_drm_gem *exynos_gem;
 122        struct drm_device *dev = helper->dev;
 123        struct drm_mode_fb_cmd2 mode_cmd = { 0 };
 124        unsigned long size;
 125        int ret;
 126
 127        DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
 128                        sizes->surface_width, sizes->surface_height,
 129                        sizes->surface_bpp);
 130
 131        mode_cmd.width = sizes->surface_width;
 132        mode_cmd.height = sizes->surface_height;
 133        mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
 134        mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
 135                                                          sizes->surface_depth);
 136
 137        size = mode_cmd.pitches[0] * mode_cmd.height;
 138
 139        exynos_gem = exynos_drm_gem_create(dev, EXYNOS_BO_CONTIG, size);
 140        /*
 141         * If physically contiguous memory allocation fails and if IOMMU is
 142         * supported then try to get buffer from non physically contiguous
 143         * memory area.
 144         */
 145        if (IS_ERR(exynos_gem) && is_drm_iommu_supported(dev)) {
 146                dev_warn(dev->dev, "contiguous FB allocation failed, falling back to non-contiguous\n");
 147                exynos_gem = exynos_drm_gem_create(dev, EXYNOS_BO_NONCONTIG,
 148                                                   size);
 149        }
 150
 151        if (IS_ERR(exynos_gem))
 152                return PTR_ERR(exynos_gem);
 153
 154        exynos_fbdev->exynos_gem = exynos_gem;
 155
 156        helper->fb =
 157                exynos_drm_framebuffer_init(dev, &mode_cmd, &exynos_gem, 1);
 158        if (IS_ERR(helper->fb)) {
 159                DRM_ERROR("failed to create drm framebuffer.\n");
 160                ret = PTR_ERR(helper->fb);
 161                goto err_destroy_gem;
 162        }
 163
 164        ret = exynos_drm_fbdev_update(helper, sizes, exynos_gem);
 165        if (ret < 0)
 166                goto err_destroy_framebuffer;
 167
 168        return ret;
 169
 170err_destroy_framebuffer:
 171        drm_framebuffer_cleanup(helper->fb);
 172err_destroy_gem:
 173        exynos_drm_gem_destroy(exynos_gem);
 174
 175        /*
 176         * if failed, all resources allocated above would be released by
 177         * drm_mode_config_cleanup() when drm_load() had been called prior
 178         * to any specific driver such as fimd or hdmi driver.
 179         */
 180
 181        return ret;
 182}
 183
 184static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
 185        .fb_probe =     exynos_drm_fbdev_create,
 186};
 187
 188int exynos_drm_fbdev_init(struct drm_device *dev)
 189{
 190        struct exynos_drm_fbdev *fbdev;
 191        struct exynos_drm_private *private = dev->dev_private;
 192        struct drm_fb_helper *helper;
 193        int ret;
 194
 195        if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
 196                return 0;
 197
 198        fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
 199        if (!fbdev)
 200                return -ENOMEM;
 201
 202        private->fb_helper = helper = &fbdev->drm_fb_helper;
 203
 204        drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
 205
 206        ret = drm_fb_helper_init(dev, helper, MAX_CONNECTOR);
 207        if (ret < 0) {
 208                DRM_ERROR("failed to initialize drm fb helper.\n");
 209                goto err_init;
 210        }
 211
 212        ret = drm_fb_helper_single_add_all_connectors(helper);
 213        if (ret < 0) {
 214                DRM_ERROR("failed to register drm_fb_helper_connector.\n");
 215                goto err_setup;
 216
 217        }
 218
 219        ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
 220        if (ret < 0) {
 221                DRM_ERROR("failed to set up hw configuration.\n");
 222                goto err_setup;
 223        }
 224
 225        return 0;
 226
 227err_setup:
 228        drm_fb_helper_fini(helper);
 229
 230err_init:
 231        private->fb_helper = NULL;
 232        kfree(fbdev);
 233
 234        return ret;
 235}
 236
 237static void exynos_drm_fbdev_destroy(struct drm_device *dev,
 238                                      struct drm_fb_helper *fb_helper)
 239{
 240        struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(fb_helper);
 241        struct exynos_drm_gem *exynos_gem = exynos_fbd->exynos_gem;
 242        struct drm_framebuffer *fb;
 243
 244        vunmap(exynos_gem->kvaddr);
 245
 246        /* release drm framebuffer and real buffer */
 247        if (fb_helper->fb && fb_helper->fb->funcs) {
 248                fb = fb_helper->fb;
 249                if (fb)
 250                        drm_framebuffer_remove(fb);
 251        }
 252
 253        drm_fb_helper_unregister_fbi(fb_helper);
 254
 255        drm_fb_helper_fini(fb_helper);
 256}
 257
 258void exynos_drm_fbdev_fini(struct drm_device *dev)
 259{
 260        struct exynos_drm_private *private = dev->dev_private;
 261        struct exynos_drm_fbdev *fbdev;
 262
 263        if (!private || !private->fb_helper)
 264                return;
 265
 266        fbdev = to_exynos_fbdev(private->fb_helper);
 267
 268        exynos_drm_fbdev_destroy(dev, private->fb_helper);
 269        kfree(fbdev);
 270        private->fb_helper = NULL;
 271}
 272
 273void exynos_drm_fbdev_suspend(struct drm_device *dev)
 274{
 275        struct exynos_drm_private *private = dev->dev_private;
 276
 277        console_lock();
 278        drm_fb_helper_set_suspend(private->fb_helper, 1);
 279        console_unlock();
 280}
 281
 282void exynos_drm_fbdev_resume(struct drm_device *dev)
 283{
 284        struct exynos_drm_private *private = dev->dev_private;
 285
 286        console_lock();
 287        drm_fb_helper_set_suspend(private->fb_helper, 0);
 288        console_unlock();
 289}
 290