xref: /freebsd/sys/dev/drm2/drm_fb_helper.c (revision ea43014585ec4131d1d7a7f027110f12781c99cc)
1 /*
2  * Copyright (c) 2006-2009 Red Hat Inc.
3  * Copyright (c) 2006-2008 Intel Corporation
4  * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
5  *
6  * DRM framebuffer helper functions
7  *
8  * Permission to use, copy, modify, distribute, and sell this software and its
9  * documentation for any purpose is hereby granted without fee, provided that
10  * the above copyright notice appear in all copies and that both that copyright
11  * notice and this permission notice appear in supporting documentation, and
12  * that the name of the copyright holders not be used in advertising or
13  * publicity pertaining to distribution of the software without specific,
14  * written prior permission.  The copyright holders make no representations
15  * about the suitability of this software for any purpose.  It is provided "as
16  * is" without express or implied warranty.
17  *
18  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
20  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
21  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
22  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
23  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24  * OF THIS SOFTWARE.
25  *
26  * Authors:
27  *      Dave Airlie <airlied@linux.ie>
28  *      Jesse Barnes <jesse.barnes@intel.com>
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <dev/drm2/drmP.h>
35 #include <dev/drm2/drm_crtc.h>
36 #include <dev/drm2/drm_fb_helper.h>
37 #include <dev/drm2/drm_crtc_helper.h>
38 
39 struct vt_kms_softc {
40 	struct drm_fb_helper *fb_helper;
41 	struct task	fb_mode_task;
42 };
43 
44 static fb_enter_t	vt_kms_postswitch;
45 static void vt_restore_fbdev_mode(void *, int);
46 
47 /* Call restore out of vt(9) locks. */
48 static void
49 vt_restore_fbdev_mode(void *arg, int pending)
50 {
51 	struct drm_fb_helper *fb_helper;
52 	struct vt_kms_softc *sc;
53 
54 	sc = (struct vt_kms_softc *)arg;
55 	fb_helper = sc->fb_helper;
56 	sx_xlock(&fb_helper->dev->mode_config.mutex);
57 	drm_fb_helper_restore_fbdev_mode(fb_helper);
58 	sx_xunlock(&fb_helper->dev->mode_config.mutex);
59 }
60 
61 static int
62 vt_kms_postswitch(void *arg)
63 {
64 	struct vt_kms_softc *sc;
65 
66 	sc = (struct vt_kms_softc *)arg;
67 	taskqueue_enqueue_fast(taskqueue_thread, &sc->fb_mode_task);
68 
69 	return (0);
70 }
71 
72 static DRM_LIST_HEAD(kernel_fb_helper_list);
73 
74 /* simple single crtc case helper function */
75 int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
76 {
77 	struct drm_device *dev = fb_helper->dev;
78 	struct drm_connector *connector;
79 
80 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
81 		struct drm_fb_helper_connector *fb_helper_connector;
82 
83 		fb_helper_connector = malloc(
84 		    sizeof(struct drm_fb_helper_connector), DRM_MEM_KMS,
85 		    M_WAITOK | M_ZERO);
86 
87 		fb_helper_connector->connector = connector;
88 		fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
89 	}
90 	return 0;
91 }
92 
93 const char *fb_mode_option;
94 
95 /**
96  * drm_fb_helper_connector_parse_command_line - parse command line for connector
97  * @connector - connector to parse line for
98  * @mode_option - per connector mode option
99  *
100  * This parses the connector specific then generic command lines for
101  * modes and options to configure the connector.
102  *
103  * This uses the same parameters as the fb modedb.c, except for extra
104  *	<xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
105  *
106  * enable/enable Digital/disable bit at the end
107  */
108 static bool drm_fb_helper_connector_parse_command_line(struct drm_fb_helper_connector *fb_helper_conn,
109 						       const char *mode_option)
110 {
111 	const char *name;
112 	unsigned int namelen;
113 	int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
114 	unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
115 	int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
116 	int i;
117 	enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
118 	struct drm_fb_helper_cmdline_mode *cmdline_mode;
119 	struct drm_connector *connector;
120 
121 	if (!fb_helper_conn)
122 		return false;
123 	connector = fb_helper_conn->connector;
124 
125 	cmdline_mode = &fb_helper_conn->cmdline_mode;
126 	if (!mode_option)
127 		mode_option = fb_mode_option;
128 
129 	if (!mode_option) {
130 		cmdline_mode->specified = false;
131 		return false;
132 	}
133 
134 	name = mode_option;
135 	namelen = strlen(name);
136 	for (i = namelen-1; i >= 0; i--) {
137 		switch (name[i]) {
138 		case '@':
139 			namelen = i;
140 			if (!refresh_specified && !bpp_specified &&
141 			    !yres_specified) {
142 				refresh = strtol(&name[i+1], NULL, 10);
143 				refresh_specified = 1;
144 				if (cvt || rb)
145 					cvt = 0;
146 			} else
147 				goto done;
148 			break;
149 		case '-':
150 			namelen = i;
151 			if (!bpp_specified && !yres_specified) {
152 				bpp = strtol(&name[i+1], NULL, 10);
153 				bpp_specified = 1;
154 				if (cvt || rb)
155 					cvt = 0;
156 			} else
157 				goto done;
158 			break;
159 		case 'x':
160 			if (!yres_specified) {
161 				yres = strtol(&name[i+1], NULL, 10);
162 				yres_specified = 1;
163 			} else
164 				goto done;
165 		case '0' ... '9':
166 			break;
167 		case 'M':
168 			if (!yres_specified)
169 				cvt = 1;
170 			break;
171 		case 'R':
172 			if (cvt)
173 				rb = 1;
174 			break;
175 		case 'm':
176 			if (!cvt)
177 				margins = 1;
178 			break;
179 		case 'i':
180 			if (!cvt)
181 				interlace = 1;
182 			break;
183 		case 'e':
184 			force = DRM_FORCE_ON;
185 			break;
186 		case 'D':
187 			if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
188 			    (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
189 				force = DRM_FORCE_ON;
190 			else
191 				force = DRM_FORCE_ON_DIGITAL;
192 			break;
193 		case 'd':
194 			force = DRM_FORCE_OFF;
195 			break;
196 		default:
197 			goto done;
198 		}
199 	}
200 	if (i < 0 && yres_specified) {
201 		xres = strtol(name, NULL, 10);
202 		res_specified = 1;
203 	}
204 done:
205 
206 	DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
207 		drm_get_connector_name(connector), xres, yres,
208 		(refresh) ? refresh : 60, (rb) ? " reduced blanking" :
209 		"", (margins) ? " with margins" : "", (interlace) ?
210 		" interlaced" : "");
211 
212 	if (force) {
213 		const char *s;
214 		switch (force) {
215 		case DRM_FORCE_OFF: s = "OFF"; break;
216 		case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
217 		default:
218 		case DRM_FORCE_ON: s = "ON"; break;
219 		}
220 
221 		DRM_INFO("forcing %s connector %s\n",
222 			 drm_get_connector_name(connector), s);
223 		connector->force = force;
224 	}
225 
226 	if (res_specified) {
227 		cmdline_mode->specified = true;
228 		cmdline_mode->xres = xres;
229 		cmdline_mode->yres = yres;
230 	}
231 
232 	if (refresh_specified) {
233 		cmdline_mode->refresh_specified = true;
234 		cmdline_mode->refresh = refresh;
235 	}
236 
237 	if (bpp_specified) {
238 		cmdline_mode->bpp_specified = true;
239 		cmdline_mode->bpp = bpp;
240 	}
241 	cmdline_mode->rb = rb ? true : false;
242 	cmdline_mode->cvt = cvt  ? true : false;
243 	cmdline_mode->interlace = interlace ? true : false;
244 
245 	return true;
246 }
247 
248 static int
249 fb_get_options(const char *connector_name, char **option)
250 {
251 
252 	/*
253 	 * TODO: store mode options pointer in ${option} for connector with
254 	 * name ${connector_name}
255 	 */
256 	return (1);
257 }
258 
259 static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
260 {
261 	struct drm_fb_helper_connector *fb_helper_conn;
262 	int i;
263 
264 	for (i = 0; i < fb_helper->connector_count; i++) {
265 		char *option = NULL;
266 
267 		fb_helper_conn = fb_helper->connector_info[i];
268 
269 		/* do something on return - turn off connector maybe */
270 		if (fb_get_options(drm_get_connector_name(fb_helper_conn->connector), &option))
271 			continue;
272 
273 		drm_fb_helper_connector_parse_command_line(fb_helper_conn, option);
274 	}
275 	return 0;
276 }
277 
278 #if 0
279 static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper)
280 {
281 	uint16_t *r_base, *g_base, *b_base;
282 	int i;
283 
284 	r_base = crtc->gamma_store;
285 	g_base = r_base + crtc->gamma_size;
286 	b_base = g_base + crtc->gamma_size;
287 
288 	for (i = 0; i < crtc->gamma_size; i++)
289 		helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i);
290 }
291 
292 static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
293 {
294 	uint16_t *r_base, *g_base, *b_base;
295 
296 	r_base = crtc->gamma_store;
297 	g_base = r_base + crtc->gamma_size;
298 	b_base = g_base + crtc->gamma_size;
299 
300 	crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
301 }
302 #endif
303 
304 #if 0
305 int drm_fb_helper_debug_enter(struct fb_info *info)
306 {
307 	struct drm_fb_helper *helper = info->par;
308 	struct drm_crtc_helper_funcs *funcs;
309 	int i;
310 
311 	if (list_empty(&kernel_fb_helper_list))
312 		return false;
313 
314 	list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
315 		for (i = 0; i < helper->crtc_count; i++) {
316 			struct drm_mode_set *mode_set =
317 				&helper->crtc_info[i].mode_set;
318 
319 			if (!mode_set->crtc->enabled)
320 				continue;
321 
322 			funcs =	mode_set->crtc->helper_private;
323 			drm_fb_helper_save_lut_atomic(mode_set->crtc, helper);
324 			funcs->mode_set_base_atomic(mode_set->crtc,
325 						    mode_set->fb,
326 						    mode_set->x,
327 						    mode_set->y,
328 						    ENTER_ATOMIC_MODE_SET);
329 		}
330 	}
331 
332 	return 0;
333 }
334 #endif
335 
336 #if 0
337 /* Find the real fb for a given fb helper CRTC */
338 static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
339 {
340 	struct drm_device *dev = crtc->dev;
341 	struct drm_crtc *c;
342 
343 	list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
344 		if (crtc->base.id == c->base.id)
345 			return c->fb;
346 	}
347 
348 	return NULL;
349 }
350 #endif
351 
352 #if 0
353 int drm_fb_helper_debug_leave(struct fb_info *info)
354 {
355 	struct drm_fb_helper *helper = info->par;
356 	struct drm_crtc *crtc;
357 	struct drm_crtc_helper_funcs *funcs;
358 	struct drm_framebuffer *fb;
359 	int i;
360 
361 	for (i = 0; i < helper->crtc_count; i++) {
362 		struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
363 		crtc = mode_set->crtc;
364 		funcs = crtc->helper_private;
365 		fb = drm_mode_config_fb(crtc);
366 
367 		if (!crtc->enabled)
368 			continue;
369 
370 		if (!fb) {
371 			DRM_ERROR("no fb to restore??\n");
372 			continue;
373 		}
374 
375 		drm_fb_helper_restore_lut_atomic(mode_set->crtc);
376 		funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,
377 					    crtc->y, LEAVE_ATOMIC_MODE_SET);
378 	}
379 
380 	return 0;
381 }
382 #endif
383 
384 bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
385 {
386 	bool error = false;
387 	int i, ret;
388 	for (i = 0; i < fb_helper->crtc_count; i++) {
389 		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
390 		ret = drm_crtc_helper_set_config(mode_set);
391 		if (ret)
392 			error = true;
393 	}
394 	return error;
395 }
396 
397 #if 0
398 bool drm_fb_helper_force_kernel_mode(void)
399 {
400 	bool ret, error = false;
401 	struct drm_fb_helper *helper;
402 
403 	if (list_empty(&kernel_fb_helper_list))
404 		return false;
405 
406 	list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
407 		if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF)
408 			continue;
409 
410 		ret = drm_fb_helper_restore_fbdev_mode(helper);
411 		if (ret)
412 			error = true;
413 	}
414 	return error;
415 }
416 #endif
417 
418 #if 0
419 int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
420 			void *panic_str)
421 {
422 	printf("panic occurred, switching back to text console\n");
423 	return drm_fb_helper_force_kernel_mode();
424 	return 0;
425 }
426 
427 static struct notifier_block paniced = {
428 	.notifier_call = drm_fb_helper_panic,
429 };
430 
431 /**
432  * drm_fb_helper_restore - restore the framebuffer console (kernel) config
433  *
434  * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
435  */
436 void drm_fb_helper_restore(void)
437 {
438 	bool ret;
439 	ret = drm_fb_helper_force_kernel_mode();
440 	if (ret == true)
441 		DRM_ERROR("Failed to restore crtc configuration\n");
442 }
443 
444 #ifdef CONFIG_MAGIC_SYSRQ
445 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
446 {
447 	drm_fb_helper_restore();
448 }
449 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
450 
451 static void drm_fb_helper_sysrq(int dummy1)
452 {
453 	schedule_work(&drm_fb_helper_restore_work);
454 }
455 
456 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
457 	.handler = drm_fb_helper_sysrq,
458 	.help_msg = "force-fb(V)",
459 	.action_msg = "Restore framebuffer console",
460 };
461 #else
462 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
463 #endif
464 #endif
465 
466 #if 0
467 static void drm_fb_helper_on(struct fb_info *info)
468 {
469 	struct drm_fb_helper *fb_helper = info->par;
470 	struct drm_device *dev = fb_helper->dev;
471 	struct drm_crtc *crtc;
472 	struct drm_crtc_helper_funcs *crtc_funcs;
473 	struct drm_connector *connector;
474 	struct drm_encoder *encoder;
475 	int i, j;
476 
477 	/*
478 	 * For each CRTC in this fb, turn the crtc on then,
479 	 * find all associated encoders and turn them on.
480 	 */
481 	sx_xlock(&dev->mode_config.mutex);
482 	for (i = 0; i < fb_helper->crtc_count; i++) {
483 		crtc = fb_helper->crtc_info[i].mode_set.crtc;
484 		crtc_funcs = crtc->helper_private;
485 
486 		if (!crtc->enabled)
487 			continue;
488 
489 		crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
490 
491 		/* Walk the connectors & encoders on this fb turning them on */
492 		for (j = 0; j < fb_helper->connector_count; j++) {
493 			connector = fb_helper->connector_info[j]->connector;
494 			connector->dpms = DRM_MODE_DPMS_ON;
495 			drm_connector_property_set_value(connector,
496 							 dev->mode_config.dpms_property,
497 							 DRM_MODE_DPMS_ON);
498 		}
499 		/* Found a CRTC on this fb, now find encoders */
500 		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
501 			if (encoder->crtc == crtc) {
502 				struct drm_encoder_helper_funcs *encoder_funcs;
503 
504 				encoder_funcs = encoder->helper_private;
505 				encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
506 			}
507 		}
508 	}
509 	sx_xunlock(&dev->mode_config.mutex);
510 }
511 #endif
512 
513 #if 0
514 static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
515 {
516 	struct drm_fb_helper *fb_helper = info->par;
517 	struct drm_device *dev = fb_helper->dev;
518 	struct drm_crtc *crtc;
519 	struct drm_crtc_helper_funcs *crtc_funcs;
520 	struct drm_connector *connector;
521 	struct drm_encoder *encoder;
522 	int i, j;
523 
524 	/*
525 	 * For each CRTC in this fb, find all associated encoders
526 	 * and turn them off, then turn off the CRTC.
527 	 */
528 	sx_xlock(&dev->mode_config.mutex);
529 	for (i = 0; i < fb_helper->crtc_count; i++) {
530 		crtc = fb_helper->crtc_info[i].mode_set.crtc;
531 		crtc_funcs = crtc->helper_private;
532 
533 		if (!crtc->enabled)
534 			continue;
535 
536 		/* Walk the connectors on this fb and mark them off */
537 		for (j = 0; j < fb_helper->connector_count; j++) {
538 			connector = fb_helper->connector_info[j]->connector;
539 			connector->dpms = dpms_mode;
540 			drm_connector_property_set_value(connector,
541 							 dev->mode_config.dpms_property,
542 							 dpms_mode);
543 		}
544 		/* Found a CRTC on this fb, now find encoders */
545 		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
546 			if (encoder->crtc == crtc) {
547 				struct drm_encoder_helper_funcs *encoder_funcs;
548 
549 				encoder_funcs = encoder->helper_private;
550 				encoder_funcs->dpms(encoder, dpms_mode);
551 			}
552 		}
553 		crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
554 	}
555 	sx_xunlock(&dev->mode_config.mutex);
556 }
557 #endif
558 
559 #if 0
560 int drm_fb_helper_blank(int blank, struct fb_info *info)
561 {
562 	switch (blank) {
563 	/* Display: On; HSync: On, VSync: On */
564 	case FB_BLANK_UNBLANK:
565 		drm_fb_helper_on(info);
566 		break;
567 	/* Display: Off; HSync: On, VSync: On */
568 	case FB_BLANK_NORMAL:
569 		drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
570 		break;
571 	/* Display: Off; HSync: Off, VSync: On */
572 	case FB_BLANK_HSYNC_SUSPEND:
573 		drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
574 		break;
575 	/* Display: Off; HSync: On, VSync: Off */
576 	case FB_BLANK_VSYNC_SUSPEND:
577 		drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
578 		break;
579 	/* Display: Off; HSync: Off, VSync: Off */
580 	case FB_BLANK_POWERDOWN:
581 		drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
582 		break;
583 	}
584 	return 0;
585 }
586 #endif
587 
588 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
589 {
590 	int i;
591 
592 	for (i = 0; i < helper->connector_count; i++)
593 		free(helper->connector_info[i], DRM_MEM_KMS);
594 	free(helper->connector_info, DRM_MEM_KMS);
595 	for (i = 0; i < helper->crtc_count; i++) {
596 		free(helper->crtc_info[i].mode_set.connectors, DRM_MEM_KMS);
597 		if (helper->crtc_info[i].mode_set.mode)
598 			drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
599 	}
600 	free(helper->crtc_info, DRM_MEM_KMS);
601 }
602 
603 int drm_fb_helper_init(struct drm_device *dev,
604 		       struct drm_fb_helper *fb_helper,
605 		       int crtc_count, int max_conn_count)
606 {
607 	struct drm_crtc *crtc;
608 	int i;
609 
610 	fb_helper->dev = dev;
611 
612 	INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
613 
614 	fb_helper->crtc_info = malloc(crtc_count *
615 	    sizeof(struct drm_fb_helper_crtc), DRM_MEM_KMS, M_WAITOK | M_ZERO);
616 
617 	fb_helper->crtc_count = crtc_count;
618 	fb_helper->connector_info = malloc(dev->mode_config.num_connector *
619 	    sizeof(struct drm_fb_helper_connector *), DRM_MEM_KMS,
620 	    M_WAITOK | M_ZERO);
621 	fb_helper->connector_count = 0;
622 
623 	for (i = 0; i < crtc_count; i++) {
624 		fb_helper->crtc_info[i].mode_set.connectors =
625 			malloc(max_conn_count * sizeof(struct drm_connector *),
626 			    DRM_MEM_KMS, M_WAITOK | M_ZERO);
627 
628 		fb_helper->crtc_info[i].mode_set.num_connectors = 0;
629 	}
630 
631 	i = 0;
632 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
633 		fb_helper->crtc_info[i].crtc_id = crtc->base.id;
634 		fb_helper->crtc_info[i].mode_set.crtc = crtc;
635 		i++;
636 	}
637 	fb_helper->conn_limit = max_conn_count;
638 	return 0;
639 }
640 
641 void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
642 {
643 	if (!list_empty(&fb_helper->kernel_fb_list)) {
644 		list_del(&fb_helper->kernel_fb_list);
645 		if (list_empty(&kernel_fb_helper_list)) {
646 #if 0
647 			printk(KERN_INFO "drm: unregistered panic notifier\n");
648 			atomic_notifier_chain_unregister(&panic_notifier_list,
649 							 &paniced);
650 			unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
651 #endif
652 		}
653 	}
654 
655 	drm_fb_helper_crtc_free(fb_helper);
656 
657 }
658 
659 #if 0
660 static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
661 		     u16 blue, u16 regno, struct fb_info *info)
662 {
663 	struct drm_fb_helper *fb_helper = info->par;
664 	struct drm_framebuffer *fb = fb_helper->fb;
665 	int pindex;
666 
667 	if (info->fix.visual == FB_VISUAL_trueCOLOR) {
668 		u32 *palette;
669 		u32 value;
670 		/* place color in psuedopalette */
671 		if (regno > 16)
672 			return -EINVAL;
673 		palette = (u32 *)info->pseudo_palette;
674 		red >>= (16 - info->var.red.length);
675 		green >>= (16 - info->var.green.length);
676 		blue >>= (16 - info->var.blue.length);
677 		value = (red << info->var.red.offset) |
678 			(green << info->var.green.offset) |
679 			(blue << info->var.blue.offset);
680 		if (info->var.transp.length > 0) {
681 			u32 mask = (1 << info->var.transp.length) - 1;
682 			mask <<= info->var.transp.offset;
683 			value |= mask;
684 		}
685 		palette[regno] = value;
686 		return 0;
687 	}
688 
689 	pindex = regno;
690 
691 	if (fb->bits_per_pixel == 16) {
692 		pindex = regno << 3;
693 
694 		if (fb->depth == 16 && regno > 63)
695 			return -EINVAL;
696 		if (fb->depth == 15 && regno > 31)
697 			return -EINVAL;
698 
699 		if (fb->depth == 16) {
700 			u16 r, g, b;
701 			int i;
702 			if (regno < 32) {
703 				for (i = 0; i < 8; i++)
704 					fb_helper->funcs->gamma_set(crtc, red,
705 						green, blue, pindex + i);
706 			}
707 
708 			fb_helper->funcs->gamma_get(crtc, &r,
709 						    &g, &b,
710 						    pindex >> 1);
711 
712 			for (i = 0; i < 4; i++)
713 				fb_helper->funcs->gamma_set(crtc, r,
714 							    green, b,
715 							    (pindex >> 1) + i);
716 		}
717 	}
718 
719 	if (fb->depth != 16)
720 		fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
721 	return 0;
722 }
723 #endif
724 
725 #if 0
726 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
727 {
728 	struct drm_fb_helper *fb_helper = info->par;
729 	struct drm_crtc_helper_funcs *crtc_funcs;
730 	u16 *red, *green, *blue, *transp;
731 	struct drm_crtc *crtc;
732 	int i, j, rc = 0;
733 	int start;
734 
735 	for (i = 0; i < fb_helper->crtc_count; i++) {
736 		crtc = fb_helper->crtc_info[i].mode_set.crtc;
737 		crtc_funcs = crtc->helper_private;
738 
739 		red = cmap->red;
740 		green = cmap->green;
741 		blue = cmap->blue;
742 		transp = cmap->transp;
743 		start = cmap->start;
744 
745 		for (j = 0; j < cmap->len; j++) {
746 			u16 hred, hgreen, hblue, htransp = 0xffff;
747 
748 			hred = *red++;
749 			hgreen = *green++;
750 			hblue = *blue++;
751 
752 			if (transp)
753 				htransp = *transp++;
754 
755 			rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
756 			if (rc)
757 				return rc;
758 		}
759 		crtc_funcs->load_lut(crtc);
760 	}
761 	return rc;
762 }
763 #endif
764 
765 #if 0
766 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
767 			    struct fb_info *info)
768 {
769 	struct drm_fb_helper *fb_helper = info->par;
770 	struct drm_framebuffer *fb = fb_helper->fb;
771 	int depth;
772 
773 	if (var->pixclock != 0 || in_dbg_master())
774 		return -EINVAL;
775 
776 	/* Need to resize the fb object !!! */
777 	if (var->bits_per_pixel > fb->bits_per_pixel ||
778 	    var->xres > fb->width || var->yres > fb->height ||
779 	    var->xres_virtual > fb->width || var->yres_virtual > fb->height) {
780 		DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
781 			  "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n",
782 			  var->xres, var->yres, var->bits_per_pixel,
783 			  var->xres_virtual, var->yres_virtual,
784 			  fb->width, fb->height, fb->bits_per_pixel);
785 		return -EINVAL;
786 	}
787 
788 	switch (var->bits_per_pixel) {
789 	case 16:
790 		depth = (var->green.length == 6) ? 16 : 15;
791 		break;
792 	case 32:
793 		depth = (var->transp.length > 0) ? 32 : 24;
794 		break;
795 	default:
796 		depth = var->bits_per_pixel;
797 		break;
798 	}
799 
800 	switch (depth) {
801 	case 8:
802 		var->red.offset = 0;
803 		var->green.offset = 0;
804 		var->blue.offset = 0;
805 		var->red.length = 8;
806 		var->green.length = 8;
807 		var->blue.length = 8;
808 		var->transp.length = 0;
809 		var->transp.offset = 0;
810 		break;
811 	case 15:
812 		var->red.offset = 10;
813 		var->green.offset = 5;
814 		var->blue.offset = 0;
815 		var->red.length = 5;
816 		var->green.length = 5;
817 		var->blue.length = 5;
818 		var->transp.length = 1;
819 		var->transp.offset = 15;
820 		break;
821 	case 16:
822 		var->red.offset = 11;
823 		var->green.offset = 5;
824 		var->blue.offset = 0;
825 		var->red.length = 5;
826 		var->green.length = 6;
827 		var->blue.length = 5;
828 		var->transp.length = 0;
829 		var->transp.offset = 0;
830 		break;
831 	case 24:
832 		var->red.offset = 16;
833 		var->green.offset = 8;
834 		var->blue.offset = 0;
835 		var->red.length = 8;
836 		var->green.length = 8;
837 		var->blue.length = 8;
838 		var->transp.length = 0;
839 		var->transp.offset = 0;
840 		break;
841 	case 32:
842 		var->red.offset = 16;
843 		var->green.offset = 8;
844 		var->blue.offset = 0;
845 		var->red.length = 8;
846 		var->green.length = 8;
847 		var->blue.length = 8;
848 		var->transp.length = 8;
849 		var->transp.offset = 24;
850 		break;
851 	default:
852 		return -EINVAL;
853 	}
854 	return 0;
855 }
856 #endif
857 
858 #if 0
859 /* this will let fbcon do the mode init */
860 int drm_fb_helper_set_par(struct fb_info *info)
861 {
862 	struct drm_fb_helper *fb_helper = info->par;
863 	struct drm_device *dev = fb_helper->dev;
864 	struct fb_var_screeninfo *var = &info->var;
865 	struct drm_crtc *crtc;
866 	int ret;
867 	int i;
868 
869 	if (var->pixclock != 0) {
870 		DRM_ERROR("PIXEL CLOCK SET\n");
871 		return -EINVAL;
872 	}
873 
874 	mutex_lock(&dev->mode_config.mutex);
875 	for (i = 0; i < fb_helper->crtc_count; i++) {
876 		crtc = fb_helper->crtc_info[i].mode_set.crtc;
877 		ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
878 		if (ret) {
879 			mutex_unlock(&dev->mode_config.mutex);
880 			return ret;
881 		}
882 	}
883 	mutex_unlock(&dev->mode_config.mutex);
884 
885 	if (fb_helper->delayed_hotplug) {
886 		fb_helper->delayed_hotplug = false;
887 		drm_fb_helper_hotplug_event(fb_helper);
888 	}
889 	return 0;
890 }
891 #endif
892 
893 #if 0
894 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
895 			      struct fb_info *info)
896 {
897 	struct drm_fb_helper *fb_helper = info->par;
898 	struct drm_device *dev = fb_helper->dev;
899 	struct drm_mode_set *modeset;
900 	struct drm_crtc *crtc;
901 	int ret = 0;
902 	int i;
903 
904 	mutex_lock(&dev->mode_config.mutex);
905 	for (i = 0; i < fb_helper->crtc_count; i++) {
906 		crtc = fb_helper->crtc_info[i].mode_set.crtc;
907 
908 		modeset = &fb_helper->crtc_info[i].mode_set;
909 
910 		modeset->x = var->xoffset;
911 		modeset->y = var->yoffset;
912 
913 		if (modeset->num_connectors) {
914 			ret = crtc->funcs->set_config(modeset);
915 			if (!ret) {
916 				info->var.xoffset = var->xoffset;
917 				info->var.yoffset = var->yoffset;
918 			}
919 		}
920 	}
921 	mutex_unlock(&dev->mode_config.mutex);
922 	return ret;
923 }
924 #endif
925 
926 int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
927 				  int preferred_bpp)
928 {
929 	int new_fb = 0;
930 	int crtc_count = 0;
931 	int i;
932 	struct fb_info *info;
933 	struct drm_fb_helper_surface_size sizes;
934 	int gamma_size = 0;
935 	struct vt_kms_softc *sc;
936 	device_t kdev;
937 
938 	memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
939 	sizes.surface_depth = 24;
940 	sizes.surface_bpp = 32;
941 	sizes.fb_width = (unsigned)-1;
942 	sizes.fb_height = (unsigned)-1;
943 
944 	/* if driver picks 8 or 16 by default use that
945 	   for both depth/bpp */
946 	if (preferred_bpp != sizes.surface_bpp) {
947 		sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
948 	}
949 	/* first up get a count of crtcs now in use and new min/maxes width/heights */
950 	for (i = 0; i < fb_helper->connector_count; i++) {
951 		struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
952 		struct drm_fb_helper_cmdline_mode *cmdline_mode;
953 
954 		cmdline_mode = &fb_helper_conn->cmdline_mode;
955 
956 		if (cmdline_mode->bpp_specified) {
957 			switch (cmdline_mode->bpp) {
958 			case 8:
959 				sizes.surface_depth = sizes.surface_bpp = 8;
960 				break;
961 			case 15:
962 				sizes.surface_depth = 15;
963 				sizes.surface_bpp = 16;
964 				break;
965 			case 16:
966 				sizes.surface_depth = sizes.surface_bpp = 16;
967 				break;
968 			case 24:
969 				sizes.surface_depth = sizes.surface_bpp = 24;
970 				break;
971 			case 32:
972 				sizes.surface_depth = 24;
973 				sizes.surface_bpp = 32;
974 				break;
975 			}
976 			break;
977 		}
978 	}
979 
980 	crtc_count = 0;
981 	for (i = 0; i < fb_helper->crtc_count; i++) {
982 		struct drm_display_mode *desired_mode;
983 		desired_mode = fb_helper->crtc_info[i].desired_mode;
984 
985 		if (desired_mode) {
986 			if (gamma_size == 0)
987 				gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
988 			if (desired_mode->hdisplay < sizes.fb_width)
989 				sizes.fb_width = desired_mode->hdisplay;
990 			if (desired_mode->vdisplay < sizes.fb_height)
991 				sizes.fb_height = desired_mode->vdisplay;
992 			if (desired_mode->hdisplay > sizes.surface_width)
993 				sizes.surface_width = desired_mode->hdisplay;
994 			if (desired_mode->vdisplay > sizes.surface_height)
995 				sizes.surface_height = desired_mode->vdisplay;
996 			crtc_count++;
997 		}
998 	}
999 
1000 	if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
1001 		/* hmm everyone went away - assume VGA cable just fell out
1002 		   and will come back later. */
1003 		DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n");
1004 		sizes.fb_width = sizes.surface_width = 1024;
1005 		sizes.fb_height = sizes.surface_height = 768;
1006 	}
1007 
1008 	/* push down into drivers */
1009 	new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
1010 	if (new_fb < 0)
1011 		return new_fb;
1012 
1013 	sc = malloc(sizeof(struct vt_kms_softc), DRM_MEM_KMS,
1014 	    M_WAITOK | M_ZERO);
1015 	sc->fb_helper = fb_helper;
1016 	TASK_INIT(&sc->fb_mode_task, 0, vt_restore_fbdev_mode, sc);
1017 
1018 	info = fb_helper->fbdev;
1019 
1020 	info->fb_name = device_get_nameunit(fb_helper->dev->device);
1021 	info->fb_depth = fb_helper->fb->bits_per_pixel;
1022 	info->fb_height = fb_helper->fb->height;
1023 	info->fb_width = fb_helper->fb->width;
1024 	info->fb_stride = fb_helper->fb->pitches[0];
1025 	info->fb_priv = sc;
1026 	info->enter = &vt_kms_postswitch;
1027 
1028 	/* set the fb pointer */
1029 	for (i = 0; i < fb_helper->crtc_count; i++) {
1030 		fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
1031 	}
1032 
1033 	if (new_fb) {
1034 		device_t fbd;
1035 		int ret;
1036 
1037 		kdev = fb_helper->dev->device;
1038 		fbd = device_add_child(kdev, "fbd", device_get_unit(kdev));
1039 		if (fbd != NULL)
1040 			ret = device_probe_and_attach(fbd);
1041 		else
1042 			ret = ENODEV;
1043 #ifdef DEV_VT
1044 		if (ret != 0)
1045 			DRM_ERROR("Failed to attach fbd device: %d\n", ret);
1046 #endif
1047 	}
1048 	return 0;
1049 }
1050 
1051 #if 0
1052 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
1053 			    uint32_t depth)
1054 {
1055 	info->fix.type = FB_TYPE_PACKED_PIXELS;
1056 	info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
1057 		FB_VISUAL_trueCOLOR;
1058 	info->fix.mmio_start = 0;
1059 	info->fix.mmio_len = 0;
1060 	info->fix.type_aux = 0;
1061 	info->fix.xpanstep = 1; /* doing it in hw */
1062 	info->fix.ypanstep = 1; /* doing it in hw */
1063 	info->fix.ywrapstep = 0;
1064 	info->fix.accel = FB_ACCEL_NONE;
1065 	info->fix.type_aux = 0;
1066 
1067 	info->fix.line_length = pitch;
1068 	return;
1069 }
1070 
1071 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
1072 			    uint32_t fb_width, uint32_t fb_height)
1073 {
1074 	struct drm_framebuffer *fb = fb_helper->fb;
1075 	info->pseudo_palette = fb_helper->pseudo_palette;
1076 	info->var.xres_virtual = fb->width;
1077 	info->var.yres_virtual = fb->height;
1078 	info->var.bits_per_pixel = fb->bits_per_pixel;
1079 	info->var.accel_flags = FB_ACCELF_TEXT;
1080 	info->var.xoffset = 0;
1081 	info->var.yoffset = 0;
1082 	info->var.activate = FB_ACTIVATE_NOW;
1083 	info->var.height = -1;
1084 	info->var.width = -1;
1085 
1086 	switch (fb->depth) {
1087 	case 8:
1088 		info->var.red.offset = 0;
1089 		info->var.green.offset = 0;
1090 		info->var.blue.offset = 0;
1091 		info->var.red.length = 8; /* 8bit DAC */
1092 		info->var.green.length = 8;
1093 		info->var.blue.length = 8;
1094 		info->var.transp.offset = 0;
1095 		info->var.transp.length = 0;
1096 		break;
1097 	case 15:
1098 		info->var.red.offset = 10;
1099 		info->var.green.offset = 5;
1100 		info->var.blue.offset = 0;
1101 		info->var.red.length = 5;
1102 		info->var.green.length = 5;
1103 		info->var.blue.length = 5;
1104 		info->var.transp.offset = 15;
1105 		info->var.transp.length = 1;
1106 		break;
1107 	case 16:
1108 		info->var.red.offset = 11;
1109 		info->var.green.offset = 5;
1110 		info->var.blue.offset = 0;
1111 		info->var.red.length = 5;
1112 		info->var.green.length = 6;
1113 		info->var.blue.length = 5;
1114 		info->var.transp.offset = 0;
1115 		break;
1116 	case 24:
1117 		info->var.red.offset = 16;
1118 		info->var.green.offset = 8;
1119 		info->var.blue.offset = 0;
1120 		info->var.red.length = 8;
1121 		info->var.green.length = 8;
1122 		info->var.blue.length = 8;
1123 		info->var.transp.offset = 0;
1124 		info->var.transp.length = 0;
1125 		break;
1126 	case 32:
1127 		info->var.red.offset = 16;
1128 		info->var.green.offset = 8;
1129 		info->var.blue.offset = 0;
1130 		info->var.red.length = 8;
1131 		info->var.green.length = 8;
1132 		info->var.blue.length = 8;
1133 		info->var.transp.offset = 24;
1134 		info->var.transp.length = 8;
1135 		break;
1136 	default:
1137 		break;
1138 	}
1139 
1140 	info->var.xres = fb_width;
1141 	info->var.yres = fb_height;
1142 }
1143 #endif
1144 
1145 static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
1146 					       uint32_t maxX,
1147 					       uint32_t maxY)
1148 {
1149 	struct drm_connector *connector;
1150 	int count = 0;
1151 	int i;
1152 
1153 	for (i = 0; i < fb_helper->connector_count; i++) {
1154 		connector = fb_helper->connector_info[i]->connector;
1155 		count += connector->funcs->fill_modes(connector, maxX, maxY);
1156 	}
1157 
1158 	return count;
1159 }
1160 
1161 static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
1162 {
1163 	struct drm_display_mode *mode;
1164 
1165 	list_for_each_entry(mode, &fb_connector->connector->modes, head) {
1166 		if (drm_mode_width(mode) > width ||
1167 		    drm_mode_height(mode) > height)
1168 			continue;
1169 		if (mode->type & DRM_MODE_TYPE_PREFERRED)
1170 			return mode;
1171 	}
1172 	return NULL;
1173 }
1174 
1175 static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
1176 {
1177 	struct drm_fb_helper_cmdline_mode *cmdline_mode;
1178 	cmdline_mode = &fb_connector->cmdline_mode;
1179 	return cmdline_mode->specified;
1180 }
1181 
1182 static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
1183 						      int width, int height)
1184 {
1185 	struct drm_cmdline_mode *cmdline_mode;
1186 	struct drm_display_mode *mode = NULL;
1187 
1188 	cmdline_mode = &fb_helper_conn->cmdline_mode1;
1189 	if (cmdline_mode->specified == false &&
1190 	    !drm_fetch_cmdline_mode_from_kenv(fb_helper_conn->connector,
1191 	    cmdline_mode))
1192 			return (NULL);
1193 
1194 	/* attempt to find a matching mode in the list of modes
1195 	 *  we have gotten so far, if not add a CVT mode that conforms
1196 	 */
1197 	if (cmdline_mode->rb || cmdline_mode->margins)
1198 		goto create_mode;
1199 
1200 	list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1201 		/* check width/height */
1202 		if (mode->hdisplay != cmdline_mode->xres ||
1203 		    mode->vdisplay != cmdline_mode->yres)
1204 			continue;
1205 
1206 		if (cmdline_mode->refresh_specified) {
1207 			if (mode->vrefresh != cmdline_mode->refresh)
1208 				continue;
1209 		}
1210 
1211 		if (cmdline_mode->interlace) {
1212 			if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
1213 				continue;
1214 		}
1215 		return mode;
1216 	}
1217 
1218 create_mode:
1219 	if (cmdline_mode->cvt)
1220 		mode = drm_cvt_mode(fb_helper_conn->connector->dev,
1221 				    cmdline_mode->xres, cmdline_mode->yres,
1222 				    cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60,
1223 				    cmdline_mode->rb, cmdline_mode->interlace,
1224 				    cmdline_mode->margins);
1225 	else
1226 		mode = drm_gtf_mode(fb_helper_conn->connector->dev,
1227 				    cmdline_mode->xres, cmdline_mode->yres,
1228 				    cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60,
1229 				    cmdline_mode->interlace,
1230 				    cmdline_mode->margins);
1231 	drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
1232 	list_add(&mode->head, &fb_helper_conn->connector->modes);
1233 	return mode;
1234 }
1235 
1236 static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
1237 {
1238 	bool enable;
1239 
1240 	if (strict) {
1241 		enable = connector->status == connector_status_connected;
1242 	} else {
1243 		enable = connector->status != connector_status_disconnected;
1244 	}
1245 	return enable;
1246 }
1247 
1248 static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
1249 				  bool *enabled)
1250 {
1251 	bool any_enabled = false;
1252 	struct drm_connector *connector;
1253 	int i = 0;
1254 
1255 	for (i = 0; i < fb_helper->connector_count; i++) {
1256 		connector = fb_helper->connector_info[i]->connector;
1257 		enabled[i] = drm_connector_enabled(connector, true);
1258 		DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
1259 			  enabled[i] ? "yes" : "no");
1260 		any_enabled |= enabled[i];
1261 	}
1262 
1263 	if (any_enabled)
1264 		return;
1265 
1266 	for (i = 0; i < fb_helper->connector_count; i++) {
1267 		connector = fb_helper->connector_info[i]->connector;
1268 		enabled[i] = drm_connector_enabled(connector, false);
1269 	}
1270 }
1271 
1272 static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
1273 			      struct drm_display_mode **modes,
1274 			      bool *enabled, int width, int height)
1275 {
1276 	int count, i, j;
1277 	bool can_clone = false;
1278 	struct drm_fb_helper_connector *fb_helper_conn;
1279 	struct drm_display_mode *dmt_mode, *mode;
1280 
1281 	/* only contemplate cloning in the single crtc case */
1282 	if (fb_helper->crtc_count > 1)
1283 		return false;
1284 
1285 	count = 0;
1286 	for (i = 0; i < fb_helper->connector_count; i++) {
1287 		if (enabled[i])
1288 			count++;
1289 	}
1290 
1291 	/* only contemplate cloning if more than one connector is enabled */
1292 	if (count <= 1)
1293 		return false;
1294 
1295 	/* check the command line or if nothing common pick 1024x768 */
1296 	can_clone = true;
1297 	for (i = 0; i < fb_helper->connector_count; i++) {
1298 		if (!enabled[i])
1299 			continue;
1300 		fb_helper_conn = fb_helper->connector_info[i];
1301 		modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1302 		if (!modes[i]) {
1303 			can_clone = false;
1304 			break;
1305 		}
1306 		for (j = 0; j < i; j++) {
1307 			if (!enabled[j])
1308 				continue;
1309 			if (!drm_mode_equal(modes[j], modes[i]))
1310 				can_clone = false;
1311 		}
1312 	}
1313 
1314 	if (can_clone) {
1315 		DRM_DEBUG_KMS("can clone using command line\n");
1316 		return true;
1317 	}
1318 
1319 	/* try and find a 1024x768 mode on each connector */
1320 	can_clone = true;
1321 	dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60);
1322 
1323 	for (i = 0; i < fb_helper->connector_count; i++) {
1324 
1325 		if (!enabled[i])
1326 			continue;
1327 
1328 		fb_helper_conn = fb_helper->connector_info[i];
1329 		list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1330 			if (drm_mode_equal(mode, dmt_mode))
1331 				modes[i] = mode;
1332 		}
1333 		if (!modes[i])
1334 			can_clone = false;
1335 	}
1336 
1337 	if (can_clone) {
1338 		DRM_DEBUG_KMS("can clone using 1024x768\n");
1339 		return true;
1340 	}
1341 	DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
1342 	return false;
1343 }
1344 
1345 static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
1346 				 struct drm_display_mode **modes,
1347 				 bool *enabled, int width, int height)
1348 {
1349 	struct drm_fb_helper_connector *fb_helper_conn;
1350 	int i;
1351 
1352 	for (i = 0; i < fb_helper->connector_count; i++) {
1353 		fb_helper_conn = fb_helper->connector_info[i];
1354 
1355 		if (enabled[i] == false)
1356 			continue;
1357 
1358 		DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
1359 			      fb_helper_conn->connector->base.id);
1360 
1361 		/* got for command line mode first */
1362 		modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1363 		if (!modes[i]) {
1364 			DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
1365 				      fb_helper_conn->connector->base.id);
1366 			modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
1367 		}
1368 		/* No preferred modes, pick one off the list */
1369 		if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
1370 			list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
1371 				break;
1372 		}
1373 		DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
1374 			  "none");
1375 	}
1376 	return true;
1377 }
1378 
1379 static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
1380 			  struct drm_fb_helper_crtc **best_crtcs,
1381 			  struct drm_display_mode **modes,
1382 			  int n, int width, int height)
1383 {
1384 	int c, o;
1385 	struct drm_device *dev = fb_helper->dev;
1386 	struct drm_connector *connector;
1387 	struct drm_connector_helper_funcs *connector_funcs;
1388 	struct drm_encoder *encoder;
1389 	struct drm_fb_helper_crtc *best_crtc;
1390 	int my_score, best_score, score;
1391 	struct drm_fb_helper_crtc **crtcs, *crtc;
1392 	struct drm_fb_helper_connector *fb_helper_conn;
1393 
1394 	if (n == fb_helper->connector_count)
1395 		return 0;
1396 
1397 	fb_helper_conn = fb_helper->connector_info[n];
1398 	connector = fb_helper_conn->connector;
1399 
1400 	best_crtcs[n] = NULL;
1401 	best_crtc = NULL;
1402 	best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
1403 	if (modes[n] == NULL)
1404 		return best_score;
1405 
1406 	crtcs = malloc(dev->mode_config.num_connector *
1407 	    sizeof(struct drm_fb_helper_crtc *), DRM_MEM_KMS,
1408 	    M_WAITOK | M_ZERO);
1409 
1410 	my_score = 1;
1411 	if (connector->status == connector_status_connected)
1412 		my_score++;
1413 	if (drm_has_cmdline_mode(fb_helper_conn))
1414 		my_score++;
1415 	if (drm_has_preferred_mode(fb_helper_conn, width, height))
1416 		my_score++;
1417 
1418 	connector_funcs = connector->helper_private;
1419 	encoder = connector_funcs->best_encoder(connector);
1420 	if (!encoder)
1421 		goto out;
1422 
1423 	/* select a crtc for this connector and then attempt to configure
1424 	   remaining connectors */
1425 	for (c = 0; c < fb_helper->crtc_count; c++) {
1426 		crtc = &fb_helper->crtc_info[c];
1427 
1428 		if ((encoder->possible_crtcs & (1 << c)) == 0) {
1429 			continue;
1430 		}
1431 
1432 		for (o = 0; o < n; o++)
1433 			if (best_crtcs[o] == crtc)
1434 				break;
1435 
1436 		if (o < n) {
1437 			/* ignore cloning unless only a single crtc */
1438 			if (fb_helper->crtc_count > 1)
1439 				continue;
1440 
1441 			if (!drm_mode_equal(modes[o], modes[n]))
1442 				continue;
1443 		}
1444 
1445 		crtcs[n] = crtc;
1446 		memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
1447 		score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
1448 						  width, height);
1449 		if (score > best_score) {
1450 			best_crtc = crtc;
1451 			best_score = score;
1452 			memcpy(best_crtcs, crtcs,
1453 			       dev->mode_config.num_connector *
1454 			       sizeof(struct drm_fb_helper_crtc *));
1455 		}
1456 	}
1457 out:
1458 	free(crtcs, DRM_MEM_KMS);
1459 	return best_score;
1460 }
1461 
1462 static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
1463 {
1464 	struct drm_device *dev = fb_helper->dev;
1465 	struct drm_fb_helper_crtc **crtcs;
1466 	struct drm_display_mode **modes;
1467 	struct drm_encoder *encoder;
1468 	struct drm_mode_set *modeset;
1469 	bool *enabled;
1470 	int width, height;
1471 	int i, ret;
1472 
1473 	DRM_DEBUG_KMS("\n");
1474 
1475 	width = dev->mode_config.max_width;
1476 	height = dev->mode_config.max_height;
1477 
1478 	/* clean out all the encoder/crtc combos */
1479 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
1480 		encoder->crtc = NULL;
1481 	}
1482 
1483 	crtcs = malloc(dev->mode_config.num_connector *
1484 	    sizeof(struct drm_fb_helper_crtc *), DRM_MEM_KMS,
1485 	    M_WAITOK | M_ZERO);
1486 	modes = malloc(dev->mode_config.num_connector *
1487 	    sizeof(struct drm_display_mode *), DRM_MEM_KMS,
1488 	    M_WAITOK | M_ZERO);
1489 	enabled = malloc(dev->mode_config.num_connector *
1490 	    sizeof(bool), DRM_MEM_KMS, M_WAITOK | M_ZERO);
1491 
1492 	drm_enable_connectors(fb_helper, enabled);
1493 
1494 	ret = drm_target_cloned(fb_helper, modes, enabled, width, height);
1495 	if (!ret) {
1496 		ret = drm_target_preferred(fb_helper, modes, enabled, width, height);
1497 		if (!ret)
1498 			DRM_ERROR("Unable to find initial modes\n");
1499 	}
1500 
1501 	DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height);
1502 
1503 	drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
1504 
1505 	/* need to set the modesets up here for use later */
1506 	/* fill out the connector<->crtc mappings into the modesets */
1507 	for (i = 0; i < fb_helper->crtc_count; i++) {
1508 		modeset = &fb_helper->crtc_info[i].mode_set;
1509 		modeset->num_connectors = 0;
1510 	}
1511 
1512 	for (i = 0; i < fb_helper->connector_count; i++) {
1513 		struct drm_display_mode *mode = modes[i];
1514 		struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
1515 		modeset = &fb_crtc->mode_set;
1516 
1517 		if (mode && fb_crtc) {
1518 			DRM_DEBUG_KMS("desired mode %s set on crtc %d\n",
1519 				      mode->name, fb_crtc->mode_set.crtc->base.id);
1520 			fb_crtc->desired_mode = mode;
1521 			if (modeset->mode)
1522 				drm_mode_destroy(dev, modeset->mode);
1523 			modeset->mode = drm_mode_duplicate(dev,
1524 							   fb_crtc->desired_mode);
1525 			modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
1526 		}
1527 	}
1528 
1529 	free(crtcs, DRM_MEM_KMS);
1530 	free(modes, DRM_MEM_KMS);
1531 	free(enabled, DRM_MEM_KMS);
1532 }
1533 
1534 /**
1535  * drm_helper_initial_config - setup a sane initial connector configuration
1536  * @dev: DRM device
1537  *
1538  * LOCKING:
1539  * Called at init time, must take mode config lock.
1540  *
1541  * Scan the CRTCs and connectors and try to put together an initial setup.
1542  * At the moment, this is a cloned configuration across all heads with
1543  * a new framebuffer object as the backing store.
1544  *
1545  * RETURNS:
1546  * Zero if everything went ok, nonzero otherwise.
1547  */
1548 bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
1549 {
1550 	struct drm_device *dev = fb_helper->dev;
1551 	int count = 0;
1552 
1553 	/* disable all the possible outputs/crtcs before entering KMS mode */
1554 	drm_helper_disable_unused_functions(fb_helper->dev);
1555 
1556 	drm_fb_helper_parse_command_line(fb_helper);
1557 
1558 	count = drm_fb_helper_probe_connector_modes(fb_helper,
1559 						    dev->mode_config.max_width,
1560 						    dev->mode_config.max_height);
1561 	/*
1562 	 * we shouldn't end up with no modes here.
1563 	 */
1564 	if (count == 0) {
1565 		printf("No connectors reported connected with modes\n");
1566 	}
1567 	drm_setup_crtcs(fb_helper);
1568 
1569 	return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
1570 }
1571 
1572 int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
1573 {
1574 	struct drm_device *dev = fb_helper->dev;
1575 	int count = 0;
1576 	u32 max_width, max_height, bpp_sel;
1577 	bool bound = false, crtcs_bound = false;
1578 	struct drm_crtc *crtc;
1579 
1580 	if (!fb_helper->fb)
1581 		return 0;
1582 
1583 	sx_xlock(&dev->mode_config.mutex);
1584 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
1585 		if (crtc->fb)
1586 			crtcs_bound = true;
1587 		if (crtc->fb == fb_helper->fb)
1588 			bound = true;
1589 	}
1590 
1591 	if (!bound && crtcs_bound) {
1592 		fb_helper->delayed_hotplug = true;
1593 		sx_xunlock(&dev->mode_config.mutex);
1594 		return 0;
1595 	}
1596 	DRM_DEBUG_KMS("\n");
1597 
1598 	max_width = fb_helper->fb->width;
1599 	max_height = fb_helper->fb->height;
1600 	bpp_sel = fb_helper->fb->bits_per_pixel;
1601 
1602 	count = drm_fb_helper_probe_connector_modes(fb_helper, max_width,
1603 						    max_height);
1604 	drm_setup_crtcs(fb_helper);
1605 	sx_xunlock(&dev->mode_config.mutex);
1606 
1607 	return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
1608 }
1609 
1610