xref: /linux/drivers/gpu/drm/drm_fb_helper.c (revision 040932cdcfca9b0ac55a4f74f194c2e2c8a2527b)
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 bool drm_fb_helper_force_kernel_mode(void)
44 {
45 	int i = 0;
46 	bool ret, error = false;
47 	struct drm_fb_helper *helper;
48 
49 	if (list_empty(&kernel_fb_helper_list))
50 		return false;
51 
52 	list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
53 		for (i = 0; i < helper->crtc_count; i++) {
54 			struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
55 			ret = drm_crtc_helper_set_config(mode_set);
56 			if (ret)
57 				error = true;
58 		}
59 	}
60 	return error;
61 }
62 
63 int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
64 			void *panic_str)
65 {
66 	DRM_ERROR("panic occurred, switching back to text console\n");
67 	return drm_fb_helper_force_kernel_mode();
68 	return 0;
69 }
70 EXPORT_SYMBOL(drm_fb_helper_panic);
71 
72 static struct notifier_block paniced = {
73 	.notifier_call = drm_fb_helper_panic,
74 };
75 
76 /**
77  * drm_fb_helper_restore - restore the framebuffer console (kernel) config
78  *
79  * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
80  */
81 void drm_fb_helper_restore(void)
82 {
83 	bool ret;
84 	ret = drm_fb_helper_force_kernel_mode();
85 	if (ret == true)
86 		DRM_ERROR("Failed to restore crtc configuration\n");
87 }
88 EXPORT_SYMBOL(drm_fb_helper_restore);
89 
90 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
91 {
92 	drm_fb_helper_restore();
93 }
94 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
95 
96 static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3)
97 {
98 	schedule_work(&drm_fb_helper_restore_work);
99 }
100 
101 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
102 	.handler = drm_fb_helper_sysrq,
103 	.help_msg = "force-fb(V)",
104 	.action_msg = "Restore framebuffer console",
105 };
106 
107 static void drm_fb_helper_on(struct fb_info *info)
108 {
109 	struct drm_fb_helper *fb_helper = info->par;
110 	struct drm_device *dev = fb_helper->dev;
111 	struct drm_crtc *crtc;
112 	struct drm_encoder *encoder;
113 	int i;
114 
115 	/*
116 	 * For each CRTC in this fb, turn the crtc on then,
117 	 * find all associated encoders and turn them on.
118 	 */
119 	for (i = 0; i < fb_helper->crtc_count; i++) {
120 		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
121 			struct drm_crtc_helper_funcs *crtc_funcs =
122 				crtc->helper_private;
123 
124 			/* Only mess with CRTCs in this fb */
125 			if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
126 			    !crtc->enabled)
127 				continue;
128 
129 			mutex_lock(&dev->mode_config.mutex);
130 			crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
131 			mutex_unlock(&dev->mode_config.mutex);
132 
133 			/* Found a CRTC on this fb, now find encoders */
134 			list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
135 				if (encoder->crtc == crtc) {
136 					struct drm_encoder_helper_funcs *encoder_funcs;
137 
138 					encoder_funcs = encoder->helper_private;
139 					mutex_lock(&dev->mode_config.mutex);
140 					encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
141 					mutex_unlock(&dev->mode_config.mutex);
142 				}
143 			}
144 		}
145 	}
146 }
147 
148 static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
149 {
150 	struct drm_fb_helper *fb_helper = info->par;
151 	struct drm_device *dev = fb_helper->dev;
152 	struct drm_crtc *crtc;
153 	struct drm_encoder *encoder;
154 	int i;
155 
156 	/*
157 	 * For each CRTC in this fb, find all associated encoders
158 	 * and turn them off, then turn off the CRTC.
159 	 */
160 	for (i = 0; i < fb_helper->crtc_count; i++) {
161 		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
162 			struct drm_crtc_helper_funcs *crtc_funcs =
163 				crtc->helper_private;
164 
165 			/* Only mess with CRTCs in this fb */
166 			if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
167 			    !crtc->enabled)
168 				continue;
169 
170 			/* Found a CRTC on this fb, now find encoders */
171 			list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
172 				if (encoder->crtc == crtc) {
173 					struct drm_encoder_helper_funcs *encoder_funcs;
174 
175 					encoder_funcs = encoder->helper_private;
176 					mutex_lock(&dev->mode_config.mutex);
177 					encoder_funcs->dpms(encoder, dpms_mode);
178 					mutex_unlock(&dev->mode_config.mutex);
179 				}
180 			}
181 			if (dpms_mode == DRM_MODE_DPMS_OFF) {
182 				mutex_lock(&dev->mode_config.mutex);
183 				crtc_funcs->dpms(crtc, dpms_mode);
184 				mutex_unlock(&dev->mode_config.mutex);
185 			}
186 		}
187 	}
188 }
189 
190 int drm_fb_helper_blank(int blank, struct fb_info *info)
191 {
192 	switch (blank) {
193 	case FB_BLANK_UNBLANK:
194 		drm_fb_helper_on(info);
195 		break;
196 	case FB_BLANK_NORMAL:
197 		drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
198 		break;
199 	case FB_BLANK_HSYNC_SUSPEND:
200 		drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
201 		break;
202 	case FB_BLANK_VSYNC_SUSPEND:
203 		drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
204 		break;
205 	case FB_BLANK_POWERDOWN:
206 		drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
207 		break;
208 	}
209 	return 0;
210 }
211 EXPORT_SYMBOL(drm_fb_helper_blank);
212 
213 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
214 {
215 	int i;
216 
217 	for (i = 0; i < helper->crtc_count; i++)
218 		kfree(helper->crtc_info[i].mode_set.connectors);
219 	kfree(helper->crtc_info);
220 }
221 
222 int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
223 {
224 	struct drm_device *dev = helper->dev;
225 	struct drm_crtc *crtc;
226 	int ret = 0;
227 	int i;
228 
229 	helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
230 	if (!helper->crtc_info)
231 		return -ENOMEM;
232 
233 	helper->crtc_count = crtc_count;
234 
235 	for (i = 0; i < crtc_count; i++) {
236 		helper->crtc_info[i].mode_set.connectors =
237 			kcalloc(max_conn_count,
238 				sizeof(struct drm_connector *),
239 				GFP_KERNEL);
240 
241 		if (!helper->crtc_info[i].mode_set.connectors) {
242 			ret = -ENOMEM;
243 			goto out_free;
244 		}
245 		helper->crtc_info[i].mode_set.num_connectors = 0;
246 	}
247 
248 	i = 0;
249 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
250 		helper->crtc_info[i].crtc_id = crtc->base.id;
251 		helper->crtc_info[i].mode_set.crtc = crtc;
252 		i++;
253 	}
254 	helper->conn_limit = max_conn_count;
255 	return 0;
256 out_free:
257 	drm_fb_helper_crtc_free(helper);
258 	return -ENOMEM;
259 }
260 EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
261 
262 int drm_fb_helper_setcolreg(unsigned regno,
263 			    unsigned red,
264 			    unsigned green,
265 			    unsigned blue,
266 			    unsigned transp,
267 			    struct fb_info *info)
268 {
269 	struct drm_fb_helper *fb_helper = info->par;
270 	struct drm_device *dev = fb_helper->dev;
271 	struct drm_crtc *crtc;
272 	int i;
273 
274 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
275 		struct drm_framebuffer *fb = fb_helper->fb;
276 
277 		for (i = 0; i < fb_helper->crtc_count; i++) {
278 			if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
279 				break;
280 		}
281 		if (i == fb_helper->crtc_count)
282 			continue;
283 
284 		if (regno > 255)
285 			return 1;
286 
287 		if (fb->depth == 8) {
288 			fb_helper->funcs->gamma_set(crtc, red, green, blue, regno);
289 			return 0;
290 		}
291 
292 		if (regno < 16) {
293 			switch (fb->depth) {
294 			case 15:
295 				fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
296 					((green & 0xf800) >>  6) |
297 					((blue & 0xf800) >> 11);
298 				break;
299 			case 16:
300 				fb->pseudo_palette[regno] = (red & 0xf800) |
301 					((green & 0xfc00) >>  5) |
302 					((blue  & 0xf800) >> 11);
303 				break;
304 			case 24:
305 			case 32:
306 				fb->pseudo_palette[regno] =
307 					(((red >> 8) & 0xff) << info->var.red.offset) |
308 					(((green >> 8) & 0xff) << info->var.green.offset) |
309 					(((blue >> 8) & 0xff) << info->var.blue.offset);
310 				break;
311 			}
312 		}
313 	}
314 	return 0;
315 }
316 EXPORT_SYMBOL(drm_fb_helper_setcolreg);
317 
318 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
319 			    struct fb_info *info)
320 {
321 	struct drm_fb_helper *fb_helper = info->par;
322 	struct drm_framebuffer *fb = fb_helper->fb;
323 	int depth;
324 
325 	if (var->pixclock == -1 || !var->pixclock)
326 		return -EINVAL;
327 
328 	/* Need to resize the fb object !!! */
329 	if (var->xres > fb->width || var->yres > fb->height) {
330 		DRM_ERROR("Requested width/height is greater than current fb "
331 			   "object %dx%d > %dx%d\n", var->xres, var->yres,
332 			   fb->width, fb->height);
333 		DRM_ERROR("Need resizing code.\n");
334 		return -EINVAL;
335 	}
336 
337 	switch (var->bits_per_pixel) {
338 	case 16:
339 		depth = (var->green.length == 6) ? 16 : 15;
340 		break;
341 	case 32:
342 		depth = (var->transp.length > 0) ? 32 : 24;
343 		break;
344 	default:
345 		depth = var->bits_per_pixel;
346 		break;
347 	}
348 
349 	switch (depth) {
350 	case 8:
351 		var->red.offset = 0;
352 		var->green.offset = 0;
353 		var->blue.offset = 0;
354 		var->red.length = 8;
355 		var->green.length = 8;
356 		var->blue.length = 8;
357 		var->transp.length = 0;
358 		var->transp.offset = 0;
359 		break;
360 	case 15:
361 		var->red.offset = 10;
362 		var->green.offset = 5;
363 		var->blue.offset = 0;
364 		var->red.length = 5;
365 		var->green.length = 5;
366 		var->blue.length = 5;
367 		var->transp.length = 1;
368 		var->transp.offset = 15;
369 		break;
370 	case 16:
371 		var->red.offset = 11;
372 		var->green.offset = 5;
373 		var->blue.offset = 0;
374 		var->red.length = 5;
375 		var->green.length = 6;
376 		var->blue.length = 5;
377 		var->transp.length = 0;
378 		var->transp.offset = 0;
379 		break;
380 	case 24:
381 		var->red.offset = 16;
382 		var->green.offset = 8;
383 		var->blue.offset = 0;
384 		var->red.length = 8;
385 		var->green.length = 8;
386 		var->blue.length = 8;
387 		var->transp.length = 0;
388 		var->transp.offset = 0;
389 		break;
390 	case 32:
391 		var->red.offset = 16;
392 		var->green.offset = 8;
393 		var->blue.offset = 0;
394 		var->red.length = 8;
395 		var->green.length = 8;
396 		var->blue.length = 8;
397 		var->transp.length = 8;
398 		var->transp.offset = 24;
399 		break;
400 	default:
401 		return -EINVAL;
402 	}
403 	return 0;
404 }
405 EXPORT_SYMBOL(drm_fb_helper_check_var);
406 
407 /* this will let fbcon do the mode init */
408 int drm_fb_helper_set_par(struct fb_info *info)
409 {
410 	struct drm_fb_helper *fb_helper = info->par;
411 	struct drm_device *dev = fb_helper->dev;
412 	struct fb_var_screeninfo *var = &info->var;
413 	struct drm_crtc *crtc;
414 	int ret;
415 	int i;
416 
417 	if (var->pixclock != -1) {
418 		DRM_ERROR("PIXEL CLCOK SET\n");
419 		return -EINVAL;
420 	}
421 
422 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
423 
424 		for (i = 0; i < fb_helper->crtc_count; i++) {
425 			if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
426 				break;
427 		}
428 		if (i == fb_helper->crtc_count)
429 			continue;
430 
431 		if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
432 			mutex_lock(&dev->mode_config.mutex);
433 			ret = crtc->funcs->set_config(&fb_helper->crtc_info->mode_set);
434 			mutex_unlock(&dev->mode_config.mutex);
435 			if (ret)
436 				return ret;
437 		}
438 	}
439 	return 0;
440 }
441 EXPORT_SYMBOL(drm_fb_helper_set_par);
442 
443 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
444 			      struct fb_info *info)
445 {
446 	struct drm_fb_helper *fb_helper = info->par;
447 	struct drm_device *dev = fb_helper->dev;
448 	struct drm_mode_set *modeset;
449 	struct drm_crtc *crtc;
450 	int ret = 0;
451 	int i;
452 
453 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
454 		for (i = 0; i < fb_helper->crtc_count; i++) {
455 			if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
456 				break;
457 		}
458 
459 		if (i == fb_helper->crtc_count)
460 			continue;
461 
462 		modeset = &fb_helper->crtc_info[i].mode_set;
463 
464 		modeset->x = var->xoffset;
465 		modeset->y = var->yoffset;
466 
467 		if (modeset->num_connectors) {
468 			mutex_lock(&dev->mode_config.mutex);
469 			ret = crtc->funcs->set_config(modeset);
470 			mutex_unlock(&dev->mode_config.mutex);
471 			if (!ret) {
472 				info->var.xoffset = var->xoffset;
473 				info->var.yoffset = var->yoffset;
474 			}
475 		}
476 	}
477 	return ret;
478 }
479 EXPORT_SYMBOL(drm_fb_helper_pan_display);
480 
481 int drm_fb_helper_single_fb_probe(struct drm_device *dev,
482 				  int (*fb_create)(struct drm_device *dev,
483 						   uint32_t fb_width,
484 						   uint32_t fb_height,
485 						   uint32_t surface_width,
486 						   uint32_t surface_height,
487 						   struct drm_framebuffer **fb_ptr))
488 {
489 	struct drm_crtc *crtc;
490 	struct drm_connector *connector;
491 	unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
492 	unsigned int surface_width = 0, surface_height = 0;
493 	int new_fb = 0;
494 	int crtc_count = 0;
495 	int ret, i, conn_count = 0;
496 	struct fb_info *info;
497 	struct drm_framebuffer *fb;
498 	struct drm_mode_set *modeset = NULL;
499 	struct drm_fb_helper *fb_helper;
500 
501 	/* first up get a count of crtcs now in use and new min/maxes width/heights */
502 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
503 		if (drm_helper_crtc_in_use(crtc)) {
504 			if (crtc->desired_mode) {
505 				if (crtc->desired_mode->hdisplay < fb_width)
506 					fb_width = crtc->desired_mode->hdisplay;
507 
508 				if (crtc->desired_mode->vdisplay < fb_height)
509 					fb_height = crtc->desired_mode->vdisplay;
510 
511 				if (crtc->desired_mode->hdisplay > surface_width)
512 					surface_width = crtc->desired_mode->hdisplay;
513 
514 				if (crtc->desired_mode->vdisplay > surface_height)
515 					surface_height = crtc->desired_mode->vdisplay;
516 			}
517 			crtc_count++;
518 		}
519 	}
520 
521 	if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
522 		/* hmm everyone went away - assume VGA cable just fell out
523 		   and will come back later. */
524 		return 0;
525 	}
526 
527 	/* do we have an fb already? */
528 	if (list_empty(&dev->mode_config.fb_kernel_list)) {
529 		ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
530 				   surface_height, &fb);
531 		if (ret)
532 			return -EINVAL;
533 		new_fb = 1;
534 	} else {
535 		fb = list_first_entry(&dev->mode_config.fb_kernel_list,
536 				      struct drm_framebuffer, filp_head);
537 
538 		/* if someone hotplugs something bigger than we have already allocated, we are pwned.
539 		   As really we can't resize an fbdev that is in the wild currently due to fbdev
540 		   not really being designed for the lower layers moving stuff around under it.
541 		   - so in the grand style of things - punt. */
542 		if ((fb->width < surface_width) ||
543 		    (fb->height < surface_height)) {
544 			DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
545 			return -EINVAL;
546 		}
547 	}
548 
549 	info = fb->fbdev;
550 	fb_helper = info->par;
551 
552 	crtc_count = 0;
553 	/* okay we need to setup new connector sets in the crtcs */
554 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
555 		modeset = &fb_helper->crtc_info[crtc_count].mode_set;
556 		modeset->fb = fb;
557 		conn_count = 0;
558 		list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
559 			if (connector->encoder)
560 				if (connector->encoder->crtc == modeset->crtc) {
561 					modeset->connectors[conn_count] = connector;
562 					conn_count++;
563 					if (conn_count > fb_helper->conn_limit)
564 						BUG();
565 				}
566 		}
567 
568 		for (i = conn_count; i < fb_helper->conn_limit; i++)
569 			modeset->connectors[i] = NULL;
570 
571 		modeset->crtc = crtc;
572 		crtc_count++;
573 
574 		modeset->num_connectors = conn_count;
575 		if (modeset->crtc->desired_mode) {
576 			if (modeset->mode)
577 				drm_mode_destroy(dev, modeset->mode);
578 			modeset->mode = drm_mode_duplicate(dev,
579 							   modeset->crtc->desired_mode);
580 		}
581 	}
582 	fb_helper->crtc_count = crtc_count;
583 	fb_helper->fb = fb;
584 
585 	if (new_fb) {
586 		info->var.pixclock = -1;
587 		if (register_framebuffer(info) < 0)
588 			return -EINVAL;
589 	} else {
590 		drm_fb_helper_set_par(info);
591 	}
592 	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
593 	       info->fix.id);
594 
595 	/* Switch back to kernel console on panic */
596 	/* multi card linked list maybe */
597 	if (list_empty(&kernel_fb_helper_list)) {
598 		printk(KERN_INFO "registered panic notifier\n");
599 		atomic_notifier_chain_register(&panic_notifier_list,
600 					       &paniced);
601 		register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
602 	}
603 	list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
604 	return 0;
605 }
606 EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
607 
608 void drm_fb_helper_free(struct drm_fb_helper *helper)
609 {
610 	list_del(&helper->kernel_fb_list);
611 	if (list_empty(&kernel_fb_helper_list)) {
612 		printk(KERN_INFO "unregistered panic notifier\n");
613 		atomic_notifier_chain_unregister(&panic_notifier_list,
614 						 &paniced);
615 		unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
616 	}
617 	drm_fb_helper_crtc_free(helper);
618 }
619 EXPORT_SYMBOL(drm_fb_helper_free);
620 
621 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch)
622 {
623 	info->fix.type = FB_TYPE_PACKED_PIXELS;
624 	info->fix.visual = FB_VISUAL_TRUECOLOR;
625 	info->fix.type_aux = 0;
626 	info->fix.xpanstep = 1; /* doing it in hw */
627 	info->fix.ypanstep = 1; /* doing it in hw */
628 	info->fix.ywrapstep = 0;
629 	info->fix.accel = FB_ACCEL_NONE;
630 	info->fix.type_aux = 0;
631 
632 	info->fix.line_length = pitch;
633 	return;
634 }
635 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
636 
637 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
638 			    uint32_t fb_width, uint32_t fb_height)
639 {
640 	info->pseudo_palette = fb->pseudo_palette;
641 	info->var.xres_virtual = fb->width;
642 	info->var.yres_virtual = fb->height;
643 	info->var.bits_per_pixel = fb->bits_per_pixel;
644 	info->var.xoffset = 0;
645 	info->var.yoffset = 0;
646 	info->var.activate = FB_ACTIVATE_NOW;
647 	info->var.height = -1;
648 	info->var.width = -1;
649 
650 	switch (fb->depth) {
651 	case 8:
652 		info->var.red.offset = 0;
653 		info->var.green.offset = 0;
654 		info->var.blue.offset = 0;
655 		info->var.red.length = 8; /* 8bit DAC */
656 		info->var.green.length = 8;
657 		info->var.blue.length = 8;
658 		info->var.transp.offset = 0;
659 		info->var.transp.length = 0;
660 		break;
661 	case 15:
662 		info->var.red.offset = 10;
663 		info->var.green.offset = 5;
664 		info->var.blue.offset = 0;
665 		info->var.red.length = 5;
666 		info->var.green.length = 5;
667 		info->var.blue.length = 5;
668 		info->var.transp.offset = 15;
669 		info->var.transp.length = 1;
670 		break;
671 	case 16:
672 		info->var.red.offset = 11;
673 		info->var.green.offset = 5;
674 		info->var.blue.offset = 0;
675 		info->var.red.length = 5;
676 		info->var.green.length = 6;
677 		info->var.blue.length = 5;
678 		info->var.transp.offset = 0;
679 		break;
680 	case 24:
681 		info->var.red.offset = 16;
682 		info->var.green.offset = 8;
683 		info->var.blue.offset = 0;
684 		info->var.red.length = 8;
685 		info->var.green.length = 8;
686 		info->var.blue.length = 8;
687 		info->var.transp.offset = 0;
688 		info->var.transp.length = 0;
689 		break;
690 	case 32:
691 		info->var.red.offset = 16;
692 		info->var.green.offset = 8;
693 		info->var.blue.offset = 0;
694 		info->var.red.length = 8;
695 		info->var.green.length = 8;
696 		info->var.blue.length = 8;
697 		info->var.transp.offset = 24;
698 		info->var.transp.length = 8;
699 		break;
700 	default:
701 		break;
702 	}
703 
704 	info->var.xres = fb_width;
705 	info->var.yres = fb_height;
706 }
707 EXPORT_SYMBOL(drm_fb_helper_fill_var);
708