18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2006-2009 Red Hat Inc. 38c2ecf20Sopenharmony_ci * Copyright (c) 2006-2008 Intel Corporation 48c2ecf20Sopenharmony_ci * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * DRM framebuffer helper functions 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Permission to use, copy, modify, distribute, and sell this software and its 98c2ecf20Sopenharmony_ci * documentation for any purpose is hereby granted without fee, provided that 108c2ecf20Sopenharmony_ci * the above copyright notice appear in all copies and that both that copyright 118c2ecf20Sopenharmony_ci * notice and this permission notice appear in supporting documentation, and 128c2ecf20Sopenharmony_ci * that the name of the copyright holders not be used in advertising or 138c2ecf20Sopenharmony_ci * publicity pertaining to distribution of the software without specific, 148c2ecf20Sopenharmony_ci * written prior permission. The copyright holders make no representations 158c2ecf20Sopenharmony_ci * about the suitability of this software for any purpose. It is provided "as 168c2ecf20Sopenharmony_ci * is" without express or implied warranty. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 198c2ecf20Sopenharmony_ci * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 208c2ecf20Sopenharmony_ci * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 218c2ecf20Sopenharmony_ci * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 228c2ecf20Sopenharmony_ci * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 238c2ecf20Sopenharmony_ci * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 248c2ecf20Sopenharmony_ci * OF THIS SOFTWARE. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Authors: 278c2ecf20Sopenharmony_ci * Dave Airlie <airlied@linux.ie> 288c2ecf20Sopenharmony_ci * Jesse Barnes <jesse.barnes@intel.com> 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <linux/console.h> 338c2ecf20Sopenharmony_ci#include <linux/dma-buf.h> 348c2ecf20Sopenharmony_ci#include <linux/kernel.h> 358c2ecf20Sopenharmony_ci#include <linux/module.h> 368c2ecf20Sopenharmony_ci#include <linux/slab.h> 378c2ecf20Sopenharmony_ci#include <linux/sysrq.h> 388c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 418c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 428c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 438c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 448c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 458c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 468c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 478c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#include "drm_crtc_helper_internal.h" 508c2ecf20Sopenharmony_ci#include "drm_internal.h" 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic bool drm_fbdev_emulation = true; 538c2ecf20Sopenharmony_cimodule_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600); 548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fbdev_emulation, 558c2ecf20Sopenharmony_ci "Enable legacy fbdev emulation [default=true]"); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic int drm_fbdev_overalloc = CONFIG_DRM_FBDEV_OVERALLOC; 588c2ecf20Sopenharmony_cimodule_param(drm_fbdev_overalloc, int, 0444); 598c2ecf20Sopenharmony_ciMODULE_PARM_DESC(drm_fbdev_overalloc, 608c2ecf20Sopenharmony_ci "Overallocation of the fbdev buffer (%) [default=" 618c2ecf20Sopenharmony_ci __MODULE_STRING(CONFIG_DRM_FBDEV_OVERALLOC) "]"); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * In order to keep user-space compatibility, we want in certain use-cases 658c2ecf20Sopenharmony_ci * to keep leaking the fbdev physical address to the user-space program 668c2ecf20Sopenharmony_ci * handling the fbdev buffer. 678c2ecf20Sopenharmony_ci * This is a bad habit essentially kept into closed source opengl driver 688c2ecf20Sopenharmony_ci * that should really be moved into open-source upstream projects instead 698c2ecf20Sopenharmony_ci * of using legacy physical addresses in user space to communicate with 708c2ecf20Sopenharmony_ci * other out-of-tree kernel modules. 718c2ecf20Sopenharmony_ci * 728c2ecf20Sopenharmony_ci * This module_param *should* be removed as soon as possible and be 738c2ecf20Sopenharmony_ci * considered as a broken and legacy behaviour from a modern fbdev device. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) 768c2ecf20Sopenharmony_cistatic bool drm_leak_fbdev_smem = false; 778c2ecf20Sopenharmony_cimodule_param_unsafe(drm_leak_fbdev_smem, bool, 0600); 788c2ecf20Sopenharmony_ciMODULE_PARM_DESC(drm_leak_fbdev_smem, 798c2ecf20Sopenharmony_ci "Allow unsafe leaking fbdev physical smem address [default=false]"); 808c2ecf20Sopenharmony_ci#endif 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic LIST_HEAD(kernel_fb_helper_list); 838c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(kernel_fb_helper_lock); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/** 868c2ecf20Sopenharmony_ci * DOC: fbdev helpers 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * The fb helper functions are useful to provide an fbdev on top of a drm kernel 898c2ecf20Sopenharmony_ci * mode setting driver. They can be used mostly independently from the crtc 908c2ecf20Sopenharmony_ci * helper functions used by many drivers to implement the kernel mode setting 918c2ecf20Sopenharmony_ci * interfaces. 928c2ecf20Sopenharmony_ci * 938c2ecf20Sopenharmony_ci * Drivers that support a dumb buffer with a virtual address and mmap support, 948c2ecf20Sopenharmony_ci * should try out the generic fbdev emulation using drm_fbdev_generic_setup(). 958c2ecf20Sopenharmony_ci * It will automatically set up deferred I/O if the driver requires a shadow 968c2ecf20Sopenharmony_ci * buffer. 978c2ecf20Sopenharmony_ci * 988c2ecf20Sopenharmony_ci * At runtime drivers should restore the fbdev console by using 998c2ecf20Sopenharmony_ci * drm_fb_helper_lastclose() as their &drm_driver.lastclose callback. 1008c2ecf20Sopenharmony_ci * They should also notify the fb helper code from updates to the output 1018c2ecf20Sopenharmony_ci * configuration by using drm_fb_helper_output_poll_changed() as their 1028c2ecf20Sopenharmony_ci * &drm_mode_config_funcs.output_poll_changed callback. 1038c2ecf20Sopenharmony_ci * 1048c2ecf20Sopenharmony_ci * For suspend/resume consider using drm_mode_config_helper_suspend() and 1058c2ecf20Sopenharmony_ci * drm_mode_config_helper_resume() which takes care of fbdev as well. 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * All other functions exported by the fb helper library can be used to 1088c2ecf20Sopenharmony_ci * implement the fbdev driver interface by the driver. 1098c2ecf20Sopenharmony_ci * 1108c2ecf20Sopenharmony_ci * It is possible, though perhaps somewhat tricky, to implement race-free 1118c2ecf20Sopenharmony_ci * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare() 1128c2ecf20Sopenharmony_ci * helper must be called first to initialize the minimum required to make 1138c2ecf20Sopenharmony_ci * hotplug detection work. Drivers also need to make sure to properly set up 1148c2ecf20Sopenharmony_ci * the &drm_mode_config.funcs member. After calling drm_kms_helper_poll_init() 1158c2ecf20Sopenharmony_ci * it is safe to enable interrupts and start processing hotplug events. At the 1168c2ecf20Sopenharmony_ci * same time, drivers should initialize all modeset objects such as CRTCs, 1178c2ecf20Sopenharmony_ci * encoders and connectors. To finish up the fbdev helper initialization, the 1188c2ecf20Sopenharmony_ci * drm_fb_helper_init() function is called. To probe for all attached displays 1198c2ecf20Sopenharmony_ci * and set up an initial configuration using the detected hardware, drivers 1208c2ecf20Sopenharmony_ci * should call drm_fb_helper_initial_config(). 1218c2ecf20Sopenharmony_ci * 1228c2ecf20Sopenharmony_ci * If &drm_framebuffer_funcs.dirty is set, the 1238c2ecf20Sopenharmony_ci * drm_fb_helper_{cfb,sys}_{write,fillrect,copyarea,imageblit} functions will 1248c2ecf20Sopenharmony_ci * accumulate changes and schedule &drm_fb_helper.dirty_work to run right 1258c2ecf20Sopenharmony_ci * away. This worker then calls the dirty() function ensuring that it will 1268c2ecf20Sopenharmony_ci * always run in process context since the fb_*() function could be running in 1278c2ecf20Sopenharmony_ci * atomic context. If drm_fb_helper_deferred_io() is used as the deferred_io 1288c2ecf20Sopenharmony_ci * callback it will also schedule dirty_work with the damage collected from the 1298c2ecf20Sopenharmony_ci * mmap page writes. 1308c2ecf20Sopenharmony_ci * 1318c2ecf20Sopenharmony_ci * Deferred I/O is not compatible with SHMEM. Such drivers should request an 1328c2ecf20Sopenharmony_ci * fbdev shadow buffer and call drm_fbdev_generic_setup() instead. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci uint16_t *r_base, *g_base, *b_base; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (crtc->funcs->gamma_set == NULL) 1408c2ecf20Sopenharmony_ci return; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci r_base = crtc->gamma_store; 1438c2ecf20Sopenharmony_ci g_base = r_base + crtc->gamma_size; 1448c2ecf20Sopenharmony_ci b_base = g_base + crtc->gamma_size; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 1478c2ecf20Sopenharmony_ci crtc->gamma_size, NULL); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/** 1518c2ecf20Sopenharmony_ci * drm_fb_helper_debug_enter - implementation for &fb_ops.fb_debug_enter 1528c2ecf20Sopenharmony_ci * @info: fbdev registered by the helper 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ciint drm_fb_helper_debug_enter(struct fb_info *info) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct drm_fb_helper *helper = info->par; 1578c2ecf20Sopenharmony_ci const struct drm_crtc_helper_funcs *funcs; 1588c2ecf20Sopenharmony_ci struct drm_mode_set *mode_set; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { 1618c2ecf20Sopenharmony_ci mutex_lock(&helper->client.modeset_mutex); 1628c2ecf20Sopenharmony_ci drm_client_for_each_modeset(mode_set, &helper->client) { 1638c2ecf20Sopenharmony_ci if (!mode_set->crtc->enabled) 1648c2ecf20Sopenharmony_ci continue; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci funcs = mode_set->crtc->helper_private; 1678c2ecf20Sopenharmony_ci if (funcs->mode_set_base_atomic == NULL) 1688c2ecf20Sopenharmony_ci continue; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (drm_drv_uses_atomic_modeset(mode_set->crtc->dev)) 1718c2ecf20Sopenharmony_ci continue; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci funcs->mode_set_base_atomic(mode_set->crtc, 1748c2ecf20Sopenharmony_ci mode_set->fb, 1758c2ecf20Sopenharmony_ci mode_set->x, 1768c2ecf20Sopenharmony_ci mode_set->y, 1778c2ecf20Sopenharmony_ci ENTER_ATOMIC_MODE_SET); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci mutex_unlock(&helper->client.modeset_mutex); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_debug_enter); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/** 1878c2ecf20Sopenharmony_ci * drm_fb_helper_debug_leave - implementation for &fb_ops.fb_debug_leave 1888c2ecf20Sopenharmony_ci * @info: fbdev registered by the helper 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ciint drm_fb_helper_debug_leave(struct fb_info *info) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct drm_fb_helper *helper = info->par; 1938c2ecf20Sopenharmony_ci struct drm_client_dev *client = &helper->client; 1948c2ecf20Sopenharmony_ci struct drm_device *dev = helper->dev; 1958c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 1968c2ecf20Sopenharmony_ci const struct drm_crtc_helper_funcs *funcs; 1978c2ecf20Sopenharmony_ci struct drm_mode_set *mode_set; 1988c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci mutex_lock(&client->modeset_mutex); 2018c2ecf20Sopenharmony_ci drm_client_for_each_modeset(mode_set, client) { 2028c2ecf20Sopenharmony_ci crtc = mode_set->crtc; 2038c2ecf20Sopenharmony_ci if (drm_drv_uses_atomic_modeset(crtc->dev)) 2048c2ecf20Sopenharmony_ci continue; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci funcs = crtc->helper_private; 2078c2ecf20Sopenharmony_ci fb = crtc->primary->fb; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (!crtc->enabled) 2108c2ecf20Sopenharmony_ci continue; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (!fb) { 2138c2ecf20Sopenharmony_ci drm_err(dev, "no fb to restore?\n"); 2148c2ecf20Sopenharmony_ci continue; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (funcs->mode_set_base_atomic == NULL) 2188c2ecf20Sopenharmony_ci continue; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci drm_fb_helper_restore_lut_atomic(mode_set->crtc); 2218c2ecf20Sopenharmony_ci funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, 2228c2ecf20Sopenharmony_ci crtc->y, LEAVE_ATOMIC_MODE_SET); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci mutex_unlock(&client->modeset_mutex); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_debug_leave); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int 2318c2ecf20Sopenharmony_ci__drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper, 2328c2ecf20Sopenharmony_ci bool force) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci bool do_delayed; 2358c2ecf20Sopenharmony_ci int ret; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (!drm_fbdev_emulation || !fb_helper) 2388c2ecf20Sopenharmony_ci return -ENODEV; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (READ_ONCE(fb_helper->deferred_setup)) 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci mutex_lock(&fb_helper->lock); 2448c2ecf20Sopenharmony_ci if (force) { 2458c2ecf20Sopenharmony_ci /* 2468c2ecf20Sopenharmony_ci * Yes this is the _locked version which expects the master lock 2478c2ecf20Sopenharmony_ci * to be held. But for forced restores we're intentionally 2488c2ecf20Sopenharmony_ci * racing here, see drm_fb_helper_set_par(). 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ci ret = drm_client_modeset_commit_locked(&fb_helper->client); 2518c2ecf20Sopenharmony_ci } else { 2528c2ecf20Sopenharmony_ci ret = drm_client_modeset_commit(&fb_helper->client); 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci do_delayed = fb_helper->delayed_hotplug; 2568c2ecf20Sopenharmony_ci if (do_delayed) 2578c2ecf20Sopenharmony_ci fb_helper->delayed_hotplug = false; 2588c2ecf20Sopenharmony_ci mutex_unlock(&fb_helper->lock); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (do_delayed) 2618c2ecf20Sopenharmony_ci drm_fb_helper_hotplug_event(fb_helper); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return ret; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/** 2678c2ecf20Sopenharmony_ci * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration 2688c2ecf20Sopenharmony_ci * @fb_helper: driver-allocated fbdev helper, can be NULL 2698c2ecf20Sopenharmony_ci * 2708c2ecf20Sopenharmony_ci * This should be called from driver's drm &drm_driver.lastclose callback 2718c2ecf20Sopenharmony_ci * when implementing an fbcon on top of kms using this helper. This ensures that 2728c2ecf20Sopenharmony_ci * the user isn't greeted with a black screen when e.g. X dies. 2738c2ecf20Sopenharmony_ci * 2748c2ecf20Sopenharmony_ci * RETURNS: 2758c2ecf20Sopenharmony_ci * Zero if everything went ok, negative error code otherwise. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ciint drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci return __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, false); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci#ifdef CONFIG_MAGIC_SYSRQ 2848c2ecf20Sopenharmony_ci/* 2858c2ecf20Sopenharmony_ci * restore fbcon display for all kms driver's using this helper, used for sysrq 2868c2ecf20Sopenharmony_ci * and panic handling. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_cistatic bool drm_fb_helper_force_kernel_mode(void) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci bool ret, error = false; 2918c2ecf20Sopenharmony_ci struct drm_fb_helper *helper; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (list_empty(&kernel_fb_helper_list)) 2948c2ecf20Sopenharmony_ci return false; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { 2978c2ecf20Sopenharmony_ci struct drm_device *dev = helper->dev; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) 3008c2ecf20Sopenharmony_ci continue; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci mutex_lock(&helper->lock); 3038c2ecf20Sopenharmony_ci ret = drm_client_modeset_commit_locked(&helper->client); 3048c2ecf20Sopenharmony_ci if (ret) 3058c2ecf20Sopenharmony_ci error = true; 3068c2ecf20Sopenharmony_ci mutex_unlock(&helper->lock); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci return error; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic void drm_fb_helper_restore_work_fn(struct work_struct *ignored) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci bool ret; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci ret = drm_fb_helper_force_kernel_mode(); 3168c2ecf20Sopenharmony_ci if (ret == true) 3178c2ecf20Sopenharmony_ci DRM_ERROR("Failed to restore crtc configuration\n"); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_cistatic DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void drm_fb_helper_sysrq(int dummy1) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci schedule_work(&drm_fb_helper_restore_work); 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic const struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { 3278c2ecf20Sopenharmony_ci .handler = drm_fb_helper_sysrq, 3288c2ecf20Sopenharmony_ci .help_msg = "force-fb(v)", 3298c2ecf20Sopenharmony_ci .action_msg = "Restore framebuffer console", 3308c2ecf20Sopenharmony_ci}; 3318c2ecf20Sopenharmony_ci#else 3328c2ecf20Sopenharmony_cistatic const struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { }; 3338c2ecf20Sopenharmony_ci#endif 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci mutex_lock(&fb_helper->lock); 3408c2ecf20Sopenharmony_ci drm_client_modeset_dpms(&fb_helper->client, dpms_mode); 3418c2ecf20Sopenharmony_ci mutex_unlock(&fb_helper->lock); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci/** 3458c2ecf20Sopenharmony_ci * drm_fb_helper_blank - implementation for &fb_ops.fb_blank 3468c2ecf20Sopenharmony_ci * @blank: desired blanking state 3478c2ecf20Sopenharmony_ci * @info: fbdev registered by the helper 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_ciint drm_fb_helper_blank(int blank, struct fb_info *info) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci if (oops_in_progress) 3528c2ecf20Sopenharmony_ci return -EBUSY; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci switch (blank) { 3558c2ecf20Sopenharmony_ci /* Display: On; HSync: On, VSync: On */ 3568c2ecf20Sopenharmony_ci case FB_BLANK_UNBLANK: 3578c2ecf20Sopenharmony_ci drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON); 3588c2ecf20Sopenharmony_ci break; 3598c2ecf20Sopenharmony_ci /* Display: Off; HSync: On, VSync: On */ 3608c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: 3618c2ecf20Sopenharmony_ci drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY); 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci /* Display: Off; HSync: Off, VSync: On */ 3648c2ecf20Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 3658c2ecf20Sopenharmony_ci drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY); 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci /* Display: Off; HSync: On, VSync: Off */ 3688c2ecf20Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 3698c2ecf20Sopenharmony_ci drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND); 3708c2ecf20Sopenharmony_ci break; 3718c2ecf20Sopenharmony_ci /* Display: Off; HSync: Off, VSync: Off */ 3728c2ecf20Sopenharmony_ci case FB_BLANK_POWERDOWN: 3738c2ecf20Sopenharmony_ci drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF); 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_blank); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic void drm_fb_helper_resume_worker(struct work_struct *work) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper, 3838c2ecf20Sopenharmony_ci resume_work); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci console_lock(); 3868c2ecf20Sopenharmony_ci fb_set_suspend(helper->fbdev, 0); 3878c2ecf20Sopenharmony_ci console_unlock(); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic void drm_fb_helper_dirty_blit_real(struct drm_fb_helper *fb_helper, 3918c2ecf20Sopenharmony_ci struct drm_clip_rect *clip) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = fb_helper->fb; 3948c2ecf20Sopenharmony_ci unsigned int cpp = fb->format->cpp[0]; 3958c2ecf20Sopenharmony_ci size_t offset = clip->y1 * fb->pitches[0] + clip->x1 * cpp; 3968c2ecf20Sopenharmony_ci void *src = fb_helper->fbdev->screen_buffer + offset; 3978c2ecf20Sopenharmony_ci void *dst = fb_helper->buffer->vaddr + offset; 3988c2ecf20Sopenharmony_ci size_t len = (clip->x2 - clip->x1) * cpp; 3998c2ecf20Sopenharmony_ci unsigned int y; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci for (y = clip->y1; y < clip->y2; y++) { 4028c2ecf20Sopenharmony_ci if (!fb_helper->dev->mode_config.fbdev_use_iomem) 4038c2ecf20Sopenharmony_ci memcpy(dst, src, len); 4048c2ecf20Sopenharmony_ci else 4058c2ecf20Sopenharmony_ci memcpy_toio((void __iomem *)dst, src, len); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci src += fb->pitches[0]; 4088c2ecf20Sopenharmony_ci dst += fb->pitches[0]; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic void drm_fb_helper_dirty_work(struct work_struct *work) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper, 4158c2ecf20Sopenharmony_ci dirty_work); 4168c2ecf20Sopenharmony_ci struct drm_clip_rect *clip = &helper->dirty_clip; 4178c2ecf20Sopenharmony_ci struct drm_clip_rect clip_copy; 4188c2ecf20Sopenharmony_ci unsigned long flags; 4198c2ecf20Sopenharmony_ci void *vaddr; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci spin_lock_irqsave(&helper->dirty_lock, flags); 4228c2ecf20Sopenharmony_ci clip_copy = *clip; 4238c2ecf20Sopenharmony_ci clip->x1 = clip->y1 = ~0; 4248c2ecf20Sopenharmony_ci clip->x2 = clip->y2 = 0; 4258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&helper->dirty_lock, flags); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* call dirty callback only when it has been really touched */ 4288c2ecf20Sopenharmony_ci if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2) { 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* Generic fbdev uses a shadow buffer */ 4318c2ecf20Sopenharmony_ci if (helper->buffer) { 4328c2ecf20Sopenharmony_ci vaddr = drm_client_buffer_vmap(helper->buffer); 4338c2ecf20Sopenharmony_ci if (IS_ERR(vaddr)) 4348c2ecf20Sopenharmony_ci return; 4358c2ecf20Sopenharmony_ci drm_fb_helper_dirty_blit_real(helper, &clip_copy); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci if (helper->fb->funcs->dirty) 4388c2ecf20Sopenharmony_ci helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, 4398c2ecf20Sopenharmony_ci &clip_copy, 1); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (helper->buffer) 4428c2ecf20Sopenharmony_ci drm_client_buffer_vunmap(helper->buffer); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci/** 4478c2ecf20Sopenharmony_ci * drm_fb_helper_prepare - setup a drm_fb_helper structure 4488c2ecf20Sopenharmony_ci * @dev: DRM device 4498c2ecf20Sopenharmony_ci * @helper: driver-allocated fbdev helper structure to set up 4508c2ecf20Sopenharmony_ci * @funcs: pointer to structure of functions associate with this helper 4518c2ecf20Sopenharmony_ci * 4528c2ecf20Sopenharmony_ci * Sets up the bare minimum to make the framebuffer helper usable. This is 4538c2ecf20Sopenharmony_ci * useful to implement race-free initialization of the polling helpers. 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_civoid drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, 4568c2ecf20Sopenharmony_ci const struct drm_fb_helper_funcs *funcs) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&helper->kernel_fb_list); 4598c2ecf20Sopenharmony_ci spin_lock_init(&helper->dirty_lock); 4608c2ecf20Sopenharmony_ci INIT_WORK(&helper->resume_work, drm_fb_helper_resume_worker); 4618c2ecf20Sopenharmony_ci INIT_WORK(&helper->dirty_work, drm_fb_helper_dirty_work); 4628c2ecf20Sopenharmony_ci helper->dirty_clip.x1 = helper->dirty_clip.y1 = ~0; 4638c2ecf20Sopenharmony_ci mutex_init(&helper->lock); 4648c2ecf20Sopenharmony_ci helper->funcs = funcs; 4658c2ecf20Sopenharmony_ci helper->dev = dev; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_prepare); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci/** 4708c2ecf20Sopenharmony_ci * drm_fb_helper_init - initialize a &struct drm_fb_helper 4718c2ecf20Sopenharmony_ci * @dev: drm device 4728c2ecf20Sopenharmony_ci * @fb_helper: driver-allocated fbdev helper structure to initialize 4738c2ecf20Sopenharmony_ci * 4748c2ecf20Sopenharmony_ci * This allocates the structures for the fbdev helper with the given limits. 4758c2ecf20Sopenharmony_ci * Note that this won't yet touch the hardware (through the driver interfaces) 4768c2ecf20Sopenharmony_ci * nor register the fbdev. This is only done in drm_fb_helper_initial_config() 4778c2ecf20Sopenharmony_ci * to allow driver writes more control over the exact init sequence. 4788c2ecf20Sopenharmony_ci * 4798c2ecf20Sopenharmony_ci * Drivers must call drm_fb_helper_prepare() before calling this function. 4808c2ecf20Sopenharmony_ci * 4818c2ecf20Sopenharmony_ci * RETURNS: 4828c2ecf20Sopenharmony_ci * Zero if everything went ok, nonzero otherwise. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ciint drm_fb_helper_init(struct drm_device *dev, 4858c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci int ret; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (!drm_fbdev_emulation) { 4908c2ecf20Sopenharmony_ci dev->fb_helper = fb_helper; 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* 4958c2ecf20Sopenharmony_ci * If this is not the generic fbdev client, initialize a drm_client 4968c2ecf20Sopenharmony_ci * without callbacks so we can use the modesets. 4978c2ecf20Sopenharmony_ci */ 4988c2ecf20Sopenharmony_ci if (!fb_helper->client.funcs) { 4998c2ecf20Sopenharmony_ci ret = drm_client_init(dev, &fb_helper->client, "drm_fb_helper", NULL); 5008c2ecf20Sopenharmony_ci if (ret) 5018c2ecf20Sopenharmony_ci return ret; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci dev->fb_helper = fb_helper; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci return 0; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_init); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci/** 5118c2ecf20Sopenharmony_ci * drm_fb_helper_alloc_fbi - allocate fb_info and some of its members 5128c2ecf20Sopenharmony_ci * @fb_helper: driver-allocated fbdev helper 5138c2ecf20Sopenharmony_ci * 5148c2ecf20Sopenharmony_ci * A helper to alloc fb_info and the members cmap and apertures. Called 5158c2ecf20Sopenharmony_ci * by the driver within the fb_probe fb_helper callback function. Drivers do not 5168c2ecf20Sopenharmony_ci * need to release the allocated fb_info structure themselves, this is 5178c2ecf20Sopenharmony_ci * automatically done when calling drm_fb_helper_fini(). 5188c2ecf20Sopenharmony_ci * 5198c2ecf20Sopenharmony_ci * RETURNS: 5208c2ecf20Sopenharmony_ci * fb_info pointer if things went okay, pointer containing error code 5218c2ecf20Sopenharmony_ci * otherwise 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_cistruct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct device *dev = fb_helper->dev->dev; 5268c2ecf20Sopenharmony_ci struct fb_info *info; 5278c2ecf20Sopenharmony_ci int ret; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci info = framebuffer_alloc(0, dev); 5308c2ecf20Sopenharmony_ci if (!info) 5318c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci ret = fb_alloc_cmap(&info->cmap, 256, 0); 5348c2ecf20Sopenharmony_ci if (ret) 5358c2ecf20Sopenharmony_ci goto err_release; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* 5388c2ecf20Sopenharmony_ci * TODO: We really should be smarter here and alloc an apperture 5398c2ecf20Sopenharmony_ci * for each IORESOURCE_MEM resource helper->dev->dev has and also 5408c2ecf20Sopenharmony_ci * init the ranges of the appertures based on the resources. 5418c2ecf20Sopenharmony_ci * Note some drivers currently count on there being only 1 empty 5428c2ecf20Sopenharmony_ci * aperture and fill this themselves, these will need to be dealt 5438c2ecf20Sopenharmony_ci * with somehow when fixing this. 5448c2ecf20Sopenharmony_ci */ 5458c2ecf20Sopenharmony_ci info->apertures = alloc_apertures(1); 5468c2ecf20Sopenharmony_ci if (!info->apertures) { 5478c2ecf20Sopenharmony_ci ret = -ENOMEM; 5488c2ecf20Sopenharmony_ci goto err_free_cmap; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci fb_helper->fbdev = info; 5528c2ecf20Sopenharmony_ci info->skip_vt_switch = true; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return info; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cierr_free_cmap: 5578c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 5588c2ecf20Sopenharmony_cierr_release: 5598c2ecf20Sopenharmony_ci framebuffer_release(info); 5608c2ecf20Sopenharmony_ci return ERR_PTR(ret); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_alloc_fbi); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci/** 5658c2ecf20Sopenharmony_ci * drm_fb_helper_unregister_fbi - unregister fb_info framebuffer device 5668c2ecf20Sopenharmony_ci * @fb_helper: driver-allocated fbdev helper, can be NULL 5678c2ecf20Sopenharmony_ci * 5688c2ecf20Sopenharmony_ci * A wrapper around unregister_framebuffer, to release the fb_info 5698c2ecf20Sopenharmony_ci * framebuffer device. This must be called before releasing all resources for 5708c2ecf20Sopenharmony_ci * @fb_helper by calling drm_fb_helper_fini(). 5718c2ecf20Sopenharmony_ci */ 5728c2ecf20Sopenharmony_civoid drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci if (fb_helper && fb_helper->fbdev) 5758c2ecf20Sopenharmony_ci unregister_framebuffer(fb_helper->fbdev); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_unregister_fbi); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci/** 5808c2ecf20Sopenharmony_ci * drm_fb_helper_fini - finialize a &struct drm_fb_helper 5818c2ecf20Sopenharmony_ci * @fb_helper: driver-allocated fbdev helper, can be NULL 5828c2ecf20Sopenharmony_ci * 5838c2ecf20Sopenharmony_ci * This cleans up all remaining resources associated with @fb_helper. 5848c2ecf20Sopenharmony_ci */ 5858c2ecf20Sopenharmony_civoid drm_fb_helper_fini(struct drm_fb_helper *fb_helper) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct fb_info *info; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (!fb_helper) 5908c2ecf20Sopenharmony_ci return; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci fb_helper->dev->fb_helper = NULL; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (!drm_fbdev_emulation) 5958c2ecf20Sopenharmony_ci return; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci cancel_work_sync(&fb_helper->resume_work); 5988c2ecf20Sopenharmony_ci cancel_work_sync(&fb_helper->dirty_work); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci info = fb_helper->fbdev; 6018c2ecf20Sopenharmony_ci if (info) { 6028c2ecf20Sopenharmony_ci if (info->cmap.len) 6038c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 6048c2ecf20Sopenharmony_ci framebuffer_release(info); 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci fb_helper->fbdev = NULL; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci mutex_lock(&kernel_fb_helper_lock); 6098c2ecf20Sopenharmony_ci if (!list_empty(&fb_helper->kernel_fb_list)) { 6108c2ecf20Sopenharmony_ci list_del(&fb_helper->kernel_fb_list); 6118c2ecf20Sopenharmony_ci if (list_empty(&kernel_fb_helper_list)) 6128c2ecf20Sopenharmony_ci unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci mutex_unlock(&kernel_fb_helper_lock); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci mutex_destroy(&fb_helper->lock); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (!fb_helper->client.funcs) 6198c2ecf20Sopenharmony_ci drm_client_release(&fb_helper->client); 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_fini); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic bool drm_fbdev_use_shadow_fb(struct drm_fb_helper *fb_helper) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct drm_device *dev = fb_helper->dev; 6268c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = fb_helper->fb; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci return dev->mode_config.prefer_shadow_fbdev || 6298c2ecf20Sopenharmony_ci dev->mode_config.prefer_shadow || 6308c2ecf20Sopenharmony_ci fb->funcs->dirty; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic void drm_fb_helper_dirty(struct fb_info *info, u32 x, u32 y, 6348c2ecf20Sopenharmony_ci u32 width, u32 height) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci struct drm_fb_helper *helper = info->par; 6378c2ecf20Sopenharmony_ci struct drm_clip_rect *clip = &helper->dirty_clip; 6388c2ecf20Sopenharmony_ci unsigned long flags; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (!drm_fbdev_use_shadow_fb(helper)) 6418c2ecf20Sopenharmony_ci return; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci spin_lock_irqsave(&helper->dirty_lock, flags); 6448c2ecf20Sopenharmony_ci clip->x1 = min_t(u32, clip->x1, x); 6458c2ecf20Sopenharmony_ci clip->y1 = min_t(u32, clip->y1, y); 6468c2ecf20Sopenharmony_ci clip->x2 = max_t(u32, clip->x2, x + width); 6478c2ecf20Sopenharmony_ci clip->y2 = max_t(u32, clip->y2, y + height); 6488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&helper->dirty_lock, flags); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci schedule_work(&helper->dirty_work); 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci/** 6548c2ecf20Sopenharmony_ci * drm_fb_helper_deferred_io() - fbdev deferred_io callback function 6558c2ecf20Sopenharmony_ci * @info: fb_info struct pointer 6568c2ecf20Sopenharmony_ci * @pagelist: list of dirty mmap framebuffer pages 6578c2ecf20Sopenharmony_ci * 6588c2ecf20Sopenharmony_ci * This function is used as the &fb_deferred_io.deferred_io 6598c2ecf20Sopenharmony_ci * callback function for flushing the fbdev mmap writes. 6608c2ecf20Sopenharmony_ci */ 6618c2ecf20Sopenharmony_civoid drm_fb_helper_deferred_io(struct fb_info *info, 6628c2ecf20Sopenharmony_ci struct list_head *pagelist) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci unsigned long start, end, min, max; 6658c2ecf20Sopenharmony_ci struct page *page; 6668c2ecf20Sopenharmony_ci u32 y1, y2; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci min = ULONG_MAX; 6698c2ecf20Sopenharmony_ci max = 0; 6708c2ecf20Sopenharmony_ci list_for_each_entry(page, pagelist, lru) { 6718c2ecf20Sopenharmony_ci start = page->index << PAGE_SHIFT; 6728c2ecf20Sopenharmony_ci end = start + PAGE_SIZE - 1; 6738c2ecf20Sopenharmony_ci min = min(min, start); 6748c2ecf20Sopenharmony_ci max = max(max, end); 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (min < max) { 6788c2ecf20Sopenharmony_ci y1 = min / info->fix.line_length; 6798c2ecf20Sopenharmony_ci y2 = min_t(u32, DIV_ROUND_UP(max, info->fix.line_length), 6808c2ecf20Sopenharmony_ci info->var.yres); 6818c2ecf20Sopenharmony_ci drm_fb_helper_dirty(info, 0, y1, info->var.xres, y2 - y1); 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_deferred_io); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci/** 6878c2ecf20Sopenharmony_ci * drm_fb_helper_sys_read - wrapper around fb_sys_read 6888c2ecf20Sopenharmony_ci * @info: fb_info struct pointer 6898c2ecf20Sopenharmony_ci * @buf: userspace buffer to read from framebuffer memory 6908c2ecf20Sopenharmony_ci * @count: number of bytes to read from framebuffer memory 6918c2ecf20Sopenharmony_ci * @ppos: read offset within framebuffer memory 6928c2ecf20Sopenharmony_ci * 6938c2ecf20Sopenharmony_ci * A wrapper around fb_sys_read implemented by fbdev core 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_cissize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf, 6968c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci return fb_sys_read(info, buf, count, ppos); 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_sys_read); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci/** 7038c2ecf20Sopenharmony_ci * drm_fb_helper_sys_write - wrapper around fb_sys_write 7048c2ecf20Sopenharmony_ci * @info: fb_info struct pointer 7058c2ecf20Sopenharmony_ci * @buf: userspace buffer to write to framebuffer memory 7068c2ecf20Sopenharmony_ci * @count: number of bytes to write to framebuffer memory 7078c2ecf20Sopenharmony_ci * @ppos: write offset within framebuffer memory 7088c2ecf20Sopenharmony_ci * 7098c2ecf20Sopenharmony_ci * A wrapper around fb_sys_write implemented by fbdev core 7108c2ecf20Sopenharmony_ci */ 7118c2ecf20Sopenharmony_cissize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf, 7128c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci ssize_t ret; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci ret = fb_sys_write(info, buf, count, ppos); 7178c2ecf20Sopenharmony_ci if (ret > 0) 7188c2ecf20Sopenharmony_ci drm_fb_helper_dirty(info, 0, 0, info->var.xres, 7198c2ecf20Sopenharmony_ci info->var.yres); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci return ret; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_sys_write); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci/** 7268c2ecf20Sopenharmony_ci * drm_fb_helper_sys_fillrect - wrapper around sys_fillrect 7278c2ecf20Sopenharmony_ci * @info: fbdev registered by the helper 7288c2ecf20Sopenharmony_ci * @rect: info about rectangle to fill 7298c2ecf20Sopenharmony_ci * 7308c2ecf20Sopenharmony_ci * A wrapper around sys_fillrect implemented by fbdev core 7318c2ecf20Sopenharmony_ci */ 7328c2ecf20Sopenharmony_civoid drm_fb_helper_sys_fillrect(struct fb_info *info, 7338c2ecf20Sopenharmony_ci const struct fb_fillrect *rect) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci sys_fillrect(info, rect); 7368c2ecf20Sopenharmony_ci drm_fb_helper_dirty(info, rect->dx, rect->dy, 7378c2ecf20Sopenharmony_ci rect->width, rect->height); 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_sys_fillrect); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci/** 7428c2ecf20Sopenharmony_ci * drm_fb_helper_sys_copyarea - wrapper around sys_copyarea 7438c2ecf20Sopenharmony_ci * @info: fbdev registered by the helper 7448c2ecf20Sopenharmony_ci * @area: info about area to copy 7458c2ecf20Sopenharmony_ci * 7468c2ecf20Sopenharmony_ci * A wrapper around sys_copyarea implemented by fbdev core 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_civoid drm_fb_helper_sys_copyarea(struct fb_info *info, 7498c2ecf20Sopenharmony_ci const struct fb_copyarea *area) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci sys_copyarea(info, area); 7528c2ecf20Sopenharmony_ci drm_fb_helper_dirty(info, area->dx, area->dy, 7538c2ecf20Sopenharmony_ci area->width, area->height); 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_sys_copyarea); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci/** 7588c2ecf20Sopenharmony_ci * drm_fb_helper_sys_imageblit - wrapper around sys_imageblit 7598c2ecf20Sopenharmony_ci * @info: fbdev registered by the helper 7608c2ecf20Sopenharmony_ci * @image: info about image to blit 7618c2ecf20Sopenharmony_ci * 7628c2ecf20Sopenharmony_ci * A wrapper around sys_imageblit implemented by fbdev core 7638c2ecf20Sopenharmony_ci */ 7648c2ecf20Sopenharmony_civoid drm_fb_helper_sys_imageblit(struct fb_info *info, 7658c2ecf20Sopenharmony_ci const struct fb_image *image) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci sys_imageblit(info, image); 7688c2ecf20Sopenharmony_ci drm_fb_helper_dirty(info, image->dx, image->dy, 7698c2ecf20Sopenharmony_ci image->width, image->height); 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_sys_imageblit); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci/** 7748c2ecf20Sopenharmony_ci * drm_fb_helper_cfb_fillrect - wrapper around cfb_fillrect 7758c2ecf20Sopenharmony_ci * @info: fbdev registered by the helper 7768c2ecf20Sopenharmony_ci * @rect: info about rectangle to fill 7778c2ecf20Sopenharmony_ci * 7788c2ecf20Sopenharmony_ci * A wrapper around cfb_fillrect implemented by fbdev core 7798c2ecf20Sopenharmony_ci */ 7808c2ecf20Sopenharmony_civoid drm_fb_helper_cfb_fillrect(struct fb_info *info, 7818c2ecf20Sopenharmony_ci const struct fb_fillrect *rect) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci cfb_fillrect(info, rect); 7848c2ecf20Sopenharmony_ci drm_fb_helper_dirty(info, rect->dx, rect->dy, 7858c2ecf20Sopenharmony_ci rect->width, rect->height); 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_cfb_fillrect); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci/** 7908c2ecf20Sopenharmony_ci * drm_fb_helper_cfb_copyarea - wrapper around cfb_copyarea 7918c2ecf20Sopenharmony_ci * @info: fbdev registered by the helper 7928c2ecf20Sopenharmony_ci * @area: info about area to copy 7938c2ecf20Sopenharmony_ci * 7948c2ecf20Sopenharmony_ci * A wrapper around cfb_copyarea implemented by fbdev core 7958c2ecf20Sopenharmony_ci */ 7968c2ecf20Sopenharmony_civoid drm_fb_helper_cfb_copyarea(struct fb_info *info, 7978c2ecf20Sopenharmony_ci const struct fb_copyarea *area) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci cfb_copyarea(info, area); 8008c2ecf20Sopenharmony_ci drm_fb_helper_dirty(info, area->dx, area->dy, 8018c2ecf20Sopenharmony_ci area->width, area->height); 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_cfb_copyarea); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci/** 8068c2ecf20Sopenharmony_ci * drm_fb_helper_cfb_imageblit - wrapper around cfb_imageblit 8078c2ecf20Sopenharmony_ci * @info: fbdev registered by the helper 8088c2ecf20Sopenharmony_ci * @image: info about image to blit 8098c2ecf20Sopenharmony_ci * 8108c2ecf20Sopenharmony_ci * A wrapper around cfb_imageblit implemented by fbdev core 8118c2ecf20Sopenharmony_ci */ 8128c2ecf20Sopenharmony_civoid drm_fb_helper_cfb_imageblit(struct fb_info *info, 8138c2ecf20Sopenharmony_ci const struct fb_image *image) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci cfb_imageblit(info, image); 8168c2ecf20Sopenharmony_ci drm_fb_helper_dirty(info, image->dx, image->dy, 8178c2ecf20Sopenharmony_ci image->width, image->height); 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_cfb_imageblit); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci/** 8228c2ecf20Sopenharmony_ci * drm_fb_helper_set_suspend - wrapper around fb_set_suspend 8238c2ecf20Sopenharmony_ci * @fb_helper: driver-allocated fbdev helper, can be NULL 8248c2ecf20Sopenharmony_ci * @suspend: whether to suspend or resume 8258c2ecf20Sopenharmony_ci * 8268c2ecf20Sopenharmony_ci * A wrapper around fb_set_suspend implemented by fbdev core. 8278c2ecf20Sopenharmony_ci * Use drm_fb_helper_set_suspend_unlocked() if you don't need to take 8288c2ecf20Sopenharmony_ci * the lock yourself 8298c2ecf20Sopenharmony_ci */ 8308c2ecf20Sopenharmony_civoid drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci if (fb_helper && fb_helper->fbdev) 8338c2ecf20Sopenharmony_ci fb_set_suspend(fb_helper->fbdev, suspend); 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_set_suspend); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci/** 8388c2ecf20Sopenharmony_ci * drm_fb_helper_set_suspend_unlocked - wrapper around fb_set_suspend that also 8398c2ecf20Sopenharmony_ci * takes the console lock 8408c2ecf20Sopenharmony_ci * @fb_helper: driver-allocated fbdev helper, can be NULL 8418c2ecf20Sopenharmony_ci * @suspend: whether to suspend or resume 8428c2ecf20Sopenharmony_ci * 8438c2ecf20Sopenharmony_ci * A wrapper around fb_set_suspend() that takes the console lock. If the lock 8448c2ecf20Sopenharmony_ci * isn't available on resume, a worker is tasked with waiting for the lock 8458c2ecf20Sopenharmony_ci * to become available. The console lock can be pretty contented on resume 8468c2ecf20Sopenharmony_ci * due to all the printk activity. 8478c2ecf20Sopenharmony_ci * 8488c2ecf20Sopenharmony_ci * This function can be called multiple times with the same state since 8498c2ecf20Sopenharmony_ci * &fb_info.state is checked to see if fbdev is running or not before locking. 8508c2ecf20Sopenharmony_ci * 8518c2ecf20Sopenharmony_ci * Use drm_fb_helper_set_suspend() if you need to take the lock yourself. 8528c2ecf20Sopenharmony_ci */ 8538c2ecf20Sopenharmony_civoid drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper, 8548c2ecf20Sopenharmony_ci bool suspend) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci if (!fb_helper || !fb_helper->fbdev) 8578c2ecf20Sopenharmony_ci return; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* make sure there's no pending/ongoing resume */ 8608c2ecf20Sopenharmony_ci flush_work(&fb_helper->resume_work); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if (suspend) { 8638c2ecf20Sopenharmony_ci if (fb_helper->fbdev->state != FBINFO_STATE_RUNNING) 8648c2ecf20Sopenharmony_ci return; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci console_lock(); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci } else { 8698c2ecf20Sopenharmony_ci if (fb_helper->fbdev->state == FBINFO_STATE_RUNNING) 8708c2ecf20Sopenharmony_ci return; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci if (!console_trylock()) { 8738c2ecf20Sopenharmony_ci schedule_work(&fb_helper->resume_work); 8748c2ecf20Sopenharmony_ci return; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci fb_set_suspend(fb_helper->fbdev, suspend); 8798c2ecf20Sopenharmony_ci console_unlock(); 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_cistatic int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci u32 *palette = (u32 *)info->pseudo_palette; 8868c2ecf20Sopenharmony_ci int i; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci if (cmap->start + cmap->len > 16) 8898c2ecf20Sopenharmony_ci return -EINVAL; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci for (i = 0; i < cmap->len; ++i) { 8928c2ecf20Sopenharmony_ci u16 red = cmap->red[i]; 8938c2ecf20Sopenharmony_ci u16 green = cmap->green[i]; 8948c2ecf20Sopenharmony_ci u16 blue = cmap->blue[i]; 8958c2ecf20Sopenharmony_ci u32 value; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci red >>= 16 - info->var.red.length; 8988c2ecf20Sopenharmony_ci green >>= 16 - info->var.green.length; 8998c2ecf20Sopenharmony_ci blue >>= 16 - info->var.blue.length; 9008c2ecf20Sopenharmony_ci value = (red << info->var.red.offset) | 9018c2ecf20Sopenharmony_ci (green << info->var.green.offset) | 9028c2ecf20Sopenharmony_ci (blue << info->var.blue.offset); 9038c2ecf20Sopenharmony_ci if (info->var.transp.length > 0) { 9048c2ecf20Sopenharmony_ci u32 mask = (1 << info->var.transp.length) - 1; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci mask <<= info->var.transp.offset; 9078c2ecf20Sopenharmony_ci value |= mask; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci palette[cmap->start + i] = value; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci return 0; 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cistatic int setcmap_legacy(struct fb_cmap *cmap, struct fb_info *info) 9168c2ecf20Sopenharmony_ci{ 9178c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 9188c2ecf20Sopenharmony_ci struct drm_mode_set *modeset; 9198c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 9208c2ecf20Sopenharmony_ci u16 *r, *g, *b; 9218c2ecf20Sopenharmony_ci int ret = 0; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci drm_modeset_lock_all(fb_helper->dev); 9248c2ecf20Sopenharmony_ci drm_client_for_each_modeset(modeset, &fb_helper->client) { 9258c2ecf20Sopenharmony_ci crtc = modeset->crtc; 9268c2ecf20Sopenharmony_ci if (!crtc->funcs->gamma_set || !crtc->gamma_size) { 9278c2ecf20Sopenharmony_ci ret = -EINVAL; 9288c2ecf20Sopenharmony_ci goto out; 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci if (cmap->start + cmap->len > crtc->gamma_size) { 9328c2ecf20Sopenharmony_ci ret = -EINVAL; 9338c2ecf20Sopenharmony_ci goto out; 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci r = crtc->gamma_store; 9378c2ecf20Sopenharmony_ci g = r + crtc->gamma_size; 9388c2ecf20Sopenharmony_ci b = g + crtc->gamma_size; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r)); 9418c2ecf20Sopenharmony_ci memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g)); 9428c2ecf20Sopenharmony_ci memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b)); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci ret = crtc->funcs->gamma_set(crtc, r, g, b, 9458c2ecf20Sopenharmony_ci crtc->gamma_size, NULL); 9468c2ecf20Sopenharmony_ci if (ret) 9478c2ecf20Sopenharmony_ci goto out; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ciout: 9508c2ecf20Sopenharmony_ci drm_modeset_unlock_all(fb_helper->dev); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci return ret; 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cistatic struct drm_property_blob *setcmap_new_gamma_lut(struct drm_crtc *crtc, 9568c2ecf20Sopenharmony_ci struct fb_cmap *cmap) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 9598c2ecf20Sopenharmony_ci struct drm_property_blob *gamma_lut; 9608c2ecf20Sopenharmony_ci struct drm_color_lut *lut; 9618c2ecf20Sopenharmony_ci int size = crtc->gamma_size; 9628c2ecf20Sopenharmony_ci int i; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (!size || cmap->start + cmap->len > size) 9658c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci gamma_lut = drm_property_create_blob(dev, sizeof(*lut) * size, NULL); 9688c2ecf20Sopenharmony_ci if (IS_ERR(gamma_lut)) 9698c2ecf20Sopenharmony_ci return gamma_lut; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci lut = gamma_lut->data; 9728c2ecf20Sopenharmony_ci if (cmap->start || cmap->len != size) { 9738c2ecf20Sopenharmony_ci u16 *r = crtc->gamma_store; 9748c2ecf20Sopenharmony_ci u16 *g = r + crtc->gamma_size; 9758c2ecf20Sopenharmony_ci u16 *b = g + crtc->gamma_size; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci for (i = 0; i < cmap->start; i++) { 9788c2ecf20Sopenharmony_ci lut[i].red = r[i]; 9798c2ecf20Sopenharmony_ci lut[i].green = g[i]; 9808c2ecf20Sopenharmony_ci lut[i].blue = b[i]; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci for (i = cmap->start + cmap->len; i < size; i++) { 9838c2ecf20Sopenharmony_ci lut[i].red = r[i]; 9848c2ecf20Sopenharmony_ci lut[i].green = g[i]; 9858c2ecf20Sopenharmony_ci lut[i].blue = b[i]; 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci for (i = 0; i < cmap->len; i++) { 9908c2ecf20Sopenharmony_ci lut[cmap->start + i].red = cmap->red[i]; 9918c2ecf20Sopenharmony_ci lut[cmap->start + i].green = cmap->green[i]; 9928c2ecf20Sopenharmony_ci lut[cmap->start + i].blue = cmap->blue[i]; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci return gamma_lut; 9968c2ecf20Sopenharmony_ci} 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_cistatic int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info) 9998c2ecf20Sopenharmony_ci{ 10008c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 10018c2ecf20Sopenharmony_ci struct drm_device *dev = fb_helper->dev; 10028c2ecf20Sopenharmony_ci struct drm_property_blob *gamma_lut = NULL; 10038c2ecf20Sopenharmony_ci struct drm_modeset_acquire_ctx ctx; 10048c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 10058c2ecf20Sopenharmony_ci struct drm_atomic_state *state; 10068c2ecf20Sopenharmony_ci struct drm_mode_set *modeset; 10078c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 10088c2ecf20Sopenharmony_ci u16 *r, *g, *b; 10098c2ecf20Sopenharmony_ci bool replaced; 10108c2ecf20Sopenharmony_ci int ret = 0; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci drm_modeset_acquire_init(&ctx, 0); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci state = drm_atomic_state_alloc(dev); 10158c2ecf20Sopenharmony_ci if (!state) { 10168c2ecf20Sopenharmony_ci ret = -ENOMEM; 10178c2ecf20Sopenharmony_ci goto out_ctx; 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci state->acquire_ctx = &ctx; 10218c2ecf20Sopenharmony_ciretry: 10228c2ecf20Sopenharmony_ci drm_client_for_each_modeset(modeset, &fb_helper->client) { 10238c2ecf20Sopenharmony_ci crtc = modeset->crtc; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci if (!gamma_lut) 10268c2ecf20Sopenharmony_ci gamma_lut = setcmap_new_gamma_lut(crtc, cmap); 10278c2ecf20Sopenharmony_ci if (IS_ERR(gamma_lut)) { 10288c2ecf20Sopenharmony_ci ret = PTR_ERR(gamma_lut); 10298c2ecf20Sopenharmony_ci gamma_lut = NULL; 10308c2ecf20Sopenharmony_ci goto out_state; 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_crtc_state(state, crtc); 10348c2ecf20Sopenharmony_ci if (IS_ERR(crtc_state)) { 10358c2ecf20Sopenharmony_ci ret = PTR_ERR(crtc_state); 10368c2ecf20Sopenharmony_ci goto out_state; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci replaced = drm_property_replace_blob(&crtc_state->degamma_lut, 10408c2ecf20Sopenharmony_ci NULL); 10418c2ecf20Sopenharmony_ci replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL); 10428c2ecf20Sopenharmony_ci replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, 10438c2ecf20Sopenharmony_ci gamma_lut); 10448c2ecf20Sopenharmony_ci crtc_state->color_mgmt_changed |= replaced; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci ret = drm_atomic_commit(state); 10488c2ecf20Sopenharmony_ci if (ret) 10498c2ecf20Sopenharmony_ci goto out_state; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci drm_client_for_each_modeset(modeset, &fb_helper->client) { 10528c2ecf20Sopenharmony_ci crtc = modeset->crtc; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci r = crtc->gamma_store; 10558c2ecf20Sopenharmony_ci g = r + crtc->gamma_size; 10568c2ecf20Sopenharmony_ci b = g + crtc->gamma_size; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r)); 10598c2ecf20Sopenharmony_ci memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g)); 10608c2ecf20Sopenharmony_ci memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b)); 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ciout_state: 10648c2ecf20Sopenharmony_ci if (ret == -EDEADLK) 10658c2ecf20Sopenharmony_ci goto backoff; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci drm_property_blob_put(gamma_lut); 10688c2ecf20Sopenharmony_ci drm_atomic_state_put(state); 10698c2ecf20Sopenharmony_ciout_ctx: 10708c2ecf20Sopenharmony_ci drm_modeset_drop_locks(&ctx); 10718c2ecf20Sopenharmony_ci drm_modeset_acquire_fini(&ctx); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci return ret; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cibackoff: 10768c2ecf20Sopenharmony_ci drm_atomic_state_clear(state); 10778c2ecf20Sopenharmony_ci drm_modeset_backoff(&ctx); 10788c2ecf20Sopenharmony_ci goto retry; 10798c2ecf20Sopenharmony_ci} 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci/** 10828c2ecf20Sopenharmony_ci * drm_fb_helper_setcmap - implementation for &fb_ops.fb_setcmap 10838c2ecf20Sopenharmony_ci * @cmap: cmap to set 10848c2ecf20Sopenharmony_ci * @info: fbdev registered by the helper 10858c2ecf20Sopenharmony_ci */ 10868c2ecf20Sopenharmony_ciint drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) 10878c2ecf20Sopenharmony_ci{ 10888c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 10898c2ecf20Sopenharmony_ci struct drm_device *dev = fb_helper->dev; 10908c2ecf20Sopenharmony_ci int ret; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (oops_in_progress) 10938c2ecf20Sopenharmony_ci return -EBUSY; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci mutex_lock(&fb_helper->lock); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci if (!drm_master_internal_acquire(dev)) { 10988c2ecf20Sopenharmony_ci ret = -EBUSY; 10998c2ecf20Sopenharmony_ci goto unlock; 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci mutex_lock(&fb_helper->client.modeset_mutex); 11038c2ecf20Sopenharmony_ci if (info->fix.visual == FB_VISUAL_TRUECOLOR) 11048c2ecf20Sopenharmony_ci ret = setcmap_pseudo_palette(cmap, info); 11058c2ecf20Sopenharmony_ci else if (drm_drv_uses_atomic_modeset(fb_helper->dev)) 11068c2ecf20Sopenharmony_ci ret = setcmap_atomic(cmap, info); 11078c2ecf20Sopenharmony_ci else 11088c2ecf20Sopenharmony_ci ret = setcmap_legacy(cmap, info); 11098c2ecf20Sopenharmony_ci mutex_unlock(&fb_helper->client.modeset_mutex); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci drm_master_internal_release(dev); 11128c2ecf20Sopenharmony_ciunlock: 11138c2ecf20Sopenharmony_ci mutex_unlock(&fb_helper->lock); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci return ret; 11168c2ecf20Sopenharmony_ci} 11178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_setcmap); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci/** 11208c2ecf20Sopenharmony_ci * drm_fb_helper_ioctl - legacy ioctl implementation 11218c2ecf20Sopenharmony_ci * @info: fbdev registered by the helper 11228c2ecf20Sopenharmony_ci * @cmd: ioctl command 11238c2ecf20Sopenharmony_ci * @arg: ioctl argument 11248c2ecf20Sopenharmony_ci * 11258c2ecf20Sopenharmony_ci * A helper to implement the standard fbdev ioctl. Only 11268c2ecf20Sopenharmony_ci * FBIO_WAITFORVSYNC is implemented for now. 11278c2ecf20Sopenharmony_ci */ 11288c2ecf20Sopenharmony_ciint drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, 11298c2ecf20Sopenharmony_ci unsigned long arg) 11308c2ecf20Sopenharmony_ci{ 11318c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 11328c2ecf20Sopenharmony_ci struct drm_device *dev = fb_helper->dev; 11338c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 11348c2ecf20Sopenharmony_ci int ret = 0; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci mutex_lock(&fb_helper->lock); 11378c2ecf20Sopenharmony_ci if (!drm_master_internal_acquire(dev)) { 11388c2ecf20Sopenharmony_ci ret = -EBUSY; 11398c2ecf20Sopenharmony_ci goto unlock; 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci switch (cmd) { 11438c2ecf20Sopenharmony_ci case FBIO_WAITFORVSYNC: 11448c2ecf20Sopenharmony_ci /* 11458c2ecf20Sopenharmony_ci * Only consider the first CRTC. 11468c2ecf20Sopenharmony_ci * 11478c2ecf20Sopenharmony_ci * This ioctl is supposed to take the CRTC number as 11488c2ecf20Sopenharmony_ci * an argument, but in fbdev times, what that number 11498c2ecf20Sopenharmony_ci * was supposed to be was quite unclear, different 11508c2ecf20Sopenharmony_ci * drivers were passing that argument differently 11518c2ecf20Sopenharmony_ci * (some by reference, some by value), and most of the 11528c2ecf20Sopenharmony_ci * userspace applications were just hardcoding 0 as an 11538c2ecf20Sopenharmony_ci * argument. 11548c2ecf20Sopenharmony_ci * 11558c2ecf20Sopenharmony_ci * The first CRTC should be the integrated panel on 11568c2ecf20Sopenharmony_ci * most drivers, so this is the best choice we can 11578c2ecf20Sopenharmony_ci * make. If we're not smart enough here, one should 11588c2ecf20Sopenharmony_ci * just consider switch the userspace to KMS. 11598c2ecf20Sopenharmony_ci */ 11608c2ecf20Sopenharmony_ci crtc = fb_helper->client.modesets[0].crtc; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* 11638c2ecf20Sopenharmony_ci * Only wait for a vblank event if the CRTC is 11648c2ecf20Sopenharmony_ci * enabled, otherwise just don't do anythintg, 11658c2ecf20Sopenharmony_ci * not even report an error. 11668c2ecf20Sopenharmony_ci */ 11678c2ecf20Sopenharmony_ci ret = drm_crtc_vblank_get(crtc); 11688c2ecf20Sopenharmony_ci if (!ret) { 11698c2ecf20Sopenharmony_ci drm_crtc_wait_one_vblank(crtc); 11708c2ecf20Sopenharmony_ci drm_crtc_vblank_put(crtc); 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci ret = 0; 11748c2ecf20Sopenharmony_ci break; 11758c2ecf20Sopenharmony_ci default: 11768c2ecf20Sopenharmony_ci ret = -ENOTTY; 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci drm_master_internal_release(dev); 11808c2ecf20Sopenharmony_ciunlock: 11818c2ecf20Sopenharmony_ci mutex_unlock(&fb_helper->lock); 11828c2ecf20Sopenharmony_ci return ret; 11838c2ecf20Sopenharmony_ci} 11848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_ioctl); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cistatic bool drm_fb_pixel_format_equal(const struct fb_var_screeninfo *var_1, 11878c2ecf20Sopenharmony_ci const struct fb_var_screeninfo *var_2) 11888c2ecf20Sopenharmony_ci{ 11898c2ecf20Sopenharmony_ci return var_1->bits_per_pixel == var_2->bits_per_pixel && 11908c2ecf20Sopenharmony_ci var_1->grayscale == var_2->grayscale && 11918c2ecf20Sopenharmony_ci var_1->red.offset == var_2->red.offset && 11928c2ecf20Sopenharmony_ci var_1->red.length == var_2->red.length && 11938c2ecf20Sopenharmony_ci var_1->red.msb_right == var_2->red.msb_right && 11948c2ecf20Sopenharmony_ci var_1->green.offset == var_2->green.offset && 11958c2ecf20Sopenharmony_ci var_1->green.length == var_2->green.length && 11968c2ecf20Sopenharmony_ci var_1->green.msb_right == var_2->green.msb_right && 11978c2ecf20Sopenharmony_ci var_1->blue.offset == var_2->blue.offset && 11988c2ecf20Sopenharmony_ci var_1->blue.length == var_2->blue.length && 11998c2ecf20Sopenharmony_ci var_1->blue.msb_right == var_2->blue.msb_right && 12008c2ecf20Sopenharmony_ci var_1->transp.offset == var_2->transp.offset && 12018c2ecf20Sopenharmony_ci var_1->transp.length == var_2->transp.length && 12028c2ecf20Sopenharmony_ci var_1->transp.msb_right == var_2->transp.msb_right; 12038c2ecf20Sopenharmony_ci} 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_cistatic void drm_fb_helper_fill_pixel_fmt(struct fb_var_screeninfo *var, 12068c2ecf20Sopenharmony_ci u8 depth) 12078c2ecf20Sopenharmony_ci{ 12088c2ecf20Sopenharmony_ci switch (depth) { 12098c2ecf20Sopenharmony_ci case 8: 12108c2ecf20Sopenharmony_ci var->red.offset = 0; 12118c2ecf20Sopenharmony_ci var->green.offset = 0; 12128c2ecf20Sopenharmony_ci var->blue.offset = 0; 12138c2ecf20Sopenharmony_ci var->red.length = 8; /* 8bit DAC */ 12148c2ecf20Sopenharmony_ci var->green.length = 8; 12158c2ecf20Sopenharmony_ci var->blue.length = 8; 12168c2ecf20Sopenharmony_ci var->transp.offset = 0; 12178c2ecf20Sopenharmony_ci var->transp.length = 0; 12188c2ecf20Sopenharmony_ci break; 12198c2ecf20Sopenharmony_ci case 15: 12208c2ecf20Sopenharmony_ci var->red.offset = 10; 12218c2ecf20Sopenharmony_ci var->green.offset = 5; 12228c2ecf20Sopenharmony_ci var->blue.offset = 0; 12238c2ecf20Sopenharmony_ci var->red.length = 5; 12248c2ecf20Sopenharmony_ci var->green.length = 5; 12258c2ecf20Sopenharmony_ci var->blue.length = 5; 12268c2ecf20Sopenharmony_ci var->transp.offset = 15; 12278c2ecf20Sopenharmony_ci var->transp.length = 1; 12288c2ecf20Sopenharmony_ci break; 12298c2ecf20Sopenharmony_ci case 16: 12308c2ecf20Sopenharmony_ci var->red.offset = 11; 12318c2ecf20Sopenharmony_ci var->green.offset = 5; 12328c2ecf20Sopenharmony_ci var->blue.offset = 0; 12338c2ecf20Sopenharmony_ci var->red.length = 5; 12348c2ecf20Sopenharmony_ci var->green.length = 6; 12358c2ecf20Sopenharmony_ci var->blue.length = 5; 12368c2ecf20Sopenharmony_ci var->transp.offset = 0; 12378c2ecf20Sopenharmony_ci break; 12388c2ecf20Sopenharmony_ci case 24: 12398c2ecf20Sopenharmony_ci var->red.offset = 16; 12408c2ecf20Sopenharmony_ci var->green.offset = 8; 12418c2ecf20Sopenharmony_ci var->blue.offset = 0; 12428c2ecf20Sopenharmony_ci var->red.length = 8; 12438c2ecf20Sopenharmony_ci var->green.length = 8; 12448c2ecf20Sopenharmony_ci var->blue.length = 8; 12458c2ecf20Sopenharmony_ci var->transp.offset = 0; 12468c2ecf20Sopenharmony_ci var->transp.length = 0; 12478c2ecf20Sopenharmony_ci break; 12488c2ecf20Sopenharmony_ci case 32: 12498c2ecf20Sopenharmony_ci var->red.offset = 16; 12508c2ecf20Sopenharmony_ci var->green.offset = 8; 12518c2ecf20Sopenharmony_ci var->blue.offset = 0; 12528c2ecf20Sopenharmony_ci var->red.length = 8; 12538c2ecf20Sopenharmony_ci var->green.length = 8; 12548c2ecf20Sopenharmony_ci var->blue.length = 8; 12558c2ecf20Sopenharmony_ci var->transp.offset = 24; 12568c2ecf20Sopenharmony_ci var->transp.length = 8; 12578c2ecf20Sopenharmony_ci break; 12588c2ecf20Sopenharmony_ci default: 12598c2ecf20Sopenharmony_ci break; 12608c2ecf20Sopenharmony_ci } 12618c2ecf20Sopenharmony_ci} 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci/** 12648c2ecf20Sopenharmony_ci * drm_fb_helper_check_var - implementation for &fb_ops.fb_check_var 12658c2ecf20Sopenharmony_ci * @var: screeninfo to check 12668c2ecf20Sopenharmony_ci * @info: fbdev registered by the helper 12678c2ecf20Sopenharmony_ci */ 12688c2ecf20Sopenharmony_ciint drm_fb_helper_check_var(struct fb_var_screeninfo *var, 12698c2ecf20Sopenharmony_ci struct fb_info *info) 12708c2ecf20Sopenharmony_ci{ 12718c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 12728c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = fb_helper->fb; 12738c2ecf20Sopenharmony_ci struct drm_device *dev = fb_helper->dev; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci if (in_dbg_master()) 12768c2ecf20Sopenharmony_ci return -EINVAL; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci if (var->pixclock != 0) { 12798c2ecf20Sopenharmony_ci drm_dbg_kms(dev, "fbdev emulation doesn't support changing the pixel clock, value of pixclock is ignored\n"); 12808c2ecf20Sopenharmony_ci var->pixclock = 0; 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci if ((drm_format_info_block_width(fb->format, 0) > 1) || 12848c2ecf20Sopenharmony_ci (drm_format_info_block_height(fb->format, 0) > 1)) 12858c2ecf20Sopenharmony_ci return -EINVAL; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci /* 12888c2ecf20Sopenharmony_ci * Changes struct fb_var_screeninfo are currently not pushed back 12898c2ecf20Sopenharmony_ci * to KMS, hence fail if different settings are requested. 12908c2ecf20Sopenharmony_ci */ 12918c2ecf20Sopenharmony_ci if (var->bits_per_pixel > fb->format->cpp[0] * 8 || 12928c2ecf20Sopenharmony_ci var->xres > fb->width || var->yres > fb->height || 12938c2ecf20Sopenharmony_ci var->xres_virtual > fb->width || var->yres_virtual > fb->height) { 12948c2ecf20Sopenharmony_ci drm_dbg_kms(dev, "fb requested width/height/bpp can't fit in current fb " 12958c2ecf20Sopenharmony_ci "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n", 12968c2ecf20Sopenharmony_ci var->xres, var->yres, var->bits_per_pixel, 12978c2ecf20Sopenharmony_ci var->xres_virtual, var->yres_virtual, 12988c2ecf20Sopenharmony_ci fb->width, fb->height, fb->format->cpp[0] * 8); 12998c2ecf20Sopenharmony_ci return -EINVAL; 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci var->xres_virtual = fb->width; 13038c2ecf20Sopenharmony_ci var->yres_virtual = fb->height; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci /* 13068c2ecf20Sopenharmony_ci * Workaround for SDL 1.2, which is known to be setting all pixel format 13078c2ecf20Sopenharmony_ci * fields values to zero in some cases. We treat this situation as a 13088c2ecf20Sopenharmony_ci * kind of "use some reasonable autodetected values". 13098c2ecf20Sopenharmony_ci */ 13108c2ecf20Sopenharmony_ci if (!var->red.offset && !var->green.offset && 13118c2ecf20Sopenharmony_ci !var->blue.offset && !var->transp.offset && 13128c2ecf20Sopenharmony_ci !var->red.length && !var->green.length && 13138c2ecf20Sopenharmony_ci !var->blue.length && !var->transp.length && 13148c2ecf20Sopenharmony_ci !var->red.msb_right && !var->green.msb_right && 13158c2ecf20Sopenharmony_ci !var->blue.msb_right && !var->transp.msb_right) { 13168c2ecf20Sopenharmony_ci drm_fb_helper_fill_pixel_fmt(var, fb->format->depth); 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci /* 13208c2ecf20Sopenharmony_ci * Likewise, bits_per_pixel should be rounded up to a supported value. 13218c2ecf20Sopenharmony_ci */ 13228c2ecf20Sopenharmony_ci var->bits_per_pixel = fb->format->cpp[0] * 8; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci /* 13258c2ecf20Sopenharmony_ci * drm fbdev emulation doesn't support changing the pixel format at all, 13268c2ecf20Sopenharmony_ci * so reject all pixel format changing requests. 13278c2ecf20Sopenharmony_ci */ 13288c2ecf20Sopenharmony_ci if (!drm_fb_pixel_format_equal(var, &info->var)) { 13298c2ecf20Sopenharmony_ci drm_dbg_kms(dev, "fbdev emulation doesn't support changing the pixel format\n"); 13308c2ecf20Sopenharmony_ci return -EINVAL; 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci return 0; 13348c2ecf20Sopenharmony_ci} 13358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_check_var); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci/** 13388c2ecf20Sopenharmony_ci * drm_fb_helper_set_par - implementation for &fb_ops.fb_set_par 13398c2ecf20Sopenharmony_ci * @info: fbdev registered by the helper 13408c2ecf20Sopenharmony_ci * 13418c2ecf20Sopenharmony_ci * This will let fbcon do the mode init and is called at initialization time by 13428c2ecf20Sopenharmony_ci * the fbdev core when registering the driver, and later on through the hotplug 13438c2ecf20Sopenharmony_ci * callback. 13448c2ecf20Sopenharmony_ci */ 13458c2ecf20Sopenharmony_ciint drm_fb_helper_set_par(struct fb_info *info) 13468c2ecf20Sopenharmony_ci{ 13478c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 13488c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 13498c2ecf20Sopenharmony_ci bool force; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci if (oops_in_progress) 13528c2ecf20Sopenharmony_ci return -EBUSY; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci if (var->pixclock != 0) { 13558c2ecf20Sopenharmony_ci drm_err(fb_helper->dev, "PIXEL CLOCK SET\n"); 13568c2ecf20Sopenharmony_ci return -EINVAL; 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci /* 13608c2ecf20Sopenharmony_ci * Normally we want to make sure that a kms master takes precedence over 13618c2ecf20Sopenharmony_ci * fbdev, to avoid fbdev flickering and occasionally stealing the 13628c2ecf20Sopenharmony_ci * display status. But Xorg first sets the vt back to text mode using 13638c2ecf20Sopenharmony_ci * the KDSET IOCTL with KD_TEXT, and only after that drops the master 13648c2ecf20Sopenharmony_ci * status when exiting. 13658c2ecf20Sopenharmony_ci * 13668c2ecf20Sopenharmony_ci * In the past this was caught by drm_fb_helper_lastclose(), but on 13678c2ecf20Sopenharmony_ci * modern systems where logind always keeps a drm fd open to orchestrate 13688c2ecf20Sopenharmony_ci * the vt switching, this doesn't work. 13698c2ecf20Sopenharmony_ci * 13708c2ecf20Sopenharmony_ci * To not break the userspace ABI we have this special case here, which 13718c2ecf20Sopenharmony_ci * is only used for the above case. Everything else uses the normal 13728c2ecf20Sopenharmony_ci * commit function, which ensures that we never steal the display from 13738c2ecf20Sopenharmony_ci * an active drm master. 13748c2ecf20Sopenharmony_ci */ 13758c2ecf20Sopenharmony_ci force = var->activate & FB_ACTIVATE_KD_TEXT; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci return 0; 13808c2ecf20Sopenharmony_ci} 13818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_set_par); 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cistatic void pan_set(struct drm_fb_helper *fb_helper, int x, int y) 13848c2ecf20Sopenharmony_ci{ 13858c2ecf20Sopenharmony_ci struct drm_mode_set *mode_set; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci mutex_lock(&fb_helper->client.modeset_mutex); 13888c2ecf20Sopenharmony_ci drm_client_for_each_modeset(mode_set, &fb_helper->client) { 13898c2ecf20Sopenharmony_ci mode_set->x = x; 13908c2ecf20Sopenharmony_ci mode_set->y = y; 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci mutex_unlock(&fb_helper->client.modeset_mutex); 13938c2ecf20Sopenharmony_ci} 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_cistatic int pan_display_atomic(struct fb_var_screeninfo *var, 13968c2ecf20Sopenharmony_ci struct fb_info *info) 13978c2ecf20Sopenharmony_ci{ 13988c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 13998c2ecf20Sopenharmony_ci int ret; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci pan_set(fb_helper, var->xoffset, var->yoffset); 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci ret = drm_client_modeset_commit_locked(&fb_helper->client); 14048c2ecf20Sopenharmony_ci if (!ret) { 14058c2ecf20Sopenharmony_ci info->var.xoffset = var->xoffset; 14068c2ecf20Sopenharmony_ci info->var.yoffset = var->yoffset; 14078c2ecf20Sopenharmony_ci } else 14088c2ecf20Sopenharmony_ci pan_set(fb_helper, info->var.xoffset, info->var.yoffset); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci return ret; 14118c2ecf20Sopenharmony_ci} 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_cistatic int pan_display_legacy(struct fb_var_screeninfo *var, 14148c2ecf20Sopenharmony_ci struct fb_info *info) 14158c2ecf20Sopenharmony_ci{ 14168c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 14178c2ecf20Sopenharmony_ci struct drm_client_dev *client = &fb_helper->client; 14188c2ecf20Sopenharmony_ci struct drm_mode_set *modeset; 14198c2ecf20Sopenharmony_ci int ret = 0; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci mutex_lock(&client->modeset_mutex); 14228c2ecf20Sopenharmony_ci drm_modeset_lock_all(fb_helper->dev); 14238c2ecf20Sopenharmony_ci drm_client_for_each_modeset(modeset, client) { 14248c2ecf20Sopenharmony_ci modeset->x = var->xoffset; 14258c2ecf20Sopenharmony_ci modeset->y = var->yoffset; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci if (modeset->num_connectors) { 14288c2ecf20Sopenharmony_ci ret = drm_mode_set_config_internal(modeset); 14298c2ecf20Sopenharmony_ci if (!ret) { 14308c2ecf20Sopenharmony_ci info->var.xoffset = var->xoffset; 14318c2ecf20Sopenharmony_ci info->var.yoffset = var->yoffset; 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci } 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci drm_modeset_unlock_all(fb_helper->dev); 14368c2ecf20Sopenharmony_ci mutex_unlock(&client->modeset_mutex); 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci return ret; 14398c2ecf20Sopenharmony_ci} 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci/** 14428c2ecf20Sopenharmony_ci * drm_fb_helper_pan_display - implementation for &fb_ops.fb_pan_display 14438c2ecf20Sopenharmony_ci * @var: updated screen information 14448c2ecf20Sopenharmony_ci * @info: fbdev registered by the helper 14458c2ecf20Sopenharmony_ci */ 14468c2ecf20Sopenharmony_ciint drm_fb_helper_pan_display(struct fb_var_screeninfo *var, 14478c2ecf20Sopenharmony_ci struct fb_info *info) 14488c2ecf20Sopenharmony_ci{ 14498c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 14508c2ecf20Sopenharmony_ci struct drm_device *dev = fb_helper->dev; 14518c2ecf20Sopenharmony_ci int ret; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci if (oops_in_progress) 14548c2ecf20Sopenharmony_ci return -EBUSY; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci mutex_lock(&fb_helper->lock); 14578c2ecf20Sopenharmony_ci if (!drm_master_internal_acquire(dev)) { 14588c2ecf20Sopenharmony_ci ret = -EBUSY; 14598c2ecf20Sopenharmony_ci goto unlock; 14608c2ecf20Sopenharmony_ci } 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci if (drm_drv_uses_atomic_modeset(dev)) 14638c2ecf20Sopenharmony_ci ret = pan_display_atomic(var, info); 14648c2ecf20Sopenharmony_ci else 14658c2ecf20Sopenharmony_ci ret = pan_display_legacy(var, info); 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci drm_master_internal_release(dev); 14688c2ecf20Sopenharmony_ciunlock: 14698c2ecf20Sopenharmony_ci mutex_unlock(&fb_helper->lock); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci return ret; 14728c2ecf20Sopenharmony_ci} 14738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_pan_display); 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci/* 14768c2ecf20Sopenharmony_ci * Allocates the backing storage and sets up the fbdev info structure through 14778c2ecf20Sopenharmony_ci * the ->fb_probe callback. 14788c2ecf20Sopenharmony_ci */ 14798c2ecf20Sopenharmony_cistatic int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, 14808c2ecf20Sopenharmony_ci int preferred_bpp) 14818c2ecf20Sopenharmony_ci{ 14828c2ecf20Sopenharmony_ci struct drm_client_dev *client = &fb_helper->client; 14838c2ecf20Sopenharmony_ci struct drm_device *dev = fb_helper->dev; 14848c2ecf20Sopenharmony_ci int ret = 0; 14858c2ecf20Sopenharmony_ci int crtc_count = 0; 14868c2ecf20Sopenharmony_ci struct drm_connector_list_iter conn_iter; 14878c2ecf20Sopenharmony_ci struct drm_fb_helper_surface_size sizes; 14888c2ecf20Sopenharmony_ci struct drm_connector *connector; 14898c2ecf20Sopenharmony_ci struct drm_mode_set *mode_set; 14908c2ecf20Sopenharmony_ci int best_depth = 0; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); 14938c2ecf20Sopenharmony_ci sizes.surface_depth = 24; 14948c2ecf20Sopenharmony_ci sizes.surface_bpp = 32; 14958c2ecf20Sopenharmony_ci sizes.fb_width = (u32)-1; 14968c2ecf20Sopenharmony_ci sizes.fb_height = (u32)-1; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci /* 14998c2ecf20Sopenharmony_ci * If driver picks 8 or 16 by default use that for both depth/bpp 15008c2ecf20Sopenharmony_ci * to begin with 15018c2ecf20Sopenharmony_ci */ 15028c2ecf20Sopenharmony_ci if (preferred_bpp != sizes.surface_bpp) 15038c2ecf20Sopenharmony_ci sizes.surface_depth = sizes.surface_bpp = preferred_bpp; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci drm_connector_list_iter_begin(fb_helper->dev, &conn_iter); 15068c2ecf20Sopenharmony_ci drm_client_for_each_connector_iter(connector, &conn_iter) { 15078c2ecf20Sopenharmony_ci struct drm_cmdline_mode *cmdline_mode; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci cmdline_mode = &connector->cmdline_mode; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci if (cmdline_mode->bpp_specified) { 15128c2ecf20Sopenharmony_ci switch (cmdline_mode->bpp) { 15138c2ecf20Sopenharmony_ci case 8: 15148c2ecf20Sopenharmony_ci sizes.surface_depth = sizes.surface_bpp = 8; 15158c2ecf20Sopenharmony_ci break; 15168c2ecf20Sopenharmony_ci case 15: 15178c2ecf20Sopenharmony_ci sizes.surface_depth = 15; 15188c2ecf20Sopenharmony_ci sizes.surface_bpp = 16; 15198c2ecf20Sopenharmony_ci break; 15208c2ecf20Sopenharmony_ci case 16: 15218c2ecf20Sopenharmony_ci sizes.surface_depth = sizes.surface_bpp = 16; 15228c2ecf20Sopenharmony_ci break; 15238c2ecf20Sopenharmony_ci case 24: 15248c2ecf20Sopenharmony_ci sizes.surface_depth = sizes.surface_bpp = 24; 15258c2ecf20Sopenharmony_ci break; 15268c2ecf20Sopenharmony_ci case 32: 15278c2ecf20Sopenharmony_ci sizes.surface_depth = 24; 15288c2ecf20Sopenharmony_ci sizes.surface_bpp = 32; 15298c2ecf20Sopenharmony_ci break; 15308c2ecf20Sopenharmony_ci } 15318c2ecf20Sopenharmony_ci break; 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* 15378c2ecf20Sopenharmony_ci * If we run into a situation where, for example, the primary plane 15388c2ecf20Sopenharmony_ci * supports RGBA5551 (16 bpp, depth 15) but not RGB565 (16 bpp, depth 15398c2ecf20Sopenharmony_ci * 16) we need to scale down the depth of the sizes we request. 15408c2ecf20Sopenharmony_ci */ 15418c2ecf20Sopenharmony_ci mutex_lock(&client->modeset_mutex); 15428c2ecf20Sopenharmony_ci drm_client_for_each_modeset(mode_set, client) { 15438c2ecf20Sopenharmony_ci struct drm_crtc *crtc = mode_set->crtc; 15448c2ecf20Sopenharmony_ci struct drm_plane *plane = crtc->primary; 15458c2ecf20Sopenharmony_ci int j; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci drm_dbg_kms(dev, "test CRTC %u primary plane\n", drm_crtc_index(crtc)); 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci for (j = 0; j < plane->format_count; j++) { 15508c2ecf20Sopenharmony_ci const struct drm_format_info *fmt; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci fmt = drm_format_info(plane->format_types[j]); 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci /* 15558c2ecf20Sopenharmony_ci * Do not consider YUV or other complicated formats 15568c2ecf20Sopenharmony_ci * for framebuffers. This means only legacy formats 15578c2ecf20Sopenharmony_ci * are supported (fmt->depth is a legacy field) but 15588c2ecf20Sopenharmony_ci * the framebuffer emulation can only deal with such 15598c2ecf20Sopenharmony_ci * formats, specifically RGB/BGA formats. 15608c2ecf20Sopenharmony_ci */ 15618c2ecf20Sopenharmony_ci if (fmt->depth == 0) 15628c2ecf20Sopenharmony_ci continue; 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci /* We found a perfect fit, great */ 15658c2ecf20Sopenharmony_ci if (fmt->depth == sizes.surface_depth) { 15668c2ecf20Sopenharmony_ci best_depth = fmt->depth; 15678c2ecf20Sopenharmony_ci break; 15688c2ecf20Sopenharmony_ci } 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci /* Skip depths above what we're looking for */ 15718c2ecf20Sopenharmony_ci if (fmt->depth > sizes.surface_depth) 15728c2ecf20Sopenharmony_ci continue; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci /* Best depth found so far */ 15758c2ecf20Sopenharmony_ci if (fmt->depth > best_depth) 15768c2ecf20Sopenharmony_ci best_depth = fmt->depth; 15778c2ecf20Sopenharmony_ci } 15788c2ecf20Sopenharmony_ci } 15798c2ecf20Sopenharmony_ci if (sizes.surface_depth != best_depth && best_depth) { 15808c2ecf20Sopenharmony_ci drm_info(dev, "requested bpp %d, scaled depth down to %d", 15818c2ecf20Sopenharmony_ci sizes.surface_bpp, best_depth); 15828c2ecf20Sopenharmony_ci sizes.surface_depth = best_depth; 15838c2ecf20Sopenharmony_ci } 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci /* first up get a count of crtcs now in use and new min/maxes width/heights */ 15868c2ecf20Sopenharmony_ci crtc_count = 0; 15878c2ecf20Sopenharmony_ci drm_client_for_each_modeset(mode_set, client) { 15888c2ecf20Sopenharmony_ci struct drm_display_mode *desired_mode; 15898c2ecf20Sopenharmony_ci int x, y, j; 15908c2ecf20Sopenharmony_ci /* in case of tile group, are we the last tile vert or horiz? 15918c2ecf20Sopenharmony_ci * If no tile group you are always the last one both vertically 15928c2ecf20Sopenharmony_ci * and horizontally 15938c2ecf20Sopenharmony_ci */ 15948c2ecf20Sopenharmony_ci bool lastv = true, lasth = true; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci desired_mode = mode_set->mode; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci if (!desired_mode) 15998c2ecf20Sopenharmony_ci continue; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci crtc_count++; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci x = mode_set->x; 16048c2ecf20Sopenharmony_ci y = mode_set->y; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci sizes.surface_width = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width); 16078c2ecf20Sopenharmony_ci sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height); 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci for (j = 0; j < mode_set->num_connectors; j++) { 16108c2ecf20Sopenharmony_ci struct drm_connector *connector = mode_set->connectors[j]; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci if (connector->has_tile && 16138c2ecf20Sopenharmony_ci desired_mode->hdisplay == connector->tile_h_size && 16148c2ecf20Sopenharmony_ci desired_mode->vdisplay == connector->tile_v_size) { 16158c2ecf20Sopenharmony_ci lasth = (connector->tile_h_loc == (connector->num_h_tile - 1)); 16168c2ecf20Sopenharmony_ci lastv = (connector->tile_v_loc == (connector->num_v_tile - 1)); 16178c2ecf20Sopenharmony_ci /* cloning to multiple tiles is just crazy-talk, so: */ 16188c2ecf20Sopenharmony_ci break; 16198c2ecf20Sopenharmony_ci } 16208c2ecf20Sopenharmony_ci } 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci if (lasth) 16238c2ecf20Sopenharmony_ci sizes.fb_width = min_t(u32, desired_mode->hdisplay + x, sizes.fb_width); 16248c2ecf20Sopenharmony_ci if (lastv) 16258c2ecf20Sopenharmony_ci sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height); 16268c2ecf20Sopenharmony_ci } 16278c2ecf20Sopenharmony_ci mutex_unlock(&client->modeset_mutex); 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { 16308c2ecf20Sopenharmony_ci drm_info(dev, "Cannot find any crtc or sizes\n"); 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci /* First time: disable all crtc's.. */ 16338c2ecf20Sopenharmony_ci if (!fb_helper->deferred_setup) 16348c2ecf20Sopenharmony_ci drm_client_modeset_commit(client); 16358c2ecf20Sopenharmony_ci return -EAGAIN; 16368c2ecf20Sopenharmony_ci } 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci /* Handle our overallocation */ 16398c2ecf20Sopenharmony_ci sizes.surface_height *= drm_fbdev_overalloc; 16408c2ecf20Sopenharmony_ci sizes.surface_height /= 100; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci /* push down into drivers */ 16438c2ecf20Sopenharmony_ci ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); 16448c2ecf20Sopenharmony_ci if (ret < 0) 16458c2ecf20Sopenharmony_ci return ret; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci strcpy(fb_helper->fb->comm, "[fbcon]"); 16488c2ecf20Sopenharmony_ci return 0; 16498c2ecf20Sopenharmony_ci} 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_cistatic void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, 16528c2ecf20Sopenharmony_ci uint32_t depth) 16538c2ecf20Sopenharmony_ci{ 16548c2ecf20Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 16558c2ecf20Sopenharmony_ci info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : 16568c2ecf20Sopenharmony_ci FB_VISUAL_TRUECOLOR; 16578c2ecf20Sopenharmony_ci info->fix.mmio_start = 0; 16588c2ecf20Sopenharmony_ci info->fix.mmio_len = 0; 16598c2ecf20Sopenharmony_ci info->fix.type_aux = 0; 16608c2ecf20Sopenharmony_ci info->fix.xpanstep = 1; /* doing it in hw */ 16618c2ecf20Sopenharmony_ci info->fix.ypanstep = 1; /* doing it in hw */ 16628c2ecf20Sopenharmony_ci info->fix.ywrapstep = 0; 16638c2ecf20Sopenharmony_ci info->fix.accel = FB_ACCEL_NONE; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci info->fix.line_length = pitch; 16668c2ecf20Sopenharmony_ci} 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_cistatic void drm_fb_helper_fill_var(struct fb_info *info, 16698c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper, 16708c2ecf20Sopenharmony_ci uint32_t fb_width, uint32_t fb_height) 16718c2ecf20Sopenharmony_ci{ 16728c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = fb_helper->fb; 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci WARN_ON((drm_format_info_block_width(fb->format, 0) > 1) || 16758c2ecf20Sopenharmony_ci (drm_format_info_block_height(fb->format, 0) > 1)); 16768c2ecf20Sopenharmony_ci info->pseudo_palette = fb_helper->pseudo_palette; 16778c2ecf20Sopenharmony_ci info->var.xres_virtual = fb->width; 16788c2ecf20Sopenharmony_ci info->var.yres_virtual = fb->height; 16798c2ecf20Sopenharmony_ci info->var.bits_per_pixel = fb->format->cpp[0] * 8; 16808c2ecf20Sopenharmony_ci info->var.accel_flags = FB_ACCELF_TEXT; 16818c2ecf20Sopenharmony_ci info->var.xoffset = 0; 16828c2ecf20Sopenharmony_ci info->var.yoffset = 0; 16838c2ecf20Sopenharmony_ci info->var.activate = FB_ACTIVATE_NOW; 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci drm_fb_helper_fill_pixel_fmt(&info->var, fb->format->depth); 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci info->var.xres = fb_width; 16888c2ecf20Sopenharmony_ci info->var.yres = fb_height; 16898c2ecf20Sopenharmony_ci} 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci/** 16928c2ecf20Sopenharmony_ci * drm_fb_helper_fill_info - initializes fbdev information 16938c2ecf20Sopenharmony_ci * @info: fbdev instance to set up 16948c2ecf20Sopenharmony_ci * @fb_helper: fb helper instance to use as template 16958c2ecf20Sopenharmony_ci * @sizes: describes fbdev size and scanout surface size 16968c2ecf20Sopenharmony_ci * 16978c2ecf20Sopenharmony_ci * Sets up the variable and fixed fbdev metainformation from the given fb helper 16988c2ecf20Sopenharmony_ci * instance and the drm framebuffer allocated in &drm_fb_helper.fb. 16998c2ecf20Sopenharmony_ci * 17008c2ecf20Sopenharmony_ci * Drivers should call this (or their equivalent setup code) from their 17018c2ecf20Sopenharmony_ci * &drm_fb_helper_funcs.fb_probe callback after having allocated the fbdev 17028c2ecf20Sopenharmony_ci * backing storage framebuffer. 17038c2ecf20Sopenharmony_ci */ 17048c2ecf20Sopenharmony_civoid drm_fb_helper_fill_info(struct fb_info *info, 17058c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper, 17068c2ecf20Sopenharmony_ci struct drm_fb_helper_surface_size *sizes) 17078c2ecf20Sopenharmony_ci{ 17088c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = fb_helper->fb; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); 17118c2ecf20Sopenharmony_ci drm_fb_helper_fill_var(info, fb_helper, 17128c2ecf20Sopenharmony_ci sizes->fb_width, sizes->fb_height); 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci info->par = fb_helper; 17158c2ecf20Sopenharmony_ci snprintf(info->fix.id, sizeof(info->fix.id), "%sdrmfb", 17168c2ecf20Sopenharmony_ci fb_helper->dev->driver->name); 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci} 17198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_fill_info); 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci/* 17228c2ecf20Sopenharmony_ci * This is a continuation of drm_setup_crtcs() that sets up anything related 17238c2ecf20Sopenharmony_ci * to the framebuffer. During initialization, drm_setup_crtcs() is called before 17248c2ecf20Sopenharmony_ci * the framebuffer has been allocated (fb_helper->fb and fb_helper->fbdev). 17258c2ecf20Sopenharmony_ci * So, any setup that touches those fields needs to be done here instead of in 17268c2ecf20Sopenharmony_ci * drm_setup_crtcs(). 17278c2ecf20Sopenharmony_ci */ 17288c2ecf20Sopenharmony_cistatic void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) 17298c2ecf20Sopenharmony_ci{ 17308c2ecf20Sopenharmony_ci struct drm_client_dev *client = &fb_helper->client; 17318c2ecf20Sopenharmony_ci struct drm_connector_list_iter conn_iter; 17328c2ecf20Sopenharmony_ci struct fb_info *info = fb_helper->fbdev; 17338c2ecf20Sopenharmony_ci unsigned int rotation, sw_rotations = 0; 17348c2ecf20Sopenharmony_ci struct drm_connector *connector; 17358c2ecf20Sopenharmony_ci struct drm_mode_set *modeset; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci mutex_lock(&client->modeset_mutex); 17388c2ecf20Sopenharmony_ci drm_client_for_each_modeset(modeset, client) { 17398c2ecf20Sopenharmony_ci if (!modeset->num_connectors) 17408c2ecf20Sopenharmony_ci continue; 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci modeset->fb = fb_helper->fb; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci if (drm_client_rotation(modeset, &rotation)) 17458c2ecf20Sopenharmony_ci /* Rotating in hardware, fbcon should not rotate */ 17468c2ecf20Sopenharmony_ci sw_rotations |= DRM_MODE_ROTATE_0; 17478c2ecf20Sopenharmony_ci else 17488c2ecf20Sopenharmony_ci sw_rotations |= rotation; 17498c2ecf20Sopenharmony_ci } 17508c2ecf20Sopenharmony_ci mutex_unlock(&client->modeset_mutex); 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci drm_connector_list_iter_begin(fb_helper->dev, &conn_iter); 17538c2ecf20Sopenharmony_ci drm_client_for_each_connector_iter(connector, &conn_iter) { 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci /* use first connected connector for the physical dimensions */ 17568c2ecf20Sopenharmony_ci if (connector->status == connector_status_connected) { 17578c2ecf20Sopenharmony_ci info->var.width = connector->display_info.width_mm; 17588c2ecf20Sopenharmony_ci info->var.height = connector->display_info.height_mm; 17598c2ecf20Sopenharmony_ci break; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci } 17628c2ecf20Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci switch (sw_rotations) { 17658c2ecf20Sopenharmony_ci case DRM_MODE_ROTATE_0: 17668c2ecf20Sopenharmony_ci info->fbcon_rotate_hint = FB_ROTATE_UR; 17678c2ecf20Sopenharmony_ci break; 17688c2ecf20Sopenharmony_ci case DRM_MODE_ROTATE_90: 17698c2ecf20Sopenharmony_ci info->fbcon_rotate_hint = FB_ROTATE_CCW; 17708c2ecf20Sopenharmony_ci break; 17718c2ecf20Sopenharmony_ci case DRM_MODE_ROTATE_180: 17728c2ecf20Sopenharmony_ci info->fbcon_rotate_hint = FB_ROTATE_UD; 17738c2ecf20Sopenharmony_ci break; 17748c2ecf20Sopenharmony_ci case DRM_MODE_ROTATE_270: 17758c2ecf20Sopenharmony_ci info->fbcon_rotate_hint = FB_ROTATE_CW; 17768c2ecf20Sopenharmony_ci break; 17778c2ecf20Sopenharmony_ci default: 17788c2ecf20Sopenharmony_ci /* 17798c2ecf20Sopenharmony_ci * Multiple bits are set / multiple rotations requested 17808c2ecf20Sopenharmony_ci * fbcon cannot handle separate rotation settings per 17818c2ecf20Sopenharmony_ci * output, so fallback to unrotated. 17828c2ecf20Sopenharmony_ci */ 17838c2ecf20Sopenharmony_ci info->fbcon_rotate_hint = FB_ROTATE_UR; 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci} 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci/* Note: Drops fb_helper->lock before returning. */ 17888c2ecf20Sopenharmony_cistatic int 17898c2ecf20Sopenharmony_ci__drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper, 17908c2ecf20Sopenharmony_ci int bpp_sel) 17918c2ecf20Sopenharmony_ci{ 17928c2ecf20Sopenharmony_ci struct drm_device *dev = fb_helper->dev; 17938c2ecf20Sopenharmony_ci struct fb_info *info; 17948c2ecf20Sopenharmony_ci unsigned int width, height; 17958c2ecf20Sopenharmony_ci int ret; 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci width = dev->mode_config.max_width; 17988c2ecf20Sopenharmony_ci height = dev->mode_config.max_height; 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci drm_client_modeset_probe(&fb_helper->client, width, height); 18018c2ecf20Sopenharmony_ci ret = drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); 18028c2ecf20Sopenharmony_ci if (ret < 0) { 18038c2ecf20Sopenharmony_ci if (ret == -EAGAIN) { 18048c2ecf20Sopenharmony_ci fb_helper->preferred_bpp = bpp_sel; 18058c2ecf20Sopenharmony_ci fb_helper->deferred_setup = true; 18068c2ecf20Sopenharmony_ci ret = 0; 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci mutex_unlock(&fb_helper->lock); 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci return ret; 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci drm_setup_crtcs_fb(fb_helper); 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci fb_helper->deferred_setup = false; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci info = fb_helper->fbdev; 18178c2ecf20Sopenharmony_ci info->var.pixclock = 0; 18188c2ecf20Sopenharmony_ci /* Shamelessly allow physical address leaking to userspace */ 18198c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) 18208c2ecf20Sopenharmony_ci if (!drm_leak_fbdev_smem) 18218c2ecf20Sopenharmony_ci#endif 18228c2ecf20Sopenharmony_ci /* don't leak any physical addresses to userspace */ 18238c2ecf20Sopenharmony_ci info->flags |= FBINFO_HIDE_SMEM_START; 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci /* Need to drop locks to avoid recursive deadlock in 18268c2ecf20Sopenharmony_ci * register_framebuffer. This is ok because the only thing left to do is 18278c2ecf20Sopenharmony_ci * register the fbdev emulation instance in kernel_fb_helper_list. */ 18288c2ecf20Sopenharmony_ci mutex_unlock(&fb_helper->lock); 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci ret = register_framebuffer(info); 18318c2ecf20Sopenharmony_ci if (ret < 0) 18328c2ecf20Sopenharmony_ci return ret; 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci drm_info(dev, "fb%d: %s frame buffer device\n", 18358c2ecf20Sopenharmony_ci info->node, info->fix.id); 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci mutex_lock(&kernel_fb_helper_lock); 18388c2ecf20Sopenharmony_ci if (list_empty(&kernel_fb_helper_list)) 18398c2ecf20Sopenharmony_ci register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); 18428c2ecf20Sopenharmony_ci mutex_unlock(&kernel_fb_helper_lock); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci return 0; 18458c2ecf20Sopenharmony_ci} 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci/** 18488c2ecf20Sopenharmony_ci * drm_fb_helper_initial_config - setup a sane initial connector configuration 18498c2ecf20Sopenharmony_ci * @fb_helper: fb_helper device struct 18508c2ecf20Sopenharmony_ci * @bpp_sel: bpp value to use for the framebuffer configuration 18518c2ecf20Sopenharmony_ci * 18528c2ecf20Sopenharmony_ci * Scans the CRTCs and connectors and tries to put together an initial setup. 18538c2ecf20Sopenharmony_ci * At the moment, this is a cloned configuration across all heads with 18548c2ecf20Sopenharmony_ci * a new framebuffer object as the backing store. 18558c2ecf20Sopenharmony_ci * 18568c2ecf20Sopenharmony_ci * Note that this also registers the fbdev and so allows userspace to call into 18578c2ecf20Sopenharmony_ci * the driver through the fbdev interfaces. 18588c2ecf20Sopenharmony_ci * 18598c2ecf20Sopenharmony_ci * This function will call down into the &drm_fb_helper_funcs.fb_probe callback 18608c2ecf20Sopenharmony_ci * to let the driver allocate and initialize the fbdev info structure and the 18618c2ecf20Sopenharmony_ci * drm framebuffer used to back the fbdev. drm_fb_helper_fill_info() is provided 18628c2ecf20Sopenharmony_ci * as a helper to setup simple default values for the fbdev info structure. 18638c2ecf20Sopenharmony_ci * 18648c2ecf20Sopenharmony_ci * HANG DEBUGGING: 18658c2ecf20Sopenharmony_ci * 18668c2ecf20Sopenharmony_ci * When you have fbcon support built-in or already loaded, this function will do 18678c2ecf20Sopenharmony_ci * a full modeset to setup the fbdev console. Due to locking misdesign in the 18688c2ecf20Sopenharmony_ci * VT/fbdev subsystem that entire modeset sequence has to be done while holding 18698c2ecf20Sopenharmony_ci * console_lock. Until console_unlock is called no dmesg lines will be sent out 18708c2ecf20Sopenharmony_ci * to consoles, not even serial console. This means when your driver crashes, 18718c2ecf20Sopenharmony_ci * you will see absolutely nothing else but a system stuck in this function, 18728c2ecf20Sopenharmony_ci * with no further output. Any kind of printk() you place within your own driver 18738c2ecf20Sopenharmony_ci * or in the drm core modeset code will also never show up. 18748c2ecf20Sopenharmony_ci * 18758c2ecf20Sopenharmony_ci * Standard debug practice is to run the fbcon setup without taking the 18768c2ecf20Sopenharmony_ci * console_lock as a hack, to be able to see backtraces and crashes on the 18778c2ecf20Sopenharmony_ci * serial line. This can be done by setting the fb.lockless_register_fb=1 kernel 18788c2ecf20Sopenharmony_ci * cmdline option. 18798c2ecf20Sopenharmony_ci * 18808c2ecf20Sopenharmony_ci * The other option is to just disable fbdev emulation since very likely the 18818c2ecf20Sopenharmony_ci * first modeset from userspace will crash in the same way, and is even easier 18828c2ecf20Sopenharmony_ci * to debug. This can be done by setting the drm_kms_helper.fbdev_emulation=0 18838c2ecf20Sopenharmony_ci * kernel cmdline option. 18848c2ecf20Sopenharmony_ci * 18858c2ecf20Sopenharmony_ci * RETURNS: 18868c2ecf20Sopenharmony_ci * Zero if everything went ok, nonzero otherwise. 18878c2ecf20Sopenharmony_ci */ 18888c2ecf20Sopenharmony_ciint drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) 18898c2ecf20Sopenharmony_ci{ 18908c2ecf20Sopenharmony_ci int ret; 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci if (!drm_fbdev_emulation) 18938c2ecf20Sopenharmony_ci return 0; 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci mutex_lock(&fb_helper->lock); 18968c2ecf20Sopenharmony_ci ret = __drm_fb_helper_initial_config_and_unlock(fb_helper, bpp_sel); 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci return ret; 18998c2ecf20Sopenharmony_ci} 19008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_initial_config); 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci/** 19038c2ecf20Sopenharmony_ci * drm_fb_helper_hotplug_event - respond to a hotplug notification by 19048c2ecf20Sopenharmony_ci * probing all the outputs attached to the fb 19058c2ecf20Sopenharmony_ci * @fb_helper: driver-allocated fbdev helper, can be NULL 19068c2ecf20Sopenharmony_ci * 19078c2ecf20Sopenharmony_ci * Scan the connectors attached to the fb_helper and try to put together a 19088c2ecf20Sopenharmony_ci * setup after notification of a change in output configuration. 19098c2ecf20Sopenharmony_ci * 19108c2ecf20Sopenharmony_ci * Called at runtime, takes the mode config locks to be able to check/change the 19118c2ecf20Sopenharmony_ci * modeset configuration. Must be run from process context (which usually means 19128c2ecf20Sopenharmony_ci * either the output polling work or a work item launched from the driver's 19138c2ecf20Sopenharmony_ci * hotplug interrupt). 19148c2ecf20Sopenharmony_ci * 19158c2ecf20Sopenharmony_ci * Note that drivers may call this even before calling 19168c2ecf20Sopenharmony_ci * drm_fb_helper_initial_config but only after drm_fb_helper_init. This allows 19178c2ecf20Sopenharmony_ci * for a race-free fbcon setup and will make sure that the fbdev emulation will 19188c2ecf20Sopenharmony_ci * not miss any hotplug events. 19198c2ecf20Sopenharmony_ci * 19208c2ecf20Sopenharmony_ci * RETURNS: 19218c2ecf20Sopenharmony_ci * 0 on success and a non-zero error code otherwise. 19228c2ecf20Sopenharmony_ci */ 19238c2ecf20Sopenharmony_ciint drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) 19248c2ecf20Sopenharmony_ci{ 19258c2ecf20Sopenharmony_ci int err = 0; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci if (!drm_fbdev_emulation || !fb_helper) 19288c2ecf20Sopenharmony_ci return 0; 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci mutex_lock(&fb_helper->lock); 19318c2ecf20Sopenharmony_ci if (fb_helper->deferred_setup) { 19328c2ecf20Sopenharmony_ci err = __drm_fb_helper_initial_config_and_unlock(fb_helper, 19338c2ecf20Sopenharmony_ci fb_helper->preferred_bpp); 19348c2ecf20Sopenharmony_ci return err; 19358c2ecf20Sopenharmony_ci } 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci if (!fb_helper->fb || !drm_master_internal_acquire(fb_helper->dev)) { 19388c2ecf20Sopenharmony_ci fb_helper->delayed_hotplug = true; 19398c2ecf20Sopenharmony_ci mutex_unlock(&fb_helper->lock); 19408c2ecf20Sopenharmony_ci return err; 19418c2ecf20Sopenharmony_ci } 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci drm_master_internal_release(fb_helper->dev); 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci drm_dbg_kms(fb_helper->dev, "\n"); 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci drm_client_modeset_probe(&fb_helper->client, fb_helper->fb->width, fb_helper->fb->height); 19488c2ecf20Sopenharmony_ci drm_setup_crtcs_fb(fb_helper); 19498c2ecf20Sopenharmony_ci mutex_unlock(&fb_helper->lock); 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci drm_fb_helper_set_par(fb_helper->fbdev); 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci return 0; 19548c2ecf20Sopenharmony_ci} 19558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_hotplug_event); 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci/** 19588c2ecf20Sopenharmony_ci * drm_fb_helper_lastclose - DRM driver lastclose helper for fbdev emulation 19598c2ecf20Sopenharmony_ci * @dev: DRM device 19608c2ecf20Sopenharmony_ci * 19618c2ecf20Sopenharmony_ci * This function can be used as the &drm_driver->lastclose callback for drivers 19628c2ecf20Sopenharmony_ci * that only need to call drm_fb_helper_restore_fbdev_mode_unlocked(). 19638c2ecf20Sopenharmony_ci */ 19648c2ecf20Sopenharmony_civoid drm_fb_helper_lastclose(struct drm_device *dev) 19658c2ecf20Sopenharmony_ci{ 19668c2ecf20Sopenharmony_ci drm_fb_helper_restore_fbdev_mode_unlocked(dev->fb_helper); 19678c2ecf20Sopenharmony_ci} 19688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_lastclose); 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci/** 19718c2ecf20Sopenharmony_ci * drm_fb_helper_output_poll_changed - DRM mode config \.output_poll_changed 19728c2ecf20Sopenharmony_ci * helper for fbdev emulation 19738c2ecf20Sopenharmony_ci * @dev: DRM device 19748c2ecf20Sopenharmony_ci * 19758c2ecf20Sopenharmony_ci * This function can be used as the 19768c2ecf20Sopenharmony_ci * &drm_mode_config_funcs.output_poll_changed callback for drivers that only 19778c2ecf20Sopenharmony_ci * need to call drm_fb_helper_hotplug_event(). 19788c2ecf20Sopenharmony_ci */ 19798c2ecf20Sopenharmony_civoid drm_fb_helper_output_poll_changed(struct drm_device *dev) 19808c2ecf20Sopenharmony_ci{ 19818c2ecf20Sopenharmony_ci drm_fb_helper_hotplug_event(dev->fb_helper); 19828c2ecf20Sopenharmony_ci} 19838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fb_helper_output_poll_changed); 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci/* @user: 1=userspace, 0=fbcon */ 19868c2ecf20Sopenharmony_cistatic int drm_fbdev_fb_open(struct fb_info *info, int user) 19878c2ecf20Sopenharmony_ci{ 19888c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci /* No need to take a ref for fbcon because it unbinds on unregister */ 19918c2ecf20Sopenharmony_ci if (user && !try_module_get(fb_helper->dev->driver->fops->owner)) 19928c2ecf20Sopenharmony_ci return -ENODEV; 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci return 0; 19958c2ecf20Sopenharmony_ci} 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_cistatic int drm_fbdev_fb_release(struct fb_info *info, int user) 19988c2ecf20Sopenharmony_ci{ 19998c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci if (user) 20028c2ecf20Sopenharmony_ci module_put(fb_helper->dev->driver->fops->owner); 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci return 0; 20058c2ecf20Sopenharmony_ci} 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_cistatic void drm_fbdev_cleanup(struct drm_fb_helper *fb_helper) 20088c2ecf20Sopenharmony_ci{ 20098c2ecf20Sopenharmony_ci struct fb_info *fbi = fb_helper->fbdev; 20108c2ecf20Sopenharmony_ci void *shadow = NULL; 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci if (!fb_helper->dev) 20138c2ecf20Sopenharmony_ci return; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci if (fbi && fbi->fbdefio) { 20168c2ecf20Sopenharmony_ci fb_deferred_io_cleanup(fbi); 20178c2ecf20Sopenharmony_ci shadow = fbi->screen_buffer; 20188c2ecf20Sopenharmony_ci } 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci drm_fb_helper_fini(fb_helper); 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci vfree(shadow); 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci drm_client_framebuffer_delete(fb_helper->buffer); 20258c2ecf20Sopenharmony_ci} 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_cistatic void drm_fbdev_release(struct drm_fb_helper *fb_helper) 20288c2ecf20Sopenharmony_ci{ 20298c2ecf20Sopenharmony_ci drm_fbdev_cleanup(fb_helper); 20308c2ecf20Sopenharmony_ci drm_client_release(&fb_helper->client); 20318c2ecf20Sopenharmony_ci kfree(fb_helper); 20328c2ecf20Sopenharmony_ci} 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci/* 20358c2ecf20Sopenharmony_ci * fb_ops.fb_destroy is called by the last put_fb_info() call at the end of 20368c2ecf20Sopenharmony_ci * unregister_framebuffer() or fb_release(). 20378c2ecf20Sopenharmony_ci */ 20388c2ecf20Sopenharmony_cistatic void drm_fbdev_fb_destroy(struct fb_info *info) 20398c2ecf20Sopenharmony_ci{ 20408c2ecf20Sopenharmony_ci drm_fbdev_release(info->par); 20418c2ecf20Sopenharmony_ci} 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_cistatic int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) 20448c2ecf20Sopenharmony_ci{ 20458c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = info->par; 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci if (fb_helper->dev->driver->gem_prime_mmap) 20488c2ecf20Sopenharmony_ci return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma); 20498c2ecf20Sopenharmony_ci else 20508c2ecf20Sopenharmony_ci return -ENODEV; 20518c2ecf20Sopenharmony_ci} 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_cistatic const struct fb_ops drm_fbdev_fb_ops = { 20548c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 20558c2ecf20Sopenharmony_ci DRM_FB_HELPER_DEFAULT_OPS, 20568c2ecf20Sopenharmony_ci .fb_open = drm_fbdev_fb_open, 20578c2ecf20Sopenharmony_ci .fb_release = drm_fbdev_fb_release, 20588c2ecf20Sopenharmony_ci .fb_destroy = drm_fbdev_fb_destroy, 20598c2ecf20Sopenharmony_ci .fb_mmap = drm_fbdev_fb_mmap, 20608c2ecf20Sopenharmony_ci .fb_read = drm_fb_helper_sys_read, 20618c2ecf20Sopenharmony_ci .fb_write = drm_fb_helper_sys_write, 20628c2ecf20Sopenharmony_ci .fb_fillrect = drm_fb_helper_sys_fillrect, 20638c2ecf20Sopenharmony_ci .fb_copyarea = drm_fb_helper_sys_copyarea, 20648c2ecf20Sopenharmony_ci .fb_imageblit = drm_fb_helper_sys_imageblit, 20658c2ecf20Sopenharmony_ci}; 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_cistatic struct fb_deferred_io drm_fbdev_defio = { 20688c2ecf20Sopenharmony_ci .delay = HZ / 20, 20698c2ecf20Sopenharmony_ci .deferred_io = drm_fb_helper_deferred_io, 20708c2ecf20Sopenharmony_ci}; 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci/* 20738c2ecf20Sopenharmony_ci * This function uses the client API to create a framebuffer backed by a dumb buffer. 20748c2ecf20Sopenharmony_ci * 20758c2ecf20Sopenharmony_ci * The _sys_ versions are used for &fb_ops.fb_read, fb_write, fb_fillrect, 20768c2ecf20Sopenharmony_ci * fb_copyarea, fb_imageblit. 20778c2ecf20Sopenharmony_ci */ 20788c2ecf20Sopenharmony_cistatic int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, 20798c2ecf20Sopenharmony_ci struct drm_fb_helper_surface_size *sizes) 20808c2ecf20Sopenharmony_ci{ 20818c2ecf20Sopenharmony_ci struct drm_client_dev *client = &fb_helper->client; 20828c2ecf20Sopenharmony_ci struct drm_device *dev = fb_helper->dev; 20838c2ecf20Sopenharmony_ci struct drm_client_buffer *buffer; 20848c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 20858c2ecf20Sopenharmony_ci struct fb_info *fbi; 20868c2ecf20Sopenharmony_ci u32 format; 20878c2ecf20Sopenharmony_ci void *vaddr; 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_ci drm_dbg_kms(dev, "surface width(%d), height(%d) and bpp(%d)\n", 20908c2ecf20Sopenharmony_ci sizes->surface_width, sizes->surface_height, 20918c2ecf20Sopenharmony_ci sizes->surface_bpp); 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); 20948c2ecf20Sopenharmony_ci buffer = drm_client_framebuffer_create(client, sizes->surface_width, 20958c2ecf20Sopenharmony_ci sizes->surface_height, format); 20968c2ecf20Sopenharmony_ci if (IS_ERR(buffer)) 20978c2ecf20Sopenharmony_ci return PTR_ERR(buffer); 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci fb_helper->buffer = buffer; 21008c2ecf20Sopenharmony_ci fb_helper->fb = buffer->fb; 21018c2ecf20Sopenharmony_ci fb = buffer->fb; 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci fbi = drm_fb_helper_alloc_fbi(fb_helper); 21048c2ecf20Sopenharmony_ci if (IS_ERR(fbi)) 21058c2ecf20Sopenharmony_ci return PTR_ERR(fbi); 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci fbi->fbops = &drm_fbdev_fb_ops; 21088c2ecf20Sopenharmony_ci fbi->screen_size = fb->height * fb->pitches[0]; 21098c2ecf20Sopenharmony_ci fbi->fix.smem_len = fbi->screen_size; 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci drm_fb_helper_fill_info(fbi, fb_helper, sizes); 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci if (drm_fbdev_use_shadow_fb(fb_helper)) { 21148c2ecf20Sopenharmony_ci fbi->screen_buffer = vzalloc(fbi->screen_size); 21158c2ecf20Sopenharmony_ci if (!fbi->screen_buffer) 21168c2ecf20Sopenharmony_ci return -ENOMEM; 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci fbi->fbdefio = &drm_fbdev_defio; 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci fb_deferred_io_init(fbi); 21218c2ecf20Sopenharmony_ci } else { 21228c2ecf20Sopenharmony_ci /* buffer is mapped for HW framebuffer */ 21238c2ecf20Sopenharmony_ci vaddr = drm_client_buffer_vmap(fb_helper->buffer); 21248c2ecf20Sopenharmony_ci if (IS_ERR(vaddr)) 21258c2ecf20Sopenharmony_ci return PTR_ERR(vaddr); 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci fbi->screen_buffer = vaddr; 21288c2ecf20Sopenharmony_ci /* Shamelessly leak the physical address to user-space */ 21298c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) 21308c2ecf20Sopenharmony_ci if (drm_leak_fbdev_smem && fbi->fix.smem_start == 0) 21318c2ecf20Sopenharmony_ci fbi->fix.smem_start = 21328c2ecf20Sopenharmony_ci page_to_phys(virt_to_page(fbi->screen_buffer)); 21338c2ecf20Sopenharmony_ci#endif 21348c2ecf20Sopenharmony_ci } 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci return 0; 21378c2ecf20Sopenharmony_ci} 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_cistatic const struct drm_fb_helper_funcs drm_fb_helper_generic_funcs = { 21408c2ecf20Sopenharmony_ci .fb_probe = drm_fb_helper_generic_probe, 21418c2ecf20Sopenharmony_ci}; 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_cistatic void drm_fbdev_client_unregister(struct drm_client_dev *client) 21448c2ecf20Sopenharmony_ci{ 21458c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci if (fb_helper->fbdev) 21488c2ecf20Sopenharmony_ci /* drm_fbdev_fb_destroy() takes care of cleanup */ 21498c2ecf20Sopenharmony_ci drm_fb_helper_unregister_fbi(fb_helper); 21508c2ecf20Sopenharmony_ci else 21518c2ecf20Sopenharmony_ci drm_fbdev_release(fb_helper); 21528c2ecf20Sopenharmony_ci} 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_cistatic int drm_fbdev_client_restore(struct drm_client_dev *client) 21558c2ecf20Sopenharmony_ci{ 21568c2ecf20Sopenharmony_ci drm_fb_helper_lastclose(client->dev); 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci return 0; 21598c2ecf20Sopenharmony_ci} 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_cistatic int drm_fbdev_client_hotplug(struct drm_client_dev *client) 21628c2ecf20Sopenharmony_ci{ 21638c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 21648c2ecf20Sopenharmony_ci struct drm_device *dev = client->dev; 21658c2ecf20Sopenharmony_ci int ret; 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci /* Setup is not retried if it has failed */ 21688c2ecf20Sopenharmony_ci if (!fb_helper->dev && fb_helper->funcs) 21698c2ecf20Sopenharmony_ci return 0; 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci if (dev->fb_helper) 21728c2ecf20Sopenharmony_ci return drm_fb_helper_hotplug_event(dev->fb_helper); 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci if (!dev->mode_config.num_connector) { 21758c2ecf20Sopenharmony_ci drm_dbg_kms(dev, "No connectors found, will not create framebuffer!\n"); 21768c2ecf20Sopenharmony_ci return 0; 21778c2ecf20Sopenharmony_ci } 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci drm_fb_helper_prepare(dev, fb_helper, &drm_fb_helper_generic_funcs); 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci ret = drm_fb_helper_init(dev, fb_helper); 21828c2ecf20Sopenharmony_ci if (ret) 21838c2ecf20Sopenharmony_ci goto err; 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci if (!drm_drv_uses_atomic_modeset(dev)) 21868c2ecf20Sopenharmony_ci drm_helper_disable_unused_functions(dev); 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci ret = drm_fb_helper_initial_config(fb_helper, fb_helper->preferred_bpp); 21898c2ecf20Sopenharmony_ci if (ret) 21908c2ecf20Sopenharmony_ci goto err_cleanup; 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci return 0; 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_cierr_cleanup: 21958c2ecf20Sopenharmony_ci drm_fbdev_cleanup(fb_helper); 21968c2ecf20Sopenharmony_cierr: 21978c2ecf20Sopenharmony_ci fb_helper->dev = NULL; 21988c2ecf20Sopenharmony_ci fb_helper->fbdev = NULL; 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci drm_err(dev, "fbdev: Failed to setup generic emulation (ret=%d)\n", ret); 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci return ret; 22038c2ecf20Sopenharmony_ci} 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_cistatic const struct drm_client_funcs drm_fbdev_client_funcs = { 22068c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 22078c2ecf20Sopenharmony_ci .unregister = drm_fbdev_client_unregister, 22088c2ecf20Sopenharmony_ci .restore = drm_fbdev_client_restore, 22098c2ecf20Sopenharmony_ci .hotplug = drm_fbdev_client_hotplug, 22108c2ecf20Sopenharmony_ci}; 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci/** 22138c2ecf20Sopenharmony_ci * drm_fbdev_generic_setup() - Setup generic fbdev emulation 22148c2ecf20Sopenharmony_ci * @dev: DRM device 22158c2ecf20Sopenharmony_ci * @preferred_bpp: Preferred bits per pixel for the device. 22168c2ecf20Sopenharmony_ci * @dev->mode_config.preferred_depth is used if this is zero. 22178c2ecf20Sopenharmony_ci * 22188c2ecf20Sopenharmony_ci * This function sets up generic fbdev emulation for drivers that supports 22198c2ecf20Sopenharmony_ci * dumb buffers with a virtual address and that can be mmap'ed. 22208c2ecf20Sopenharmony_ci * drm_fbdev_generic_setup() shall be called after the DRM driver registered 22218c2ecf20Sopenharmony_ci * the new DRM device with drm_dev_register(). 22228c2ecf20Sopenharmony_ci * 22238c2ecf20Sopenharmony_ci * Restore, hotplug events and teardown are all taken care of. Drivers that do 22248c2ecf20Sopenharmony_ci * suspend/resume need to call drm_fb_helper_set_suspend_unlocked() themselves. 22258c2ecf20Sopenharmony_ci * Simple drivers might use drm_mode_config_helper_suspend(). 22268c2ecf20Sopenharmony_ci * 22278c2ecf20Sopenharmony_ci * Drivers that set the dirty callback on their framebuffer will get a shadow 22288c2ecf20Sopenharmony_ci * fbdev buffer that is blitted onto the real buffer. This is done in order to 22298c2ecf20Sopenharmony_ci * make deferred I/O work with all kinds of buffers. A shadow buffer can be 22308c2ecf20Sopenharmony_ci * requested explicitly by setting struct drm_mode_config.prefer_shadow or 22318c2ecf20Sopenharmony_ci * struct drm_mode_config.prefer_shadow_fbdev to true beforehand. This is 22328c2ecf20Sopenharmony_ci * required to use generic fbdev emulation with SHMEM helpers. 22338c2ecf20Sopenharmony_ci * 22348c2ecf20Sopenharmony_ci * This function is safe to call even when there are no connectors present. 22358c2ecf20Sopenharmony_ci * Setup will be retried on the next hotplug event. 22368c2ecf20Sopenharmony_ci * 22378c2ecf20Sopenharmony_ci * The fbdev is destroyed by drm_dev_unregister(). 22388c2ecf20Sopenharmony_ci */ 22398c2ecf20Sopenharmony_civoid drm_fbdev_generic_setup(struct drm_device *dev, 22408c2ecf20Sopenharmony_ci unsigned int preferred_bpp) 22418c2ecf20Sopenharmony_ci{ 22428c2ecf20Sopenharmony_ci struct drm_fb_helper *fb_helper; 22438c2ecf20Sopenharmony_ci int ret; 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci drm_WARN(dev, !dev->registered, "Device has not been registered.\n"); 22468c2ecf20Sopenharmony_ci drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n"); 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci if (!drm_fbdev_emulation) 22498c2ecf20Sopenharmony_ci return; 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL); 22528c2ecf20Sopenharmony_ci if (!fb_helper) { 22538c2ecf20Sopenharmony_ci drm_err(dev, "Failed to allocate fb_helper\n"); 22548c2ecf20Sopenharmony_ci return; 22558c2ecf20Sopenharmony_ci } 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs); 22588c2ecf20Sopenharmony_ci if (ret) { 22598c2ecf20Sopenharmony_ci kfree(fb_helper); 22608c2ecf20Sopenharmony_ci drm_err(dev, "Failed to register client: %d\n", ret); 22618c2ecf20Sopenharmony_ci return; 22628c2ecf20Sopenharmony_ci } 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci if (!preferred_bpp) 22658c2ecf20Sopenharmony_ci preferred_bpp = dev->mode_config.preferred_depth; 22668c2ecf20Sopenharmony_ci if (!preferred_bpp) 22678c2ecf20Sopenharmony_ci preferred_bpp = 32; 22688c2ecf20Sopenharmony_ci fb_helper->preferred_bpp = preferred_bpp; 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci ret = drm_fbdev_client_hotplug(&fb_helper->client); 22718c2ecf20Sopenharmony_ci if (ret) 22728c2ecf20Sopenharmony_ci drm_dbg_kms(dev, "client hotplug ret=%d\n", ret); 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_ci drm_client_register(&fb_helper->client); 22758c2ecf20Sopenharmony_ci} 22768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_fbdev_generic_setup); 2277