xref: /linux/drivers/gpu/drm/drm_fb_helper.c (revision 91284224da5b15ec6c2b45e10fa5eccd1c92a204)
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 #include <linux/sysrq.h>
31 #include <linux/fb.h>
32 #include "drmP.h"
33 #include "drm_crtc.h"
34 #include "drm_fb_helper.h"
35 #include "drm_crtc_helper.h"
36 
37 MODULE_AUTHOR("David Airlie, Jesse Barnes");
38 MODULE_DESCRIPTION("DRM KMS helper");
39 MODULE_LICENSE("GPL and additional rights");
40 
41 static LIST_HEAD(kernel_fb_helper_list);
42 
43 int drm_fb_helper_add_connector(struct drm_connector *connector)
44 {
45 	connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
46 	if (!connector->fb_helper_private)
47 		return -ENOMEM;
48 
49 	return 0;
50 }
51 EXPORT_SYMBOL(drm_fb_helper_add_connector);
52 
53 static int my_atoi(const char *name)
54 {
55 	int val = 0;
56 
57 	for (;; name++) {
58 		switch (*name) {
59 		case '0' ... '9':
60 			val = 10*val+(*name-'0');
61 			break;
62 		default:
63 			return val;
64 		}
65 	}
66 }
67 
68 /**
69  * drm_fb_helper_connector_parse_command_line - parse command line for connector
70  * @connector - connector to parse line for
71  * @mode_option - per connector mode option
72  *
73  * This parses the connector specific then generic command lines for
74  * modes and options to configure the connector.
75  *
76  * This uses the same parameters as the fb modedb.c, except for extra
77  *	<xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
78  *
79  * enable/enable Digital/disable bit at the end
80  */
81 static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *connector,
82 						       const char *mode_option)
83 {
84 	const char *name;
85 	unsigned int namelen;
86 	int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
87 	unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
88 	int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
89 	int i;
90 	enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
91 	struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
92 	struct drm_fb_helper_cmdline_mode *cmdline_mode;
93 
94 	if (!fb_help_conn)
95 		return false;
96 
97 	cmdline_mode = &fb_help_conn->cmdline_mode;
98 	if (!mode_option)
99 		mode_option = fb_mode_option;
100 
101 	if (!mode_option) {
102 		cmdline_mode->specified = false;
103 		return false;
104 	}
105 
106 	name = mode_option;
107 	namelen = strlen(name);
108 	for (i = namelen-1; i >= 0; i--) {
109 		switch (name[i]) {
110 		case '@':
111 			namelen = i;
112 			if (!refresh_specified && !bpp_specified &&
113 			    !yres_specified) {
114 				refresh = my_atoi(&name[i+1]);
115 				refresh_specified = 1;
116 				if (cvt || rb)
117 					cvt = 0;
118 			} else
119 				goto done;
120 			break;
121 		case '-':
122 			namelen = i;
123 			if (!bpp_specified && !yres_specified) {
124 				bpp = my_atoi(&name[i+1]);
125 				bpp_specified = 1;
126 				if (cvt || rb)
127 					cvt = 0;
128 			} else
129 				goto done;
130 			break;
131 		case 'x':
132 			if (!yres_specified) {
133 				yres = my_atoi(&name[i+1]);
134 				yres_specified = 1;
135 			} else
136 				goto done;
137 		case '0' ... '9':
138 			break;
139 		case 'M':
140 			if (!yres_specified)
141 				cvt = 1;
142 			break;
143 		case 'R':
144 			if (!cvt)
145 				rb = 1;
146 			break;
147 		case 'm':
148 			if (!cvt)
149 				margins = 1;
150 			break;
151 		case 'i':
152 			if (!cvt)
153 				interlace = 1;
154 			break;
155 		case 'e':
156 			force = DRM_FORCE_ON;
157 			break;
158 		case 'D':
159 			if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) ||
160 			    (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
161 				force = DRM_FORCE_ON;
162 			else
163 				force = DRM_FORCE_ON_DIGITAL;
164 			break;
165 		case 'd':
166 			force = DRM_FORCE_OFF;
167 			break;
168 		default:
169 			goto done;
170 		}
171 	}
172 	if (i < 0 && yres_specified) {
173 		xres = my_atoi(name);
174 		res_specified = 1;
175 	}
176 done:
177 
178 	DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
179 		drm_get_connector_name(connector), xres, yres,
180 		(refresh) ? refresh : 60, (rb) ? " reduced blanking" :
181 		"", (margins) ? " with margins" : "", (interlace) ?
182 		" interlaced" : "");
183 
184 	if (force) {
185 		const char *s;
186 		switch (force) {
187 		case DRM_FORCE_OFF: s = "OFF"; break;
188 		case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
189 		default:
190 		case DRM_FORCE_ON: s = "ON"; break;
191 		}
192 
193 		DRM_INFO("forcing %s connector %s\n",
194 			 drm_get_connector_name(connector), s);
195 		connector->force = force;
196 	}
197 
198 	if (res_specified) {
199 		cmdline_mode->specified = true;
200 		cmdline_mode->xres = xres;
201 		cmdline_mode->yres = yres;
202 	}
203 
204 	if (refresh_specified) {
205 		cmdline_mode->refresh_specified = true;
206 		cmdline_mode->refresh = refresh;
207 	}
208 
209 	if (bpp_specified) {
210 		cmdline_mode->bpp_specified = true;
211 		cmdline_mode->bpp = bpp;
212 	}
213 	cmdline_mode->rb = rb ? true : false;
214 	cmdline_mode->cvt = cvt  ? true : false;
215 	cmdline_mode->interlace = interlace ? true : false;
216 
217 	return true;
218 }
219 
220 int drm_fb_helper_parse_command_line(struct drm_device *dev)
221 {
222 	struct drm_connector *connector;
223 
224 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
225 		char *option = NULL;
226 
227 		/* do something on return - turn off connector maybe */
228 		if (fb_get_options(drm_get_connector_name(connector), &option))
229 			continue;
230 
231 		drm_fb_helper_connector_parse_command_line(connector, option);
232 	}
233 	return 0;
234 }
235 
236 bool drm_fb_helper_force_kernel_mode(void)
237 {
238 	int i = 0;
239 	bool ret, error = false;
240 	struct drm_fb_helper *helper;
241 
242 	if (list_empty(&kernel_fb_helper_list))
243 		return false;
244 
245 	list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
246 		for (i = 0; i < helper->crtc_count; i++) {
247 			struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
248 			ret = drm_crtc_helper_set_config(mode_set);
249 			if (ret)
250 				error = true;
251 		}
252 	}
253 	return error;
254 }
255 
256 int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
257 			void *panic_str)
258 {
259 	DRM_ERROR("panic occurred, switching back to text console\n");
260 	return drm_fb_helper_force_kernel_mode();
261 	return 0;
262 }
263 EXPORT_SYMBOL(drm_fb_helper_panic);
264 
265 static struct notifier_block paniced = {
266 	.notifier_call = drm_fb_helper_panic,
267 };
268 
269 /**
270  * drm_fb_helper_restore - restore the framebuffer console (kernel) config
271  *
272  * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
273  */
274 void drm_fb_helper_restore(void)
275 {
276 	bool ret;
277 	ret = drm_fb_helper_force_kernel_mode();
278 	if (ret == true)
279 		DRM_ERROR("Failed to restore crtc configuration\n");
280 }
281 EXPORT_SYMBOL(drm_fb_helper_restore);
282 
283 #ifdef CONFIG_MAGIC_SYSRQ
284 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
285 {
286 	drm_fb_helper_restore();
287 }
288 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
289 
290 static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3)
291 {
292 	schedule_work(&drm_fb_helper_restore_work);
293 }
294 
295 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
296 	.handler = drm_fb_helper_sysrq,
297 	.help_msg = "force-fb(V)",
298 	.action_msg = "Restore framebuffer console",
299 };
300 #endif
301 
302 static void drm_fb_helper_on(struct fb_info *info)
303 {
304 	struct drm_fb_helper *fb_helper = info->par;
305 	struct drm_device *dev = fb_helper->dev;
306 	struct drm_crtc *crtc;
307 	struct drm_encoder *encoder;
308 	int i;
309 
310 	/*
311 	 * For each CRTC in this fb, turn the crtc on then,
312 	 * find all associated encoders and turn them on.
313 	 */
314 	for (i = 0; i < fb_helper->crtc_count; i++) {
315 		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
316 			struct drm_crtc_helper_funcs *crtc_funcs =
317 				crtc->helper_private;
318 
319 			/* Only mess with CRTCs in this fb */
320 			if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
321 			    !crtc->enabled)
322 				continue;
323 
324 			mutex_lock(&dev->mode_config.mutex);
325 			crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
326 			mutex_unlock(&dev->mode_config.mutex);
327 
328 			/* Found a CRTC on this fb, now find encoders */
329 			list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
330 				if (encoder->crtc == crtc) {
331 					struct drm_encoder_helper_funcs *encoder_funcs;
332 
333 					encoder_funcs = encoder->helper_private;
334 					mutex_lock(&dev->mode_config.mutex);
335 					encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
336 					mutex_unlock(&dev->mode_config.mutex);
337 				}
338 			}
339 		}
340 	}
341 }
342 
343 static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
344 {
345 	struct drm_fb_helper *fb_helper = info->par;
346 	struct drm_device *dev = fb_helper->dev;
347 	struct drm_crtc *crtc;
348 	struct drm_encoder *encoder;
349 	int i;
350 
351 	/*
352 	 * For each CRTC in this fb, find all associated encoders
353 	 * and turn them off, then turn off the CRTC.
354 	 */
355 	for (i = 0; i < fb_helper->crtc_count; i++) {
356 		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
357 			struct drm_crtc_helper_funcs *crtc_funcs =
358 				crtc->helper_private;
359 
360 			/* Only mess with CRTCs in this fb */
361 			if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
362 			    !crtc->enabled)
363 				continue;
364 
365 			/* Found a CRTC on this fb, now find encoders */
366 			list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
367 				if (encoder->crtc == crtc) {
368 					struct drm_encoder_helper_funcs *encoder_funcs;
369 
370 					encoder_funcs = encoder->helper_private;
371 					mutex_lock(&dev->mode_config.mutex);
372 					encoder_funcs->dpms(encoder, dpms_mode);
373 					mutex_unlock(&dev->mode_config.mutex);
374 				}
375 			}
376 			if (dpms_mode == DRM_MODE_DPMS_OFF) {
377 				mutex_lock(&dev->mode_config.mutex);
378 				crtc_funcs->dpms(crtc, dpms_mode);
379 				mutex_unlock(&dev->mode_config.mutex);
380 			}
381 		}
382 	}
383 }
384 
385 int drm_fb_helper_blank(int blank, struct fb_info *info)
386 {
387 	switch (blank) {
388 	case FB_BLANK_UNBLANK:
389 		drm_fb_helper_on(info);
390 		break;
391 	case FB_BLANK_NORMAL:
392 		drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
393 		break;
394 	case FB_BLANK_HSYNC_SUSPEND:
395 		drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
396 		break;
397 	case FB_BLANK_VSYNC_SUSPEND:
398 		drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
399 		break;
400 	case FB_BLANK_POWERDOWN:
401 		drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
402 		break;
403 	}
404 	return 0;
405 }
406 EXPORT_SYMBOL(drm_fb_helper_blank);
407 
408 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
409 {
410 	int i;
411 
412 	for (i = 0; i < helper->crtc_count; i++)
413 		kfree(helper->crtc_info[i].mode_set.connectors);
414 	kfree(helper->crtc_info);
415 }
416 
417 int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
418 {
419 	struct drm_device *dev = helper->dev;
420 	struct drm_crtc *crtc;
421 	int ret = 0;
422 	int i;
423 
424 	helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
425 	if (!helper->crtc_info)
426 		return -ENOMEM;
427 
428 	helper->crtc_count = crtc_count;
429 
430 	for (i = 0; i < crtc_count; i++) {
431 		helper->crtc_info[i].mode_set.connectors =
432 			kcalloc(max_conn_count,
433 				sizeof(struct drm_connector *),
434 				GFP_KERNEL);
435 
436 		if (!helper->crtc_info[i].mode_set.connectors) {
437 			ret = -ENOMEM;
438 			goto out_free;
439 		}
440 		helper->crtc_info[i].mode_set.num_connectors = 0;
441 	}
442 
443 	i = 0;
444 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
445 		helper->crtc_info[i].crtc_id = crtc->base.id;
446 		helper->crtc_info[i].mode_set.crtc = crtc;
447 		i++;
448 	}
449 	helper->conn_limit = max_conn_count;
450 	return 0;
451 out_free:
452 	drm_fb_helper_crtc_free(helper);
453 	return -ENOMEM;
454 }
455 EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
456 
457 static void setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
458 		     u16 blue, u16 regno, struct fb_info *info)
459 {
460 	struct drm_fb_helper *fb_helper = info->par;
461 	struct drm_framebuffer *fb = fb_helper->fb;
462 	int pindex;
463 
464 	pindex = regno;
465 
466 	if (fb->bits_per_pixel == 16) {
467 		pindex = regno << 3;
468 
469 		if (fb->depth == 16 && regno > 63)
470 			return;
471 		if (fb->depth == 15 && regno > 31)
472 			return;
473 
474 		if (fb->depth == 16) {
475 			u16 r, g, b;
476 			int i;
477 			if (regno < 32) {
478 				for (i = 0; i < 8; i++)
479 					fb_helper->funcs->gamma_set(crtc, red,
480 						green, blue, pindex + i);
481 			}
482 
483 			fb_helper->funcs->gamma_get(crtc, &r,
484 						    &g, &b,
485 						    pindex >> 1);
486 
487 			for (i = 0; i < 4; i++)
488 				fb_helper->funcs->gamma_set(crtc, r,
489 							    green, b,
490 							    (pindex >> 1) + i);
491 		}
492 	}
493 
494 	if (fb->depth != 16)
495 		fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
496 
497 	if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
498 		((u32 *) fb->pseudo_palette)[regno] =
499 			(regno << info->var.red.offset) |
500 			(regno << info->var.green.offset) |
501 			(regno << info->var.blue.offset);
502 	}
503 }
504 
505 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
506 {
507 	struct drm_fb_helper *fb_helper = info->par;
508 	struct drm_device *dev = fb_helper->dev;
509 	u16 *red, *green, *blue, *transp;
510 	struct drm_crtc *crtc;
511 	int i, rc = 0;
512 	int start;
513 
514 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
515 		struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
516 		for (i = 0; i < fb_helper->crtc_count; i++) {
517 			if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
518 				break;
519 		}
520 		if (i == fb_helper->crtc_count)
521 			continue;
522 
523 		red = cmap->red;
524 		green = cmap->green;
525 		blue = cmap->blue;
526 		transp = cmap->transp;
527 		start = cmap->start;
528 
529 		for (i = 0; i < cmap->len; i++) {
530 			u16 hred, hgreen, hblue, htransp = 0xffff;
531 
532 			hred = *red++;
533 			hgreen = *green++;
534 			hblue = *blue++;
535 
536 			if (transp)
537 				htransp = *transp++;
538 
539 			setcolreg(crtc, hred, hgreen, hblue, start++, info);
540 		}
541 		crtc_funcs->load_lut(crtc);
542 	}
543 	return rc;
544 }
545 EXPORT_SYMBOL(drm_fb_helper_setcmap);
546 
547 int drm_fb_helper_setcolreg(unsigned regno,
548 			    unsigned red,
549 			    unsigned green,
550 			    unsigned blue,
551 			    unsigned transp,
552 			    struct fb_info *info)
553 {
554 	struct drm_fb_helper *fb_helper = info->par;
555 	struct drm_device *dev = fb_helper->dev;
556 	struct drm_crtc *crtc;
557 	int i;
558 
559 	if (regno > 255)
560 		return 1;
561 
562 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
563 		struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
564 		for (i = 0; i < fb_helper->crtc_count; i++) {
565 			if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
566 				break;
567 		}
568 		if (i == fb_helper->crtc_count)
569 			continue;
570 
571 
572 		setcolreg(crtc, red, green, blue, regno, info);
573 		crtc_funcs->load_lut(crtc);
574 	}
575 	return 0;
576 }
577 EXPORT_SYMBOL(drm_fb_helper_setcolreg);
578 
579 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
580 			    struct fb_info *info)
581 {
582 	struct drm_fb_helper *fb_helper = info->par;
583 	struct drm_framebuffer *fb = fb_helper->fb;
584 	int depth;
585 
586 	if (var->pixclock == -1 || !var->pixclock)
587 		return -EINVAL;
588 
589 	/* Need to resize the fb object !!! */
590 	if (var->xres > fb->width || var->yres > fb->height) {
591 		DRM_ERROR("Requested width/height is greater than current fb "
592 			   "object %dx%d > %dx%d\n", var->xres, var->yres,
593 			   fb->width, fb->height);
594 		DRM_ERROR("Need resizing code.\n");
595 		return -EINVAL;
596 	}
597 
598 	switch (var->bits_per_pixel) {
599 	case 16:
600 		depth = (var->green.length == 6) ? 16 : 15;
601 		break;
602 	case 32:
603 		depth = (var->transp.length > 0) ? 32 : 24;
604 		break;
605 	default:
606 		depth = var->bits_per_pixel;
607 		break;
608 	}
609 
610 	switch (depth) {
611 	case 8:
612 		var->red.offset = 0;
613 		var->green.offset = 0;
614 		var->blue.offset = 0;
615 		var->red.length = 8;
616 		var->green.length = 8;
617 		var->blue.length = 8;
618 		var->transp.length = 0;
619 		var->transp.offset = 0;
620 		break;
621 	case 15:
622 		var->red.offset = 10;
623 		var->green.offset = 5;
624 		var->blue.offset = 0;
625 		var->red.length = 5;
626 		var->green.length = 5;
627 		var->blue.length = 5;
628 		var->transp.length = 1;
629 		var->transp.offset = 15;
630 		break;
631 	case 16:
632 		var->red.offset = 11;
633 		var->green.offset = 5;
634 		var->blue.offset = 0;
635 		var->red.length = 5;
636 		var->green.length = 6;
637 		var->blue.length = 5;
638 		var->transp.length = 0;
639 		var->transp.offset = 0;
640 		break;
641 	case 24:
642 		var->red.offset = 16;
643 		var->green.offset = 8;
644 		var->blue.offset = 0;
645 		var->red.length = 8;
646 		var->green.length = 8;
647 		var->blue.length = 8;
648 		var->transp.length = 0;
649 		var->transp.offset = 0;
650 		break;
651 	case 32:
652 		var->red.offset = 16;
653 		var->green.offset = 8;
654 		var->blue.offset = 0;
655 		var->red.length = 8;
656 		var->green.length = 8;
657 		var->blue.length = 8;
658 		var->transp.length = 8;
659 		var->transp.offset = 24;
660 		break;
661 	default:
662 		return -EINVAL;
663 	}
664 	return 0;
665 }
666 EXPORT_SYMBOL(drm_fb_helper_check_var);
667 
668 /* this will let fbcon do the mode init */
669 int drm_fb_helper_set_par(struct fb_info *info)
670 {
671 	struct drm_fb_helper *fb_helper = info->par;
672 	struct drm_device *dev = fb_helper->dev;
673 	struct fb_var_screeninfo *var = &info->var;
674 	struct drm_crtc *crtc;
675 	int ret;
676 	int i;
677 
678 	if (var->pixclock != -1) {
679 		DRM_ERROR("PIXEL CLCOK SET\n");
680 		return -EINVAL;
681 	}
682 
683 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
684 
685 		for (i = 0; i < fb_helper->crtc_count; i++) {
686 			if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
687 				break;
688 		}
689 		if (i == fb_helper->crtc_count)
690 			continue;
691 
692 		if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
693 			mutex_lock(&dev->mode_config.mutex);
694 			ret = crtc->funcs->set_config(&fb_helper->crtc_info->mode_set);
695 			mutex_unlock(&dev->mode_config.mutex);
696 			if (ret)
697 				return ret;
698 		}
699 	}
700 	return 0;
701 }
702 EXPORT_SYMBOL(drm_fb_helper_set_par);
703 
704 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
705 			      struct fb_info *info)
706 {
707 	struct drm_fb_helper *fb_helper = info->par;
708 	struct drm_device *dev = fb_helper->dev;
709 	struct drm_mode_set *modeset;
710 	struct drm_crtc *crtc;
711 	int ret = 0;
712 	int i;
713 
714 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
715 		for (i = 0; i < fb_helper->crtc_count; i++) {
716 			if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
717 				break;
718 		}
719 
720 		if (i == fb_helper->crtc_count)
721 			continue;
722 
723 		modeset = &fb_helper->crtc_info[i].mode_set;
724 
725 		modeset->x = var->xoffset;
726 		modeset->y = var->yoffset;
727 
728 		if (modeset->num_connectors) {
729 			mutex_lock(&dev->mode_config.mutex);
730 			ret = crtc->funcs->set_config(modeset);
731 			mutex_unlock(&dev->mode_config.mutex);
732 			if (!ret) {
733 				info->var.xoffset = var->xoffset;
734 				info->var.yoffset = var->yoffset;
735 			}
736 		}
737 	}
738 	return ret;
739 }
740 EXPORT_SYMBOL(drm_fb_helper_pan_display);
741 
742 int drm_fb_helper_single_fb_probe(struct drm_device *dev,
743 				  int preferred_bpp,
744 				  int (*fb_create)(struct drm_device *dev,
745 						   uint32_t fb_width,
746 						   uint32_t fb_height,
747 						   uint32_t surface_width,
748 						   uint32_t surface_height,
749 						   uint32_t surface_depth,
750 						   uint32_t surface_bpp,
751 						   struct drm_framebuffer **fb_ptr))
752 {
753 	struct drm_crtc *crtc;
754 	struct drm_connector *connector;
755 	unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
756 	unsigned int surface_width = 0, surface_height = 0;
757 	int new_fb = 0;
758 	int crtc_count = 0;
759 	int ret, i, conn_count = 0;
760 	struct fb_info *info;
761 	struct drm_framebuffer *fb;
762 	struct drm_mode_set *modeset = NULL;
763 	struct drm_fb_helper *fb_helper;
764 	uint32_t surface_depth = 24, surface_bpp = 32;
765 
766 	/* if driver picks 8 or 16 by default use that
767 	   for both depth/bpp */
768 	if (preferred_bpp != surface_bpp) {
769 		surface_depth = surface_bpp = preferred_bpp;
770 	}
771 	/* first up get a count of crtcs now in use and new min/maxes width/heights */
772 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
773 		struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
774 
775 		struct drm_fb_helper_cmdline_mode *cmdline_mode;
776 
777 		if (!fb_help_conn)
778 			continue;
779 
780 		cmdline_mode = &fb_help_conn->cmdline_mode;
781 
782 		if (cmdline_mode->bpp_specified) {
783 			switch (cmdline_mode->bpp) {
784 			case 8:
785 				surface_depth = surface_bpp = 8;
786 				break;
787 			case 15:
788 				surface_depth = 15;
789 				surface_bpp = 16;
790 				break;
791 			case 16:
792 				surface_depth = surface_bpp = 16;
793 				break;
794 			case 24:
795 				surface_depth = surface_bpp = 24;
796 				break;
797 			case 32:
798 				surface_depth = 24;
799 				surface_bpp = 32;
800 				break;
801 			}
802 			break;
803 		}
804 	}
805 
806 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
807 		if (drm_helper_crtc_in_use(crtc)) {
808 			if (crtc->desired_mode) {
809 				if (crtc->desired_mode->hdisplay < fb_width)
810 					fb_width = crtc->desired_mode->hdisplay;
811 
812 				if (crtc->desired_mode->vdisplay < fb_height)
813 					fb_height = crtc->desired_mode->vdisplay;
814 
815 				if (crtc->desired_mode->hdisplay > surface_width)
816 					surface_width = crtc->desired_mode->hdisplay;
817 
818 				if (crtc->desired_mode->vdisplay > surface_height)
819 					surface_height = crtc->desired_mode->vdisplay;
820 			}
821 			crtc_count++;
822 		}
823 	}
824 
825 	if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
826 		/* hmm everyone went away - assume VGA cable just fell out
827 		   and will come back later. */
828 		return 0;
829 	}
830 
831 	/* do we have an fb already? */
832 	if (list_empty(&dev->mode_config.fb_kernel_list)) {
833 		ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
834 				   surface_height, surface_depth, surface_bpp,
835 				   &fb);
836 		if (ret)
837 			return -EINVAL;
838 		new_fb = 1;
839 	} else {
840 		fb = list_first_entry(&dev->mode_config.fb_kernel_list,
841 				      struct drm_framebuffer, filp_head);
842 
843 		/* if someone hotplugs something bigger than we have already allocated, we are pwned.
844 		   As really we can't resize an fbdev that is in the wild currently due to fbdev
845 		   not really being designed for the lower layers moving stuff around under it.
846 		   - so in the grand style of things - punt. */
847 		if ((fb->width < surface_width) ||
848 		    (fb->height < surface_height)) {
849 			DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
850 			return -EINVAL;
851 		}
852 	}
853 
854 	info = fb->fbdev;
855 	fb_helper = info->par;
856 
857 	crtc_count = 0;
858 	/* okay we need to setup new connector sets in the crtcs */
859 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
860 		modeset = &fb_helper->crtc_info[crtc_count].mode_set;
861 		modeset->fb = fb;
862 		conn_count = 0;
863 		list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
864 			if (connector->encoder)
865 				if (connector->encoder->crtc == modeset->crtc) {
866 					modeset->connectors[conn_count] = connector;
867 					conn_count++;
868 					if (conn_count > fb_helper->conn_limit)
869 						BUG();
870 				}
871 		}
872 
873 		for (i = conn_count; i < fb_helper->conn_limit; i++)
874 			modeset->connectors[i] = NULL;
875 
876 		modeset->crtc = crtc;
877 		crtc_count++;
878 
879 		modeset->num_connectors = conn_count;
880 		if (modeset->crtc->desired_mode) {
881 			if (modeset->mode)
882 				drm_mode_destroy(dev, modeset->mode);
883 			modeset->mode = drm_mode_duplicate(dev,
884 							   modeset->crtc->desired_mode);
885 		}
886 	}
887 	fb_helper->crtc_count = crtc_count;
888 	fb_helper->fb = fb;
889 
890 	if (new_fb) {
891 		info->var.pixclock = -1;
892 		if (register_framebuffer(info) < 0)
893 			return -EINVAL;
894 	} else {
895 		drm_fb_helper_set_par(info);
896 	}
897 	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
898 	       info->fix.id);
899 
900 	/* Switch back to kernel console on panic */
901 	/* multi card linked list maybe */
902 	if (list_empty(&kernel_fb_helper_list)) {
903 		printk(KERN_INFO "registered panic notifier\n");
904 		atomic_notifier_chain_register(&panic_notifier_list,
905 					       &paniced);
906 		register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
907 	}
908 	list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
909 	return 0;
910 }
911 EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
912 
913 void drm_fb_helper_free(struct drm_fb_helper *helper)
914 {
915 	list_del(&helper->kernel_fb_list);
916 	if (list_empty(&kernel_fb_helper_list)) {
917 		printk(KERN_INFO "unregistered panic notifier\n");
918 		atomic_notifier_chain_unregister(&panic_notifier_list,
919 						 &paniced);
920 		unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
921 	}
922 	drm_fb_helper_crtc_free(helper);
923 }
924 EXPORT_SYMBOL(drm_fb_helper_free);
925 
926 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
927 			    uint32_t depth)
928 {
929 	info->fix.type = FB_TYPE_PACKED_PIXELS;
930 	info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
931 		FB_VISUAL_DIRECTCOLOR;
932 	info->fix.type_aux = 0;
933 	info->fix.xpanstep = 1; /* doing it in hw */
934 	info->fix.ypanstep = 1; /* doing it in hw */
935 	info->fix.ywrapstep = 0;
936 	info->fix.accel = FB_ACCEL_NONE;
937 	info->fix.type_aux = 0;
938 
939 	info->fix.line_length = pitch;
940 	return;
941 }
942 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
943 
944 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
945 			    uint32_t fb_width, uint32_t fb_height)
946 {
947 	info->pseudo_palette = fb->pseudo_palette;
948 	info->var.xres_virtual = fb->width;
949 	info->var.yres_virtual = fb->height;
950 	info->var.bits_per_pixel = fb->bits_per_pixel;
951 	info->var.xoffset = 0;
952 	info->var.yoffset = 0;
953 	info->var.activate = FB_ACTIVATE_NOW;
954 	info->var.height = -1;
955 	info->var.width = -1;
956 
957 	switch (fb->depth) {
958 	case 8:
959 		info->var.red.offset = 0;
960 		info->var.green.offset = 0;
961 		info->var.blue.offset = 0;
962 		info->var.red.length = 8; /* 8bit DAC */
963 		info->var.green.length = 8;
964 		info->var.blue.length = 8;
965 		info->var.transp.offset = 0;
966 		info->var.transp.length = 0;
967 		break;
968 	case 15:
969 		info->var.red.offset = 10;
970 		info->var.green.offset = 5;
971 		info->var.blue.offset = 0;
972 		info->var.red.length = 5;
973 		info->var.green.length = 5;
974 		info->var.blue.length = 5;
975 		info->var.transp.offset = 15;
976 		info->var.transp.length = 1;
977 		break;
978 	case 16:
979 		info->var.red.offset = 11;
980 		info->var.green.offset = 5;
981 		info->var.blue.offset = 0;
982 		info->var.red.length = 5;
983 		info->var.green.length = 6;
984 		info->var.blue.length = 5;
985 		info->var.transp.offset = 0;
986 		break;
987 	case 24:
988 		info->var.red.offset = 16;
989 		info->var.green.offset = 8;
990 		info->var.blue.offset = 0;
991 		info->var.red.length = 8;
992 		info->var.green.length = 8;
993 		info->var.blue.length = 8;
994 		info->var.transp.offset = 0;
995 		info->var.transp.length = 0;
996 		break;
997 	case 32:
998 		info->var.red.offset = 16;
999 		info->var.green.offset = 8;
1000 		info->var.blue.offset = 0;
1001 		info->var.red.length = 8;
1002 		info->var.green.length = 8;
1003 		info->var.blue.length = 8;
1004 		info->var.transp.offset = 24;
1005 		info->var.transp.length = 8;
1006 		break;
1007 	default:
1008 		break;
1009 	}
1010 
1011 	info->var.xres = fb_width;
1012 	info->var.yres = fb_height;
1013 }
1014 EXPORT_SYMBOL(drm_fb_helper_fill_var);
1015