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