1235783Skib/*
2235783Skib * Copyright (c) 2006-2009 Red Hat Inc.
3235783Skib * Copyright (c) 2006-2008 Intel Corporation
4235783Skib * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
5235783Skib *
6235783Skib * DRM framebuffer helper functions
7235783Skib *
8235783Skib * Permission to use, copy, modify, distribute, and sell this software and its
9235783Skib * documentation for any purpose is hereby granted without fee, provided that
10235783Skib * the above copyright notice appear in all copies and that both that copyright
11235783Skib * notice and this permission notice appear in supporting documentation, and
12235783Skib * that the name of the copyright holders not be used in advertising or
13235783Skib * publicity pertaining to distribution of the software without specific,
14235783Skib * written prior permission.  The copyright holders make no representations
15235783Skib * about the suitability of this software for any purpose.  It is provided "as
16235783Skib * is" without express or implied warranty.
17235783Skib *
18235783Skib * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19235783Skib * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
20235783Skib * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
21235783Skib * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
22235783Skib * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
23235783Skib * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24235783Skib * OF THIS SOFTWARE.
25235783Skib *
26235783Skib * Authors:
27235783Skib *      Dave Airlie <airlied@linux.ie>
28235783Skib *      Jesse Barnes <jesse.barnes@intel.com>
29235783Skib */
30235783Skib
31235783Skib#include <sys/cdefs.h>
32235783Skib__FBSDID("$FreeBSD$");
33235783Skib
34235783Skib#include <dev/drm2/drmP.h>
35235783Skib#include <dev/drm2/drm_crtc.h>
36235783Skib#include <dev/drm2/drm_fb_helper.h>
37235783Skib#include <dev/drm2/drm_crtc_helper.h>
38235783Skib
39271769Sdumbbell
40271769Sdumbbell#include <sys/kdb.h>
41271769Sdumbbell
42262861Sjhbstruct vt_kms_softc {
43262861Sjhb	struct drm_fb_helper *fb_helper;
44262861Sjhb	struct task	fb_mode_task;
45262861Sjhb};
46262861Sjhb
47262861Sjhbstatic fb_enter_t	vt_kms_postswitch;
48262861Sjhbstatic void vt_restore_fbdev_mode(void *, int);
49262861Sjhb
50262861Sjhb/* Call restore out of vt(9) locks. */
51262861Sjhbstatic void
52262861Sjhbvt_restore_fbdev_mode(void *arg, int pending)
53262861Sjhb{
54262861Sjhb	struct drm_fb_helper *fb_helper;
55262861Sjhb	struct vt_kms_softc *sc;
56262861Sjhb
57262861Sjhb	sc = (struct vt_kms_softc *)arg;
58262861Sjhb	fb_helper = sc->fb_helper;
59262861Sjhb	sx_xlock(&fb_helper->dev->mode_config.mutex);
60262861Sjhb	drm_fb_helper_restore_fbdev_mode(fb_helper);
61262861Sjhb	sx_xunlock(&fb_helper->dev->mode_config.mutex);
62262861Sjhb}
63262861Sjhb
64262861Sjhbstatic int
65262861Sjhbvt_kms_postswitch(void *arg)
66262861Sjhb{
67262861Sjhb	struct vt_kms_softc *sc;
68262861Sjhb
69262861Sjhb	sc = (struct vt_kms_softc *)arg;
70262861Sjhb
71271769Sdumbbell	if (!kdb_active && panicstr == NULL)
72271769Sdumbbell		taskqueue_enqueue_fast(taskqueue_thread, &sc->fb_mode_task);
73271769Sdumbbell	else
74271769Sdumbbell		drm_fb_helper_restore_fbdev_mode(sc->fb_helper);
75271769Sdumbbell
76262861Sjhb	return (0);
77262861Sjhb}
78262861Sjhb
79235783Skibstatic DRM_LIST_HEAD(kernel_fb_helper_list);
80235783Skib
81235783Skib/* simple single crtc case helper function */
82235783Skibint drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
83235783Skib{
84235783Skib	struct drm_device *dev = fb_helper->dev;
85235783Skib	struct drm_connector *connector;
86235783Skib
87235783Skib	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
88235783Skib		struct drm_fb_helper_connector *fb_helper_connector;
89235783Skib
90235783Skib		fb_helper_connector = malloc(
91235783Skib		    sizeof(struct drm_fb_helper_connector), DRM_MEM_KMS,
92235783Skib		    M_WAITOK | M_ZERO);
93235783Skib
94235783Skib		fb_helper_connector->connector = connector;
95235783Skib		fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
96235783Skib	}
97235783Skib	return 0;
98235783Skib}
99235783Skib
100235783Skibconst char *fb_mode_option;
101235783Skib
102235783Skib/**
103235783Skib * drm_fb_helper_connector_parse_command_line - parse command line for connector
104235783Skib * @connector - connector to parse line for
105235783Skib * @mode_option - per connector mode option
106235783Skib *
107235783Skib * This parses the connector specific then generic command lines for
108235783Skib * modes and options to configure the connector.
109235783Skib *
110235783Skib * This uses the same parameters as the fb modedb.c, except for extra
111235783Skib *	<xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
112235783Skib *
113235783Skib * enable/enable Digital/disable bit at the end
114235783Skib */
115235783Skibstatic bool drm_fb_helper_connector_parse_command_line(struct drm_fb_helper_connector *fb_helper_conn,
116235783Skib						       const char *mode_option)
117235783Skib{
118235783Skib	const char *name;
119235783Skib	unsigned int namelen;
120235783Skib	int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
121235783Skib	unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
122235783Skib	int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
123235783Skib	int i;
124235783Skib	enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
125235783Skib	struct drm_fb_helper_cmdline_mode *cmdline_mode;
126235783Skib	struct drm_connector *connector;
127235783Skib
128235783Skib	if (!fb_helper_conn)
129235783Skib		return false;
130235783Skib	connector = fb_helper_conn->connector;
131235783Skib
132235783Skib	cmdline_mode = &fb_helper_conn->cmdline_mode;
133235783Skib	if (!mode_option)
134235783Skib		mode_option = fb_mode_option;
135235783Skib
136235783Skib	if (!mode_option) {
137235783Skib		cmdline_mode->specified = false;
138235783Skib		return false;
139235783Skib	}
140235783Skib
141235783Skib	name = mode_option;
142235783Skib	namelen = strlen(name);
143235783Skib	for (i = namelen-1; i >= 0; i--) {
144235783Skib		switch (name[i]) {
145235783Skib		case '@':
146235783Skib			namelen = i;
147235783Skib			if (!refresh_specified && !bpp_specified &&
148235783Skib			    !yres_specified) {
149235783Skib				refresh = strtol(&name[i+1], NULL, 10);
150235783Skib				refresh_specified = 1;
151235783Skib				if (cvt || rb)
152235783Skib					cvt = 0;
153235783Skib			} else
154235783Skib				goto done;
155235783Skib			break;
156235783Skib		case '-':
157235783Skib			namelen = i;
158235783Skib			if (!bpp_specified && !yres_specified) {
159235783Skib				bpp = strtol(&name[i+1], NULL, 10);
160235783Skib				bpp_specified = 1;
161235783Skib				if (cvt || rb)
162235783Skib					cvt = 0;
163235783Skib			} else
164235783Skib				goto done;
165235783Skib			break;
166235783Skib		case 'x':
167235783Skib			if (!yres_specified) {
168235783Skib				yres = strtol(&name[i+1], NULL, 10);
169235783Skib				yres_specified = 1;
170235783Skib			} else
171235783Skib				goto done;
172235783Skib		case '0' ... '9':
173235783Skib			break;
174235783Skib		case 'M':
175235783Skib			if (!yres_specified)
176235783Skib				cvt = 1;
177235783Skib			break;
178235783Skib		case 'R':
179235783Skib			if (cvt)
180235783Skib				rb = 1;
181235783Skib			break;
182235783Skib		case 'm':
183235783Skib			if (!cvt)
184235783Skib				margins = 1;
185235783Skib			break;
186235783Skib		case 'i':
187235783Skib			if (!cvt)
188235783Skib				interlace = 1;
189235783Skib			break;
190235783Skib		case 'e':
191235783Skib			force = DRM_FORCE_ON;
192235783Skib			break;
193235783Skib		case 'D':
194235783Skib			if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
195235783Skib			    (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
196235783Skib				force = DRM_FORCE_ON;
197235783Skib			else
198235783Skib				force = DRM_FORCE_ON_DIGITAL;
199235783Skib			break;
200235783Skib		case 'd':
201235783Skib			force = DRM_FORCE_OFF;
202235783Skib			break;
203235783Skib		default:
204235783Skib			goto done;
205235783Skib		}
206235783Skib	}
207235783Skib	if (i < 0 && yres_specified) {
208235783Skib		xres = strtol(name, NULL, 10);
209235783Skib		res_specified = 1;
210235783Skib	}
211235783Skibdone:
212235783Skib
213235783Skib	DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
214235783Skib		drm_get_connector_name(connector), xres, yres,
215235783Skib		(refresh) ? refresh : 60, (rb) ? " reduced blanking" :
216235783Skib		"", (margins) ? " with margins" : "", (interlace) ?
217235783Skib		" interlaced" : "");
218235783Skib
219235783Skib	if (force) {
220235783Skib		const char *s;
221235783Skib		switch (force) {
222235783Skib		case DRM_FORCE_OFF: s = "OFF"; break;
223235783Skib		case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
224235783Skib		default:
225235783Skib		case DRM_FORCE_ON: s = "ON"; break;
226235783Skib		}
227235783Skib
228235783Skib		DRM_INFO("forcing %s connector %s\n",
229235783Skib			 drm_get_connector_name(connector), s);
230235783Skib		connector->force = force;
231235783Skib	}
232235783Skib
233235783Skib	if (res_specified) {
234235783Skib		cmdline_mode->specified = true;
235235783Skib		cmdline_mode->xres = xres;
236235783Skib		cmdline_mode->yres = yres;
237235783Skib	}
238235783Skib
239235783Skib	if (refresh_specified) {
240235783Skib		cmdline_mode->refresh_specified = true;
241235783Skib		cmdline_mode->refresh = refresh;
242235783Skib	}
243235783Skib
244235783Skib	if (bpp_specified) {
245235783Skib		cmdline_mode->bpp_specified = true;
246235783Skib		cmdline_mode->bpp = bpp;
247235783Skib	}
248235783Skib	cmdline_mode->rb = rb ? true : false;
249235783Skib	cmdline_mode->cvt = cvt  ? true : false;
250235783Skib	cmdline_mode->interlace = interlace ? true : false;
251235783Skib
252235783Skib	return true;
253235783Skib}
254235783Skib
255235783Skibstatic int
256235783Skibfb_get_options(const char *connector_name, char **option)
257235783Skib{
258235783Skib
259262861Sjhb	/*
260262861Sjhb	 * TODO: store mode options pointer in ${option} for connector with
261262861Sjhb	 * name ${connector_name}
262262861Sjhb	 */
263235783Skib	return (1);
264235783Skib}
265235783Skib
266235783Skibstatic int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
267235783Skib{
268235783Skib	struct drm_fb_helper_connector *fb_helper_conn;
269235783Skib	int i;
270235783Skib
271235783Skib	for (i = 0; i < fb_helper->connector_count; i++) {
272235783Skib		char *option = NULL;
273235783Skib
274235783Skib		fb_helper_conn = fb_helper->connector_info[i];
275235783Skib
276235783Skib		/* do something on return - turn off connector maybe */
277235783Skib		if (fb_get_options(drm_get_connector_name(fb_helper_conn->connector), &option))
278235783Skib			continue;
279235783Skib
280235783Skib		drm_fb_helper_connector_parse_command_line(fb_helper_conn, option);
281235783Skib	}
282235783Skib	return 0;
283235783Skib}
284235783Skib
285235783Skib#if 0
286235783Skibstatic void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper)
287235783Skib{
288235783Skib	uint16_t *r_base, *g_base, *b_base;
289235783Skib	int i;
290235783Skib
291235783Skib	r_base = crtc->gamma_store;
292235783Skib	g_base = r_base + crtc->gamma_size;
293235783Skib	b_base = g_base + crtc->gamma_size;
294235783Skib
295235783Skib	for (i = 0; i < crtc->gamma_size; i++)
296235783Skib		helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i);
297235783Skib}
298235783Skib
299235783Skibstatic void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
300235783Skib{
301235783Skib	uint16_t *r_base, *g_base, *b_base;
302235783Skib
303235783Skib	r_base = crtc->gamma_store;
304235783Skib	g_base = r_base + crtc->gamma_size;
305235783Skib	b_base = g_base + crtc->gamma_size;
306235783Skib
307235783Skib	crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
308235783Skib}
309235783Skib#endif
310235783Skib
311235783Skib#if 0
312235783Skibint drm_fb_helper_debug_enter(struct fb_info *info)
313235783Skib{
314235783Skib	struct drm_fb_helper *helper = info->par;
315235783Skib	struct drm_crtc_helper_funcs *funcs;
316235783Skib	int i;
317235783Skib
318235783Skib	if (list_empty(&kernel_fb_helper_list))
319235783Skib		return false;
320235783Skib
321235783Skib	list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
322235783Skib		for (i = 0; i < helper->crtc_count; i++) {
323235783Skib			struct drm_mode_set *mode_set =
324235783Skib				&helper->crtc_info[i].mode_set;
325235783Skib
326235783Skib			if (!mode_set->crtc->enabled)
327235783Skib				continue;
328235783Skib
329235783Skib			funcs =	mode_set->crtc->helper_private;
330235783Skib			drm_fb_helper_save_lut_atomic(mode_set->crtc, helper);
331235783Skib			funcs->mode_set_base_atomic(mode_set->crtc,
332235783Skib						    mode_set->fb,
333235783Skib						    mode_set->x,
334235783Skib						    mode_set->y,
335235783Skib						    ENTER_ATOMIC_MODE_SET);
336235783Skib		}
337235783Skib	}
338235783Skib
339235783Skib	return 0;
340235783Skib}
341235783Skib#endif
342235783Skib
343235783Skib#if 0
344235783Skib/* Find the real fb for a given fb helper CRTC */
345235783Skibstatic struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
346235783Skib{
347235783Skib	struct drm_device *dev = crtc->dev;
348235783Skib	struct drm_crtc *c;
349235783Skib
350235783Skib	list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
351235783Skib		if (crtc->base.id == c->base.id)
352235783Skib			return c->fb;
353235783Skib	}
354235783Skib
355235783Skib	return NULL;
356235783Skib}
357235783Skib#endif
358235783Skib
359235783Skib#if 0
360235783Skibint drm_fb_helper_debug_leave(struct fb_info *info)
361235783Skib{
362235783Skib	struct drm_fb_helper *helper = info->par;
363235783Skib	struct drm_crtc *crtc;
364235783Skib	struct drm_crtc_helper_funcs *funcs;
365235783Skib	struct drm_framebuffer *fb;
366235783Skib	int i;
367235783Skib
368235783Skib	for (i = 0; i < helper->crtc_count; i++) {
369235783Skib		struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
370235783Skib		crtc = mode_set->crtc;
371235783Skib		funcs = crtc->helper_private;
372235783Skib		fb = drm_mode_config_fb(crtc);
373235783Skib
374235783Skib		if (!crtc->enabled)
375235783Skib			continue;
376235783Skib
377235783Skib		if (!fb) {
378235783Skib			DRM_ERROR("no fb to restore??\n");
379235783Skib			continue;
380235783Skib		}
381235783Skib
382235783Skib		drm_fb_helper_restore_lut_atomic(mode_set->crtc);
383235783Skib		funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,
384235783Skib					    crtc->y, LEAVE_ATOMIC_MODE_SET);
385235783Skib	}
386235783Skib
387235783Skib	return 0;
388235783Skib}
389235783Skib#endif
390235783Skib
391235783Skibbool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
392235783Skib{
393235783Skib	bool error = false;
394235783Skib	int i, ret;
395235783Skib	for (i = 0; i < fb_helper->crtc_count; i++) {
396235783Skib		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
397235783Skib		ret = drm_crtc_helper_set_config(mode_set);
398235783Skib		if (ret)
399235783Skib			error = true;
400235783Skib	}
401235783Skib	return error;
402235783Skib}
403235783Skib
404235783Skib#if 0
405235783Skibbool drm_fb_helper_force_kernel_mode(void)
406235783Skib{
407235783Skib	bool ret, error = false;
408235783Skib	struct drm_fb_helper *helper;
409235783Skib
410235783Skib	if (list_empty(&kernel_fb_helper_list))
411235783Skib		return false;
412235783Skib
413235783Skib	list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
414235783Skib		if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF)
415235783Skib			continue;
416235783Skib
417235783Skib		ret = drm_fb_helper_restore_fbdev_mode(helper);
418235783Skib		if (ret)
419235783Skib			error = true;
420235783Skib	}
421235783Skib	return error;
422235783Skib}
423235783Skib#endif
424235783Skib
425235783Skib#if 0
426235783Skibint drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
427235783Skib			void *panic_str)
428235783Skib{
429235783Skib	printf("panic occurred, switching back to text console\n");
430235783Skib	return drm_fb_helper_force_kernel_mode();
431235783Skib	return 0;
432235783Skib}
433235783Skib
434235783Skibstatic struct notifier_block paniced = {
435235783Skib	.notifier_call = drm_fb_helper_panic,
436235783Skib};
437235783Skib
438235783Skib/**
439235783Skib * drm_fb_helper_restore - restore the framebuffer console (kernel) config
440235783Skib *
441235783Skib * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
442235783Skib */
443235783Skibvoid drm_fb_helper_restore(void)
444235783Skib{
445235783Skib	bool ret;
446235783Skib	ret = drm_fb_helper_force_kernel_mode();
447235783Skib	if (ret == true)
448235783Skib		DRM_ERROR("Failed to restore crtc configuration\n");
449235783Skib}
450235783Skib
451235783Skib#ifdef CONFIG_MAGIC_SYSRQ
452235783Skibstatic void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
453235783Skib{
454235783Skib	drm_fb_helper_restore();
455235783Skib}
456235783Skibstatic DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
457235783Skib
458235783Skibstatic void drm_fb_helper_sysrq(int dummy1)
459235783Skib{
460235783Skib	schedule_work(&drm_fb_helper_restore_work);
461235783Skib}
462235783Skib
463235783Skibstatic struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
464235783Skib	.handler = drm_fb_helper_sysrq,
465235783Skib	.help_msg = "force-fb(V)",
466235783Skib	.action_msg = "Restore framebuffer console",
467235783Skib};
468235783Skib#else
469235783Skibstatic struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
470235783Skib#endif
471235783Skib#endif
472235783Skib
473235783Skib#if 0
474235783Skibstatic void drm_fb_helper_on(struct fb_info *info)
475235783Skib{
476235783Skib	struct drm_fb_helper *fb_helper = info->par;
477235783Skib	struct drm_device *dev = fb_helper->dev;
478235783Skib	struct drm_crtc *crtc;
479235783Skib	struct drm_crtc_helper_funcs *crtc_funcs;
480235783Skib	struct drm_connector *connector;
481235783Skib	struct drm_encoder *encoder;
482235783Skib	int i, j;
483235783Skib
484235783Skib	/*
485235783Skib	 * For each CRTC in this fb, turn the crtc on then,
486235783Skib	 * find all associated encoders and turn them on.
487235783Skib	 */
488235783Skib	sx_xlock(&dev->mode_config.mutex);
489235783Skib	for (i = 0; i < fb_helper->crtc_count; i++) {
490235783Skib		crtc = fb_helper->crtc_info[i].mode_set.crtc;
491235783Skib		crtc_funcs = crtc->helper_private;
492235783Skib
493235783Skib		if (!crtc->enabled)
494235783Skib			continue;
495235783Skib
496235783Skib		crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
497235783Skib
498235783Skib		/* Walk the connectors & encoders on this fb turning them on */
499235783Skib		for (j = 0; j < fb_helper->connector_count; j++) {
500235783Skib			connector = fb_helper->connector_info[j]->connector;
501235783Skib			connector->dpms = DRM_MODE_DPMS_ON;
502235783Skib			drm_connector_property_set_value(connector,
503235783Skib							 dev->mode_config.dpms_property,
504235783Skib							 DRM_MODE_DPMS_ON);
505235783Skib		}
506235783Skib		/* Found a CRTC on this fb, now find encoders */
507235783Skib		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
508235783Skib			if (encoder->crtc == crtc) {
509235783Skib				struct drm_encoder_helper_funcs *encoder_funcs;
510235783Skib
511235783Skib				encoder_funcs = encoder->helper_private;
512235783Skib				encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
513235783Skib			}
514235783Skib		}
515235783Skib	}
516235783Skib	sx_xunlock(&dev->mode_config.mutex);
517235783Skib}
518235783Skib#endif
519235783Skib
520235783Skib#if 0
521235783Skibstatic void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
522235783Skib{
523235783Skib	struct drm_fb_helper *fb_helper = info->par;
524235783Skib	struct drm_device *dev = fb_helper->dev;
525235783Skib	struct drm_crtc *crtc;
526235783Skib	struct drm_crtc_helper_funcs *crtc_funcs;
527235783Skib	struct drm_connector *connector;
528235783Skib	struct drm_encoder *encoder;
529235783Skib	int i, j;
530235783Skib
531235783Skib	/*
532235783Skib	 * For each CRTC in this fb, find all associated encoders
533235783Skib	 * and turn them off, then turn off the CRTC.
534235783Skib	 */
535235783Skib	sx_xlock(&dev->mode_config.mutex);
536235783Skib	for (i = 0; i < fb_helper->crtc_count; i++) {
537235783Skib		crtc = fb_helper->crtc_info[i].mode_set.crtc;
538235783Skib		crtc_funcs = crtc->helper_private;
539235783Skib
540235783Skib		if (!crtc->enabled)
541235783Skib			continue;
542235783Skib
543235783Skib		/* Walk the connectors on this fb and mark them off */
544235783Skib		for (j = 0; j < fb_helper->connector_count; j++) {
545235783Skib			connector = fb_helper->connector_info[j]->connector;
546235783Skib			connector->dpms = dpms_mode;
547235783Skib			drm_connector_property_set_value(connector,
548235783Skib							 dev->mode_config.dpms_property,
549235783Skib							 dpms_mode);
550235783Skib		}
551235783Skib		/* Found a CRTC on this fb, now find encoders */
552235783Skib		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
553235783Skib			if (encoder->crtc == crtc) {
554235783Skib				struct drm_encoder_helper_funcs *encoder_funcs;
555235783Skib
556235783Skib				encoder_funcs = encoder->helper_private;
557235783Skib				encoder_funcs->dpms(encoder, dpms_mode);
558235783Skib			}
559235783Skib		}
560235783Skib		crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
561235783Skib	}
562235783Skib	sx_xunlock(&dev->mode_config.mutex);
563235783Skib}
564235783Skib#endif
565235783Skib
566235783Skib#if 0
567235783Skibint drm_fb_helper_blank(int blank, struct fb_info *info)
568235783Skib{
569235783Skib	switch (blank) {
570235783Skib	/* Display: On; HSync: On, VSync: On */
571235783Skib	case FB_BLANK_UNBLANK:
572235783Skib		drm_fb_helper_on(info);
573235783Skib		break;
574235783Skib	/* Display: Off; HSync: On, VSync: On */
575235783Skib	case FB_BLANK_NORMAL:
576235783Skib		drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
577235783Skib		break;
578235783Skib	/* Display: Off; HSync: Off, VSync: On */
579235783Skib	case FB_BLANK_HSYNC_SUSPEND:
580235783Skib		drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
581235783Skib		break;
582235783Skib	/* Display: Off; HSync: On, VSync: Off */
583235783Skib	case FB_BLANK_VSYNC_SUSPEND:
584235783Skib		drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
585235783Skib		break;
586235783Skib	/* Display: Off; HSync: Off, VSync: Off */
587235783Skib	case FB_BLANK_POWERDOWN:
588235783Skib		drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
589235783Skib		break;
590235783Skib	}
591235783Skib	return 0;
592235783Skib}
593235783Skib#endif
594235783Skib
595235783Skibstatic void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
596235783Skib{
597235783Skib	int i;
598235783Skib
599235783Skib	for (i = 0; i < helper->connector_count; i++)
600235783Skib		free(helper->connector_info[i], DRM_MEM_KMS);
601235783Skib	free(helper->connector_info, DRM_MEM_KMS);
602248061Sdumbbell	for (i = 0; i < helper->crtc_count; i++) {
603235783Skib		free(helper->crtc_info[i].mode_set.connectors, DRM_MEM_KMS);
604248061Sdumbbell		if (helper->crtc_info[i].mode_set.mode)
605248061Sdumbbell			drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
606248061Sdumbbell	}
607235783Skib	free(helper->crtc_info, DRM_MEM_KMS);
608235783Skib}
609235783Skib
610235783Skibint drm_fb_helper_init(struct drm_device *dev,
611235783Skib		       struct drm_fb_helper *fb_helper,
612235783Skib		       int crtc_count, int max_conn_count)
613235783Skib{
614235783Skib	struct drm_crtc *crtc;
615235783Skib	int i;
616235783Skib
617235783Skib	fb_helper->dev = dev;
618235783Skib
619235783Skib	INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
620235783Skib
621235783Skib	fb_helper->crtc_info = malloc(crtc_count *
622235783Skib	    sizeof(struct drm_fb_helper_crtc), DRM_MEM_KMS, M_WAITOK | M_ZERO);
623235783Skib
624235783Skib	fb_helper->crtc_count = crtc_count;
625235783Skib	fb_helper->connector_info = malloc(dev->mode_config.num_connector *
626235783Skib	    sizeof(struct drm_fb_helper_connector *), DRM_MEM_KMS,
627235783Skib	    M_WAITOK | M_ZERO);
628235783Skib	fb_helper->connector_count = 0;
629235783Skib
630235783Skib	for (i = 0; i < crtc_count; i++) {
631235783Skib		fb_helper->crtc_info[i].mode_set.connectors =
632235783Skib			malloc(max_conn_count * sizeof(struct drm_connector *),
633235783Skib			    DRM_MEM_KMS, M_WAITOK | M_ZERO);
634235783Skib
635235783Skib		fb_helper->crtc_info[i].mode_set.num_connectors = 0;
636235783Skib	}
637235783Skib
638235783Skib	i = 0;
639235783Skib	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
640235783Skib		fb_helper->crtc_info[i].crtc_id = crtc->base.id;
641235783Skib		fb_helper->crtc_info[i].mode_set.crtc = crtc;
642235783Skib		i++;
643235783Skib	}
644235783Skib	fb_helper->conn_limit = max_conn_count;
645235783Skib	return 0;
646235783Skib}
647235783Skib
648235783Skibvoid drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
649235783Skib{
650235783Skib	if (!list_empty(&fb_helper->kernel_fb_list)) {
651235783Skib		list_del(&fb_helper->kernel_fb_list);
652235783Skib		if (list_empty(&kernel_fb_helper_list)) {
653235783Skib#if 0
654235783Skib			printk(KERN_INFO "drm: unregistered panic notifier\n");
655235783Skib			atomic_notifier_chain_unregister(&panic_notifier_list,
656235783Skib							 &paniced);
657235783Skib			unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
658235783Skib#endif
659235783Skib		}
660235783Skib	}
661235783Skib
662235783Skib	drm_fb_helper_crtc_free(fb_helper);
663235783Skib
664235783Skib}
665235783Skib
666235783Skib#if 0
667235783Skibstatic int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
668235783Skib		     u16 blue, u16 regno, struct fb_info *info)
669235783Skib{
670235783Skib	struct drm_fb_helper *fb_helper = info->par;
671235783Skib	struct drm_framebuffer *fb = fb_helper->fb;
672235783Skib	int pindex;
673235783Skib
674235783Skib	if (info->fix.visual == FB_VISUAL_trueCOLOR) {
675235783Skib		u32 *palette;
676235783Skib		u32 value;
677235783Skib		/* place color in psuedopalette */
678235783Skib		if (regno > 16)
679235783Skib			return -EINVAL;
680235783Skib		palette = (u32 *)info->pseudo_palette;
681235783Skib		red >>= (16 - info->var.red.length);
682235783Skib		green >>= (16 - info->var.green.length);
683235783Skib		blue >>= (16 - info->var.blue.length);
684235783Skib		value = (red << info->var.red.offset) |
685235783Skib			(green << info->var.green.offset) |
686235783Skib			(blue << info->var.blue.offset);
687235783Skib		if (info->var.transp.length > 0) {
688235783Skib			u32 mask = (1 << info->var.transp.length) - 1;
689235783Skib			mask <<= info->var.transp.offset;
690235783Skib			value |= mask;
691235783Skib		}
692235783Skib		palette[regno] = value;
693235783Skib		return 0;
694235783Skib	}
695235783Skib
696235783Skib	pindex = regno;
697235783Skib
698235783Skib	if (fb->bits_per_pixel == 16) {
699235783Skib		pindex = regno << 3;
700235783Skib
701235783Skib		if (fb->depth == 16 && regno > 63)
702235783Skib			return -EINVAL;
703235783Skib		if (fb->depth == 15 && regno > 31)
704235783Skib			return -EINVAL;
705235783Skib
706235783Skib		if (fb->depth == 16) {
707235783Skib			u16 r, g, b;
708235783Skib			int i;
709235783Skib			if (regno < 32) {
710235783Skib				for (i = 0; i < 8; i++)
711235783Skib					fb_helper->funcs->gamma_set(crtc, red,
712235783Skib						green, blue, pindex + i);
713235783Skib			}
714235783Skib
715235783Skib			fb_helper->funcs->gamma_get(crtc, &r,
716235783Skib						    &g, &b,
717235783Skib						    pindex >> 1);
718235783Skib
719235783Skib			for (i = 0; i < 4; i++)
720235783Skib				fb_helper->funcs->gamma_set(crtc, r,
721235783Skib							    green, b,
722235783Skib							    (pindex >> 1) + i);
723235783Skib		}
724235783Skib	}
725235783Skib
726235783Skib	if (fb->depth != 16)
727235783Skib		fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
728235783Skib	return 0;
729235783Skib}
730235783Skib#endif
731235783Skib
732235783Skib#if 0
733235783Skibint drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
734235783Skib{
735235783Skib	struct drm_fb_helper *fb_helper = info->par;
736235783Skib	struct drm_crtc_helper_funcs *crtc_funcs;
737235783Skib	u16 *red, *green, *blue, *transp;
738235783Skib	struct drm_crtc *crtc;
739235783Skib	int i, j, rc = 0;
740235783Skib	int start;
741235783Skib
742235783Skib	for (i = 0; i < fb_helper->crtc_count; i++) {
743235783Skib		crtc = fb_helper->crtc_info[i].mode_set.crtc;
744235783Skib		crtc_funcs = crtc->helper_private;
745235783Skib
746235783Skib		red = cmap->red;
747235783Skib		green = cmap->green;
748235783Skib		blue = cmap->blue;
749235783Skib		transp = cmap->transp;
750235783Skib		start = cmap->start;
751235783Skib
752235783Skib		for (j = 0; j < cmap->len; j++) {
753235783Skib			u16 hred, hgreen, hblue, htransp = 0xffff;
754235783Skib
755235783Skib			hred = *red++;
756235783Skib			hgreen = *green++;
757235783Skib			hblue = *blue++;
758235783Skib
759235783Skib			if (transp)
760235783Skib				htransp = *transp++;
761235783Skib
762235783Skib			rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
763235783Skib			if (rc)
764235783Skib				return rc;
765235783Skib		}
766235783Skib		crtc_funcs->load_lut(crtc);
767235783Skib	}
768235783Skib	return rc;
769235783Skib}
770235783Skib#endif
771235783Skib
772235783Skib#if 0
773235783Skibint drm_fb_helper_check_var(struct fb_var_screeninfo *var,
774235783Skib			    struct fb_info *info)
775235783Skib{
776235783Skib	struct drm_fb_helper *fb_helper = info->par;
777235783Skib	struct drm_framebuffer *fb = fb_helper->fb;
778235783Skib	int depth;
779235783Skib
780235783Skib	if (var->pixclock != 0 || in_dbg_master())
781235783Skib		return -EINVAL;
782235783Skib
783235783Skib	/* Need to resize the fb object !!! */
784235783Skib	if (var->bits_per_pixel > fb->bits_per_pixel ||
785235783Skib	    var->xres > fb->width || var->yres > fb->height ||
786235783Skib	    var->xres_virtual > fb->width || var->yres_virtual > fb->height) {
787235783Skib		DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
788235783Skib			  "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n",
789235783Skib			  var->xres, var->yres, var->bits_per_pixel,
790235783Skib			  var->xres_virtual, var->yres_virtual,
791235783Skib			  fb->width, fb->height, fb->bits_per_pixel);
792235783Skib		return -EINVAL;
793235783Skib	}
794235783Skib
795235783Skib	switch (var->bits_per_pixel) {
796235783Skib	case 16:
797235783Skib		depth = (var->green.length == 6) ? 16 : 15;
798235783Skib		break;
799235783Skib	case 32:
800235783Skib		depth = (var->transp.length > 0) ? 32 : 24;
801235783Skib		break;
802235783Skib	default:
803235783Skib		depth = var->bits_per_pixel;
804235783Skib		break;
805235783Skib	}
806235783Skib
807235783Skib	switch (depth) {
808235783Skib	case 8:
809235783Skib		var->red.offset = 0;
810235783Skib		var->green.offset = 0;
811235783Skib		var->blue.offset = 0;
812235783Skib		var->red.length = 8;
813235783Skib		var->green.length = 8;
814235783Skib		var->blue.length = 8;
815235783Skib		var->transp.length = 0;
816235783Skib		var->transp.offset = 0;
817235783Skib		break;
818235783Skib	case 15:
819235783Skib		var->red.offset = 10;
820235783Skib		var->green.offset = 5;
821235783Skib		var->blue.offset = 0;
822235783Skib		var->red.length = 5;
823235783Skib		var->green.length = 5;
824235783Skib		var->blue.length = 5;
825235783Skib		var->transp.length = 1;
826235783Skib		var->transp.offset = 15;
827235783Skib		break;
828235783Skib	case 16:
829235783Skib		var->red.offset = 11;
830235783Skib		var->green.offset = 5;
831235783Skib		var->blue.offset = 0;
832235783Skib		var->red.length = 5;
833235783Skib		var->green.length = 6;
834235783Skib		var->blue.length = 5;
835235783Skib		var->transp.length = 0;
836235783Skib		var->transp.offset = 0;
837235783Skib		break;
838235783Skib	case 24:
839235783Skib		var->red.offset = 16;
840235783Skib		var->green.offset = 8;
841235783Skib		var->blue.offset = 0;
842235783Skib		var->red.length = 8;
843235783Skib		var->green.length = 8;
844235783Skib		var->blue.length = 8;
845235783Skib		var->transp.length = 0;
846235783Skib		var->transp.offset = 0;
847235783Skib		break;
848235783Skib	case 32:
849235783Skib		var->red.offset = 16;
850235783Skib		var->green.offset = 8;
851235783Skib		var->blue.offset = 0;
852235783Skib		var->red.length = 8;
853235783Skib		var->green.length = 8;
854235783Skib		var->blue.length = 8;
855235783Skib		var->transp.length = 8;
856235783Skib		var->transp.offset = 24;
857235783Skib		break;
858235783Skib	default:
859235783Skib		return -EINVAL;
860235783Skib	}
861235783Skib	return 0;
862235783Skib}
863235783Skib#endif
864235783Skib
865235783Skib#if 0
866235783Skib/* this will let fbcon do the mode init */
867235783Skibint drm_fb_helper_set_par(struct fb_info *info)
868235783Skib{
869235783Skib	struct drm_fb_helper *fb_helper = info->par;
870235783Skib	struct drm_device *dev = fb_helper->dev;
871235783Skib	struct fb_var_screeninfo *var = &info->var;
872235783Skib	struct drm_crtc *crtc;
873235783Skib	int ret;
874235783Skib	int i;
875235783Skib
876235783Skib	if (var->pixclock != 0) {
877235783Skib		DRM_ERROR("PIXEL CLOCK SET\n");
878235783Skib		return -EINVAL;
879235783Skib	}
880235783Skib
881235783Skib	mutex_lock(&dev->mode_config.mutex);
882235783Skib	for (i = 0; i < fb_helper->crtc_count; i++) {
883235783Skib		crtc = fb_helper->crtc_info[i].mode_set.crtc;
884235783Skib		ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
885235783Skib		if (ret) {
886235783Skib			mutex_unlock(&dev->mode_config.mutex);
887235783Skib			return ret;
888235783Skib		}
889235783Skib	}
890235783Skib	mutex_unlock(&dev->mode_config.mutex);
891235783Skib
892235783Skib	if (fb_helper->delayed_hotplug) {
893235783Skib		fb_helper->delayed_hotplug = false;
894235783Skib		drm_fb_helper_hotplug_event(fb_helper);
895235783Skib	}
896235783Skib	return 0;
897235783Skib}
898235783Skib#endif
899235783Skib
900235783Skib#if 0
901235783Skibint drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
902235783Skib			      struct fb_info *info)
903235783Skib{
904235783Skib	struct drm_fb_helper *fb_helper = info->par;
905235783Skib	struct drm_device *dev = fb_helper->dev;
906235783Skib	struct drm_mode_set *modeset;
907235783Skib	struct drm_crtc *crtc;
908235783Skib	int ret = 0;
909235783Skib	int i;
910235783Skib
911235783Skib	mutex_lock(&dev->mode_config.mutex);
912235783Skib	for (i = 0; i < fb_helper->crtc_count; i++) {
913235783Skib		crtc = fb_helper->crtc_info[i].mode_set.crtc;
914235783Skib
915235783Skib		modeset = &fb_helper->crtc_info[i].mode_set;
916235783Skib
917235783Skib		modeset->x = var->xoffset;
918235783Skib		modeset->y = var->yoffset;
919235783Skib
920235783Skib		if (modeset->num_connectors) {
921235783Skib			ret = crtc->funcs->set_config(modeset);
922235783Skib			if (!ret) {
923235783Skib				info->var.xoffset = var->xoffset;
924235783Skib				info->var.yoffset = var->yoffset;
925235783Skib			}
926235783Skib		}
927235783Skib	}
928235783Skib	mutex_unlock(&dev->mode_config.mutex);
929235783Skib	return ret;
930235783Skib}
931235783Skib#endif
932235783Skib
933235783Skibint drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
934235783Skib				  int preferred_bpp)
935235783Skib{
936235783Skib	int new_fb = 0;
937235783Skib	int crtc_count = 0;
938235783Skib	int i;
939235783Skib	struct fb_info *info;
940235783Skib	struct drm_fb_helper_surface_size sizes;
941235783Skib	int gamma_size = 0;
942262861Sjhb	struct vt_kms_softc *sc;
943262861Sjhb	device_t kdev;
944235783Skib
945235783Skib	memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
946235783Skib	sizes.surface_depth = 24;
947235783Skib	sizes.surface_bpp = 32;
948235783Skib	sizes.fb_width = (unsigned)-1;
949235783Skib	sizes.fb_height = (unsigned)-1;
950235783Skib
951235783Skib	/* if driver picks 8 or 16 by default use that
952235783Skib	   for both depth/bpp */
953235783Skib	if (preferred_bpp != sizes.surface_bpp) {
954235783Skib		sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
955235783Skib	}
956235783Skib	/* first up get a count of crtcs now in use and new min/maxes width/heights */
957235783Skib	for (i = 0; i < fb_helper->connector_count; i++) {
958235783Skib		struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
959235783Skib		struct drm_fb_helper_cmdline_mode *cmdline_mode;
960235783Skib
961235783Skib		cmdline_mode = &fb_helper_conn->cmdline_mode;
962235783Skib
963235783Skib		if (cmdline_mode->bpp_specified) {
964235783Skib			switch (cmdline_mode->bpp) {
965235783Skib			case 8:
966235783Skib				sizes.surface_depth = sizes.surface_bpp = 8;
967235783Skib				break;
968235783Skib			case 15:
969235783Skib				sizes.surface_depth = 15;
970235783Skib				sizes.surface_bpp = 16;
971235783Skib				break;
972235783Skib			case 16:
973235783Skib				sizes.surface_depth = sizes.surface_bpp = 16;
974235783Skib				break;
975235783Skib			case 24:
976235783Skib				sizes.surface_depth = sizes.surface_bpp = 24;
977235783Skib				break;
978235783Skib			case 32:
979235783Skib				sizes.surface_depth = 24;
980235783Skib				sizes.surface_bpp = 32;
981235783Skib				break;
982235783Skib			}
983235783Skib			break;
984235783Skib		}
985235783Skib	}
986235783Skib
987235783Skib	crtc_count = 0;
988235783Skib	for (i = 0; i < fb_helper->crtc_count; i++) {
989235783Skib		struct drm_display_mode *desired_mode;
990235783Skib		desired_mode = fb_helper->crtc_info[i].desired_mode;
991235783Skib
992235783Skib		if (desired_mode) {
993235783Skib			if (gamma_size == 0)
994235783Skib				gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
995235783Skib			if (desired_mode->hdisplay < sizes.fb_width)
996235783Skib				sizes.fb_width = desired_mode->hdisplay;
997235783Skib			if (desired_mode->vdisplay < sizes.fb_height)
998235783Skib				sizes.fb_height = desired_mode->vdisplay;
999235783Skib			if (desired_mode->hdisplay > sizes.surface_width)
1000235783Skib				sizes.surface_width = desired_mode->hdisplay;
1001235783Skib			if (desired_mode->vdisplay > sizes.surface_height)
1002235783Skib				sizes.surface_height = desired_mode->vdisplay;
1003235783Skib			crtc_count++;
1004235783Skib		}
1005235783Skib	}
1006235783Skib
1007235783Skib	if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
1008235783Skib		/* hmm everyone went away - assume VGA cable just fell out
1009235783Skib		   and will come back later. */
1010235783Skib		DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n");
1011235783Skib		sizes.fb_width = sizes.surface_width = 1024;
1012235783Skib		sizes.fb_height = sizes.surface_height = 768;
1013235783Skib	}
1014235783Skib
1015235783Skib	/* push down into drivers */
1016235783Skib	new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
1017235783Skib	if (new_fb < 0)
1018235783Skib		return new_fb;
1019235783Skib
1020262861Sjhb	sc = malloc(sizeof(struct vt_kms_softc), DRM_MEM_KMS,
1021262861Sjhb	    M_WAITOK | M_ZERO);
1022262861Sjhb	sc->fb_helper = fb_helper;
1023262861Sjhb	TASK_INIT(&sc->fb_mode_task, 0, vt_restore_fbdev_mode, sc);
1024262861Sjhb
1025235783Skib	info = fb_helper->fbdev;
1026262861Sjhb
1027262861Sjhb	info->fb_name = device_get_nameunit(fb_helper->dev->device);
1028262861Sjhb	info->fb_depth = fb_helper->fb->bits_per_pixel;
1029262861Sjhb	info->fb_height = fb_helper->fb->height;
1030262861Sjhb	info->fb_width = fb_helper->fb->width;
1031262861Sjhb	info->fb_stride = fb_helper->fb->pitches[0];
1032262861Sjhb	info->fb_priv = sc;
1033262861Sjhb	info->enter = &vt_kms_postswitch;
1034235783Skib
1035235783Skib	/* set the fb pointer */
1036235783Skib	for (i = 0; i < fb_helper->crtc_count; i++) {
1037235783Skib		fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
1038235783Skib	}
1039235783Skib
1040235783Skib	if (new_fb) {
1041262861Sjhb		device_t fbd;
1042262861Sjhb		int ret;
1043262861Sjhb
1044262861Sjhb		kdev = fb_helper->dev->device;
1045262861Sjhb		fbd = device_add_child(kdev, "fbd", device_get_unit(kdev));
1046262861Sjhb		if (fbd != NULL)
1047262861Sjhb			ret = device_probe_and_attach(fbd);
1048262861Sjhb		else
1049262861Sjhb			ret = ENODEV;
1050262861Sjhb#ifdef DEV_VT
1051262861Sjhb		if (ret != 0)
1052262861Sjhb			DRM_ERROR("Failed to attach fbd device: %d\n", ret);
1053262861Sjhb#endif
1054262861Sjhb	}
1055235783Skib	return 0;
1056235783Skib}
1057235783Skib
1058235783Skib#if 0
1059235783Skibvoid drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
1060235783Skib			    uint32_t depth)
1061235783Skib{
1062235783Skib	info->fix.type = FB_TYPE_PACKED_PIXELS;
1063235783Skib	info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
1064235783Skib		FB_VISUAL_trueCOLOR;
1065235783Skib	info->fix.mmio_start = 0;
1066235783Skib	info->fix.mmio_len = 0;
1067235783Skib	info->fix.type_aux = 0;
1068235783Skib	info->fix.xpanstep = 1; /* doing it in hw */
1069235783Skib	info->fix.ypanstep = 1; /* doing it in hw */
1070235783Skib	info->fix.ywrapstep = 0;
1071235783Skib	info->fix.accel = FB_ACCEL_NONE;
1072235783Skib	info->fix.type_aux = 0;
1073235783Skib
1074235783Skib	info->fix.line_length = pitch;
1075235783Skib	return;
1076235783Skib}
1077235783Skib
1078235783Skibvoid drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
1079235783Skib			    uint32_t fb_width, uint32_t fb_height)
1080235783Skib{
1081235783Skib	struct drm_framebuffer *fb = fb_helper->fb;
1082235783Skib	info->pseudo_palette = fb_helper->pseudo_palette;
1083235783Skib	info->var.xres_virtual = fb->width;
1084235783Skib	info->var.yres_virtual = fb->height;
1085235783Skib	info->var.bits_per_pixel = fb->bits_per_pixel;
1086235783Skib	info->var.accel_flags = FB_ACCELF_TEXT;
1087235783Skib	info->var.xoffset = 0;
1088235783Skib	info->var.yoffset = 0;
1089235783Skib	info->var.activate = FB_ACTIVATE_NOW;
1090235783Skib	info->var.height = -1;
1091235783Skib	info->var.width = -1;
1092235783Skib
1093235783Skib	switch (fb->depth) {
1094235783Skib	case 8:
1095235783Skib		info->var.red.offset = 0;
1096235783Skib		info->var.green.offset = 0;
1097235783Skib		info->var.blue.offset = 0;
1098235783Skib		info->var.red.length = 8; /* 8bit DAC */
1099235783Skib		info->var.green.length = 8;
1100235783Skib		info->var.blue.length = 8;
1101235783Skib		info->var.transp.offset = 0;
1102235783Skib		info->var.transp.length = 0;
1103235783Skib		break;
1104235783Skib	case 15:
1105235783Skib		info->var.red.offset = 10;
1106235783Skib		info->var.green.offset = 5;
1107235783Skib		info->var.blue.offset = 0;
1108235783Skib		info->var.red.length = 5;
1109235783Skib		info->var.green.length = 5;
1110235783Skib		info->var.blue.length = 5;
1111235783Skib		info->var.transp.offset = 15;
1112235783Skib		info->var.transp.length = 1;
1113235783Skib		break;
1114235783Skib	case 16:
1115235783Skib		info->var.red.offset = 11;
1116235783Skib		info->var.green.offset = 5;
1117235783Skib		info->var.blue.offset = 0;
1118235783Skib		info->var.red.length = 5;
1119235783Skib		info->var.green.length = 6;
1120235783Skib		info->var.blue.length = 5;
1121235783Skib		info->var.transp.offset = 0;
1122235783Skib		break;
1123235783Skib	case 24:
1124235783Skib		info->var.red.offset = 16;
1125235783Skib		info->var.green.offset = 8;
1126235783Skib		info->var.blue.offset = 0;
1127235783Skib		info->var.red.length = 8;
1128235783Skib		info->var.green.length = 8;
1129235783Skib		info->var.blue.length = 8;
1130235783Skib		info->var.transp.offset = 0;
1131235783Skib		info->var.transp.length = 0;
1132235783Skib		break;
1133235783Skib	case 32:
1134235783Skib		info->var.red.offset = 16;
1135235783Skib		info->var.green.offset = 8;
1136235783Skib		info->var.blue.offset = 0;
1137235783Skib		info->var.red.length = 8;
1138235783Skib		info->var.green.length = 8;
1139235783Skib		info->var.blue.length = 8;
1140235783Skib		info->var.transp.offset = 24;
1141235783Skib		info->var.transp.length = 8;
1142235783Skib		break;
1143235783Skib	default:
1144235783Skib		break;
1145235783Skib	}
1146235783Skib
1147235783Skib	info->var.xres = fb_width;
1148235783Skib	info->var.yres = fb_height;
1149235783Skib}
1150235783Skib#endif
1151235783Skib
1152235783Skibstatic int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
1153235783Skib					       uint32_t maxX,
1154235783Skib					       uint32_t maxY)
1155235783Skib{
1156235783Skib	struct drm_connector *connector;
1157235783Skib	int count = 0;
1158235783Skib	int i;
1159235783Skib
1160235783Skib	for (i = 0; i < fb_helper->connector_count; i++) {
1161235783Skib		connector = fb_helper->connector_info[i]->connector;
1162235783Skib		count += connector->funcs->fill_modes(connector, maxX, maxY);
1163235783Skib	}
1164235783Skib
1165235783Skib	return count;
1166235783Skib}
1167235783Skib
1168235783Skibstatic struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
1169235783Skib{
1170235783Skib	struct drm_display_mode *mode;
1171235783Skib
1172235783Skib	list_for_each_entry(mode, &fb_connector->connector->modes, head) {
1173235783Skib		if (drm_mode_width(mode) > width ||
1174235783Skib		    drm_mode_height(mode) > height)
1175235783Skib			continue;
1176235783Skib		if (mode->type & DRM_MODE_TYPE_PREFERRED)
1177235783Skib			return mode;
1178235783Skib	}
1179235783Skib	return NULL;
1180235783Skib}
1181235783Skib
1182235783Skibstatic bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
1183235783Skib{
1184235783Skib	struct drm_fb_helper_cmdline_mode *cmdline_mode;
1185235783Skib	cmdline_mode = &fb_connector->cmdline_mode;
1186235783Skib	return cmdline_mode->specified;
1187235783Skib}
1188235783Skib
1189235783Skibstatic struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
1190235783Skib						      int width, int height)
1191235783Skib{
1192235783Skib	struct drm_cmdline_mode *cmdline_mode;
1193235783Skib	struct drm_display_mode *mode = NULL;
1194235783Skib
1195235783Skib	cmdline_mode = &fb_helper_conn->cmdline_mode1;
1196235783Skib	if (cmdline_mode->specified == false &&
1197235783Skib	    !drm_fetch_cmdline_mode_from_kenv(fb_helper_conn->connector,
1198235783Skib	    cmdline_mode))
1199235783Skib			return (NULL);
1200235783Skib
1201235783Skib	/* attempt to find a matching mode in the list of modes
1202235783Skib	 *  we have gotten so far, if not add a CVT mode that conforms
1203235783Skib	 */
1204235783Skib	if (cmdline_mode->rb || cmdline_mode->margins)
1205235783Skib		goto create_mode;
1206235783Skib
1207235783Skib	list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1208235783Skib		/* check width/height */
1209235783Skib		if (mode->hdisplay != cmdline_mode->xres ||
1210235783Skib		    mode->vdisplay != cmdline_mode->yres)
1211235783Skib			continue;
1212235783Skib
1213235783Skib		if (cmdline_mode->refresh_specified) {
1214235783Skib			if (mode->vrefresh != cmdline_mode->refresh)
1215235783Skib				continue;
1216235783Skib		}
1217235783Skib
1218235783Skib		if (cmdline_mode->interlace) {
1219235783Skib			if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
1220235783Skib				continue;
1221235783Skib		}
1222235783Skib		return mode;
1223235783Skib	}
1224235783Skib
1225235783Skibcreate_mode:
1226235783Skib	if (cmdline_mode->cvt)
1227235783Skib		mode = drm_cvt_mode(fb_helper_conn->connector->dev,
1228235783Skib				    cmdline_mode->xres, cmdline_mode->yres,
1229235783Skib				    cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60,
1230235783Skib				    cmdline_mode->rb, cmdline_mode->interlace,
1231235783Skib				    cmdline_mode->margins);
1232235783Skib	else
1233235783Skib		mode = drm_gtf_mode(fb_helper_conn->connector->dev,
1234235783Skib				    cmdline_mode->xres, cmdline_mode->yres,
1235235783Skib				    cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60,
1236235783Skib				    cmdline_mode->interlace,
1237235783Skib				    cmdline_mode->margins);
1238235783Skib	drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
1239235783Skib	list_add(&mode->head, &fb_helper_conn->connector->modes);
1240235783Skib	return mode;
1241235783Skib}
1242235783Skib
1243235783Skibstatic bool drm_connector_enabled(struct drm_connector *connector, bool strict)
1244235783Skib{
1245235783Skib	bool enable;
1246235783Skib
1247235783Skib	if (strict) {
1248235783Skib		enable = connector->status == connector_status_connected;
1249235783Skib	} else {
1250235783Skib		enable = connector->status != connector_status_disconnected;
1251235783Skib	}
1252235783Skib	return enable;
1253235783Skib}
1254235783Skib
1255235783Skibstatic void drm_enable_connectors(struct drm_fb_helper *fb_helper,
1256235783Skib				  bool *enabled)
1257235783Skib{
1258235783Skib	bool any_enabled = false;
1259235783Skib	struct drm_connector *connector;
1260235783Skib	int i = 0;
1261235783Skib
1262235783Skib	for (i = 0; i < fb_helper->connector_count; i++) {
1263235783Skib		connector = fb_helper->connector_info[i]->connector;
1264235783Skib		enabled[i] = drm_connector_enabled(connector, true);
1265235783Skib		DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
1266235783Skib			  enabled[i] ? "yes" : "no");
1267235783Skib		any_enabled |= enabled[i];
1268235783Skib	}
1269235783Skib
1270235783Skib	if (any_enabled)
1271235783Skib		return;
1272235783Skib
1273235783Skib	for (i = 0; i < fb_helper->connector_count; i++) {
1274235783Skib		connector = fb_helper->connector_info[i]->connector;
1275235783Skib		enabled[i] = drm_connector_enabled(connector, false);
1276235783Skib	}
1277235783Skib}
1278235783Skib
1279235783Skibstatic bool drm_target_cloned(struct drm_fb_helper *fb_helper,
1280235783Skib			      struct drm_display_mode **modes,
1281235783Skib			      bool *enabled, int width, int height)
1282235783Skib{
1283235783Skib	int count, i, j;
1284235783Skib	bool can_clone = false;
1285235783Skib	struct drm_fb_helper_connector *fb_helper_conn;
1286235783Skib	struct drm_display_mode *dmt_mode, *mode;
1287235783Skib
1288235783Skib	/* only contemplate cloning in the single crtc case */
1289235783Skib	if (fb_helper->crtc_count > 1)
1290235783Skib		return false;
1291235783Skib
1292235783Skib	count = 0;
1293235783Skib	for (i = 0; i < fb_helper->connector_count; i++) {
1294235783Skib		if (enabled[i])
1295235783Skib			count++;
1296235783Skib	}
1297235783Skib
1298235783Skib	/* only contemplate cloning if more than one connector is enabled */
1299235783Skib	if (count <= 1)
1300235783Skib		return false;
1301235783Skib
1302235783Skib	/* check the command line or if nothing common pick 1024x768 */
1303235783Skib	can_clone = true;
1304235783Skib	for (i = 0; i < fb_helper->connector_count; i++) {
1305235783Skib		if (!enabled[i])
1306235783Skib			continue;
1307235783Skib		fb_helper_conn = fb_helper->connector_info[i];
1308235783Skib		modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1309235783Skib		if (!modes[i]) {
1310235783Skib			can_clone = false;
1311235783Skib			break;
1312235783Skib		}
1313235783Skib		for (j = 0; j < i; j++) {
1314235783Skib			if (!enabled[j])
1315235783Skib				continue;
1316235783Skib			if (!drm_mode_equal(modes[j], modes[i]))
1317235783Skib				can_clone = false;
1318235783Skib		}
1319235783Skib	}
1320235783Skib
1321235783Skib	if (can_clone) {
1322235783Skib		DRM_DEBUG_KMS("can clone using command line\n");
1323235783Skib		return true;
1324235783Skib	}
1325235783Skib
1326235783Skib	/* try and find a 1024x768 mode on each connector */
1327235783Skib	can_clone = true;
1328235783Skib	dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60);
1329235783Skib
1330235783Skib	for (i = 0; i < fb_helper->connector_count; i++) {
1331235783Skib
1332235783Skib		if (!enabled[i])
1333235783Skib			continue;
1334235783Skib
1335235783Skib		fb_helper_conn = fb_helper->connector_info[i];
1336235783Skib		list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1337235783Skib			if (drm_mode_equal(mode, dmt_mode))
1338235783Skib				modes[i] = mode;
1339235783Skib		}
1340235783Skib		if (!modes[i])
1341235783Skib			can_clone = false;
1342235783Skib	}
1343235783Skib
1344235783Skib	if (can_clone) {
1345235783Skib		DRM_DEBUG_KMS("can clone using 1024x768\n");
1346235783Skib		return true;
1347235783Skib	}
1348235783Skib	DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
1349235783Skib	return false;
1350235783Skib}
1351235783Skib
1352235783Skibstatic bool drm_target_preferred(struct drm_fb_helper *fb_helper,
1353235783Skib				 struct drm_display_mode **modes,
1354235783Skib				 bool *enabled, int width, int height)
1355235783Skib{
1356235783Skib	struct drm_fb_helper_connector *fb_helper_conn;
1357235783Skib	int i;
1358235783Skib
1359235783Skib	for (i = 0; i < fb_helper->connector_count; i++) {
1360235783Skib		fb_helper_conn = fb_helper->connector_info[i];
1361235783Skib
1362235783Skib		if (enabled[i] == false)
1363235783Skib			continue;
1364235783Skib
1365235783Skib		DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
1366235783Skib			      fb_helper_conn->connector->base.id);
1367235783Skib
1368235783Skib		/* got for command line mode first */
1369235783Skib		modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1370235783Skib		if (!modes[i]) {
1371235783Skib			DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
1372235783Skib				      fb_helper_conn->connector->base.id);
1373235783Skib			modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
1374235783Skib		}
1375235783Skib		/* No preferred modes, pick one off the list */
1376235783Skib		if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
1377235783Skib			list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
1378235783Skib				break;
1379235783Skib		}
1380235783Skib		DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
1381235783Skib			  "none");
1382235783Skib	}
1383235783Skib	return true;
1384235783Skib}
1385235783Skib
1386235783Skibstatic int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
1387235783Skib			  struct drm_fb_helper_crtc **best_crtcs,
1388235783Skib			  struct drm_display_mode **modes,
1389235783Skib			  int n, int width, int height)
1390235783Skib{
1391235783Skib	int c, o;
1392235783Skib	struct drm_device *dev = fb_helper->dev;
1393235783Skib	struct drm_connector *connector;
1394235783Skib	struct drm_connector_helper_funcs *connector_funcs;
1395235783Skib	struct drm_encoder *encoder;
1396235783Skib	struct drm_fb_helper_crtc *best_crtc;
1397235783Skib	int my_score, best_score, score;
1398235783Skib	struct drm_fb_helper_crtc **crtcs, *crtc;
1399235783Skib	struct drm_fb_helper_connector *fb_helper_conn;
1400235783Skib
1401235783Skib	if (n == fb_helper->connector_count)
1402235783Skib		return 0;
1403235783Skib
1404235783Skib	fb_helper_conn = fb_helper->connector_info[n];
1405235783Skib	connector = fb_helper_conn->connector;
1406235783Skib
1407235783Skib	best_crtcs[n] = NULL;
1408235783Skib	best_crtc = NULL;
1409235783Skib	best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
1410235783Skib	if (modes[n] == NULL)
1411235783Skib		return best_score;
1412235783Skib
1413235783Skib	crtcs = malloc(dev->mode_config.num_connector *
1414235783Skib	    sizeof(struct drm_fb_helper_crtc *), DRM_MEM_KMS,
1415235783Skib	    M_WAITOK | M_ZERO);
1416235783Skib
1417235783Skib	my_score = 1;
1418235783Skib	if (connector->status == connector_status_connected)
1419235783Skib		my_score++;
1420235783Skib	if (drm_has_cmdline_mode(fb_helper_conn))
1421235783Skib		my_score++;
1422235783Skib	if (drm_has_preferred_mode(fb_helper_conn, width, height))
1423235783Skib		my_score++;
1424235783Skib
1425235783Skib	connector_funcs = connector->helper_private;
1426235783Skib	encoder = connector_funcs->best_encoder(connector);
1427235783Skib	if (!encoder)
1428235783Skib		goto out;
1429235783Skib
1430235783Skib	/* select a crtc for this connector and then attempt to configure
1431235783Skib	   remaining connectors */
1432235783Skib	for (c = 0; c < fb_helper->crtc_count; c++) {
1433235783Skib		crtc = &fb_helper->crtc_info[c];
1434235783Skib
1435235783Skib		if ((encoder->possible_crtcs & (1 << c)) == 0) {
1436235783Skib			continue;
1437235783Skib		}
1438235783Skib
1439235783Skib		for (o = 0; o < n; o++)
1440235783Skib			if (best_crtcs[o] == crtc)
1441235783Skib				break;
1442235783Skib
1443235783Skib		if (o < n) {
1444235783Skib			/* ignore cloning unless only a single crtc */
1445235783Skib			if (fb_helper->crtc_count > 1)
1446235783Skib				continue;
1447235783Skib
1448235783Skib			if (!drm_mode_equal(modes[o], modes[n]))
1449235783Skib				continue;
1450235783Skib		}
1451235783Skib
1452235783Skib		crtcs[n] = crtc;
1453235783Skib		memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
1454235783Skib		score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
1455235783Skib						  width, height);
1456235783Skib		if (score > best_score) {
1457235783Skib			best_crtc = crtc;
1458235783Skib			best_score = score;
1459235783Skib			memcpy(best_crtcs, crtcs,
1460235783Skib			       dev->mode_config.num_connector *
1461235783Skib			       sizeof(struct drm_fb_helper_crtc *));
1462235783Skib		}
1463235783Skib	}
1464235783Skibout:
1465235783Skib	free(crtcs, DRM_MEM_KMS);
1466235783Skib	return best_score;
1467235783Skib}
1468235783Skib
1469235783Skibstatic void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
1470235783Skib{
1471235783Skib	struct drm_device *dev = fb_helper->dev;
1472235783Skib	struct drm_fb_helper_crtc **crtcs;
1473235783Skib	struct drm_display_mode **modes;
1474235783Skib	struct drm_encoder *encoder;
1475235783Skib	struct drm_mode_set *modeset;
1476235783Skib	bool *enabled;
1477235783Skib	int width, height;
1478235783Skib	int i, ret;
1479235783Skib
1480235783Skib	DRM_DEBUG_KMS("\n");
1481235783Skib
1482235783Skib	width = dev->mode_config.max_width;
1483235783Skib	height = dev->mode_config.max_height;
1484235783Skib
1485235783Skib	/* clean out all the encoder/crtc combos */
1486235783Skib	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
1487235783Skib		encoder->crtc = NULL;
1488235783Skib	}
1489235783Skib
1490235783Skib	crtcs = malloc(dev->mode_config.num_connector *
1491235783Skib	    sizeof(struct drm_fb_helper_crtc *), DRM_MEM_KMS,
1492235783Skib	    M_WAITOK | M_ZERO);
1493235783Skib	modes = malloc(dev->mode_config.num_connector *
1494235783Skib	    sizeof(struct drm_display_mode *), DRM_MEM_KMS,
1495235783Skib	    M_WAITOK | M_ZERO);
1496235783Skib	enabled = malloc(dev->mode_config.num_connector *
1497235783Skib	    sizeof(bool), DRM_MEM_KMS, M_WAITOK | M_ZERO);
1498235783Skib
1499235783Skib	drm_enable_connectors(fb_helper, enabled);
1500235783Skib
1501235783Skib	ret = drm_target_cloned(fb_helper, modes, enabled, width, height);
1502235783Skib	if (!ret) {
1503235783Skib		ret = drm_target_preferred(fb_helper, modes, enabled, width, height);
1504235783Skib		if (!ret)
1505235783Skib			DRM_ERROR("Unable to find initial modes\n");
1506235783Skib	}
1507235783Skib
1508235783Skib	DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height);
1509235783Skib
1510235783Skib	drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
1511235783Skib
1512235783Skib	/* need to set the modesets up here for use later */
1513235783Skib	/* fill out the connector<->crtc mappings into the modesets */
1514235783Skib	for (i = 0; i < fb_helper->crtc_count; i++) {
1515235783Skib		modeset = &fb_helper->crtc_info[i].mode_set;
1516235783Skib		modeset->num_connectors = 0;
1517235783Skib	}
1518235783Skib
1519235783Skib	for (i = 0; i < fb_helper->connector_count; i++) {
1520235783Skib		struct drm_display_mode *mode = modes[i];
1521235783Skib		struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
1522235783Skib		modeset = &fb_crtc->mode_set;
1523235783Skib
1524235783Skib		if (mode && fb_crtc) {
1525235783Skib			DRM_DEBUG_KMS("desired mode %s set on crtc %d\n",
1526235783Skib				      mode->name, fb_crtc->mode_set.crtc->base.id);
1527235783Skib			fb_crtc->desired_mode = mode;
1528235783Skib			if (modeset->mode)
1529235783Skib				drm_mode_destroy(dev, modeset->mode);
1530235783Skib			modeset->mode = drm_mode_duplicate(dev,
1531235783Skib							   fb_crtc->desired_mode);
1532235783Skib			modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
1533235783Skib		}
1534235783Skib	}
1535235783Skib
1536235783Skib	free(crtcs, DRM_MEM_KMS);
1537235783Skib	free(modes, DRM_MEM_KMS);
1538235783Skib	free(enabled, DRM_MEM_KMS);
1539235783Skib}
1540235783Skib
1541235783Skib/**
1542235783Skib * drm_helper_initial_config - setup a sane initial connector configuration
1543235783Skib * @dev: DRM device
1544235783Skib *
1545235783Skib * LOCKING:
1546235783Skib * Called at init time, must take mode config lock.
1547235783Skib *
1548235783Skib * Scan the CRTCs and connectors and try to put together an initial setup.
1549235783Skib * At the moment, this is a cloned configuration across all heads with
1550235783Skib * a new framebuffer object as the backing store.
1551235783Skib *
1552235783Skib * RETURNS:
1553235783Skib * Zero if everything went ok, nonzero otherwise.
1554235783Skib */
1555235783Skibbool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
1556235783Skib{
1557235783Skib	struct drm_device *dev = fb_helper->dev;
1558235783Skib	int count = 0;
1559235783Skib
1560235783Skib	/* disable all the possible outputs/crtcs before entering KMS mode */
1561235783Skib	drm_helper_disable_unused_functions(fb_helper->dev);
1562235783Skib
1563235783Skib	drm_fb_helper_parse_command_line(fb_helper);
1564235783Skib
1565235783Skib	count = drm_fb_helper_probe_connector_modes(fb_helper,
1566235783Skib						    dev->mode_config.max_width,
1567235783Skib						    dev->mode_config.max_height);
1568235783Skib	/*
1569235783Skib	 * we shouldn't end up with no modes here.
1570235783Skib	 */
1571235783Skib	if (count == 0) {
1572235783Skib		printf("No connectors reported connected with modes\n");
1573235783Skib	}
1574235783Skib	drm_setup_crtcs(fb_helper);
1575235783Skib
1576235783Skib	return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
1577235783Skib}
1578235783Skib
1579235783Skibint drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
1580235783Skib{
1581235783Skib	struct drm_device *dev = fb_helper->dev;
1582235783Skib	int count = 0;
1583235783Skib	u32 max_width, max_height, bpp_sel;
1584235783Skib	bool bound = false, crtcs_bound = false;
1585235783Skib	struct drm_crtc *crtc;
1586235783Skib
1587235783Skib	if (!fb_helper->fb)
1588235783Skib		return 0;
1589235783Skib
1590235783Skib	sx_xlock(&dev->mode_config.mutex);
1591235783Skib	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
1592235783Skib		if (crtc->fb)
1593235783Skib			crtcs_bound = true;
1594235783Skib		if (crtc->fb == fb_helper->fb)
1595235783Skib			bound = true;
1596235783Skib	}
1597235783Skib
1598235783Skib	if (!bound && crtcs_bound) {
1599235783Skib		fb_helper->delayed_hotplug = true;
1600235783Skib		sx_xunlock(&dev->mode_config.mutex);
1601235783Skib		return 0;
1602235783Skib	}
1603235783Skib	DRM_DEBUG_KMS("\n");
1604235783Skib
1605235783Skib	max_width = fb_helper->fb->width;
1606235783Skib	max_height = fb_helper->fb->height;
1607235783Skib	bpp_sel = fb_helper->fb->bits_per_pixel;
1608235783Skib
1609235783Skib	count = drm_fb_helper_probe_connector_modes(fb_helper, max_width,
1610235783Skib						    max_height);
1611235783Skib	drm_setup_crtcs(fb_helper);
1612235783Skib	sx_xunlock(&dev->mode_config.mutex);
1613235783Skib
1614235783Skib	return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
1615235783Skib}
1616235783Skib
1617