162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright © 2007 David Airlie 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next 1262306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 1362306a36Sopenharmony_ci * Software. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1862306a36Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1962306a36Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2062306a36Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 2162306a36Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Authors: 2462306a36Sopenharmony_ci * David Airlie 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <linux/fb.h> 2862306a36Sopenharmony_ci#include <linux/pci.h> 2962306a36Sopenharmony_ci#include <linux/pm_runtime.h> 3062306a36Sopenharmony_ci#include <linux/vga_switcheroo.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <drm/drm_crtc_helper.h> 3362306a36Sopenharmony_ci#include <drm/drm_drv.h> 3462306a36Sopenharmony_ci#include <drm/drm_fb_helper.h> 3562306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 3662306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 3762306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include "radeon.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void radeon_fbdev_destroy_pinned_object(struct drm_gem_object *gobj) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct radeon_bo *rbo = gem_to_radeon_bo(gobj); 4462306a36Sopenharmony_ci int ret; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci ret = radeon_bo_reserve(rbo, false); 4762306a36Sopenharmony_ci if (likely(ret == 0)) { 4862306a36Sopenharmony_ci radeon_bo_kunmap(rbo); 4962306a36Sopenharmony_ci radeon_bo_unpin(rbo); 5062306a36Sopenharmony_ci radeon_bo_unreserve(rbo); 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci drm_gem_object_put(gobj); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int radeon_fbdev_create_pinned_object(struct drm_fb_helper *fb_helper, 5662306a36Sopenharmony_ci struct drm_mode_fb_cmd2 *mode_cmd, 5762306a36Sopenharmony_ci struct drm_gem_object **gobj_p) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci const struct drm_format_info *info; 6062306a36Sopenharmony_ci struct radeon_device *rdev = fb_helper->dev->dev_private; 6162306a36Sopenharmony_ci struct drm_gem_object *gobj = NULL; 6262306a36Sopenharmony_ci struct radeon_bo *rbo = NULL; 6362306a36Sopenharmony_ci bool fb_tiled = false; /* useful for testing */ 6462306a36Sopenharmony_ci u32 tiling_flags = 0; 6562306a36Sopenharmony_ci int ret; 6662306a36Sopenharmony_ci int aligned_size, size; 6762306a36Sopenharmony_ci int height = mode_cmd->height; 6862306a36Sopenharmony_ci u32 cpp; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci info = drm_get_format_info(rdev->ddev, mode_cmd); 7162306a36Sopenharmony_ci cpp = info->cpp[0]; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* need to align pitch with crtc limits */ 7462306a36Sopenharmony_ci mode_cmd->pitches[0] = radeon_align_pitch(rdev, mode_cmd->width, cpp, 7562306a36Sopenharmony_ci fb_tiled); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (rdev->family >= CHIP_R600) 7862306a36Sopenharmony_ci height = ALIGN(mode_cmd->height, 8); 7962306a36Sopenharmony_ci size = mode_cmd->pitches[0] * height; 8062306a36Sopenharmony_ci aligned_size = ALIGN(size, PAGE_SIZE); 8162306a36Sopenharmony_ci ret = radeon_gem_object_create(rdev, aligned_size, 0, 8262306a36Sopenharmony_ci RADEON_GEM_DOMAIN_VRAM, 8362306a36Sopenharmony_ci 0, true, &gobj); 8462306a36Sopenharmony_ci if (ret) { 8562306a36Sopenharmony_ci pr_err("failed to allocate framebuffer (%d)\n", aligned_size); 8662306a36Sopenharmony_ci return -ENOMEM; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci rbo = gem_to_radeon_bo(gobj); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (fb_tiled) 9162306a36Sopenharmony_ci tiling_flags = RADEON_TILING_MACRO; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 9462306a36Sopenharmony_ci switch (cpp) { 9562306a36Sopenharmony_ci case 4: 9662306a36Sopenharmony_ci tiling_flags |= RADEON_TILING_SWAP_32BIT; 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci case 2: 9962306a36Sopenharmony_ci tiling_flags |= RADEON_TILING_SWAP_16BIT; 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci default: 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci#endif 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (tiling_flags) { 10762306a36Sopenharmony_ci ret = radeon_bo_set_tiling_flags(rbo, 10862306a36Sopenharmony_ci tiling_flags | RADEON_TILING_SURFACE, 10962306a36Sopenharmony_ci mode_cmd->pitches[0]); 11062306a36Sopenharmony_ci if (ret) 11162306a36Sopenharmony_ci dev_err(rdev->dev, "FB failed to set tiling flags\n"); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci ret = radeon_bo_reserve(rbo, false); 11562306a36Sopenharmony_ci if (unlikely(ret != 0)) 11662306a36Sopenharmony_ci goto err_radeon_fbdev_destroy_pinned_object; 11762306a36Sopenharmony_ci /* Only 27 bit offset for legacy CRTC */ 11862306a36Sopenharmony_ci ret = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM, 11962306a36Sopenharmony_ci ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, 12062306a36Sopenharmony_ci NULL); 12162306a36Sopenharmony_ci if (ret) { 12262306a36Sopenharmony_ci radeon_bo_unreserve(rbo); 12362306a36Sopenharmony_ci goto err_radeon_fbdev_destroy_pinned_object; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci if (fb_tiled) 12662306a36Sopenharmony_ci radeon_bo_check_tiling(rbo, 0, 0); 12762306a36Sopenharmony_ci ret = radeon_bo_kmap(rbo, NULL); 12862306a36Sopenharmony_ci radeon_bo_unreserve(rbo); 12962306a36Sopenharmony_ci if (ret) 13062306a36Sopenharmony_ci goto err_radeon_fbdev_destroy_pinned_object; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci *gobj_p = gobj; 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cierr_radeon_fbdev_destroy_pinned_object: 13662306a36Sopenharmony_ci radeon_fbdev_destroy_pinned_object(gobj); 13762306a36Sopenharmony_ci *gobj_p = NULL; 13862306a36Sopenharmony_ci return ret; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* 14262306a36Sopenharmony_ci * Fbdev ops and struct fb_ops 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int radeon_fbdev_fb_open(struct fb_info *info, int user) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 14862306a36Sopenharmony_ci struct radeon_device *rdev = fb_helper->dev->dev_private; 14962306a36Sopenharmony_ci int ret; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci ret = pm_runtime_get_sync(rdev->ddev->dev); 15262306a36Sopenharmony_ci if (ret < 0 && ret != -EACCES) 15362306a36Sopenharmony_ci goto err_pm_runtime_mark_last_busy; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cierr_pm_runtime_mark_last_busy: 15862306a36Sopenharmony_ci pm_runtime_mark_last_busy(rdev->ddev->dev); 15962306a36Sopenharmony_ci pm_runtime_put_autosuspend(rdev->ddev->dev); 16062306a36Sopenharmony_ci return ret; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int radeon_fbdev_fb_release(struct fb_info *info, int user) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 16662306a36Sopenharmony_ci struct radeon_device *rdev = fb_helper->dev->dev_private; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci pm_runtime_mark_last_busy(rdev->ddev->dev); 16962306a36Sopenharmony_ci pm_runtime_put_autosuspend(rdev->ddev->dev); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void radeon_fbdev_fb_destroy(struct fb_info *info) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 17762306a36Sopenharmony_ci struct drm_framebuffer *fb = fb_helper->fb; 17862306a36Sopenharmony_ci struct drm_gem_object *gobj = drm_gem_fb_get_obj(fb, 0); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci drm_fb_helper_fini(fb_helper); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci drm_framebuffer_unregister_private(fb); 18362306a36Sopenharmony_ci drm_framebuffer_cleanup(fb); 18462306a36Sopenharmony_ci kfree(fb); 18562306a36Sopenharmony_ci radeon_fbdev_destroy_pinned_object(gobj); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci drm_client_release(&fb_helper->client); 18862306a36Sopenharmony_ci drm_fb_helper_unprepare(fb_helper); 18962306a36Sopenharmony_ci kfree(fb_helper); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic const struct fb_ops radeon_fbdev_fb_ops = { 19362306a36Sopenharmony_ci .owner = THIS_MODULE, 19462306a36Sopenharmony_ci .fb_open = radeon_fbdev_fb_open, 19562306a36Sopenharmony_ci .fb_release = radeon_fbdev_fb_release, 19662306a36Sopenharmony_ci FB_DEFAULT_IOMEM_OPS, 19762306a36Sopenharmony_ci DRM_FB_HELPER_DEFAULT_OPS, 19862306a36Sopenharmony_ci .fb_destroy = radeon_fbdev_fb_destroy, 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/* 20262306a36Sopenharmony_ci * Fbdev helpers and struct drm_fb_helper_funcs 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int radeon_fbdev_fb_helper_fb_probe(struct drm_fb_helper *fb_helper, 20662306a36Sopenharmony_ci struct drm_fb_helper_surface_size *sizes) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct radeon_device *rdev = fb_helper->dev->dev_private; 20962306a36Sopenharmony_ci struct drm_mode_fb_cmd2 mode_cmd = { }; 21062306a36Sopenharmony_ci struct fb_info *info; 21162306a36Sopenharmony_ci struct drm_gem_object *gobj; 21262306a36Sopenharmony_ci struct radeon_bo *rbo; 21362306a36Sopenharmony_ci struct drm_framebuffer *fb; 21462306a36Sopenharmony_ci int ret; 21562306a36Sopenharmony_ci unsigned long tmp; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci mode_cmd.width = sizes->surface_width; 21862306a36Sopenharmony_ci mode_cmd.height = sizes->surface_height; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* avivo can't scanout real 24bpp */ 22162306a36Sopenharmony_ci if ((sizes->surface_bpp == 24) && ASIC_IS_AVIVO(rdev)) 22262306a36Sopenharmony_ci sizes->surface_bpp = 32; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 22562306a36Sopenharmony_ci sizes->surface_depth); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ret = radeon_fbdev_create_pinned_object(fb_helper, &mode_cmd, &gobj); 22862306a36Sopenharmony_ci if (ret) { 22962306a36Sopenharmony_ci DRM_ERROR("failed to create fbcon object %d\n", ret); 23062306a36Sopenharmony_ci return ret; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci rbo = gem_to_radeon_bo(gobj); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci fb = kzalloc(sizeof(*fb), GFP_KERNEL); 23562306a36Sopenharmony_ci if (!fb) { 23662306a36Sopenharmony_ci ret = -ENOMEM; 23762306a36Sopenharmony_ci goto err_radeon_fbdev_destroy_pinned_object; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci ret = radeon_framebuffer_init(rdev->ddev, fb, &mode_cmd, gobj); 24062306a36Sopenharmony_ci if (ret) { 24162306a36Sopenharmony_ci DRM_ERROR("failed to initialize framebuffer %d\n", ret); 24262306a36Sopenharmony_ci goto err_kfree; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* setup helper */ 24662306a36Sopenharmony_ci fb_helper->fb = fb; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* okay we have an object now allocate the framebuffer */ 24962306a36Sopenharmony_ci info = drm_fb_helper_alloc_info(fb_helper); 25062306a36Sopenharmony_ci if (IS_ERR(info)) { 25162306a36Sopenharmony_ci ret = PTR_ERR(info); 25262306a36Sopenharmony_ci goto err_drm_framebuffer_unregister_private; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci info->fbops = &radeon_fbdev_fb_ops; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* radeon resume is fragile and needs a vt switch to help it along */ 25862306a36Sopenharmony_ci info->skip_vt_switch = false; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci drm_fb_helper_fill_info(info, fb_helper, sizes); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci tmp = radeon_bo_gpu_offset(rbo) - rdev->mc.vram_start; 26362306a36Sopenharmony_ci info->fix.smem_start = rdev->mc.aper_base + tmp; 26462306a36Sopenharmony_ci info->fix.smem_len = radeon_bo_size(rbo); 26562306a36Sopenharmony_ci info->screen_base = (__force void __iomem *)rbo->kptr; 26662306a36Sopenharmony_ci info->screen_size = radeon_bo_size(rbo); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci memset_io(info->screen_base, 0, info->screen_size); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start); 27362306a36Sopenharmony_ci DRM_INFO("vram apper at 0x%lX\n", (unsigned long)rdev->mc.aper_base); 27462306a36Sopenharmony_ci DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo)); 27562306a36Sopenharmony_ci DRM_INFO("fb depth is %d\n", fb->format->depth); 27662306a36Sopenharmony_ci DRM_INFO(" pitch is %d\n", fb->pitches[0]); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cierr_drm_framebuffer_unregister_private: 28162306a36Sopenharmony_ci fb_helper->fb = NULL; 28262306a36Sopenharmony_ci drm_framebuffer_unregister_private(fb); 28362306a36Sopenharmony_ci drm_framebuffer_cleanup(fb); 28462306a36Sopenharmony_cierr_kfree: 28562306a36Sopenharmony_ci kfree(fb); 28662306a36Sopenharmony_cierr_radeon_fbdev_destroy_pinned_object: 28762306a36Sopenharmony_ci radeon_fbdev_destroy_pinned_object(gobj); 28862306a36Sopenharmony_ci return ret; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic const struct drm_fb_helper_funcs radeon_fbdev_fb_helper_funcs = { 29262306a36Sopenharmony_ci .fb_probe = radeon_fbdev_fb_helper_fb_probe, 29362306a36Sopenharmony_ci}; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/* 29662306a36Sopenharmony_ci * Fbdev client and struct drm_client_funcs 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic void radeon_fbdev_client_unregister(struct drm_client_dev *client) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 30262306a36Sopenharmony_ci struct drm_device *dev = fb_helper->dev; 30362306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (fb_helper->info) { 30662306a36Sopenharmony_ci vga_switcheroo_client_fb_set(rdev->pdev, NULL); 30762306a36Sopenharmony_ci drm_helper_force_disable_all(dev); 30862306a36Sopenharmony_ci drm_fb_helper_unregister_info(fb_helper); 30962306a36Sopenharmony_ci } else { 31062306a36Sopenharmony_ci drm_client_release(&fb_helper->client); 31162306a36Sopenharmony_ci drm_fb_helper_unprepare(fb_helper); 31262306a36Sopenharmony_ci kfree(fb_helper); 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int radeon_fbdev_client_restore(struct drm_client_dev *client) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci drm_fb_helper_lastclose(client->dev); 31962306a36Sopenharmony_ci vga_switcheroo_process_delayed_switch(); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic int radeon_fbdev_client_hotplug(struct drm_client_dev *client) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 32762306a36Sopenharmony_ci struct drm_device *dev = client->dev; 32862306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 32962306a36Sopenharmony_ci int ret; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (dev->fb_helper) 33262306a36Sopenharmony_ci return drm_fb_helper_hotplug_event(dev->fb_helper); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci ret = drm_fb_helper_init(dev, fb_helper); 33562306a36Sopenharmony_ci if (ret) 33662306a36Sopenharmony_ci goto err_drm_err; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (!drm_drv_uses_atomic_modeset(dev)) 33962306a36Sopenharmony_ci drm_helper_disable_unused_functions(dev); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci ret = drm_fb_helper_initial_config(fb_helper); 34262306a36Sopenharmony_ci if (ret) 34362306a36Sopenharmony_ci goto err_drm_fb_helper_fini; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci vga_switcheroo_client_fb_set(rdev->pdev, fb_helper->info); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cierr_drm_fb_helper_fini: 35062306a36Sopenharmony_ci drm_fb_helper_fini(fb_helper); 35162306a36Sopenharmony_cierr_drm_err: 35262306a36Sopenharmony_ci drm_err(dev, "Failed to setup radeon fbdev emulation (ret=%d)\n", ret); 35362306a36Sopenharmony_ci return ret; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic const struct drm_client_funcs radeon_fbdev_client_funcs = { 35762306a36Sopenharmony_ci .owner = THIS_MODULE, 35862306a36Sopenharmony_ci .unregister = radeon_fbdev_client_unregister, 35962306a36Sopenharmony_ci .restore = radeon_fbdev_client_restore, 36062306a36Sopenharmony_ci .hotplug = radeon_fbdev_client_hotplug, 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_civoid radeon_fbdev_setup(struct radeon_device *rdev) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct drm_fb_helper *fb_helper; 36662306a36Sopenharmony_ci int bpp_sel = 32; 36762306a36Sopenharmony_ci int ret; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (rdev->mc.real_vram_size <= (8 * 1024 * 1024)) 37062306a36Sopenharmony_ci bpp_sel = 8; 37162306a36Sopenharmony_ci else if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32 * 1024 * 1024)) 37262306a36Sopenharmony_ci bpp_sel = 16; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL); 37562306a36Sopenharmony_ci if (!fb_helper) 37662306a36Sopenharmony_ci return; 37762306a36Sopenharmony_ci drm_fb_helper_prepare(rdev->ddev, fb_helper, bpp_sel, &radeon_fbdev_fb_helper_funcs); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ret = drm_client_init(rdev->ddev, &fb_helper->client, "radeon-fbdev", 38062306a36Sopenharmony_ci &radeon_fbdev_client_funcs); 38162306a36Sopenharmony_ci if (ret) { 38262306a36Sopenharmony_ci drm_err(rdev->ddev, "Failed to register client: %d\n", ret); 38362306a36Sopenharmony_ci goto err_drm_client_init; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci drm_client_register(&fb_helper->client); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cierr_drm_client_init: 39162306a36Sopenharmony_ci drm_fb_helper_unprepare(fb_helper); 39262306a36Sopenharmony_ci kfree(fb_helper); 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_civoid radeon_fbdev_set_suspend(struct radeon_device *rdev, int state) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci if (rdev->ddev->fb_helper) 39862306a36Sopenharmony_ci drm_fb_helper_set_suspend(rdev->ddev->fb_helper, state); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cibool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct drm_fb_helper *fb_helper = rdev->ddev->fb_helper; 40462306a36Sopenharmony_ci struct drm_gem_object *gobj; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (!fb_helper) 40762306a36Sopenharmony_ci return false; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci gobj = drm_gem_fb_get_obj(fb_helper->fb, 0); 41062306a36Sopenharmony_ci if (!gobj) 41162306a36Sopenharmony_ci return false; 41262306a36Sopenharmony_ci if (gobj != &robj->tbo.base) 41362306a36Sopenharmony_ci return false; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return true; 41662306a36Sopenharmony_ci} 417