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