2006 lines · c
1/*2 * Copyright (c) 2006-2009 Red Hat Inc.3 * Copyright (c) 2006-2008 Intel Corporation4 * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>5 *6 * DRM framebuffer helper functions7 *8 * Permission to use, copy, modify, distribute, and sell this software and its9 * documentation for any purpose is hereby granted without fee, provided that10 * the above copyright notice appear in all copies and that both that copyright11 * notice and this permission notice appear in supporting documentation, and12 * that the name of the copyright holders not be used in advertising or13 * publicity pertaining to distribution of the software without specific,14 * written prior permission. The copyright holders make no representations15 * about the suitability of this software for any purpose. It is provided "as16 * is" without express or implied warranty.17 *18 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,19 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO20 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR21 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,22 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER23 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE24 * OF THIS SOFTWARE.25 *26 * Authors:27 * Dave Airlie <airlied@linux.ie>28 * Jesse Barnes <jesse.barnes@intel.com>29 */30#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt31 32#include <linux/console.h>33#include <linux/pci.h>34#include <linux/sysrq.h>35#include <linux/vga_switcheroo.h>36 37#include <drm/drm_atomic.h>38#include <drm/drm_drv.h>39#include <drm/drm_fb_helper.h>40#include <drm/drm_fourcc.h>41#include <drm/drm_framebuffer.h>42#include <drm/drm_modeset_helper_vtables.h>43#include <drm/drm_print.h>44#include <drm/drm_vblank.h>45 46#include "drm_internal.h"47#include "drm_crtc_internal.h"48 49static bool drm_fbdev_emulation = true;50module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);51MODULE_PARM_DESC(fbdev_emulation,52 "Enable legacy fbdev emulation [default=true]");53 54static int drm_fbdev_overalloc = CONFIG_DRM_FBDEV_OVERALLOC;55module_param(drm_fbdev_overalloc, int, 0444);56MODULE_PARM_DESC(drm_fbdev_overalloc,57 "Overallocation of the fbdev buffer (%) [default="58 __MODULE_STRING(CONFIG_DRM_FBDEV_OVERALLOC) "]");59 60/*61 * In order to keep user-space compatibility, we want in certain use-cases62 * to keep leaking the fbdev physical address to the user-space program63 * handling the fbdev buffer.64 *65 * This is a bad habit, essentially kept to support closed-source OpenGL66 * drivers that should really be moved into open-source upstream projects67 * instead of using legacy physical addresses in user space to communicate68 * with other out-of-tree kernel modules.69 *70 * This module_param *should* be removed as soon as possible and be71 * considered as a broken and legacy behaviour from a modern fbdev device.72 */73static bool drm_leak_fbdev_smem;74#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM)75module_param_unsafe(drm_leak_fbdev_smem, bool, 0600);76MODULE_PARM_DESC(drm_leak_fbdev_smem,77 "Allow unsafe leaking fbdev physical smem address [default=false]");78#endif79 80static LIST_HEAD(kernel_fb_helper_list);81static DEFINE_MUTEX(kernel_fb_helper_lock);82 83/**84 * DOC: fbdev helpers85 *86 * The fb helper functions are useful to provide an fbdev on top of a drm kernel87 * mode setting driver. They can be used mostly independently from the crtc88 * helper functions used by many drivers to implement the kernel mode setting89 * interfaces. Drivers that use one of the shared memory managers, TTM, SHMEM,90 * DMA, should instead use the corresponding fbdev emulation.91 *92 * For suspend/resume consider using drm_mode_config_helper_suspend() and93 * drm_mode_config_helper_resume() which takes care of fbdev as well.94 *95 * All other functions exported by the fb helper library can be used to96 * implement the fbdev driver interface by the driver.97 *98 * It is possible, though perhaps somewhat tricky, to implement race-free99 * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare()100 * helper must be called first to initialize the minimum required to make101 * hotplug detection work. Drivers also need to make sure to properly set up102 * the &drm_mode_config.funcs member. After calling drm_kms_helper_poll_init()103 * it is safe to enable interrupts and start processing hotplug events. At the104 * same time, drivers should initialize all modeset objects such as CRTCs,105 * encoders and connectors. To finish up the fbdev helper initialization, the106 * drm_fb_helper_init() function is called. To probe for all attached displays107 * and set up an initial configuration using the detected hardware, drivers108 * should call drm_fb_helper_initial_config().109 *110 * If &drm_framebuffer_funcs.dirty is set, the111 * drm_fb_helper_{cfb,sys}_{write,fillrect,copyarea,imageblit} functions will112 * accumulate changes and schedule &drm_fb_helper.dirty_work to run right113 * away. This worker then calls the dirty() function ensuring that it will114 * always run in process context since the fb_*() function could be running in115 * atomic context. If drm_fb_helper_deferred_io() is used as the deferred_io116 * callback it will also schedule dirty_work with the damage collected from the117 * mmap page writes.118 */119 120static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)121{122 uint16_t *r_base, *g_base, *b_base;123 124 if (crtc->funcs->gamma_set == NULL)125 return;126 127 r_base = crtc->gamma_store;128 g_base = r_base + crtc->gamma_size;129 b_base = g_base + crtc->gamma_size;130 131 crtc->funcs->gamma_set(crtc, r_base, g_base, b_base,132 crtc->gamma_size, NULL);133}134 135/**136 * drm_fb_helper_debug_enter - implementation for &fb_ops.fb_debug_enter137 * @info: fbdev registered by the helper138 */139int drm_fb_helper_debug_enter(struct fb_info *info)140{141 struct drm_fb_helper *helper = info->par;142 const struct drm_crtc_helper_funcs *funcs;143 struct drm_mode_set *mode_set;144 145 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {146 mutex_lock(&helper->client.modeset_mutex);147 drm_client_for_each_modeset(mode_set, &helper->client) {148 if (!mode_set->crtc->enabled)149 continue;150 151 funcs = mode_set->crtc->helper_private;152 if (funcs->mode_set_base_atomic == NULL)153 continue;154 155 if (drm_drv_uses_atomic_modeset(mode_set->crtc->dev))156 continue;157 158 funcs->mode_set_base_atomic(mode_set->crtc,159 mode_set->fb,160 mode_set->x,161 mode_set->y,162 ENTER_ATOMIC_MODE_SET);163 }164 mutex_unlock(&helper->client.modeset_mutex);165 }166 167 return 0;168}169EXPORT_SYMBOL(drm_fb_helper_debug_enter);170 171/**172 * drm_fb_helper_debug_leave - implementation for &fb_ops.fb_debug_leave173 * @info: fbdev registered by the helper174 */175int drm_fb_helper_debug_leave(struct fb_info *info)176{177 struct drm_fb_helper *helper = info->par;178 struct drm_client_dev *client = &helper->client;179 struct drm_device *dev = helper->dev;180 struct drm_crtc *crtc;181 const struct drm_crtc_helper_funcs *funcs;182 struct drm_mode_set *mode_set;183 struct drm_framebuffer *fb;184 185 mutex_lock(&client->modeset_mutex);186 drm_client_for_each_modeset(mode_set, client) {187 crtc = mode_set->crtc;188 if (drm_drv_uses_atomic_modeset(crtc->dev))189 continue;190 191 funcs = crtc->helper_private;192 fb = crtc->primary->fb;193 194 if (!crtc->enabled)195 continue;196 197 if (!fb) {198 drm_err(dev, "no fb to restore?\n");199 continue;200 }201 202 if (funcs->mode_set_base_atomic == NULL)203 continue;204 205 drm_fb_helper_restore_lut_atomic(mode_set->crtc);206 funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,207 crtc->y, LEAVE_ATOMIC_MODE_SET);208 }209 mutex_unlock(&client->modeset_mutex);210 211 return 0;212}213EXPORT_SYMBOL(drm_fb_helper_debug_leave);214 215static int216__drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper,217 bool force)218{219 bool do_delayed;220 int ret;221 222 if (!drm_fbdev_emulation || !fb_helper)223 return -ENODEV;224 225 if (READ_ONCE(fb_helper->deferred_setup))226 return 0;227 228 mutex_lock(&fb_helper->lock);229 if (force) {230 /*231 * Yes this is the _locked version which expects the master lock232 * to be held. But for forced restores we're intentionally233 * racing here, see drm_fb_helper_set_par().234 */235 ret = drm_client_modeset_commit_locked(&fb_helper->client);236 } else {237 ret = drm_client_modeset_commit(&fb_helper->client);238 }239 240 do_delayed = fb_helper->delayed_hotplug;241 if (do_delayed)242 fb_helper->delayed_hotplug = false;243 mutex_unlock(&fb_helper->lock);244 245 if (do_delayed)246 drm_fb_helper_hotplug_event(fb_helper);247 248 return ret;249}250 251/**252 * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration253 * @fb_helper: driver-allocated fbdev helper, can be NULL254 *255 * This helper should be called from fbdev emulation's &drm_client_funcs.restore256 * callback. It ensures that the user isn't greeted with a black screen when the257 * userspace compositor releases the display device.258 *259 * Returns:260 * 0 on success, or a negative errno code otherwise.261 */262int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)263{264 return __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, false);265}266EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);267 268#ifdef CONFIG_MAGIC_SYSRQ269/* emergency restore, don't bother with error reporting */270static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)271{272 struct drm_fb_helper *helper;273 274 mutex_lock(&kernel_fb_helper_lock);275 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {276 struct drm_device *dev = helper->dev;277 278 if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)279 continue;280 281 mutex_lock(&helper->lock);282 drm_client_modeset_commit_locked(&helper->client);283 mutex_unlock(&helper->lock);284 }285 mutex_unlock(&kernel_fb_helper_lock);286}287 288static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);289 290static void drm_fb_helper_sysrq(u8 dummy1)291{292 schedule_work(&drm_fb_helper_restore_work);293}294 295static const struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {296 .handler = drm_fb_helper_sysrq,297 .help_msg = "force-fb(v)",298 .action_msg = "Restore framebuffer console",299};300#else301static const struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };302#endif303 304static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)305{306 struct drm_fb_helper *fb_helper = info->par;307 308 mutex_lock(&fb_helper->lock);309 drm_client_modeset_dpms(&fb_helper->client, dpms_mode);310 mutex_unlock(&fb_helper->lock);311}312 313/**314 * drm_fb_helper_blank - implementation for &fb_ops.fb_blank315 * @blank: desired blanking state316 * @info: fbdev registered by the helper317 */318int drm_fb_helper_blank(int blank, struct fb_info *info)319{320 if (oops_in_progress)321 return -EBUSY;322 323 switch (blank) {324 /* Display: On; HSync: On, VSync: On */325 case FB_BLANK_UNBLANK:326 drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON);327 break;328 /* Display: Off; HSync: On, VSync: On */329 case FB_BLANK_NORMAL:330 drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);331 break;332 /* Display: Off; HSync: Off, VSync: On */333 case FB_BLANK_HSYNC_SUSPEND:334 drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);335 break;336 /* Display: Off; HSync: On, VSync: Off */337 case FB_BLANK_VSYNC_SUSPEND:338 drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND);339 break;340 /* Display: Off; HSync: Off, VSync: Off */341 case FB_BLANK_POWERDOWN:342 drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF);343 break;344 }345 return 0;346}347EXPORT_SYMBOL(drm_fb_helper_blank);348 349static void drm_fb_helper_resume_worker(struct work_struct *work)350{351 struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,352 resume_work);353 354 console_lock();355 fb_set_suspend(helper->info, 0);356 console_unlock();357}358 359static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper)360{361 struct drm_device *dev = helper->dev;362 struct drm_clip_rect *clip = &helper->damage_clip;363 struct drm_clip_rect clip_copy;364 unsigned long flags;365 int ret;366 367 if (drm_WARN_ON_ONCE(dev, !helper->funcs->fb_dirty))368 return;369 370 spin_lock_irqsave(&helper->damage_lock, flags);371 clip_copy = *clip;372 clip->x1 = clip->y1 = ~0;373 clip->x2 = clip->y2 = 0;374 spin_unlock_irqrestore(&helper->damage_lock, flags);375 376 ret = helper->funcs->fb_dirty(helper, &clip_copy);377 if (ret)378 goto err;379 380 return;381 382err:383 /*384 * Restore damage clip rectangle on errors. The next run385 * of the damage worker will perform the update.386 */387 spin_lock_irqsave(&helper->damage_lock, flags);388 clip->x1 = min_t(u32, clip->x1, clip_copy.x1);389 clip->y1 = min_t(u32, clip->y1, clip_copy.y1);390 clip->x2 = max_t(u32, clip->x2, clip_copy.x2);391 clip->y2 = max_t(u32, clip->y2, clip_copy.y2);392 spin_unlock_irqrestore(&helper->damage_lock, flags);393}394 395static void drm_fb_helper_damage_work(struct work_struct *work)396{397 struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper, damage_work);398 399 drm_fb_helper_fb_dirty(helper);400}401 402/**403 * drm_fb_helper_prepare - setup a drm_fb_helper structure404 * @dev: DRM device405 * @helper: driver-allocated fbdev helper structure to set up406 * @preferred_bpp: Preferred bits per pixel for the device.407 * @funcs: pointer to structure of functions associate with this helper408 *409 * Sets up the bare minimum to make the framebuffer helper usable. This is410 * useful to implement race-free initialization of the polling helpers.411 */412void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,413 unsigned int preferred_bpp,414 const struct drm_fb_helper_funcs *funcs)415{416 /*417 * Pick a preferred bpp of 32 if no value has been given. This418 * will select XRGB8888 for the framebuffer formats. All drivers419 * have to support XRGB8888 for backwards compatibility with legacy420 * userspace, so it's the safe choice here.421 *422 * TODO: Replace struct drm_mode_config.preferred_depth and this423 * bpp value with a preferred format that is given as struct424 * drm_format_info. Then derive all other values from the425 * format.426 */427 if (!preferred_bpp)428 preferred_bpp = 32;429 430 INIT_LIST_HEAD(&helper->kernel_fb_list);431 spin_lock_init(&helper->damage_lock);432 INIT_WORK(&helper->resume_work, drm_fb_helper_resume_worker);433 INIT_WORK(&helper->damage_work, drm_fb_helper_damage_work);434 helper->damage_clip.x1 = helper->damage_clip.y1 = ~0;435 mutex_init(&helper->lock);436 helper->funcs = funcs;437 helper->dev = dev;438 helper->preferred_bpp = preferred_bpp;439}440EXPORT_SYMBOL(drm_fb_helper_prepare);441 442/**443 * drm_fb_helper_unprepare - clean up a drm_fb_helper structure444 * @fb_helper: driver-allocated fbdev helper structure to set up445 *446 * Cleans up the framebuffer helper. Inverse of drm_fb_helper_prepare().447 */448void drm_fb_helper_unprepare(struct drm_fb_helper *fb_helper)449{450 mutex_destroy(&fb_helper->lock);451}452EXPORT_SYMBOL(drm_fb_helper_unprepare);453 454/**455 * drm_fb_helper_init - initialize a &struct drm_fb_helper456 * @dev: drm device457 * @fb_helper: driver-allocated fbdev helper structure to initialize458 *459 * This allocates the structures for the fbdev helper with the given limits.460 * Note that this won't yet touch the hardware (through the driver interfaces)461 * nor register the fbdev. This is only done in drm_fb_helper_initial_config()462 * to allow driver writes more control over the exact init sequence.463 *464 * Drivers must call drm_fb_helper_prepare() before calling this function.465 *466 * RETURNS:467 * Zero if everything went ok, nonzero otherwise.468 */469int drm_fb_helper_init(struct drm_device *dev,470 struct drm_fb_helper *fb_helper)471{472 int ret;473 474 /*475 * If this is not the generic fbdev client, initialize a drm_client476 * without callbacks so we can use the modesets.477 */478 if (!fb_helper->client.funcs) {479 ret = drm_client_init(dev, &fb_helper->client, "drm_fb_helper", NULL);480 if (ret)481 return ret;482 }483 484 dev->fb_helper = fb_helper;485 486 return 0;487}488EXPORT_SYMBOL(drm_fb_helper_init);489 490/**491 * drm_fb_helper_alloc_info - allocate fb_info and some of its members492 * @fb_helper: driver-allocated fbdev helper493 *494 * A helper to alloc fb_info and the member cmap. Called by the driver495 * within the fb_probe fb_helper callback function. Drivers do not496 * need to release the allocated fb_info structure themselves, this is497 * automatically done when calling drm_fb_helper_fini().498 *499 * RETURNS:500 * fb_info pointer if things went okay, pointer containing error code501 * otherwise502 */503struct fb_info *drm_fb_helper_alloc_info(struct drm_fb_helper *fb_helper)504{505 struct device *dev = fb_helper->dev->dev;506 struct fb_info *info;507 int ret;508 509 info = framebuffer_alloc(0, dev);510 if (!info)511 return ERR_PTR(-ENOMEM);512 513 if (!drm_leak_fbdev_smem)514 info->flags |= FBINFO_HIDE_SMEM_START;515 516 ret = fb_alloc_cmap(&info->cmap, 256, 0);517 if (ret)518 goto err_release;519 520 fb_helper->info = info;521 info->skip_vt_switch = true;522 523 info->skip_panic = drm_panic_is_enabled(fb_helper->dev);524 return info;525 526err_release:527 framebuffer_release(info);528 return ERR_PTR(ret);529}530EXPORT_SYMBOL(drm_fb_helper_alloc_info);531 532/**533 * drm_fb_helper_release_info - release fb_info and its members534 * @fb_helper: driver-allocated fbdev helper535 *536 * A helper to release fb_info and the member cmap. Drivers do not537 * need to release the allocated fb_info structure themselves, this is538 * automatically done when calling drm_fb_helper_fini().539 */540void drm_fb_helper_release_info(struct drm_fb_helper *fb_helper)541{542 struct fb_info *info = fb_helper->info;543 544 if (!info)545 return;546 547 fb_helper->info = NULL;548 549 if (info->cmap.len)550 fb_dealloc_cmap(&info->cmap);551 framebuffer_release(info);552}553EXPORT_SYMBOL(drm_fb_helper_release_info);554 555/**556 * drm_fb_helper_unregister_info - unregister fb_info framebuffer device557 * @fb_helper: driver-allocated fbdev helper, can be NULL558 *559 * A wrapper around unregister_framebuffer, to release the fb_info560 * framebuffer device. This must be called before releasing all resources for561 * @fb_helper by calling drm_fb_helper_fini().562 */563void drm_fb_helper_unregister_info(struct drm_fb_helper *fb_helper)564{565 if (fb_helper && fb_helper->info)566 unregister_framebuffer(fb_helper->info);567}568EXPORT_SYMBOL(drm_fb_helper_unregister_info);569 570/**571 * drm_fb_helper_fini - finialize a &struct drm_fb_helper572 * @fb_helper: driver-allocated fbdev helper, can be NULL573 *574 * This cleans up all remaining resources associated with @fb_helper.575 */576void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)577{578 if (!fb_helper)579 return;580 581 fb_helper->dev->fb_helper = NULL;582 583 if (!drm_fbdev_emulation)584 return;585 586 cancel_work_sync(&fb_helper->resume_work);587 cancel_work_sync(&fb_helper->damage_work);588 589 drm_fb_helper_release_info(fb_helper);590 591 mutex_lock(&kernel_fb_helper_lock);592 if (!list_empty(&fb_helper->kernel_fb_list)) {593 list_del(&fb_helper->kernel_fb_list);594 if (list_empty(&kernel_fb_helper_list))595 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);596 }597 mutex_unlock(&kernel_fb_helper_lock);598 599 if (!fb_helper->client.funcs)600 drm_client_release(&fb_helper->client);601}602EXPORT_SYMBOL(drm_fb_helper_fini);603 604static void drm_fb_helper_add_damage_clip(struct drm_fb_helper *helper, u32 x, u32 y,605 u32 width, u32 height)606{607 struct drm_clip_rect *clip = &helper->damage_clip;608 unsigned long flags;609 610 spin_lock_irqsave(&helper->damage_lock, flags);611 clip->x1 = min_t(u32, clip->x1, x);612 clip->y1 = min_t(u32, clip->y1, y);613 clip->x2 = max_t(u32, clip->x2, x + width);614 clip->y2 = max_t(u32, clip->y2, y + height);615 spin_unlock_irqrestore(&helper->damage_lock, flags);616}617 618static void drm_fb_helper_damage(struct drm_fb_helper *helper, u32 x, u32 y,619 u32 width, u32 height)620{621 /*622 * This function may be invoked by panic() to flush the frame623 * buffer, where all CPUs except the panic CPU are stopped.624 * During the following schedule_work(), the panic CPU needs625 * the worker_pool lock, which might be held by a stopped CPU,626 * causing schedule_work() and panic() to block. Return early on627 * oops_in_progress to prevent this blocking.628 */629 if (oops_in_progress)630 return;631 632 drm_fb_helper_add_damage_clip(helper, x, y, width, height);633 634 schedule_work(&helper->damage_work);635}636 637/*638 * Convert memory region into area of scanlines and pixels per639 * scanline. The parameters off and len must not reach beyond640 * the end of the framebuffer.641 */642static void drm_fb_helper_memory_range_to_clip(struct fb_info *info, off_t off, size_t len,643 struct drm_rect *clip)644{645 u32 line_length = info->fix.line_length;646 u32 fb_height = info->var.yres;647 off_t end = off + len;648 u32 x1 = 0;649 u32 y1 = off / line_length;650 u32 x2 = info->var.xres;651 u32 y2 = DIV_ROUND_UP(end, line_length);652 653 /* Don't allow any of them beyond the bottom bound of display area */654 if (y1 > fb_height)655 y1 = fb_height;656 if (y2 > fb_height)657 y2 = fb_height;658 659 if ((y2 - y1) == 1) {660 /*661 * We've only written to a single scanline. Try to reduce662 * the number of horizontal pixels that need an update.663 */664 off_t bit_off = (off % line_length) * 8;665 off_t bit_end = (end % line_length) * 8;666 667 x1 = bit_off / info->var.bits_per_pixel;668 x2 = DIV_ROUND_UP(bit_end, info->var.bits_per_pixel);669 }670 671 drm_rect_init(clip, x1, y1, x2 - x1, y2 - y1);672}673 674/* Don't use in new code. */675void drm_fb_helper_damage_range(struct fb_info *info, off_t off, size_t len)676{677 struct drm_fb_helper *fb_helper = info->par;678 struct drm_rect damage_area;679 680 drm_fb_helper_memory_range_to_clip(info, off, len, &damage_area);681 drm_fb_helper_damage(fb_helper, damage_area.x1, damage_area.y1,682 drm_rect_width(&damage_area),683 drm_rect_height(&damage_area));684}685EXPORT_SYMBOL(drm_fb_helper_damage_range);686 687/* Don't use in new code. */688void drm_fb_helper_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u32 height)689{690 struct drm_fb_helper *fb_helper = info->par;691 692 drm_fb_helper_damage(fb_helper, x, y, width, height);693}694EXPORT_SYMBOL(drm_fb_helper_damage_area);695 696/**697 * drm_fb_helper_deferred_io() - fbdev deferred_io callback function698 * @info: fb_info struct pointer699 * @pagereflist: list of mmap framebuffer pages that have to be flushed700 *701 * This function is used as the &fb_deferred_io.deferred_io702 * callback function for flushing the fbdev mmap writes.703 */704void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist)705{706 struct drm_fb_helper *helper = info->par;707 unsigned long start, end, min_off, max_off, total_size;708 struct fb_deferred_io_pageref *pageref;709 struct drm_rect damage_area;710 711 min_off = ULONG_MAX;712 max_off = 0;713 list_for_each_entry(pageref, pagereflist, list) {714 start = pageref->offset;715 end = start + PAGE_SIZE;716 min_off = min(min_off, start);717 max_off = max(max_off, end);718 }719 720 /*721 * As we can only track pages, we might reach beyond the end722 * of the screen and account for non-existing scanlines. Hence,723 * keep the covered memory area within the screen buffer.724 */725 if (info->screen_size)726 total_size = info->screen_size;727 else728 total_size = info->fix.smem_len;729 max_off = min(max_off, total_size);730 731 if (min_off < max_off) {732 drm_fb_helper_memory_range_to_clip(info, min_off, max_off - min_off, &damage_area);733 drm_fb_helper_damage(helper, damage_area.x1, damage_area.y1,734 drm_rect_width(&damage_area),735 drm_rect_height(&damage_area));736 }737}738EXPORT_SYMBOL(drm_fb_helper_deferred_io);739 740/**741 * drm_fb_helper_set_suspend - wrapper around fb_set_suspend742 * @fb_helper: driver-allocated fbdev helper, can be NULL743 * @suspend: whether to suspend or resume744 *745 * A wrapper around fb_set_suspend implemented by fbdev core.746 * Use drm_fb_helper_set_suspend_unlocked() if you don't need to take747 * the lock yourself748 */749void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend)750{751 if (fb_helper && fb_helper->info)752 fb_set_suspend(fb_helper->info, suspend);753}754EXPORT_SYMBOL(drm_fb_helper_set_suspend);755 756/**757 * drm_fb_helper_set_suspend_unlocked - wrapper around fb_set_suspend that also758 * takes the console lock759 * @fb_helper: driver-allocated fbdev helper, can be NULL760 * @suspend: whether to suspend or resume761 *762 * A wrapper around fb_set_suspend() that takes the console lock. If the lock763 * isn't available on resume, a worker is tasked with waiting for the lock764 * to become available. The console lock can be pretty contented on resume765 * due to all the printk activity.766 *767 * This function can be called multiple times with the same state since768 * &fb_info.state is checked to see if fbdev is running or not before locking.769 *770 * Use drm_fb_helper_set_suspend() if you need to take the lock yourself.771 */772void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,773 bool suspend)774{775 if (!fb_helper || !fb_helper->info)776 return;777 778 /* make sure there's no pending/ongoing resume */779 flush_work(&fb_helper->resume_work);780 781 if (suspend) {782 if (fb_helper->info->state != FBINFO_STATE_RUNNING)783 return;784 785 console_lock();786 787 } else {788 if (fb_helper->info->state == FBINFO_STATE_RUNNING)789 return;790 791 if (!console_trylock()) {792 schedule_work(&fb_helper->resume_work);793 return;794 }795 }796 797 fb_set_suspend(fb_helper->info, suspend);798 console_unlock();799}800EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked);801 802static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info)803{804 u32 *palette = (u32 *)info->pseudo_palette;805 int i;806 807 if (cmap->start + cmap->len > 16)808 return -EINVAL;809 810 for (i = 0; i < cmap->len; ++i) {811 u16 red = cmap->red[i];812 u16 green = cmap->green[i];813 u16 blue = cmap->blue[i];814 u32 value;815 816 red >>= 16 - info->var.red.length;817 green >>= 16 - info->var.green.length;818 blue >>= 16 - info->var.blue.length;819 value = (red << info->var.red.offset) |820 (green << info->var.green.offset) |821 (blue << info->var.blue.offset);822 if (info->var.transp.length > 0) {823 u32 mask = (1 << info->var.transp.length) - 1;824 825 mask <<= info->var.transp.offset;826 value |= mask;827 }828 palette[cmap->start + i] = value;829 }830 831 return 0;832}833 834static int setcmap_legacy(struct fb_cmap *cmap, struct fb_info *info)835{836 struct drm_fb_helper *fb_helper = info->par;837 struct drm_mode_set *modeset;838 struct drm_crtc *crtc;839 u16 *r, *g, *b;840 int ret = 0;841 842 drm_modeset_lock_all(fb_helper->dev);843 drm_client_for_each_modeset(modeset, &fb_helper->client) {844 crtc = modeset->crtc;845 if (!crtc->funcs->gamma_set || !crtc->gamma_size) {846 ret = -EINVAL;847 goto out;848 }849 850 if (cmap->start + cmap->len > crtc->gamma_size) {851 ret = -EINVAL;852 goto out;853 }854 855 r = crtc->gamma_store;856 g = r + crtc->gamma_size;857 b = g + crtc->gamma_size;858 859 memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r));860 memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g));861 memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b));862 863 ret = crtc->funcs->gamma_set(crtc, r, g, b,864 crtc->gamma_size, NULL);865 if (ret)866 goto out;867 }868out:869 drm_modeset_unlock_all(fb_helper->dev);870 871 return ret;872}873 874static struct drm_property_blob *setcmap_new_gamma_lut(struct drm_crtc *crtc,875 struct fb_cmap *cmap)876{877 struct drm_device *dev = crtc->dev;878 struct drm_property_blob *gamma_lut;879 struct drm_color_lut *lut;880 int size = crtc->gamma_size;881 int i;882 883 if (!size || cmap->start + cmap->len > size)884 return ERR_PTR(-EINVAL);885 886 gamma_lut = drm_property_create_blob(dev, sizeof(*lut) * size, NULL);887 if (IS_ERR(gamma_lut))888 return gamma_lut;889 890 lut = gamma_lut->data;891 if (cmap->start || cmap->len != size) {892 u16 *r = crtc->gamma_store;893 u16 *g = r + crtc->gamma_size;894 u16 *b = g + crtc->gamma_size;895 896 for (i = 0; i < cmap->start; i++) {897 lut[i].red = r[i];898 lut[i].green = g[i];899 lut[i].blue = b[i];900 }901 for (i = cmap->start + cmap->len; i < size; i++) {902 lut[i].red = r[i];903 lut[i].green = g[i];904 lut[i].blue = b[i];905 }906 }907 908 for (i = 0; i < cmap->len; i++) {909 lut[cmap->start + i].red = cmap->red[i];910 lut[cmap->start + i].green = cmap->green[i];911 lut[cmap->start + i].blue = cmap->blue[i];912 }913 914 return gamma_lut;915}916 917static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info)918{919 struct drm_fb_helper *fb_helper = info->par;920 struct drm_device *dev = fb_helper->dev;921 struct drm_property_blob *gamma_lut = NULL;922 struct drm_modeset_acquire_ctx ctx;923 struct drm_crtc_state *crtc_state;924 struct drm_atomic_state *state;925 struct drm_mode_set *modeset;926 struct drm_crtc *crtc;927 u16 *r, *g, *b;928 bool replaced;929 int ret = 0;930 931 drm_modeset_acquire_init(&ctx, 0);932 933 state = drm_atomic_state_alloc(dev);934 if (!state) {935 ret = -ENOMEM;936 goto out_ctx;937 }938 939 state->acquire_ctx = &ctx;940retry:941 drm_client_for_each_modeset(modeset, &fb_helper->client) {942 crtc = modeset->crtc;943 944 if (!gamma_lut)945 gamma_lut = setcmap_new_gamma_lut(crtc, cmap);946 if (IS_ERR(gamma_lut)) {947 ret = PTR_ERR(gamma_lut);948 gamma_lut = NULL;949 goto out_state;950 }951 952 crtc_state = drm_atomic_get_crtc_state(state, crtc);953 if (IS_ERR(crtc_state)) {954 ret = PTR_ERR(crtc_state);955 goto out_state;956 }957 958 /*959 * FIXME: This always uses gamma_lut. Some HW have only960 * degamma_lut, in which case we should reset gamma_lut and set961 * degamma_lut. See drm_crtc_legacy_gamma_set().962 */963 replaced = drm_property_replace_blob(&crtc_state->degamma_lut,964 NULL);965 replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL);966 replaced |= drm_property_replace_blob(&crtc_state->gamma_lut,967 gamma_lut);968 crtc_state->color_mgmt_changed |= replaced;969 }970 971 ret = drm_atomic_commit(state);972 if (ret)973 goto out_state;974 975 drm_client_for_each_modeset(modeset, &fb_helper->client) {976 crtc = modeset->crtc;977 978 r = crtc->gamma_store;979 g = r + crtc->gamma_size;980 b = g + crtc->gamma_size;981 982 memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r));983 memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g));984 memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b));985 }986 987out_state:988 if (ret == -EDEADLK)989 goto backoff;990 991 drm_property_blob_put(gamma_lut);992 drm_atomic_state_put(state);993out_ctx:994 drm_modeset_drop_locks(&ctx);995 drm_modeset_acquire_fini(&ctx);996 997 return ret;998 999backoff:1000 drm_atomic_state_clear(state);1001 drm_modeset_backoff(&ctx);1002 goto retry;1003}1004 1005/**1006 * drm_fb_helper_setcmap - implementation for &fb_ops.fb_setcmap1007 * @cmap: cmap to set1008 * @info: fbdev registered by the helper1009 */1010int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)1011{1012 struct drm_fb_helper *fb_helper = info->par;1013 struct drm_device *dev = fb_helper->dev;1014 int ret;1015 1016 if (oops_in_progress)1017 return -EBUSY;1018 1019 mutex_lock(&fb_helper->lock);1020 1021 if (!drm_master_internal_acquire(dev)) {1022 ret = -EBUSY;1023 goto unlock;1024 }1025 1026 mutex_lock(&fb_helper->client.modeset_mutex);1027 if (info->fix.visual == FB_VISUAL_TRUECOLOR)1028 ret = setcmap_pseudo_palette(cmap, info);1029 else if (drm_drv_uses_atomic_modeset(fb_helper->dev))1030 ret = setcmap_atomic(cmap, info);1031 else1032 ret = setcmap_legacy(cmap, info);1033 mutex_unlock(&fb_helper->client.modeset_mutex);1034 1035 drm_master_internal_release(dev);1036unlock:1037 mutex_unlock(&fb_helper->lock);1038 1039 return ret;1040}1041EXPORT_SYMBOL(drm_fb_helper_setcmap);1042 1043/**1044 * drm_fb_helper_ioctl - legacy ioctl implementation1045 * @info: fbdev registered by the helper1046 * @cmd: ioctl command1047 * @arg: ioctl argument1048 *1049 * A helper to implement the standard fbdev ioctl. Only1050 * FBIO_WAITFORVSYNC is implemented for now.1051 */1052int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,1053 unsigned long arg)1054{1055 struct drm_fb_helper *fb_helper = info->par;1056 struct drm_device *dev = fb_helper->dev;1057 struct drm_crtc *crtc;1058 int ret = 0;1059 1060 mutex_lock(&fb_helper->lock);1061 if (!drm_master_internal_acquire(dev)) {1062 ret = -EBUSY;1063 goto unlock;1064 }1065 1066 switch (cmd) {1067 case FBIO_WAITFORVSYNC:1068 /*1069 * Only consider the first CRTC.1070 *1071 * This ioctl is supposed to take the CRTC number as1072 * an argument, but in fbdev times, what that number1073 * was supposed to be was quite unclear, different1074 * drivers were passing that argument differently1075 * (some by reference, some by value), and most of the1076 * userspace applications were just hardcoding 0 as an1077 * argument.1078 *1079 * The first CRTC should be the integrated panel on1080 * most drivers, so this is the best choice we can1081 * make. If we're not smart enough here, one should1082 * just consider switch the userspace to KMS.1083 */1084 crtc = fb_helper->client.modesets[0].crtc;1085 1086 /*1087 * Only wait for a vblank event if the CRTC is1088 * enabled, otherwise just don't do anythintg,1089 * not even report an error.1090 */1091 ret = drm_crtc_vblank_get(crtc);1092 if (!ret) {1093 drm_crtc_wait_one_vblank(crtc);1094 drm_crtc_vblank_put(crtc);1095 }1096 1097 ret = 0;1098 break;1099 default:1100 ret = -ENOTTY;1101 }1102 1103 drm_master_internal_release(dev);1104unlock:1105 mutex_unlock(&fb_helper->lock);1106 return ret;1107}1108EXPORT_SYMBOL(drm_fb_helper_ioctl);1109 1110static bool drm_fb_pixel_format_equal(const struct fb_var_screeninfo *var_1,1111 const struct fb_var_screeninfo *var_2)1112{1113 return var_1->bits_per_pixel == var_2->bits_per_pixel &&1114 var_1->grayscale == var_2->grayscale &&1115 var_1->red.offset == var_2->red.offset &&1116 var_1->red.length == var_2->red.length &&1117 var_1->red.msb_right == var_2->red.msb_right &&1118 var_1->green.offset == var_2->green.offset &&1119 var_1->green.length == var_2->green.length &&1120 var_1->green.msb_right == var_2->green.msb_right &&1121 var_1->blue.offset == var_2->blue.offset &&1122 var_1->blue.length == var_2->blue.length &&1123 var_1->blue.msb_right == var_2->blue.msb_right &&1124 var_1->transp.offset == var_2->transp.offset &&1125 var_1->transp.length == var_2->transp.length &&1126 var_1->transp.msb_right == var_2->transp.msb_right;1127}1128 1129static void drm_fb_helper_fill_pixel_fmt(struct fb_var_screeninfo *var,1130 const struct drm_format_info *format)1131{1132 u8 depth = format->depth;1133 1134 if (format->is_color_indexed) {1135 var->red.offset = 0;1136 var->green.offset = 0;1137 var->blue.offset = 0;1138 var->red.length = depth;1139 var->green.length = depth;1140 var->blue.length = depth;1141 var->transp.offset = 0;1142 var->transp.length = 0;1143 return;1144 }1145 1146 switch (depth) {1147 case 15:1148 var->red.offset = 10;1149 var->green.offset = 5;1150 var->blue.offset = 0;1151 var->red.length = 5;1152 var->green.length = 5;1153 var->blue.length = 5;1154 var->transp.offset = 15;1155 var->transp.length = 1;1156 break;1157 case 16:1158 var->red.offset = 11;1159 var->green.offset = 5;1160 var->blue.offset = 0;1161 var->red.length = 5;1162 var->green.length = 6;1163 var->blue.length = 5;1164 var->transp.offset = 0;1165 break;1166 case 24:1167 var->red.offset = 16;1168 var->green.offset = 8;1169 var->blue.offset = 0;1170 var->red.length = 8;1171 var->green.length = 8;1172 var->blue.length = 8;1173 var->transp.offset = 0;1174 var->transp.length = 0;1175 break;1176 case 32:1177 var->red.offset = 16;1178 var->green.offset = 8;1179 var->blue.offset = 0;1180 var->red.length = 8;1181 var->green.length = 8;1182 var->blue.length = 8;1183 var->transp.offset = 24;1184 var->transp.length = 8;1185 break;1186 default:1187 break;1188 }1189}1190 1191static void __fill_var(struct fb_var_screeninfo *var, struct fb_info *info,1192 struct drm_framebuffer *fb)1193{1194 int i;1195 1196 var->xres_virtual = fb->width;1197 var->yres_virtual = fb->height;1198 var->accel_flags = 0;1199 var->bits_per_pixel = drm_format_info_bpp(fb->format, 0);1200 1201 var->height = info->var.height;1202 var->width = info->var.width;1203 1204 var->left_margin = var->right_margin = 0;1205 var->upper_margin = var->lower_margin = 0;1206 var->hsync_len = var->vsync_len = 0;1207 var->sync = var->vmode = 0;1208 var->rotate = 0;1209 var->colorspace = 0;1210 for (i = 0; i < 4; i++)1211 var->reserved[i] = 0;1212}1213 1214/**1215 * drm_fb_helper_check_var - implementation for &fb_ops.fb_check_var1216 * @var: screeninfo to check1217 * @info: fbdev registered by the helper1218 */1219int drm_fb_helper_check_var(struct fb_var_screeninfo *var,1220 struct fb_info *info)1221{1222 struct drm_fb_helper *fb_helper = info->par;1223 struct drm_framebuffer *fb = fb_helper->fb;1224 const struct drm_format_info *format = fb->format;1225 struct drm_device *dev = fb_helper->dev;1226 unsigned int bpp;1227 1228 if (in_dbg_master())1229 return -EINVAL;1230 1231 if (var->pixclock != 0) {1232 drm_dbg_kms(dev, "fbdev emulation doesn't support changing the pixel clock, value of pixclock is ignored\n");1233 var->pixclock = 0;1234 }1235 1236 switch (format->format) {1237 case DRM_FORMAT_C1:1238 case DRM_FORMAT_C2:1239 case DRM_FORMAT_C4:1240 /* supported format with sub-byte pixels */1241 break;1242 1243 default:1244 if ((drm_format_info_block_width(format, 0) > 1) ||1245 (drm_format_info_block_height(format, 0) > 1))1246 return -EINVAL;1247 break;1248 }1249 1250 /*1251 * Changes struct fb_var_screeninfo are currently not pushed back1252 * to KMS, hence fail if different settings are requested.1253 */1254 bpp = drm_format_info_bpp(format, 0);1255 if (var->bits_per_pixel > bpp ||1256 var->xres > fb->width || var->yres > fb->height ||1257 var->xres_virtual > fb->width || var->yres_virtual > fb->height) {1258 drm_dbg_kms(dev, "fb requested width/height/bpp can't fit in current fb "1259 "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n",1260 var->xres, var->yres, var->bits_per_pixel,1261 var->xres_virtual, var->yres_virtual,1262 fb->width, fb->height, bpp);1263 return -EINVAL;1264 }1265 1266 __fill_var(var, info, fb);1267 1268 /*1269 * fb_pan_display() validates this, but fb_set_par() doesn't and just1270 * falls over. Note that __fill_var above adjusts y/res_virtual.1271 */1272 if (var->yoffset > var->yres_virtual - var->yres ||1273 var->xoffset > var->xres_virtual - var->xres)1274 return -EINVAL;1275 1276 /* We neither support grayscale nor FOURCC (also stored in here). */1277 if (var->grayscale > 0)1278 return -EINVAL;1279 1280 if (var->nonstd)1281 return -EINVAL;1282 1283 /*1284 * Workaround for SDL 1.2, which is known to be setting all pixel format1285 * fields values to zero in some cases. We treat this situation as a1286 * kind of "use some reasonable autodetected values".1287 */1288 if (!var->red.offset && !var->green.offset &&1289 !var->blue.offset && !var->transp.offset &&1290 !var->red.length && !var->green.length &&1291 !var->blue.length && !var->transp.length &&1292 !var->red.msb_right && !var->green.msb_right &&1293 !var->blue.msb_right && !var->transp.msb_right) {1294 drm_fb_helper_fill_pixel_fmt(var, format);1295 }1296 1297 /*1298 * drm fbdev emulation doesn't support changing the pixel format at all,1299 * so reject all pixel format changing requests.1300 */1301 if (!drm_fb_pixel_format_equal(var, &info->var)) {1302 drm_dbg_kms(dev, "fbdev emulation doesn't support changing the pixel format\n");1303 return -EINVAL;1304 }1305 1306 return 0;1307}1308EXPORT_SYMBOL(drm_fb_helper_check_var);1309 1310/**1311 * drm_fb_helper_set_par - implementation for &fb_ops.fb_set_par1312 * @info: fbdev registered by the helper1313 *1314 * This will let fbcon do the mode init and is called at initialization time by1315 * the fbdev core when registering the driver, and later on through the hotplug1316 * callback.1317 */1318int drm_fb_helper_set_par(struct fb_info *info)1319{1320 struct drm_fb_helper *fb_helper = info->par;1321 struct fb_var_screeninfo *var = &info->var;1322 bool force;1323 1324 if (oops_in_progress)1325 return -EBUSY;1326 1327 /*1328 * Normally we want to make sure that a kms master takes precedence over1329 * fbdev, to avoid fbdev flickering and occasionally stealing the1330 * display status. But Xorg first sets the vt back to text mode using1331 * the KDSET IOCTL with KD_TEXT, and only after that drops the master1332 * status when exiting.1333 *1334 * In the past this was caught by drm_fb_helper_lastclose(), but on1335 * modern systems where logind always keeps a drm fd open to orchestrate1336 * the vt switching, this doesn't work.1337 *1338 * To not break the userspace ABI we have this special case here, which1339 * is only used for the above case. Everything else uses the normal1340 * commit function, which ensures that we never steal the display from1341 * an active drm master.1342 */1343 force = var->activate & FB_ACTIVATE_KD_TEXT;1344 1345 __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);1346 1347 return 0;1348}1349EXPORT_SYMBOL(drm_fb_helper_set_par);1350 1351static void pan_set(struct drm_fb_helper *fb_helper, int x, int y)1352{1353 struct drm_mode_set *mode_set;1354 1355 mutex_lock(&fb_helper->client.modeset_mutex);1356 drm_client_for_each_modeset(mode_set, &fb_helper->client) {1357 mode_set->x = x;1358 mode_set->y = y;1359 }1360 mutex_unlock(&fb_helper->client.modeset_mutex);1361}1362 1363static int pan_display_atomic(struct fb_var_screeninfo *var,1364 struct fb_info *info)1365{1366 struct drm_fb_helper *fb_helper = info->par;1367 int ret;1368 1369 pan_set(fb_helper, var->xoffset, var->yoffset);1370 1371 ret = drm_client_modeset_commit_locked(&fb_helper->client);1372 if (!ret) {1373 info->var.xoffset = var->xoffset;1374 info->var.yoffset = var->yoffset;1375 } else1376 pan_set(fb_helper, info->var.xoffset, info->var.yoffset);1377 1378 return ret;1379}1380 1381static int pan_display_legacy(struct fb_var_screeninfo *var,1382 struct fb_info *info)1383{1384 struct drm_fb_helper *fb_helper = info->par;1385 struct drm_client_dev *client = &fb_helper->client;1386 struct drm_mode_set *modeset;1387 int ret = 0;1388 1389 mutex_lock(&client->modeset_mutex);1390 drm_modeset_lock_all(fb_helper->dev);1391 drm_client_for_each_modeset(modeset, client) {1392 modeset->x = var->xoffset;1393 modeset->y = var->yoffset;1394 1395 if (modeset->num_connectors) {1396 ret = drm_mode_set_config_internal(modeset);1397 if (!ret) {1398 info->var.xoffset = var->xoffset;1399 info->var.yoffset = var->yoffset;1400 }1401 }1402 }1403 drm_modeset_unlock_all(fb_helper->dev);1404 mutex_unlock(&client->modeset_mutex);1405 1406 return ret;1407}1408 1409/**1410 * drm_fb_helper_pan_display - implementation for &fb_ops.fb_pan_display1411 * @var: updated screen information1412 * @info: fbdev registered by the helper1413 */1414int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,1415 struct fb_info *info)1416{1417 struct drm_fb_helper *fb_helper = info->par;1418 struct drm_device *dev = fb_helper->dev;1419 int ret;1420 1421 if (oops_in_progress)1422 return -EBUSY;1423 1424 mutex_lock(&fb_helper->lock);1425 if (!drm_master_internal_acquire(dev)) {1426 ret = -EBUSY;1427 goto unlock;1428 }1429 1430 if (drm_drv_uses_atomic_modeset(dev))1431 ret = pan_display_atomic(var, info);1432 else1433 ret = pan_display_legacy(var, info);1434 1435 drm_master_internal_release(dev);1436unlock:1437 mutex_unlock(&fb_helper->lock);1438 1439 return ret;1440}1441EXPORT_SYMBOL(drm_fb_helper_pan_display);1442 1443static uint32_t drm_fb_helper_find_format(struct drm_fb_helper *fb_helper, const uint32_t *formats,1444 size_t format_count, uint32_t bpp, uint32_t depth)1445{1446 struct drm_device *dev = fb_helper->dev;1447 uint32_t format;1448 size_t i;1449 1450 /*1451 * Do not consider YUV or other complicated formats1452 * for framebuffers. This means only legacy formats1453 * are supported (fmt->depth is a legacy field), but1454 * the framebuffer emulation can only deal with such1455 * formats, specifically RGB/BGA formats.1456 */1457 format = drm_mode_legacy_fb_format(bpp, depth);1458 if (!format)1459 goto err;1460 1461 for (i = 0; i < format_count; ++i) {1462 if (formats[i] == format)1463 return format;1464 }1465 1466err:1467 /* We found nothing. */1468 drm_warn(dev, "bpp/depth value of %u/%u not supported\n", bpp, depth);1469 1470 return DRM_FORMAT_INVALID;1471}1472 1473static uint32_t drm_fb_helper_find_color_mode_format(struct drm_fb_helper *fb_helper,1474 const uint32_t *formats, size_t format_count,1475 unsigned int color_mode)1476{1477 struct drm_device *dev = fb_helper->dev;1478 uint32_t bpp, depth;1479 1480 switch (color_mode) {1481 case 1:1482 case 2:1483 case 4:1484 case 8:1485 case 16:1486 case 24:1487 bpp = depth = color_mode;1488 break;1489 case 15:1490 bpp = 16;1491 depth = 15;1492 break;1493 case 32:1494 bpp = 32;1495 depth = 24;1496 break;1497 default:1498 drm_info(dev, "unsupported color mode of %d\n", color_mode);1499 return DRM_FORMAT_INVALID;1500 }1501 1502 return drm_fb_helper_find_format(fb_helper, formats, format_count, bpp, depth);1503}1504 1505static int __drm_fb_helper_find_sizes(struct drm_fb_helper *fb_helper,1506 struct drm_fb_helper_surface_size *sizes)1507{1508 struct drm_client_dev *client = &fb_helper->client;1509 struct drm_device *dev = fb_helper->dev;1510 int crtc_count = 0;1511 struct drm_connector_list_iter conn_iter;1512 struct drm_connector *connector;1513 struct drm_mode_set *mode_set;1514 uint32_t surface_format = DRM_FORMAT_INVALID;1515 const struct drm_format_info *info;1516 1517 memset(sizes, 0, sizeof(*sizes));1518 sizes->fb_width = (u32)-1;1519 sizes->fb_height = (u32)-1;1520 1521 drm_client_for_each_modeset(mode_set, client) {1522 struct drm_crtc *crtc = mode_set->crtc;1523 struct drm_plane *plane = crtc->primary;1524 1525 drm_dbg_kms(dev, "test CRTC %u primary plane\n", drm_crtc_index(crtc));1526 1527 drm_connector_list_iter_begin(fb_helper->dev, &conn_iter);1528 drm_client_for_each_connector_iter(connector, &conn_iter) {1529 struct drm_cmdline_mode *cmdline_mode = &connector->cmdline_mode;1530 1531 if (!cmdline_mode->bpp_specified)1532 continue;1533 1534 surface_format = drm_fb_helper_find_color_mode_format(fb_helper,1535 plane->format_types,1536 plane->format_count,1537 cmdline_mode->bpp);1538 if (surface_format != DRM_FORMAT_INVALID)1539 break; /* found supported format */1540 }1541 drm_connector_list_iter_end(&conn_iter);1542 1543 if (surface_format != DRM_FORMAT_INVALID)1544 break; /* found supported format */1545 1546 /* try preferred color mode */1547 surface_format = drm_fb_helper_find_color_mode_format(fb_helper,1548 plane->format_types,1549 plane->format_count,1550 fb_helper->preferred_bpp);1551 if (surface_format != DRM_FORMAT_INVALID)1552 break; /* found supported format */1553 }1554 1555 if (surface_format == DRM_FORMAT_INVALID) {1556 /*1557 * If none of the given color modes works, fall back1558 * to XRGB8888. Drivers are expected to provide this1559 * format for compatibility with legacy applications.1560 */1561 drm_warn(dev, "No compatible format found\n");1562 surface_format = drm_driver_legacy_fb_format(dev, 32, 24);1563 }1564 1565 info = drm_format_info(surface_format);1566 sizes->surface_bpp = drm_format_info_bpp(info, 0);1567 sizes->surface_depth = info->depth;1568 1569 /* first up get a count of crtcs now in use and new min/maxes width/heights */1570 crtc_count = 0;1571 drm_client_for_each_modeset(mode_set, client) {1572 struct drm_display_mode *desired_mode;1573 int x, y, j;1574 /* in case of tile group, are we the last tile vert or horiz?1575 * If no tile group you are always the last one both vertically1576 * and horizontally1577 */1578 bool lastv = true, lasth = true;1579 1580 desired_mode = mode_set->mode;1581 1582 if (!desired_mode)1583 continue;1584 1585 crtc_count++;1586 1587 x = mode_set->x;1588 y = mode_set->y;1589 1590 sizes->surface_width =1591 max_t(u32, desired_mode->hdisplay + x, sizes->surface_width);1592 sizes->surface_height =1593 max_t(u32, desired_mode->vdisplay + y, sizes->surface_height);1594 1595 for (j = 0; j < mode_set->num_connectors; j++) {1596 struct drm_connector *connector = mode_set->connectors[j];1597 1598 if (connector->has_tile &&1599 desired_mode->hdisplay == connector->tile_h_size &&1600 desired_mode->vdisplay == connector->tile_v_size) {1601 lasth = (connector->tile_h_loc == (connector->num_h_tile - 1));1602 lastv = (connector->tile_v_loc == (connector->num_v_tile - 1));1603 /* cloning to multiple tiles is just crazy-talk, so: */1604 break;1605 }1606 }1607 1608 if (lasth)1609 sizes->fb_width = min_t(u32, desired_mode->hdisplay + x, sizes->fb_width);1610 if (lastv)1611 sizes->fb_height = min_t(u32, desired_mode->vdisplay + y, sizes->fb_height);1612 }1613 1614 if (crtc_count == 0 || sizes->fb_width == -1 || sizes->fb_height == -1) {1615 drm_info(dev, "Cannot find any crtc or sizes\n");1616 return -EAGAIN;1617 }1618 1619 return 0;1620}1621 1622static int drm_fb_helper_find_sizes(struct drm_fb_helper *fb_helper,1623 struct drm_fb_helper_surface_size *sizes)1624{1625 struct drm_client_dev *client = &fb_helper->client;1626 struct drm_device *dev = fb_helper->dev;1627 struct drm_mode_config *config = &dev->mode_config;1628 int ret;1629 1630 mutex_lock(&client->modeset_mutex);1631 ret = __drm_fb_helper_find_sizes(fb_helper, sizes);1632 mutex_unlock(&client->modeset_mutex);1633 1634 if (ret)1635 return ret;1636 1637 /* Handle our overallocation */1638 sizes->surface_height *= drm_fbdev_overalloc;1639 sizes->surface_height /= 100;1640 if (sizes->surface_height > config->max_height) {1641 drm_dbg_kms(dev, "Fbdev over-allocation too large; clamping height to %d\n",1642 config->max_height);1643 sizes->surface_height = config->max_height;1644 }1645 1646 return 0;1647}1648 1649/*1650 * Allocates the backing storage and sets up the fbdev info structure through1651 * the ->fb_probe callback.1652 */1653static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)1654{1655 struct drm_client_dev *client = &fb_helper->client;1656 struct drm_device *dev = fb_helper->dev;1657 struct drm_fb_helper_surface_size sizes;1658 int ret;1659 1660 ret = drm_fb_helper_find_sizes(fb_helper, &sizes);1661 if (ret) {1662 /* First time: disable all crtc's.. */1663 if (!fb_helper->deferred_setup)1664 drm_client_modeset_commit(client);1665 return ret;1666 }1667 1668 /* push down into drivers */1669 ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);1670 if (ret < 0)1671 return ret;1672 1673 strcpy(fb_helper->fb->comm, "[fbcon]");1674 1675 /* Set the fb info for vgaswitcheroo clients. Does nothing otherwise. */1676 if (dev_is_pci(dev->dev))1677 vga_switcheroo_client_fb_set(to_pci_dev(dev->dev), fb_helper->info);1678 1679 return 0;1680}1681 1682static void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,1683 bool is_color_indexed)1684{1685 info->fix.type = FB_TYPE_PACKED_PIXELS;1686 info->fix.visual = is_color_indexed ? FB_VISUAL_PSEUDOCOLOR1687 : FB_VISUAL_TRUECOLOR;1688 info->fix.mmio_start = 0;1689 info->fix.mmio_len = 0;1690 info->fix.type_aux = 0;1691 info->fix.xpanstep = 1; /* doing it in hw */1692 info->fix.ypanstep = 1; /* doing it in hw */1693 info->fix.ywrapstep = 0;1694 info->fix.accel = FB_ACCEL_NONE;1695 1696 info->fix.line_length = pitch;1697}1698 1699static void drm_fb_helper_fill_var(struct fb_info *info,1700 struct drm_fb_helper *fb_helper,1701 uint32_t fb_width, uint32_t fb_height)1702{1703 struct drm_framebuffer *fb = fb_helper->fb;1704 const struct drm_format_info *format = fb->format;1705 1706 switch (format->format) {1707 case DRM_FORMAT_C1:1708 case DRM_FORMAT_C2:1709 case DRM_FORMAT_C4:1710 /* supported format with sub-byte pixels */1711 break;1712 1713 default:1714 WARN_ON((drm_format_info_block_width(format, 0) > 1) ||1715 (drm_format_info_block_height(format, 0) > 1));1716 break;1717 }1718 1719 info->pseudo_palette = fb_helper->pseudo_palette;1720 info->var.xoffset = 0;1721 info->var.yoffset = 0;1722 __fill_var(&info->var, info, fb);1723 info->var.activate = FB_ACTIVATE_NOW;1724 1725 drm_fb_helper_fill_pixel_fmt(&info->var, format);1726 1727 info->var.xres = fb_width;1728 info->var.yres = fb_height;1729}1730 1731/**1732 * drm_fb_helper_fill_info - initializes fbdev information1733 * @info: fbdev instance to set up1734 * @fb_helper: fb helper instance to use as template1735 * @sizes: describes fbdev size and scanout surface size1736 *1737 * Sets up the variable and fixed fbdev metainformation from the given fb helper1738 * instance and the drm framebuffer allocated in &drm_fb_helper.fb.1739 *1740 * Drivers should call this (or their equivalent setup code) from their1741 * &drm_fb_helper_funcs.fb_probe callback after having allocated the fbdev1742 * backing storage framebuffer.1743 */1744void drm_fb_helper_fill_info(struct fb_info *info,1745 struct drm_fb_helper *fb_helper,1746 struct drm_fb_helper_surface_size *sizes)1747{1748 struct drm_framebuffer *fb = fb_helper->fb;1749 1750 drm_fb_helper_fill_fix(info, fb->pitches[0],1751 fb->format->is_color_indexed);1752 drm_fb_helper_fill_var(info, fb_helper,1753 sizes->fb_width, sizes->fb_height);1754 1755 info->par = fb_helper;1756 /*1757 * The DRM drivers fbdev emulation device name can be confusing if the1758 * driver name also has a "drm" suffix on it. Leading to names such as1759 * "simpledrmdrmfb" in /proc/fb. Unfortunately, it's an uAPI and can't1760 * be changed due user-space tools (e.g: pm-utils) matching against it.1761 */1762 snprintf(info->fix.id, sizeof(info->fix.id), "%sdrmfb",1763 fb_helper->dev->driver->name);1764 1765}1766EXPORT_SYMBOL(drm_fb_helper_fill_info);1767 1768/*1769 * This is a continuation of drm_setup_crtcs() that sets up anything related1770 * to the framebuffer. During initialization, drm_setup_crtcs() is called before1771 * the framebuffer has been allocated (fb_helper->fb and fb_helper->info).1772 * So, any setup that touches those fields needs to be done here instead of in1773 * drm_setup_crtcs().1774 */1775static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)1776{1777 struct drm_client_dev *client = &fb_helper->client;1778 struct drm_connector_list_iter conn_iter;1779 struct fb_info *info = fb_helper->info;1780 unsigned int rotation, sw_rotations = 0;1781 struct drm_connector *connector;1782 struct drm_mode_set *modeset;1783 1784 mutex_lock(&client->modeset_mutex);1785 drm_client_for_each_modeset(modeset, client) {1786 if (!modeset->num_connectors)1787 continue;1788 1789 modeset->fb = fb_helper->fb;1790 1791 if (drm_client_rotation(modeset, &rotation))1792 /* Rotating in hardware, fbcon should not rotate */1793 sw_rotations |= DRM_MODE_ROTATE_0;1794 else1795 sw_rotations |= rotation;1796 }1797 mutex_unlock(&client->modeset_mutex);1798 1799 drm_connector_list_iter_begin(fb_helper->dev, &conn_iter);1800 drm_client_for_each_connector_iter(connector, &conn_iter) {1801 1802 /* use first connected connector for the physical dimensions */1803 if (connector->status == connector_status_connected) {1804 info->var.width = connector->display_info.width_mm;1805 info->var.height = connector->display_info.height_mm;1806 break;1807 }1808 }1809 drm_connector_list_iter_end(&conn_iter);1810 1811 switch (sw_rotations) {1812 case DRM_MODE_ROTATE_0:1813 info->fbcon_rotate_hint = FB_ROTATE_UR;1814 break;1815 case DRM_MODE_ROTATE_90:1816 info->fbcon_rotate_hint = FB_ROTATE_CCW;1817 break;1818 case DRM_MODE_ROTATE_180:1819 info->fbcon_rotate_hint = FB_ROTATE_UD;1820 break;1821 case DRM_MODE_ROTATE_270:1822 info->fbcon_rotate_hint = FB_ROTATE_CW;1823 break;1824 default:1825 /*1826 * Multiple bits are set / multiple rotations requested1827 * fbcon cannot handle separate rotation settings per1828 * output, so fallback to unrotated.1829 */1830 info->fbcon_rotate_hint = FB_ROTATE_UR;1831 }1832}1833 1834/* Note: Drops fb_helper->lock before returning. */1835static int1836__drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper)1837{1838 struct drm_device *dev = fb_helper->dev;1839 struct fb_info *info;1840 unsigned int width, height;1841 int ret;1842 1843 width = dev->mode_config.max_width;1844 height = dev->mode_config.max_height;1845 1846 drm_client_modeset_probe(&fb_helper->client, width, height);1847 ret = drm_fb_helper_single_fb_probe(fb_helper);1848 if (ret < 0) {1849 if (ret == -EAGAIN) {1850 fb_helper->deferred_setup = true;1851 ret = 0;1852 }1853 mutex_unlock(&fb_helper->lock);1854 1855 return ret;1856 }1857 drm_setup_crtcs_fb(fb_helper);1858 1859 fb_helper->deferred_setup = false;1860 1861 info = fb_helper->info;1862 info->var.pixclock = 0;1863 1864 /* Need to drop locks to avoid recursive deadlock in1865 * register_framebuffer. This is ok because the only thing left to do is1866 * register the fbdev emulation instance in kernel_fb_helper_list. */1867 mutex_unlock(&fb_helper->lock);1868 1869 ret = register_framebuffer(info);1870 if (ret < 0)1871 return ret;1872 1873 drm_info(dev, "fb%d: %s frame buffer device\n",1874 info->node, info->fix.id);1875 1876 mutex_lock(&kernel_fb_helper_lock);1877 if (list_empty(&kernel_fb_helper_list))1878 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);1879 1880 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);1881 mutex_unlock(&kernel_fb_helper_lock);1882 1883 return 0;1884}1885 1886/**1887 * drm_fb_helper_initial_config - setup a sane initial connector configuration1888 * @fb_helper: fb_helper device struct1889 *1890 * Scans the CRTCs and connectors and tries to put together an initial setup.1891 * At the moment, this is a cloned configuration across all heads with1892 * a new framebuffer object as the backing store.1893 *1894 * Note that this also registers the fbdev and so allows userspace to call into1895 * the driver through the fbdev interfaces.1896 *1897 * This function will call down into the &drm_fb_helper_funcs.fb_probe callback1898 * to let the driver allocate and initialize the fbdev info structure and the1899 * drm framebuffer used to back the fbdev. drm_fb_helper_fill_info() is provided1900 * as a helper to setup simple default values for the fbdev info structure.1901 *1902 * HANG DEBUGGING:1903 *1904 * When you have fbcon support built-in or already loaded, this function will do1905 * a full modeset to setup the fbdev console. Due to locking misdesign in the1906 * VT/fbdev subsystem that entire modeset sequence has to be done while holding1907 * console_lock. Until console_unlock is called no dmesg lines will be sent out1908 * to consoles, not even serial console. This means when your driver crashes,1909 * you will see absolutely nothing else but a system stuck in this function,1910 * with no further output. Any kind of printk() you place within your own driver1911 * or in the drm core modeset code will also never show up.1912 *1913 * Standard debug practice is to run the fbcon setup without taking the1914 * console_lock as a hack, to be able to see backtraces and crashes on the1915 * serial line. This can be done by setting the fb.lockless_register_fb=1 kernel1916 * cmdline option.1917 *1918 * The other option is to just disable fbdev emulation since very likely the1919 * first modeset from userspace will crash in the same way, and is even easier1920 * to debug. This can be done by setting the drm_kms_helper.fbdev_emulation=01921 * kernel cmdline option.1922 *1923 * RETURNS:1924 * Zero if everything went ok, nonzero otherwise.1925 */1926int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper)1927{1928 int ret;1929 1930 if (!drm_fbdev_emulation)1931 return 0;1932 1933 mutex_lock(&fb_helper->lock);1934 ret = __drm_fb_helper_initial_config_and_unlock(fb_helper);1935 1936 return ret;1937}1938EXPORT_SYMBOL(drm_fb_helper_initial_config);1939 1940/**1941 * drm_fb_helper_hotplug_event - respond to a hotplug notification by1942 * probing all the outputs attached to the fb1943 * @fb_helper: driver-allocated fbdev helper, can be NULL1944 *1945 * Scan the connectors attached to the fb_helper and try to put together a1946 * setup after notification of a change in output configuration.1947 *1948 * Called at runtime, takes the mode config locks to be able to check/change the1949 * modeset configuration. Must be run from process context (which usually means1950 * either the output polling work or a work item launched from the driver's1951 * hotplug interrupt).1952 *1953 * Note that drivers may call this even before calling1954 * drm_fb_helper_initial_config but only after drm_fb_helper_init. This allows1955 * for a race-free fbcon setup and will make sure that the fbdev emulation will1956 * not miss any hotplug events.1957 *1958 * RETURNS:1959 * 0 on success and a non-zero error code otherwise.1960 */1961int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)1962{1963 int err = 0;1964 1965 if (!drm_fbdev_emulation || !fb_helper)1966 return 0;1967 1968 mutex_lock(&fb_helper->lock);1969 if (fb_helper->deferred_setup) {1970 err = __drm_fb_helper_initial_config_and_unlock(fb_helper);1971 return err;1972 }1973 1974 if (!fb_helper->fb || !drm_master_internal_acquire(fb_helper->dev)) {1975 fb_helper->delayed_hotplug = true;1976 mutex_unlock(&fb_helper->lock);1977 return err;1978 }1979 1980 drm_master_internal_release(fb_helper->dev);1981 1982 drm_dbg_kms(fb_helper->dev, "\n");1983 1984 drm_client_modeset_probe(&fb_helper->client, fb_helper->fb->width, fb_helper->fb->height);1985 drm_setup_crtcs_fb(fb_helper);1986 mutex_unlock(&fb_helper->lock);1987 1988 drm_fb_helper_set_par(fb_helper->info);1989 1990 return 0;1991}1992EXPORT_SYMBOL(drm_fb_helper_hotplug_event);1993 1994/**1995 * drm_fb_helper_lastclose - DRM driver lastclose helper for fbdev emulation1996 * @dev: DRM device1997 *1998 * This function is obsolete. Call drm_fb_helper_restore_fbdev_mode_unlocked()1999 * instead.2000 */2001void drm_fb_helper_lastclose(struct drm_device *dev)2002{2003 drm_fb_helper_restore_fbdev_mode_unlocked(dev->fb_helper);2004}2005EXPORT_SYMBOL(drm_fb_helper_lastclose);2006