18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2012-2013 Avionic Design GmbH 48c2ecf20Sopenharmony_ci * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on the KMS/FB CMA helpers 78c2ecf20Sopenharmony_ci * Copyright (C) 2012 Analog Devices Inc. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/console.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 138c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_modeset_helper.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "drm.h" 178c2ecf20Sopenharmony_ci#include "gem.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#ifdef CONFIG_DRM_FBDEV_EMULATION 208c2ecf20Sopenharmony_cistatic inline struct tegra_fbdev *to_tegra_fbdev(struct drm_fb_helper *helper) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci return container_of(helper, struct tegra_fbdev, base); 238c2ecf20Sopenharmony_ci} 248c2ecf20Sopenharmony_ci#endif 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, 278c2ecf20Sopenharmony_ci unsigned int index) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci return to_tegra_bo(drm_gem_fb_get_obj(framebuffer, index)); 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cibool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct tegra_bo *bo = tegra_fb_get_plane(framebuffer, 0); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (bo->flags & TEGRA_BO_BOTTOM_UP) 378c2ecf20Sopenharmony_ci return true; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return false; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ciint tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, 438c2ecf20Sopenharmony_ci struct tegra_bo_tiling *tiling) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci uint64_t modifier = framebuffer->modifier; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci switch (modifier) { 488c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_LINEAR: 498c2ecf20Sopenharmony_ci tiling->mode = TEGRA_BO_TILING_MODE_PITCH; 508c2ecf20Sopenharmony_ci tiling->value = 0; 518c2ecf20Sopenharmony_ci break; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED: 548c2ecf20Sopenharmony_ci tiling->mode = TEGRA_BO_TILING_MODE_TILED; 558c2ecf20Sopenharmony_ci tiling->value = 0; 568c2ecf20Sopenharmony_ci break; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0): 598c2ecf20Sopenharmony_ci tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 608c2ecf20Sopenharmony_ci tiling->value = 0; 618c2ecf20Sopenharmony_ci break; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1): 648c2ecf20Sopenharmony_ci tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 658c2ecf20Sopenharmony_ci tiling->value = 1; 668c2ecf20Sopenharmony_ci break; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2): 698c2ecf20Sopenharmony_ci tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 708c2ecf20Sopenharmony_ci tiling->value = 2; 718c2ecf20Sopenharmony_ci break; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3): 748c2ecf20Sopenharmony_ci tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 758c2ecf20Sopenharmony_ci tiling->value = 3; 768c2ecf20Sopenharmony_ci break; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4): 798c2ecf20Sopenharmony_ci tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 808c2ecf20Sopenharmony_ci tiling->value = 4; 818c2ecf20Sopenharmony_ci break; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5): 848c2ecf20Sopenharmony_ci tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 858c2ecf20Sopenharmony_ci tiling->value = 5; 868c2ecf20Sopenharmony_ci break; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci default: 898c2ecf20Sopenharmony_ci return -EINVAL; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic const struct drm_framebuffer_funcs tegra_fb_funcs = { 968c2ecf20Sopenharmony_ci .destroy = drm_gem_fb_destroy, 978c2ecf20Sopenharmony_ci .create_handle = drm_gem_fb_create_handle, 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic struct drm_framebuffer *tegra_fb_alloc(struct drm_device *drm, 1018c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd, 1028c2ecf20Sopenharmony_ci struct tegra_bo **planes, 1038c2ecf20Sopenharmony_ci unsigned int num_planes) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 1068c2ecf20Sopenharmony_ci unsigned int i; 1078c2ecf20Sopenharmony_ci int err; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci fb = kzalloc(sizeof(*fb), GFP_KERNEL); 1108c2ecf20Sopenharmony_ci if (!fb) 1118c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci drm_helper_mode_fill_fb_struct(drm, fb, mode_cmd); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci for (i = 0; i < fb->format->num_planes; i++) 1168c2ecf20Sopenharmony_ci fb->obj[i] = &planes[i]->gem; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci err = drm_framebuffer_init(drm, fb, &tegra_fb_funcs); 1198c2ecf20Sopenharmony_ci if (err < 0) { 1208c2ecf20Sopenharmony_ci dev_err(drm->dev, "failed to initialize framebuffer: %d\n", 1218c2ecf20Sopenharmony_ci err); 1228c2ecf20Sopenharmony_ci kfree(fb); 1238c2ecf20Sopenharmony_ci return ERR_PTR(err); 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return fb; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistruct drm_framebuffer *tegra_fb_create(struct drm_device *drm, 1308c2ecf20Sopenharmony_ci struct drm_file *file, 1318c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 *cmd) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci const struct drm_format_info *info = drm_get_format_info(drm, cmd); 1348c2ecf20Sopenharmony_ci struct tegra_bo *planes[4]; 1358c2ecf20Sopenharmony_ci struct drm_gem_object *gem; 1368c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 1378c2ecf20Sopenharmony_ci unsigned int i; 1388c2ecf20Sopenharmony_ci int err; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci for (i = 0; i < info->num_planes; i++) { 1418c2ecf20Sopenharmony_ci unsigned int width = cmd->width / (i ? info->hsub : 1); 1428c2ecf20Sopenharmony_ci unsigned int height = cmd->height / (i ? info->vsub : 1); 1438c2ecf20Sopenharmony_ci unsigned int size, bpp; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci gem = drm_gem_object_lookup(file, cmd->handles[i]); 1468c2ecf20Sopenharmony_ci if (!gem) { 1478c2ecf20Sopenharmony_ci err = -ENXIO; 1488c2ecf20Sopenharmony_ci goto unreference; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci bpp = info->cpp[i]; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci size = (height - 1) * cmd->pitches[i] + 1548c2ecf20Sopenharmony_ci width * bpp + cmd->offsets[i]; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (gem->size < size) { 1578c2ecf20Sopenharmony_ci err = -EINVAL; 1588c2ecf20Sopenharmony_ci goto unreference; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci planes[i] = to_tegra_bo(gem); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci fb = tegra_fb_alloc(drm, cmd, planes, i); 1658c2ecf20Sopenharmony_ci if (IS_ERR(fb)) { 1668c2ecf20Sopenharmony_ci err = PTR_ERR(fb); 1678c2ecf20Sopenharmony_ci goto unreference; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return fb; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciunreference: 1738c2ecf20Sopenharmony_ci while (i--) 1748c2ecf20Sopenharmony_ci drm_gem_object_put(&planes[i]->gem); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return ERR_PTR(err); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci#ifdef CONFIG_DRM_FBDEV_EMULATION 1808c2ecf20Sopenharmony_cistatic int tegra_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct drm_fb_helper *helper = info->par; 1838c2ecf20Sopenharmony_ci struct tegra_bo *bo; 1848c2ecf20Sopenharmony_ci int err; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci bo = tegra_fb_get_plane(helper->fb, 0); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci err = drm_gem_mmap_obj(&bo->gem, bo->gem.size, vma); 1898c2ecf20Sopenharmony_ci if (err < 0) 1908c2ecf20Sopenharmony_ci return err; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return __tegra_gem_mmap(&bo->gem, vma); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic const struct fb_ops tegra_fb_ops = { 1968c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1978c2ecf20Sopenharmony_ci DRM_FB_HELPER_DEFAULT_OPS, 1988c2ecf20Sopenharmony_ci .fb_fillrect = drm_fb_helper_sys_fillrect, 1998c2ecf20Sopenharmony_ci .fb_copyarea = drm_fb_helper_sys_copyarea, 2008c2ecf20Sopenharmony_ci .fb_imageblit = drm_fb_helper_sys_imageblit, 2018c2ecf20Sopenharmony_ci .fb_mmap = tegra_fb_mmap, 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int tegra_fbdev_probe(struct drm_fb_helper *helper, 2058c2ecf20Sopenharmony_ci struct drm_fb_helper_surface_size *sizes) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct tegra_fbdev *fbdev = to_tegra_fbdev(helper); 2088c2ecf20Sopenharmony_ci struct tegra_drm *tegra = helper->dev->dev_private; 2098c2ecf20Sopenharmony_ci struct drm_device *drm = helper->dev; 2108c2ecf20Sopenharmony_ci struct drm_mode_fb_cmd2 cmd = { 0 }; 2118c2ecf20Sopenharmony_ci unsigned int bytes_per_pixel; 2128c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 2138c2ecf20Sopenharmony_ci unsigned long offset; 2148c2ecf20Sopenharmony_ci struct fb_info *info; 2158c2ecf20Sopenharmony_ci struct tegra_bo *bo; 2168c2ecf20Sopenharmony_ci size_t size; 2178c2ecf20Sopenharmony_ci int err; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci cmd.width = sizes->surface_width; 2228c2ecf20Sopenharmony_ci cmd.height = sizes->surface_height; 2238c2ecf20Sopenharmony_ci cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel, 2248c2ecf20Sopenharmony_ci tegra->pitch_align); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 2278c2ecf20Sopenharmony_ci sizes->surface_depth); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci size = cmd.pitches[0] * cmd.height; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci bo = tegra_bo_create(drm, size, 0); 2328c2ecf20Sopenharmony_ci if (IS_ERR(bo)) 2338c2ecf20Sopenharmony_ci return PTR_ERR(bo); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci info = drm_fb_helper_alloc_fbi(helper); 2368c2ecf20Sopenharmony_ci if (IS_ERR(info)) { 2378c2ecf20Sopenharmony_ci dev_err(drm->dev, "failed to allocate framebuffer info\n"); 2388c2ecf20Sopenharmony_ci drm_gem_object_put(&bo->gem); 2398c2ecf20Sopenharmony_ci return PTR_ERR(info); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci fbdev->fb = tegra_fb_alloc(drm, &cmd, &bo, 1); 2438c2ecf20Sopenharmony_ci if (IS_ERR(fbdev->fb)) { 2448c2ecf20Sopenharmony_ci err = PTR_ERR(fbdev->fb); 2458c2ecf20Sopenharmony_ci dev_err(drm->dev, "failed to allocate DRM framebuffer: %d\n", 2468c2ecf20Sopenharmony_ci err); 2478c2ecf20Sopenharmony_ci drm_gem_object_put(&bo->gem); 2488c2ecf20Sopenharmony_ci return PTR_ERR(fbdev->fb); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci fb = fbdev->fb; 2528c2ecf20Sopenharmony_ci helper->fb = fb; 2538c2ecf20Sopenharmony_ci helper->fbdev = info; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci info->fbops = &tegra_fb_ops; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci drm_fb_helper_fill_info(info, helper, sizes); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci offset = info->var.xoffset * bytes_per_pixel + 2608c2ecf20Sopenharmony_ci info->var.yoffset * fb->pitches[0]; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (bo->pages) { 2638c2ecf20Sopenharmony_ci bo->vaddr = vmap(bo->pages, bo->num_pages, VM_MAP, 2648c2ecf20Sopenharmony_ci pgprot_writecombine(PAGE_KERNEL)); 2658c2ecf20Sopenharmony_ci if (!bo->vaddr) { 2668c2ecf20Sopenharmony_ci dev_err(drm->dev, "failed to vmap() framebuffer\n"); 2678c2ecf20Sopenharmony_ci err = -ENOMEM; 2688c2ecf20Sopenharmony_ci goto destroy; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci drm->mode_config.fb_base = (resource_size_t)bo->iova; 2738c2ecf20Sopenharmony_ci info->screen_base = (void __iomem *)bo->vaddr + offset; 2748c2ecf20Sopenharmony_ci info->screen_size = size; 2758c2ecf20Sopenharmony_ci info->fix.smem_start = (unsigned long)(bo->iova + offset); 2768c2ecf20Sopenharmony_ci info->fix.smem_len = size; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return 0; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cidestroy: 2818c2ecf20Sopenharmony_ci drm_framebuffer_remove(fb); 2828c2ecf20Sopenharmony_ci return err; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic const struct drm_fb_helper_funcs tegra_fb_helper_funcs = { 2868c2ecf20Sopenharmony_ci .fb_probe = tegra_fbdev_probe, 2878c2ecf20Sopenharmony_ci}; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct tegra_fbdev *fbdev; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); 2948c2ecf20Sopenharmony_ci if (!fbdev) { 2958c2ecf20Sopenharmony_ci dev_err(drm->dev, "failed to allocate DRM fbdev\n"); 2968c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return fbdev; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic void tegra_fbdev_free(struct tegra_fbdev *fbdev) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci kfree(fbdev); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int tegra_fbdev_init(struct tegra_fbdev *fbdev, 3108c2ecf20Sopenharmony_ci unsigned int preferred_bpp, 3118c2ecf20Sopenharmony_ci unsigned int num_crtc, 3128c2ecf20Sopenharmony_ci unsigned int max_connectors) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct drm_device *drm = fbdev->base.dev; 3158c2ecf20Sopenharmony_ci int err; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci err = drm_fb_helper_init(drm, &fbdev->base); 3188c2ecf20Sopenharmony_ci if (err < 0) { 3198c2ecf20Sopenharmony_ci dev_err(drm->dev, "failed to initialize DRM FB helper: %d\n", 3208c2ecf20Sopenharmony_ci err); 3218c2ecf20Sopenharmony_ci return err; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp); 3258c2ecf20Sopenharmony_ci if (err < 0) { 3268c2ecf20Sopenharmony_ci dev_err(drm->dev, "failed to set initial configuration: %d\n", 3278c2ecf20Sopenharmony_ci err); 3288c2ecf20Sopenharmony_ci goto fini; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci return 0; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cifini: 3348c2ecf20Sopenharmony_ci drm_fb_helper_fini(&fbdev->base); 3358c2ecf20Sopenharmony_ci return err; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void tegra_fbdev_exit(struct tegra_fbdev *fbdev) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci drm_fb_helper_unregister_fbi(&fbdev->base); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (fbdev->fb) { 3438c2ecf20Sopenharmony_ci struct tegra_bo *bo = tegra_fb_get_plane(fbdev->fb, 0); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* Undo the special mapping we made in fbdev probe. */ 3468c2ecf20Sopenharmony_ci if (bo && bo->pages) { 3478c2ecf20Sopenharmony_ci vunmap(bo->vaddr); 3488c2ecf20Sopenharmony_ci bo->vaddr = NULL; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci drm_framebuffer_remove(fbdev->fb); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci drm_fb_helper_fini(&fbdev->base); 3558c2ecf20Sopenharmony_ci tegra_fbdev_free(fbdev); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci#endif 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ciint tegra_drm_fb_prepare(struct drm_device *drm) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci#ifdef CONFIG_DRM_FBDEV_EMULATION 3628c2ecf20Sopenharmony_ci struct tegra_drm *tegra = drm->dev_private; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci tegra->fbdev = tegra_fbdev_create(drm); 3658c2ecf20Sopenharmony_ci if (IS_ERR(tegra->fbdev)) 3668c2ecf20Sopenharmony_ci return PTR_ERR(tegra->fbdev); 3678c2ecf20Sopenharmony_ci#endif 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_civoid tegra_drm_fb_free(struct drm_device *drm) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci#ifdef CONFIG_DRM_FBDEV_EMULATION 3758c2ecf20Sopenharmony_ci struct tegra_drm *tegra = drm->dev_private; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci tegra_fbdev_free(tegra->fbdev); 3788c2ecf20Sopenharmony_ci#endif 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ciint tegra_drm_fb_init(struct drm_device *drm) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci#ifdef CONFIG_DRM_FBDEV_EMULATION 3848c2ecf20Sopenharmony_ci struct tegra_drm *tegra = drm->dev_private; 3858c2ecf20Sopenharmony_ci int err; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci err = tegra_fbdev_init(tegra->fbdev, 32, drm->mode_config.num_crtc, 3888c2ecf20Sopenharmony_ci drm->mode_config.num_connector); 3898c2ecf20Sopenharmony_ci if (err < 0) 3908c2ecf20Sopenharmony_ci return err; 3918c2ecf20Sopenharmony_ci#endif 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_civoid tegra_drm_fb_exit(struct drm_device *drm) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci#ifdef CONFIG_DRM_FBDEV_EMULATION 3998c2ecf20Sopenharmony_ci struct tegra_drm *tegra = drm->dev_private; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci tegra_fbdev_exit(tegra->fbdev); 4028c2ecf20Sopenharmony_ci#endif 4038c2ecf20Sopenharmony_ci} 404