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 * 91c248b7dSInki Dae * Permission is hereby granted, free of charge, to any person obtaining a 101c248b7dSInki Dae * copy of this software and associated documentation files (the "Software"), 111c248b7dSInki Dae * to deal in the Software without restriction, including without limitation 121c248b7dSInki Dae * the rights to use, copy, modify, merge, publish, distribute, sublicense, 131c248b7dSInki Dae * and/or sell copies of the Software, and to permit persons to whom the 141c248b7dSInki Dae * Software is furnished to do so, subject to the following conditions: 151c248b7dSInki Dae * 161c248b7dSInki Dae * The above copyright notice and this permission notice (including the next 171c248b7dSInki Dae * paragraph) shall be included in all copies or substantial portions of the 181c248b7dSInki Dae * Software. 191c248b7dSInki Dae * 201c248b7dSInki Dae * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 211c248b7dSInki Dae * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 221c248b7dSInki Dae * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 231c248b7dSInki Dae * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 241c248b7dSInki Dae * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 251c248b7dSInki Dae * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 261c248b7dSInki Dae * OTHER DEALINGS IN THE SOFTWARE. 271c248b7dSInki Dae */ 281c248b7dSInki Dae 291c248b7dSInki Dae #include "drmP.h" 301c248b7dSInki Dae #include "drm_crtc.h" 311c248b7dSInki Dae #include "drm_fb_helper.h" 321c248b7dSInki Dae #include "drm_crtc_helper.h" 331c248b7dSInki Dae 341c248b7dSInki Dae #include "exynos_drm_drv.h" 351c248b7dSInki Dae #include "exynos_drm_fb.h" 362c871127SInki Dae #include "exynos_drm_gem.h" 371c248b7dSInki Dae 381c248b7dSInki Dae #define MAX_CONNECTOR 4 391c248b7dSInki Dae #define PREFERRED_BPP 32 401c248b7dSInki Dae 411c248b7dSInki Dae #define to_exynos_fbdev(x) container_of(x, struct exynos_drm_fbdev,\ 421c248b7dSInki Dae drm_fb_helper) 431c248b7dSInki Dae 441c248b7dSInki Dae struct exynos_drm_fbdev { 451c248b7dSInki Dae struct drm_fb_helper drm_fb_helper; 46e1533c08SJoonyoung Shim struct exynos_drm_gem_obj *exynos_gem_obj; 471c248b7dSInki Dae }; 481c248b7dSInki Dae 491c248b7dSInki Dae static struct fb_ops exynos_drm_fb_ops = { 501c248b7dSInki Dae .owner = THIS_MODULE, 511c248b7dSInki Dae .fb_fillrect = cfb_fillrect, 521c248b7dSInki Dae .fb_copyarea = cfb_copyarea, 531c248b7dSInki Dae .fb_imageblit = cfb_imageblit, 541c248b7dSInki Dae .fb_check_var = drm_fb_helper_check_var, 5583b316fdSSascha Hauer .fb_set_par = drm_fb_helper_set_par, 561c248b7dSInki Dae .fb_blank = drm_fb_helper_blank, 571c248b7dSInki Dae .fb_pan_display = drm_fb_helper_pan_display, 581c248b7dSInki Dae .fb_setcmap = drm_fb_helper_setcmap, 591c248b7dSInki Dae }; 601c248b7dSInki Dae 6119c8b834SInki Dae static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, 62aa6b2b6cSSeung-Woo Kim struct drm_framebuffer *fb) 631c248b7dSInki Dae { 641c248b7dSInki Dae struct fb_info *fbi = helper->fbdev; 651c248b7dSInki Dae struct drm_device *dev = helper->dev; 662c871127SInki Dae struct exynos_drm_gem_buf *buffer; 67aa6b2b6cSSeung-Woo Kim unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3); 6819c8b834SInki Dae unsigned long offset; 691c248b7dSInki Dae 701c248b7dSInki Dae DRM_DEBUG_KMS("%s\n", __FILE__); 711c248b7dSInki Dae 7201f2c773SVille Syrjälä drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); 73aa6b2b6cSSeung-Woo Kim drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height); 741c248b7dSInki Dae 75229d3534SSeung-Woo Kim /* RGB formats use only one buffer */ 76229d3534SSeung-Woo Kim buffer = exynos_drm_fb_buffer(fb, 0); 772c871127SInki Dae if (!buffer) { 782c871127SInki Dae DRM_LOG_KMS("buffer is null.\n"); 7919c8b834SInki Dae return -EFAULT; 8019c8b834SInki Dae } 811c248b7dSInki Dae 82*01ed8126SInki Dae /* buffer count to framebuffer always is 1 at booting time. */ 83*01ed8126SInki Dae exynos_drm_fb_set_buf_cnt(fb, 1); 84*01ed8126SInki Dae 8519c8b834SInki Dae offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3); 8601f2c773SVille Syrjälä offset += fbi->var.yoffset * fb->pitches[0]; 871c248b7dSInki Dae 882c871127SInki Dae dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr; 892c871127SInki Dae fbi->screen_base = buffer->kvaddr + offset; 902c871127SInki Dae fbi->fix.smem_start = (unsigned long)(buffer->dma_addr + offset); 911c248b7dSInki Dae fbi->screen_size = size; 921c248b7dSInki Dae fbi->fix.smem_len = size; 9319c8b834SInki Dae 9419c8b834SInki Dae return 0; 951c248b7dSInki Dae } 961c248b7dSInki Dae 971c248b7dSInki Dae static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, 981c248b7dSInki Dae struct drm_fb_helper_surface_size *sizes) 991c248b7dSInki Dae { 1001c248b7dSInki Dae struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); 101e1533c08SJoonyoung Shim struct exynos_drm_gem_obj *exynos_gem_obj; 1021c248b7dSInki Dae struct drm_device *dev = helper->dev; 1031c248b7dSInki Dae struct fb_info *fbi; 104a794d57dSJoonyoung Shim struct drm_mode_fb_cmd2 mode_cmd = { 0 }; 1051c248b7dSInki Dae struct platform_device *pdev = dev->platformdev; 106e1533c08SJoonyoung Shim unsigned long size; 1071c248b7dSInki Dae int ret; 1081c248b7dSInki Dae 1091c248b7dSInki Dae DRM_DEBUG_KMS("%s\n", __FILE__); 1101c248b7dSInki Dae 1111c248b7dSInki Dae DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n", 1121c248b7dSInki Dae sizes->surface_width, sizes->surface_height, 1131c248b7dSInki Dae sizes->surface_bpp); 1141c248b7dSInki Dae 1151c248b7dSInki Dae mode_cmd.width = sizes->surface_width; 1161c248b7dSInki Dae mode_cmd.height = sizes->surface_height; 117a794d57dSJoonyoung Shim mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3); 118a794d57dSJoonyoung Shim mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 119a794d57dSJoonyoung Shim sizes->surface_depth); 1201c248b7dSInki Dae 1211c248b7dSInki Dae mutex_lock(&dev->struct_mutex); 1221c248b7dSInki Dae 1231c248b7dSInki Dae fbi = framebuffer_alloc(0, &pdev->dev); 1241c248b7dSInki Dae if (!fbi) { 1251c248b7dSInki Dae DRM_ERROR("failed to allocate fb info.\n"); 1261c248b7dSInki Dae ret = -ENOMEM; 1271c248b7dSInki Dae goto out; 1281c248b7dSInki Dae } 1291c248b7dSInki Dae 130e1533c08SJoonyoung Shim size = mode_cmd.pitches[0] * mode_cmd.height; 1312b35892eSInki Dae 1322b35892eSInki Dae /* 0 means to allocate physically continuous memory */ 1332b35892eSInki Dae exynos_gem_obj = exynos_drm_gem_create(dev, 0, size); 134e1533c08SJoonyoung Shim if (IS_ERR(exynos_gem_obj)) { 135e1533c08SJoonyoung Shim ret = PTR_ERR(exynos_gem_obj); 1361c248b7dSInki Dae goto out; 1371c248b7dSInki Dae } 1381c248b7dSInki Dae 139e1533c08SJoonyoung Shim exynos_fbdev->exynos_gem_obj = exynos_gem_obj; 140e1533c08SJoonyoung Shim 141e1533c08SJoonyoung Shim helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd, 142e1533c08SJoonyoung Shim &exynos_gem_obj->base); 143e1533c08SJoonyoung Shim if (IS_ERR_OR_NULL(helper->fb)) { 144e1533c08SJoonyoung Shim DRM_ERROR("failed to create drm framebuffer.\n"); 145e1533c08SJoonyoung Shim ret = PTR_ERR(helper->fb); 146e1533c08SJoonyoung Shim goto out; 147e1533c08SJoonyoung Shim } 148e1533c08SJoonyoung Shim 1491c248b7dSInki Dae helper->fbdev = fbi; 1501c248b7dSInki Dae 1511c248b7dSInki Dae fbi->par = helper; 1521c248b7dSInki Dae fbi->flags = FBINFO_FLAG_DEFAULT; 1531c248b7dSInki Dae fbi->fbops = &exynos_drm_fb_ops; 1541c248b7dSInki Dae 1551c248b7dSInki Dae ret = fb_alloc_cmap(&fbi->cmap, 256, 0); 1561c248b7dSInki Dae if (ret) { 1571c248b7dSInki Dae DRM_ERROR("failed to allocate cmap.\n"); 1581c248b7dSInki Dae goto out; 1591c248b7dSInki Dae } 1601c248b7dSInki Dae 161aa6b2b6cSSeung-Woo Kim ret = exynos_drm_fbdev_update(helper, helper->fb); 162e1533c08SJoonyoung Shim if (ret < 0) { 16319c8b834SInki Dae fb_dealloc_cmap(&fbi->cmap); 164e1533c08SJoonyoung Shim goto out; 165e1533c08SJoonyoung Shim } 1661c248b7dSInki Dae 1671c248b7dSInki Dae /* 1681c248b7dSInki Dae * if failed, all resources allocated above would be released by 1691c248b7dSInki Dae * drm_mode_config_cleanup() when drm_load() had been called prior 1701c248b7dSInki Dae * to any specific driver such as fimd or hdmi driver. 1711c248b7dSInki Dae */ 1721c248b7dSInki Dae out: 1731c248b7dSInki Dae mutex_unlock(&dev->struct_mutex); 1741c248b7dSInki Dae return ret; 1751c248b7dSInki Dae } 1761c248b7dSInki Dae 1771c248b7dSInki Dae static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper, 1781c248b7dSInki Dae struct drm_fb_helper_surface_size *sizes) 1791c248b7dSInki Dae { 1801c248b7dSInki Dae int ret = 0; 1811c248b7dSInki Dae 1821c248b7dSInki Dae DRM_DEBUG_KMS("%s\n", __FILE__); 1831c248b7dSInki Dae 184bc41eae2SInki Dae /* 185bc41eae2SInki Dae * with !helper->fb, it means that this funcion is called first time 186bc41eae2SInki Dae * and after that, the helper->fb would be used as clone mode. 187bc41eae2SInki Dae */ 1881c248b7dSInki Dae if (!helper->fb) { 1891c248b7dSInki Dae ret = exynos_drm_fbdev_create(helper, sizes); 1901c248b7dSInki Dae if (ret < 0) { 1911c248b7dSInki Dae DRM_ERROR("failed to create fbdev.\n"); 1921c248b7dSInki Dae return ret; 1931c248b7dSInki Dae } 1941c248b7dSInki Dae 1951c248b7dSInki Dae /* 1961c248b7dSInki Dae * fb_helper expects a value more than 1 if succeed 1971c248b7dSInki Dae * because register_framebuffer() should be called. 1981c248b7dSInki Dae */ 1991c248b7dSInki Dae ret = 1; 2001c248b7dSInki Dae } 2011c248b7dSInki Dae 2021c248b7dSInki Dae return ret; 2031c248b7dSInki Dae } 2041c248b7dSInki Dae 2051c248b7dSInki Dae static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { 2061c248b7dSInki Dae .fb_probe = exynos_drm_fbdev_probe, 2071c248b7dSInki Dae }; 2081c248b7dSInki Dae 2091c248b7dSInki Dae int exynos_drm_fbdev_init(struct drm_device *dev) 2101c248b7dSInki Dae { 2111c248b7dSInki Dae struct exynos_drm_fbdev *fbdev; 2121c248b7dSInki Dae struct exynos_drm_private *private = dev->dev_private; 2131c248b7dSInki Dae struct drm_fb_helper *helper; 2141c248b7dSInki Dae unsigned int num_crtc; 2151c248b7dSInki Dae int ret; 2161c248b7dSInki Dae 2171c248b7dSInki Dae DRM_DEBUG_KMS("%s\n", __FILE__); 2181c248b7dSInki Dae 2191c248b7dSInki Dae if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) 2201c248b7dSInki Dae return 0; 2211c248b7dSInki Dae 2221c248b7dSInki Dae fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); 2231c248b7dSInki Dae if (!fbdev) { 2241c248b7dSInki Dae DRM_ERROR("failed to allocate drm fbdev.\n"); 2251c248b7dSInki Dae return -ENOMEM; 2261c248b7dSInki Dae } 2271c248b7dSInki Dae 2281c248b7dSInki Dae private->fb_helper = helper = &fbdev->drm_fb_helper; 2291c248b7dSInki Dae helper->funcs = &exynos_drm_fb_helper_funcs; 2301c248b7dSInki Dae 2311c248b7dSInki Dae num_crtc = dev->mode_config.num_crtc; 2321c248b7dSInki Dae 2331c248b7dSInki Dae ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR); 2341c248b7dSInki Dae if (ret < 0) { 2351c248b7dSInki Dae DRM_ERROR("failed to initialize drm fb helper.\n"); 2361c248b7dSInki Dae goto err_init; 2371c248b7dSInki Dae } 2381c248b7dSInki Dae 2391c248b7dSInki Dae ret = drm_fb_helper_single_add_all_connectors(helper); 2401c248b7dSInki Dae if (ret < 0) { 2411c248b7dSInki Dae DRM_ERROR("failed to register drm_fb_helper_connector.\n"); 2421c248b7dSInki Dae goto err_setup; 2431c248b7dSInki Dae 2441c248b7dSInki Dae } 2451c248b7dSInki Dae 2461c248b7dSInki Dae ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); 2471c248b7dSInki Dae if (ret < 0) { 2481c248b7dSInki Dae DRM_ERROR("failed to set up hw configuration.\n"); 2491c248b7dSInki Dae goto err_setup; 2501c248b7dSInki Dae } 2511c248b7dSInki Dae 2521c248b7dSInki Dae return 0; 2531c248b7dSInki Dae 2541c248b7dSInki Dae err_setup: 2551c248b7dSInki Dae drm_fb_helper_fini(helper); 2561c248b7dSInki Dae 2571c248b7dSInki Dae err_init: 2581c248b7dSInki Dae private->fb_helper = NULL; 2591c248b7dSInki Dae kfree(fbdev); 2601c248b7dSInki Dae 2611c248b7dSInki Dae return ret; 2621c248b7dSInki Dae } 2631c248b7dSInki Dae 2641c248b7dSInki Dae static void exynos_drm_fbdev_destroy(struct drm_device *dev, 2651c248b7dSInki Dae struct drm_fb_helper *fb_helper) 2661c248b7dSInki Dae { 2671c248b7dSInki Dae struct drm_framebuffer *fb; 2681c248b7dSInki Dae 2691c248b7dSInki Dae /* release drm framebuffer and real buffer */ 2701c248b7dSInki Dae if (fb_helper->fb && fb_helper->fb->funcs) { 2711c248b7dSInki Dae fb = fb_helper->fb; 272f7eff60eSRob Clark if (fb) 273f7eff60eSRob Clark drm_framebuffer_remove(fb); 2741c248b7dSInki Dae } 2751c248b7dSInki Dae 2761c248b7dSInki Dae /* release linux framebuffer */ 2771c248b7dSInki Dae if (fb_helper->fbdev) { 2781c248b7dSInki Dae struct fb_info *info; 2791c248b7dSInki Dae int ret; 2801c248b7dSInki Dae 2811c248b7dSInki Dae info = fb_helper->fbdev; 2821c248b7dSInki Dae ret = unregister_framebuffer(info); 2831c248b7dSInki Dae if (ret < 0) 2841c248b7dSInki Dae DRM_DEBUG_KMS("failed unregister_framebuffer()\n"); 2851c248b7dSInki Dae 2861c248b7dSInki Dae if (info->cmap.len) 2871c248b7dSInki Dae fb_dealloc_cmap(&info->cmap); 2881c248b7dSInki Dae 2891c248b7dSInki Dae framebuffer_release(info); 2901c248b7dSInki Dae } 2911c248b7dSInki Dae 2921c248b7dSInki Dae drm_fb_helper_fini(fb_helper); 2931c248b7dSInki Dae } 2941c248b7dSInki Dae 2951c248b7dSInki Dae void exynos_drm_fbdev_fini(struct drm_device *dev) 2961c248b7dSInki Dae { 2971c248b7dSInki Dae struct exynos_drm_private *private = dev->dev_private; 2981c248b7dSInki Dae struct exynos_drm_fbdev *fbdev; 2991c248b7dSInki Dae 3001c248b7dSInki Dae if (!private || !private->fb_helper) 3011c248b7dSInki Dae return; 3021c248b7dSInki Dae 3031c248b7dSInki Dae fbdev = to_exynos_fbdev(private->fb_helper); 3041c248b7dSInki Dae 305e1533c08SJoonyoung Shim if (fbdev->exynos_gem_obj) 306e1533c08SJoonyoung Shim exynos_drm_gem_destroy(fbdev->exynos_gem_obj); 307e1533c08SJoonyoung Shim 3081c248b7dSInki Dae exynos_drm_fbdev_destroy(dev, private->fb_helper); 3091c248b7dSInki Dae kfree(fbdev); 3101c248b7dSInki Dae private->fb_helper = NULL; 3111c248b7dSInki Dae } 3121c248b7dSInki Dae 3131c248b7dSInki Dae void exynos_drm_fbdev_restore_mode(struct drm_device *dev) 3141c248b7dSInki Dae { 3151c248b7dSInki Dae struct exynos_drm_private *private = dev->dev_private; 3161c248b7dSInki Dae 3171c248b7dSInki Dae if (!private || !private->fb_helper) 3181c248b7dSInki Dae return; 3191c248b7dSInki Dae 3201c248b7dSInki Dae drm_fb_helper_restore_fbdev_mode(private->fb_helper); 3211c248b7dSInki Dae } 322