/*
- * 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,
.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);
}