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