xref: /gfx-drm/usr/src/uts/common/io/drm/drm_fb_helper.c (revision 47dc10d7)
1 /*
2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 /*
6  * Copyright (c) 2006-2009 Red Hat Inc.
7  * Copyright (c) 2006-2008, 2013, Intel Corporation
8  * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
9  *
10  * DRM framebuffer helper functions
11  *
12  * Permission to use, copy, modify, distribute, and sell this software and its
13  * documentation for any purpose is hereby granted without fee, provided that
14  * the above copyright notice appear in all copies and that both that copyright
15  * notice and this permission notice appear in supporting documentation, and
16  * that the name of the copyright holders not be used in advertising or
17  * publicity pertaining to distribution of the software without specific,
18  * written prior permission.  The copyright holders make no representations
19  * about the suitability of this software for any purpose.  It is provided "as
20  * is" without express or implied warranty.
21  *
22  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
23  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
24  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
25  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
26  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
27  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
28  * OF THIS SOFTWARE.
29  *
30  * Authors:
31  *      Dave Airlie <airlied@linux.ie>
32  *      Jesse Barnes <jesse.barnes@intel.com>
33  */
34 #include "drmP.h"
35 #include "drm_crtc.h"
36 #include "drm_fb_helper.h"
37 #include "drm_crtc_helper.h"
38 
39 
40 struct list_head kernel_fb_helper_list;
41 /**
42  * DOC: fbdev helpers
43  *
44  * The fb helper functions are useful to provide an fbdev on top of a drm kernel
45  * mode setting driver. They can be used mostly independantely from the crtc
46  * helper functions used by many drivers to implement the kernel mode setting
47  * interfaces.
48  *
49  * Initialization is done as a three-step process with drm_fb_helper_init(),
50  * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
51  * Drivers with fancier requirements than the default beheviour can override the
52  * second step with their own code.  Teardown is done with drm_fb_helper_fini().
53  *
54  * At runtime drivers should restore the fbdev console by calling
55  * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
56  * should also notify the fb helper code from updates to the output
57  * configuration by calling drm_fb_helper_hotplug_event(). For easier
58  * integration with the output polling code in drm_crtc_helper.c the modeset
59  * code proves a ->output_poll_changed callback.
60  *
61  * All other functions exported by the fb helper library can be used to
62  * implement the fbdev driver interface by the driver.
63  */
64 
65 /* simple single crtc case helper function
66  * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
67  * 					       emulation helper
68  * @fb_helper: fbdev initialized with drm_fb_helper_init
69  *
70  * This functions adds all the available connectors for use with the given
71  * fb_helper. This is a separate step to allow drivers to freely assign
72  * connectors to the fbdev, e.g. if some are reserved for special purposes or
73  * not adequate to be used for the fbcon.
74  *
75  * Since this is part of the initial setup before the fbdev is published, no
76  * locking is required.
77  */
drm_fb_helper_single_add_all_connectors(struct drm_fb_helper * fb_helper)78 int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
79 {
80 	struct drm_device *dev = fb_helper->dev;
81 	struct drm_connector *connector;
82 	int i;
83 
84 	list_for_each_entry(connector, struct drm_connector, &dev->mode_config.connector_list, head) {
85 		struct drm_fb_helper_connector *fb_helper_connector;
86 
87 		fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
88 		if (!fb_helper_connector)
89 			goto fail;
90 
91 		fb_helper_connector->connector = connector;
92 		fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
93 	}
94 	return 0;
95 fail:
96 	for (i = 0; i < fb_helper->connector_count; i++) {
97 		kfree(fb_helper->connector_info[i], sizeof(struct drm_fb_helper_connector));
98 		fb_helper->connector_info[i] = NULL;
99 	}
100 	fb_helper->connector_count = 0;
101 	return -ENOMEM;
102 }
103 
drm_fb_helper_parse_command_line(struct drm_fb_helper * fb_helper)104 static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
105 {
106 	struct drm_device *dev = fb_helper->dev;
107 	struct drm_fb_helper_connector *fb_helper_conn;
108 	struct gfxp_bm_fb_info fb_info;
109 	int i;
110 
111 	gfxp_bm_getfb_info(dev->vgatext->private, &fb_info);
112 	for (i = 0; i < fb_helper->connector_count; i++) {
113 
114 		fb_helper_conn = fb_helper->connector_info[i];
115 
116 		struct drm_cmdline_mode *cmdline_mode;
117 		cmdline_mode = &fb_helper_conn->cmdline_mode;
118 		cmdline_mode->specified = true;
119 		cmdline_mode->xres = fb_info.xres;
120 		cmdline_mode->yres = fb_info.yres;
121 		cmdline_mode->refresh_specified = true;
122 		cmdline_mode->refresh = 60;
123 		cmdline_mode->bpp_specified = true;
124 		cmdline_mode->bpp = fb_info.depth;
125 	}
126 	return 0;
127 }
128 
drm_fb_helper_save_lut_atomic(struct drm_crtc * crtc,struct drm_fb_helper * helper)129 static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper)
130 {
131 	uint16_t *r_base, *g_base, *b_base;
132 	int i;
133 
134 	if (helper->funcs->gamma_get == NULL)
135 		return;
136 
137 	r_base = crtc->gamma_store;
138 	g_base = r_base + crtc->gamma_size;
139 	b_base = g_base + crtc->gamma_size;
140 
141 	for (i = 0; i < crtc->gamma_size; i++)
142 		helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i);
143 }
144 
drm_fb_helper_restore_lut_atomic(struct drm_crtc * crtc)145 static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
146 {
147 	uint16_t *r_base, *g_base, *b_base;
148 
149 	if (crtc->funcs->gamma_set == NULL)
150 		return;
151 
152 	r_base = crtc->gamma_store;
153 	g_base = r_base + crtc->gamma_size;
154 	b_base = g_base + crtc->gamma_size;
155 
156 	crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
157 }
158 
159 /* Find the real fb for a given fb helper CRTC */
drm_mode_config_fb(struct drm_crtc * crtc)160 static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
161 {
162 	struct drm_device *dev = crtc->dev;
163 	struct drm_crtc *c;
164 
165 	list_for_each_entry(c, struct drm_crtc, &dev->mode_config.crtc_list, head) {
166 		if (crtc->base.id == c->base.id)
167 			return c->fb;
168 	}
169 
170 	return NULL;
171 }
172 
drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper * fb_helper)173 bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
174 {
175 	struct drm_device *dev;
176 	struct drm_plane *plane;
177 	bool error = false;
178 	int i;
179 
180 	if (fb_helper == NULL)
181 		return error;
182 
183 	dev = fb_helper->dev;
184 
185 	list_for_each_entry(plane, struct drm_plane, &dev->mode_config.plane_list, head)
186 		drm_plane_force_disable(plane);
187 
188 	for (i = 0; i < fb_helper->crtc_count; i++) {
189 		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
190 		struct drm_crtc *crtc = mode_set->crtc;
191 		int ret;
192 
193 		if (crtc->funcs->cursor_set) {
194 			ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
195 			if (ret)
196 				error = true;
197 		}
198 
199 		ret = drm_mode_set_config_internal(mode_set);
200 		if (ret)
201 			error = true;
202 	}
203 	return error;
204 }
205 
drm_fb_helper_force_kernel_mode(void)206 bool drm_fb_helper_force_kernel_mode(void)
207 {
208 	bool ret, error = false;
209 	struct drm_fb_helper *helper;
210 
211 	if (list_empty(&kernel_fb_helper_list))
212 		return false;
213 
214 	list_for_each_entry(helper, struct drm_fb_helper, &kernel_fb_helper_list, kernel_fb_list) {
215 		if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF)
216 			continue;
217 
218 		ret = drm_fb_helper_restore_fbdev_mode(helper);
219 		if (ret)
220 			error = true;
221 	}
222 	return error;
223 }
224 
drm_fb_helper_is_bound(struct drm_fb_helper * fb_helper)225 static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
226 {
227 	struct drm_device *dev = fb_helper->dev;
228 	struct drm_crtc *crtc;
229 	int bound = 0, crtcs_bound = 0;
230 
231 	list_for_each_entry(crtc, struct drm_crtc, &dev->mode_config.crtc_list, head) {
232 		if (crtc->fb)
233 			crtcs_bound++;
234 		if (crtc->fb == fb_helper->fb)
235 			bound++;
236 	}
237 
238 	if (bound < crtcs_bound)
239 		return false;
240 	return true;
241 }
242 
243 /* maximum connectors per crtcs in the mode set */
244 #define INTELFB_CONN_LIMIT 4
245 
drm_fb_helper_crtc_free(struct drm_fb_helper * helper)246 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
247 {
248 	int i;
249 	struct drm_device *dev = helper->dev;
250 
251 	kfree(helper->connector_info, dev->mode_config.num_connector * sizeof(struct drm_fb_helper_connector *));
252 	for (i = 0; i < helper->crtc_count; i++) {
253 		kfree(helper->crtc_info[i].mode_set.connectors, INTELFB_CONN_LIMIT * sizeof(struct drm_connector *));
254 		if (helper->crtc_info[i].mode_set.mode)
255 			drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
256 	}
257 	kfree(helper->crtc_info, helper->crtc_count * sizeof(struct drm_fb_helper_crtc));
258 }
259 
drm_fb_helper_init(struct drm_device * dev,struct drm_fb_helper * fb_helper,int crtc_count,int max_conn_count)260 int drm_fb_helper_init(struct drm_device *dev,
261 		       struct drm_fb_helper *fb_helper,
262 		       int crtc_count, int max_conn_count)
263 {
264 	struct drm_crtc *crtc;
265 	int i;
266 
267 	fb_helper->dev = dev;
268 
269 	INIT_LIST_HEAD(&kernel_fb_helper_list);
270 	INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
271 
272 	fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
273 	if (!fb_helper->crtc_info)
274 		return -ENOMEM;
275 
276 	fb_helper->crtc_count = crtc_count;
277 	fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
278 	if (!fb_helper->connector_info) {
279 		kfree(fb_helper->crtc_info, sizeof(struct drm_fb_helper_crtc));
280 		return -ENOMEM;
281 	}
282 	fb_helper->connector_count = 0;
283 
284 	for (i = 0; i < crtc_count; i++) {
285 		fb_helper->crtc_info[i].mode_set.connectors =
286 			kcalloc(max_conn_count,
287 				sizeof(struct drm_connector *),
288 				GFP_KERNEL);
289 
290 		if (!fb_helper->crtc_info[i].mode_set.connectors)
291 			goto out_free;
292 		fb_helper->crtc_info[i].mode_set.num_connectors = 0;
293 	}
294 
295 	i = 0;
296 	list_for_each_entry(crtc, struct drm_crtc, &dev->mode_config.crtc_list, head) {
297 		fb_helper->crtc_info[i].mode_set.crtc = crtc;
298 		i++;
299 	}
300 
301 	return 0;
302 out_free:
303 	drm_fb_helper_crtc_free(fb_helper);
304 	return -ENOMEM;
305 }
306 
drm_fb_helper_fini(struct drm_fb_helper * fb_helper)307 void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
308 {
309 	if (!list_empty(&fb_helper->kernel_fb_list)) {
310 		list_del(&fb_helper->kernel_fb_list);
311 		if (list_empty(&kernel_fb_helper_list)) {
312 			DRM_INFO("drm: unregistered panic notifier");
313 		}
314 	}
315 
316 	drm_fb_helper_crtc_free(fb_helper);
317 
318 }
319 
drm_fb_helper_single_fb_probe(struct drm_fb_helper * fb_helper,int preferred_bpp)320 int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
321 				  int preferred_bpp)
322 {
323 	int ret = 0;
324 	int crtc_count = 0;
325 	int i;
326 	struct drm_fb_helper_surface_size sizes;
327 	int gamma_size = 0;
328 
329 	(void) memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
330 	sizes.surface_depth = 24;
331 	sizes.surface_bpp = 32;
332 	sizes.fb_width = (unsigned)-1;
333 	sizes.fb_height = (unsigned)-1;
334 
335 	/* if driver picks 8 or 16 by default use that
336 	   for both depth/bpp */
337 	if (preferred_bpp != sizes.surface_bpp)
338 		sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
339 
340 	/* first up get a count of crtcs now in use and new min/maxes width/heights */
341 	for (i = 0; i < fb_helper->connector_count; i++) {
342 		struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
343 		struct drm_cmdline_mode *cmdline_mode;
344 
345 		cmdline_mode = &fb_helper_conn->cmdline_mode;
346 
347 		if (cmdline_mode->bpp_specified) {
348 			switch (cmdline_mode->bpp) {
349 			case 8:
350 				sizes.surface_depth = sizes.surface_bpp = 8;
351 				break;
352 			case 15:
353 				sizes.surface_depth = 15;
354 				sizes.surface_bpp = 16;
355 				break;
356 			case 16:
357 				sizes.surface_depth = sizes.surface_bpp = 16;
358 				break;
359 			case 24:
360 				sizes.surface_depth = sizes.surface_bpp = 24;
361 				break;
362 			case 32:
363 				sizes.surface_depth = 24;
364 				sizes.surface_bpp = 32;
365 				break;
366 			}
367 			break;
368 		}
369 	}
370 
371 	crtc_count = 0;
372 	for (i = 0; i < fb_helper->crtc_count; i++) {
373 		struct drm_display_mode *desired_mode;
374 		desired_mode = fb_helper->crtc_info[i].desired_mode;
375 
376 		if (desired_mode) {
377 			if (gamma_size == 0)
378 				gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
379 			if (desired_mode->hdisplay < sizes.fb_width)
380 				sizes.fb_width = desired_mode->hdisplay;
381 			if (desired_mode->vdisplay < sizes.fb_height)
382 				sizes.fb_height = desired_mode->vdisplay;
383 			if (desired_mode->hdisplay > sizes.surface_width)
384 				sizes.surface_width = desired_mode->hdisplay;
385 			if (desired_mode->vdisplay > sizes.surface_height)
386 				sizes.surface_height = desired_mode->vdisplay;
387 			crtc_count++;
388 		}
389 	}
390 
391 	if (crtc_count == 0 || sizes.fb_width == (unsigned)-1 || sizes.fb_height == (unsigned)-1) {
392 		/* hmm everyone went away - assume VGA cable just fell out
393 		   and will come back later. */
394 		DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n");
395 		sizes.fb_width = sizes.surface_width = 1024;
396 		sizes.fb_height = sizes.surface_height = 768;
397 	}
398 
399 	/* push down into drivers */
400 	ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
401 	if (ret < 0)
402 		return ret;
403 
404 	/*
405 	 * Set the fb pointer - usually drm_setup_crtcs does this for hotplug
406 	 * events, but at init time drm_setup_crtcs needs to be called before
407 	 * the fb is allocated (since we need to figure out the desired size of
408 	 * the fb before we can allocate it ...). Hence we need to fix things up
409 	 * here again.
410 	 */
411 	for (i = 0; i < fb_helper->crtc_count; i++)
412 		if (fb_helper->crtc_info[i].mode_set.num_connectors)
413 			fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
414 
415 
416 	/* Switch back to kernel console on panic */
417 	/* multi card linked list maybe */
418 	if (list_empty(&kernel_fb_helper_list)) {
419 		DRM_INFO("registered panic notifier");
420 	}
421 
422 	list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list, (caddr_t)fb_helper);
423 
424 	return 0;
425 }
426 
drm_fb_helper_probe_connector_modes(struct drm_fb_helper * fb_helper,uint32_t maxX,uint32_t maxY)427 static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
428 					       uint32_t maxX,
429 					       uint32_t maxY)
430 {
431 	struct drm_connector *connector;
432 	int count = 0;
433 	int i;
434 
435 	for (i = 0; i < fb_helper->connector_count; i++) {
436 		connector = fb_helper->connector_info[i]->connector;
437 		count += connector->funcs->fill_modes(connector, maxX, maxY);
438 	}
439 
440 	return count;
441 }
442 
drm_has_preferred_mode(struct drm_fb_helper_connector * fb_connector,int width,int height)443 static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
444 {
445 	struct drm_display_mode *mode;
446 
447 	list_for_each_entry(mode, struct drm_display_mode, &fb_connector->connector->modes, head) {
448 		if (drm_mode_width(mode) > width ||
449 		    drm_mode_height(mode) > height)
450 			continue;
451 		if (mode->type & DRM_MODE_TYPE_PREFERRED)
452 			return mode;
453 	}
454 	return NULL;
455 }
456 
drm_has_cmdline_mode(struct drm_fb_helper_connector * fb_connector)457 static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
458 {
459 	struct drm_cmdline_mode *cmdline_mode;
460 	cmdline_mode = &fb_connector->cmdline_mode;
461 	return cmdline_mode->specified;
462 }
463 
drm_pick_cmdline_mode(struct drm_fb_helper_connector * fb_helper_conn,int width,int height)464 static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
465 					/* LINTED E_FUNC_ARG_UNUSED */
466 						      int width, int height)
467 {
468 	struct drm_cmdline_mode *cmdline_mode;
469 	struct drm_display_mode *mode = NULL;
470 
471 	cmdline_mode = &fb_helper_conn->cmdline_mode;
472 	if (cmdline_mode->specified == false)
473 		return mode;
474 
475 	/* attempt to find a matching mode in the list of modes
476 	 *  we have gotten so far, if not add a CVT mode that conforms
477 	 */
478 	if (cmdline_mode->rb || cmdline_mode->margins)
479 		goto create_mode;
480 
481 	list_for_each_entry(mode, struct drm_display_mode, &fb_helper_conn->connector->modes, head) {
482 		/* check width/height */
483 		if (mode->hdisplay != cmdline_mode->xres ||
484 		    mode->vdisplay != cmdline_mode->yres)
485 			continue;
486 
487 		if (cmdline_mode->refresh_specified) {
488 			if (mode->vrefresh != cmdline_mode->refresh)
489 				continue;
490 		}
491 
492 		if (cmdline_mode->interlace) {
493 			if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
494 				continue;
495 		}
496 		return mode;
497 	}
498 
499 create_mode:
500 	mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
501 						 cmdline_mode);
502 	if (mode)
503 		list_add(&mode->head, &fb_helper_conn->connector->modes,
504 		    (caddr_t)mode);
505 	return mode;
506 }
507 
drm_connector_enabled(struct drm_connector * connector,bool strict)508 static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
509 {
510 	bool enable;
511 
512 	if (strict)
513 		enable = connector->status == connector_status_connected;
514 	else
515 		enable = connector->status != connector_status_disconnected;
516 
517 	return enable;
518 }
519 
drm_enable_connectors(struct drm_fb_helper * fb_helper,bool * enabled)520 static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
521 				  bool *enabled)
522 {
523 	bool any_enabled = false;
524 	struct drm_connector *connector;
525 	int i = 0;
526 
527 	for (i = 0; i < fb_helper->connector_count; i++) {
528 		connector = fb_helper->connector_info[i]->connector;
529 		enabled[i] = drm_connector_enabled(connector, true);
530 		DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
531 			  enabled[i] ? "yes" : "no");
532 		any_enabled |= enabled[i];
533 	}
534 
535 	if (any_enabled)
536 		return;
537 
538 	for (i = 0; i < fb_helper->connector_count; i++) {
539 		connector = fb_helper->connector_info[i]->connector;
540 		enabled[i] = drm_connector_enabled(connector, false);
541 	}
542 }
543 
drm_target_cloned(struct drm_fb_helper * fb_helper,struct drm_display_mode ** modes,bool * enabled,int width,int height)544 static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
545 			      struct drm_display_mode **modes,
546 			      bool *enabled, int width, int height)
547 {
548 	int count, i, j;
549 	bool can_clone = false;
550 	struct drm_fb_helper_connector *fb_helper_conn;
551 	struct drm_display_mode *dmt_mode, *mode;
552 
553 	/* only contemplate cloning in the single crtc case */
554 	if (fb_helper->crtc_count > 1)
555 		return false;
556 
557 	count = 0;
558 	for (i = 0; i < fb_helper->connector_count; i++) {
559 		if (enabled[i])
560 			count++;
561 	}
562 
563 	/* only contemplate cloning if more than one connector is enabled */
564 	if (count <= 1)
565 		return false;
566 
567 	/* check the command line or if nothing common pick 1024x768 */
568 	can_clone = true;
569 	for (i = 0; i < fb_helper->connector_count; i++) {
570 		if (!enabled[i])
571 			continue;
572 		fb_helper_conn = fb_helper->connector_info[i];
573 		modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
574 		if (!modes[i]) {
575 			can_clone = false;
576 			break;
577 		}
578 		for (j = 0; j < i; j++) {
579 			if (!enabled[j])
580 				continue;
581 			if (!drm_mode_equal(modes[j], modes[i]))
582 				can_clone = false;
583 		}
584 	}
585 
586 	if (can_clone) {
587 		DRM_DEBUG_KMS("can clone using command line\n");
588 		return true;
589 	}
590 
591 	/* try and find a 1024x768 mode on each connector */
592 	can_clone = true;
593 	dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
594 
595 	for (i = 0; i < fb_helper->connector_count; i++) {
596 
597 		if (!enabled[i])
598 			continue;
599 
600 		fb_helper_conn = fb_helper->connector_info[i];
601 		list_for_each_entry(mode, struct drm_display_mode, &fb_helper_conn->connector->modes, head) {
602 			if (drm_mode_equal(mode, dmt_mode))
603 				modes[i] = mode;
604 		}
605 		if (!modes[i])
606 			can_clone = false;
607 	}
608 
609 	if (can_clone) {
610 		DRM_DEBUG_KMS("can clone using 1024x768\n");
611 		return true;
612 	}
613 	DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
614 	return false;
615 }
616 
drm_target_preferred(struct drm_fb_helper * fb_helper,struct drm_display_mode ** modes,bool * enabled,int width,int height)617 static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
618 				 struct drm_display_mode **modes,
619 				 bool *enabled, int width, int height)
620 {
621 	struct drm_fb_helper_connector *fb_helper_conn;
622 	int i;
623 
624 	for (i = 0; i < fb_helper->connector_count; i++) {
625 		fb_helper_conn = fb_helper->connector_info[i];
626 
627 		if (enabled[i] == false)
628 			continue;
629 
630 		DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
631 			      fb_helper_conn->connector->base.id);
632 
633 		/* got for command line mode first */
634 		modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
635 		if (!modes[i]) {
636 			DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
637 				      fb_helper_conn->connector->base.id);
638 			modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
639 		}
640 		/* No preferred modes, pick one off the list */
641 		if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
642 			list_for_each_entry(modes[i], struct drm_display_mode, &fb_helper_conn->connector->modes, head)
643 				/* LINTED */
644 				{ if (1) break; }
645 		}
646 		DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
647 			  "none");
648 	}
649 	return true;
650 }
651 
drm_pick_crtcs(struct drm_fb_helper * fb_helper,struct drm_fb_helper_crtc ** best_crtcs,struct drm_display_mode ** modes,int n,int width,int height)652 static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
653 			  struct drm_fb_helper_crtc **best_crtcs,
654 			  struct drm_display_mode **modes,
655 			  int n, int width, int height)
656 {
657 	int c, o;
658 	struct drm_device *dev = fb_helper->dev;
659 	struct drm_connector *connector;
660 	struct drm_connector_helper_funcs *connector_funcs;
661 	struct drm_encoder *encoder;
662 	int my_score, best_score, score;
663 	struct drm_fb_helper_crtc **crtcs, *crtc;
664 	struct drm_fb_helper_connector *fb_helper_conn;
665 	int  num_connector = dev->mode_config.num_connector;
666 
667 	if (n == fb_helper->connector_count)
668 		return 0;
669 
670 	ASSERT(n <= num_connector);
671 
672 	fb_helper_conn = fb_helper->connector_info[n];
673 	connector = fb_helper_conn->connector;
674 
675 	best_crtcs[n] = NULL;
676 	best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
677 	if (modes[n] == NULL)
678 		return best_score;
679 
680 	crtcs = kzalloc(num_connector * sizeof(struct drm_fb_helper_crtc *),
681 	    GFP_KERNEL);
682 	if (!crtcs)
683 		return best_score;
684 
685 	my_score = 1;
686 	if (connector->status == connector_status_connected)
687 		my_score++;
688 	if (drm_has_cmdline_mode(fb_helper_conn))
689 		my_score++;
690 	if (drm_has_preferred_mode(fb_helper_conn, width, height))
691 		my_score++;
692 
693 	connector_funcs = connector->helper_private;
694 	encoder = connector_funcs->best_encoder(connector);
695 	if (!encoder)
696 		goto out;
697 
698 	/* select a crtc for this connector and then attempt to configure
699 	   remaining connectors */
700 	for (c = 0; c < fb_helper->crtc_count; c++) {
701 		crtc = &fb_helper->crtc_info[c];
702 
703 		if ((encoder->possible_crtcs & (1 << c)) == 0)
704 			continue;
705 
706 		for (o = 0; o < n; o++)
707 			if (best_crtcs[o] == crtc)
708 				break;
709 
710 		if (o < n) {
711 			/* ignore cloning unless only a single crtc */
712 			if (fb_helper->crtc_count > 1)
713 				continue;
714 
715 			if (!drm_mode_equal(modes[o], modes[n]))
716 				continue;
717 		}
718 
719 		crtcs[n] = crtc;
720 		(void) memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
721 		score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
722 						  width, height);
723 		if (score > best_score) {
724 			best_score = score;
725 			(void) memcpy(best_crtcs, crtcs,
726 			    num_connector *
727 			    sizeof(struct drm_fb_helper_crtc *));
728 		}
729 	}
730 out:
731 	kfree(crtcs, (num_connector * sizeof(struct drm_fb_helper_crtc *)));
732 	return best_score;
733 }
734 
drm_setup_crtcs(struct drm_fb_helper * fb_helper)735 static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
736 {
737 	struct drm_device *dev = fb_helper->dev;
738 	struct drm_fb_helper_crtc **crtcs;
739 	struct drm_display_mode **modes;
740 	struct drm_mode_set *modeset;
741 	bool *enabled;
742 	int width, height;
743 	int i, num_connector = dev->mode_config.num_connector;
744 
745 	DRM_DEBUG_KMS("\n");
746 
747 	width = dev->mode_config.max_width;
748 	height = dev->mode_config.max_height;
749 
750 	if ((crtcs = kcalloc(num_connector,
751 	    sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL)) == NULL) {
752 		DRM_ERROR("Memory allocation failed for crtcs\n");
753 		return;
754 	}
755 	if ((modes = kcalloc(num_connector,
756 	    sizeof(struct drm_display_mode *), GFP_KERNEL)) == NULL) {
757 		DRM_ERROR("Memory allocation failed for modes\n");
758 		goto errout1;
759 	}
760 	if ((enabled = kcalloc(num_connector,
761 	    sizeof(bool), GFP_KERNEL)) == NULL) {
762 		DRM_ERROR("Memory allocation failed for enabled\n");
763 		goto errout2;
764 	}
765 
766 
767 	drm_enable_connectors(fb_helper, enabled);
768 
769 	if (!(fb_helper->funcs->initial_config &&
770 	      fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
771 					       enabled, width, height))) {
772 		(void) memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0]));
773 		(void) memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0]));
774 
775 		if (!drm_target_cloned(fb_helper,
776 				       modes, enabled, width, height) &&
777 		    !drm_target_preferred(fb_helper,
778 					  modes, enabled, width, height))
779 			DRM_ERROR("Unable to find initial modes\n");
780 
781 		DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
782 			      width, height);
783 
784 		(void) drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
785 	}
786 
787 	/* need to set the modesets up here for use later */
788 	/* fill out the connector<->crtc mappings into the modesets */
789 	for (i = 0; i < fb_helper->crtc_count; i++) {
790 		modeset = &fb_helper->crtc_info[i].mode_set;
791 		modeset->num_connectors = 0;
792 		modeset->fb = NULL;
793 	}
794 
795 	for (i = 0; i < fb_helper->connector_count; i++) {
796 		struct drm_display_mode *mode = modes[i];
797 		struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
798 		modeset = &fb_crtc->mode_set;
799 
800 		if (mode && fb_crtc) {
801 			DRM_DEBUG_KMS("desired mode %s set on crtc %d\n",
802 				      mode->name, fb_crtc->mode_set.crtc->base.id);
803 			fb_crtc->desired_mode = mode;
804 			if (modeset->mode)
805 				drm_mode_destroy(dev, modeset->mode);
806 			modeset->mode = drm_mode_duplicate(dev,
807 							   fb_crtc->desired_mode);
808 			modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
809 			modeset->fb = fb_helper->fb;
810 		}
811 	}
812 
813 	/* Clear out any old modes if there are no more connected outputs. */
814 	for (i = 0; i < fb_helper->crtc_count; i++) {
815 		modeset = &fb_helper->crtc_info[i].mode_set;
816 		if (modeset->num_connectors == 0) {
817 			BUG_ON(modeset->fb);
818 			BUG_ON(modeset->num_connectors);
819 			if (modeset->mode)
820 				drm_mode_destroy(dev, modeset->mode);
821 			modeset->mode = NULL;
822 		}
823 	}
824 
825 	kfree(enabled, num_connector * sizeof(bool));
826 errout2:
827 	kfree(modes, num_connector * sizeof(struct drm_display_mode *));
828 errout1:
829 	kfree(crtcs, num_connector * sizeof(struct drm_fb_helper_crtc *));
830 }
831 
832 /**
833  * drm_helper_initial_config - setup a sane initial connector configuration
834  * @dev: DRM device
835  *
836  *
837  * Scan the CRTCs and connectors and try to put together an initial setup.
838  * At the moment, this is a cloned configuration across all heads with
839  * a new framebuffer object as the backing store.
840  *
841  * Note that this also registers the fbdev and so allows userspace to call into
842  * the driver through the fbdev interfaces.
843  *
844  * This function will call down into the ->fb_probe callback to let
845  * the driver allocate and initialize the fbdev info structure and the drm
846  * framebuffer used to back the fbdev. drm_fb_helper_fill_var() and
847  * drm_fb_helper_fill_fix() are provided as helpers to setup simple default
848  * values for the fbdev info structure.
849  *
850  * RETURNS:
851  * Zero if everything went ok, nonzero otherwise.
852  */
drm_fb_helper_initial_config(struct drm_fb_helper * fb_helper,int bpp_sel)853 bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
854 {
855 	struct drm_device *dev = fb_helper->dev;
856 	int count = 0;
857 
858 	(void) drm_fb_helper_parse_command_line(fb_helper);
859 
860 	count = drm_fb_helper_probe_connector_modes(fb_helper,
861 						    dev->mode_config.max_width,
862 						    dev->mode_config.max_height);
863 	/*
864 	 * we shouldn't end up with no modes here.
865 	 */
866 	if (count == 0) {
867 		DRM_INFO("No connectors reported connected with modes");
868 	}
869 	drm_setup_crtcs(fb_helper);
870 
871 	return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
872 }
873 
874 
875 /**
876  * drm_fb_helper_hotplug_event - respond to a hotplug notification by
877  *                               probing all the outputs attached to the fb.
878  * @fb_helper: the drm_fb_helper
879  *
880  * Scan the connectors attached to the fb_helper and try to put together a
881  * setup after *notification of a change in output configuration.
882  *
883  * Called at runtime, takes the mode config locks to be able to check/change the
884  * modeset configuration. Must be run from process context (which usually means
885  * either the output polling work or a work item launched from the driver's
886  * hotplug interrupt).
887  *
888  * Scan the connectors attached to the fb_helper and try to put together a
889  * setup after *notification of a change in output configuration.
890  *
891  * RETURNS:
892  * 0 on success and a non-zero error code otherwise.
893  */
drm_fb_helper_hotplug_event(struct drm_fb_helper * fb_helper)894 int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
895 {
896 	struct drm_device *dev = fb_helper->dev;
897 	u32 max_width, max_height, bpp_sel;
898 
899 	if (!fb_helper->fb)
900 		return 0;
901 
902 	mutex_lock(&fb_helper->dev->mode_config.mutex);
903 	if (!drm_fb_helper_is_bound(fb_helper)) {
904 		fb_helper->delayed_hotplug = true;
905 		mutex_unlock(&fb_helper->dev->mode_config.mutex);
906 		return 0;
907 	}
908 	DRM_DEBUG_KMS("\n");
909 
910 	max_width = fb_helper->fb->width;
911 	max_height = fb_helper->fb->height;
912 	bpp_sel = fb_helper->fb->bits_per_pixel;
913 
914 	(void) drm_fb_helper_probe_connector_modes(fb_helper, max_width,
915 						    max_height);
916 	mutex_unlock(&fb_helper->dev->mode_config.mutex);
917 
918 	drm_modeset_lock_all(dev);
919 	drm_setup_crtcs(fb_helper);
920 	drm_modeset_unlock_all(dev);
921 
922 	return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
923 }
924 
drm_gfxp_setmode(int mode)925 int drm_gfxp_setmode(int mode)
926 {
927 		bool ret = 0;
928         if (mode == 0) {
929                 ret = drm_fb_helper_force_kernel_mode();
930 		if (ret == true)
931 			DRM_ERROR("Failed to restore crtc configuration\n");
932 	}
933         if (mode == 1)
934                 DRM_DEBUG_KMS("do nothing in entervt");
935         return ret;
936 }
937 
938 struct gfxp_blt_ops drm_gfxp_ops = {
939         NULL,   /* blt */
940         NULL,   /* copy */
941         NULL,   /* clear */
942         drm_gfxp_setmode, /* setmode */
943 };
944 
drm_register_fbops(struct drm_device * dev)945 void drm_register_fbops(struct drm_device *dev)
946 {
947 	gfxp_bm_register_fbops(dev->vgatext->private, &drm_gfxp_ops);
948 }
949 
drm_getfb_size(struct drm_device * dev)950 int drm_getfb_size(struct drm_device *dev)
951 {
952 	struct gfxp_bm_fb_info fb_info;
953 	int size, pitch;
954         gfxp_bm_getfb_info(dev->vgatext->private, &fb_info);
955         pitch = ALIGN(fb_info.xres * ((fb_info.depth + 7) / 8), 64);
956         size = ALIGN(pitch *fb_info.yres, PAGE_SIZE);
957 	return size;
958 }
959