1746fbbdbSjsg /* 2746fbbdbSjsg * Copyright (c) 2006-2009 Red Hat Inc. 3746fbbdbSjsg * Copyright (c) 2006-2008 Intel Corporation 4746fbbdbSjsg * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 5746fbbdbSjsg * 6746fbbdbSjsg * DRM framebuffer helper functions 7746fbbdbSjsg * 8746fbbdbSjsg * Permission to use, copy, modify, distribute, and sell this software and its 9746fbbdbSjsg * documentation for any purpose is hereby granted without fee, provided that 10746fbbdbSjsg * the above copyright notice appear in all copies and that both that copyright 11746fbbdbSjsg * notice and this permission notice appear in supporting documentation, and 12746fbbdbSjsg * that the name of the copyright holders not be used in advertising or 13746fbbdbSjsg * publicity pertaining to distribution of the software without specific, 14746fbbdbSjsg * written prior permission. The copyright holders make no representations 15746fbbdbSjsg * about the suitability of this software for any purpose. It is provided "as 16746fbbdbSjsg * is" without express or implied warranty. 17746fbbdbSjsg * 18746fbbdbSjsg * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 19746fbbdbSjsg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 20746fbbdbSjsg * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 21746fbbdbSjsg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 22746fbbdbSjsg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 23746fbbdbSjsg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 24746fbbdbSjsg * OF THIS SOFTWARE. 25746fbbdbSjsg * 26746fbbdbSjsg * Authors: 27746fbbdbSjsg * Dave Airlie <airlied@linux.ie> 28746fbbdbSjsg * Jesse Barnes <jesse.barnes@intel.com> 29746fbbdbSjsg */ 30746fbbdbSjsg #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 31746fbbdbSjsg 327f4dd379Sjsg #include <linux/console.h> 33f005ef32Sjsg #include <linux/pci.h> 34c349dbc7Sjsg #include <linux/sysrq.h> 35f005ef32Sjsg #include <linux/vga_switcheroo.h> 367f4dd379Sjsg 37c349dbc7Sjsg #include <drm/drm_atomic.h> 38c349dbc7Sjsg #include <drm/drm_drv.h> 39c349dbc7Sjsg #include <drm/drm_fb_helper.h> 40c349dbc7Sjsg #include <drm/drm_fourcc.h> 411bb76ff1Sjsg #include <drm/drm_framebuffer.h> 42f005ef32Sjsg #include <drm/drm_modeset_helper_vtables.h> 43c349dbc7Sjsg #include <drm/drm_print.h> 44c349dbc7Sjsg #include <drm/drm_vblank.h> 45c349dbc7Sjsg 46c349dbc7Sjsg #include "drm_internal.h" 47e0b53ceeSkettenis 48e0b53ceeSkettenis static bool drm_fbdev_emulation = true; 49e0b53ceeSkettenis module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600); 50e0b53ceeSkettenis MODULE_PARM_DESC(fbdev_emulation, 51e0b53ceeSkettenis "Enable legacy fbdev emulation [default=true]"); 52746fbbdbSjsg 537f4dd379Sjsg static int drm_fbdev_overalloc = CONFIG_DRM_FBDEV_OVERALLOC; 547f4dd379Sjsg module_param(drm_fbdev_overalloc, int, 0444); 557f4dd379Sjsg MODULE_PARM_DESC(drm_fbdev_overalloc, 567f4dd379Sjsg "Overallocation of the fbdev buffer (%) [default=" 577f4dd379Sjsg __MODULE_STRING(CONFIG_DRM_FBDEV_OVERALLOC) "]"); 587f4dd379Sjsg 597f4dd379Sjsg /* 607f4dd379Sjsg * In order to keep user-space compatibility, we want in certain use-cases 617f4dd379Sjsg * to keep leaking the fbdev physical address to the user-space program 627f4dd379Sjsg * handling the fbdev buffer. 63f005ef32Sjsg * 64f005ef32Sjsg * This is a bad habit, essentially kept to support closed-source OpenGL 65f005ef32Sjsg * drivers that should really be moved into open-source upstream projects 66f005ef32Sjsg * instead of using legacy physical addresses in user space to communicate 67f005ef32Sjsg * with other out-of-tree kernel modules. 687f4dd379Sjsg * 697f4dd379Sjsg * This module_param *should* be removed as soon as possible and be 707f4dd379Sjsg * considered as a broken and legacy behaviour from a modern fbdev device. 717f4dd379Sjsg */ 72f005ef32Sjsg static bool drm_leak_fbdev_smem; 737f4dd379Sjsg #if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) 747f4dd379Sjsg module_param_unsafe(drm_leak_fbdev_smem, bool, 0600); 75c349dbc7Sjsg MODULE_PARM_DESC(drm_leak_fbdev_smem, 767f4dd379Sjsg "Allow unsafe leaking fbdev physical smem address [default=false]"); 777f4dd379Sjsg #endif 787f4dd379Sjsg 79746fbbdbSjsg static DRM_LIST_HEAD(kernel_fb_helper_list); 807f4dd379Sjsg static DEFINE_MUTEX(kernel_fb_helper_lock); 81746fbbdbSjsg 82746fbbdbSjsg /** 83746fbbdbSjsg * DOC: fbdev helpers 84746fbbdbSjsg * 85746fbbdbSjsg * The fb helper functions are useful to provide an fbdev on top of a drm kernel 86e0b53ceeSkettenis * mode setting driver. They can be used mostly independently from the crtc 87746fbbdbSjsg * helper functions used by many drivers to implement the kernel mode setting 88746fbbdbSjsg * interfaces. 89e1001332Skettenis * 907f4dd379Sjsg * Drivers that support a dumb buffer with a virtual address and mmap support, 917f4dd379Sjsg * should try out the generic fbdev emulation using drm_fbdev_generic_setup(). 92c349dbc7Sjsg * It will automatically set up deferred I/O if the driver requires a shadow 93c349dbc7Sjsg * buffer. 947f4dd379Sjsg * 95f005ef32Sjsg * Existing fbdev implementations should restore the fbdev console by using 967f4dd379Sjsg * drm_fb_helper_lastclose() as their &drm_driver.lastclose callback. 97e0b53ceeSkettenis * They should also notify the fb helper code from updates to the output 987f4dd379Sjsg * configuration by using drm_fb_helper_output_poll_changed() as their 99f005ef32Sjsg * &drm_mode_config_funcs.output_poll_changed callback. New implementations 100f005ef32Sjsg * of fbdev should be build on top of struct &drm_client_funcs, which handles 101f005ef32Sjsg * this automatically. Setting the old callbacks should be avoided. 1027f4dd379Sjsg * 1037f4dd379Sjsg * For suspend/resume consider using drm_mode_config_helper_suspend() and 1047f4dd379Sjsg * drm_mode_config_helper_resume() which takes care of fbdev as well. 105e1001332Skettenis * 106e1001332Skettenis * All other functions exported by the fb helper library can be used to 107e1001332Skettenis * implement the fbdev driver interface by the driver. 108e0b53ceeSkettenis * 109e0b53ceeSkettenis * It is possible, though perhaps somewhat tricky, to implement race-free 110e0b53ceeSkettenis * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare() 111e0b53ceeSkettenis * helper must be called first to initialize the minimum required to make 112e0b53ceeSkettenis * hotplug detection work. Drivers also need to make sure to properly set up 1137f4dd379Sjsg * the &drm_mode_config.funcs member. After calling drm_kms_helper_poll_init() 114e0b53ceeSkettenis * it is safe to enable interrupts and start processing hotplug events. At the 115e0b53ceeSkettenis * same time, drivers should initialize all modeset objects such as CRTCs, 116e0b53ceeSkettenis * encoders and connectors. To finish up the fbdev helper initialization, the 117e0b53ceeSkettenis * drm_fb_helper_init() function is called. To probe for all attached displays 118e0b53ceeSkettenis * and set up an initial configuration using the detected hardware, drivers 119c349dbc7Sjsg * should call drm_fb_helper_initial_config(). 1207f4dd379Sjsg * 1217f4dd379Sjsg * If &drm_framebuffer_funcs.dirty is set, the 1227f4dd379Sjsg * drm_fb_helper_{cfb,sys}_{write,fillrect,copyarea,imageblit} functions will 1237f4dd379Sjsg * accumulate changes and schedule &drm_fb_helper.dirty_work to run right 1247f4dd379Sjsg * away. This worker then calls the dirty() function ensuring that it will 1257f4dd379Sjsg * always run in process context since the fb_*() function could be running in 1267f4dd379Sjsg * atomic context. If drm_fb_helper_deferred_io() is used as the deferred_io 1277f4dd379Sjsg * callback it will also schedule dirty_work with the damage collected from the 128c349dbc7Sjsg * mmap page writes. 129e1001332Skettenis * 130c349dbc7Sjsg * Deferred I/O is not compatible with SHMEM. Such drivers should request an 131c349dbc7Sjsg * fbdev shadow buffer and call drm_fbdev_generic_setup() instead. 132e1001332Skettenis */ 133746fbbdbSjsg 134d7873f4eSjsg static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) 135746fbbdbSjsg { 136746fbbdbSjsg uint16_t *r_base, *g_base, *b_base; 137746fbbdbSjsg 138746fbbdbSjsg if (crtc->funcs->gamma_set == NULL) 139746fbbdbSjsg return; 140746fbbdbSjsg 141746fbbdbSjsg r_base = crtc->gamma_store; 142746fbbdbSjsg g_base = r_base + crtc->gamma_size; 143746fbbdbSjsg b_base = g_base + crtc->gamma_size; 144746fbbdbSjsg 1457f4dd379Sjsg crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 1467f4dd379Sjsg crtc->gamma_size, NULL); 147746fbbdbSjsg } 148746fbbdbSjsg 149e1001332Skettenis /** 1507f4dd379Sjsg * drm_fb_helper_debug_enter - implementation for &fb_ops.fb_debug_enter 151e1001332Skettenis * @info: fbdev registered by the helper 152e1001332Skettenis */ 153e0b53ceeSkettenis int drm_fb_helper_debug_enter(struct fb_info *info) 154746fbbdbSjsg { 155e0b53ceeSkettenis struct drm_fb_helper *helper = info->par; 156e0b53ceeSkettenis const struct drm_crtc_helper_funcs *funcs; 157c349dbc7Sjsg struct drm_mode_set *mode_set; 158746fbbdbSjsg 159746fbbdbSjsg list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { 160c349dbc7Sjsg mutex_lock(&helper->client.modeset_mutex); 161c349dbc7Sjsg drm_client_for_each_modeset(mode_set, &helper->client) { 162746fbbdbSjsg if (!mode_set->crtc->enabled) 163746fbbdbSjsg continue; 164746fbbdbSjsg 165746fbbdbSjsg funcs = mode_set->crtc->helper_private; 1667f4dd379Sjsg if (funcs->mode_set_base_atomic == NULL) 1677f4dd379Sjsg continue; 1687f4dd379Sjsg 1697f4dd379Sjsg if (drm_drv_uses_atomic_modeset(mode_set->crtc->dev)) 1707f4dd379Sjsg continue; 1717f4dd379Sjsg 172746fbbdbSjsg funcs->mode_set_base_atomic(mode_set->crtc, 173746fbbdbSjsg mode_set->fb, 174746fbbdbSjsg mode_set->x, 175746fbbdbSjsg mode_set->y, 176746fbbdbSjsg ENTER_ATOMIC_MODE_SET); 177746fbbdbSjsg } 178c349dbc7Sjsg mutex_unlock(&helper->client.modeset_mutex); 179746fbbdbSjsg } 180746fbbdbSjsg 181746fbbdbSjsg return 0; 182746fbbdbSjsg } 183746fbbdbSjsg EXPORT_SYMBOL(drm_fb_helper_debug_enter); 184746fbbdbSjsg 185e1001332Skettenis /** 1867f4dd379Sjsg * drm_fb_helper_debug_leave - implementation for &fb_ops.fb_debug_leave 187e1001332Skettenis * @info: fbdev registered by the helper 188e1001332Skettenis */ 189e0b53ceeSkettenis int drm_fb_helper_debug_leave(struct fb_info *info) 190746fbbdbSjsg { 191e0b53ceeSkettenis struct drm_fb_helper *helper = info->par; 192c349dbc7Sjsg struct drm_client_dev *client = &helper->client; 193c349dbc7Sjsg #ifdef notyet 194c349dbc7Sjsg struct drm_device *dev = helper->dev; 195c349dbc7Sjsg #endif 196746fbbdbSjsg struct drm_crtc *crtc; 197e0b53ceeSkettenis const struct drm_crtc_helper_funcs *funcs; 198c349dbc7Sjsg struct drm_mode_set *mode_set; 199746fbbdbSjsg struct drm_framebuffer *fb; 200746fbbdbSjsg 201c349dbc7Sjsg mutex_lock(&client->modeset_mutex); 202c349dbc7Sjsg drm_client_for_each_modeset(mode_set, client) { 203746fbbdbSjsg crtc = mode_set->crtc; 2047f4dd379Sjsg if (drm_drv_uses_atomic_modeset(crtc->dev)) 2057f4dd379Sjsg continue; 2067f4dd379Sjsg 207746fbbdbSjsg funcs = crtc->helper_private; 2087f4dd379Sjsg fb = crtc->primary->fb; 209746fbbdbSjsg 210746fbbdbSjsg if (!crtc->enabled) 211746fbbdbSjsg continue; 212746fbbdbSjsg 213746fbbdbSjsg if (!fb) { 214c349dbc7Sjsg drm_err(dev, "no fb to restore?\n"); 215746fbbdbSjsg continue; 216746fbbdbSjsg } 217746fbbdbSjsg 2187f4dd379Sjsg if (funcs->mode_set_base_atomic == NULL) 2197f4dd379Sjsg continue; 2207f4dd379Sjsg 221746fbbdbSjsg drm_fb_helper_restore_lut_atomic(mode_set->crtc); 222746fbbdbSjsg funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, 223746fbbdbSjsg crtc->y, LEAVE_ATOMIC_MODE_SET); 224746fbbdbSjsg } 225c349dbc7Sjsg mutex_unlock(&client->modeset_mutex); 226746fbbdbSjsg 227746fbbdbSjsg return 0; 228746fbbdbSjsg } 229746fbbdbSjsg EXPORT_SYMBOL(drm_fb_helper_debug_leave); 230746fbbdbSjsg 231f5087ea2Sjsg static int 232f5087ea2Sjsg __drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper, 233f5087ea2Sjsg bool force) 234f5087ea2Sjsg { 235f5087ea2Sjsg bool do_delayed; 236f5087ea2Sjsg int ret; 237f5087ea2Sjsg 238f5087ea2Sjsg if (!drm_fbdev_emulation || !fb_helper) 239f5087ea2Sjsg return -ENODEV; 240f5087ea2Sjsg 241f5087ea2Sjsg if (READ_ONCE(fb_helper->deferred_setup)) 242f5087ea2Sjsg return 0; 243f5087ea2Sjsg 244f5087ea2Sjsg #ifdef __OpenBSD__ 245f5087ea2Sjsg force = true; 246f5087ea2Sjsg #endif 247f5087ea2Sjsg 248f5087ea2Sjsg mutex_lock(&fb_helper->lock); 249f5087ea2Sjsg if (force) { 250f5087ea2Sjsg /* 251f5087ea2Sjsg * Yes this is the _locked version which expects the master lock 252f5087ea2Sjsg * to be held. But for forced restores we're intentionally 253f5087ea2Sjsg * racing here, see drm_fb_helper_set_par(). 254f5087ea2Sjsg */ 255f5087ea2Sjsg ret = drm_client_modeset_commit_locked(&fb_helper->client); 256f5087ea2Sjsg } else { 257f5087ea2Sjsg ret = drm_client_modeset_commit(&fb_helper->client); 258f5087ea2Sjsg } 259f5087ea2Sjsg 260f5087ea2Sjsg do_delayed = fb_helper->delayed_hotplug; 261f5087ea2Sjsg if (do_delayed) 262f5087ea2Sjsg fb_helper->delayed_hotplug = false; 263f5087ea2Sjsg mutex_unlock(&fb_helper->lock); 264f5087ea2Sjsg 265f5087ea2Sjsg if (do_delayed) 266f5087ea2Sjsg drm_fb_helper_hotplug_event(fb_helper); 267f5087ea2Sjsg 268f5087ea2Sjsg return ret; 269f5087ea2Sjsg } 270f5087ea2Sjsg 271e0b53ceeSkettenis /** 272e0b53ceeSkettenis * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration 2737f4dd379Sjsg * @fb_helper: driver-allocated fbdev helper, can be NULL 274e0b53ceeSkettenis * 2757f4dd379Sjsg * This should be called from driver's drm &drm_driver.lastclose callback 276e0b53ceeSkettenis * when implementing an fbcon on top of kms using this helper. This ensures that 277e0b53ceeSkettenis * the user isn't greeted with a black screen when e.g. X dies. 278e0b53ceeSkettenis * 279e0b53ceeSkettenis * RETURNS: 280e0b53ceeSkettenis * Zero if everything went ok, negative error code otherwise. 281e0b53ceeSkettenis */ 282e0b53ceeSkettenis int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) 283e0b53ceeSkettenis { 284f5087ea2Sjsg return __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, false); 285e0b53ceeSkettenis } 286e0b53ceeSkettenis EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked); 287746fbbdbSjsg 288746fbbdbSjsg #ifdef CONFIG_MAGIC_SYSRQ 2895ca02815Sjsg /* emergency restore, don't bother with error reporting */ 2905ca02815Sjsg static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) 291e0b53ceeSkettenis { 292e0b53ceeSkettenis struct drm_fb_helper *helper; 293e0b53ceeSkettenis 2945ca02815Sjsg mutex_lock(&kernel_fb_helper_lock); 295e0b53ceeSkettenis list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { 296e0b53ceeSkettenis struct drm_device *dev = helper->dev; 297e0b53ceeSkettenis 298e0b53ceeSkettenis if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) 299e0b53ceeSkettenis continue; 300e0b53ceeSkettenis 3017f4dd379Sjsg mutex_lock(&helper->lock); 3025ca02815Sjsg drm_client_modeset_commit_locked(&helper->client); 3037f4dd379Sjsg mutex_unlock(&helper->lock); 304e0b53ceeSkettenis } 3055ca02815Sjsg mutex_unlock(&kernel_fb_helper_lock); 306e0b53ceeSkettenis } 307e0b53ceeSkettenis 308746fbbdbSjsg static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); 309746fbbdbSjsg 310f005ef32Sjsg static void drm_fb_helper_sysrq(u8 dummy1) 311746fbbdbSjsg { 312746fbbdbSjsg schedule_work(&drm_fb_helper_restore_work); 313746fbbdbSjsg } 314746fbbdbSjsg 315ad8b1aafSjsg static const struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { 316746fbbdbSjsg .handler = drm_fb_helper_sysrq, 317ad8b1aafSjsg .help_msg = "force-fb(v)", 318746fbbdbSjsg .action_msg = "Restore framebuffer console", 319746fbbdbSjsg }; 320c349dbc7Sjsg #else 321ad8b1aafSjsg static const struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { }; 322746fbbdbSjsg #endif 323746fbbdbSjsg 3247f4dd379Sjsg static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) 3257f4dd379Sjsg { 3267f4dd379Sjsg struct drm_fb_helper *fb_helper = info->par; 3277f4dd379Sjsg 3287f4dd379Sjsg mutex_lock(&fb_helper->lock); 329c349dbc7Sjsg drm_client_modeset_dpms(&fb_helper->client, dpms_mode); 3307f4dd379Sjsg mutex_unlock(&fb_helper->lock); 3317f4dd379Sjsg } 3327f4dd379Sjsg 333e1001332Skettenis /** 3347f4dd379Sjsg * drm_fb_helper_blank - implementation for &fb_ops.fb_blank 335e1001332Skettenis * @blank: desired blanking state 336e1001332Skettenis * @info: fbdev registered by the helper 337e1001332Skettenis */ 338d7873f4eSjsg int drm_fb_helper_blank(int blank, struct fb_info *info) 339746fbbdbSjsg { 340e0b53ceeSkettenis if (oops_in_progress) 341e0b53ceeSkettenis return -EBUSY; 342e0b53ceeSkettenis 343746fbbdbSjsg switch (blank) { 344746fbbdbSjsg /* Display: On; HSync: On, VSync: On */ 345746fbbdbSjsg case FB_BLANK_UNBLANK: 346746fbbdbSjsg drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON); 347746fbbdbSjsg break; 348746fbbdbSjsg /* Display: Off; HSync: On, VSync: On */ 349746fbbdbSjsg case FB_BLANK_NORMAL: 350746fbbdbSjsg drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY); 351746fbbdbSjsg break; 352746fbbdbSjsg /* Display: Off; HSync: Off, VSync: On */ 353746fbbdbSjsg case FB_BLANK_HSYNC_SUSPEND: 354746fbbdbSjsg drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY); 355746fbbdbSjsg break; 356746fbbdbSjsg /* Display: Off; HSync: On, VSync: Off */ 357746fbbdbSjsg case FB_BLANK_VSYNC_SUSPEND: 358746fbbdbSjsg drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND); 359746fbbdbSjsg break; 360746fbbdbSjsg /* Display: Off; HSync: Off, VSync: Off */ 361746fbbdbSjsg case FB_BLANK_POWERDOWN: 362746fbbdbSjsg drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF); 363746fbbdbSjsg break; 364746fbbdbSjsg } 365746fbbdbSjsg return 0; 366746fbbdbSjsg } 367746fbbdbSjsg EXPORT_SYMBOL(drm_fb_helper_blank); 368746fbbdbSjsg 3697f4dd379Sjsg static void drm_fb_helper_resume_worker(struct work_struct *work) 3707f4dd379Sjsg { 3717f4dd379Sjsg struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper, 3727f4dd379Sjsg resume_work); 3737f4dd379Sjsg 3747f4dd379Sjsg console_lock(); 375f005ef32Sjsg fb_set_suspend(helper->info, 0); 3767f4dd379Sjsg console_unlock(); 3777f4dd379Sjsg } 3787f4dd379Sjsg 379f005ef32Sjsg static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper) 3807f4dd379Sjsg { 3815ca02815Sjsg struct drm_device *dev = helper->dev; 3825ca02815Sjsg struct drm_clip_rect *clip = &helper->damage_clip; 3837f4dd379Sjsg struct drm_clip_rect clip_copy; 3847f4dd379Sjsg unsigned long flags; 3855ca02815Sjsg int ret; 3867f4dd379Sjsg 387f005ef32Sjsg if (drm_WARN_ON_ONCE(dev, !helper->funcs->fb_dirty)) 388f005ef32Sjsg return; 389f005ef32Sjsg 3905ca02815Sjsg spin_lock_irqsave(&helper->damage_lock, flags); 3917f4dd379Sjsg clip_copy = *clip; 3927f4dd379Sjsg clip->x1 = clip->y1 = ~0; 3937f4dd379Sjsg clip->x2 = clip->y2 = 0; 3945ca02815Sjsg spin_unlock_irqrestore(&helper->damage_lock, flags); 3957f4dd379Sjsg 396f005ef32Sjsg ret = helper->funcs->fb_dirty(helper, &clip_copy); 397f005ef32Sjsg if (ret) 3985ca02815Sjsg goto err; 3995ca02815Sjsg 4005ca02815Sjsg return; 4015ca02815Sjsg 4025ca02815Sjsg err: 4035ca02815Sjsg /* 4045ca02815Sjsg * Restore damage clip rectangle on errors. The next run 4055ca02815Sjsg * of the damage worker will perform the update. 4065ca02815Sjsg */ 4075ca02815Sjsg spin_lock_irqsave(&helper->damage_lock, flags); 4085ca02815Sjsg clip->x1 = min_t(u32, clip->x1, clip_copy.x1); 4095ca02815Sjsg clip->y1 = min_t(u32, clip->y1, clip_copy.y1); 4105ca02815Sjsg clip->x2 = max_t(u32, clip->x2, clip_copy.x2); 4115ca02815Sjsg clip->y2 = max_t(u32, clip->y2, clip_copy.y2); 4125ca02815Sjsg spin_unlock_irqrestore(&helper->damage_lock, flags); 4137f4dd379Sjsg } 4147f4dd379Sjsg 415f005ef32Sjsg static void drm_fb_helper_damage_work(struct work_struct *work) 416f005ef32Sjsg { 417f005ef32Sjsg struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper, damage_work); 418f005ef32Sjsg 419f005ef32Sjsg drm_fb_helper_fb_dirty(helper); 420f005ef32Sjsg } 421f005ef32Sjsg 422e1001332Skettenis /** 423e0b53ceeSkettenis * drm_fb_helper_prepare - setup a drm_fb_helper structure 424e0b53ceeSkettenis * @dev: DRM device 425e0b53ceeSkettenis * @helper: driver-allocated fbdev helper structure to set up 426f005ef32Sjsg * @preferred_bpp: Preferred bits per pixel for the device. 427e0b53ceeSkettenis * @funcs: pointer to structure of functions associate with this helper 428e0b53ceeSkettenis * 429e0b53ceeSkettenis * Sets up the bare minimum to make the framebuffer helper usable. This is 430e0b53ceeSkettenis * useful to implement race-free initialization of the polling helpers. 431e0b53ceeSkettenis */ 432e0b53ceeSkettenis void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, 433f005ef32Sjsg unsigned int preferred_bpp, 434e0b53ceeSkettenis const struct drm_fb_helper_funcs *funcs) 435e0b53ceeSkettenis { 436f005ef32Sjsg /* 437f005ef32Sjsg * Pick a preferred bpp of 32 if no value has been given. This 438f005ef32Sjsg * will select XRGB8888 for the framebuffer formats. All drivers 439f005ef32Sjsg * have to support XRGB8888 for backwards compatibility with legacy 440f005ef32Sjsg * userspace, so it's the safe choice here. 441f005ef32Sjsg * 442f005ef32Sjsg * TODO: Replace struct drm_mode_config.preferred_depth and this 443f005ef32Sjsg * bpp value with a preferred format that is given as struct 444f005ef32Sjsg * drm_format_info. Then derive all other values from the 445f005ef32Sjsg * format. 446f005ef32Sjsg */ 447f005ef32Sjsg if (!preferred_bpp) 448f005ef32Sjsg preferred_bpp = 32; 449f005ef32Sjsg 450e0b53ceeSkettenis INIT_LIST_HEAD(&helper->kernel_fb_list); 4515ca02815Sjsg mtx_init(&helper->damage_lock, IPL_TTY); 4527f4dd379Sjsg INIT_WORK(&helper->resume_work, drm_fb_helper_resume_worker); 4535ca02815Sjsg INIT_WORK(&helper->damage_work, drm_fb_helper_damage_work); 4545ca02815Sjsg helper->damage_clip.x1 = helper->damage_clip.y1 = ~0; 4557f4dd379Sjsg rw_init(&helper->lock, "fbhlk"); 456e0b53ceeSkettenis helper->funcs = funcs; 457e0b53ceeSkettenis helper->dev = dev; 458f005ef32Sjsg helper->preferred_bpp = preferred_bpp; 459e0b53ceeSkettenis } 460e0b53ceeSkettenis EXPORT_SYMBOL(drm_fb_helper_prepare); 461e0b53ceeSkettenis 462e0b53ceeSkettenis /** 463f005ef32Sjsg * drm_fb_helper_unprepare - clean up a drm_fb_helper structure 464f005ef32Sjsg * @fb_helper: driver-allocated fbdev helper structure to set up 465f005ef32Sjsg * 466f005ef32Sjsg * Cleans up the framebuffer helper. Inverse of drm_fb_helper_prepare(). 467f005ef32Sjsg */ 468f005ef32Sjsg void drm_fb_helper_unprepare(struct drm_fb_helper *fb_helper) 469f005ef32Sjsg { 470f005ef32Sjsg mutex_destroy(&fb_helper->lock); 471f005ef32Sjsg } 472f005ef32Sjsg EXPORT_SYMBOL(drm_fb_helper_unprepare); 473f005ef32Sjsg 474f005ef32Sjsg /** 4757f4dd379Sjsg * drm_fb_helper_init - initialize a &struct drm_fb_helper 476e1001332Skettenis * @dev: drm device 477e1001332Skettenis * @fb_helper: driver-allocated fbdev helper structure to initialize 478e1001332Skettenis * 479e1001332Skettenis * This allocates the structures for the fbdev helper with the given limits. 480e1001332Skettenis * Note that this won't yet touch the hardware (through the driver interfaces) 481e1001332Skettenis * nor register the fbdev. This is only done in drm_fb_helper_initial_config() 482e1001332Skettenis * to allow driver writes more control over the exact init sequence. 483e1001332Skettenis * 484e0b53ceeSkettenis * Drivers must call drm_fb_helper_prepare() before calling this function. 485e1001332Skettenis * 486e1001332Skettenis * RETURNS: 487e1001332Skettenis * Zero if everything went ok, nonzero otherwise. 488e1001332Skettenis */ 489d7873f4eSjsg int drm_fb_helper_init(struct drm_device *dev, 490c349dbc7Sjsg struct drm_fb_helper *fb_helper) 491746fbbdbSjsg { 492c349dbc7Sjsg int ret; 493746fbbdbSjsg 494c349dbc7Sjsg /* 495c349dbc7Sjsg * If this is not the generic fbdev client, initialize a drm_client 496c349dbc7Sjsg * without callbacks so we can use the modesets. 497c349dbc7Sjsg */ 498c349dbc7Sjsg if (!fb_helper->client.funcs) { 499c349dbc7Sjsg ret = drm_client_init(dev, &fb_helper->client, "drm_fb_helper", NULL); 500c349dbc7Sjsg if (ret) 501c349dbc7Sjsg return ret; 502746fbbdbSjsg } 503746fbbdbSjsg 5047f4dd379Sjsg dev->fb_helper = fb_helper; 505e0b53ceeSkettenis 506746fbbdbSjsg return 0; 507746fbbdbSjsg } 508746fbbdbSjsg EXPORT_SYMBOL(drm_fb_helper_init); 509746fbbdbSjsg 510e0b53ceeSkettenis /** 511f005ef32Sjsg * drm_fb_helper_alloc_info - allocate fb_info and some of its members 512e0b53ceeSkettenis * @fb_helper: driver-allocated fbdev helper 513e0b53ceeSkettenis * 514f005ef32Sjsg * A helper to alloc fb_info and the member cmap. Called by the driver 515f005ef32Sjsg * within the fb_probe fb_helper callback function. Drivers do not 5167f4dd379Sjsg * need to release the allocated fb_info structure themselves, this is 5177f4dd379Sjsg * automatically done when calling drm_fb_helper_fini(). 518e0b53ceeSkettenis * 519e0b53ceeSkettenis * RETURNS: 520e0b53ceeSkettenis * fb_info pointer if things went okay, pointer containing error code 521e0b53ceeSkettenis * otherwise 522e0b53ceeSkettenis */ 523f005ef32Sjsg struct fb_info *drm_fb_helper_alloc_info(struct drm_fb_helper *fb_helper) 524746fbbdbSjsg { 525e0b53ceeSkettenis struct device *dev = fb_helper->dev->dev; 526e0b53ceeSkettenis struct fb_info *info; 527e0b53ceeSkettenis #ifdef __linux__ 528e0b53ceeSkettenis int ret; 529e0b53ceeSkettenis #endif 530e0b53ceeSkettenis 531e0b53ceeSkettenis info = framebuffer_alloc(0, dev); 532e0b53ceeSkettenis if (!info) 533e0b53ceeSkettenis return ERR_PTR(-ENOMEM); 534e0b53ceeSkettenis 535487b4255Sjsg if (!drm_leak_fbdev_smem) 536487b4255Sjsg info->flags |= FBINFO_HIDE_SMEM_START; 537487b4255Sjsg 5387f4dd379Sjsg #ifdef __linux__ 539e0b53ceeSkettenis ret = fb_alloc_cmap(&info->cmap, 256, 0); 540e0b53ceeSkettenis if (ret) 541e0b53ceeSkettenis goto err_release; 542e0b53ceeSkettenis #endif 543e0b53ceeSkettenis 544f005ef32Sjsg fb_helper->info = info; 545c349dbc7Sjsg info->skip_vt_switch = true; 546e0b53ceeSkettenis 547e0b53ceeSkettenis return info; 548e0b53ceeSkettenis 549e0b53ceeSkettenis #ifdef __linux__ 550e0b53ceeSkettenis err_release: 551e0b53ceeSkettenis framebuffer_release(info); 552e0b53ceeSkettenis return ERR_PTR(ret); 553746fbbdbSjsg #endif 554746fbbdbSjsg } 555f005ef32Sjsg EXPORT_SYMBOL(drm_fb_helper_alloc_info); 556e0b53ceeSkettenis 557e0b53ceeSkettenis /** 558f005ef32Sjsg * drm_fb_helper_release_info - release fb_info and its members 559f005ef32Sjsg * @fb_helper: driver-allocated fbdev helper 560f005ef32Sjsg * 561f005ef32Sjsg * A helper to release fb_info and the member cmap. Drivers do not 562f005ef32Sjsg * need to release the allocated fb_info structure themselves, this is 563f005ef32Sjsg * automatically done when calling drm_fb_helper_fini(). 564f005ef32Sjsg */ 565f005ef32Sjsg void drm_fb_helper_release_info(struct drm_fb_helper *fb_helper) 566f005ef32Sjsg { 567f005ef32Sjsg struct fb_info *info = fb_helper->info; 568f005ef32Sjsg 569f005ef32Sjsg if (!info) 570f005ef32Sjsg return; 571f005ef32Sjsg 572f005ef32Sjsg fb_helper->info = NULL; 573f005ef32Sjsg 574f005ef32Sjsg #ifdef __linux__ 575f005ef32Sjsg if (info->cmap.len) 576f005ef32Sjsg fb_dealloc_cmap(&info->cmap); 577f005ef32Sjsg #endif 578f005ef32Sjsg framebuffer_release(info); 579f005ef32Sjsg } 580f005ef32Sjsg EXPORT_SYMBOL(drm_fb_helper_release_info); 581f005ef32Sjsg 582f005ef32Sjsg /** 583f005ef32Sjsg * drm_fb_helper_unregister_info - unregister fb_info framebuffer device 5847f4dd379Sjsg * @fb_helper: driver-allocated fbdev helper, can be NULL 585e0b53ceeSkettenis * 586e0b53ceeSkettenis * A wrapper around unregister_framebuffer, to release the fb_info 5877f4dd379Sjsg * framebuffer device. This must be called before releasing all resources for 5887f4dd379Sjsg * @fb_helper by calling drm_fb_helper_fini(). 589e0b53ceeSkettenis */ 590f005ef32Sjsg void drm_fb_helper_unregister_info(struct drm_fb_helper *fb_helper) 591e0b53ceeSkettenis { 592f005ef32Sjsg if (fb_helper && fb_helper->info) 593f005ef32Sjsg unregister_framebuffer(fb_helper->info); 594e0b53ceeSkettenis } 595f005ef32Sjsg EXPORT_SYMBOL(drm_fb_helper_unregister_info); 596e0b53ceeSkettenis 597e0b53ceeSkettenis /** 5987f4dd379Sjsg * drm_fb_helper_fini - finialize a &struct drm_fb_helper 5997f4dd379Sjsg * @fb_helper: driver-allocated fbdev helper, can be NULL 600e0b53ceeSkettenis * 601c349dbc7Sjsg * This cleans up all remaining resources associated with @fb_helper. 602e0b53ceeSkettenis */ 6037f4dd379Sjsg void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) 604e0b53ceeSkettenis { 6057f4dd379Sjsg if (!fb_helper) 6067f4dd379Sjsg return; 6077f4dd379Sjsg 6087f4dd379Sjsg fb_helper->dev->fb_helper = NULL; 6097f4dd379Sjsg 6107f4dd379Sjsg if (!drm_fbdev_emulation) 6117f4dd379Sjsg return; 6127f4dd379Sjsg 6137f4dd379Sjsg cancel_work_sync(&fb_helper->resume_work); 6145ca02815Sjsg cancel_work_sync(&fb_helper->damage_work); 6157f4dd379Sjsg 616f005ef32Sjsg drm_fb_helper_release_info(fb_helper); 617e0b53ceeSkettenis 6187f4dd379Sjsg mutex_lock(&kernel_fb_helper_lock); 619e0b53ceeSkettenis if (!list_empty(&fb_helper->kernel_fb_list)) { 620e0b53ceeSkettenis list_del(&fb_helper->kernel_fb_list); 6217f4dd379Sjsg if (list_empty(&kernel_fb_helper_list)) 622e0b53ceeSkettenis unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); 623746fbbdbSjsg } 6247f4dd379Sjsg mutex_unlock(&kernel_fb_helper_lock); 625746fbbdbSjsg 626c349dbc7Sjsg if (!fb_helper->client.funcs) 627c349dbc7Sjsg drm_client_release(&fb_helper->client); 628746fbbdbSjsg } 629746fbbdbSjsg EXPORT_SYMBOL(drm_fb_helper_fini); 630746fbbdbSjsg 6311bb76ff1Sjsg #ifdef __linux__ 6321bb76ff1Sjsg 633f005ef32Sjsg static void drm_fb_helper_add_damage_clip(struct drm_fb_helper *helper, u32 x, u32 y, 6347f4dd379Sjsg u32 width, u32 height) 6357f4dd379Sjsg { 6365ca02815Sjsg struct drm_clip_rect *clip = &helper->damage_clip; 6377f4dd379Sjsg unsigned long flags; 6387f4dd379Sjsg 6395ca02815Sjsg spin_lock_irqsave(&helper->damage_lock, flags); 6407f4dd379Sjsg clip->x1 = min_t(u32, clip->x1, x); 6417f4dd379Sjsg clip->y1 = min_t(u32, clip->y1, y); 6427f4dd379Sjsg clip->x2 = max_t(u32, clip->x2, x + width); 6437f4dd379Sjsg clip->y2 = max_t(u32, clip->y2, y + height); 6445ca02815Sjsg spin_unlock_irqrestore(&helper->damage_lock, flags); 645f005ef32Sjsg } 646f005ef32Sjsg 647f005ef32Sjsg static void drm_fb_helper_damage(struct drm_fb_helper *helper, u32 x, u32 y, 648f005ef32Sjsg u32 width, u32 height) 649f005ef32Sjsg { 650*61e353b9Sjsg /* 651*61e353b9Sjsg * This function may be invoked by panic() to flush the frame 652*61e353b9Sjsg * buffer, where all CPUs except the panic CPU are stopped. 653*61e353b9Sjsg * During the following schedule_work(), the panic CPU needs 654*61e353b9Sjsg * the worker_pool lock, which might be held by a stopped CPU, 655*61e353b9Sjsg * causing schedule_work() and panic() to block. Return early on 656*61e353b9Sjsg * oops_in_progress to prevent this blocking. 657*61e353b9Sjsg */ 658*61e353b9Sjsg if (oops_in_progress) 659*61e353b9Sjsg return; 660*61e353b9Sjsg 661f005ef32Sjsg drm_fb_helper_add_damage_clip(helper, x, y, width, height); 6627f4dd379Sjsg 6635ca02815Sjsg schedule_work(&helper->damage_work); 6647f4dd379Sjsg } 6657f4dd379Sjsg 6661bb76ff1Sjsg /* 6671bb76ff1Sjsg * Convert memory region into area of scanlines and pixels per 6681bb76ff1Sjsg * scanline. The parameters off and len must not reach beyond 6691bb76ff1Sjsg * the end of the framebuffer. 6701bb76ff1Sjsg */ 6711bb76ff1Sjsg static void drm_fb_helper_memory_range_to_clip(struct fb_info *info, off_t off, size_t len, 6721bb76ff1Sjsg struct drm_rect *clip) 6731bb76ff1Sjsg { 67433bc3dfaSjsg u32 line_length = info->fix.line_length; 67533bc3dfaSjsg u32 fb_height = info->var.yres; 6761bb76ff1Sjsg off_t end = off + len; 6771bb76ff1Sjsg u32 x1 = 0; 67833bc3dfaSjsg u32 y1 = off / line_length; 6791bb76ff1Sjsg u32 x2 = info->var.xres; 68033bc3dfaSjsg u32 y2 = DIV_ROUND_UP(end, line_length); 68133bc3dfaSjsg 68233bc3dfaSjsg /* Don't allow any of them beyond the bottom bound of display area */ 68333bc3dfaSjsg if (y1 > fb_height) 68433bc3dfaSjsg y1 = fb_height; 68533bc3dfaSjsg if (y2 > fb_height) 68633bc3dfaSjsg y2 = fb_height; 6871bb76ff1Sjsg 6881bb76ff1Sjsg if ((y2 - y1) == 1) { 6891bb76ff1Sjsg /* 6901bb76ff1Sjsg * We've only written to a single scanline. Try to reduce 6911bb76ff1Sjsg * the number of horizontal pixels that need an update. 6921bb76ff1Sjsg */ 69333bc3dfaSjsg off_t bit_off = (off % line_length) * 8; 69433bc3dfaSjsg off_t bit_end = (end % line_length) * 8; 6951bb76ff1Sjsg 6961bb76ff1Sjsg x1 = bit_off / info->var.bits_per_pixel; 6971bb76ff1Sjsg x2 = DIV_ROUND_UP(bit_end, info->var.bits_per_pixel); 6981bb76ff1Sjsg } 6991bb76ff1Sjsg 7001bb76ff1Sjsg drm_rect_init(clip, x1, y1, x2 - x1, y2 - y1); 7011bb76ff1Sjsg } 7021bb76ff1Sjsg 703f005ef32Sjsg /* Don't use in new code. */ 704f005ef32Sjsg void drm_fb_helper_damage_range(struct fb_info *info, off_t off, size_t len) 705f005ef32Sjsg { 706f005ef32Sjsg struct drm_fb_helper *fb_helper = info->par; 707f005ef32Sjsg struct drm_rect damage_area; 708f005ef32Sjsg 709f005ef32Sjsg drm_fb_helper_memory_range_to_clip(info, off, len, &damage_area); 710f005ef32Sjsg drm_fb_helper_damage(fb_helper, damage_area.x1, damage_area.y1, 711f005ef32Sjsg drm_rect_width(&damage_area), 712f005ef32Sjsg drm_rect_height(&damage_area)); 713f005ef32Sjsg } 714f005ef32Sjsg EXPORT_SYMBOL(drm_fb_helper_damage_range); 715f005ef32Sjsg 716f005ef32Sjsg /* Don't use in new code. */ 717f005ef32Sjsg void drm_fb_helper_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u32 height) 718f005ef32Sjsg { 719f005ef32Sjsg struct drm_fb_helper *fb_helper = info->par; 720f005ef32Sjsg 721f005ef32Sjsg drm_fb_helper_damage(fb_helper, x, y, width, height); 722f005ef32Sjsg } 723f005ef32Sjsg EXPORT_SYMBOL(drm_fb_helper_damage_area); 724f005ef32Sjsg 7257f4dd379Sjsg /** 7267f4dd379Sjsg * drm_fb_helper_deferred_io() - fbdev deferred_io callback function 7277f4dd379Sjsg * @info: fb_info struct pointer 7281bb76ff1Sjsg * @pagereflist: list of mmap framebuffer pages that have to be flushed 7297f4dd379Sjsg * 7307f4dd379Sjsg * This function is used as the &fb_deferred_io.deferred_io 7317f4dd379Sjsg * callback function for flushing the fbdev mmap writes. 7327f4dd379Sjsg */ 7331bb76ff1Sjsg void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist) 7347f4dd379Sjsg { 735f005ef32Sjsg struct drm_fb_helper *helper = info->par; 736f005ef32Sjsg unsigned long start, end, min_off, max_off, total_size; 7371bb76ff1Sjsg struct fb_deferred_io_pageref *pageref; 7381bb76ff1Sjsg struct drm_rect damage_area; 7397f4dd379Sjsg 7401bb76ff1Sjsg min_off = ULONG_MAX; 7411bb76ff1Sjsg max_off = 0; 7421bb76ff1Sjsg list_for_each_entry(pageref, pagereflist, list) { 7431bb76ff1Sjsg start = pageref->offset; 7441bb76ff1Sjsg end = start + PAGE_SIZE; 7451bb76ff1Sjsg min_off = min(min_off, start); 7461bb76ff1Sjsg max_off = max(max_off, end); 7477f4dd379Sjsg } 7487f4dd379Sjsg 7491bb76ff1Sjsg /* 7501bb76ff1Sjsg * As we can only track pages, we might reach beyond the end 7511bb76ff1Sjsg * of the screen and account for non-existing scanlines. Hence, 7521bb76ff1Sjsg * keep the covered memory area within the screen buffer. 7531bb76ff1Sjsg */ 754f005ef32Sjsg if (info->screen_size) 755f005ef32Sjsg total_size = info->screen_size; 756f005ef32Sjsg else 757f005ef32Sjsg total_size = info->fix.smem_len; 758f005ef32Sjsg max_off = min(max_off, total_size); 7591bb76ff1Sjsg 760f005ef32Sjsg if (min_off < max_off) { 7611bb76ff1Sjsg drm_fb_helper_memory_range_to_clip(info, min_off, max_off - min_off, &damage_area); 762f005ef32Sjsg drm_fb_helper_damage(helper, damage_area.x1, damage_area.y1, 7631bb76ff1Sjsg drm_rect_width(&damage_area), 7641bb76ff1Sjsg drm_rect_height(&damage_area)); 7657f4dd379Sjsg } 766f005ef32Sjsg } 7677f4dd379Sjsg EXPORT_SYMBOL(drm_fb_helper_deferred_io); 7687f4dd379Sjsg 769c349dbc7Sjsg #endif /* __linux__ */ 7707f4dd379Sjsg 771e0b53ceeSkettenis /** 772e0b53ceeSkettenis * drm_fb_helper_set_suspend - wrapper around fb_set_suspend 7737f4dd379Sjsg * @fb_helper: driver-allocated fbdev helper, can be NULL 7747f4dd379Sjsg * @suspend: whether to suspend or resume 775e0b53ceeSkettenis * 7767f4dd379Sjsg * A wrapper around fb_set_suspend implemented by fbdev core. 7777f4dd379Sjsg * Use drm_fb_helper_set_suspend_unlocked() if you don't need to take 7787f4dd379Sjsg * the lock yourself 779e0b53ceeSkettenis */ 7807f4dd379Sjsg void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend) 781e0b53ceeSkettenis { 782f005ef32Sjsg #ifdef __linux__ 783f005ef32Sjsg if (fb_helper && fb_helper->info) 784f005ef32Sjsg fb_set_suspend(fb_helper->info, suspend); 785f005ef32Sjsg #endif 786e0b53ceeSkettenis } 787e0b53ceeSkettenis EXPORT_SYMBOL(drm_fb_helper_set_suspend); 788e0b53ceeSkettenis 7897f4dd379Sjsg /** 7907f4dd379Sjsg * drm_fb_helper_set_suspend_unlocked - wrapper around fb_set_suspend that also 7917f4dd379Sjsg * takes the console lock 7927f4dd379Sjsg * @fb_helper: driver-allocated fbdev helper, can be NULL 7937f4dd379Sjsg * @suspend: whether to suspend or resume 7947f4dd379Sjsg * 7957f4dd379Sjsg * A wrapper around fb_set_suspend() that takes the console lock. If the lock 7967f4dd379Sjsg * isn't available on resume, a worker is tasked with waiting for the lock 7977f4dd379Sjsg * to become available. The console lock can be pretty contented on resume 7987f4dd379Sjsg * due to all the printk activity. 7997f4dd379Sjsg * 8007f4dd379Sjsg * This function can be called multiple times with the same state since 8017f4dd379Sjsg * &fb_info.state is checked to see if fbdev is running or not before locking. 8027f4dd379Sjsg * 8037f4dd379Sjsg * Use drm_fb_helper_set_suspend() if you need to take the lock yourself. 8047f4dd379Sjsg */ 8057f4dd379Sjsg void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper, 8067f4dd379Sjsg bool suspend) 807746fbbdbSjsg { 808977aa752Sjsg #ifdef __linux__ 809f005ef32Sjsg if (!fb_helper || !fb_helper->info) 8107f4dd379Sjsg return; 811746fbbdbSjsg 8127f4dd379Sjsg /* make sure there's no pending/ongoing resume */ 8137f4dd379Sjsg flush_work(&fb_helper->resume_work); 8147f4dd379Sjsg 8157f4dd379Sjsg if (suspend) { 816f005ef32Sjsg if (fb_helper->info->state != FBINFO_STATE_RUNNING) 8177f4dd379Sjsg return; 8187f4dd379Sjsg 8197f4dd379Sjsg console_lock(); 8207f4dd379Sjsg 8217f4dd379Sjsg } else { 822f005ef32Sjsg if (fb_helper->info->state == FBINFO_STATE_RUNNING) 8237f4dd379Sjsg return; 8247f4dd379Sjsg 8257f4dd379Sjsg if (!console_trylock()) { 8267f4dd379Sjsg schedule_work(&fb_helper->resume_work); 8277f4dd379Sjsg return; 8287f4dd379Sjsg } 8297f4dd379Sjsg } 8307f4dd379Sjsg 831f005ef32Sjsg fb_set_suspend(fb_helper->info, suspend); 8327f4dd379Sjsg console_unlock(); 8337f4dd379Sjsg #endif 8347f4dd379Sjsg } 8357f4dd379Sjsg EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked); 8367f4dd379Sjsg 8377f4dd379Sjsg #ifdef __linux__ 8387f4dd379Sjsg 8397f4dd379Sjsg static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info) 8407f4dd379Sjsg { 8417f4dd379Sjsg u32 *palette = (u32 *)info->pseudo_palette; 8427f4dd379Sjsg int i; 8437f4dd379Sjsg 8447f4dd379Sjsg if (cmap->start + cmap->len > 16) 845746fbbdbSjsg return -EINVAL; 8467f4dd379Sjsg 8477f4dd379Sjsg for (i = 0; i < cmap->len; ++i) { 8487f4dd379Sjsg u16 red = cmap->red[i]; 8497f4dd379Sjsg u16 green = cmap->green[i]; 8507f4dd379Sjsg u16 blue = cmap->blue[i]; 8517f4dd379Sjsg u32 value; 8527f4dd379Sjsg 8537f4dd379Sjsg red >>= 16 - info->var.red.length; 8547f4dd379Sjsg green >>= 16 - info->var.green.length; 8557f4dd379Sjsg blue >>= 16 - info->var.blue.length; 856746fbbdbSjsg value = (red << info->var.red.offset) | 857746fbbdbSjsg (green << info->var.green.offset) | 858746fbbdbSjsg (blue << info->var.blue.offset); 859746fbbdbSjsg if (info->var.transp.length > 0) { 860746fbbdbSjsg u32 mask = (1 << info->var.transp.length) - 1; 8617f4dd379Sjsg 862746fbbdbSjsg mask <<= info->var.transp.offset; 863746fbbdbSjsg value |= mask; 864746fbbdbSjsg } 8657f4dd379Sjsg palette[cmap->start + i] = value; 8667f4dd379Sjsg } 8677f4dd379Sjsg 868746fbbdbSjsg return 0; 869746fbbdbSjsg } 870746fbbdbSjsg 8717f4dd379Sjsg static int setcmap_legacy(struct fb_cmap *cmap, struct fb_info *info) 8727f4dd379Sjsg { 8737f4dd379Sjsg struct drm_fb_helper *fb_helper = info->par; 874c349dbc7Sjsg struct drm_mode_set *modeset; 8757f4dd379Sjsg struct drm_crtc *crtc; 8767f4dd379Sjsg u16 *r, *g, *b; 877c349dbc7Sjsg int ret = 0; 8787f4dd379Sjsg 8797f4dd379Sjsg drm_modeset_lock_all(fb_helper->dev); 880c349dbc7Sjsg drm_client_for_each_modeset(modeset, &fb_helper->client) { 881c349dbc7Sjsg crtc = modeset->crtc; 882ad8b1aafSjsg if (!crtc->funcs->gamma_set || !crtc->gamma_size) { 883ad8b1aafSjsg ret = -EINVAL; 884ad8b1aafSjsg goto out; 885ad8b1aafSjsg } 886e1001332Skettenis 887ad8b1aafSjsg if (cmap->start + cmap->len > crtc->gamma_size) { 888ad8b1aafSjsg ret = -EINVAL; 889ad8b1aafSjsg goto out; 890ad8b1aafSjsg } 891746fbbdbSjsg 8927f4dd379Sjsg r = crtc->gamma_store; 8937f4dd379Sjsg g = r + crtc->gamma_size; 8947f4dd379Sjsg b = g + crtc->gamma_size; 8957f4dd379Sjsg 8967f4dd379Sjsg memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r)); 8977f4dd379Sjsg memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g)); 8987f4dd379Sjsg memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b)); 8997f4dd379Sjsg 9007f4dd379Sjsg ret = crtc->funcs->gamma_set(crtc, r, g, b, 9017f4dd379Sjsg crtc->gamma_size, NULL); 9027f4dd379Sjsg if (ret) 903ad8b1aafSjsg goto out; 9047f4dd379Sjsg } 905ad8b1aafSjsg out: 9067f4dd379Sjsg drm_modeset_unlock_all(fb_helper->dev); 9077f4dd379Sjsg 9087f4dd379Sjsg return ret; 9097f4dd379Sjsg } 9107f4dd379Sjsg 9117f4dd379Sjsg static struct drm_property_blob *setcmap_new_gamma_lut(struct drm_crtc *crtc, 9127f4dd379Sjsg struct fb_cmap *cmap) 9137f4dd379Sjsg { 9147f4dd379Sjsg struct drm_device *dev = crtc->dev; 9157f4dd379Sjsg struct drm_property_blob *gamma_lut; 9167f4dd379Sjsg struct drm_color_lut *lut; 9177f4dd379Sjsg int size = crtc->gamma_size; 918746fbbdbSjsg int i; 9197f4dd379Sjsg 9207f4dd379Sjsg if (!size || cmap->start + cmap->len > size) 9217f4dd379Sjsg return ERR_PTR(-EINVAL); 9227f4dd379Sjsg 9237f4dd379Sjsg gamma_lut = drm_property_create_blob(dev, sizeof(*lut) * size, NULL); 9247f4dd379Sjsg if (IS_ERR(gamma_lut)) 9257f4dd379Sjsg return gamma_lut; 9267f4dd379Sjsg 9277f4dd379Sjsg lut = gamma_lut->data; 9287f4dd379Sjsg if (cmap->start || cmap->len != size) { 9297f4dd379Sjsg u16 *r = crtc->gamma_store; 9307f4dd379Sjsg u16 *g = r + crtc->gamma_size; 9317f4dd379Sjsg u16 *b = g + crtc->gamma_size; 9327f4dd379Sjsg 9337f4dd379Sjsg for (i = 0; i < cmap->start; i++) { 9347f4dd379Sjsg lut[i].red = r[i]; 9357f4dd379Sjsg lut[i].green = g[i]; 9367f4dd379Sjsg lut[i].blue = b[i]; 937746fbbdbSjsg } 9387f4dd379Sjsg for (i = cmap->start + cmap->len; i < size; i++) { 9397f4dd379Sjsg lut[i].red = r[i]; 9407f4dd379Sjsg lut[i].green = g[i]; 9417f4dd379Sjsg lut[i].blue = b[i]; 942746fbbdbSjsg } 943746fbbdbSjsg } 944746fbbdbSjsg 9457f4dd379Sjsg for (i = 0; i < cmap->len; i++) { 9467f4dd379Sjsg lut[cmap->start + i].red = cmap->red[i]; 9477f4dd379Sjsg lut[cmap->start + i].green = cmap->green[i]; 9487f4dd379Sjsg lut[cmap->start + i].blue = cmap->blue[i]; 9497f4dd379Sjsg } 9507f4dd379Sjsg 9517f4dd379Sjsg return gamma_lut; 9527f4dd379Sjsg } 9537f4dd379Sjsg 9547f4dd379Sjsg static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info) 9557f4dd379Sjsg { 9567f4dd379Sjsg struct drm_fb_helper *fb_helper = info->par; 9577f4dd379Sjsg struct drm_device *dev = fb_helper->dev; 9587f4dd379Sjsg struct drm_property_blob *gamma_lut = NULL; 9597f4dd379Sjsg struct drm_modeset_acquire_ctx ctx; 9607f4dd379Sjsg struct drm_crtc_state *crtc_state; 9617f4dd379Sjsg struct drm_atomic_state *state; 962c349dbc7Sjsg struct drm_mode_set *modeset; 9637f4dd379Sjsg struct drm_crtc *crtc; 9647f4dd379Sjsg u16 *r, *g, *b; 9657f4dd379Sjsg bool replaced; 966c349dbc7Sjsg int ret = 0; 9677f4dd379Sjsg 9687f4dd379Sjsg drm_modeset_acquire_init(&ctx, 0); 9697f4dd379Sjsg 9707f4dd379Sjsg state = drm_atomic_state_alloc(dev); 9717f4dd379Sjsg if (!state) { 9727f4dd379Sjsg ret = -ENOMEM; 9737f4dd379Sjsg goto out_ctx; 9747f4dd379Sjsg } 9757f4dd379Sjsg 9767f4dd379Sjsg state->acquire_ctx = &ctx; 9777f4dd379Sjsg retry: 978c349dbc7Sjsg drm_client_for_each_modeset(modeset, &fb_helper->client) { 979c349dbc7Sjsg crtc = modeset->crtc; 9807f4dd379Sjsg 9817f4dd379Sjsg if (!gamma_lut) 9827f4dd379Sjsg gamma_lut = setcmap_new_gamma_lut(crtc, cmap); 9837f4dd379Sjsg if (IS_ERR(gamma_lut)) { 9847f4dd379Sjsg ret = PTR_ERR(gamma_lut); 9857f4dd379Sjsg gamma_lut = NULL; 9867f4dd379Sjsg goto out_state; 9877f4dd379Sjsg } 9887f4dd379Sjsg 9897f4dd379Sjsg crtc_state = drm_atomic_get_crtc_state(state, crtc); 9907f4dd379Sjsg if (IS_ERR(crtc_state)) { 9917f4dd379Sjsg ret = PTR_ERR(crtc_state); 9927f4dd379Sjsg goto out_state; 9937f4dd379Sjsg } 9947f4dd379Sjsg 9955ca02815Sjsg /* 9965ca02815Sjsg * FIXME: This always uses gamma_lut. Some HW have only 9975ca02815Sjsg * degamma_lut, in which case we should reset gamma_lut and set 9985ca02815Sjsg * degamma_lut. See drm_crtc_legacy_gamma_set(). 9995ca02815Sjsg */ 10007f4dd379Sjsg replaced = drm_property_replace_blob(&crtc_state->degamma_lut, 10017f4dd379Sjsg NULL); 10027f4dd379Sjsg replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL); 10037f4dd379Sjsg replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, 10047f4dd379Sjsg gamma_lut); 10057f4dd379Sjsg crtc_state->color_mgmt_changed |= replaced; 10067f4dd379Sjsg } 10077f4dd379Sjsg 10087f4dd379Sjsg ret = drm_atomic_commit(state); 10097f4dd379Sjsg if (ret) 10107f4dd379Sjsg goto out_state; 10117f4dd379Sjsg 1012c349dbc7Sjsg drm_client_for_each_modeset(modeset, &fb_helper->client) { 1013c349dbc7Sjsg crtc = modeset->crtc; 10147f4dd379Sjsg 10157f4dd379Sjsg r = crtc->gamma_store; 10167f4dd379Sjsg g = r + crtc->gamma_size; 10177f4dd379Sjsg b = g + crtc->gamma_size; 10187f4dd379Sjsg 10197f4dd379Sjsg memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r)); 10207f4dd379Sjsg memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g)); 10217f4dd379Sjsg memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b)); 10227f4dd379Sjsg } 10237f4dd379Sjsg 10247f4dd379Sjsg out_state: 10257f4dd379Sjsg if (ret == -EDEADLK) 10267f4dd379Sjsg goto backoff; 10277f4dd379Sjsg 10287f4dd379Sjsg drm_property_blob_put(gamma_lut); 10297f4dd379Sjsg drm_atomic_state_put(state); 10307f4dd379Sjsg out_ctx: 10317f4dd379Sjsg drm_modeset_drop_locks(&ctx); 10327f4dd379Sjsg drm_modeset_acquire_fini(&ctx); 10337f4dd379Sjsg 10347f4dd379Sjsg return ret; 10357f4dd379Sjsg 10367f4dd379Sjsg backoff: 10377f4dd379Sjsg drm_atomic_state_clear(state); 10387f4dd379Sjsg drm_modeset_backoff(&ctx); 10397f4dd379Sjsg goto retry; 1040746fbbdbSjsg } 1041746fbbdbSjsg 1042e1001332Skettenis /** 10437f4dd379Sjsg * drm_fb_helper_setcmap - implementation for &fb_ops.fb_setcmap 1044e1001332Skettenis * @cmap: cmap to set 1045e1001332Skettenis * @info: fbdev registered by the helper 1046e1001332Skettenis */ 1047d7873f4eSjsg int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) 1048746fbbdbSjsg { 1049746fbbdbSjsg struct drm_fb_helper *fb_helper = info->par; 1050c349dbc7Sjsg struct drm_device *dev = fb_helper->dev; 10517f4dd379Sjsg int ret; 1052746fbbdbSjsg 1053e0b53ceeSkettenis if (oops_in_progress) 1054e0b53ceeSkettenis return -EBUSY; 1055e0b53ceeSkettenis 10567f4dd379Sjsg mutex_lock(&fb_helper->lock); 10577f4dd379Sjsg 1058c349dbc7Sjsg if (!drm_master_internal_acquire(dev)) { 10597f4dd379Sjsg ret = -EBUSY; 1060c349dbc7Sjsg goto unlock; 1061746fbbdbSjsg } 10627f4dd379Sjsg 1063c349dbc7Sjsg mutex_lock(&fb_helper->client.modeset_mutex); 10647f4dd379Sjsg if (info->fix.visual == FB_VISUAL_TRUECOLOR) 10657f4dd379Sjsg ret = setcmap_pseudo_palette(cmap, info); 10667f4dd379Sjsg else if (drm_drv_uses_atomic_modeset(fb_helper->dev)) 10677f4dd379Sjsg ret = setcmap_atomic(cmap, info); 10687f4dd379Sjsg else 10697f4dd379Sjsg ret = setcmap_legacy(cmap, info); 1070c349dbc7Sjsg mutex_unlock(&fb_helper->client.modeset_mutex); 10717f4dd379Sjsg 1072c349dbc7Sjsg drm_master_internal_release(dev); 1073c349dbc7Sjsg unlock: 10747f4dd379Sjsg mutex_unlock(&fb_helper->lock); 10757f4dd379Sjsg 10767f4dd379Sjsg return ret; 1077746fbbdbSjsg } 1078746fbbdbSjsg EXPORT_SYMBOL(drm_fb_helper_setcmap); 1079746fbbdbSjsg 1080e1001332Skettenis /** 10817f4dd379Sjsg * drm_fb_helper_ioctl - legacy ioctl implementation 1082e1001332Skettenis * @info: fbdev registered by the helper 10837f4dd379Sjsg * @cmd: ioctl command 10847f4dd379Sjsg * @arg: ioctl argument 10857f4dd379Sjsg * 10867f4dd379Sjsg * A helper to implement the standard fbdev ioctl. Only 10877f4dd379Sjsg * FBIO_WAITFORVSYNC is implemented for now. 1088e1001332Skettenis */ 10897f4dd379Sjsg int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, 10907f4dd379Sjsg unsigned long arg) 1091746fbbdbSjsg { 1092746fbbdbSjsg struct drm_fb_helper *fb_helper = info->par; 1093c349dbc7Sjsg struct drm_device *dev = fb_helper->dev; 10947f4dd379Sjsg struct drm_crtc *crtc; 10957f4dd379Sjsg int ret = 0; 1096746fbbdbSjsg 10977f4dd379Sjsg mutex_lock(&fb_helper->lock); 1098c349dbc7Sjsg if (!drm_master_internal_acquire(dev)) { 10997f4dd379Sjsg ret = -EBUSY; 11007f4dd379Sjsg goto unlock; 1101746fbbdbSjsg } 1102746fbbdbSjsg 11037f4dd379Sjsg switch (cmd) { 11047f4dd379Sjsg case FBIO_WAITFORVSYNC: 11057f4dd379Sjsg /* 11067f4dd379Sjsg * Only consider the first CRTC. 11077f4dd379Sjsg * 11087f4dd379Sjsg * This ioctl is supposed to take the CRTC number as 11097f4dd379Sjsg * an argument, but in fbdev times, what that number 11107f4dd379Sjsg * was supposed to be was quite unclear, different 11117f4dd379Sjsg * drivers were passing that argument differently 11127f4dd379Sjsg * (some by reference, some by value), and most of the 11137f4dd379Sjsg * userspace applications were just hardcoding 0 as an 11147f4dd379Sjsg * argument. 11157f4dd379Sjsg * 11167f4dd379Sjsg * The first CRTC should be the integrated panel on 11177f4dd379Sjsg * most drivers, so this is the best choice we can 11187f4dd379Sjsg * make. If we're not smart enough here, one should 11197f4dd379Sjsg * just consider switch the userspace to KMS. 11207f4dd379Sjsg */ 1121c349dbc7Sjsg crtc = fb_helper->client.modesets[0].crtc; 11227f4dd379Sjsg 11237f4dd379Sjsg /* 11247f4dd379Sjsg * Only wait for a vblank event if the CRTC is 11257f4dd379Sjsg * enabled, otherwise just don't do anythintg, 11267f4dd379Sjsg * not even report an error. 11277f4dd379Sjsg */ 11287f4dd379Sjsg ret = drm_crtc_vblank_get(crtc); 11297f4dd379Sjsg if (!ret) { 11307f4dd379Sjsg drm_crtc_wait_one_vblank(crtc); 11317f4dd379Sjsg drm_crtc_vblank_put(crtc); 11327f4dd379Sjsg } 11337f4dd379Sjsg 11347f4dd379Sjsg ret = 0; 1135c349dbc7Sjsg break; 1136746fbbdbSjsg default: 11377f4dd379Sjsg ret = -ENOTTY; 1138746fbbdbSjsg } 1139746fbbdbSjsg 1140c349dbc7Sjsg drm_master_internal_release(dev); 11417f4dd379Sjsg unlock: 11427f4dd379Sjsg mutex_unlock(&fb_helper->lock); 11437f4dd379Sjsg return ret; 11447f4dd379Sjsg } 11457f4dd379Sjsg EXPORT_SYMBOL(drm_fb_helper_ioctl); 11467f4dd379Sjsg 11477f4dd379Sjsg static bool drm_fb_pixel_format_equal(const struct fb_var_screeninfo *var_1, 11487f4dd379Sjsg const struct fb_var_screeninfo *var_2) 11497f4dd379Sjsg { 11507f4dd379Sjsg return var_1->bits_per_pixel == var_2->bits_per_pixel && 11517f4dd379Sjsg var_1->grayscale == var_2->grayscale && 11527f4dd379Sjsg var_1->red.offset == var_2->red.offset && 11537f4dd379Sjsg var_1->red.length == var_2->red.length && 11547f4dd379Sjsg var_1->red.msb_right == var_2->red.msb_right && 11557f4dd379Sjsg var_1->green.offset == var_2->green.offset && 11567f4dd379Sjsg var_1->green.length == var_2->green.length && 11577f4dd379Sjsg var_1->green.msb_right == var_2->green.msb_right && 11587f4dd379Sjsg var_1->blue.offset == var_2->blue.offset && 11597f4dd379Sjsg var_1->blue.length == var_2->blue.length && 11607f4dd379Sjsg var_1->blue.msb_right == var_2->blue.msb_right && 11617f4dd379Sjsg var_1->transp.offset == var_2->transp.offset && 11627f4dd379Sjsg var_1->transp.length == var_2->transp.length && 11637f4dd379Sjsg var_1->transp.msb_right == var_2->transp.msb_right; 11647f4dd379Sjsg } 11657f4dd379Sjsg 11667f4dd379Sjsg static void drm_fb_helper_fill_pixel_fmt(struct fb_var_screeninfo *var, 11671bb76ff1Sjsg const struct drm_format_info *format) 11687f4dd379Sjsg { 11691bb76ff1Sjsg u8 depth = format->depth; 11701bb76ff1Sjsg 11711bb76ff1Sjsg if (format->is_color_indexed) { 1172746fbbdbSjsg var->red.offset = 0; 1173746fbbdbSjsg var->green.offset = 0; 1174746fbbdbSjsg var->blue.offset = 0; 11751bb76ff1Sjsg var->red.length = depth; 11761bb76ff1Sjsg var->green.length = depth; 11771bb76ff1Sjsg var->blue.length = depth; 1178746fbbdbSjsg var->transp.offset = 0; 11797f4dd379Sjsg var->transp.length = 0; 11801bb76ff1Sjsg return; 11811bb76ff1Sjsg } 11821bb76ff1Sjsg 11831bb76ff1Sjsg switch (depth) { 1184746fbbdbSjsg case 15: 1185746fbbdbSjsg var->red.offset = 10; 1186746fbbdbSjsg var->green.offset = 5; 1187746fbbdbSjsg var->blue.offset = 0; 1188746fbbdbSjsg var->red.length = 5; 1189746fbbdbSjsg var->green.length = 5; 1190746fbbdbSjsg var->blue.length = 5; 1191746fbbdbSjsg var->transp.offset = 15; 11927f4dd379Sjsg var->transp.length = 1; 1193746fbbdbSjsg break; 1194746fbbdbSjsg case 16: 1195746fbbdbSjsg var->red.offset = 11; 1196746fbbdbSjsg var->green.offset = 5; 1197746fbbdbSjsg var->blue.offset = 0; 1198746fbbdbSjsg var->red.length = 5; 1199746fbbdbSjsg var->green.length = 6; 1200746fbbdbSjsg var->blue.length = 5; 1201746fbbdbSjsg var->transp.offset = 0; 1202746fbbdbSjsg break; 1203746fbbdbSjsg case 24: 1204746fbbdbSjsg var->red.offset = 16; 1205746fbbdbSjsg var->green.offset = 8; 1206746fbbdbSjsg var->blue.offset = 0; 1207746fbbdbSjsg var->red.length = 8; 1208746fbbdbSjsg var->green.length = 8; 1209746fbbdbSjsg var->blue.length = 8; 1210746fbbdbSjsg var->transp.offset = 0; 12117f4dd379Sjsg var->transp.length = 0; 1212746fbbdbSjsg break; 1213746fbbdbSjsg case 32: 1214746fbbdbSjsg var->red.offset = 16; 1215746fbbdbSjsg var->green.offset = 8; 1216746fbbdbSjsg var->blue.offset = 0; 1217746fbbdbSjsg var->red.length = 8; 1218746fbbdbSjsg var->green.length = 8; 1219746fbbdbSjsg var->blue.length = 8; 1220746fbbdbSjsg var->transp.offset = 24; 12217f4dd379Sjsg var->transp.length = 8; 1222746fbbdbSjsg break; 1223746fbbdbSjsg default: 12247f4dd379Sjsg break; 12257f4dd379Sjsg } 12267f4dd379Sjsg } 12277f4dd379Sjsg 1228f005ef32Sjsg static void __fill_var(struct fb_var_screeninfo *var, struct fb_info *info, 1229f005ef32Sjsg struct drm_framebuffer *fb) 1230f005ef32Sjsg { 1231f005ef32Sjsg int i; 1232f005ef32Sjsg 1233f005ef32Sjsg var->xres_virtual = fb->width; 1234f005ef32Sjsg var->yres_virtual = fb->height; 1235f005ef32Sjsg var->accel_flags = 0; 1236f005ef32Sjsg var->bits_per_pixel = drm_format_info_bpp(fb->format, 0); 1237f005ef32Sjsg 1238f005ef32Sjsg var->height = info->var.height; 1239f005ef32Sjsg var->width = info->var.width; 1240f005ef32Sjsg 1241f005ef32Sjsg var->left_margin = var->right_margin = 0; 1242f005ef32Sjsg var->upper_margin = var->lower_margin = 0; 1243f005ef32Sjsg var->hsync_len = var->vsync_len = 0; 1244f005ef32Sjsg var->sync = var->vmode = 0; 1245f005ef32Sjsg var->rotate = 0; 1246f005ef32Sjsg var->colorspace = 0; 1247f005ef32Sjsg for (i = 0; i < 4; i++) 1248f005ef32Sjsg var->reserved[i] = 0; 1249f005ef32Sjsg } 1250f005ef32Sjsg 12517f4dd379Sjsg /** 12527f4dd379Sjsg * drm_fb_helper_check_var - implementation for &fb_ops.fb_check_var 12537f4dd379Sjsg * @var: screeninfo to check 12547f4dd379Sjsg * @info: fbdev registered by the helper 12557f4dd379Sjsg */ 12567f4dd379Sjsg int drm_fb_helper_check_var(struct fb_var_screeninfo *var, 12577f4dd379Sjsg struct fb_info *info) 12587f4dd379Sjsg { 12597f4dd379Sjsg struct drm_fb_helper *fb_helper = info->par; 12607f4dd379Sjsg struct drm_framebuffer *fb = fb_helper->fb; 12611bb76ff1Sjsg const struct drm_format_info *format = fb->format; 1262c349dbc7Sjsg struct drm_device *dev = fb_helper->dev; 12631bb76ff1Sjsg unsigned int bpp; 12647f4dd379Sjsg 12657f4dd379Sjsg if (in_dbg_master()) 12667f4dd379Sjsg return -EINVAL; 12677f4dd379Sjsg 12687f4dd379Sjsg if (var->pixclock != 0) { 1269c349dbc7Sjsg drm_dbg_kms(dev, "fbdev emulation doesn't support changing the pixel clock, value of pixclock is ignored\n"); 12707f4dd379Sjsg var->pixclock = 0; 12717f4dd379Sjsg } 12727f4dd379Sjsg 12731bb76ff1Sjsg switch (format->format) { 12741bb76ff1Sjsg case DRM_FORMAT_C1: 12751bb76ff1Sjsg case DRM_FORMAT_C2: 12761bb76ff1Sjsg case DRM_FORMAT_C4: 12771bb76ff1Sjsg /* supported format with sub-byte pixels */ 12781bb76ff1Sjsg break; 12791bb76ff1Sjsg 12801bb76ff1Sjsg default: 12811bb76ff1Sjsg if ((drm_format_info_block_width(format, 0) > 1) || 12821bb76ff1Sjsg (drm_format_info_block_height(format, 0) > 1)) 1283c349dbc7Sjsg return -EINVAL; 12841bb76ff1Sjsg break; 12851bb76ff1Sjsg } 1286c349dbc7Sjsg 12877f4dd379Sjsg /* 12887f4dd379Sjsg * Changes struct fb_var_screeninfo are currently not pushed back 12897f4dd379Sjsg * to KMS, hence fail if different settings are requested. 12907f4dd379Sjsg */ 12911bb76ff1Sjsg bpp = drm_format_info_bpp(format, 0); 12921bb76ff1Sjsg if (var->bits_per_pixel > bpp || 12937f4dd379Sjsg var->xres > fb->width || var->yres > fb->height || 12947f4dd379Sjsg var->xres_virtual > fb->width || var->yres_virtual > fb->height) { 1295c349dbc7Sjsg drm_dbg_kms(dev, "fb requested width/height/bpp can't fit in current fb " 12967f4dd379Sjsg "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n", 12977f4dd379Sjsg var->xres, var->yres, var->bits_per_pixel, 12987f4dd379Sjsg var->xres_virtual, var->yres_virtual, 12991bb76ff1Sjsg fb->width, fb->height, bpp); 1300746fbbdbSjsg return -EINVAL; 1301746fbbdbSjsg } 13027f4dd379Sjsg 1303f005ef32Sjsg __fill_var(var, info, fb); 1304f005ef32Sjsg 1305f005ef32Sjsg /* 1306f005ef32Sjsg * fb_pan_display() validates this, but fb_set_par() doesn't and just 1307f005ef32Sjsg * falls over. Note that __fill_var above adjusts y/res_virtual. 1308f005ef32Sjsg */ 1309f005ef32Sjsg if (var->yoffset > var->yres_virtual - var->yres || 1310f005ef32Sjsg var->xoffset > var->xres_virtual - var->xres) 1311f005ef32Sjsg return -EINVAL; 1312f005ef32Sjsg 1313f005ef32Sjsg /* We neither support grayscale nor FOURCC (also stored in here). */ 1314f005ef32Sjsg if (var->grayscale > 0) 1315f005ef32Sjsg return -EINVAL; 1316f005ef32Sjsg 1317f005ef32Sjsg if (var->nonstd) 1318f005ef32Sjsg return -EINVAL; 1319bba11a86Sjsg 13207f4dd379Sjsg /* 13217f4dd379Sjsg * Workaround for SDL 1.2, which is known to be setting all pixel format 13227f4dd379Sjsg * fields values to zero in some cases. We treat this situation as a 13237f4dd379Sjsg * kind of "use some reasonable autodetected values". 13247f4dd379Sjsg */ 13257f4dd379Sjsg if (!var->red.offset && !var->green.offset && 13267f4dd379Sjsg !var->blue.offset && !var->transp.offset && 13277f4dd379Sjsg !var->red.length && !var->green.length && 13287f4dd379Sjsg !var->blue.length && !var->transp.length && 13297f4dd379Sjsg !var->red.msb_right && !var->green.msb_right && 13307f4dd379Sjsg !var->blue.msb_right && !var->transp.msb_right) { 13311bb76ff1Sjsg drm_fb_helper_fill_pixel_fmt(var, format); 13327f4dd379Sjsg } 13337f4dd379Sjsg 13347f4dd379Sjsg /* 13357f4dd379Sjsg * drm fbdev emulation doesn't support changing the pixel format at all, 13367f4dd379Sjsg * so reject all pixel format changing requests. 13377f4dd379Sjsg */ 13387f4dd379Sjsg if (!drm_fb_pixel_format_equal(var, &info->var)) { 1339c349dbc7Sjsg drm_dbg_kms(dev, "fbdev emulation doesn't support changing the pixel format\n"); 13407f4dd379Sjsg return -EINVAL; 13417f4dd379Sjsg } 13427f4dd379Sjsg 1343746fbbdbSjsg return 0; 1344746fbbdbSjsg } 1345746fbbdbSjsg EXPORT_SYMBOL(drm_fb_helper_check_var); 1346c349dbc7Sjsg 1347c349dbc7Sjsg #endif /* __linux__ */ 1348746fbbdbSjsg 1349e1001332Skettenis /** 13507f4dd379Sjsg * drm_fb_helper_set_par - implementation for &fb_ops.fb_set_par 1351e1001332Skettenis * @info: fbdev registered by the helper 1352e1001332Skettenis * 1353e1001332Skettenis * This will let fbcon do the mode init and is called at initialization time by 1354e1001332Skettenis * the fbdev core when registering the driver, and later on through the hotplug 1355e1001332Skettenis * callback. 1356e1001332Skettenis */ 1357d7873f4eSjsg int drm_fb_helper_set_par(struct fb_info *info) 1358746fbbdbSjsg { 1359746fbbdbSjsg struct drm_fb_helper *fb_helper = info->par; 1360f005ef32Sjsg #ifdef __linux__ 1361746fbbdbSjsg struct fb_var_screeninfo *var = &info->var; 1362f005ef32Sjsg #endif 1363f5087ea2Sjsg bool force; 1364e0b53ceeSkettenis 1365e0b53ceeSkettenis if (oops_in_progress) 1366e0b53ceeSkettenis return -EBUSY; 1367746fbbdbSjsg 1368f5087ea2Sjsg /* 1369f5087ea2Sjsg * Normally we want to make sure that a kms master takes precedence over 1370f5087ea2Sjsg * fbdev, to avoid fbdev flickering and occasionally stealing the 1371f5087ea2Sjsg * display status. But Xorg first sets the vt back to text mode using 1372f5087ea2Sjsg * the KDSET IOCTL with KD_TEXT, and only after that drops the master 1373f5087ea2Sjsg * status when exiting. 1374f5087ea2Sjsg * 1375f5087ea2Sjsg * In the past this was caught by drm_fb_helper_lastclose(), but on 1376f5087ea2Sjsg * modern systems where logind always keeps a drm fd open to orchestrate 1377f5087ea2Sjsg * the vt switching, this doesn't work. 1378f5087ea2Sjsg * 1379f5087ea2Sjsg * To not break the userspace ABI we have this special case here, which 1380f5087ea2Sjsg * is only used for the above case. Everything else uses the normal 1381f5087ea2Sjsg * commit function, which ensures that we never steal the display from 1382f5087ea2Sjsg * an active drm master. 1383f5087ea2Sjsg */ 1384f5087ea2Sjsg #ifdef __linux__ 1385f5087ea2Sjsg force = var->activate & FB_ACTIVATE_KD_TEXT; 1386f5087ea2Sjsg #else 1387f5087ea2Sjsg force = true; 1388f5087ea2Sjsg #endif 1389f5087ea2Sjsg 1390f5087ea2Sjsg __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force); 1391746fbbdbSjsg 1392746fbbdbSjsg return 0; 1393746fbbdbSjsg } 1394746fbbdbSjsg EXPORT_SYMBOL(drm_fb_helper_set_par); 1395746fbbdbSjsg 1396c349dbc7Sjsg #ifdef notyet 13977f4dd379Sjsg static void pan_set(struct drm_fb_helper *fb_helper, int x, int y) 1398e0b53ceeSkettenis { 1399e0b53ceeSkettenis struct drm_mode_set *mode_set; 1400e0b53ceeSkettenis 1401c349dbc7Sjsg mutex_lock(&fb_helper->client.modeset_mutex); 1402c349dbc7Sjsg drm_client_for_each_modeset(mode_set, &fb_helper->client) { 14037f4dd379Sjsg mode_set->x = x; 14047f4dd379Sjsg mode_set->y = y; 14057f4dd379Sjsg } 1406c349dbc7Sjsg mutex_unlock(&fb_helper->client.modeset_mutex); 1407e0b53ceeSkettenis } 1408c349dbc7Sjsg #endif 1409e0b53ceeSkettenis 14107f4dd379Sjsg static int pan_display_atomic(struct fb_var_screeninfo *var, 1411746fbbdbSjsg struct fb_info *info) 1412746fbbdbSjsg { 1413c349dbc7Sjsg STUB(); 1414c349dbc7Sjsg return -ENOSYS; 1415c349dbc7Sjsg #ifdef notyet 1416746fbbdbSjsg struct drm_fb_helper *fb_helper = info->par; 14177f4dd379Sjsg int ret; 14187f4dd379Sjsg 14197f4dd379Sjsg pan_set(fb_helper, var->xoffset, var->yoffset); 14207f4dd379Sjsg 1421c349dbc7Sjsg ret = drm_client_modeset_commit_locked(&fb_helper->client); 14227f4dd379Sjsg if (!ret) { 14237f4dd379Sjsg info->var.xoffset = var->xoffset; 14247f4dd379Sjsg info->var.yoffset = var->yoffset; 14257f4dd379Sjsg } else 14267f4dd379Sjsg pan_set(fb_helper, info->var.xoffset, info->var.yoffset); 14277f4dd379Sjsg 14287f4dd379Sjsg return ret; 1429c349dbc7Sjsg #endif 14307f4dd379Sjsg } 14317f4dd379Sjsg 14327f4dd379Sjsg static int pan_display_legacy(struct fb_var_screeninfo *var, 14337f4dd379Sjsg struct fb_info *info) 14347f4dd379Sjsg { 1435c349dbc7Sjsg STUB(); 1436c349dbc7Sjsg return -ENOSYS; 1437c349dbc7Sjsg #ifdef notyet 14387f4dd379Sjsg struct drm_fb_helper *fb_helper = info->par; 1439c349dbc7Sjsg struct drm_client_dev *client = &fb_helper->client; 1440746fbbdbSjsg struct drm_mode_set *modeset; 1441746fbbdbSjsg int ret = 0; 1442746fbbdbSjsg 1443c349dbc7Sjsg mutex_lock(&client->modeset_mutex); 14447f4dd379Sjsg drm_modeset_lock_all(fb_helper->dev); 1445c349dbc7Sjsg drm_client_for_each_modeset(modeset, client) { 1446746fbbdbSjsg modeset->x = var->xoffset; 1447746fbbdbSjsg modeset->y = var->yoffset; 1448746fbbdbSjsg 1449746fbbdbSjsg if (modeset->num_connectors) { 1450e1001332Skettenis ret = drm_mode_set_config_internal(modeset); 1451746fbbdbSjsg if (!ret) { 1452746fbbdbSjsg info->var.xoffset = var->xoffset; 1453746fbbdbSjsg info->var.yoffset = var->yoffset; 1454746fbbdbSjsg } 1455746fbbdbSjsg } 1456746fbbdbSjsg } 14577f4dd379Sjsg drm_modeset_unlock_all(fb_helper->dev); 1458c349dbc7Sjsg mutex_unlock(&client->modeset_mutex); 14597f4dd379Sjsg 14607f4dd379Sjsg return ret; 1461c349dbc7Sjsg #endif 14627f4dd379Sjsg } 14637f4dd379Sjsg 14647f4dd379Sjsg /** 14657f4dd379Sjsg * drm_fb_helper_pan_display - implementation for &fb_ops.fb_pan_display 14667f4dd379Sjsg * @var: updated screen information 14677f4dd379Sjsg * @info: fbdev registered by the helper 14687f4dd379Sjsg */ 14697f4dd379Sjsg int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, 14707f4dd379Sjsg struct fb_info *info) 14717f4dd379Sjsg { 14727f4dd379Sjsg struct drm_fb_helper *fb_helper = info->par; 14737f4dd379Sjsg struct drm_device *dev = fb_helper->dev; 14747f4dd379Sjsg int ret; 14757f4dd379Sjsg 14767f4dd379Sjsg if (oops_in_progress) 14777f4dd379Sjsg return -EBUSY; 14787f4dd379Sjsg 14797f4dd379Sjsg mutex_lock(&fb_helper->lock); 1480c349dbc7Sjsg if (!drm_master_internal_acquire(dev)) { 1481c349dbc7Sjsg ret = -EBUSY; 1482c349dbc7Sjsg goto unlock; 14837f4dd379Sjsg } 14847f4dd379Sjsg 14857f4dd379Sjsg if (drm_drv_uses_atomic_modeset(dev)) 14867f4dd379Sjsg ret = pan_display_atomic(var, info); 14877f4dd379Sjsg else 14887f4dd379Sjsg ret = pan_display_legacy(var, info); 1489c349dbc7Sjsg 1490c349dbc7Sjsg drm_master_internal_release(dev); 1491c349dbc7Sjsg unlock: 14927f4dd379Sjsg mutex_unlock(&fb_helper->lock); 14937f4dd379Sjsg 1494746fbbdbSjsg return ret; 1495746fbbdbSjsg } 1496746fbbdbSjsg EXPORT_SYMBOL(drm_fb_helper_pan_display); 14977f4dd379Sjsg 1498f005ef32Sjsg static uint32_t drm_fb_helper_find_format(struct drm_fb_helper *fb_helper, const uint32_t *formats, 1499f005ef32Sjsg size_t format_count, uint32_t bpp, uint32_t depth) 1500746fbbdbSjsg { 1501f005ef32Sjsg #ifdef notyet 1502c349dbc7Sjsg struct drm_device *dev = fb_helper->dev; 1503f005ef32Sjsg #endif 1504f005ef32Sjsg uint32_t format; 1505f005ef32Sjsg size_t i; 1506c349dbc7Sjsg 1507c349dbc7Sjsg /* 1508c349dbc7Sjsg * Do not consider YUV or other complicated formats 1509c349dbc7Sjsg * for framebuffers. This means only legacy formats 1510f005ef32Sjsg * are supported (fmt->depth is a legacy field), but 1511c349dbc7Sjsg * the framebuffer emulation can only deal with such 1512c349dbc7Sjsg * formats, specifically RGB/BGA formats. 1513c349dbc7Sjsg */ 1514f005ef32Sjsg format = drm_mode_legacy_fb_format(bpp, depth); 1515f005ef32Sjsg if (!format) 1516f005ef32Sjsg goto err; 1517c349dbc7Sjsg 1518f005ef32Sjsg for (i = 0; i < format_count; ++i) { 1519f005ef32Sjsg if (formats[i] == format) 1520f005ef32Sjsg return format; 1521f005ef32Sjsg } 1522f005ef32Sjsg 1523f005ef32Sjsg err: 1524f005ef32Sjsg /* We found nothing. */ 1525f005ef32Sjsg drm_warn(dev, "bpp/depth value of %u/%u not supported\n", bpp, depth); 1526f005ef32Sjsg 1527f005ef32Sjsg return DRM_FORMAT_INVALID; 1528f005ef32Sjsg } 1529f005ef32Sjsg 1530f005ef32Sjsg static uint32_t drm_fb_helper_find_color_mode_format(struct drm_fb_helper *fb_helper, 1531f005ef32Sjsg const uint32_t *formats, size_t format_count, 1532f005ef32Sjsg unsigned int color_mode) 1533f005ef32Sjsg { 1534f005ef32Sjsg struct drm_device *dev = fb_helper->dev; 1535f005ef32Sjsg uint32_t bpp, depth; 1536f005ef32Sjsg 1537f005ef32Sjsg switch (color_mode) { 1538f005ef32Sjsg case 1: 1539f005ef32Sjsg case 2: 1540f005ef32Sjsg case 4: 1541f005ef32Sjsg case 8: 1542f005ef32Sjsg case 16: 1543f005ef32Sjsg case 24: 1544f005ef32Sjsg bpp = depth = color_mode; 1545c349dbc7Sjsg break; 1546f005ef32Sjsg case 15: 1547f005ef32Sjsg bpp = 16; 1548f005ef32Sjsg depth = 15; 1549f005ef32Sjsg break; 1550f005ef32Sjsg case 32: 1551f005ef32Sjsg bpp = 32; 1552f005ef32Sjsg depth = 24; 1553f005ef32Sjsg break; 1554f005ef32Sjsg default: 1555f005ef32Sjsg drm_info(dev, "unsupported color mode of %d\n", color_mode); 1556f005ef32Sjsg return DRM_FORMAT_INVALID; 1557c349dbc7Sjsg } 1558c349dbc7Sjsg 1559f005ef32Sjsg return drm_fb_helper_find_format(fb_helper, formats, format_count, bpp, depth); 1560f005ef32Sjsg } 1561f005ef32Sjsg 1562f005ef32Sjsg static int __drm_fb_helper_find_sizes(struct drm_fb_helper *fb_helper, 1563f005ef32Sjsg struct drm_fb_helper_surface_size *sizes) 1564f005ef32Sjsg { 1565f005ef32Sjsg struct drm_client_dev *client = &fb_helper->client; 1566f005ef32Sjsg struct drm_device *dev = fb_helper->dev; 1567f005ef32Sjsg int crtc_count = 0; 1568f005ef32Sjsg struct drm_connector_list_iter conn_iter; 1569f005ef32Sjsg struct drm_connector *connector; 1570f005ef32Sjsg struct drm_mode_set *mode_set; 1571f005ef32Sjsg uint32_t surface_format = DRM_FORMAT_INVALID; 1572f005ef32Sjsg const struct drm_format_info *info; 1573f005ef32Sjsg 1574f005ef32Sjsg memset(sizes, 0, sizeof(*sizes)); 1575f005ef32Sjsg sizes->fb_width = (u32)-1; 1576f005ef32Sjsg sizes->fb_height = (u32)-1; 1577f005ef32Sjsg 1578f005ef32Sjsg drm_client_for_each_modeset(mode_set, client) { 1579f005ef32Sjsg struct drm_crtc *crtc = mode_set->crtc; 1580f005ef32Sjsg struct drm_plane *plane = crtc->primary; 1581f005ef32Sjsg 1582f005ef32Sjsg drm_dbg_kms(dev, "test CRTC %u primary plane\n", drm_crtc_index(crtc)); 1583f005ef32Sjsg 1584f005ef32Sjsg drm_connector_list_iter_begin(fb_helper->dev, &conn_iter); 1585f005ef32Sjsg drm_client_for_each_connector_iter(connector, &conn_iter) { 1586f005ef32Sjsg struct drm_cmdline_mode *cmdline_mode = &connector->cmdline_mode; 1587f005ef32Sjsg 1588f005ef32Sjsg if (!cmdline_mode->bpp_specified) 1589c349dbc7Sjsg continue; 1590c349dbc7Sjsg 1591f005ef32Sjsg surface_format = drm_fb_helper_find_color_mode_format(fb_helper, 1592f005ef32Sjsg plane->format_types, 1593f005ef32Sjsg plane->format_count, 1594f005ef32Sjsg cmdline_mode->bpp); 1595f005ef32Sjsg if (surface_format != DRM_FORMAT_INVALID) 1596f005ef32Sjsg break; /* found supported format */ 1597c349dbc7Sjsg } 1598f005ef32Sjsg drm_connector_list_iter_end(&conn_iter); 1599f005ef32Sjsg 1600f005ef32Sjsg if (surface_format != DRM_FORMAT_INVALID) 1601f005ef32Sjsg break; /* found supported format */ 1602f005ef32Sjsg 1603f005ef32Sjsg /* try preferred color mode */ 1604f005ef32Sjsg surface_format = drm_fb_helper_find_color_mode_format(fb_helper, 1605f005ef32Sjsg plane->format_types, 1606f005ef32Sjsg plane->format_count, 1607f005ef32Sjsg fb_helper->preferred_bpp); 1608f005ef32Sjsg if (surface_format != DRM_FORMAT_INVALID) 1609f005ef32Sjsg break; /* found supported format */ 1610c349dbc7Sjsg } 1611f005ef32Sjsg 1612f005ef32Sjsg if (surface_format == DRM_FORMAT_INVALID) { 1613f005ef32Sjsg /* 1614f005ef32Sjsg * If none of the given color modes works, fall back 1615f005ef32Sjsg * to XRGB8888. Drivers are expected to provide this 1616f005ef32Sjsg * format for compatibility with legacy applications. 1617f005ef32Sjsg */ 1618f005ef32Sjsg drm_warn(dev, "No compatible format found\n"); 1619f005ef32Sjsg surface_format = drm_driver_legacy_fb_format(dev, 32, 24); 1620c349dbc7Sjsg } 1621c349dbc7Sjsg 1622f005ef32Sjsg info = drm_format_info(surface_format); 1623f005ef32Sjsg sizes->surface_bpp = drm_format_info_bpp(info, 0); 1624f005ef32Sjsg sizes->surface_depth = info->depth; 1625f005ef32Sjsg 1626c349dbc7Sjsg /* first up get a count of crtcs now in use and new min/maxes width/heights */ 1627746fbbdbSjsg crtc_count = 0; 1628c349dbc7Sjsg drm_client_for_each_modeset(mode_set, client) { 1629746fbbdbSjsg struct drm_display_mode *desired_mode; 1630e0b53ceeSkettenis int x, y, j; 1631e0b53ceeSkettenis /* in case of tile group, are we the last tile vert or horiz? 1632e0b53ceeSkettenis * If no tile group you are always the last one both vertically 1633e0b53ceeSkettenis * and horizontally 1634e0b53ceeSkettenis */ 1635e0b53ceeSkettenis bool lastv = true, lasth = true; 1636746fbbdbSjsg 1637c349dbc7Sjsg desired_mode = mode_set->mode; 1638e0b53ceeSkettenis 1639e0b53ceeSkettenis if (!desired_mode) 1640e0b53ceeSkettenis continue; 1641e0b53ceeSkettenis 1642e0b53ceeSkettenis crtc_count++; 1643e0b53ceeSkettenis 1644c349dbc7Sjsg x = mode_set->x; 1645c349dbc7Sjsg y = mode_set->y; 1646e0b53ceeSkettenis 1647f005ef32Sjsg sizes->surface_width = 1648f005ef32Sjsg max_t(u32, desired_mode->hdisplay + x, sizes->surface_width); 1649f005ef32Sjsg sizes->surface_height = 1650f005ef32Sjsg max_t(u32, desired_mode->vdisplay + y, sizes->surface_height); 1651e0b53ceeSkettenis 1652e0b53ceeSkettenis for (j = 0; j < mode_set->num_connectors; j++) { 1653e0b53ceeSkettenis struct drm_connector *connector = mode_set->connectors[j]; 16547f4dd379Sjsg 1655c349dbc7Sjsg if (connector->has_tile && 1656c349dbc7Sjsg desired_mode->hdisplay == connector->tile_h_size && 1657c349dbc7Sjsg desired_mode->vdisplay == connector->tile_v_size) { 1658e0b53ceeSkettenis lasth = (connector->tile_h_loc == (connector->num_h_tile - 1)); 1659e0b53ceeSkettenis lastv = (connector->tile_v_loc == (connector->num_v_tile - 1)); 1660e0b53ceeSkettenis /* cloning to multiple tiles is just crazy-talk, so: */ 1661e0b53ceeSkettenis break; 1662746fbbdbSjsg } 1663746fbbdbSjsg } 1664746fbbdbSjsg 1665e0b53ceeSkettenis if (lasth) 1666f005ef32Sjsg sizes->fb_width = min_t(u32, desired_mode->hdisplay + x, sizes->fb_width); 1667e0b53ceeSkettenis if (lastv) 1668f005ef32Sjsg sizes->fb_height = min_t(u32, desired_mode->vdisplay + y, sizes->fb_height); 1669e0b53ceeSkettenis } 1670e0b53ceeSkettenis 1671f005ef32Sjsg if (crtc_count == 0 || sizes->fb_width == -1 || sizes->fb_height == -1) { 167278cd6032Sjsg #ifdef __linux__ 1673c349dbc7Sjsg drm_info(dev, "Cannot find any crtc or sizes\n"); 16747f4dd379Sjsg return -EAGAIN; 167578cd6032Sjsg #else 1676c349dbc7Sjsg drm_info(dev, "Cannot find any crtc or sizes - going 1024x768\n"); 1677f005ef32Sjsg sizes->fb_width = sizes->surface_width = 1024; 1678f005ef32Sjsg sizes->fb_height = sizes->surface_height = 768; 167978cd6032Sjsg #endif 1680746fbbdbSjsg } 1681746fbbdbSjsg 1682f005ef32Sjsg return 0; 1683f005ef32Sjsg } 1684f005ef32Sjsg 1685f005ef32Sjsg static int drm_fb_helper_find_sizes(struct drm_fb_helper *fb_helper, 1686f005ef32Sjsg struct drm_fb_helper_surface_size *sizes) 1687f005ef32Sjsg { 1688f005ef32Sjsg struct drm_client_dev *client = &fb_helper->client; 1689f005ef32Sjsg struct drm_device *dev = fb_helper->dev; 1690f005ef32Sjsg struct drm_mode_config *config = &dev->mode_config; 1691f005ef32Sjsg int ret; 1692f005ef32Sjsg 1693f005ef32Sjsg mutex_lock(&client->modeset_mutex); 1694f005ef32Sjsg ret = __drm_fb_helper_find_sizes(fb_helper, sizes); 1695f005ef32Sjsg mutex_unlock(&client->modeset_mutex); 1696f005ef32Sjsg 1697f005ef32Sjsg if (ret) 1698f005ef32Sjsg return ret; 1699f005ef32Sjsg 17007f4dd379Sjsg /* Handle our overallocation */ 1701f005ef32Sjsg sizes->surface_height *= drm_fbdev_overalloc; 1702f005ef32Sjsg sizes->surface_height /= 100; 1703f005ef32Sjsg if (sizes->surface_height > config->max_height) { 17045ca02815Sjsg drm_dbg_kms(dev, "Fbdev over-allocation too large; clamping height to %d\n", 17055ca02815Sjsg config->max_height); 1706f005ef32Sjsg sizes->surface_height = config->max_height; 1707f005ef32Sjsg } 1708f005ef32Sjsg 1709f005ef32Sjsg return 0; 1710f005ef32Sjsg } 1711f005ef32Sjsg 1712f005ef32Sjsg /* 1713f005ef32Sjsg * Allocates the backing storage and sets up the fbdev info structure through 1714f005ef32Sjsg * the ->fb_probe callback. 1715f005ef32Sjsg */ 1716f005ef32Sjsg static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper) 1717f005ef32Sjsg { 1718f005ef32Sjsg struct drm_client_dev *client = &fb_helper->client; 1719f005ef32Sjsg #ifdef __linux__ 1720f005ef32Sjsg struct drm_device *dev = fb_helper->dev; 1721f005ef32Sjsg #endif 1722f005ef32Sjsg struct drm_fb_helper_surface_size sizes; 1723f005ef32Sjsg int ret; 1724f005ef32Sjsg 1725f005ef32Sjsg ret = drm_fb_helper_find_sizes(fb_helper, &sizes); 1726f005ef32Sjsg if (ret) { 1727f005ef32Sjsg /* First time: disable all crtc's.. */ 1728f005ef32Sjsg if (!fb_helper->deferred_setup) 1729f005ef32Sjsg drm_client_modeset_commit(client); 1730f005ef32Sjsg return ret; 17315ca02815Sjsg } 17327f4dd379Sjsg 1733746fbbdbSjsg /* push down into drivers */ 1734e1001332Skettenis ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); 1735e1001332Skettenis if (ret < 0) 1736e1001332Skettenis return ret; 1737746fbbdbSjsg 17381111c8a2Skettenis #ifdef __linux__ 17397f4dd379Sjsg strcpy(fb_helper->fb->comm, "[fbcon]"); 1740f005ef32Sjsg 1741f005ef32Sjsg /* Set the fb info for vgaswitcheroo clients. Does nothing otherwise. */ 1742f005ef32Sjsg if (dev_is_pci(dev->dev)) 1743f005ef32Sjsg vga_switcheroo_client_fb_set(to_pci_dev(dev->dev), fb_helper->info); 1744dc309f62Sjsg #else 1745dc309f62Sjsg strlcpy(fb_helper->fb->comm, "[fbcon]", sizeof(fb_helper->fb->comm)); 1746746fbbdbSjsg #endif 1747f005ef32Sjsg 1748746fbbdbSjsg return 0; 1749746fbbdbSjsg } 1750746fbbdbSjsg 1751e0b53ceeSkettenis #ifdef __linux__ 17527f4dd379Sjsg 1753c349dbc7Sjsg static void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, 17541bb76ff1Sjsg bool is_color_indexed) 1755746fbbdbSjsg { 1756746fbbdbSjsg info->fix.type = FB_TYPE_PACKED_PIXELS; 17571bb76ff1Sjsg info->fix.visual = is_color_indexed ? FB_VISUAL_PSEUDOCOLOR 17581bb76ff1Sjsg : FB_VISUAL_TRUECOLOR; 1759746fbbdbSjsg info->fix.mmio_start = 0; 1760746fbbdbSjsg info->fix.mmio_len = 0; 1761746fbbdbSjsg info->fix.type_aux = 0; 1762746fbbdbSjsg info->fix.xpanstep = 1; /* doing it in hw */ 1763746fbbdbSjsg info->fix.ypanstep = 1; /* doing it in hw */ 1764746fbbdbSjsg info->fix.ywrapstep = 0; 1765746fbbdbSjsg info->fix.accel = FB_ACCEL_NONE; 1766746fbbdbSjsg 1767746fbbdbSjsg info->fix.line_length = pitch; 1768746fbbdbSjsg } 1769746fbbdbSjsg 17701bb76ff1Sjsg #endif /* __linux__ */ 17711bb76ff1Sjsg 1772c349dbc7Sjsg static void drm_fb_helper_fill_var(struct fb_info *info, 1773c349dbc7Sjsg struct drm_fb_helper *fb_helper, 1774746fbbdbSjsg uint32_t fb_width, uint32_t fb_height) 1775746fbbdbSjsg { 1776746fbbdbSjsg struct drm_framebuffer *fb = fb_helper->fb; 17771bb76ff1Sjsg const struct drm_format_info *format = fb->format; 17787f4dd379Sjsg 17791bb76ff1Sjsg switch (format->format) { 17801bb76ff1Sjsg case DRM_FORMAT_C1: 17811bb76ff1Sjsg case DRM_FORMAT_C2: 17821bb76ff1Sjsg case DRM_FORMAT_C4: 17831bb76ff1Sjsg /* supported format with sub-byte pixels */ 17841bb76ff1Sjsg break; 17851bb76ff1Sjsg 17861bb76ff1Sjsg default: 17871bb76ff1Sjsg WARN_ON((drm_format_info_block_width(format, 0) > 1) || 17881bb76ff1Sjsg (drm_format_info_block_height(format, 0) > 1)); 17891bb76ff1Sjsg break; 17901bb76ff1Sjsg } 17911bb76ff1Sjsg 17921bb76ff1Sjsg #ifdef notyet 1793746fbbdbSjsg info->pseudo_palette = fb_helper->pseudo_palette; 1794746fbbdbSjsg info->var.xoffset = 0; 1795746fbbdbSjsg info->var.yoffset = 0; 1796f005ef32Sjsg __fill_var(&info->var, info, fb); 1797746fbbdbSjsg info->var.activate = FB_ACTIVATE_NOW; 1798746fbbdbSjsg 17991bb76ff1Sjsg drm_fb_helper_fill_pixel_fmt(&info->var, format); 18001bb76ff1Sjsg #endif 1801746fbbdbSjsg 1802746fbbdbSjsg info->var.xres = fb_width; 1803746fbbdbSjsg info->var.yres = fb_height; 1804746fbbdbSjsg } 1805c349dbc7Sjsg 1806c349dbc7Sjsg /** 1807c349dbc7Sjsg * drm_fb_helper_fill_info - initializes fbdev information 1808c349dbc7Sjsg * @info: fbdev instance to set up 1809c349dbc7Sjsg * @fb_helper: fb helper instance to use as template 1810c349dbc7Sjsg * @sizes: describes fbdev size and scanout surface size 1811c349dbc7Sjsg * 1812c349dbc7Sjsg * Sets up the variable and fixed fbdev metainformation from the given fb helper 1813c349dbc7Sjsg * instance and the drm framebuffer allocated in &drm_fb_helper.fb. 1814c349dbc7Sjsg * 1815c349dbc7Sjsg * Drivers should call this (or their equivalent setup code) from their 1816c349dbc7Sjsg * &drm_fb_helper_funcs.fb_probe callback after having allocated the fbdev 1817c349dbc7Sjsg * backing storage framebuffer. 1818746fbbdbSjsg */ 1819c349dbc7Sjsg void drm_fb_helper_fill_info(struct fb_info *info, 1820c349dbc7Sjsg struct drm_fb_helper *fb_helper, 1821c349dbc7Sjsg struct drm_fb_helper_surface_size *sizes) 1822746fbbdbSjsg { 1823c349dbc7Sjsg #ifdef __linux__ 1824c349dbc7Sjsg struct drm_framebuffer *fb = fb_helper->fb; 1825746fbbdbSjsg 18261bb76ff1Sjsg drm_fb_helper_fill_fix(info, fb->pitches[0], 18271bb76ff1Sjsg fb->format->is_color_indexed); 18281bb76ff1Sjsg #endif 1829c349dbc7Sjsg drm_fb_helper_fill_var(info, fb_helper, 1830c349dbc7Sjsg sizes->fb_width, sizes->fb_height); 18317f4dd379Sjsg 1832c349dbc7Sjsg info->par = fb_helper; 1833c349dbc7Sjsg #ifdef __linux__ 18345ca02815Sjsg /* 18355ca02815Sjsg * The DRM drivers fbdev emulation device name can be confusing if the 18365ca02815Sjsg * driver name also has a "drm" suffix on it. Leading to names such as 18375ca02815Sjsg * "simpledrmdrmfb" in /proc/fb. Unfortunately, it's an uAPI and can't 18385ca02815Sjsg * be changed due user-space tools (e.g: pm-utils) matching against it. 18395ca02815Sjsg */ 1840c349dbc7Sjsg snprintf(info->fix.id, sizeof(info->fix.id), "%sdrmfb", 1841c349dbc7Sjsg fb_helper->dev->driver->name); 1842c349dbc7Sjsg #endif 1843746fbbdbSjsg } 1844c349dbc7Sjsg EXPORT_SYMBOL(drm_fb_helper_fill_info); 1845746fbbdbSjsg 18467f4dd379Sjsg /* 18477f4dd379Sjsg * This is a continuation of drm_setup_crtcs() that sets up anything related 18487f4dd379Sjsg * to the framebuffer. During initialization, drm_setup_crtcs() is called before 1849f005ef32Sjsg * the framebuffer has been allocated (fb_helper->fb and fb_helper->info). 18507f4dd379Sjsg * So, any setup that touches those fields needs to be done here instead of in 18517f4dd379Sjsg * drm_setup_crtcs(). 18527f4dd379Sjsg */ 18537f4dd379Sjsg static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) 18547f4dd379Sjsg { 1855c349dbc7Sjsg struct drm_client_dev *client = &fb_helper->client; 1856c349dbc7Sjsg struct drm_connector_list_iter conn_iter; 1857f005ef32Sjsg struct fb_info *info = fb_helper->info; 1858c349dbc7Sjsg unsigned int rotation, sw_rotations = 0; 1859c349dbc7Sjsg struct drm_connector *connector; 1860c349dbc7Sjsg struct drm_mode_set *modeset; 18617f4dd379Sjsg 1862c349dbc7Sjsg mutex_lock(&client->modeset_mutex); 1863c349dbc7Sjsg drm_client_for_each_modeset(modeset, client) { 1864c349dbc7Sjsg if (!modeset->num_connectors) 1865c349dbc7Sjsg continue; 18667f4dd379Sjsg 1867c349dbc7Sjsg modeset->fb = fb_helper->fb; 1868c349dbc7Sjsg 1869c349dbc7Sjsg if (drm_client_rotation(modeset, &rotation)) 1870c349dbc7Sjsg /* Rotating in hardware, fbcon should not rotate */ 1871c349dbc7Sjsg sw_rotations |= DRM_MODE_ROTATE_0; 1872c349dbc7Sjsg else 1873c349dbc7Sjsg sw_rotations |= rotation; 1874c349dbc7Sjsg } 1875c349dbc7Sjsg mutex_unlock(&client->modeset_mutex); 1876c349dbc7Sjsg 1877c349dbc7Sjsg drm_connector_list_iter_begin(fb_helper->dev, &conn_iter); 1878c349dbc7Sjsg drm_client_for_each_connector_iter(connector, &conn_iter) { 18797f4dd379Sjsg 18807f4dd379Sjsg /* use first connected connector for the physical dimensions */ 18817f4dd379Sjsg if (connector->status == connector_status_connected) { 18827f4dd379Sjsg info->var.width = connector->display_info.width_mm; 18837f4dd379Sjsg info->var.height = connector->display_info.height_mm; 18847f4dd379Sjsg break; 18857f4dd379Sjsg } 18867f4dd379Sjsg } 1887c349dbc7Sjsg drm_connector_list_iter_end(&conn_iter); 18887f4dd379Sjsg 1889c349dbc7Sjsg switch (sw_rotations) { 18907f4dd379Sjsg case DRM_MODE_ROTATE_0: 18917f4dd379Sjsg info->fbcon_rotate_hint = FB_ROTATE_UR; 18927f4dd379Sjsg break; 18937f4dd379Sjsg case DRM_MODE_ROTATE_90: 18947f4dd379Sjsg info->fbcon_rotate_hint = FB_ROTATE_CCW; 18957f4dd379Sjsg break; 18967f4dd379Sjsg case DRM_MODE_ROTATE_180: 18977f4dd379Sjsg info->fbcon_rotate_hint = FB_ROTATE_UD; 18987f4dd379Sjsg break; 18997f4dd379Sjsg case DRM_MODE_ROTATE_270: 19007f4dd379Sjsg info->fbcon_rotate_hint = FB_ROTATE_CW; 19017f4dd379Sjsg break; 19027f4dd379Sjsg default: 19037f4dd379Sjsg /* 19047f4dd379Sjsg * Multiple bits are set / multiple rotations requested 19057f4dd379Sjsg * fbcon cannot handle separate rotation settings per 19067f4dd379Sjsg * output, so fallback to unrotated. 19077f4dd379Sjsg */ 19087f4dd379Sjsg info->fbcon_rotate_hint = FB_ROTATE_UR; 19097f4dd379Sjsg } 19107f4dd379Sjsg } 19117f4dd379Sjsg 19127f4dd379Sjsg /* Note: Drops fb_helper->lock before returning. */ 19137f4dd379Sjsg static int 1914f005ef32Sjsg __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper) 19157f4dd379Sjsg { 19167f4dd379Sjsg struct drm_device *dev = fb_helper->dev; 19177f4dd379Sjsg struct fb_info *info; 19187f4dd379Sjsg unsigned int width, height; 19197f4dd379Sjsg int ret; 19207f4dd379Sjsg 19217f4dd379Sjsg width = dev->mode_config.max_width; 19227f4dd379Sjsg height = dev->mode_config.max_height; 19237f4dd379Sjsg 1924c349dbc7Sjsg drm_client_modeset_probe(&fb_helper->client, width, height); 1925f005ef32Sjsg ret = drm_fb_helper_single_fb_probe(fb_helper); 19267f4dd379Sjsg if (ret < 0) { 19277f4dd379Sjsg if (ret == -EAGAIN) { 19287f4dd379Sjsg fb_helper->deferred_setup = true; 19297f4dd379Sjsg ret = 0; 19307f4dd379Sjsg } 19317f4dd379Sjsg mutex_unlock(&fb_helper->lock); 19327f4dd379Sjsg 19337f4dd379Sjsg return ret; 19347f4dd379Sjsg } 19357f4dd379Sjsg drm_setup_crtcs_fb(fb_helper); 19367f4dd379Sjsg 19377f4dd379Sjsg fb_helper->deferred_setup = false; 19387f4dd379Sjsg 1939f005ef32Sjsg info = fb_helper->info; 19407f4dd379Sjsg info->var.pixclock = 0; 1941f005ef32Sjsg 19427f4dd379Sjsg /* Need to drop locks to avoid recursive deadlock in 19437f4dd379Sjsg * register_framebuffer. This is ok because the only thing left to do is 19447f4dd379Sjsg * register the fbdev emulation instance in kernel_fb_helper_list. */ 19457f4dd379Sjsg mutex_unlock(&fb_helper->lock); 19467f4dd379Sjsg 19477f4dd379Sjsg ret = register_framebuffer(info); 19487f4dd379Sjsg if (ret < 0) 19497f4dd379Sjsg return ret; 19507f4dd379Sjsg 1951c349dbc7Sjsg #ifdef __linux__ 1952ad8b1aafSjsg drm_info(dev, "fb%d: %s frame buffer device\n", 19537f4dd379Sjsg info->node, info->fix.id); 19547f4dd379Sjsg #endif 19557f4dd379Sjsg 19567f4dd379Sjsg mutex_lock(&kernel_fb_helper_lock); 19577f4dd379Sjsg if (list_empty(&kernel_fb_helper_list)) 19587f4dd379Sjsg register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); 19597f4dd379Sjsg 19607f4dd379Sjsg list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); 19617f4dd379Sjsg mutex_unlock(&kernel_fb_helper_lock); 19627f4dd379Sjsg 19637f4dd379Sjsg return 0; 19647f4dd379Sjsg } 19657f4dd379Sjsg 1966746fbbdbSjsg /** 1967e1001332Skettenis * drm_fb_helper_initial_config - setup a sane initial connector configuration 1968746fbbdbSjsg * @fb_helper: fb_helper device struct 1969746fbbdbSjsg * 1970746fbbdbSjsg * Scans the CRTCs and connectors and tries to put together an initial setup. 1971746fbbdbSjsg * At the moment, this is a cloned configuration across all heads with 1972746fbbdbSjsg * a new framebuffer object as the backing store. 1973746fbbdbSjsg * 1974e1001332Skettenis * Note that this also registers the fbdev and so allows userspace to call into 1975e1001332Skettenis * the driver through the fbdev interfaces. 1976e1001332Skettenis * 19777f4dd379Sjsg * This function will call down into the &drm_fb_helper_funcs.fb_probe callback 19787f4dd379Sjsg * to let the driver allocate and initialize the fbdev info structure and the 1979c349dbc7Sjsg * drm framebuffer used to back the fbdev. drm_fb_helper_fill_info() is provided 1980c349dbc7Sjsg * as a helper to setup simple default values for the fbdev info structure. 1981e1001332Skettenis * 19827f4dd379Sjsg * HANG DEBUGGING: 19837f4dd379Sjsg * 19847f4dd379Sjsg * When you have fbcon support built-in or already loaded, this function will do 19857f4dd379Sjsg * a full modeset to setup the fbdev console. Due to locking misdesign in the 19867f4dd379Sjsg * VT/fbdev subsystem that entire modeset sequence has to be done while holding 19877f4dd379Sjsg * console_lock. Until console_unlock is called no dmesg lines will be sent out 19887f4dd379Sjsg * to consoles, not even serial console. This means when your driver crashes, 19897f4dd379Sjsg * you will see absolutely nothing else but a system stuck in this function, 19907f4dd379Sjsg * with no further output. Any kind of printk() you place within your own driver 19917f4dd379Sjsg * or in the drm core modeset code will also never show up. 19927f4dd379Sjsg * 19937f4dd379Sjsg * Standard debug practice is to run the fbcon setup without taking the 19947f4dd379Sjsg * console_lock as a hack, to be able to see backtraces and crashes on the 19957f4dd379Sjsg * serial line. This can be done by setting the fb.lockless_register_fb=1 kernel 19967f4dd379Sjsg * cmdline option. 19977f4dd379Sjsg * 19987f4dd379Sjsg * The other option is to just disable fbdev emulation since very likely the 19997f4dd379Sjsg * first modeset from userspace will crash in the same way, and is even easier 20007f4dd379Sjsg * to debug. This can be done by setting the drm_kms_helper.fbdev_emulation=0 20017f4dd379Sjsg * kernel cmdline option. 20027f4dd379Sjsg * 2003746fbbdbSjsg * RETURNS: 2004746fbbdbSjsg * Zero if everything went ok, nonzero otherwise. 2005746fbbdbSjsg */ 2006f005ef32Sjsg int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper) 2007746fbbdbSjsg { 20087f4dd379Sjsg int ret; 2009746fbbdbSjsg 2010e0b53ceeSkettenis if (!drm_fbdev_emulation) 2011e0b53ceeSkettenis return 0; 2012746fbbdbSjsg 20137f4dd379Sjsg mutex_lock(&fb_helper->lock); 2014f005ef32Sjsg ret = __drm_fb_helper_initial_config_and_unlock(fb_helper); 2015746fbbdbSjsg 20167f4dd379Sjsg return ret; 2017746fbbdbSjsg } 2018746fbbdbSjsg EXPORT_SYMBOL(drm_fb_helper_initial_config); 2019746fbbdbSjsg 2020746fbbdbSjsg /** 2021746fbbdbSjsg * drm_fb_helper_hotplug_event - respond to a hotplug notification by 2022746fbbdbSjsg * probing all the outputs attached to the fb 20237f4dd379Sjsg * @fb_helper: driver-allocated fbdev helper, can be NULL 2024746fbbdbSjsg * 2025746fbbdbSjsg * Scan the connectors attached to the fb_helper and try to put together a 20267f4dd379Sjsg * setup after notification of a change in output configuration. 2027746fbbdbSjsg * 2028e1001332Skettenis * Called at runtime, takes the mode config locks to be able to check/change the 2029e1001332Skettenis * modeset configuration. Must be run from process context (which usually means 2030e1001332Skettenis * either the output polling work or a work item launched from the driver's 2031e1001332Skettenis * hotplug interrupt). 2032e1001332Skettenis * 2033e0b53ceeSkettenis * Note that drivers may call this even before calling 20347f4dd379Sjsg * drm_fb_helper_initial_config but only after drm_fb_helper_init. This allows 2035e0b53ceeSkettenis * for a race-free fbcon setup and will make sure that the fbdev emulation will 2036e0b53ceeSkettenis * not miss any hotplug events. 2037e1001332Skettenis * 2038746fbbdbSjsg * RETURNS: 2039746fbbdbSjsg * 0 on success and a non-zero error code otherwise. 2040746fbbdbSjsg */ 2041d7873f4eSjsg int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) 2042746fbbdbSjsg { 204345a90880Sjsg struct fb_info *fbi; 20447f4dd379Sjsg int err = 0; 2045746fbbdbSjsg 20467f4dd379Sjsg if (!drm_fbdev_emulation || !fb_helper) 2047746fbbdbSjsg return 0; 2048746fbbdbSjsg 20497f4dd379Sjsg mutex_lock(&fb_helper->lock); 20507f4dd379Sjsg if (fb_helper->deferred_setup) { 2051f005ef32Sjsg err = __drm_fb_helper_initial_config_and_unlock(fb_helper); 20527f4dd379Sjsg return err; 20537f4dd379Sjsg } 20547f4dd379Sjsg 2055c349dbc7Sjsg if (!fb_helper->fb || !drm_master_internal_acquire(fb_helper->dev)) { 2056746fbbdbSjsg fb_helper->delayed_hotplug = true; 20577f4dd379Sjsg mutex_unlock(&fb_helper->lock); 20587f4dd379Sjsg return err; 2059746fbbdbSjsg } 20607f4dd379Sjsg 2061c349dbc7Sjsg drm_master_internal_release(fb_helper->dev); 2062746fbbdbSjsg 2063c349dbc7Sjsg drm_dbg_kms(fb_helper->dev, "\n"); 2064c349dbc7Sjsg 2065c349dbc7Sjsg drm_client_modeset_probe(&fb_helper->client, fb_helper->fb->width, fb_helper->fb->height); 20667f4dd379Sjsg drm_setup_crtcs_fb(fb_helper); 20677f4dd379Sjsg mutex_unlock(&fb_helper->lock); 2068746fbbdbSjsg 2069f005ef32Sjsg fbi = fb_helper->info; 20709fffc08cSjsg if (fbi->fbops && fbi->fbops->fb_set_par) 20719fffc08cSjsg fbi->fbops->fb_set_par(fbi); 20729fffc08cSjsg else 2073f005ef32Sjsg drm_fb_helper_set_par(fb_helper->info); 2074746fbbdbSjsg 2075e1001332Skettenis return 0; 2076746fbbdbSjsg } 2077746fbbdbSjsg EXPORT_SYMBOL(drm_fb_helper_hotplug_event); 2078746fbbdbSjsg 20797f4dd379Sjsg /** 20807f4dd379Sjsg * drm_fb_helper_lastclose - DRM driver lastclose helper for fbdev emulation 20817f4dd379Sjsg * @dev: DRM device 20827f4dd379Sjsg * 20837f4dd379Sjsg * This function can be used as the &drm_driver->lastclose callback for drivers 20847f4dd379Sjsg * that only need to call drm_fb_helper_restore_fbdev_mode_unlocked(). 20857f4dd379Sjsg */ 20867f4dd379Sjsg void drm_fb_helper_lastclose(struct drm_device *dev) 20877f4dd379Sjsg { 20887f4dd379Sjsg drm_fb_helper_restore_fbdev_mode_unlocked(dev->fb_helper); 20897f4dd379Sjsg } 20907f4dd379Sjsg EXPORT_SYMBOL(drm_fb_helper_lastclose); 20917f4dd379Sjsg 20927f4dd379Sjsg /** 20937f4dd379Sjsg * drm_fb_helper_output_poll_changed - DRM mode config \.output_poll_changed 20947f4dd379Sjsg * helper for fbdev emulation 20957f4dd379Sjsg * @dev: DRM device 20967f4dd379Sjsg * 20977f4dd379Sjsg * This function can be used as the 20987f4dd379Sjsg * &drm_mode_config_funcs.output_poll_changed callback for drivers that only 2099f005ef32Sjsg * need to call drm_fbdev.hotplug_event(). 21007f4dd379Sjsg */ 21017f4dd379Sjsg void drm_fb_helper_output_poll_changed(struct drm_device *dev) 21027f4dd379Sjsg { 21037f4dd379Sjsg drm_fb_helper_hotplug_event(dev->fb_helper); 21047f4dd379Sjsg } 21057f4dd379Sjsg EXPORT_SYMBOL(drm_fb_helper_output_poll_changed); 2106