18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright © 2007 David Airlie
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next
128c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
138c2ecf20Sopenharmony_ci * Software.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
188c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
198c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
208c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
218c2ecf20Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * Authors:
248c2ecf20Sopenharmony_ci *     David Airlie
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/module.h>
288c2ecf20Sopenharmony_ci#include <linux/pci.h>
298c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
308c2ecf20Sopenharmony_ci#include <linux/slab.h>
318c2ecf20Sopenharmony_ci#include <linux/vga_switcheroo.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h>
348c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h>
358c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h>
368c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
378c2ecf20Sopenharmony_ci#include <drm/radeon_drm.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#include "radeon.h"
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* object hierarchy -
428c2ecf20Sopenharmony_ci * this contains a helper + a radeon fb
438c2ecf20Sopenharmony_ci * the helper contains a pointer to radeon framebuffer baseclass.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_cistruct radeon_fbdev {
468c2ecf20Sopenharmony_ci	struct drm_fb_helper helper; /* must be first */
478c2ecf20Sopenharmony_ci	struct drm_framebuffer fb;
488c2ecf20Sopenharmony_ci	struct radeon_device *rdev;
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic int
528c2ecf20Sopenharmony_ciradeonfb_open(struct fb_info *info, int user)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	struct radeon_fbdev *rfbdev = info->par;
558c2ecf20Sopenharmony_ci	struct radeon_device *rdev = rfbdev->rdev;
568c2ecf20Sopenharmony_ci	int ret = pm_runtime_get_sync(rdev->ddev->dev);
578c2ecf20Sopenharmony_ci	if (ret < 0 && ret != -EACCES) {
588c2ecf20Sopenharmony_ci		pm_runtime_mark_last_busy(rdev->ddev->dev);
598c2ecf20Sopenharmony_ci		pm_runtime_put_autosuspend(rdev->ddev->dev);
608c2ecf20Sopenharmony_ci		return ret;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci	return 0;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int
668c2ecf20Sopenharmony_ciradeonfb_release(struct fb_info *info, int user)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct radeon_fbdev *rfbdev = info->par;
698c2ecf20Sopenharmony_ci	struct radeon_device *rdev = rfbdev->rdev;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(rdev->ddev->dev);
728c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(rdev->ddev->dev);
738c2ecf20Sopenharmony_ci	return 0;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic const struct fb_ops radeonfb_ops = {
778c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
788c2ecf20Sopenharmony_ci	DRM_FB_HELPER_DEFAULT_OPS,
798c2ecf20Sopenharmony_ci	.fb_open = radeonfb_open,
808c2ecf20Sopenharmony_ci	.fb_release = radeonfb_release,
818c2ecf20Sopenharmony_ci	.fb_fillrect = drm_fb_helper_cfb_fillrect,
828c2ecf20Sopenharmony_ci	.fb_copyarea = drm_fb_helper_cfb_copyarea,
838c2ecf20Sopenharmony_ci	.fb_imageblit = drm_fb_helper_cfb_imageblit,
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ciint radeon_align_pitch(struct radeon_device *rdev, int width, int cpp, bool tiled)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	int aligned = width;
908c2ecf20Sopenharmony_ci	int align_large = (ASIC_IS_AVIVO(rdev)) || tiled;
918c2ecf20Sopenharmony_ci	int pitch_mask = 0;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	switch (cpp) {
948c2ecf20Sopenharmony_ci	case 1:
958c2ecf20Sopenharmony_ci		pitch_mask = align_large ? 255 : 127;
968c2ecf20Sopenharmony_ci		break;
978c2ecf20Sopenharmony_ci	case 2:
988c2ecf20Sopenharmony_ci		pitch_mask = align_large ? 127 : 31;
998c2ecf20Sopenharmony_ci		break;
1008c2ecf20Sopenharmony_ci	case 3:
1018c2ecf20Sopenharmony_ci	case 4:
1028c2ecf20Sopenharmony_ci		pitch_mask = align_large ? 63 : 15;
1038c2ecf20Sopenharmony_ci		break;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	aligned += pitch_mask;
1078c2ecf20Sopenharmony_ci	aligned &= ~pitch_mask;
1088c2ecf20Sopenharmony_ci	return aligned * cpp;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic void radeonfb_destroy_pinned_object(struct drm_gem_object *gobj)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct radeon_bo *rbo = gem_to_radeon_bo(gobj);
1148c2ecf20Sopenharmony_ci	int ret;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	ret = radeon_bo_reserve(rbo, false);
1178c2ecf20Sopenharmony_ci	if (likely(ret == 0)) {
1188c2ecf20Sopenharmony_ci		radeon_bo_kunmap(rbo);
1198c2ecf20Sopenharmony_ci		radeon_bo_unpin(rbo);
1208c2ecf20Sopenharmony_ci		radeon_bo_unreserve(rbo);
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci	drm_gem_object_put(gobj);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev,
1268c2ecf20Sopenharmony_ci					 struct drm_mode_fb_cmd2 *mode_cmd,
1278c2ecf20Sopenharmony_ci					 struct drm_gem_object **gobj_p)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	const struct drm_format_info *info;
1308c2ecf20Sopenharmony_ci	struct radeon_device *rdev = rfbdev->rdev;
1318c2ecf20Sopenharmony_ci	struct drm_gem_object *gobj = NULL;
1328c2ecf20Sopenharmony_ci	struct radeon_bo *rbo = NULL;
1338c2ecf20Sopenharmony_ci	bool fb_tiled = false; /* useful for testing */
1348c2ecf20Sopenharmony_ci	u32 tiling_flags = 0;
1358c2ecf20Sopenharmony_ci	int ret;
1368c2ecf20Sopenharmony_ci	int aligned_size, size;
1378c2ecf20Sopenharmony_ci	int height = mode_cmd->height;
1388c2ecf20Sopenharmony_ci	u32 cpp;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	info = drm_get_format_info(rdev->ddev, mode_cmd);
1418c2ecf20Sopenharmony_ci	cpp = info->cpp[0];
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* need to align pitch with crtc limits */
1448c2ecf20Sopenharmony_ci	mode_cmd->pitches[0] = radeon_align_pitch(rdev, mode_cmd->width, cpp,
1458c2ecf20Sopenharmony_ci						  fb_tiled);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (rdev->family >= CHIP_R600)
1488c2ecf20Sopenharmony_ci		height = ALIGN(mode_cmd->height, 8);
1498c2ecf20Sopenharmony_ci	size = mode_cmd->pitches[0] * height;
1508c2ecf20Sopenharmony_ci	aligned_size = ALIGN(size, PAGE_SIZE);
1518c2ecf20Sopenharmony_ci	ret = radeon_gem_object_create(rdev, aligned_size, 0,
1528c2ecf20Sopenharmony_ci				       RADEON_GEM_DOMAIN_VRAM,
1538c2ecf20Sopenharmony_ci				       0, true, &gobj);
1548c2ecf20Sopenharmony_ci	if (ret) {
1558c2ecf20Sopenharmony_ci		pr_err("failed to allocate framebuffer (%d)\n", aligned_size);
1568c2ecf20Sopenharmony_ci		return -ENOMEM;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci	rbo = gem_to_radeon_bo(gobj);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (fb_tiled)
1618c2ecf20Sopenharmony_ci		tiling_flags = RADEON_TILING_MACRO;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
1648c2ecf20Sopenharmony_ci	switch (cpp) {
1658c2ecf20Sopenharmony_ci	case 4:
1668c2ecf20Sopenharmony_ci		tiling_flags |= RADEON_TILING_SWAP_32BIT;
1678c2ecf20Sopenharmony_ci		break;
1688c2ecf20Sopenharmony_ci	case 2:
1698c2ecf20Sopenharmony_ci		tiling_flags |= RADEON_TILING_SWAP_16BIT;
1708c2ecf20Sopenharmony_ci	default:
1718c2ecf20Sopenharmony_ci		break;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci#endif
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (tiling_flags) {
1768c2ecf20Sopenharmony_ci		ret = radeon_bo_set_tiling_flags(rbo,
1778c2ecf20Sopenharmony_ci						 tiling_flags | RADEON_TILING_SURFACE,
1788c2ecf20Sopenharmony_ci						 mode_cmd->pitches[0]);
1798c2ecf20Sopenharmony_ci		if (ret)
1808c2ecf20Sopenharmony_ci			dev_err(rdev->dev, "FB failed to set tiling flags\n");
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	ret = radeon_bo_reserve(rbo, false);
1858c2ecf20Sopenharmony_ci	if (unlikely(ret != 0))
1868c2ecf20Sopenharmony_ci		goto out_unref;
1878c2ecf20Sopenharmony_ci	/* Only 27 bit offset for legacy CRTC */
1888c2ecf20Sopenharmony_ci	ret = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM,
1898c2ecf20Sopenharmony_ci				       ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27,
1908c2ecf20Sopenharmony_ci				       NULL);
1918c2ecf20Sopenharmony_ci	if (ret) {
1928c2ecf20Sopenharmony_ci		radeon_bo_unreserve(rbo);
1938c2ecf20Sopenharmony_ci		goto out_unref;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci	if (fb_tiled)
1968c2ecf20Sopenharmony_ci		radeon_bo_check_tiling(rbo, 0, 0);
1978c2ecf20Sopenharmony_ci	ret = radeon_bo_kmap(rbo, NULL);
1988c2ecf20Sopenharmony_ci	radeon_bo_unreserve(rbo);
1998c2ecf20Sopenharmony_ci	if (ret) {
2008c2ecf20Sopenharmony_ci		goto out_unref;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	*gobj_p = gobj;
2048c2ecf20Sopenharmony_ci	return 0;
2058c2ecf20Sopenharmony_ciout_unref:
2068c2ecf20Sopenharmony_ci	radeonfb_destroy_pinned_object(gobj);
2078c2ecf20Sopenharmony_ci	*gobj_p = NULL;
2088c2ecf20Sopenharmony_ci	return ret;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic int radeonfb_create(struct drm_fb_helper *helper,
2128c2ecf20Sopenharmony_ci			   struct drm_fb_helper_surface_size *sizes)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct radeon_fbdev *rfbdev =
2158c2ecf20Sopenharmony_ci		container_of(helper, struct radeon_fbdev, helper);
2168c2ecf20Sopenharmony_ci	struct radeon_device *rdev = rfbdev->rdev;
2178c2ecf20Sopenharmony_ci	struct fb_info *info;
2188c2ecf20Sopenharmony_ci	struct drm_framebuffer *fb = NULL;
2198c2ecf20Sopenharmony_ci	struct drm_mode_fb_cmd2 mode_cmd;
2208c2ecf20Sopenharmony_ci	struct drm_gem_object *gobj = NULL;
2218c2ecf20Sopenharmony_ci	struct radeon_bo *rbo = NULL;
2228c2ecf20Sopenharmony_ci	int ret;
2238c2ecf20Sopenharmony_ci	unsigned long tmp;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	mode_cmd.width = sizes->surface_width;
2268c2ecf20Sopenharmony_ci	mode_cmd.height = sizes->surface_height;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/* avivo can't scanout real 24bpp */
2298c2ecf20Sopenharmony_ci	if ((sizes->surface_bpp == 24) && ASIC_IS_AVIVO(rdev))
2308c2ecf20Sopenharmony_ci		sizes->surface_bpp = 32;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
2338c2ecf20Sopenharmony_ci							  sizes->surface_depth);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	ret = radeonfb_create_pinned_object(rfbdev, &mode_cmd, &gobj);
2368c2ecf20Sopenharmony_ci	if (ret) {
2378c2ecf20Sopenharmony_ci		DRM_ERROR("failed to create fbcon object %d\n", ret);
2388c2ecf20Sopenharmony_ci		return ret;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	rbo = gem_to_radeon_bo(gobj);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	/* okay we have an object now allocate the framebuffer */
2448c2ecf20Sopenharmony_ci	info = drm_fb_helper_alloc_fbi(helper);
2458c2ecf20Sopenharmony_ci	if (IS_ERR(info)) {
2468c2ecf20Sopenharmony_ci		ret = PTR_ERR(info);
2478c2ecf20Sopenharmony_ci		goto out;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* radeon resume is fragile and needs a vt switch to help it along */
2518c2ecf20Sopenharmony_ci	info->skip_vt_switch = false;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	ret = radeon_framebuffer_init(rdev->ddev, &rfbdev->fb, &mode_cmd, gobj);
2548c2ecf20Sopenharmony_ci	if (ret) {
2558c2ecf20Sopenharmony_ci		DRM_ERROR("failed to initialize framebuffer %d\n", ret);
2568c2ecf20Sopenharmony_ci		goto out;
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	fb = &rfbdev->fb;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	/* setup helper */
2628c2ecf20Sopenharmony_ci	rfbdev->helper.fb = fb;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	memset_io(rbo->kptr, 0x0, radeon_bo_size(rbo));
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	info->fbops = &radeonfb_ops;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	tmp = radeon_bo_gpu_offset(rbo) - rdev->mc.vram_start;
2698c2ecf20Sopenharmony_ci	info->fix.smem_start = rdev->mc.aper_base + tmp;
2708c2ecf20Sopenharmony_ci	info->fix.smem_len = radeon_bo_size(rbo);
2718c2ecf20Sopenharmony_ci	info->screen_base = rbo->kptr;
2728c2ecf20Sopenharmony_ci	info->screen_size = radeon_bo_size(rbo);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	drm_fb_helper_fill_info(info, &rfbdev->helper, sizes);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* setup aperture base/size for vesafb takeover */
2778c2ecf20Sopenharmony_ci	info->apertures->ranges[0].base = rdev->ddev->mode_config.fb_base;
2788c2ecf20Sopenharmony_ci	info->apertures->ranges[0].size = rdev->mc.aper_size;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (info->screen_base == NULL) {
2838c2ecf20Sopenharmony_ci		ret = -ENOSPC;
2848c2ecf20Sopenharmony_ci		goto out;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	DRM_INFO("fb mappable at 0x%lX\n",  info->fix.smem_start);
2888c2ecf20Sopenharmony_ci	DRM_INFO("vram apper at 0x%lX\n",  (unsigned long)rdev->mc.aper_base);
2898c2ecf20Sopenharmony_ci	DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo));
2908c2ecf20Sopenharmony_ci	DRM_INFO("fb depth is %d\n", fb->format->depth);
2918c2ecf20Sopenharmony_ci	DRM_INFO("   pitch is %d\n", fb->pitches[0]);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	vga_switcheroo_client_fb_set(rdev->ddev->pdev, info);
2948c2ecf20Sopenharmony_ci	return 0;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ciout:
2978c2ecf20Sopenharmony_ci	if (rbo) {
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci	if (fb && ret) {
3018c2ecf20Sopenharmony_ci		drm_gem_object_put(gobj);
3028c2ecf20Sopenharmony_ci		drm_framebuffer_unregister_private(fb);
3038c2ecf20Sopenharmony_ci		drm_framebuffer_cleanup(fb);
3048c2ecf20Sopenharmony_ci		kfree(fb);
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci	return ret;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	struct drm_framebuffer *fb = &rfbdev->fb;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	drm_fb_helper_unregister_fbi(&rfbdev->helper);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (fb->obj[0]) {
3168c2ecf20Sopenharmony_ci		radeonfb_destroy_pinned_object(fb->obj[0]);
3178c2ecf20Sopenharmony_ci		fb->obj[0] = NULL;
3188c2ecf20Sopenharmony_ci		drm_framebuffer_unregister_private(fb);
3198c2ecf20Sopenharmony_ci		drm_framebuffer_cleanup(fb);
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci	drm_fb_helper_fini(&rfbdev->helper);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	return 0;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic const struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
3278c2ecf20Sopenharmony_ci	.fb_probe = radeonfb_create,
3288c2ecf20Sopenharmony_ci};
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ciint radeon_fbdev_init(struct radeon_device *rdev)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct radeon_fbdev *rfbdev;
3338c2ecf20Sopenharmony_ci	int bpp_sel = 32;
3348c2ecf20Sopenharmony_ci	int ret;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* don't enable fbdev if no connectors */
3378c2ecf20Sopenharmony_ci	if (list_empty(&rdev->ddev->mode_config.connector_list))
3388c2ecf20Sopenharmony_ci		return 0;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* select 8 bpp console on 8MB cards, or 16 bpp on RN50 or 32MB */
3418c2ecf20Sopenharmony_ci	if (rdev->mc.real_vram_size <= (8*1024*1024))
3428c2ecf20Sopenharmony_ci		bpp_sel = 8;
3438c2ecf20Sopenharmony_ci	else if (ASIC_IS_RN50(rdev) ||
3448c2ecf20Sopenharmony_ci		 rdev->mc.real_vram_size <= (32*1024*1024))
3458c2ecf20Sopenharmony_ci		bpp_sel = 16;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	rfbdev = kzalloc(sizeof(struct radeon_fbdev), GFP_KERNEL);
3488c2ecf20Sopenharmony_ci	if (!rfbdev)
3498c2ecf20Sopenharmony_ci		return -ENOMEM;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	rfbdev->rdev = rdev;
3528c2ecf20Sopenharmony_ci	rdev->mode_info.rfbdev = rfbdev;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper,
3558c2ecf20Sopenharmony_ci			      &radeon_fb_helper_funcs);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper);
3588c2ecf20Sopenharmony_ci	if (ret)
3598c2ecf20Sopenharmony_ci		goto free;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/* disable all the possible outputs/crtcs before entering KMS mode */
3628c2ecf20Sopenharmony_ci	drm_helper_disable_unused_functions(rdev->ddev);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	ret = drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel);
3658c2ecf20Sopenharmony_ci	if (ret)
3668c2ecf20Sopenharmony_ci		goto fini;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	return 0;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cifini:
3718c2ecf20Sopenharmony_ci	drm_fb_helper_fini(&rfbdev->helper);
3728c2ecf20Sopenharmony_cifree:
3738c2ecf20Sopenharmony_ci	kfree(rfbdev);
3748c2ecf20Sopenharmony_ci	return ret;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_civoid radeon_fbdev_fini(struct radeon_device *rdev)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	if (!rdev->mode_info.rfbdev)
3808c2ecf20Sopenharmony_ci		return;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev);
3838c2ecf20Sopenharmony_ci	kfree(rdev->mode_info.rfbdev);
3848c2ecf20Sopenharmony_ci	rdev->mode_info.rfbdev = NULL;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_civoid radeon_fbdev_set_suspend(struct radeon_device *rdev, int state)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	if (rdev->mode_info.rfbdev)
3908c2ecf20Sopenharmony_ci		drm_fb_helper_set_suspend(&rdev->mode_info.rfbdev->helper, state);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cibool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	if (!rdev->mode_info.rfbdev)
3968c2ecf20Sopenharmony_ci		return false;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if (robj == gem_to_radeon_bo(rdev->mode_info.rfbdev->fb.obj[0]))
3998c2ecf20Sopenharmony_ci		return true;
4008c2ecf20Sopenharmony_ci	return false;
4018c2ecf20Sopenharmony_ci}
402