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