FROMLIST: drm/rockchip: fix fbdev crash when not use DRM_FBDEV_EMULATION
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / rockchip / rockchip_drm_fbdev.c
index 43c1b0469e8062c52875d48addc23a7ce2719527..6b50dfae4ad4ee14fabf16ee23b859e00ea3ba88 100644 (file)
@@ -1,8 +1,6 @@
 /*
- * Copyright (C) ROCKCHIP, Inc.
- * Author:yzq<yzq@rock-chips.com>
- *
- * based on exynos_drm_fbdev.c
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
+
+#include <drm/drm.h>
 #include <drm/drmP.h>
-#include <drm/drm_crtc.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_crtc_helper.h>
 
 #include "rockchip_drm_drv.h"
-#include "rockchip_drm_fb.h"
 #include "rockchip_drm_gem.h"
-#include "rockchip_drm_iommu.h"
+#include "rockchip_drm_fb.h"
 
-#define MAX_CONNECTOR          4
 #define PREFERRED_BPP          32
 
-#define to_rockchip_fbdev(x)   container_of(x, struct rockchip_drm_fbdev,\
-                               drm_fb_helper)
-
-struct rockchip_drm_fbdev {
-       struct drm_fb_helper            drm_fb_helper;
-       struct rockchip_drm_gem_obj     *rockchip_gem_obj;
-};
-
-static int rockchip_drm_fb_mmap(struct fb_info *info,
-                       struct vm_area_struct *vma)
+static int rockchip_fbdev_mmap(struct fb_info *info,
+                              struct vm_area_struct *vma)
 {
        struct drm_fb_helper *helper = info->par;
-       struct rockchip_drm_fbdev *rockchip_fbd = to_rockchip_fbdev(helper);
-       struct rockchip_drm_gem_obj *rockchip_gem_obj = rockchip_fbd->rockchip_gem_obj;
-       struct rockchip_drm_gem_buf *buffer = rockchip_gem_obj->buffer;
-       unsigned long vm_size;
-       int ret;
-
-       DRM_DEBUG_KMS("%s\n", __func__);
+       struct rockchip_drm_private *private = helper->dev->dev_private;
 
-       vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
-
-       vm_size = vma->vm_end - vma->vm_start;
-
-       if (vm_size > buffer->size)
-               return -EINVAL;
-
-       ret = dma_mmap_attrs(helper->dev->dev, vma, buffer->pages,
-               buffer->dma_addr, buffer->size, &buffer->dma_attrs);
-       if (ret < 0) {
-               DRM_ERROR("failed to mmap.\n");
-               return ret;
-       }
-
-       return 0;
+       return rockchip_gem_mmap_buf(private->fbdev_bo, vma);
 }
 
-static struct fb_ops rockchip_drm_fb_ops = {
+static struct fb_ops rockchip_drm_fbdev_ops = {
        .owner          = THIS_MODULE,
-       .fb_mmap        = rockchip_drm_fb_mmap,
-       .fb_fillrect    = cfb_fillrect,
-       .fb_copyarea    = cfb_copyarea,
-       .fb_imageblit   = cfb_imageblit,
+       .fb_mmap        = rockchip_fbdev_mmap,
+       .fb_fillrect    = drm_fb_helper_cfb_fillrect,
+       .fb_copyarea    = drm_fb_helper_cfb_copyarea,
+       .fb_imageblit   = drm_fb_helper_cfb_imageblit,
        .fb_check_var   = drm_fb_helper_check_var,
        .fb_set_par     = drm_fb_helper_set_par,
        .fb_blank       = drm_fb_helper_blank,
@@ -76,283 +45,146 @@ static struct fb_ops rockchip_drm_fb_ops = {
        .fb_setcmap     = drm_fb_helper_setcmap,
 };
 
-static int rockchip_drm_fbdev_update(struct drm_fb_helper *helper,
-                                    struct drm_framebuffer *fb)
-{
-       struct fb_info *fbi = helper->fbdev;
-       struct drm_device *dev = helper->dev;
-       struct rockchip_drm_gem_buf *buffer;
-       unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);
-       unsigned long offset;
-
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
-       drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
-       drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
-
-       /* RGB formats use only one buffer */
-       buffer = rockchip_drm_fb_buffer(fb, 0);
-       if (!buffer) {
-               DRM_LOG_KMS("buffer is null.\n");
-               return -EFAULT;
-       }
-
-       /* map pages with kernel virtual space. */
-       if (!buffer->kvaddr) {
-               if (is_drm_iommu_supported(dev)) {
-                       unsigned int nr_pages = buffer->size >> PAGE_SHIFT;
-
-                       buffer->kvaddr = vmap(buffer->pages, nr_pages, VM_MAP,
-                                       pgprot_writecombine(PAGE_KERNEL));
-               } else {
-                       phys_addr_t dma_addr = buffer->dma_addr;
-                       if (dma_addr)
-                               buffer->kvaddr = phys_to_virt(dma_addr);
-                       else
-                               buffer->kvaddr = (void __iomem *)NULL;
-               }
-               if (!buffer->kvaddr) {
-                       DRM_ERROR("failed to map pages to kernel space.\n");
-                       return -EIO;
-               }
-       }
-
-       /* buffer count to framebuffer always is 1 at booting time. */
-       rockchip_drm_fb_set_buf_cnt(fb, 1);
-
-       offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
-       offset += fbi->var.yoffset * fb->pitches[0];
-
-       dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr;
-       fbi->screen_base = buffer->kvaddr + offset;
-       if (is_drm_iommu_supported(dev))
-               fbi->fix.smem_start = (unsigned long)
-                       (page_to_phys(sg_page(buffer->sgt->sgl)) + offset);
-       else
-               fbi->fix.smem_start = (unsigned long)buffer->dma_addr;
-
-       fbi->screen_size = size;
-       fbi->fix.smem_len = size;
-
-       return 0;
-}
-
 static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
-                                   struct drm_fb_helper_surface_size *sizes)
+                                    struct drm_fb_helper_surface_size *sizes)
 {
-       struct rockchip_drm_fbdev *rockchip_fbdev = to_rockchip_fbdev(helper);
-       struct rockchip_drm_gem_obj *rockchip_gem_obj;
+       struct rockchip_drm_private *private = helper->dev->dev_private;
+       struct drm_mode_fb_cmd2 mode_cmd = { 0 };
        struct drm_device *dev = helper->dev;
+       struct rockchip_gem_object *rk_obj;
+       struct drm_framebuffer *fb;
+       unsigned int bytes_per_pixel;
+       unsigned long offset;
        struct fb_info *fbi;
-       struct drm_mode_fb_cmd2 mode_cmd = { 0 };
-       struct platform_device *pdev = dev->platformdev;
-       unsigned long size;
+       size_t size;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
-       DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
-                       sizes->surface_width, sizes->surface_height,
-                       sizes->surface_bpp);
+       bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
 
        mode_cmd.width = sizes->surface_width;
        mode_cmd.height = sizes->surface_height;
-       mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
+       mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
        mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
-                                                         sizes->surface_depth);
+               sizes->surface_depth);
 
-       mutex_lock(&dev->struct_mutex);
+       size = mode_cmd.pitches[0] * mode_cmd.height;
 
-       fbi = framebuffer_alloc(0, &pdev->dev);
-       if (!fbi) {
-               DRM_ERROR("failed to allocate fb info.\n");
-               ret = -ENOMEM;
-               goto out;
-       }
+       rk_obj = rockchip_gem_create_object(dev, size, true);
+       if (IS_ERR(rk_obj))
+               return -ENOMEM;
 
-       size = mode_cmd.pitches[0] * mode_cmd.height;
+       private->fbdev_bo = &rk_obj->base;
 
-       /* 0 means to allocate physically continuous memory */
-       rockchip_gem_obj = rockchip_drm_gem_create(dev, 0, size);
-       if (IS_ERR(rockchip_gem_obj)) {
-               ret = PTR_ERR(rockchip_gem_obj);
-               goto err_release_framebuffer;
+       fbi = drm_fb_helper_alloc_fbi(helper);
+       if (IS_ERR(fbi)) {
+               dev_err(dev->dev, "Failed to create framebuffer info.\n");
+               ret = PTR_ERR(fbi);
+               goto err_rockchip_gem_free_object;
        }
 
-       rockchip_fbdev->rockchip_gem_obj = rockchip_gem_obj;
-
        helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd,
-                       &rockchip_gem_obj->base);
+                                                  private->fbdev_bo);
        if (IS_ERR(helper->fb)) {
-               DRM_ERROR("failed to create drm framebuffer.\n");
+               dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
                ret = PTR_ERR(helper->fb);
-               goto err_destroy_gem;
+               goto err_release_fbi;
        }
 
-       helper->fbdev = fbi;
-
        fbi->par = helper;
        fbi->flags = FBINFO_FLAG_DEFAULT;
-       fbi->fbops = &rockchip_drm_fb_ops;
+       fbi->fbops = &rockchip_drm_fbdev_ops;
 
-       ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
-       if (ret) {
-               DRM_ERROR("failed to allocate cmap.\n");
-               goto err_destroy_framebuffer;
-       }
+       fb = helper->fb;
+       drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
+       drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height);
 
-       ret = rockchip_drm_fbdev_update(helper, helper->fb);
-       if (ret < 0)
-               goto err_dealloc_cmap;
+       offset = fbi->var.xoffset * bytes_per_pixel;
+       offset += fbi->var.yoffset * fb->pitches[0];
 
-       mutex_unlock(&dev->struct_mutex);
-       return ret;
+       dev->mode_config.fb_base = 0;
+       fbi->screen_base = rk_obj->kvaddr + offset;
+       fbi->screen_size = rk_obj->base.size;
+       fbi->fix.smem_len = rk_obj->base.size;
 
-err_dealloc_cmap:
-       fb_dealloc_cmap(&fbi->cmap);
-err_destroy_framebuffer:
-       drm_framebuffer_cleanup(helper->fb);
-err_destroy_gem:
-       rockchip_drm_gem_destroy(rockchip_gem_obj);
-err_release_framebuffer:
-       framebuffer_release(fbi);
+       DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%zu\n",
+                     fb->width, fb->height, fb->depth, rk_obj->kvaddr,
+                     offset, size);
 
-/*
- * if failed, all resources allocated above would be released by
- * drm_mode_config_cleanup() when drm_load() had been called prior
- * to any specific driver such as fimd or hdmi driver.
- */
-out:
-       mutex_unlock(&dev->struct_mutex);
+       fbi->skip_vt_switch = true;
+
+       return 0;
+
+err_release_fbi:
+       drm_fb_helper_release_fbi(helper);
+err_rockchip_gem_free_object:
+       rockchip_gem_free_object(&rk_obj->base);
        return ret;
 }
 
-static struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
-       .fb_probe =     rockchip_drm_fbdev_create,
+static const struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
+       .fb_probe = rockchip_drm_fbdev_create,
 };
 
 int rockchip_drm_fbdev_init(struct drm_device *dev)
 {
-       struct rockchip_drm_fbdev *fbdev;
        struct rockchip_drm_private *private = dev->dev_private;
        struct drm_fb_helper *helper;
        unsigned int num_crtc;
        int ret;
 
-       DRM_DEBUG_KMS("%s\n", __FILE__);
-
        if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
-               return 0;
+               return -EINVAL;
 
-       fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
-       if (!fbdev) {
-               DRM_ERROR("failed to allocate drm fbdev.\n");
+       num_crtc = dev->mode_config.num_crtc;
+
+       helper = devm_kzalloc(dev->dev, sizeof(*helper), GFP_KERNEL);
+       if (!helper)
                return -ENOMEM;
-       }
 
-       private->fb_helper = helper = &fbdev->drm_fb_helper;
-       helper->funcs = &rockchip_drm_fb_helper_funcs;
+       private->fbdev_helper = helper;
 
-       num_crtc = dev->mode_config.num_crtc;
+       drm_fb_helper_prepare(dev, helper, &rockchip_drm_fb_helper_funcs);
 
-       ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR);
+       ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR);
        if (ret < 0) {
-               DRM_ERROR("failed to initialize drm fb helper.\n");
-               goto err_init;
+               dev_err(dev->dev, "Failed to initialize drm fb helper - %d.\n",
+                       ret);
+               return ret;
        }
 
        ret = drm_fb_helper_single_add_all_connectors(helper);
        if (ret < 0) {
-               DRM_ERROR("failed to register drm_fb_helper_connector.\n");
-               goto err_setup;
-
+               dev_err(dev->dev, "Failed to add connectors - %d.\n", ret);
+               goto err_drm_fb_helper_fini;
        }
 
-       /* disable all the possible outputs/crtcs before entering KMS mode */
-       drm_helper_disable_unused_functions(dev);
-
        ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
        if (ret < 0) {
-               DRM_ERROR("failed to set up hw configuration.\n");
-               goto err_setup;
+               dev_err(dev->dev, "Failed to set initial hw config - %d.\n",
+                       ret);
+               goto err_drm_fb_helper_fini;
        }
 
        return 0;
 
-err_setup:
+err_drm_fb_helper_fini:
        drm_fb_helper_fini(helper);
-
-err_init:
-       private->fb_helper = NULL;
-       kfree(fbdev);
-
        return ret;
 }
 
-static void rockchip_drm_fbdev_destroy(struct drm_device *dev,
-                                     struct drm_fb_helper *fb_helper)
-{
-       struct rockchip_drm_fbdev *rockchip_fbd = to_rockchip_fbdev(fb_helper);
-       struct rockchip_drm_gem_obj *rockchip_gem_obj = rockchip_fbd->rockchip_gem_obj;
-       struct drm_framebuffer *fb;
-
-       if (is_drm_iommu_supported(dev) && rockchip_gem_obj->buffer->kvaddr)
-               vunmap(rockchip_gem_obj->buffer->kvaddr);
-
-       /* release drm framebuffer and real buffer */
-       if (fb_helper->fb && fb_helper->fb->funcs) {
-               fb = fb_helper->fb;
-               if (fb) {
-                       drm_framebuffer_unregister_private(fb);
-                       drm_framebuffer_remove(fb);
-               }
-       }
-
-       /* release linux framebuffer */
-       if (fb_helper->fbdev) {
-               struct fb_info *info;
-               int ret;
-
-               info = fb_helper->fbdev;
-               ret = unregister_framebuffer(info);
-               if (ret < 0)
-                       DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
-
-               if (info->cmap.len)
-                       fb_dealloc_cmap(&info->cmap);
-
-               framebuffer_release(info);
-       }
-
-       drm_fb_helper_fini(fb_helper);
-}
-
 void rockchip_drm_fbdev_fini(struct drm_device *dev)
 {
        struct rockchip_drm_private *private = dev->dev_private;
-       struct rockchip_drm_fbdev *fbdev;
+       struct drm_fb_helper *helper = private->fbdev_helper;
 
-       if (!private || !private->fb_helper)
+       if (!helper)
                return;
 
-       fbdev = to_rockchip_fbdev(private->fb_helper);
+       drm_fb_helper_unregister_fbi(helper);
+       drm_fb_helper_release_fbi(helper);
 
-       if (fbdev->rockchip_gem_obj)
-               rockchip_drm_gem_destroy(fbdev->rockchip_gem_obj);
+       if (helper->fb)
+               drm_framebuffer_unreference(helper->fb);
 
-       rockchip_drm_fbdev_destroy(dev, private->fb_helper);
-       kfree(fbdev);
-       private->fb_helper = NULL;
-}
-
-void rockchip_drm_fbdev_restore_mode(struct drm_device *dev)
-{
-       struct rockchip_drm_private *private = dev->dev_private;
-
-       if (!private || !private->fb_helper)
-               return;
-
-       drm_modeset_lock_all(dev);
-       drm_fb_helper_restore_fbdev_mode(private->fb_helper);
-       drm_modeset_unlock_all(dev);
+       drm_fb_helper_fini(helper);
 }