11c248b7dSInki Dae /* exynos_drm_fbdev.c 21c248b7dSInki Dae * 31c248b7dSInki Dae * Copyright (c) 2011 Samsung Electronics Co., Ltd. 41c248b7dSInki Dae * Authors: 51c248b7dSInki Dae * Inki Dae <inki.dae@samsung.com> 61c248b7dSInki Dae * Joonyoung Shim <jy0922.shim@samsung.com> 71c248b7dSInki Dae * Seung-Woo Kim <sw0312.kim@samsung.com> 81c248b7dSInki Dae * 9*d81aecb5SInki Dae * This program is free software; you can redistribute it and/or modify it 10*d81aecb5SInki Dae * under the terms of the GNU General Public License as published by the 11*d81aecb5SInki Dae * Free Software Foundation; either version 2 of the License, or (at your 12*d81aecb5SInki Dae * option) any later version. 131c248b7dSInki Dae */ 141c248b7dSInki Dae 15760285e7SDavid Howells #include <drm/drmP.h> 16760285e7SDavid Howells #include <drm/drm_crtc.h> 17760285e7SDavid Howells #include <drm/drm_fb_helper.h> 18760285e7SDavid Howells #include <drm/drm_crtc_helper.h> 191c248b7dSInki Dae 201c248b7dSInki Dae #include "exynos_drm_drv.h" 211c248b7dSInki Dae #include "exynos_drm_fb.h" 222c871127SInki Dae #include "exynos_drm_gem.h" 23c704f1b4SInki Dae #include "exynos_drm_iommu.h" 241c248b7dSInki Dae 251c248b7dSInki Dae #define MAX_CONNECTOR 4 261c248b7dSInki Dae #define PREFERRED_BPP 32 271c248b7dSInki Dae 281c248b7dSInki Dae #define to_exynos_fbdev(x) container_of(x, struct exynos_drm_fbdev,\ 291c248b7dSInki Dae drm_fb_helper) 301c248b7dSInki Dae 311c248b7dSInki Dae struct exynos_drm_fbdev { 321c248b7dSInki Dae struct drm_fb_helper drm_fb_helper; 33e1533c08SJoonyoung Shim struct exynos_drm_gem_obj *exynos_gem_obj; 341c248b7dSInki Dae }; 351c248b7dSInki Dae 36dd265850SPrathyush K static int exynos_drm_fb_mmap(struct fb_info *info, 37dd265850SPrathyush K struct vm_area_struct *vma) 38dd265850SPrathyush K { 39dd265850SPrathyush K struct drm_fb_helper *helper = info->par; 40dd265850SPrathyush K struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(helper); 41dd265850SPrathyush K struct exynos_drm_gem_obj *exynos_gem_obj = exynos_fbd->exynos_gem_obj; 42dd265850SPrathyush K struct exynos_drm_gem_buf *buffer = exynos_gem_obj->buffer; 43dd265850SPrathyush K unsigned long vm_size; 44dd265850SPrathyush K int ret; 45dd265850SPrathyush K 46dd265850SPrathyush K DRM_DEBUG_KMS("%s\n", __func__); 47dd265850SPrathyush K 48dd265850SPrathyush K vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; 49dd265850SPrathyush K 50dd265850SPrathyush K vm_size = vma->vm_end - vma->vm_start; 51dd265850SPrathyush K 52dd265850SPrathyush K if (vm_size > buffer->size) 53dd265850SPrathyush K return -EINVAL; 54dd265850SPrathyush K 554744ad24SInki Dae ret = dma_mmap_attrs(helper->dev->dev, vma, buffer->pages, 56dd265850SPrathyush K buffer->dma_addr, buffer->size, &buffer->dma_attrs); 57dd265850SPrathyush K if (ret < 0) { 58dd265850SPrathyush K DRM_ERROR("failed to mmap.\n"); 59dd265850SPrathyush K return ret; 60dd265850SPrathyush K } 61dd265850SPrathyush K 62dd265850SPrathyush K return 0; 63dd265850SPrathyush K } 64dd265850SPrathyush K 651c248b7dSInki Dae static struct fb_ops exynos_drm_fb_ops = { 661c248b7dSInki Dae .owner = THIS_MODULE, 67dd265850SPrathyush K .fb_mmap = exynos_drm_fb_mmap, 681c248b7dSInki Dae .fb_fillrect = cfb_fillrect, 691c248b7dSInki Dae .fb_copyarea = cfb_copyarea, 701c248b7dSInki Dae .fb_imageblit = cfb_imageblit, 711c248b7dSInki Dae .fb_check_var = drm_fb_helper_check_var, 7283b316fdSSascha Hauer .fb_set_par = drm_fb_helper_set_par, 731c248b7dSInki Dae .fb_blank = drm_fb_helper_blank, 741c248b7dSInki Dae .fb_pan_display = drm_fb_helper_pan_display, 751c248b7dSInki Dae .fb_setcmap = drm_fb_helper_setcmap, 761c248b7dSInki Dae }; 771c248b7dSInki Dae 7819c8b834SInki Dae static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, 79aa6b2b6cSSeung-Woo Kim struct drm_framebuffer *fb) 801c248b7dSInki Dae { 811c248b7dSInki Dae struct fb_info *fbi = helper->fbdev; 821c248b7dSInki Dae struct drm_device *dev = helper->dev; 832c871127SInki Dae struct exynos_drm_gem_buf *buffer; 84aa6b2b6cSSeung-Woo Kim unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3); 8519c8b834SInki Dae unsigned long offset; 861c248b7dSInki Dae 871c248b7dSInki Dae DRM_DEBUG_KMS("%s\n", __FILE__); 881c248b7dSInki Dae 8901f2c773SVille Syrjälä drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); 90aa6b2b6cSSeung-Woo Kim drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height); 911c248b7dSInki Dae 92229d3534SSeung-Woo Kim /* RGB formats use only one buffer */ 93229d3534SSeung-Woo Kim buffer = exynos_drm_fb_buffer(fb, 0); 942c871127SInki Dae if (!buffer) { 952c871127SInki Dae DRM_LOG_KMS("buffer is null.\n"); 9619c8b834SInki Dae return -EFAULT; 9719c8b834SInki Dae } 981c248b7dSInki Dae 994744ad24SInki Dae /* map pages with kernel virtual space. */ 1004744ad24SInki Dae if (!buffer->kvaddr) { 101c704f1b4SInki Dae if (is_drm_iommu_supported(dev)) { 1024744ad24SInki Dae unsigned int nr_pages = buffer->size >> PAGE_SHIFT; 103c704f1b4SInki Dae 1044744ad24SInki Dae buffer->kvaddr = vmap(buffer->pages, nr_pages, VM_MAP, 1054744ad24SInki Dae pgprot_writecombine(PAGE_KERNEL)); 106c704f1b4SInki Dae } else { 107c704f1b4SInki Dae phys_addr_t dma_addr = buffer->dma_addr; 108c704f1b4SInki Dae if (dma_addr) 109c704f1b4SInki Dae buffer->kvaddr = phys_to_virt(dma_addr); 110c704f1b4SInki Dae else 111c704f1b4SInki Dae buffer->kvaddr = (void __iomem *)NULL; 112c704f1b4SInki Dae } 1134744ad24SInki Dae if (!buffer->kvaddr) { 1144744ad24SInki Dae DRM_ERROR("failed to map pages to kernel space.\n"); 1154744ad24SInki Dae return -EIO; 1164744ad24SInki Dae } 1174744ad24SInki Dae } 1184744ad24SInki Dae 11901ed8126SInki Dae /* buffer count to framebuffer always is 1 at booting time. */ 12001ed8126SInki Dae exynos_drm_fb_set_buf_cnt(fb, 1); 12101ed8126SInki Dae 12219c8b834SInki Dae offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3); 12301f2c773SVille Syrjälä offset += fbi->var.yoffset * fb->pitches[0]; 1241c248b7dSInki Dae 1252c871127SInki Dae dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr; 1262c871127SInki Dae fbi->screen_base = buffer->kvaddr + offset; 127c704f1b4SInki Dae if (is_drm_iommu_supported(dev)) 128640631d0SPrathyush K fbi->fix.smem_start = (unsigned long) 129640631d0SPrathyush K (page_to_phys(sg_page(buffer->sgt->sgl)) + offset); 130c704f1b4SInki Dae else 131c704f1b4SInki Dae fbi->fix.smem_start = (unsigned long)buffer->dma_addr; 132c704f1b4SInki Dae 1331c248b7dSInki Dae fbi->screen_size = size; 1341c248b7dSInki Dae fbi->fix.smem_len = size; 13519c8b834SInki Dae 13619c8b834SInki Dae return 0; 1371c248b7dSInki Dae } 1381c248b7dSInki Dae 1391c248b7dSInki Dae static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, 1401c248b7dSInki Dae struct drm_fb_helper_surface_size *sizes) 1411c248b7dSInki Dae { 1421c248b7dSInki Dae struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); 143e1533c08SJoonyoung Shim struct exynos_drm_gem_obj *exynos_gem_obj; 1441c248b7dSInki Dae struct drm_device *dev = helper->dev; 1451c248b7dSInki Dae struct fb_info *fbi; 146a794d57dSJoonyoung Shim struct drm_mode_fb_cmd2 mode_cmd = { 0 }; 1471c248b7dSInki Dae struct platform_device *pdev = dev->platformdev; 148e1533c08SJoonyoung Shim unsigned long size; 1491c248b7dSInki Dae int ret; 1501c248b7dSInki Dae 1511c248b7dSInki Dae DRM_DEBUG_KMS("%s\n", __FILE__); 1521c248b7dSInki Dae 1531c248b7dSInki Dae DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n", 1541c248b7dSInki Dae sizes->surface_width, sizes->surface_height, 1551c248b7dSInki Dae sizes->surface_bpp); 1561c248b7dSInki Dae 1571c248b7dSInki Dae mode_cmd.width = sizes->surface_width; 1581c248b7dSInki Dae mode_cmd.height = sizes->surface_height; 159a794d57dSJoonyoung Shim mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3); 160a794d57dSJoonyoung Shim mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 161a794d57dSJoonyoung Shim sizes->surface_depth); 1621c248b7dSInki Dae 1631c248b7dSInki Dae mutex_lock(&dev->struct_mutex); 1641c248b7dSInki Dae 1651c248b7dSInki Dae fbi = framebuffer_alloc(0, &pdev->dev); 1661c248b7dSInki Dae if (!fbi) { 1671c248b7dSInki Dae DRM_ERROR("failed to allocate fb info.\n"); 1681c248b7dSInki Dae ret = -ENOMEM; 1691c248b7dSInki Dae goto out; 1701c248b7dSInki Dae } 1711c248b7dSInki Dae 172e1533c08SJoonyoung Shim size = mode_cmd.pitches[0] * mode_cmd.height; 1732b35892eSInki Dae 1742b35892eSInki Dae /* 0 means to allocate physically continuous memory */ 1752b35892eSInki Dae exynos_gem_obj = exynos_drm_gem_create(dev, 0, size); 176e1533c08SJoonyoung Shim if (IS_ERR(exynos_gem_obj)) { 177e1533c08SJoonyoung Shim ret = PTR_ERR(exynos_gem_obj); 178662aa6d7SInki Dae goto err_release_framebuffer; 1791c248b7dSInki Dae } 1801c248b7dSInki Dae 181e1533c08SJoonyoung Shim exynos_fbdev->exynos_gem_obj = exynos_gem_obj; 182e1533c08SJoonyoung Shim 183e1533c08SJoonyoung Shim helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd, 184e1533c08SJoonyoung Shim &exynos_gem_obj->base); 185e1533c08SJoonyoung Shim if (IS_ERR_OR_NULL(helper->fb)) { 186e1533c08SJoonyoung Shim DRM_ERROR("failed to create drm framebuffer.\n"); 187e1533c08SJoonyoung Shim ret = PTR_ERR(helper->fb); 188662aa6d7SInki Dae goto err_destroy_gem; 189e1533c08SJoonyoung Shim } 190e1533c08SJoonyoung Shim 1911c248b7dSInki Dae helper->fbdev = fbi; 1921c248b7dSInki Dae 1931c248b7dSInki Dae fbi->par = helper; 1941c248b7dSInki Dae fbi->flags = FBINFO_FLAG_DEFAULT; 1951c248b7dSInki Dae fbi->fbops = &exynos_drm_fb_ops; 1961c248b7dSInki Dae 1971c248b7dSInki Dae ret = fb_alloc_cmap(&fbi->cmap, 256, 0); 1981c248b7dSInki Dae if (ret) { 1991c248b7dSInki Dae DRM_ERROR("failed to allocate cmap.\n"); 200662aa6d7SInki Dae goto err_destroy_framebuffer; 2011c248b7dSInki Dae } 2021c248b7dSInki Dae 203aa6b2b6cSSeung-Woo Kim ret = exynos_drm_fbdev_update(helper, helper->fb); 204662aa6d7SInki Dae if (ret < 0) 205662aa6d7SInki Dae goto err_dealloc_cmap; 206662aa6d7SInki Dae 207662aa6d7SInki Dae mutex_unlock(&dev->struct_mutex); 208662aa6d7SInki Dae return ret; 209662aa6d7SInki Dae 210662aa6d7SInki Dae err_dealloc_cmap: 21119c8b834SInki Dae fb_dealloc_cmap(&fbi->cmap); 212662aa6d7SInki Dae err_destroy_framebuffer: 213662aa6d7SInki Dae drm_framebuffer_cleanup(helper->fb); 214662aa6d7SInki Dae err_destroy_gem: 215662aa6d7SInki Dae exynos_drm_gem_destroy(exynos_gem_obj); 216662aa6d7SInki Dae err_release_framebuffer: 217662aa6d7SInki Dae framebuffer_release(fbi); 2181c248b7dSInki Dae 2191c248b7dSInki Dae /* 2201c248b7dSInki Dae * if failed, all resources allocated above would be released by 2211c248b7dSInki Dae * drm_mode_config_cleanup() when drm_load() had been called prior 2221c248b7dSInki Dae * to any specific driver such as fimd or hdmi driver. 2231c248b7dSInki Dae */ 2241c248b7dSInki Dae out: 2251c248b7dSInki Dae mutex_unlock(&dev->struct_mutex); 2261c248b7dSInki Dae return ret; 2271c248b7dSInki Dae } 2281c248b7dSInki Dae 2291c248b7dSInki Dae static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper, 2301c248b7dSInki Dae struct drm_fb_helper_surface_size *sizes) 2311c248b7dSInki Dae { 2321c248b7dSInki Dae int ret = 0; 2331c248b7dSInki Dae 2341c248b7dSInki Dae DRM_DEBUG_KMS("%s\n", __FILE__); 2351c248b7dSInki Dae 236bc41eae2SInki Dae /* 237bc41eae2SInki Dae * with !helper->fb, it means that this funcion is called first time 238bc41eae2SInki Dae * and after that, the helper->fb would be used as clone mode. 239bc41eae2SInki Dae */ 2401c248b7dSInki Dae if (!helper->fb) { 2411c248b7dSInki Dae ret = exynos_drm_fbdev_create(helper, sizes); 2421c248b7dSInki Dae if (ret < 0) { 2431c248b7dSInki Dae DRM_ERROR("failed to create fbdev.\n"); 2441c248b7dSInki Dae return ret; 2451c248b7dSInki Dae } 2461c248b7dSInki Dae 2471c248b7dSInki Dae /* 2481c248b7dSInki Dae * fb_helper expects a value more than 1 if succeed 2491c248b7dSInki Dae * because register_framebuffer() should be called. 2501c248b7dSInki Dae */ 2511c248b7dSInki Dae ret = 1; 2521c248b7dSInki Dae } 2531c248b7dSInki Dae 2541c248b7dSInki Dae return ret; 2551c248b7dSInki Dae } 2561c248b7dSInki Dae 2571c248b7dSInki Dae static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { 2581c248b7dSInki Dae .fb_probe = exynos_drm_fbdev_probe, 2591c248b7dSInki Dae }; 2601c248b7dSInki Dae 2611c248b7dSInki Dae int exynos_drm_fbdev_init(struct drm_device *dev) 2621c248b7dSInki Dae { 2631c248b7dSInki Dae struct exynos_drm_fbdev *fbdev; 2641c248b7dSInki Dae struct exynos_drm_private *private = dev->dev_private; 2651c248b7dSInki Dae struct drm_fb_helper *helper; 2661c248b7dSInki Dae unsigned int num_crtc; 2671c248b7dSInki Dae int ret; 2681c248b7dSInki Dae 2691c248b7dSInki Dae DRM_DEBUG_KMS("%s\n", __FILE__); 2701c248b7dSInki Dae 2711c248b7dSInki Dae if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) 2721c248b7dSInki Dae return 0; 2731c248b7dSInki Dae 2741c248b7dSInki Dae fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); 2751c248b7dSInki Dae if (!fbdev) { 2761c248b7dSInki Dae DRM_ERROR("failed to allocate drm fbdev.\n"); 2771c248b7dSInki Dae return -ENOMEM; 2781c248b7dSInki Dae } 2791c248b7dSInki Dae 2801c248b7dSInki Dae private->fb_helper = helper = &fbdev->drm_fb_helper; 2811c248b7dSInki Dae helper->funcs = &exynos_drm_fb_helper_funcs; 2821c248b7dSInki Dae 2831c248b7dSInki Dae num_crtc = dev->mode_config.num_crtc; 2841c248b7dSInki Dae 2851c248b7dSInki Dae ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR); 2861c248b7dSInki Dae if (ret < 0) { 2871c248b7dSInki Dae DRM_ERROR("failed to initialize drm fb helper.\n"); 2881c248b7dSInki Dae goto err_init; 2891c248b7dSInki Dae } 2901c248b7dSInki Dae 2911c248b7dSInki Dae ret = drm_fb_helper_single_add_all_connectors(helper); 2921c248b7dSInki Dae if (ret < 0) { 2931c248b7dSInki Dae DRM_ERROR("failed to register drm_fb_helper_connector.\n"); 2941c248b7dSInki Dae goto err_setup; 2951c248b7dSInki Dae 2961c248b7dSInki Dae } 2971c248b7dSInki Dae 2981c248b7dSInki Dae ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); 2991c248b7dSInki Dae if (ret < 0) { 3001c248b7dSInki Dae DRM_ERROR("failed to set up hw configuration.\n"); 3011c248b7dSInki Dae goto err_setup; 3021c248b7dSInki Dae } 3031c248b7dSInki Dae 3041c248b7dSInki Dae return 0; 3051c248b7dSInki Dae 3061c248b7dSInki Dae err_setup: 3071c248b7dSInki Dae drm_fb_helper_fini(helper); 3081c248b7dSInki Dae 3091c248b7dSInki Dae err_init: 3101c248b7dSInki Dae private->fb_helper = NULL; 3111c248b7dSInki Dae kfree(fbdev); 3121c248b7dSInki Dae 3131c248b7dSInki Dae return ret; 3141c248b7dSInki Dae } 3151c248b7dSInki Dae 3161c248b7dSInki Dae static void exynos_drm_fbdev_destroy(struct drm_device *dev, 3171c248b7dSInki Dae struct drm_fb_helper *fb_helper) 3181c248b7dSInki Dae { 3194744ad24SInki Dae struct exynos_drm_fbdev *exynos_fbd = to_exynos_fbdev(fb_helper); 3204744ad24SInki Dae struct exynos_drm_gem_obj *exynos_gem_obj = exynos_fbd->exynos_gem_obj; 3211c248b7dSInki Dae struct drm_framebuffer *fb; 3221c248b7dSInki Dae 323c704f1b4SInki Dae if (is_drm_iommu_supported(dev) && exynos_gem_obj->buffer->kvaddr) 3244744ad24SInki Dae vunmap(exynos_gem_obj->buffer->kvaddr); 3254744ad24SInki Dae 3261c248b7dSInki Dae /* release drm framebuffer and real buffer */ 3271c248b7dSInki Dae if (fb_helper->fb && fb_helper->fb->funcs) { 3281c248b7dSInki Dae fb = fb_helper->fb; 329f7eff60eSRob Clark if (fb) 330f7eff60eSRob Clark drm_framebuffer_remove(fb); 3311c248b7dSInki Dae } 3321c248b7dSInki Dae 3331c248b7dSInki Dae /* release linux framebuffer */ 3341c248b7dSInki Dae if (fb_helper->fbdev) { 3351c248b7dSInki Dae struct fb_info *info; 3361c248b7dSInki Dae int ret; 3371c248b7dSInki Dae 3381c248b7dSInki Dae info = fb_helper->fbdev; 3391c248b7dSInki Dae ret = unregister_framebuffer(info); 3401c248b7dSInki Dae if (ret < 0) 3411c248b7dSInki Dae DRM_DEBUG_KMS("failed unregister_framebuffer()\n"); 3421c248b7dSInki Dae 3431c248b7dSInki Dae if (info->cmap.len) 3441c248b7dSInki Dae fb_dealloc_cmap(&info->cmap); 3451c248b7dSInki Dae 3461c248b7dSInki Dae framebuffer_release(info); 3471c248b7dSInki Dae } 3481c248b7dSInki Dae 3491c248b7dSInki Dae drm_fb_helper_fini(fb_helper); 3501c248b7dSInki Dae } 3511c248b7dSInki Dae 3521c248b7dSInki Dae void exynos_drm_fbdev_fini(struct drm_device *dev) 3531c248b7dSInki Dae { 3541c248b7dSInki Dae struct exynos_drm_private *private = dev->dev_private; 3551c248b7dSInki Dae struct exynos_drm_fbdev *fbdev; 3561c248b7dSInki Dae 3571c248b7dSInki Dae if (!private || !private->fb_helper) 3581c248b7dSInki Dae return; 3591c248b7dSInki Dae 3601c248b7dSInki Dae fbdev = to_exynos_fbdev(private->fb_helper); 3611c248b7dSInki Dae 362e1533c08SJoonyoung Shim if (fbdev->exynos_gem_obj) 363e1533c08SJoonyoung Shim exynos_drm_gem_destroy(fbdev->exynos_gem_obj); 364e1533c08SJoonyoung Shim 3651c248b7dSInki Dae exynos_drm_fbdev_destroy(dev, private->fb_helper); 3661c248b7dSInki Dae kfree(fbdev); 3671c248b7dSInki Dae private->fb_helper = NULL; 3681c248b7dSInki Dae } 3691c248b7dSInki Dae 3701c248b7dSInki Dae void exynos_drm_fbdev_restore_mode(struct drm_device *dev) 3711c248b7dSInki Dae { 3721c248b7dSInki Dae struct exynos_drm_private *private = dev->dev_private; 3731c248b7dSInki Dae 3741c248b7dSInki Dae if (!private || !private->fb_helper) 3751c248b7dSInki Dae return; 3761c248b7dSInki Dae 3771c248b7dSInki Dae drm_fb_helper_restore_fbdev_mode(private->fb_helper); 3781c248b7dSInki Dae } 379