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