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