Merge branch 'master' into export-slabh
[kernel.git] / 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 #else
288 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
289 #endif
290
291 static void drm_fb_helper_on(struct fb_info *info)
292 {
293         struct drm_fb_helper *fb_helper = info->par;
294         struct drm_device *dev = fb_helper->dev;
295         struct drm_crtc *crtc;
296         struct drm_encoder *encoder;
297         int i;
298
299         /*
300          * For each CRTC in this fb, turn the crtc on then,
301          * find all associated encoders and turn them on.
302          */
303         for (i = 0; i < fb_helper->crtc_count; i++) {
304                 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
305                         struct drm_crtc_helper_funcs *crtc_funcs =
306                                 crtc->helper_private;
307
308                         /* Only mess with CRTCs in this fb */
309                         if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
310                             !crtc->enabled)
311                                 continue;
312
313                         mutex_lock(&dev->mode_config.mutex);
314                         crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
315                         mutex_unlock(&dev->mode_config.mutex);
316
317                         /* Found a CRTC on this fb, now find encoders */
318                         list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
319                                 if (encoder->crtc == crtc) {
320                                         struct drm_encoder_helper_funcs *encoder_funcs;
321
322                                         encoder_funcs = encoder->helper_private;
323                                         mutex_lock(&dev->mode_config.mutex);
324                                         encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
325                                         mutex_unlock(&dev->mode_config.mutex);
326                                 }
327                         }
328                 }
329         }
330 }
331
332 static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
333 {
334         struct drm_fb_helper *fb_helper = info->par;
335         struct drm_device *dev = fb_helper->dev;
336         struct drm_crtc *crtc;
337         struct drm_encoder *encoder;
338         int i;
339
340         /*
341          * For each CRTC in this fb, find all associated encoders
342          * and turn them off, then turn off the CRTC.
343          */
344         for (i = 0; i < fb_helper->crtc_count; i++) {
345                 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
346                         struct drm_crtc_helper_funcs *crtc_funcs =
347                                 crtc->helper_private;
348
349                         /* Only mess with CRTCs in this fb */
350                         if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
351                             !crtc->enabled)
352                                 continue;
353
354                         /* Found a CRTC on this fb, now find encoders */
355                         list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
356                                 if (encoder->crtc == crtc) {
357                                         struct drm_encoder_helper_funcs *encoder_funcs;
358
359                                         encoder_funcs = encoder->helper_private;
360                                         mutex_lock(&dev->mode_config.mutex);
361                                         encoder_funcs->dpms(encoder, dpms_mode);
362                                         mutex_unlock(&dev->mode_config.mutex);
363                                 }
364                         }
365                         mutex_lock(&dev->mode_config.mutex);
366                         crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
367                         mutex_unlock(&dev->mode_config.mutex);
368                 }
369         }
370 }
371
372 int drm_fb_helper_blank(int blank, struct fb_info *info)
373 {
374         switch (blank) {
375         /* Display: On; HSync: On, VSync: On */
376         case FB_BLANK_UNBLANK:
377                 drm_fb_helper_on(info);
378                 break;
379         /* Display: Off; HSync: On, VSync: On */
380         case FB_BLANK_NORMAL:
381                 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
382                 break;
383         /* Display: Off; HSync: Off, VSync: On */
384         case FB_BLANK_HSYNC_SUSPEND:
385                 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
386                 break;
387         /* Display: Off; HSync: On, VSync: Off */
388         case FB_BLANK_VSYNC_SUSPEND:
389                 drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
390                 break;
391         /* Display: Off; HSync: Off, VSync: Off */
392         case FB_BLANK_POWERDOWN:
393                 drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
394                 break;
395         }
396         return 0;
397 }
398 EXPORT_SYMBOL(drm_fb_helper_blank);
399
400 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
401 {
402         int i;
403
404         for (i = 0; i < helper->crtc_count; i++)
405                 kfree(helper->crtc_info[i].mode_set.connectors);
406         kfree(helper->crtc_info);
407 }
408
409 int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
410 {
411         struct drm_device *dev = helper->dev;
412         struct drm_crtc *crtc;
413         int ret = 0;
414         int i;
415
416         helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
417         if (!helper->crtc_info)
418                 return -ENOMEM;
419
420         helper->crtc_count = crtc_count;
421
422         for (i = 0; i < crtc_count; i++) {
423                 helper->crtc_info[i].mode_set.connectors =
424                         kcalloc(max_conn_count,
425                                 sizeof(struct drm_connector *),
426                                 GFP_KERNEL);
427
428                 if (!helper->crtc_info[i].mode_set.connectors) {
429                         ret = -ENOMEM;
430                         goto out_free;
431                 }
432                 helper->crtc_info[i].mode_set.num_connectors = 0;
433         }
434
435         i = 0;
436         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
437                 helper->crtc_info[i].crtc_id = crtc->base.id;
438                 helper->crtc_info[i].mode_set.crtc = crtc;
439                 i++;
440         }
441         helper->conn_limit = max_conn_count;
442         return 0;
443 out_free:
444         drm_fb_helper_crtc_free(helper);
445         return -ENOMEM;
446 }
447 EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
448
449 static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
450                      u16 blue, u16 regno, struct fb_info *info)
451 {
452         struct drm_fb_helper *fb_helper = info->par;
453         struct drm_framebuffer *fb = fb_helper->fb;
454         int pindex;
455
456         if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
457                 u32 *palette;
458                 u32 value;
459                 /* place color in psuedopalette */
460                 if (regno > 16)
461                         return -EINVAL;
462                 palette = (u32 *)info->pseudo_palette;
463                 red >>= (16 - info->var.red.length);
464                 green >>= (16 - info->var.green.length);
465                 blue >>= (16 - info->var.blue.length);
466                 value = (red << info->var.red.offset) |
467                         (green << info->var.green.offset) |
468                         (blue << info->var.blue.offset);
469                 palette[regno] = value;
470                 return 0;
471         }
472
473         pindex = regno;
474
475         if (fb->bits_per_pixel == 16) {
476                 pindex = regno << 3;
477
478                 if (fb->depth == 16 && regno > 63)
479                         return -EINVAL;
480                 if (fb->depth == 15 && regno > 31)
481                         return -EINVAL;
482
483                 if (fb->depth == 16) {
484                         u16 r, g, b;
485                         int i;
486                         if (regno < 32) {
487                                 for (i = 0; i < 8; i++)
488                                         fb_helper->funcs->gamma_set(crtc, red,
489                                                 green, blue, pindex + i);
490                         }
491
492                         fb_helper->funcs->gamma_get(crtc, &r,
493                                                     &g, &b,
494                                                     pindex >> 1);
495
496                         for (i = 0; i < 4; i++)
497                                 fb_helper->funcs->gamma_set(crtc, r,
498                                                             green, b,
499                                                             (pindex >> 1) + i);
500                 }
501         }
502
503         if (fb->depth != 16)
504                 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
505         return 0;
506 }
507
508 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
509 {
510         struct drm_fb_helper *fb_helper = info->par;
511         struct drm_device *dev = fb_helper->dev;
512         u16 *red, *green, *blue, *transp;
513         struct drm_crtc *crtc;
514         int i, rc = 0;
515         int start;
516
517         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
518                 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
519                 for (i = 0; i < fb_helper->crtc_count; i++) {
520                         if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
521                                 break;
522                 }
523                 if (i == fb_helper->crtc_count)
524                         continue;
525
526                 red = cmap->red;
527                 green = cmap->green;
528                 blue = cmap->blue;
529                 transp = cmap->transp;
530                 start = cmap->start;
531
532                 for (i = 0; i < cmap->len; i++) {
533                         u16 hred, hgreen, hblue, htransp = 0xffff;
534
535                         hred = *red++;
536                         hgreen = *green++;
537                         hblue = *blue++;
538
539                         if (transp)
540                                 htransp = *transp++;
541
542                         rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
543                         if (rc)
544                                 return rc;
545                 }
546                 crtc_funcs->load_lut(crtc);
547         }
548         return rc;
549 }
550 EXPORT_SYMBOL(drm_fb_helper_setcmap);
551
552 int drm_fb_helper_setcolreg(unsigned regno,
553                             unsigned red,
554                             unsigned green,
555                             unsigned blue,
556                             unsigned transp,
557                             struct fb_info *info)
558 {
559         struct drm_fb_helper *fb_helper = info->par;
560         struct drm_device *dev = fb_helper->dev;
561         struct drm_crtc *crtc;
562         int i;
563         int ret;
564
565         if (regno > 255)
566                 return 1;
567
568         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
569                 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
570                 for (i = 0; i < fb_helper->crtc_count; i++) {
571                         if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
572                                 break;
573                 }
574                 if (i == fb_helper->crtc_count)
575                         continue;
576
577                 ret = setcolreg(crtc, red, green, blue, regno, info);
578                 if (ret)
579                         return ret;
580
581                 crtc_funcs->load_lut(crtc);
582         }
583         return 0;
584 }
585 EXPORT_SYMBOL(drm_fb_helper_setcolreg);
586
587 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
588                             struct fb_info *info)
589 {
590         struct drm_fb_helper *fb_helper = info->par;
591         struct drm_framebuffer *fb = fb_helper->fb;
592         int depth;
593
594         if (var->pixclock != 0)
595                 return -EINVAL;
596
597         /* Need to resize the fb object !!! */
598         if (var->bits_per_pixel > fb->bits_per_pixel || var->xres > fb->width || var->yres > fb->height) {
599                 DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
600                           "object %dx%d-%d > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel,
601                           fb->width, fb->height, fb->bits_per_pixel);
602                 return -EINVAL;
603         }
604
605         switch (var->bits_per_pixel) {
606         case 16:
607                 depth = (var->green.length == 6) ? 16 : 15;
608                 break;
609         case 32:
610                 depth = (var->transp.length > 0) ? 32 : 24;
611                 break;
612         default:
613                 depth = var->bits_per_pixel;
614                 break;
615         }
616
617         switch (depth) {
618         case 8:
619                 var->red.offset = 0;
620                 var->green.offset = 0;
621                 var->blue.offset = 0;
622                 var->red.length = 8;
623                 var->green.length = 8;
624                 var->blue.length = 8;
625                 var->transp.length = 0;
626                 var->transp.offset = 0;
627                 break;
628         case 15:
629                 var->red.offset = 10;
630                 var->green.offset = 5;
631                 var->blue.offset = 0;
632                 var->red.length = 5;
633                 var->green.length = 5;
634                 var->blue.length = 5;
635                 var->transp.length = 1;
636                 var->transp.offset = 15;
637                 break;
638         case 16:
639                 var->red.offset = 11;
640                 var->green.offset = 5;
641                 var->blue.offset = 0;
642                 var->red.length = 5;
643                 var->green.length = 6;
644                 var->blue.length = 5;
645                 var->transp.length = 0;
646                 var->transp.offset = 0;
647                 break;
648         case 24:
649                 var->red.offset = 16;
650                 var->green.offset = 8;
651                 var->blue.offset = 0;
652                 var->red.length = 8;
653                 var->green.length = 8;
654                 var->blue.length = 8;
655                 var->transp.length = 0;
656                 var->transp.offset = 0;
657                 break;
658         case 32:
659                 var->red.offset = 16;
660                 var->green.offset = 8;
661                 var->blue.offset = 0;
662                 var->red.length = 8;
663                 var->green.length = 8;
664                 var->blue.length = 8;
665                 var->transp.length = 8;
666                 var->transp.offset = 24;
667                 break;
668         default:
669                 return -EINVAL;
670         }
671         return 0;
672 }
673 EXPORT_SYMBOL(drm_fb_helper_check_var);
674
675 /* this will let fbcon do the mode init */
676 int drm_fb_helper_set_par(struct fb_info *info)
677 {
678         struct drm_fb_helper *fb_helper = info->par;
679         struct drm_device *dev = fb_helper->dev;
680         struct fb_var_screeninfo *var = &info->var;
681         struct drm_crtc *crtc;
682         int ret;
683         int i;
684
685         if (var->pixclock != 0) {
686                 DRM_ERROR("PIXEL CLOCK SET\n");
687                 return -EINVAL;
688         }
689
690         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
691
692                 for (i = 0; i < fb_helper->crtc_count; i++) {
693                         if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
694                                 break;
695                 }
696                 if (i == fb_helper->crtc_count)
697                         continue;
698
699                 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
700                         mutex_lock(&dev->mode_config.mutex);
701                         ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
702                         mutex_unlock(&dev->mode_config.mutex);
703                         if (ret)
704                                 return ret;
705                 }
706         }
707         return 0;
708 }
709 EXPORT_SYMBOL(drm_fb_helper_set_par);
710
711 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
712                               struct fb_info *info)
713 {
714         struct drm_fb_helper *fb_helper = info->par;
715         struct drm_device *dev = fb_helper->dev;
716         struct drm_mode_set *modeset;
717         struct drm_crtc *crtc;
718         int ret = 0;
719         int i;
720
721         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
722                 for (i = 0; i < fb_helper->crtc_count; i++) {
723                         if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
724                                 break;
725                 }
726
727                 if (i == fb_helper->crtc_count)
728                         continue;
729
730                 modeset = &fb_helper->crtc_info[i].mode_set;
731
732                 modeset->x = var->xoffset;
733                 modeset->y = var->yoffset;
734
735                 if (modeset->num_connectors) {
736                         mutex_lock(&dev->mode_config.mutex);
737                         ret = crtc->funcs->set_config(modeset);
738                         mutex_unlock(&dev->mode_config.mutex);
739                         if (!ret) {
740                                 info->var.xoffset = var->xoffset;
741                                 info->var.yoffset = var->yoffset;
742                         }
743                 }
744         }
745         return ret;
746 }
747 EXPORT_SYMBOL(drm_fb_helper_pan_display);
748
749 int drm_fb_helper_single_fb_probe(struct drm_device *dev,
750                                   int preferred_bpp,
751                                   int (*fb_create)(struct drm_device *dev,
752                                                    uint32_t fb_width,
753                                                    uint32_t fb_height,
754                                                    uint32_t surface_width,
755                                                    uint32_t surface_height,
756                                                    uint32_t surface_depth,
757                                                    uint32_t surface_bpp,
758                                                    struct drm_framebuffer **fb_ptr))
759 {
760         struct drm_crtc *crtc;
761         struct drm_connector *connector;
762         unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
763         unsigned int surface_width = 0, surface_height = 0;
764         int new_fb = 0;
765         int crtc_count = 0;
766         int ret, i, conn_count = 0;
767         struct fb_info *info;
768         struct drm_framebuffer *fb;
769         struct drm_mode_set *modeset = NULL;
770         struct drm_fb_helper *fb_helper;
771         uint32_t surface_depth = 24, surface_bpp = 32;
772
773         /* if driver picks 8 or 16 by default use that
774            for both depth/bpp */
775         if (preferred_bpp != surface_bpp) {
776                 surface_depth = surface_bpp = preferred_bpp;
777         }
778         /* first up get a count of crtcs now in use and new min/maxes width/heights */
779         list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
780                 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
781
782                 struct drm_fb_helper_cmdline_mode *cmdline_mode;
783
784                 if (!fb_help_conn)
785                         continue;
786                 
787                 cmdline_mode = &fb_help_conn->cmdline_mode;
788
789                 if (cmdline_mode->bpp_specified) {
790                         switch (cmdline_mode->bpp) {
791                         case 8:
792                                 surface_depth = surface_bpp = 8;
793                                 break;
794                         case 15:
795                                 surface_depth = 15;
796                                 surface_bpp = 16;
797                                 break;
798                         case 16:
799                                 surface_depth = surface_bpp = 16;
800                                 break;
801                         case 24:
802                                 surface_depth = surface_bpp = 24;
803                                 break;
804                         case 32:
805                                 surface_depth = 24;
806                                 surface_bpp = 32;
807                                 break;
808                         }
809                         break;
810                 }
811         }
812
813         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
814                 if (drm_helper_crtc_in_use(crtc)) {
815                         if (crtc->desired_mode) {
816                                 if (crtc->desired_mode->hdisplay < fb_width)
817                                         fb_width = crtc->desired_mode->hdisplay;
818
819                                 if (crtc->desired_mode->vdisplay < fb_height)
820                                         fb_height = crtc->desired_mode->vdisplay;
821
822                                 if (crtc->desired_mode->hdisplay > surface_width)
823                                         surface_width = crtc->desired_mode->hdisplay;
824
825                                 if (crtc->desired_mode->vdisplay > surface_height)
826                                         surface_height = crtc->desired_mode->vdisplay;
827                         }
828                         crtc_count++;
829                 }
830         }
831
832         if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
833                 /* hmm everyone went away - assume VGA cable just fell out
834                    and will come back later. */
835                 return 0;
836         }
837
838         /* do we have an fb already? */
839         if (list_empty(&dev->mode_config.fb_kernel_list)) {
840                 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
841                                    surface_height, surface_depth, surface_bpp,
842                                    &fb);
843                 if (ret)
844                         return -EINVAL;
845                 new_fb = 1;
846         } else {
847                 fb = list_first_entry(&dev->mode_config.fb_kernel_list,
848                                       struct drm_framebuffer, filp_head);
849
850                 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
851                    As really we can't resize an fbdev that is in the wild currently due to fbdev
852                    not really being designed for the lower layers moving stuff around under it.
853                    - so in the grand style of things - punt. */
854                 if ((fb->width < surface_width) ||
855                     (fb->height < surface_height)) {
856                         DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
857                         return -EINVAL;
858                 }
859         }
860
861         info = fb->fbdev;
862         fb_helper = info->par;
863
864         crtc_count = 0;
865         /* okay we need to setup new connector sets in the crtcs */
866         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
867                 modeset = &fb_helper->crtc_info[crtc_count].mode_set;
868                 modeset->fb = fb;
869                 conn_count = 0;
870                 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
871                         if (connector->encoder)
872                                 if (connector->encoder->crtc == modeset->crtc) {
873                                         modeset->connectors[conn_count] = connector;
874                                         conn_count++;
875                                         if (conn_count > fb_helper->conn_limit)
876                                                 BUG();
877                                 }
878                 }
879
880                 for (i = conn_count; i < fb_helper->conn_limit; i++)
881                         modeset->connectors[i] = NULL;
882
883                 modeset->crtc = crtc;
884                 crtc_count++;
885
886                 modeset->num_connectors = conn_count;
887                 if (modeset->crtc->desired_mode) {
888                         if (modeset->mode)
889                                 drm_mode_destroy(dev, modeset->mode);
890                         modeset->mode = drm_mode_duplicate(dev,
891                                                            modeset->crtc->desired_mode);
892                 }
893         }
894         fb_helper->crtc_count = crtc_count;
895         fb_helper->fb = fb;
896
897         if (new_fb) {
898                 info->var.pixclock = 0;
899                 ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0);
900                 if (ret)
901                         return ret;
902                 if (register_framebuffer(info) < 0) {
903                         fb_dealloc_cmap(&info->cmap);
904                         return -EINVAL;
905                 }
906         } else {
907                 drm_fb_helper_set_par(info);
908         }
909         printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
910                info->fix.id);
911
912         /* Switch back to kernel console on panic */
913         /* multi card linked list maybe */
914         if (list_empty(&kernel_fb_helper_list)) {
915                 printk(KERN_INFO "registered panic notifier\n");
916                 atomic_notifier_chain_register(&panic_notifier_list,
917                                                &paniced);
918                 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
919         }
920         list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
921         return 0;
922 }
923 EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
924
925 void drm_fb_helper_free(struct drm_fb_helper *helper)
926 {
927         list_del(&helper->kernel_fb_list);
928         if (list_empty(&kernel_fb_helper_list)) {
929                 printk(KERN_INFO "unregistered panic notifier\n");
930                 atomic_notifier_chain_unregister(&panic_notifier_list,
931                                                  &paniced);
932                 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
933         }
934         drm_fb_helper_crtc_free(helper);
935         fb_dealloc_cmap(&helper->fb->fbdev->cmap);
936 }
937 EXPORT_SYMBOL(drm_fb_helper_free);
938
939 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
940                             uint32_t depth)
941 {
942         info->fix.type = FB_TYPE_PACKED_PIXELS;
943         info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
944                 FB_VISUAL_TRUECOLOR;
945         info->fix.type_aux = 0;
946         info->fix.xpanstep = 1; /* doing it in hw */
947         info->fix.ypanstep = 1; /* doing it in hw */
948         info->fix.ywrapstep = 0;
949         info->fix.accel = FB_ACCEL_NONE;
950         info->fix.type_aux = 0;
951
952         info->fix.line_length = pitch;
953         return;
954 }
955 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
956
957 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
958                             uint32_t fb_width, uint32_t fb_height)
959 {
960         info->pseudo_palette = fb->pseudo_palette;
961         info->var.xres_virtual = fb->width;
962         info->var.yres_virtual = fb->height;
963         info->var.bits_per_pixel = fb->bits_per_pixel;
964         info->var.xoffset = 0;
965         info->var.yoffset = 0;
966         info->var.activate = FB_ACTIVATE_NOW;
967         info->var.height = -1;
968         info->var.width = -1;
969
970         switch (fb->depth) {
971         case 8:
972                 info->var.red.offset = 0;
973                 info->var.green.offset = 0;
974                 info->var.blue.offset = 0;
975                 info->var.red.length = 8; /* 8bit DAC */
976                 info->var.green.length = 8;
977                 info->var.blue.length = 8;
978                 info->var.transp.offset = 0;
979                 info->var.transp.length = 0;
980                 break;
981         case 15:
982                 info->var.red.offset = 10;
983                 info->var.green.offset = 5;
984                 info->var.blue.offset = 0;
985                 info->var.red.length = 5;
986                 info->var.green.length = 5;
987                 info->var.blue.length = 5;
988                 info->var.transp.offset = 15;
989                 info->var.transp.length = 1;
990                 break;
991         case 16:
992                 info->var.red.offset = 11;
993                 info->var.green.offset = 5;
994                 info->var.blue.offset = 0;
995                 info->var.red.length = 5;
996                 info->var.green.length = 6;
997                 info->var.blue.length = 5;
998                 info->var.transp.offset = 0;
999                 break;
1000         case 24:
1001                 info->var.red.offset = 16;
1002                 info->var.green.offset = 8;
1003                 info->var.blue.offset = 0;
1004                 info->var.red.length = 8;
1005                 info->var.green.length = 8;
1006                 info->var.blue.length = 8;
1007                 info->var.transp.offset = 0;
1008                 info->var.transp.length = 0;
1009                 break;
1010         case 32:
1011                 info->var.red.offset = 16;
1012                 info->var.green.offset = 8;
1013                 info->var.blue.offset = 0;
1014                 info->var.red.length = 8;
1015                 info->var.green.length = 8;
1016                 info->var.blue.length = 8;
1017                 info->var.transp.offset = 24;
1018                 info->var.transp.length = 8;
1019                 break;
1020         default:
1021                 break;
1022         }
1023
1024         info->var.xres = fb_width;
1025         info->var.yres = fb_height;
1026 }
1027 EXPORT_SYMBOL(drm_fb_helper_fill_var);