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