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