Home | History | Annotate | Line # | Download | only in drm
drm_fb_helper.c revision 1.8.18.3
      1 /*	$NetBSD: drm_fb_helper.c,v 1.8.18.3 2020/04/13 08:04:57 martin Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2006-2009 Red Hat Inc.
      5  * Copyright (c) 2006-2008 Intel Corporation
      6  * Copyright (c) 2007 Dave Airlie <airlied (at) linux.ie>
      7  *
      8  * DRM framebuffer helper functions
      9  *
     10  * Permission to use, copy, modify, distribute, and sell this software and its
     11  * documentation for any purpose is hereby granted without fee, provided that
     12  * the above copyright notice appear in all copies and that both that copyright
     13  * notice and this permission notice appear in supporting documentation, and
     14  * that the name of the copyright holders not be used in advertising or
     15  * publicity pertaining to distribution of the software without specific,
     16  * written prior permission.  The copyright holders make no representations
     17  * about the suitability of this software for any purpose.  It is provided "as
     18  * is" without express or implied warranty.
     19  *
     20  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     21  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
     22  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
     23  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
     24  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
     25  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
     26  * OF THIS SOFTWARE.
     27  *
     28  * Authors:
     29  *      Dave Airlie <airlied (at) linux.ie>
     30  *      Jesse Barnes <jesse.barnes (at) intel.com>
     31  */
     32 #include <sys/cdefs.h>
     33 __KERNEL_RCSID(0, "$NetBSD: drm_fb_helper.c,v 1.8.18.3 2020/04/13 08:04:57 martin Exp $");
     34 
     35 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     36 
     37 #include <linux/kernel.h>
     38 #include <linux/sysrq.h>
     39 #include <linux/slab.h>
     40 #include <linux/fb.h>
     41 #include <linux/module.h>
     42 #include <linux/sysrq.h>
     43 #include <drm/drmP.h>
     44 #include <drm/drm_crtc.h>
     45 #include <drm/drm_fb_helper.h>
     46 #include <drm/drm_crtc_helper.h>
     47 #include <drm/drm_atomic.h>
     48 #include <drm/drm_atomic_helper.h>
     49 
     50 static bool drm_fbdev_emulation = true;
     51 module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
     52 MODULE_PARM_DESC(fbdev_emulation,
     53 		 "Enable legacy fbdev emulation [default=true]");
     54 
     55 #ifdef __NetBSD__		/* XXX LIST_HEAD means something else */
     56 static struct list_head kernel_fb_helper_list =
     57     LIST_HEAD_INIT(kernel_fb_helper_list);
     58 #else
     59 static LIST_HEAD(kernel_fb_helper_list);
     60 #endif
     61 
     62 /**
     63  * DOC: fbdev helpers
     64  *
     65  * The fb helper functions are useful to provide an fbdev on top of a drm kernel
     66  * mode setting driver. They can be used mostly independently from the crtc
     67  * helper functions used by many drivers to implement the kernel mode setting
     68  * interfaces.
     69  *
     70  * Initialization is done as a four-step process with drm_fb_helper_prepare(),
     71  * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
     72  * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
     73  * default behaviour can override the third step with their own code.
     74  * Teardown is done with drm_fb_helper_fini().
     75  *
     76  * At runtime drivers should restore the fbdev console by calling
     77  * drm_fb_helper_restore_fbdev_mode_unlocked() from their ->lastclose callback.
     78  * They should also notify the fb helper code from updates to the output
     79  * configuration by calling drm_fb_helper_hotplug_event(). For easier
     80  * integration with the output polling code in drm_crtc_helper.c the modeset
     81  * code provides a ->output_poll_changed callback.
     82  *
     83  * All other functions exported by the fb helper library can be used to
     84  * implement the fbdev driver interface by the driver.
     85  *
     86  * It is possible, though perhaps somewhat tricky, to implement race-free
     87  * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare()
     88  * helper must be called first to initialize the minimum required to make
     89  * hotplug detection work. Drivers also need to make sure to properly set up
     90  * the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init()
     91  * it is safe to enable interrupts and start processing hotplug events. At the
     92  * same time, drivers should initialize all modeset objects such as CRTCs,
     93  * encoders and connectors. To finish up the fbdev helper initialization, the
     94  * drm_fb_helper_init() function is called. To probe for all attached displays
     95  * and set up an initial configuration using the detected hardware, drivers
     96  * should call drm_fb_helper_single_add_all_connectors() followed by
     97  * drm_fb_helper_initial_config().
     98  */
     99 
    100 /**
    101  * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
    102  * 					       emulation helper
    103  * @fb_helper: fbdev initialized with drm_fb_helper_init
    104  *
    105  * This functions adds all the available connectors for use with the given
    106  * fb_helper. This is a separate step to allow drivers to freely assign
    107  * connectors to the fbdev, e.g. if some are reserved for special purposes or
    108  * not adequate to be used for the fbcon.
    109  *
    110  * This function is protected against concurrent connector hotadds/removals
    111  * using drm_fb_helper_add_one_connector() and
    112  * drm_fb_helper_remove_one_connector().
    113  */
    114 int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
    115 {
    116 	struct drm_device *dev = fb_helper->dev;
    117 	struct drm_connector *connector;
    118 	int i;
    119 
    120 	if (!drm_fbdev_emulation)
    121 		return 0;
    122 
    123 	mutex_lock(&dev->mode_config.mutex);
    124 	drm_for_each_connector(connector, dev) {
    125 		struct drm_fb_helper_connector *fb_helper_connector;
    126 
    127 		fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
    128 		if (!fb_helper_connector)
    129 			goto fail;
    130 
    131 		fb_helper_connector->connector = connector;
    132 		fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
    133 	}
    134 	mutex_unlock(&dev->mode_config.mutex);
    135 	return 0;
    136 fail:
    137 	for (i = 0; i < fb_helper->connector_count; i++) {
    138 		kfree(fb_helper->connector_info[i]);
    139 		fb_helper->connector_info[i] = NULL;
    140 	}
    141 	fb_helper->connector_count = 0;
    142 	mutex_unlock(&dev->mode_config.mutex);
    143 
    144 	return -ENOMEM;
    145 }
    146 EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
    147 
    148 int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector)
    149 {
    150 	struct drm_fb_helper_connector **temp;
    151 	struct drm_fb_helper_connector *fb_helper_connector;
    152 
    153 	if (!drm_fbdev_emulation)
    154 		return 0;
    155 
    156 	WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
    157 	if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) {
    158 		temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector *) * (fb_helper->connector_count + 1), GFP_KERNEL);
    159 		if (!temp)
    160 			return -ENOMEM;
    161 
    162 		fb_helper->connector_info_alloc_count = fb_helper->connector_count + 1;
    163 		fb_helper->connector_info = temp;
    164 	}
    165 
    166 
    167 	fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
    168 	if (!fb_helper_connector)
    169 		return -ENOMEM;
    170 
    171 	fb_helper_connector->connector = connector;
    172 	fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
    173 	return 0;
    174 }
    175 EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
    176 
    177 static void remove_from_modeset(struct drm_mode_set *set,
    178 		struct drm_connector *connector)
    179 {
    180 	int i, j;
    181 
    182 	for (i = 0; i < set->num_connectors; i++) {
    183 		if (set->connectors[i] == connector)
    184 			break;
    185 	}
    186 
    187 	if (i == set->num_connectors)
    188 		return;
    189 
    190 	for (j = i + 1; j < set->num_connectors; j++) {
    191 		set->connectors[j - 1] = set->connectors[j];
    192 	}
    193 	set->num_connectors--;
    194 
    195 	/*
    196 	 * TODO maybe need to makes sure we set it back to !=NULL somewhere?
    197 	 */
    198 	if (set->num_connectors == 0) {
    199 		set->fb = NULL;
    200 		drm_mode_destroy(connector->dev, set->mode);
    201 		set->mode = NULL;
    202 	}
    203 }
    204 
    205 int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
    206 				       struct drm_connector *connector)
    207 {
    208 	struct drm_fb_helper_connector *fb_helper_connector;
    209 	int i, j;
    210 
    211 	if (!drm_fbdev_emulation)
    212 		return 0;
    213 
    214 	WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
    215 
    216 	for (i = 0; i < fb_helper->connector_count; i++) {
    217 		if (fb_helper->connector_info[i]->connector == connector)
    218 			break;
    219 	}
    220 
    221 	if (i == fb_helper->connector_count)
    222 		return -EINVAL;
    223 	fb_helper_connector = fb_helper->connector_info[i];
    224 
    225 	for (j = i + 1; j < fb_helper->connector_count; j++) {
    226 		fb_helper->connector_info[j - 1] = fb_helper->connector_info[j];
    227 	}
    228 	fb_helper->connector_count--;
    229 	kfree(fb_helper_connector);
    230 
    231 	/* also cleanup dangling references to the connector: */
    232 	for (i = 0; i < fb_helper->crtc_count; i++)
    233 		remove_from_modeset(&fb_helper->crtc_info[i].mode_set, connector);
    234 
    235 	return 0;
    236 }
    237 EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
    238 
    239 static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper)
    240 {
    241 	uint16_t *r_base, *g_base, *b_base;
    242 	int i;
    243 
    244 	if (helper->funcs->gamma_get == NULL)
    245 		return;
    246 
    247 	r_base = crtc->gamma_store;
    248 	g_base = r_base + crtc->gamma_size;
    249 	b_base = g_base + crtc->gamma_size;
    250 
    251 	for (i = 0; i < crtc->gamma_size; i++)
    252 		helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i);
    253 }
    254 
    255 static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
    256 {
    257 	uint16_t *r_base, *g_base, *b_base;
    258 
    259 	if (crtc->funcs->gamma_set == NULL)
    260 		return;
    261 
    262 	r_base = crtc->gamma_store;
    263 	g_base = r_base + crtc->gamma_size;
    264 	b_base = g_base + crtc->gamma_size;
    265 
    266 	crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
    267 }
    268 
    269 /**
    270  * drm_fb_helper_debug_enter - implementation for ->fb_debug_enter
    271  * @info: fbdev registered by the helper
    272  */
    273 #ifndef __NetBSD__
    274 int drm_fb_helper_debug_enter(struct fb_info *info)
    275 {
    276 	return drm_fb_helper_debug_enter_fb(info->par);
    277 }
    278 #endif
    279 
    280 int
    281 drm_fb_helper_debug_enter_fb(struct drm_fb_helper *helper)
    282 {
    283 	const struct drm_crtc_helper_funcs *funcs;
    284 	int i;
    285 
    286 	list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
    287 		for (i = 0; i < helper->crtc_count; i++) {
    288 			struct drm_mode_set *mode_set =
    289 				&helper->crtc_info[i].mode_set;
    290 
    291 			if (!mode_set->crtc->enabled)
    292 				continue;
    293 
    294 			funcs =	mode_set->crtc->helper_private;
    295 			drm_fb_helper_save_lut_atomic(mode_set->crtc, helper);
    296 			funcs->mode_set_base_atomic(mode_set->crtc,
    297 						    mode_set->fb,
    298 						    mode_set->x,
    299 						    mode_set->y,
    300 						    ENTER_ATOMIC_MODE_SET);
    301 		}
    302 	}
    303 
    304 	return 0;
    305 }
    306 EXPORT_SYMBOL(drm_fb_helper_debug_enter);
    307 
    308 /* Find the real fb for a given fb helper CRTC */
    309 static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
    310 {
    311 	struct drm_device *dev = crtc->dev;
    312 	struct drm_crtc *c;
    313 
    314 	drm_for_each_crtc(c, dev) {
    315 		if (crtc->base.id == c->base.id)
    316 			return c->primary->fb;
    317 	}
    318 
    319 	return NULL;
    320 }
    321 
    322 /**
    323  * drm_fb_helper_debug_leave - implementation for ->fb_debug_leave
    324  * @info: fbdev registered by the helper
    325  */
    326 #ifndef __NetBSD__
    327 int drm_fb_helper_debug_leave(struct fb_info *info)
    328 {
    329 	return drm_fb_helper_debug_leave_fb(info->par);
    330 }
    331 #endif
    332 
    333 int
    334 drm_fb_helper_debug_leave_fb(struct drm_fb_helper *helper)
    335 {
    336 	struct drm_crtc *crtc;
    337 	const struct drm_crtc_helper_funcs *funcs;
    338 	struct drm_framebuffer *fb;
    339 	int i;
    340 
    341 	for (i = 0; i < helper->crtc_count; i++) {
    342 		struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
    343 		crtc = mode_set->crtc;
    344 		funcs = crtc->helper_private;
    345 		fb = drm_mode_config_fb(crtc);
    346 
    347 		if (!crtc->enabled)
    348 			continue;
    349 
    350 		if (!fb) {
    351 			DRM_ERROR("no fb to restore??\n");
    352 			continue;
    353 		}
    354 
    355 		drm_fb_helper_restore_lut_atomic(mode_set->crtc);
    356 		funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,
    357 					    crtc->y, LEAVE_ATOMIC_MODE_SET);
    358 	}
    359 
    360 	return 0;
    361 }
    362 EXPORT_SYMBOL(drm_fb_helper_debug_leave);
    363 
    364 static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper)
    365 {
    366 	struct drm_device *dev = fb_helper->dev;
    367 	struct drm_plane *plane;
    368 	struct drm_atomic_state *state;
    369 	int i, ret;
    370 	unsigned plane_mask;
    371 
    372 	state = drm_atomic_state_alloc(dev);
    373 	if (!state)
    374 		return -ENOMEM;
    375 
    376 	state->acquire_ctx = dev->mode_config.acquire_ctx;
    377 retry:
    378 	plane_mask = 0;
    379 	drm_for_each_plane(plane, dev) {
    380 		struct drm_plane_state *plane_state;
    381 
    382 		plane_state = drm_atomic_get_plane_state(state, plane);
    383 		if (IS_ERR(plane_state)) {
    384 			ret = PTR_ERR(plane_state);
    385 			goto fail;
    386 		}
    387 
    388 		plane_state->rotation = BIT(DRM_ROTATE_0);
    389 
    390 		plane->old_fb = plane->fb;
    391 		plane_mask |= 1 << drm_plane_index(plane);
    392 
    393 		/* disable non-primary: */
    394 		if (plane->type == DRM_PLANE_TYPE_PRIMARY)
    395 			continue;
    396 
    397 		ret = __drm_atomic_helper_disable_plane(plane, plane_state);
    398 		if (ret != 0)
    399 			goto fail;
    400 	}
    401 
    402 	for(i = 0; i < fb_helper->crtc_count; i++) {
    403 		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
    404 
    405 		ret = __drm_atomic_helper_set_config(mode_set, state);
    406 		if (ret != 0)
    407 			goto fail;
    408 	}
    409 
    410 	ret = drm_atomic_commit(state);
    411 
    412 fail:
    413 	drm_atomic_clean_old_fb(dev, plane_mask, ret);
    414 
    415 	if (ret == -EDEADLK)
    416 		goto backoff;
    417 
    418 	if (ret != 0)
    419 		drm_atomic_state_free(state);
    420 
    421 	return ret;
    422 
    423 backoff:
    424 	drm_atomic_state_clear(state);
    425 	drm_atomic_legacy_backoff(state);
    426 
    427 	goto retry;
    428 }
    429 
    430 static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
    431 {
    432 	struct drm_device *dev = fb_helper->dev;
    433 	struct drm_plane *plane;
    434 	int i;
    435 
    436 	drm_warn_on_modeset_not_all_locked(dev);
    437 
    438 	if (fb_helper->atomic)
    439 		return restore_fbdev_mode_atomic(fb_helper);
    440 
    441 	drm_for_each_plane(plane, dev) {
    442 		if (plane->type != DRM_PLANE_TYPE_PRIMARY)
    443 			drm_plane_force_disable(plane);
    444 
    445 		if (dev->mode_config.rotation_property) {
    446 			drm_mode_plane_set_obj_prop(plane,
    447 						    dev->mode_config.rotation_property,
    448 						    BIT(DRM_ROTATE_0));
    449 		}
    450 	}
    451 
    452 	for (i = 0; i < fb_helper->crtc_count; i++) {
    453 		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
    454 		struct drm_crtc *crtc = mode_set->crtc;
    455 		int ret;
    456 
    457 		if (crtc->funcs->cursor_set2) {
    458 			ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0);
    459 			if (ret)
    460 				return ret;
    461 		} else if (crtc->funcs->cursor_set) {
    462 			ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
    463 			if (ret)
    464 				return ret;
    465 		}
    466 
    467 		ret = drm_mode_set_config_internal(mode_set);
    468 		if (ret)
    469 			return ret;
    470 	}
    471 
    472 	return 0;
    473 }
    474 
    475 /**
    476  * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
    477  * @fb_helper: fbcon to restore
    478  *
    479  * This should be called from driver's drm ->lastclose callback
    480  * when implementing an fbcon on top of kms using this helper. This ensures that
    481  * the user isn't greeted with a black screen when e.g. X dies.
    482  *
    483  * RETURNS:
    484  * Zero if everything went ok, negative error code otherwise.
    485  */
    486 int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
    487 {
    488 	struct drm_device *dev = fb_helper->dev;
    489 	bool do_delayed;
    490 	int ret;
    491 
    492 	if (!drm_fbdev_emulation)
    493 		return -ENODEV;
    494 
    495 	drm_modeset_lock_all(dev);
    496 	ret = restore_fbdev_mode(fb_helper);
    497 
    498 	do_delayed = fb_helper->delayed_hotplug;
    499 	if (do_delayed)
    500 		fb_helper->delayed_hotplug = false;
    501 	drm_modeset_unlock_all(dev);
    502 
    503 	if (do_delayed)
    504 		drm_fb_helper_hotplug_event(fb_helper);
    505 	return ret;
    506 }
    507 EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
    508 
    509 static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
    510 {
    511 	struct drm_device *dev = fb_helper->dev;
    512 	struct drm_crtc *crtc;
    513 	int bound = 0, crtcs_bound = 0;
    514 
    515 	/* Sometimes user space wants everything disabled, so don't steal the
    516 	 * display if there's a master. */
    517 	if (dev->primary->master)
    518 		return false;
    519 
    520 	drm_for_each_crtc(crtc, dev) {
    521 		if (crtc->primary->fb)
    522 			crtcs_bound++;
    523 		if (crtc->primary->fb == fb_helper->fb)
    524 			bound++;
    525 	}
    526 
    527 	if (bound < crtcs_bound)
    528 		return false;
    529 
    530 	return true;
    531 }
    532 
    533 #ifdef CONFIG_MAGIC_SYSRQ
    534 /*
    535  * restore fbcon display for all kms driver's using this helper, used for sysrq
    536  * and panic handling.
    537  */
    538 static bool drm_fb_helper_force_kernel_mode(void)
    539 {
    540 	bool ret, error = false;
    541 	struct drm_fb_helper *helper;
    542 
    543 	if (list_empty(&kernel_fb_helper_list))
    544 		return false;
    545 
    546 	list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
    547 		struct drm_device *dev = helper->dev;
    548 
    549 		if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
    550 			continue;
    551 
    552 		drm_modeset_lock_all(dev);
    553 		ret = restore_fbdev_mode(helper);
    554 		if (ret)
    555 			error = true;
    556 		drm_modeset_unlock_all(dev);
    557 	}
    558 	return error;
    559 }
    560 
    561 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
    562 {
    563 	bool ret;
    564 	ret = drm_fb_helper_force_kernel_mode();
    565 	if (ret == true)
    566 		DRM_ERROR("Failed to restore crtc configuration\n");
    567 }
    568 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
    569 
    570 static void drm_fb_helper_sysrq(int dummy1)
    571 {
    572 	schedule_work(&drm_fb_helper_restore_work);
    573 }
    574 
    575 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
    576 	.handler = drm_fb_helper_sysrq,
    577 	.help_msg = "force-fb(V)",
    578 	.action_msg = "Restore framebuffer console",
    579 };
    580 #else
    581 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
    582 #endif
    583 
    584 #ifndef __NetBSD__		/* XXX fb info */
    585 static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
    586 {
    587 	struct drm_fb_helper *fb_helper = info->par;
    588 	struct drm_device *dev = fb_helper->dev;
    589 	struct drm_crtc *crtc;
    590 	struct drm_connector *connector;
    591 	int i, j;
    592 
    593 	/*
    594 	 * For each CRTC in this fb, turn the connectors on/off.
    595 	 */
    596 	drm_modeset_lock_all(dev);
    597 	if (!drm_fb_helper_is_bound(fb_helper)) {
    598 		drm_modeset_unlock_all(dev);
    599 		return;
    600 	}
    601 
    602 	for (i = 0; i < fb_helper->crtc_count; i++) {
    603 		crtc = fb_helper->crtc_info[i].mode_set.crtc;
    604 
    605 		if (!crtc->enabled)
    606 			continue;
    607 
    608 		/* Walk the connectors & encoders on this fb turning them on/off */
    609 		for (j = 0; j < fb_helper->connector_count; j++) {
    610 			connector = fb_helper->connector_info[j]->connector;
    611 			connector->funcs->dpms(connector, dpms_mode);
    612 			drm_object_property_set_value(&connector->base,
    613 				dev->mode_config.dpms_property, dpms_mode);
    614 		}
    615 	}
    616 	drm_modeset_unlock_all(dev);
    617 }
    618 
    619 /**
    620  * drm_fb_helper_blank - implementation for ->fb_blank
    621  * @blank: desired blanking state
    622  * @info: fbdev registered by the helper
    623  */
    624 int drm_fb_helper_blank(int blank, struct fb_info *info)
    625 {
    626 	if (oops_in_progress)
    627 		return -EBUSY;
    628 
    629 	switch (blank) {
    630 	/* Display: On; HSync: On, VSync: On */
    631 	case FB_BLANK_UNBLANK:
    632 		drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON);
    633 		break;
    634 	/* Display: Off; HSync: On, VSync: On */
    635 	case FB_BLANK_NORMAL:
    636 		drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
    637 		break;
    638 	/* Display: Off; HSync: Off, VSync: On */
    639 	case FB_BLANK_HSYNC_SUSPEND:
    640 		drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
    641 		break;
    642 	/* Display: Off; HSync: On, VSync: Off */
    643 	case FB_BLANK_VSYNC_SUSPEND:
    644 		drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND);
    645 		break;
    646 	/* Display: Off; HSync: Off, VSync: Off */
    647 	case FB_BLANK_POWERDOWN:
    648 		drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF);
    649 		break;
    650 	}
    651 	return 0;
    652 }
    653 EXPORT_SYMBOL(drm_fb_helper_blank);
    654 #endif
    655 
    656 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
    657 {
    658 	int i;
    659 
    660 	for (i = 0; i < helper->connector_count; i++)
    661 		kfree(helper->connector_info[i]);
    662 	kfree(helper->connector_info);
    663 	for (i = 0; i < helper->crtc_count; i++) {
    664 		kfree(helper->crtc_info[i].mode_set.connectors);
    665 		if (helper->crtc_info[i].mode_set.mode)
    666 			drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
    667 	}
    668 	kfree(helper->crtc_info);
    669 }
    670 
    671 /**
    672  * drm_fb_helper_prepare - setup a drm_fb_helper structure
    673  * @dev: DRM device
    674  * @helper: driver-allocated fbdev helper structure to set up
    675  * @funcs: pointer to structure of functions associate with this helper
    676  *
    677  * Sets up the bare minimum to make the framebuffer helper usable. This is
    678  * useful to implement race-free initialization of the polling helpers.
    679  */
    680 void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
    681 			   const struct drm_fb_helper_funcs *funcs)
    682 {
    683 	INIT_LIST_HEAD(&helper->kernel_fb_list);
    684 	helper->funcs = funcs;
    685 	helper->dev = dev;
    686 }
    687 EXPORT_SYMBOL(drm_fb_helper_prepare);
    688 
    689 /**
    690  * drm_fb_helper_init - initialize a drm_fb_helper structure
    691  * @dev: drm device
    692  * @fb_helper: driver-allocated fbdev helper structure to initialize
    693  * @crtc_count: maximum number of crtcs to support in this fbdev emulation
    694  * @max_conn_count: max connector count
    695  *
    696  * This allocates the structures for the fbdev helper with the given limits.
    697  * Note that this won't yet touch the hardware (through the driver interfaces)
    698  * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
    699  * to allow driver writes more control over the exact init sequence.
    700  *
    701  * Drivers must call drm_fb_helper_prepare() before calling this function.
    702  *
    703  * RETURNS:
    704  * Zero if everything went ok, nonzero otherwise.
    705  */
    706 int drm_fb_helper_init(struct drm_device *dev,
    707 		       struct drm_fb_helper *fb_helper,
    708 		       int crtc_count, int max_conn_count)
    709 {
    710 	struct drm_crtc *crtc;
    711 	int i;
    712 
    713 	if (!drm_fbdev_emulation)
    714 		return 0;
    715 
    716 	if (!max_conn_count)
    717 		return -EINVAL;
    718 
    719 	fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
    720 	if (!fb_helper->crtc_info)
    721 		return -ENOMEM;
    722 
    723 	fb_helper->crtc_count = crtc_count;
    724 	fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
    725 	if (!fb_helper->connector_info) {
    726 		kfree(fb_helper->crtc_info);
    727 		return -ENOMEM;
    728 	}
    729 	fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
    730 	fb_helper->connector_count = 0;
    731 
    732 	for (i = 0; i < crtc_count; i++) {
    733 		fb_helper->crtc_info[i].mode_set.connectors =
    734 			kcalloc(max_conn_count,
    735 				sizeof(struct drm_connector *),
    736 				GFP_KERNEL);
    737 
    738 		if (!fb_helper->crtc_info[i].mode_set.connectors)
    739 			goto out_free;
    740 		fb_helper->crtc_info[i].mode_set.num_connectors = 0;
    741 	}
    742 
    743 	i = 0;
    744 	drm_for_each_crtc(crtc, dev) {
    745 		fb_helper->crtc_info[i].mode_set.crtc = crtc;
    746 		i++;
    747 	}
    748 
    749 	fb_helper->atomic = !!drm_core_check_feature(dev, DRIVER_ATOMIC);
    750 
    751 	return 0;
    752 out_free:
    753 	drm_fb_helper_crtc_free(fb_helper);
    754 	return -ENOMEM;
    755 }
    756 EXPORT_SYMBOL(drm_fb_helper_init);
    757 
    758 #ifndef __NetBSD__		/* XXX fb info */
    759 
    760 /**
    761  * drm_fb_helper_alloc_fbi - allocate fb_info and some of its members
    762  * @fb_helper: driver-allocated fbdev helper
    763  *
    764  * A helper to alloc fb_info and the members cmap and apertures. Called
    765  * by the driver within the fb_probe fb_helper callback function.
    766  *
    767  * RETURNS:
    768  * fb_info pointer if things went okay, pointer containing error code
    769  * otherwise
    770  */
    771 struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper)
    772 {
    773 	struct device *dev = fb_helper->dev->dev;
    774 	struct fb_info *info;
    775 	int ret;
    776 
    777 	info = framebuffer_alloc(0, dev);
    778 	if (!info)
    779 		return ERR_PTR(-ENOMEM);
    780 
    781 	ret = fb_alloc_cmap(&info->cmap, 256, 0);
    782 	if (ret)
    783 		goto err_release;
    784 
    785 	info->apertures = alloc_apertures(1);
    786 	if (!info->apertures) {
    787 		ret = -ENOMEM;
    788 		goto err_free_cmap;
    789 	}
    790 
    791 	fb_helper->fbdev = info;
    792 
    793 	return info;
    794 
    795 err_free_cmap:
    796 	fb_dealloc_cmap(&info->cmap);
    797 err_release:
    798 	framebuffer_release(info);
    799 	return ERR_PTR(ret);
    800 }
    801 EXPORT_SYMBOL(drm_fb_helper_alloc_fbi);
    802 
    803 /**
    804  * drm_fb_helper_unregister_fbi - unregister fb_info framebuffer device
    805  * @fb_helper: driver-allocated fbdev helper
    806  *
    807  * A wrapper around unregister_framebuffer, to release the fb_info
    808  * framebuffer device
    809  */
    810 void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper)
    811 {
    812 	if (fb_helper && fb_helper->fbdev)
    813 		unregister_framebuffer(fb_helper->fbdev);
    814 }
    815 EXPORT_SYMBOL(drm_fb_helper_unregister_fbi);
    816 
    817 /**
    818  * drm_fb_helper_release_fbi - dealloc fb_info and its members
    819  * @fb_helper: driver-allocated fbdev helper
    820  *
    821  * A helper to free memory taken by fb_info and the members cmap and
    822  * apertures
    823  */
    824 void drm_fb_helper_release_fbi(struct drm_fb_helper *fb_helper)
    825 {
    826 	if (fb_helper) {
    827 		struct fb_info *info = fb_helper->fbdev;
    828 
    829 		if (info) {
    830 			if (info->cmap.len)
    831 				fb_dealloc_cmap(&info->cmap);
    832 			framebuffer_release(info);
    833 		}
    834 
    835 		fb_helper->fbdev = NULL;
    836 	}
    837 }
    838 EXPORT_SYMBOL(drm_fb_helper_release_fbi);
    839 
    840 #endif	/* __NetBSD__ */
    841 
    842 void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
    843 {
    844 	if (!drm_fbdev_emulation)
    845 		return;
    846 
    847 	if (!list_empty(&fb_helper->kernel_fb_list)) {
    848 		list_del(&fb_helper->kernel_fb_list);
    849 		if (list_empty(&kernel_fb_helper_list)) {
    850 #ifndef __NetBSD__		/* XXX drm sysrq */
    851 			unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
    852 #endif
    853 		}
    854 	}
    855 
    856 	drm_fb_helper_crtc_free(fb_helper);
    857 
    858 }
    859 EXPORT_SYMBOL(drm_fb_helper_fini);
    860 
    861 #ifdef __NetBSD__		/* XXX fb info */
    862 void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, int state)
    863 {
    864 }
    865 #else
    866 /**
    867  * drm_fb_helper_unlink_fbi - wrapper around unlink_framebuffer
    868  * @fb_helper: driver-allocated fbdev helper
    869  *
    870  * A wrapper around unlink_framebuffer implemented by fbdev core
    871  */
    872 void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper)
    873 {
    874 	if (fb_helper && fb_helper->fbdev)
    875 		unlink_framebuffer(fb_helper->fbdev);
    876 }
    877 EXPORT_SYMBOL(drm_fb_helper_unlink_fbi);
    878 
    879 /**
    880  * drm_fb_helper_sys_read - wrapper around fb_sys_read
    881  * @info: fb_info struct pointer
    882  * @buf: userspace buffer to read from framebuffer memory
    883  * @count: number of bytes to read from framebuffer memory
    884  * @ppos: read offset within framebuffer memory
    885  *
    886  * A wrapper around fb_sys_read implemented by fbdev core
    887  */
    888 ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
    889 			       size_t count, loff_t *ppos)
    890 {
    891 	return fb_sys_read(info, buf, count, ppos);
    892 }
    893 EXPORT_SYMBOL(drm_fb_helper_sys_read);
    894 
    895 /**
    896  * drm_fb_helper_sys_write - wrapper around fb_sys_write
    897  * @info: fb_info struct pointer
    898  * @buf: userspace buffer to write to framebuffer memory
    899  * @count: number of bytes to write to framebuffer memory
    900  * @ppos: write offset within framebuffer memory
    901  *
    902  * A wrapper around fb_sys_write implemented by fbdev core
    903  */
    904 ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf,
    905 				size_t count, loff_t *ppos)
    906 {
    907 	return fb_sys_write(info, buf, count, ppos);
    908 }
    909 EXPORT_SYMBOL(drm_fb_helper_sys_write);
    910 
    911 /**
    912  * drm_fb_helper_sys_fillrect - wrapper around sys_fillrect
    913  * @info: fbdev registered by the helper
    914  * @rect: info about rectangle to fill
    915  *
    916  * A wrapper around sys_fillrect implemented by fbdev core
    917  */
    918 void drm_fb_helper_sys_fillrect(struct fb_info *info,
    919 				const struct fb_fillrect *rect)
    920 {
    921 	sys_fillrect(info, rect);
    922 }
    923 EXPORT_SYMBOL(drm_fb_helper_sys_fillrect);
    924 
    925 /**
    926  * drm_fb_helper_sys_copyarea - wrapper around sys_copyarea
    927  * @info: fbdev registered by the helper
    928  * @area: info about area to copy
    929  *
    930  * A wrapper around sys_copyarea implemented by fbdev core
    931  */
    932 void drm_fb_helper_sys_copyarea(struct fb_info *info,
    933 				const struct fb_copyarea *area)
    934 {
    935 	sys_copyarea(info, area);
    936 }
    937 EXPORT_SYMBOL(drm_fb_helper_sys_copyarea);
    938 
    939 /**
    940  * drm_fb_helper_sys_imageblit - wrapper around sys_imageblit
    941  * @info: fbdev registered by the helper
    942  * @image: info about image to blit
    943  *
    944  * A wrapper around sys_imageblit implemented by fbdev core
    945  */
    946 void drm_fb_helper_sys_imageblit(struct fb_info *info,
    947 				 const struct fb_image *image)
    948 {
    949 	sys_imageblit(info, image);
    950 }
    951 EXPORT_SYMBOL(drm_fb_helper_sys_imageblit);
    952 
    953 /**
    954  * drm_fb_helper_cfb_fillrect - wrapper around cfb_fillrect
    955  * @info: fbdev registered by the helper
    956  * @rect: info about rectangle to fill
    957  *
    958  * A wrapper around cfb_imageblit implemented by fbdev core
    959  */
    960 void drm_fb_helper_cfb_fillrect(struct fb_info *info,
    961 				const struct fb_fillrect *rect)
    962 {
    963 	cfb_fillrect(info, rect);
    964 }
    965 EXPORT_SYMBOL(drm_fb_helper_cfb_fillrect);
    966 
    967 /**
    968  * drm_fb_helper_cfb_copyarea - wrapper around cfb_copyarea
    969  * @info: fbdev registered by the helper
    970  * @area: info about area to copy
    971  *
    972  * A wrapper around cfb_copyarea implemented by fbdev core
    973  */
    974 void drm_fb_helper_cfb_copyarea(struct fb_info *info,
    975 				const struct fb_copyarea *area)
    976 {
    977 	cfb_copyarea(info, area);
    978 }
    979 EXPORT_SYMBOL(drm_fb_helper_cfb_copyarea);
    980 
    981 /**
    982  * drm_fb_helper_cfb_imageblit - wrapper around cfb_imageblit
    983  * @info: fbdev registered by the helper
    984  * @image: info about image to blit
    985  *
    986  * A wrapper around cfb_imageblit implemented by fbdev core
    987  */
    988 void drm_fb_helper_cfb_imageblit(struct fb_info *info,
    989 				 const struct fb_image *image)
    990 {
    991 	cfb_imageblit(info, image);
    992 }
    993 EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit);
    994 
    995 /**
    996  * drm_fb_helper_set_suspend - wrapper around fb_set_suspend
    997  * @fb_helper: driver-allocated fbdev helper
    998  * @state: desired state, zero to resume, non-zero to suspend
    999  *
   1000  * A wrapper around fb_set_suspend implemented by fbdev core
   1001  */
   1002 void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, int state)
   1003 {
   1004 	if (fb_helper && fb_helper->fbdev)
   1005 		fb_set_suspend(fb_helper->fbdev, state);
   1006 }
   1007 EXPORT_SYMBOL(drm_fb_helper_set_suspend);
   1008 
   1009 static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
   1010 		     u16 blue, u16 regno, struct fb_info *info)
   1011 {
   1012 	struct drm_fb_helper *fb_helper = info->par;
   1013 	struct drm_framebuffer *fb = fb_helper->fb;
   1014 	int pindex;
   1015 
   1016 	if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
   1017 		u32 *palette;
   1018 		u32 value;
   1019 		/* place color in psuedopalette */
   1020 		if (regno > 16)
   1021 			return -EINVAL;
   1022 		palette = (u32 *)info->pseudo_palette;
   1023 		red >>= (16 - info->var.red.length);
   1024 		green >>= (16 - info->var.green.length);
   1025 		blue >>= (16 - info->var.blue.length);
   1026 		value = (red << info->var.red.offset) |
   1027 			(green << info->var.green.offset) |
   1028 			(blue << info->var.blue.offset);
   1029 		if (info->var.transp.length > 0) {
   1030 			u32 mask = (1 << info->var.transp.length) - 1;
   1031 			mask <<= info->var.transp.offset;
   1032 			value |= mask;
   1033 		}
   1034 		palette[regno] = value;
   1035 		return 0;
   1036 	}
   1037 
   1038 	/*
   1039 	 * The driver really shouldn't advertise pseudo/directcolor
   1040 	 * visuals if it can't deal with the palette.
   1041 	 */
   1042 	if (WARN_ON(!fb_helper->funcs->gamma_set ||
   1043 		    !fb_helper->funcs->gamma_get))
   1044 		return -EINVAL;
   1045 
   1046 	pindex = regno;
   1047 
   1048 	if (fb->bits_per_pixel == 16) {
   1049 		pindex = regno << 3;
   1050 
   1051 		if (fb->depth == 16 && regno > 63)
   1052 			return -EINVAL;
   1053 		if (fb->depth == 15 && regno > 31)
   1054 			return -EINVAL;
   1055 
   1056 		if (fb->depth == 16) {
   1057 			u16 r, g, b;
   1058 			int i;
   1059 			if (regno < 32) {
   1060 				for (i = 0; i < 8; i++)
   1061 					fb_helper->funcs->gamma_set(crtc, red,
   1062 						green, blue, pindex + i);
   1063 			}
   1064 
   1065 			fb_helper->funcs->gamma_get(crtc, &r,
   1066 						    &g, &b,
   1067 						    pindex >> 1);
   1068 
   1069 			for (i = 0; i < 4; i++)
   1070 				fb_helper->funcs->gamma_set(crtc, r,
   1071 							    green, b,
   1072 							    (pindex >> 1) + i);
   1073 		}
   1074 	}
   1075 
   1076 	if (fb->depth != 16)
   1077 		fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
   1078 	return 0;
   1079 }
   1080 
   1081 /**
   1082  * drm_fb_helper_setcmap - implementation for ->fb_setcmap
   1083  * @cmap: cmap to set
   1084  * @info: fbdev registered by the helper
   1085  */
   1086 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
   1087 {
   1088 	struct drm_fb_helper *fb_helper = info->par;
   1089 	struct drm_device *dev = fb_helper->dev;
   1090 	const struct drm_crtc_helper_funcs *crtc_funcs;
   1091 	u16 *red, *green, *blue, *transp;
   1092 	struct drm_crtc *crtc;
   1093 	int i, j, rc = 0;
   1094 	int start;
   1095 
   1096 	if (oops_in_progress)
   1097 		return -EBUSY;
   1098 
   1099 	drm_modeset_lock_all(dev);
   1100 	if (!drm_fb_helper_is_bound(fb_helper)) {
   1101 		drm_modeset_unlock_all(dev);
   1102 		return -EBUSY;
   1103 	}
   1104 
   1105 	for (i = 0; i < fb_helper->crtc_count; i++) {
   1106 		crtc = fb_helper->crtc_info[i].mode_set.crtc;
   1107 		crtc_funcs = crtc->helper_private;
   1108 
   1109 		red = cmap->red;
   1110 		green = cmap->green;
   1111 		blue = cmap->blue;
   1112 		transp = cmap->transp;
   1113 		start = cmap->start;
   1114 
   1115 		for (j = 0; j < cmap->len; j++) {
   1116 			u16 hred, hgreen, hblue, htransp = 0xffff;
   1117 
   1118 			hred = *red++;
   1119 			hgreen = *green++;
   1120 			hblue = *blue++;
   1121 
   1122 			if (transp)
   1123 				htransp = *transp++;
   1124 
   1125 			rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
   1126 			if (rc)
   1127 				goto out;
   1128 		}
   1129 		if (crtc_funcs->load_lut)
   1130 			crtc_funcs->load_lut(crtc);
   1131 	}
   1132  out:
   1133 	drm_modeset_unlock_all(dev);
   1134 	return rc;
   1135 }
   1136 EXPORT_SYMBOL(drm_fb_helper_setcmap);
   1137 
   1138 /**
   1139  * drm_fb_helper_check_var - implementation for ->fb_check_var
   1140  * @var: screeninfo to check
   1141  * @info: fbdev registered by the helper
   1142  */
   1143 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
   1144 			    struct fb_info *info)
   1145 {
   1146 	struct drm_fb_helper *fb_helper = info->par;
   1147 	struct drm_framebuffer *fb = fb_helper->fb;
   1148 	int depth;
   1149 
   1150 	if (var->pixclock != 0 || in_dbg_master())
   1151 		return -EINVAL;
   1152 
   1153 	/* Need to resize the fb object !!! */
   1154 	if (var->bits_per_pixel > fb->bits_per_pixel ||
   1155 	    var->xres > fb->width || var->yres > fb->height ||
   1156 	    var->xres_virtual > fb->width || var->yres_virtual > fb->height) {
   1157 		DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
   1158 			  "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n",
   1159 			  var->xres, var->yres, var->bits_per_pixel,
   1160 			  var->xres_virtual, var->yres_virtual,
   1161 			  fb->width, fb->height, fb->bits_per_pixel);
   1162 		return -EINVAL;
   1163 	}
   1164 
   1165 	switch (var->bits_per_pixel) {
   1166 	case 16:
   1167 		depth = (var->green.length == 6) ? 16 : 15;
   1168 		break;
   1169 	case 32:
   1170 		depth = (var->transp.length > 0) ? 32 : 24;
   1171 		break;
   1172 	default:
   1173 		depth = var->bits_per_pixel;
   1174 		break;
   1175 	}
   1176 
   1177 	switch (depth) {
   1178 	case 8:
   1179 		var->red.offset = 0;
   1180 		var->green.offset = 0;
   1181 		var->blue.offset = 0;
   1182 		var->red.length = 8;
   1183 		var->green.length = 8;
   1184 		var->blue.length = 8;
   1185 		var->transp.length = 0;
   1186 		var->transp.offset = 0;
   1187 		break;
   1188 	case 15:
   1189 		var->red.offset = 10;
   1190 		var->green.offset = 5;
   1191 		var->blue.offset = 0;
   1192 		var->red.length = 5;
   1193 		var->green.length = 5;
   1194 		var->blue.length = 5;
   1195 		var->transp.length = 1;
   1196 		var->transp.offset = 15;
   1197 		break;
   1198 	case 16:
   1199 		var->red.offset = 11;
   1200 		var->green.offset = 5;
   1201 		var->blue.offset = 0;
   1202 		var->red.length = 5;
   1203 		var->green.length = 6;
   1204 		var->blue.length = 5;
   1205 		var->transp.length = 0;
   1206 		var->transp.offset = 0;
   1207 		break;
   1208 	case 24:
   1209 		var->red.offset = 16;
   1210 		var->green.offset = 8;
   1211 		var->blue.offset = 0;
   1212 		var->red.length = 8;
   1213 		var->green.length = 8;
   1214 		var->blue.length = 8;
   1215 		var->transp.length = 0;
   1216 		var->transp.offset = 0;
   1217 		break;
   1218 	case 32:
   1219 		var->red.offset = 16;
   1220 		var->green.offset = 8;
   1221 		var->blue.offset = 0;
   1222 		var->red.length = 8;
   1223 		var->green.length = 8;
   1224 		var->blue.length = 8;
   1225 		var->transp.length = 8;
   1226 		var->transp.offset = 24;
   1227 		break;
   1228 	default:
   1229 		return -EINVAL;
   1230 	}
   1231 	return 0;
   1232 }
   1233 EXPORT_SYMBOL(drm_fb_helper_check_var);
   1234 #endif
   1235 
   1236 #ifndef __NetBSD__		/* XXX fb info */
   1237 /**
   1238  * drm_fb_helper_set_par - implementation for ->fb_set_par
   1239  * @info: fbdev registered by the helper
   1240  *
   1241  * This will let fbcon do the mode init and is called at initialization time by
   1242  * the fbdev core when registering the driver, and later on through the hotplug
   1243  * callback.
   1244  */
   1245 int drm_fb_helper_set_par(struct fb_info *info)
   1246 {
   1247 	struct drm_fb_helper *fb_helper = info->par;
   1248 	struct fb_var_screeninfo *var = &info->var;
   1249 
   1250 	if (oops_in_progress)
   1251 		return -EBUSY;
   1252 
   1253 	if (var->pixclock != 0) {
   1254 		DRM_ERROR("PIXEL CLOCK SET\n");
   1255 		return -EINVAL;
   1256 	}
   1257 
   1258 	drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
   1259 
   1260 	return 0;
   1261 }
   1262 EXPORT_SYMBOL(drm_fb_helper_set_par);
   1263 
   1264 static int pan_display_atomic(struct fb_var_screeninfo *var,
   1265 			      struct fb_info *info)
   1266 {
   1267 	struct drm_fb_helper *fb_helper = info->par;
   1268 	struct drm_device *dev = fb_helper->dev;
   1269 	struct drm_atomic_state *state;
   1270 	struct drm_plane *plane;
   1271 	int i, ret;
   1272 	unsigned plane_mask;
   1273 
   1274 	state = drm_atomic_state_alloc(dev);
   1275 	if (!state)
   1276 		return -ENOMEM;
   1277 
   1278 	state->acquire_ctx = dev->mode_config.acquire_ctx;
   1279 retry:
   1280 	plane_mask = 0;
   1281 	for(i = 0; i < fb_helper->crtc_count; i++) {
   1282 		struct drm_mode_set *mode_set;
   1283 
   1284 		mode_set = &fb_helper->crtc_info[i].mode_set;
   1285 
   1286 		mode_set->x = var->xoffset;
   1287 		mode_set->y = var->yoffset;
   1288 
   1289 		ret = __drm_atomic_helper_set_config(mode_set, state);
   1290 		if (ret != 0)
   1291 			goto fail;
   1292 
   1293 		plane = mode_set->crtc->primary;
   1294 		plane_mask |= drm_plane_index(plane);
   1295 		plane->old_fb = plane->fb;
   1296 	}
   1297 
   1298 	ret = drm_atomic_commit(state);
   1299 	if (ret != 0)
   1300 		goto fail;
   1301 
   1302 	info->var.xoffset = var->xoffset;
   1303 	info->var.yoffset = var->yoffset;
   1304 
   1305 
   1306 fail:
   1307 	drm_atomic_clean_old_fb(dev, plane_mask, ret);
   1308 
   1309 	if (ret == -EDEADLK)
   1310 		goto backoff;
   1311 
   1312 	if (ret != 0)
   1313 		drm_atomic_state_free(state);
   1314 
   1315 	return ret;
   1316 
   1317 backoff:
   1318 	drm_atomic_state_clear(state);
   1319 	drm_atomic_legacy_backoff(state);
   1320 
   1321 	goto retry;
   1322 }
   1323 
   1324 /**
   1325  * drm_fb_helper_pan_display - implementation for ->fb_pan_display
   1326  * @var: updated screen information
   1327  * @info: fbdev registered by the helper
   1328  */
   1329 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
   1330 			      struct fb_info *info)
   1331 {
   1332 	struct drm_fb_helper *fb_helper = info->par;
   1333 	struct drm_device *dev = fb_helper->dev;
   1334 	struct drm_mode_set *modeset;
   1335 	int ret = 0;
   1336 	int i;
   1337 
   1338 	if (oops_in_progress)
   1339 		return -EBUSY;
   1340 
   1341 	drm_modeset_lock_all(dev);
   1342 	if (!drm_fb_helper_is_bound(fb_helper)) {
   1343 		drm_modeset_unlock_all(dev);
   1344 		return -EBUSY;
   1345 	}
   1346 
   1347 	if (fb_helper->atomic) {
   1348 		ret = pan_display_atomic(var, info);
   1349 		goto unlock;
   1350 	}
   1351 
   1352 	for (i = 0; i < fb_helper->crtc_count; i++) {
   1353 		modeset = &fb_helper->crtc_info[i].mode_set;
   1354 
   1355 		modeset->x = var->xoffset;
   1356 		modeset->y = var->yoffset;
   1357 
   1358 		if (modeset->num_connectors) {
   1359 			ret = drm_mode_set_config_internal(modeset);
   1360 			if (!ret) {
   1361 				info->var.xoffset = var->xoffset;
   1362 				info->var.yoffset = var->yoffset;
   1363 			}
   1364 		}
   1365 	}
   1366 unlock:
   1367 	drm_modeset_unlock_all(dev);
   1368 	return ret;
   1369 }
   1370 EXPORT_SYMBOL(drm_fb_helper_pan_display);
   1371 #endif
   1372 
   1373 /*
   1374  * Allocates the backing storage and sets up the fbdev info structure through
   1375  * the ->fb_probe callback and then registers the fbdev and sets up the panic
   1376  * notifier.
   1377  */
   1378 static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
   1379 					 int preferred_bpp)
   1380 {
   1381 	int ret = 0;
   1382 	int crtc_count = 0;
   1383 	int i;
   1384 #ifndef __NetBSD__		/* XXX fb info */
   1385 	struct fb_info *info;
   1386 #endif
   1387 	struct drm_fb_helper_surface_size sizes;
   1388 	int gamma_size = 0;
   1389 
   1390 	memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
   1391 	sizes.surface_depth = 24;
   1392 	sizes.surface_bpp = 32;
   1393 	sizes.fb_width = (unsigned)-1;
   1394 	sizes.fb_height = (unsigned)-1;
   1395 
   1396 	/* if driver picks 8 or 16 by default use that
   1397 	   for both depth/bpp */
   1398 	if (preferred_bpp != sizes.surface_bpp)
   1399 		sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
   1400 
   1401 	/* first up get a count of crtcs now in use and new min/maxes width/heights */
   1402 	for (i = 0; i < fb_helper->connector_count; i++) {
   1403 		struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
   1404 		struct drm_cmdline_mode *cmdline_mode;
   1405 
   1406 		cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
   1407 
   1408 		if (cmdline_mode->bpp_specified) {
   1409 			switch (cmdline_mode->bpp) {
   1410 			case 8:
   1411 				sizes.surface_depth = sizes.surface_bpp = 8;
   1412 				break;
   1413 			case 15:
   1414 				sizes.surface_depth = 15;
   1415 				sizes.surface_bpp = 16;
   1416 				break;
   1417 			case 16:
   1418 				sizes.surface_depth = sizes.surface_bpp = 16;
   1419 				break;
   1420 			case 24:
   1421 				sizes.surface_depth = sizes.surface_bpp = 24;
   1422 				break;
   1423 			case 32:
   1424 				sizes.surface_depth = 24;
   1425 				sizes.surface_bpp = 32;
   1426 				break;
   1427 			}
   1428 			break;
   1429 		}
   1430 	}
   1431 
   1432 	crtc_count = 0;
   1433 	for (i = 0; i < fb_helper->crtc_count; i++) {
   1434 		struct drm_display_mode *desired_mode;
   1435 		struct drm_mode_set *mode_set;
   1436 		int x, y, j;
   1437 		/* in case of tile group, are we the last tile vert or horiz?
   1438 		 * If no tile group you are always the last one both vertically
   1439 		 * and horizontally
   1440 		 */
   1441 		bool lastv = true, lasth = true;
   1442 
   1443 		desired_mode = fb_helper->crtc_info[i].desired_mode;
   1444 		mode_set = &fb_helper->crtc_info[i].mode_set;
   1445 
   1446 		if (!desired_mode)
   1447 			continue;
   1448 
   1449 		crtc_count++;
   1450 
   1451 		x = fb_helper->crtc_info[i].x;
   1452 		y = fb_helper->crtc_info[i].y;
   1453 
   1454 		if (gamma_size == 0)
   1455 			gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
   1456 
   1457 		sizes.surface_width  = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width);
   1458 		sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height);
   1459 
   1460 		for (j = 0; j < mode_set->num_connectors; j++) {
   1461 			struct drm_connector *connector = mode_set->connectors[j];
   1462 			if (connector->has_tile) {
   1463 				lasth = (connector->tile_h_loc == (connector->num_h_tile - 1));
   1464 				lastv = (connector->tile_v_loc == (connector->num_v_tile - 1));
   1465 				/* cloning to multiple tiles is just crazy-talk, so: */
   1466 				break;
   1467 			}
   1468 		}
   1469 
   1470 		if (lasth)
   1471 			sizes.fb_width  = min_t(u32, desired_mode->hdisplay + x, sizes.fb_width);
   1472 		if (lastv)
   1473 			sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height);
   1474 	}
   1475 
   1476 	if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
   1477 		/* hmm everyone went away - assume VGA cable just fell out
   1478 		   and will come back later. */
   1479 		DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n");
   1480 		sizes.fb_width = sizes.surface_width = 1024;
   1481 		sizes.fb_height = sizes.surface_height = 768;
   1482 	}
   1483 
   1484 	/* push down into drivers */
   1485 	ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
   1486 	if (ret < 0)
   1487 		return ret;
   1488 
   1489 #ifndef __NetBSD__		/* XXX fb info */
   1490 	info = fb_helper->fbdev;
   1491 #endif
   1492 
   1493 	/*
   1494 	 * Set the fb pointer - usually drm_setup_crtcs does this for hotplug
   1495 	 * events, but at init time drm_setup_crtcs needs to be called before
   1496 	 * the fb is allocated (since we need to figure out the desired size of
   1497 	 * the fb before we can allocate it ...). Hence we need to fix things up
   1498 	 * here again.
   1499 	 */
   1500 	for (i = 0; i < fb_helper->crtc_count; i++)
   1501 		if (fb_helper->crtc_info[i].mode_set.num_connectors)
   1502 			fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
   1503 
   1504 #ifndef __NetBSD__		/* XXX fb info */
   1505 	info->var.pixclock = 0;
   1506 	if (register_framebuffer(info) < 0)
   1507 		return -EINVAL;
   1508 
   1509 	dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n",
   1510 			info->node, info->fix.id);
   1511 #endif
   1512 
   1513 	if (list_empty(&kernel_fb_helper_list)) {
   1514 		register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
   1515 	}
   1516 
   1517 	list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
   1518 
   1519 	return 0;
   1520 }
   1521 
   1522 #ifndef __NetBSD__		/* XXX fb info */
   1523 /**
   1524  * drm_fb_helper_fill_fix - initializes fixed fbdev information
   1525  * @info: fbdev registered by the helper
   1526  * @pitch: desired pitch
   1527  * @depth: desired depth
   1528  *
   1529  * Helper to fill in the fixed fbdev information useful for a non-accelerated
   1530  * fbdev emulations. Drivers which support acceleration methods which impose
   1531  * additional constraints need to set up their own limits.
   1532  *
   1533  * Drivers should call this (or their equivalent setup code) from their
   1534  * ->fb_probe callback.
   1535  */
   1536 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
   1537 			    uint32_t depth)
   1538 {
   1539 	info->fix.type = FB_TYPE_PACKED_PIXELS;
   1540 	info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
   1541 		FB_VISUAL_TRUECOLOR;
   1542 	info->fix.mmio_start = 0;
   1543 	info->fix.mmio_len = 0;
   1544 	info->fix.type_aux = 0;
   1545 	info->fix.xpanstep = 1; /* doing it in hw */
   1546 	info->fix.ypanstep = 1; /* doing it in hw */
   1547 	info->fix.ywrapstep = 0;
   1548 	info->fix.accel = FB_ACCEL_NONE;
   1549 
   1550 	info->fix.line_length = pitch;
   1551 	return;
   1552 }
   1553 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
   1554 
   1555 /**
   1556  * drm_fb_helper_fill_var - initalizes variable fbdev information
   1557  * @info: fbdev instance to set up
   1558  * @fb_helper: fb helper instance to use as template
   1559  * @fb_width: desired fb width
   1560  * @fb_height: desired fb height
   1561  *
   1562  * Sets up the variable fbdev metainformation from the given fb helper instance
   1563  * and the drm framebuffer allocated in fb_helper->fb.
   1564  *
   1565  * Drivers should call this (or their equivalent setup code) from their
   1566  * ->fb_probe callback after having allocated the fbdev backing
   1567  * storage framebuffer.
   1568  */
   1569 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
   1570 			    uint32_t fb_width, uint32_t fb_height)
   1571 {
   1572 	struct drm_framebuffer *fb = fb_helper->fb;
   1573 	info->pseudo_palette = fb_helper->pseudo_palette;
   1574 	info->var.xres_virtual = fb->width;
   1575 	info->var.yres_virtual = fb->height;
   1576 	info->var.bits_per_pixel = fb->bits_per_pixel;
   1577 	info->var.accel_flags = FB_ACCELF_TEXT;
   1578 	info->var.xoffset = 0;
   1579 	info->var.yoffset = 0;
   1580 	info->var.activate = FB_ACTIVATE_NOW;
   1581 	info->var.height = -1;
   1582 	info->var.width = -1;
   1583 
   1584 	switch (fb->depth) {
   1585 	case 8:
   1586 		info->var.red.offset = 0;
   1587 		info->var.green.offset = 0;
   1588 		info->var.blue.offset = 0;
   1589 		info->var.red.length = 8; /* 8bit DAC */
   1590 		info->var.green.length = 8;
   1591 		info->var.blue.length = 8;
   1592 		info->var.transp.offset = 0;
   1593 		info->var.transp.length = 0;
   1594 		break;
   1595 	case 15:
   1596 		info->var.red.offset = 10;
   1597 		info->var.green.offset = 5;
   1598 		info->var.blue.offset = 0;
   1599 		info->var.red.length = 5;
   1600 		info->var.green.length = 5;
   1601 		info->var.blue.length = 5;
   1602 		info->var.transp.offset = 15;
   1603 		info->var.transp.length = 1;
   1604 		break;
   1605 	case 16:
   1606 		info->var.red.offset = 11;
   1607 		info->var.green.offset = 5;
   1608 		info->var.blue.offset = 0;
   1609 		info->var.red.length = 5;
   1610 		info->var.green.length = 6;
   1611 		info->var.blue.length = 5;
   1612 		info->var.transp.offset = 0;
   1613 		break;
   1614 	case 24:
   1615 		info->var.red.offset = 16;
   1616 		info->var.green.offset = 8;
   1617 		info->var.blue.offset = 0;
   1618 		info->var.red.length = 8;
   1619 		info->var.green.length = 8;
   1620 		info->var.blue.length = 8;
   1621 		info->var.transp.offset = 0;
   1622 		info->var.transp.length = 0;
   1623 		break;
   1624 	case 32:
   1625 		info->var.red.offset = 16;
   1626 		info->var.green.offset = 8;
   1627 		info->var.blue.offset = 0;
   1628 		info->var.red.length = 8;
   1629 		info->var.green.length = 8;
   1630 		info->var.blue.length = 8;
   1631 		info->var.transp.offset = 24;
   1632 		info->var.transp.length = 8;
   1633 		break;
   1634 	default:
   1635 		break;
   1636 	}
   1637 
   1638 	info->var.xres = fb_width;
   1639 	info->var.yres = fb_height;
   1640 }
   1641 EXPORT_SYMBOL(drm_fb_helper_fill_var);
   1642 #endif
   1643 
   1644 static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
   1645 					       uint32_t maxX,
   1646 					       uint32_t maxY)
   1647 {
   1648 	struct drm_connector *connector;
   1649 	int count = 0;
   1650 	int i;
   1651 
   1652 	for (i = 0; i < fb_helper->connector_count; i++) {
   1653 		connector = fb_helper->connector_info[i]->connector;
   1654 		count += connector->funcs->fill_modes(connector, maxX, maxY);
   1655 	}
   1656 
   1657 	return count;
   1658 }
   1659 
   1660 struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
   1661 {
   1662 	struct drm_display_mode *mode;
   1663 
   1664 	list_for_each_entry(mode, &fb_connector->connector->modes, head) {
   1665 		if (mode->hdisplay > width ||
   1666 		    mode->vdisplay > height)
   1667 			continue;
   1668 		if (mode->type & DRM_MODE_TYPE_PREFERRED)
   1669 			return mode;
   1670 	}
   1671 	return NULL;
   1672 }
   1673 EXPORT_SYMBOL(drm_has_preferred_mode);
   1674 
   1675 static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
   1676 {
   1677 	return fb_connector->connector->cmdline_mode.specified;
   1678 }
   1679 
   1680 struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
   1681 						      int width, int height)
   1682 {
   1683 	struct drm_cmdline_mode *cmdline_mode;
   1684 	struct drm_display_mode *mode;
   1685 	bool prefer_non_interlace;
   1686 
   1687 	cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
   1688 	if (cmdline_mode->specified == false)
   1689 		return NULL;
   1690 
   1691 	/* attempt to find a matching mode in the list of modes
   1692 	 *  we have gotten so far, if not add a CVT mode that conforms
   1693 	 */
   1694 	if (cmdline_mode->rb || cmdline_mode->margins)
   1695 		goto create_mode;
   1696 
   1697 	prefer_non_interlace = !cmdline_mode->interlace;
   1698 again:
   1699 	list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
   1700 		/* check width/height */
   1701 		if (mode->hdisplay != cmdline_mode->xres ||
   1702 		    mode->vdisplay != cmdline_mode->yres)
   1703 			continue;
   1704 
   1705 		if (cmdline_mode->refresh_specified) {
   1706 			if (mode->vrefresh != cmdline_mode->refresh)
   1707 				continue;
   1708 		}
   1709 
   1710 		if (cmdline_mode->interlace) {
   1711 			if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
   1712 				continue;
   1713 		} else if (prefer_non_interlace) {
   1714 			if (mode->flags & DRM_MODE_FLAG_INTERLACE)
   1715 				continue;
   1716 		}
   1717 		return mode;
   1718 	}
   1719 
   1720 	if (prefer_non_interlace) {
   1721 		prefer_non_interlace = false;
   1722 		goto again;
   1723 	}
   1724 
   1725 create_mode:
   1726 	mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
   1727 						 cmdline_mode);
   1728 	list_add(&mode->head, &fb_helper_conn->connector->modes);
   1729 	return mode;
   1730 }
   1731 EXPORT_SYMBOL(drm_pick_cmdline_mode);
   1732 
   1733 static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
   1734 {
   1735 	bool enable;
   1736 
   1737 	if (strict)
   1738 		enable = connector->status == connector_status_connected;
   1739 	else
   1740 		enable = connector->status != connector_status_disconnected;
   1741 
   1742 	return enable;
   1743 }
   1744 
   1745 static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
   1746 				  bool *enabled)
   1747 {
   1748 	bool any_enabled = false;
   1749 	struct drm_connector *connector;
   1750 	int i = 0;
   1751 
   1752 	for (i = 0; i < fb_helper->connector_count; i++) {
   1753 		connector = fb_helper->connector_info[i]->connector;
   1754 		enabled[i] = drm_connector_enabled(connector, true);
   1755 		DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
   1756 			  enabled[i] ? "yes" : "no");
   1757 		any_enabled |= enabled[i];
   1758 	}
   1759 
   1760 	if (any_enabled)
   1761 		return;
   1762 
   1763 	for (i = 0; i < fb_helper->connector_count; i++) {
   1764 		connector = fb_helper->connector_info[i]->connector;
   1765 		enabled[i] = drm_connector_enabled(connector, false);
   1766 	}
   1767 }
   1768 
   1769 static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
   1770 			      struct drm_display_mode **modes,
   1771 			      struct drm_fb_offset *offsets,
   1772 			      bool *enabled, int width, int height)
   1773 {
   1774 	int count, i, j;
   1775 	bool can_clone = false;
   1776 	struct drm_fb_helper_connector *fb_helper_conn;
   1777 	struct drm_display_mode *dmt_mode, *mode;
   1778 
   1779 	/* only contemplate cloning in the single crtc case */
   1780 	if (fb_helper->crtc_count > 1)
   1781 		return false;
   1782 
   1783 	count = 0;
   1784 	for (i = 0; i < fb_helper->connector_count; i++) {
   1785 		if (enabled[i])
   1786 			count++;
   1787 	}
   1788 
   1789 	/* only contemplate cloning if more than one connector is enabled */
   1790 	if (count <= 1)
   1791 		return false;
   1792 
   1793 	/* check the command line or if nothing common pick 1024x768 */
   1794 	can_clone = true;
   1795 	for (i = 0; i < fb_helper->connector_count; i++) {
   1796 		if (!enabled[i])
   1797 			continue;
   1798 		fb_helper_conn = fb_helper->connector_info[i];
   1799 		modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
   1800 		if (!modes[i]) {
   1801 			can_clone = false;
   1802 			break;
   1803 		}
   1804 		for (j = 0; j < i; j++) {
   1805 			if (!enabled[j])
   1806 				continue;
   1807 			if (!drm_mode_equal(modes[j], modes[i]))
   1808 				can_clone = false;
   1809 		}
   1810 	}
   1811 
   1812 	if (can_clone) {
   1813 		DRM_DEBUG_KMS("can clone using command line\n");
   1814 		return true;
   1815 	}
   1816 
   1817 	/* try and find a 1024x768 mode on each connector */
   1818 	can_clone = true;
   1819 	dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
   1820 
   1821 	for (i = 0; i < fb_helper->connector_count; i++) {
   1822 
   1823 		if (!enabled[i])
   1824 			continue;
   1825 
   1826 		fb_helper_conn = fb_helper->connector_info[i];
   1827 		list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
   1828 			if (drm_mode_equal(mode, dmt_mode))
   1829 				modes[i] = mode;
   1830 		}
   1831 		if (!modes[i])
   1832 			can_clone = false;
   1833 	}
   1834 
   1835 	if (can_clone) {
   1836 		DRM_DEBUG_KMS("can clone using 1024x768\n");
   1837 		return true;
   1838 	}
   1839 	DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
   1840 	return false;
   1841 }
   1842 
   1843 static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper,
   1844 				struct drm_display_mode **modes,
   1845 				struct drm_fb_offset *offsets,
   1846 				int idx,
   1847 				int h_idx, int v_idx)
   1848 {
   1849 	struct drm_fb_helper_connector *fb_helper_conn;
   1850 	int i;
   1851 	int hoffset = 0, voffset = 0;
   1852 
   1853 	for (i = 0; i < fb_helper->connector_count; i++) {
   1854 		fb_helper_conn = fb_helper->connector_info[i];
   1855 		if (!fb_helper_conn->connector->has_tile)
   1856 			continue;
   1857 
   1858 		if (!modes[i] && (h_idx || v_idx)) {
   1859 			DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
   1860 				      fb_helper_conn->connector->base.id);
   1861 			continue;
   1862 		}
   1863 		if (fb_helper_conn->connector->tile_h_loc < h_idx)
   1864 			hoffset += modes[i]->hdisplay;
   1865 
   1866 		if (fb_helper_conn->connector->tile_v_loc < v_idx)
   1867 			voffset += modes[i]->vdisplay;
   1868 	}
   1869 	offsets[idx].x = hoffset;
   1870 	offsets[idx].y = voffset;
   1871 	DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
   1872 	return 0;
   1873 }
   1874 
   1875 static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
   1876 				 struct drm_display_mode **modes,
   1877 				 struct drm_fb_offset *offsets,
   1878 				 bool *enabled, int width, int height)
   1879 {
   1880 	struct drm_fb_helper_connector *fb_helper_conn;
   1881 	int i;
   1882 	uint64_t conn_configured = 0, mask;
   1883 	int tile_pass = 0;
   1884 	mask = (1 << fb_helper->connector_count) - 1;
   1885 retry:
   1886 	for (i = 0; i < fb_helper->connector_count; i++) {
   1887 		fb_helper_conn = fb_helper->connector_info[i];
   1888 
   1889 		if (conn_configured & (1 << i))
   1890 			continue;
   1891 
   1892 		if (enabled[i] == false) {
   1893 			conn_configured |= (1 << i);
   1894 			continue;
   1895 		}
   1896 
   1897 		/* first pass over all the untiled connectors */
   1898 		if (tile_pass == 0 && fb_helper_conn->connector->has_tile)
   1899 			continue;
   1900 
   1901 		if (tile_pass == 1) {
   1902 			if (fb_helper_conn->connector->tile_h_loc != 0 ||
   1903 			    fb_helper_conn->connector->tile_v_loc != 0)
   1904 				continue;
   1905 
   1906 		} else {
   1907 			if (fb_helper_conn->connector->tile_h_loc != tile_pass -1 &&
   1908 			    fb_helper_conn->connector->tile_v_loc != tile_pass - 1)
   1909 			/* if this tile_pass doesn't cover any of the tiles - keep going */
   1910 				continue;
   1911 
   1912 			/* find the tile offsets for this pass - need
   1913 			   to find all tiles left and above */
   1914 			drm_get_tile_offsets(fb_helper, modes, offsets,
   1915 					     i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc);
   1916 		}
   1917 		DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
   1918 			      fb_helper_conn->connector->base.id);
   1919 
   1920 		/* got for command line mode first */
   1921 		modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
   1922 		if (!modes[i]) {
   1923 			DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
   1924 				      fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0);
   1925 			modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
   1926 		}
   1927 		/* No preferred modes, pick one off the list */
   1928 		if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
   1929 			list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
   1930 				break;
   1931 		}
   1932 		DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
   1933 			  "none");
   1934 		conn_configured |= (1 << i);
   1935 	}
   1936 
   1937 	if ((conn_configured & mask) != mask) {
   1938 		tile_pass++;
   1939 		goto retry;
   1940 	}
   1941 	return true;
   1942 }
   1943 
   1944 static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
   1945 			  struct drm_fb_helper_crtc **best_crtcs,
   1946 			  struct drm_display_mode **modes,
   1947 			  int n, int width, int height)
   1948 {
   1949 	int c, o;
   1950 	struct drm_connector *connector;
   1951 	const struct drm_connector_helper_funcs *connector_funcs;
   1952 	struct drm_encoder *encoder;
   1953 	int my_score, best_score, score;
   1954 	struct drm_fb_helper_crtc **crtcs, *crtc;
   1955 	struct drm_fb_helper_connector *fb_helper_conn;
   1956 
   1957 	if (n == fb_helper->connector_count)
   1958 		return 0;
   1959 
   1960 	fb_helper_conn = fb_helper->connector_info[n];
   1961 	connector = fb_helper_conn->connector;
   1962 
   1963 	best_crtcs[n] = NULL;
   1964 	best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
   1965 	if (modes[n] == NULL)
   1966 		return best_score;
   1967 
   1968 	crtcs = kzalloc(fb_helper->connector_count *
   1969 			sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
   1970 	if (!crtcs)
   1971 		return best_score;
   1972 
   1973 	my_score = 1;
   1974 	if (connector->status == connector_status_connected)
   1975 		my_score++;
   1976 	if (drm_has_cmdline_mode(fb_helper_conn))
   1977 		my_score++;
   1978 	if (drm_has_preferred_mode(fb_helper_conn, width, height))
   1979 		my_score++;
   1980 
   1981 	connector_funcs = connector->helper_private;
   1982 	encoder = connector_funcs->best_encoder(connector);
   1983 	if (!encoder)
   1984 		goto out;
   1985 
   1986 	/* select a crtc for this connector and then attempt to configure
   1987 	   remaining connectors */
   1988 	for (c = 0; c < fb_helper->crtc_count; c++) {
   1989 		crtc = &fb_helper->crtc_info[c];
   1990 
   1991 		if ((encoder->possible_crtcs & (1 << c)) == 0)
   1992 			continue;
   1993 
   1994 		for (o = 0; o < n; o++)
   1995 			if (best_crtcs[o] == crtc)
   1996 				break;
   1997 
   1998 		if (o < n) {
   1999 			/* ignore cloning unless only a single crtc */
   2000 			if (fb_helper->crtc_count > 1)
   2001 				continue;
   2002 
   2003 			if (!drm_mode_equal(modes[o], modes[n]))
   2004 				continue;
   2005 		}
   2006 
   2007 		crtcs[n] = crtc;
   2008 		memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
   2009 		score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
   2010 						  width, height);
   2011 		if (score > best_score) {
   2012 			best_score = score;
   2013 			memcpy(best_crtcs, crtcs,
   2014 			       fb_helper->connector_count *
   2015 			       sizeof(struct drm_fb_helper_crtc *));
   2016 		}
   2017 	}
   2018 out:
   2019 	kfree(crtcs);
   2020 	return best_score;
   2021 }
   2022 
   2023 static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
   2024 {
   2025 	struct drm_device *dev = fb_helper->dev;
   2026 	struct drm_fb_helper_crtc **crtcs;
   2027 	struct drm_display_mode **modes;
   2028 	struct drm_fb_offset *offsets;
   2029 	struct drm_mode_set *modeset;
   2030 	bool *enabled;
   2031 	int width, height;
   2032 	int i;
   2033 
   2034 	DRM_DEBUG_KMS("\n");
   2035 
   2036 	width = dev->mode_config.max_width;
   2037 	height = dev->mode_config.max_height;
   2038 
   2039 	crtcs = kcalloc(dev->mode_config.num_connector,
   2040 			sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
   2041 	modes = kcalloc(dev->mode_config.num_connector,
   2042 			sizeof(struct drm_display_mode *), GFP_KERNEL);
   2043 	offsets = kcalloc(dev->mode_config.num_connector,
   2044 			  sizeof(struct drm_fb_offset), GFP_KERNEL);
   2045 	enabled = kcalloc(dev->mode_config.num_connector,
   2046 			  sizeof(bool), GFP_KERNEL);
   2047 	if (!crtcs || !modes || !enabled || !offsets) {
   2048 		DRM_ERROR("Memory allocation failed\n");
   2049 		goto out;
   2050 	}
   2051 
   2052 
   2053 	drm_enable_connectors(fb_helper, enabled);
   2054 
   2055 	if (!(fb_helper->funcs->initial_config &&
   2056 	      fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
   2057 					       offsets,
   2058 					       enabled, width, height))) {
   2059 		memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0]));
   2060 		memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0]));
   2061 		memset(offsets, 0, dev->mode_config.num_connector*sizeof(offsets[0]));
   2062 
   2063 		if (!drm_target_cloned(fb_helper, modes, offsets,
   2064 				       enabled, width, height) &&
   2065 		    !drm_target_preferred(fb_helper, modes, offsets,
   2066 					  enabled, width, height))
   2067 			DRM_ERROR("Unable to find initial modes\n");
   2068 
   2069 		DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
   2070 			      width, height);
   2071 
   2072 		drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
   2073 	}
   2074 
   2075 	/* need to set the modesets up here for use later */
   2076 	/* fill out the connector<->crtc mappings into the modesets */
   2077 	for (i = 0; i < fb_helper->crtc_count; i++) {
   2078 		modeset = &fb_helper->crtc_info[i].mode_set;
   2079 		modeset->num_connectors = 0;
   2080 		modeset->fb = NULL;
   2081 	}
   2082 
   2083 	for (i = 0; i < fb_helper->connector_count; i++) {
   2084 		struct drm_display_mode *mode = modes[i];
   2085 		struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
   2086 		struct drm_fb_offset *offset = &offsets[i];
   2087 
   2088 		if (mode && fb_crtc) {
   2089 			modeset = &fb_crtc->mode_set;
   2090 			DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
   2091 				      mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y);
   2092 			fb_crtc->desired_mode = mode;
   2093 			fb_crtc->x = offset->x;
   2094 			fb_crtc->y = offset->y;
   2095 			if (modeset->mode)
   2096 				drm_mode_destroy(dev, modeset->mode);
   2097 			modeset->mode = drm_mode_duplicate(dev,
   2098 							   fb_crtc->desired_mode);
   2099 			modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
   2100 			modeset->fb = fb_helper->fb;
   2101 			modeset->x = offset->x;
   2102 			modeset->y = offset->y;
   2103 		}
   2104 	}
   2105 
   2106 	/* Clear out any old modes if there are no more connected outputs. */
   2107 	for (i = 0; i < fb_helper->crtc_count; i++) {
   2108 		modeset = &fb_helper->crtc_info[i].mode_set;
   2109 		if (modeset->num_connectors == 0) {
   2110 			BUG_ON(modeset->fb);
   2111 			if (modeset->mode)
   2112 				drm_mode_destroy(dev, modeset->mode);
   2113 			modeset->mode = NULL;
   2114 			fb_helper->crtc_info[i].desired_mode = NULL;
   2115 		}
   2116 	}
   2117 out:
   2118 	kfree(crtcs);
   2119 	kfree(modes);
   2120 	kfree(offsets);
   2121 	kfree(enabled);
   2122 }
   2123 
   2124 /**
   2125  * drm_fb_helper_initial_config - setup a sane initial connector configuration
   2126  * @fb_helper: fb_helper device struct
   2127  * @bpp_sel: bpp value to use for the framebuffer configuration
   2128  *
   2129  * Scans the CRTCs and connectors and tries to put together an initial setup.
   2130  * At the moment, this is a cloned configuration across all heads with
   2131  * a new framebuffer object as the backing store.
   2132  *
   2133  * Note that this also registers the fbdev and so allows userspace to call into
   2134  * the driver through the fbdev interfaces.
   2135  *
   2136  * This function will call down into the ->fb_probe callback to let
   2137  * the driver allocate and initialize the fbdev info structure and the drm
   2138  * framebuffer used to back the fbdev. drm_fb_helper_fill_var() and
   2139  * drm_fb_helper_fill_fix() are provided as helpers to setup simple default
   2140  * values for the fbdev info structure.
   2141  *
   2142  * RETURNS:
   2143  * Zero if everything went ok, nonzero otherwise.
   2144  */
   2145 int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
   2146 {
   2147 	struct drm_device *dev = fb_helper->dev;
   2148 	int count = 0;
   2149 
   2150 	if (!drm_fbdev_emulation)
   2151 		return 0;
   2152 
   2153 	mutex_lock(&dev->mode_config.mutex);
   2154 	count = drm_fb_helper_probe_connector_modes(fb_helper,
   2155 						    dev->mode_config.max_width,
   2156 						    dev->mode_config.max_height);
   2157 	mutex_unlock(&dev->mode_config.mutex);
   2158 	/*
   2159 	 * we shouldn't end up with no modes here.
   2160 	 */
   2161 	if (count == 0)
   2162 		dev_info(fb_helper->dev->dev, "No connectors reported connected with modes\n");
   2163 
   2164 	drm_setup_crtcs(fb_helper);
   2165 
   2166 	return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
   2167 }
   2168 EXPORT_SYMBOL(drm_fb_helper_initial_config);
   2169 
   2170 /**
   2171  * drm_fb_helper_hotplug_event - respond to a hotplug notification by
   2172  *                               probing all the outputs attached to the fb
   2173  * @fb_helper: the drm_fb_helper
   2174  *
   2175  * Scan the connectors attached to the fb_helper and try to put together a
   2176  * setup after *notification of a change in output configuration.
   2177  *
   2178  * Called at runtime, takes the mode config locks to be able to check/change the
   2179  * modeset configuration. Must be run from process context (which usually means
   2180  * either the output polling work or a work item launched from the driver's
   2181  * hotplug interrupt).
   2182  *
   2183  * Note that drivers may call this even before calling
   2184  * drm_fb_helper_initial_config but only aftert drm_fb_helper_init. This allows
   2185  * for a race-free fbcon setup and will make sure that the fbdev emulation will
   2186  * not miss any hotplug events.
   2187  *
   2188  * RETURNS:
   2189  * 0 on success and a non-zero error code otherwise.
   2190  */
   2191 int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
   2192 {
   2193 	struct drm_device *dev = fb_helper->dev;
   2194 	u32 max_width, max_height;
   2195 
   2196 	if (!drm_fbdev_emulation)
   2197 		return 0;
   2198 
   2199 	mutex_lock(&fb_helper->dev->mode_config.mutex);
   2200 	if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
   2201 		fb_helper->delayed_hotplug = true;
   2202 		mutex_unlock(&fb_helper->dev->mode_config.mutex);
   2203 		return 0;
   2204 	}
   2205 	DRM_DEBUG_KMS("\n");
   2206 
   2207 	max_width = fb_helper->fb->width;
   2208 	max_height = fb_helper->fb->height;
   2209 
   2210 	drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height);
   2211 	mutex_unlock(&fb_helper->dev->mode_config.mutex);
   2212 
   2213 	drm_modeset_lock_all(dev);
   2214 	drm_setup_crtcs(fb_helper);
   2215 	drm_modeset_unlock_all(dev);
   2216 #ifdef __NetBSD__
   2217 	drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
   2218 #else
   2219 	drm_fb_helper_set_par(fb_helper->fbdev);
   2220 #endif
   2221 
   2222 	return 0;
   2223 }
   2224 EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
   2225 
   2226 /* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
   2227  * but the module doesn't depend on any fb console symbols.  At least
   2228  * attempt to load fbcon to avoid leaving the system without a usable console.
   2229  */
   2230 #if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT)
   2231 static int __init drm_fb_helper_modinit(void)
   2232 {
   2233 	const char *name = "fbcon";
   2234 	struct module *fbcon;
   2235 
   2236 	mutex_lock(&module_mutex);
   2237 	fbcon = find_module(name);
   2238 	mutex_unlock(&module_mutex);
   2239 
   2240 	if (!fbcon)
   2241 		request_module_nowait(name);
   2242 	return 0;
   2243 }
   2244 
   2245 module_init(drm_fb_helper_modinit);
   2246 #endif
   2247