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