diff options
author | Chanho Park <chanho61.park@samsung.com> | 2014-01-02 15:45:40 +0900 |
---|---|---|
committer | Chanho Park <chanho61.park@samsung.com> | 2014-04-16 21:47:08 +0900 |
commit | 071a9cfb7a1239d0e41a7a4044381c2bbe2afe7f (patch) | |
tree | 90c76641e02d692557892d571f104b378fc67ca2 | |
parent | cb60c4c6781e6cf14dedb831b5796e5f5975303f (diff) | |
download | linux-3.10-071a9cfb7a1239d0e41a7a4044381c2bbe2afe7f.tar.gz linux-3.10-071a9cfb7a1239d0e41a7a4044381c2bbe2afe7f.tar.bz2 linux-3.10-071a9cfb7a1239d0e41a7a4044381c2bbe2afe7f.zip |
drm/pl111: add fb helper device to create fb during probe
Signed-off-by: Chanho Park <chanho61.park@samsung.com>
-rw-r--r-- | drivers/gpu/drm/pl111/Kbuild | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/pl111/Kconfig | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/pl111/pl111_drm.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/pl111/pl111_drm_device.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/pl111/pl111_drm_fb.c | 34 | ||||
-rw-r--r-- | drivers/gpu/drm/pl111/pl111_drm_fbdev.c | 300 | ||||
-rw-r--r-- | drivers/gpu/drm/pl111/pl111_drm_funcs.h | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/pl111/pl111_drm_gem.c | 90 | ||||
-rw-r--r-- | drivers/gpu/drm/pl111/pl111_drm_pl111.c | 16 |
9 files changed, 459 insertions, 3 deletions
diff --git a/drivers/gpu/drm/pl111/Kbuild b/drivers/gpu/drm/pl111/Kbuild index 5dbd333d045..a738ecdff4a 100644 --- a/drivers/gpu/drm/pl111/Kbuild +++ b/drivers/gpu/drm/pl111/Kbuild @@ -9,6 +9,7 @@ pl111_drm-y += pl111_drm_device.o \ pl111_drm_pl111.o \ pl111_drm_platform.o \ pl111_drm_suspend.o \ - pl111_drm_vma.o + pl111_drm_vma.o \ + pl111_drm_fbdev.o obj-$(CONFIG_DRM_PL111) += pl111_drm.o diff --git a/drivers/gpu/drm/pl111/Kconfig b/drivers/gpu/drm/pl111/Kconfig index 6aa4739da3a..e7a61e2f73d 100644 --- a/drivers/gpu/drm/pl111/Kconfig +++ b/drivers/gpu/drm/pl111/Kconfig @@ -2,6 +2,9 @@ config DRM_PL111 tristate "DRM Support for PL111 CLCD Controller" depends on DRM select DRM_KMS_HELPER + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE help Choose this option for DRM support for the PL111 CLCD controller. diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h index 6d39a8bfd5b..9a408bb833e 100644 --- a/drivers/gpu/drm/pl111/pl111_drm.h +++ b/drivers/gpu/drm/pl111/pl111_drm.h @@ -158,6 +158,7 @@ struct pl111_drm_encoder { }; struct pl111_drm_dev_private { + struct drm_fb_helper *fb_helper; struct pl111_drm_crtc *pl111_crtc; struct amba_device *amba_dev; diff --git a/drivers/gpu/drm/pl111/pl111_drm_device.c b/drivers/gpu/drm/pl111/pl111_drm_device.c index 4ade09aaad3..d5a78a8c78f 100644 --- a/drivers/gpu/drm/pl111/pl111_drm_device.c +++ b/drivers/gpu/drm/pl111/pl111_drm_device.c @@ -217,6 +217,12 @@ static int pl111_drm_load(struct drm_device *dev, unsigned long chipset) goto out_vblank; } + ret = pl111_drm_fbdev_init(dev); + if (ret) { + DRM_ERROR("Failed to init vblank\n"); + goto out_vblank; + } + goto finish; out_vblank: diff --git a/drivers/gpu/drm/pl111/pl111_drm_fb.c b/drivers/gpu/drm/pl111/pl111_drm_fb.c index fa376238215..a8e254b8d9c 100644 --- a/drivers/gpu/drm/pl111/pl111_drm_fb.c +++ b/drivers/gpu/drm/pl111/pl111_drm_fb.c @@ -96,6 +96,40 @@ const struct drm_framebuffer_funcs fb_funcs = { .create_handle = pl111_fb_create_handle, }; +struct drm_framebuffer *pl111_drm_fb_init(struct drm_device *dev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj) +{ + struct pl111_drm_framebuffer *pl111_fb; + struct drm_framebuffer *fb = NULL; + struct pl111_gem_bo *bo = PL111_BO_FROM_GEM(obj); + + pl111_fb = kzalloc(sizeof(struct pl111_drm_framebuffer), GFP_KERNEL); + if (pl111_fb == NULL) { + DRM_ERROR("Could not allocate pl111_drm_framebuffer\n"); + return ERR_PTR(-ENOMEM); + } + + fb = &pl111_fb->fb; + + if (drm_framebuffer_init(dev, fb, &fb_funcs)) { + DRM_ERROR("drm_framebuffer_init failed\n"); + kfree(pl111_fb); + fb = NULL; + goto out; + } + + drm_helper_mode_fill_fb_struct(fb, mode_cmd); + + PL111_BO_TO_FRAMEBUFFER(fb, bo); + + DRM_DEBUG_KMS("Created fb 0x%p for gem_obj 0x%p physaddr=0x%.8x\n", + fb, obj, bo->backing_data.dma.fb_dev_addr); + +out: + return fb; +} + struct drm_framebuffer *pl111_fb_create(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) diff --git a/drivers/gpu/drm/pl111/pl111_drm_fbdev.c b/drivers/gpu/drm/pl111/pl111_drm_fbdev.c new file mode 100644 index 00000000000..cf4691c1580 --- /dev/null +++ b/drivers/gpu/drm/pl111/pl111_drm_fbdev.c @@ -0,0 +1,300 @@ +/* + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved. + * + * Parts of this file were based on sources as follows: + * + * Copyright (c) 2006-2008 Intel Corporation + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> + * Copyright (C) 2011 Texas Instruments + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms of + * such GNU licence. + * + */ + +/** + * pl111_drm_fbdev.c + * Implementation of the framebuffer device for PL111 DRM + */ + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_crtc_helper.h> + +#include "pl111_drm.h" + +#define MAX_CONNECTOR 4 +#define PREFERRED_BPP 16 + +#define to_pl111_fbdev(x) container_of(x, struct pl111_drm_fbdev,\ + drm_fb_helper) + +struct pl111_drm_fbdev { + struct drm_fb_helper drm_fb_helper; + struct pl111_gem_bo *gem_bo; +}; + +static struct fb_ops pl111_drm_fb_ops = { + .owner = THIS_MODULE, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = 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_pan_display = drm_fb_helper_pan_display, + .fb_setcmap = drm_fb_helper_setcmap, +}; + +static int pl111_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 pl111_gem_bo *bo = PL111_BO_FROM_FRAMEBUFFER(fb); + struct pl111_gem_bo_dma *bo_dma = &bo->backing_data.dma; + unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3); + unsigned long offset; + + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); + drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height); + + /* map pages with kernel virtual space. */ + if (!bo_dma->fb_cpu_addr) { + phys_addr_t dma_addr = bo_dma->fb_dev_addr; + if (dma_addr) + bo_dma->fb_cpu_addr = phys_to_virt(dma_addr); + else + bo_dma->fb_cpu_addr = (void __iomem *)NULL; + + if (!bo_dma->fb_cpu_addr) { + DRM_ERROR("failed to map pages to kernel space.\n"); + return -EIO; + } + } + + offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3); + offset += fbi->var.yoffset * fb->pitches[0]; + + dev->mode_config.fb_base = (resource_size_t)bo_dma->fb_dev_addr; + fbi->screen_base = bo_dma->fb_cpu_addr + offset; + fbi->fix.smem_start = (unsigned long)bo_dma->fb_dev_addr; + + fbi->screen_size = size; + fbi->fix.smem_len = size; + + return 0; +} + +static int pl111_drm_fbdev_create(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct pl111_drm_fbdev *pl111_fbdev = to_pl111_fbdev(helper); + struct pl111_gem_bo *gem_bo; + struct drm_device *dev = helper->dev; + struct fb_info *fbi; + struct drm_mode_fb_cmd2 mode_cmd = { 0 }; + struct platform_device *pdev = dev->platformdev; + unsigned long size; + int ret; + + DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n", + sizes->surface_width, sizes->surface_height, + sizes->surface_bpp); + + 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.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + + mutex_lock(&dev->struct_mutex); + + fbi = framebuffer_alloc(0, &pdev->dev); + if (!fbi) { + DRM_ERROR("failed to allocate fb info.\n"); + ret = -ENOMEM; + goto out; + } + + size = mode_cmd.pitches[0] * mode_cmd.height; + + /* 0 means to allocate physically continuous memory */ + gem_bo = pl111_drm_gem_create(dev, 0, size); + if (IS_ERR(gem_bo)) { + ret = PTR_ERR(gem_bo); + goto err_release_framebuffer; + } + pl111_fbdev->gem_bo = gem_bo; + + helper->fb = pl111_drm_fb_init(dev, &mode_cmd, + &gem_bo->gem_object); + if (IS_ERR(helper->fb)) { + DRM_ERROR("failed to create drm framebuffer.\n"); + ret = PTR_ERR(helper->fb); + goto err_destroy_gem; + } + + helper->fbdev = fbi; + + fbi->par = helper; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->fbops = &pl111_drm_fb_ops; + + ret = fb_alloc_cmap(&fbi->cmap, 256, 0); + if (ret) { + DRM_ERROR("failed to allocate cmap.\n"); + goto err_destroy_framebuffer; + } + + ret = pl111_drm_fbdev_update(helper, helper->fb); + if (ret < 0) + goto err_dealloc_cmap; + + mutex_unlock(&dev->struct_mutex); + return ret; + +err_dealloc_cmap: + fb_dealloc_cmap(&fbi->cmap); +err_destroy_framebuffer: + drm_framebuffer_cleanup(helper->fb); +err_destroy_gem: + pl111_gem_free_object(&gem_bo->gem_object); +err_release_framebuffer: + framebuffer_release(fbi); + +/* + * 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); + return ret; +} + +static struct drm_fb_helper_funcs pl111_drm_fb_helper_funcs = { + .fb_probe = pl111_drm_fbdev_create, +}; + +int pl111_drm_fbdev_init(struct drm_device *dev) +{ + struct pl111_drm_fbdev *fbdev; + struct pl111_drm_dev_private *private = dev->dev_private; + struct drm_fb_helper *helper; + unsigned int num_crtc; + int ret; + + if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) + return 0; + + fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); + if (!fbdev) + return -ENOMEM; + + private->fb_helper = helper = &fbdev->drm_fb_helper; + helper->funcs = &pl111_drm_fb_helper_funcs; + + num_crtc = dev->mode_config.num_crtc; + + ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR); + if (ret < 0) { + DRM_ERROR("failed to initialize drm fb helper.\n"); + goto err_init; + } + + 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; + + } + + /* 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; + } + + return 0; + +err_setup: + drm_fb_helper_fini(helper); + +err_init: + private->fb_helper = NULL; + kfree(fbdev); + + return ret; +} + +static void pl111_drm_fbdev_destroy(struct drm_device *dev, + struct drm_fb_helper *fb_helper) +{ + struct pl111_drm_fbdev *pl111_fbd = to_pl111_fbdev(fb_helper); + struct pl111_gem_bo *gem_bo = pl111_fbd->gem_bo; + struct drm_framebuffer *fb; + + /* 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 pl111_drm_fbdev_fini(struct drm_device *dev) +{ + struct pl111_drm_dev_private *private = dev->dev_private; + struct pl111_drm_fbdev *fbdev; + + if (!private || !private->fb_helper) + return; + + fbdev = to_pl111_fbdev(private->fb_helper); + + if (fbdev->gem_bo) + pl111_gem_free_object(&fbdev->gem_bo->gem_object); + + pl111_drm_fbdev_destroy(dev, private->fb_helper); + kfree(fbdev); + private->fb_helper = NULL; +} + +void pl111_drm_fbdev_restore_mode(struct drm_device *dev) +{ + struct pl111_drm_dev_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); +} diff --git a/drivers/gpu/drm/pl111/pl111_drm_funcs.h b/drivers/gpu/drm/pl111/pl111_drm_funcs.h index de8a8266f89..25101ba8dca 100644 --- a/drivers/gpu/drm/pl111/pl111_drm_funcs.h +++ b/drivers/gpu/drm/pl111/pl111_drm_funcs.h @@ -64,10 +64,16 @@ struct pl111_drm_encoder *pl111_encoder_dummy_create(struct drm_device *dev, void pl111_encoder_destroy(struct drm_encoder *encoder); /* Frame Buffer Functions */ +struct drm_framebuffer *pl111_drm_fb_init(struct drm_device *dev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj); struct drm_framebuffer *pl111_fb_create(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd); +/* Frame Buffer Device Functions */ +int pl111_drm_fbdev_init(struct drm_device *dev); + /* VMA Functions */ int pl111_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); int pl111_gem_mmap(struct file *file_priv, struct vm_area_struct *vma); @@ -79,6 +85,9 @@ int pl111_drm_resume(struct drm_device *dev); int pl111_drm_suspend(struct drm_device *dev, pm_message_t state); /* GEM Functions */ +struct pl111_gem_bo *pl111_drm_gem_create(struct drm_device *dev, + unsigned int flags, + unsigned long size); int pl111_dumb_create(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args); diff --git a/drivers/gpu/drm/pl111/pl111_drm_gem.c b/drivers/gpu/drm/pl111/pl111_drm_gem.c index 01989ecd933..bd52399be76 100644 --- a/drivers/gpu/drm/pl111/pl111_drm_gem.c +++ b/drivers/gpu/drm/pl111/pl111_drm_gem.c @@ -28,6 +28,96 @@ #include <drm/drm_crtc_helper.h> #include "pl111_drm.h" +struct pl111_gem_bo *pl111_drm_gem_create(struct drm_device *dev, + unsigned int flags, + unsigned long size) +{ + int ret = 0; + struct pl111_gem_bo *bo = NULL; + bool create_contig_buffer; + + size = roundup(size, PAGE_SIZE); + + bo = kzalloc(sizeof(*bo), GFP_KERNEL); + if (bo == NULL) { + ret = -ENOMEM; + goto finish; + } + + create_contig_buffer = flags & PL111_BO_SCANOUT; +#ifndef ARCH_HAS_SG_CHAIN + /* + * If the ARCH can't chain we can't have non-contiguous allocs larger + * than a single sg can hold. + * In this case we fall back to using contiguous memory + */ + if (!create_contig_buffer) { + long unsigned int n_pages = + PAGE_ALIGN(size) >> PAGE_SHIFT; + if (n_pages > SG_MAX_SINGLE_ALLOC) { + create_contig_buffer = true; + /* + * Non-contiguous allocation request changed to + * contigous + */ + DRM_INFO("non-contig alloc to contig %lu > %lu pages.", + n_pages, SG_MAX_SINGLE_ALLOC); + } + } +#endif + if (!create_contig_buffer) { + /* not scanout compatible - use non-contiguous buffer */ + bo->type = PL111_BOT_SHM; + ret = drm_gem_object_init(dev, &bo->gem_object, size); + if (ret != 0) { + DRM_ERROR("DRM could not init SHM backed GEM obj\n"); + kfree(bo); + ret = -ENOMEM; + goto finish; + } + DRM_DEBUG_KMS("Num bytes: %d\n", bo->gem_object.size); + } else { + /* scanout compatible - use contiguous buffer */ + bo->type = PL111_BOT_DMA; + + bo->backing_data.dma.fb_cpu_addr = + dma_alloc_writecombine(dev->dev, size, + &bo->backing_data.dma.fb_dev_addr, + GFP_KERNEL); + if (bo->backing_data.dma.fb_cpu_addr == NULL) { + DRM_ERROR("dma_alloc_writecombine failed\n"); + kfree(bo); + ret = -ENOMEM; + goto finish; + } + + ret = drm_gem_private_object_init(dev, &bo->gem_object, + size); + if (ret != 0) { + DRM_ERROR("DRM could not initialise GEM object\n"); + dma_free_writecombine(dev->dev, size, + bo->backing_data.dma.fb_cpu_addr, + bo->backing_data.dma.fb_dev_addr); + kfree(bo); + ret = -ENOMEM; + goto finish; + } + } + + DRM_DEBUG_KMS("s=%llu, flags=0x%x, %s 0x%.8lx, type=%d\n", + size, flags, + (bo->type == PL111_BOT_DMA) ? "physaddr" : "shared page array", + (bo->type == PL111_BOT_DMA) + ? (unsigned long)bo->backing_data.dma.fb_dev_addr + : (unsigned long)bo->backing_data.shm.pages, bo->type); + + return bo; +finish: + if (ret) + return ERR_PTR(ret); + +} + void pl111_gem_free_object(struct drm_gem_object *obj) { struct pl111_gem_bo *bo; diff --git a/drivers/gpu/drm/pl111/pl111_drm_pl111.c b/drivers/gpu/drm/pl111/pl111_drm_pl111.c index daaa5ba791a..57d21f235b0 100644 --- a/drivers/gpu/drm/pl111/pl111_drm_pl111.c +++ b/drivers/gpu/drm/pl111/pl111_drm_pl111.c @@ -317,8 +317,14 @@ int pl111_amba_probe(struct amba_device *dev, const struct amba_id *id) int ret; pr_info("DRM %s\n", __func__); - if (board == NULL) - return -EINVAL; + if (board == NULL) { +#ifdef CONFIG_OF + board = kzalloc(sizeof(struct clcd_board), GFP_KERNEL); + if (!board) + return -ENOMEM; + dev->dev.platform_data = board; +#endif + } ret = amba_request_regions(dev, NULL); if (ret != 0) { @@ -335,8 +341,14 @@ int pl111_amba_probe(struct amba_device *dev, const struct amba_id *id) goto clk_err; } + ret = clk_prepare_enable(priv.clk); + if (ret) + goto clk_prepare_err; + return 0; +clk_prepare_err: + clk_put(priv.clk); clk_err: amba_release_regions(dev); out: |