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 "exynos_drm_drv.h"
  22#include "exynos_drm_fb.h"
  23#include "exynos_drm_fbdev.h"
  24#include "exynos_drm_gem.h"
  25#include "exynos_drm_iommu.h"
  26
  27#define MAX_CONNECTOR           4
  28#define PREFERRED_BPP           32
  29
  30#define to_exynos_fbdev(x)      container_of(x, struct exynos_drm_fbdev,\
  31                                drm_fb_helper)
  32
  33struct exynos_drm_fbdev {
  34        struct drm_fb_helper            drm_fb_helper;
  35        struct exynos_drm_gem_obj       *exynos_gem_obj;
  36};
  37
  38static int exynos_drm_fb_mmap(struct fb_info *info,
  39                        struct vm_area_struct *vma)
  40{
  41        struct drm_fb_helper *helper = info->par;
  42        struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(helper);
  43        struct exynos_drm_gem_obj *exynos_gem_obj = exynos_fbd->exynos_gem_obj;
  44        struct exynos_drm_gem_buf *buffer = exynos_gem_obj->buffer;
  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 > buffer->size)
  53                return -EINVAL;
  54
  55        ret = dma_mmap_attrs(helper->dev->dev, vma, buffer->pages,
  56                buffer->dma_addr, buffer->size, &buffer->dma_attrs);
  57        if (ret < 0) {
  58                DRM_ERROR("failed to mmap.\n");
  59                return ret;
  60        }
  61
  62        return 0;
  63}
  64
  65static struct fb_ops exynos_drm_fb_ops = {
  66        .owner          = THIS_MODULE,
  67        .fb_mmap        = exynos_drm_fb_mmap,
  68        .fb_fillrect    = cfb_fillrect,
  69        .fb_copyarea    = cfb_copyarea,
  70        .fb_imageblit   = cfb_imageblit,
  71        .fb_check_var   = drm_fb_helper_check_var,
  72        .fb_set_par     = drm_fb_helper_set_par,
  73        .fb_blank       = drm_fb_helper_blank,
  74        .fb_pan_display = drm_fb_helper_pan_display,
  75        .fb_setcmap     = drm_fb_helper_setcmap,
  76};
  77
  78static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
  79                                     struct drm_fb_helper_surface_size *sizes,
  80                                     struct drm_framebuffer *fb)
  81{
  82        struct fb_info *fbi = helper->fbdev;
  83        struct exynos_drm_gem_buf *buffer;
  84        unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);
  85        unsigned int nr_pages;
  86        unsigned long offset;
  87
  88        drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
  89        drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height);
  90
  91        /* RGB formats use only one buffer */
  92        buffer = exynos_drm_fb_buffer(fb, 0);
  93        if (!buffer) {
  94                DRM_DEBUG_KMS("buffer is null.\n");
  95                return -EFAULT;
  96        }
  97
  98        nr_pages = buffer->size >> PAGE_SHIFT;
  99
 100        buffer->kvaddr = (void __iomem *) vmap(buffer->pages,
 101                        nr_pages, VM_MAP,
 102                        pgprot_writecombine(PAGE_KERNEL));
 103        if (!buffer->kvaddr) {
 104                DRM_ERROR("failed to map pages to kernel space.\n");
 105                return -EIO;
 106        }
 107
 108        /* buffer count to framebuffer always is 1 at booting time. */
 109        exynos_drm_fb_set_buf_cnt(fb, 1);
 110
 111        offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
 112        offset += fbi->var.yoffset * fb->pitches[0];
 113
 114        fbi->screen_base = buffer->kvaddr + offset;
 115        fbi->screen_size = size;
 116        fbi->fix.smem_len = size;
 117
 118        return 0;
 119}
 120
 121static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
 122                                    struct drm_fb_helper_surface_size *sizes)
 123{
 124        struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
 125        struct exynos_drm_gem_obj *exynos_gem_obj;
 126        struct drm_device *dev = helper->dev;
 127        struct fb_info *fbi;
 128        struct drm_mode_fb_cmd2 mode_cmd = { 0 };
 129        struct platform_device *pdev = dev->platformdev;
 130        unsigned long size;
 131        int ret;
 132
 133        DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
 134                        sizes->surface_width, sizes->surface_height,
 135                        sizes->surface_bpp);
 136
 137        mode_cmd.width = sizes->surface_width;
 138        mode_cmd.height = sizes->surface_height;
 139        mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
 140        mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
 141                                                          sizes->surface_depth);
 142
 143        mutex_lock(&dev->struct_mutex);
 144
 145        fbi = framebuffer_alloc(0, &pdev->dev);
 146        if (!fbi) {
 147                DRM_ERROR("failed to allocate fb info.\n");
 148                ret = -ENOMEM;
 149                goto out;
 150        }
 151
 152        size = mode_cmd.pitches[0] * mode_cmd.height;
 153
 154        exynos_gem_obj = exynos_drm_gem_create(dev, EXYNOS_BO_CONTIG, size);
 155        /*
 156         * If physically contiguous memory allocation fails and if IOMMU is
 157         * supported then try to get buffer from non physically contiguous
 158         * memory area.
 159         */
 160        if (IS_ERR(exynos_gem_obj) && is_drm_iommu_supported(dev)) {
 161                dev_warn(&pdev->dev, "contiguous FB allocation failed, falling back to non-contiguous\n");
 162                exynos_gem_obj = exynos_drm_gem_create(dev, EXYNOS_BO_NONCONTIG,
 163                                                        size);
 164        }
 165
 166        if (IS_ERR(exynos_gem_obj)) {
 167                ret = PTR_ERR(exynos_gem_obj);
 168                goto err_release_framebuffer;
 169        }
 170
 171        exynos_fbdev->exynos_gem_obj = exynos_gem_obj;
 172
 173        helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd,
 174                        &exynos_gem_obj->base);
 175        if (IS_ERR(helper->fb)) {
 176                DRM_ERROR("failed to create drm framebuffer.\n");
 177                ret = PTR_ERR(helper->fb);
 178                goto err_destroy_gem;
 179        }
 180
 181        helper->fbdev = fbi;
 182
 183        fbi->par = helper;
 184        fbi->flags = FBINFO_FLAG_DEFAULT;
 185        fbi->fbops = &exynos_drm_fb_ops;
 186
 187        ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
 188        if (ret) {
 189                DRM_ERROR("failed to allocate cmap.\n");
 190                goto err_destroy_framebuffer;
 191        }
 192
 193        ret = exynos_drm_fbdev_update(helper, sizes, helper->fb);
 194        if (ret < 0)
 195                goto err_dealloc_cmap;
 196
 197        mutex_unlock(&dev->struct_mutex);
 198        return ret;
 199
 200err_dealloc_cmap:
 201        fb_dealloc_cmap(&fbi->cmap);
 202err_destroy_framebuffer:
 203        drm_framebuffer_cleanup(helper->fb);
 204err_destroy_gem:
 205        exynos_drm_gem_destroy(exynos_gem_obj);
 206err_release_framebuffer:
 207        framebuffer_release(fbi);
 208
 209/*
 210 * if failed, all resources allocated above would be released by
 211 * drm_mode_config_cleanup() when drm_load() had been called prior
 212 * to any specific driver such as fimd or hdmi driver.
 213 */
 214out:
 215        mutex_unlock(&dev->struct_mutex);
 216        return ret;
 217}
 218
 219static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
 220        .fb_probe =     exynos_drm_fbdev_create,
 221};
 222
 223static bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev)
 224{
 225        struct drm_connector *connector;
 226        bool ret = false;
 227
 228        mutex_lock(&dev->mode_config.mutex);
 229        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 230                if (connector->status != connector_status_connected)
 231                        continue;
 232
 233                ret = true;
 234                break;
 235        }
 236        mutex_unlock(&dev->mode_config.mutex);
 237
 238        return ret;
 239}
 240
 241int exynos_drm_fbdev_init(struct drm_device *dev)
 242{
 243        struct exynos_drm_fbdev *fbdev;
 244        struct exynos_drm_private *private = dev->dev_private;
 245        struct drm_fb_helper *helper;
 246        unsigned int num_crtc;
 247        int ret;
 248
 249        if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
 250                return 0;
 251
 252        if (!exynos_drm_fbdev_is_anything_connected(dev))
 253                return 0;
 254
 255        fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
 256        if (!fbdev)
 257                return -ENOMEM;
 258
 259        private->fb_helper = helper = &fbdev->drm_fb_helper;
 260
 261        drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
 262
 263        num_crtc = dev->mode_config.num_crtc;
 264
 265        ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR);
 266        if (ret < 0) {
 267                DRM_ERROR("failed to initialize drm fb helper.\n");
 268                goto err_init;
 269        }
 270
 271        ret = drm_fb_helper_single_add_all_connectors(helper);
 272        if (ret < 0) {
 273                DRM_ERROR("failed to register drm_fb_helper_connector.\n");
 274                goto err_setup;
 275
 276        }
 277
 278        ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
 279        if (ret < 0) {
 280                DRM_ERROR("failed to set up hw configuration.\n");
 281                goto err_setup;
 282        }
 283
 284        return 0;
 285
 286err_setup:
 287        drm_fb_helper_fini(helper);
 288
 289err_init:
 290        private->fb_helper = NULL;
 291        kfree(fbdev);
 292
 293        return ret;
 294}
 295
 296static void exynos_drm_fbdev_destroy(struct drm_device *dev,
 297                                      struct drm_fb_helper *fb_helper)
 298{
 299        struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(fb_helper);
 300        struct exynos_drm_gem_obj *exynos_gem_obj = exynos_fbd->exynos_gem_obj;
 301        struct drm_framebuffer *fb;
 302
 303        if (exynos_gem_obj->buffer->kvaddr)
 304                vunmap(exynos_gem_obj->buffer->kvaddr);
 305
 306        /* release drm framebuffer and real buffer */
 307        if (fb_helper->fb && fb_helper->fb->funcs) {
 308                fb = fb_helper->fb;
 309                if (fb) {
 310                        drm_framebuffer_unregister_private(fb);
 311                        drm_framebuffer_remove(fb);
 312                }
 313        }
 314
 315        /* release linux framebuffer */
 316        if (fb_helper->fbdev) {
 317                struct fb_info *info;
 318                int ret;
 319
 320                info = fb_helper->fbdev;
 321                ret = unregister_framebuffer(info);
 322                if (ret < 0)
 323                        DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
 324
 325                if (info->cmap.len)
 326                        fb_dealloc_cmap(&info->cmap);
 327
 328                framebuffer_release(info);
 329        }
 330
 331        drm_fb_helper_fini(fb_helper);
 332}
 333
 334void exynos_drm_fbdev_fini(struct drm_device *dev)
 335{
 336        struct exynos_drm_private *private = dev->dev_private;
 337        struct exynos_drm_fbdev *fbdev;
 338
 339        if (!private || !private->fb_helper)
 340                return;
 341
 342        fbdev = to_exynos_fbdev(private->fb_helper);
 343
 344        exynos_drm_fbdev_destroy(dev, private->fb_helper);
 345        kfree(fbdev);
 346        private->fb_helper = NULL;
 347}
 348
 349void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
 350{
 351        struct exynos_drm_private *private = dev->dev_private;
 352
 353        if (!private || !private->fb_helper)
 354                return;
 355
 356        drm_fb_helper_restore_fbdev_mode_unlocked(private->fb_helper);
 357}
 358