brintos

brintos / linux-shallow public Read only

0
0
Text · 57.5 KiB · 29c53f9 Raw
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