OSDN Git Service

radeon: Fix type in check for tmds type.
[android-x86/external-libdrm.git] / linux-core / intel_fb.c
1 /*
2  * Copyright © 2007 David Airlie
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *     David Airlie
25  */
26     /*
27      *  Modularization
28      */
29
30 #include <linux/module.h>
31 #include <linux/kernel.h>
32 #include <linux/errno.h>
33 #include <linux/string.h>
34 #include <linux/mm.h>
35 #include <linux/tty.h>
36 #include <linux/slab.h>
37 #include <linux/delay.h>
38 #include <linux/fb.h>
39 #include <linux/init.h>
40
41 #include "drmP.h"
42 #include "drm.h"
43 #include "drm_crtc.h"
44 #include "intel_drv.h"
45 #include "i915_drm.h"
46 #include "i915_drv.h"
47
48 struct intelfb_par {
49         struct drm_device *dev;
50         struct drm_display_mode *our_mode;
51         struct intel_framebuffer *intel_fb;
52         int crtc_count;
53         /* crtc currently bound to this */
54         uint32_t crtc_ids[2];
55 };
56 /*
57 static int
58 var_to_refresh(const struct fb_var_screeninfo *var)
59 {
60         int xtot = var->xres + var->left_margin + var->right_margin +
61                 var->hsync_len;
62         int ytot = var->yres + var->upper_margin + var->lower_margin +
63                 var->vsync_len;
64
65         return (1000000000 / var->pixclock * 1000 + 500) / xtot / ytot;
66 }*/
67
68 static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green,
69                         unsigned blue, unsigned transp,
70                         struct fb_info *info)
71 {
72         struct intelfb_par *par = info->par;
73         struct drm_device *dev = par->dev;
74         struct drm_crtc *crtc;
75         int i;
76
77         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
78                 struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
79                 struct drm_mode_set *modeset = &intel_crtc->mode_set;
80                 struct drm_framebuffer *fb = modeset->fb;
81
82                 for (i = 0; i < par->crtc_count; i++)
83                         if (crtc->base.id == par->crtc_ids[i])
84                                 break;
85
86                 if (i == par->crtc_count)
87                         continue;
88                 
89
90                 if (regno > 255)
91                         return 1;
92
93                 if (fb->depth == 8) {
94                         intel_crtc_fb_gamma_set(crtc, red, green, blue, regno);
95                         return 0;
96                 }
97
98                 if (regno < 16) {
99                         switch (fb->depth) {
100                         case 15:
101                                 fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
102                                         ((green & 0xf800) >>  6) |
103                                         ((blue & 0xf800) >> 11);
104                                 break;
105                         case 16:
106                                 fb->pseudo_palette[regno] = (red & 0xf800) |
107                                         ((green & 0xfc00) >>  5) |
108                                         ((blue  & 0xf800) >> 11);
109                                 break;
110                         case 24:
111                         case 32:
112                                 fb->pseudo_palette[regno] = ((red & 0xff00) << 8) |
113                                         (green & 0xff00) |
114                                         ((blue  & 0xff00) >> 8);
115                                 break;
116                         }
117                 }
118         }
119         return 0;
120 }
121
122 static int intelfb_check_var(struct fb_var_screeninfo *var,
123                         struct fb_info *info)
124 {
125         struct intelfb_par *par = info->par;
126         struct intel_framebuffer *intel_fb = par->intel_fb;
127         struct drm_framebuffer *fb = &intel_fb->base;
128         int depth;
129
130         if (var->pixclock == -1 || !var->pixclock)
131                 return -EINVAL;
132
133         /* Need to resize the fb object !!! */
134         if (var->xres > fb->width || var->yres > fb->height) {
135                 DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height);
136                 DRM_ERROR("Need resizing code.\n");
137                 return -EINVAL;
138         }
139
140         switch (var->bits_per_pixel) {
141         case 16:
142                 depth = (var->green.length == 6) ? 16 : 15;
143                 break;
144         case 32:
145                 depth = (var->transp.length > 0) ? 32 : 24;
146                 break;
147         default:
148                 depth = var->bits_per_pixel;
149                 break;
150         }
151                 
152         switch (depth) {
153         case 8:
154                 var->red.offset = 0;
155                 var->green.offset = 0;
156                 var->blue.offset = 0;
157                 var->red.length = 8;
158                 var->green.length = 8;
159                 var->blue.length = 8;
160                 var->transp.length = 0;
161                 var->transp.offset = 0;
162                 break;
163         case 15:
164                 var->red.offset = 10;
165                 var->green.offset = 5;
166                 var->blue.offset = 0;
167                 var->red.length = 5;
168                 var->green.length = 5;
169                 var->blue.length = 5;
170                 var->transp.length = 1;
171                 var->transp.offset = 15;
172                 break;
173         case 16:
174                 var->red.offset = 11;
175                 var->green.offset = 5;
176                 var->blue.offset = 0;
177                 var->red.length = 5;
178                 var->green.length = 6;
179                 var->blue.length = 5;
180                 var->transp.length = 0;
181                 var->transp.offset = 0;
182                 break;
183         case 24:
184                 var->red.offset = 16;
185                 var->green.offset = 8;
186                 var->blue.offset = 0;
187                 var->red.length = 8;
188                 var->green.length = 8;
189                 var->blue.length = 8;
190                 var->transp.length = 0;
191                 var->transp.offset = 0;
192                 break;
193         case 32:
194                 var->red.offset = 16;
195                 var->green.offset = 8;
196                 var->blue.offset = 0;
197                 var->red.length = 8;
198                 var->green.length = 8;
199                 var->blue.length = 8;
200                 var->transp.length = 8;
201                 var->transp.offset = 24;
202                 break;
203         default:
204                 return -EINVAL; 
205         }
206
207         return 0;
208 }
209
210 /* this will let fbcon do the mode init */
211 /* FIXME: take mode config lock? */
212 static int intelfb_set_par(struct fb_info *info)
213 {
214         struct intelfb_par *par = info->par;
215         struct drm_device *dev = par->dev;
216         struct fb_var_screeninfo *var = &info->var;
217         int i;
218
219         DRM_DEBUG("%d %d\n", var->xres, var->pixclock);
220
221         if (var->pixclock != -1) {
222
223                 DRM_ERROR("PIXEL CLCOK SET\n");
224 #if 0
225                 struct intel_framebuffer *intel_fb = par->intel_fb;
226                 struct drm_framebuffer *fb = &intel_fb->base;
227                 struct drm_display_mode *drm_mode, *search_mode;
228                 struct drm_connector *connector = NULL;
229                 struct drm_device *dev = par->dev;
230
231                 int found = 0;
232
233                 switch (var->bits_per_pixel) {
234                 case 16:
235                         fb->depth = (var->green.length == 6) ? 16 : 15;
236                         break;
237                 case 32:
238                         fb->depth = (var->transp.length > 0) ? 32 : 24;
239                         break;
240                 default:
241                         fb->depth = var->bits_per_pixel;
242                         break;
243                 }
244                 
245                 fb->bits_per_pixel = var->bits_per_pixel;
246                 
247                 info->fix.line_length = fb->pitch;
248                 info->fix.smem_len = info->fix.line_length * fb->height;
249                 info->fix.visual = (fb->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
250                 
251                 info->screen_size = info->fix.smem_len; /* ??? */
252                 /* reuse desired mode if possible */
253                 /* create a drm mode */
254                 drm_mode = drm_mode_create(dev);
255                 drm_mode->hdisplay = var->xres;
256                 drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin;
257                 drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len;
258                 drm_mode->htotal = drm_mode->hsync_end + var->left_margin;
259                 drm_mode->vdisplay = var->yres;
260                 drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin;
261                 drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len;
262                 drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin;
263                 drm_mode->clock = PICOS2KHZ(var->pixclock);
264                 drm_mode->vrefresh = drm_mode_vrefresh(drm_mode);
265                 drm_mode->flags = 0;
266                 drm_mode->flags |= var->sync & FB_SYNC_HOR_HIGH_ACT ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
267                 drm_mode->flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
268                 
269                 drm_mode_set_name(drm_mode);
270                 drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V);
271                 
272                 found = 0;
273                 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
274                         if (connector->encoder &&
275                             connector->encoder->crtc == par->set.crtc){
276                                 found = 1;
277                                 break;
278                         }
279                 }
280                 
281                 /* no connector bound, bail */
282                 if (!found)
283                         return -EINVAL;
284                 
285                 found = 0;
286                 drm_mode_debug_printmodeline(drm_mode);
287                 list_for_each_entry(search_mode, &connector->modes, head) {
288                         drm_mode_debug_printmodeline(search_mode);
289                         if (drm_mode_equal(drm_mode, search_mode)) {
290                                 drm_mode_destroy(dev, drm_mode);
291                                 drm_mode = search_mode;
292                                 found = 1;
293                                 break;
294                         }
295                 }
296                 
297                 /* If we didn't find a matching mode that exists on our connector,
298                  * create a new attachment for the incoming user specified mode
299                  */
300                 if (!found) {
301                         if (par->our_mode) {
302                                 /* this also destroys the mode */
303                                 drm_mode_detachmode_crtc(dev, par->our_mode);
304                         }
305                         
306                         par->set.mode = drm_mode;
307                         par->our_mode = drm_mode;
308                         drm_mode_debug_printmodeline(drm_mode);
309                         /* attach mode */
310                         drm_mode_attachmode_crtc(dev, par->set.crtc, par->set.mode);
311                 } else {
312                         par->set.mode = drm_mode;
313                         if (par->our_mode)
314                                 drm_mode_detachmode_crtc(dev, par->our_mode);
315                         par->our_mode = NULL;
316                 }
317                 return par->set.crtc->funcs->set_config(&par->set);
318 #endif
319                 return -EINVAL;
320         } else {
321                 struct drm_crtc *crtc;
322                 int ret;
323
324                 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
325                         struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
326
327                         for (i = 0; i < par->crtc_count; i++)
328                                 if (crtc->base.id == par->crtc_ids[i])
329                                         break;
330
331                         if (i == par->crtc_count)
332                                 continue;
333
334                         if (crtc->fb == intel_crtc->mode_set.fb) {
335                                 ret = crtc->funcs->set_config(&intel_crtc->mode_set);
336                                 if (ret)
337                                         return ret;
338                         }
339                 }
340                 return 0;
341         }
342 }
343
344 #if 0
345 static void intelfb_copyarea(struct fb_info *info,
346                         const struct fb_copyarea *region)
347 {
348         struct intelfb_par *par = info->par;
349         struct drm_device *dev = par->dev;
350         struct drm_i915_private *dev_priv = dev->dev_private;
351         u32 src_x1, src_y1, dst_x1, dst_y1, dst_x2, dst_y2, offset;
352         u32 cmd, rop_depth_pitch, src_pitch;
353         RING_LOCALS;
354
355         cmd = XY_SRC_COPY_BLT_CMD;
356         src_x1 = region->sx;
357         src_y1 = region->sy;
358         dst_x1 = region->dx;
359         dst_y1 = region->dy;
360         dst_x2 = region->dx + region->width;
361         dst_y2 = region->dy + region->height;
362         offset = par->fb->offset;
363         rop_depth_pitch = BLT_ROP_GXCOPY | par->fb->pitch;
364         src_pitch = par->fb->pitch;
365
366         switch (par->fb->bits_per_pixel) {
367         case 16:
368                 rop_depth_pitch |= BLT_DEPTH_16_565;
369                 break;
370         case 32:
371                 rop_depth_pitch |= BLT_DEPTH_32;
372                 cmd |= XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB;
373                 break;
374         }
375
376         BEGIN_LP_RING(8);
377         OUT_RING(cmd);
378         OUT_RING(rop_depth_pitch);
379         OUT_RING((dst_y1 << 16) | (dst_x1 & 0xffff));
380         OUT_RING((dst_y2 << 16) | (dst_x2 & 0xffff));
381         OUT_RING(offset);
382         OUT_RING((src_y1 << 16) | (src_x1 & 0xffff));
383         OUT_RING(src_pitch);
384         OUT_RING(offset);
385         ADVANCE_LP_RING();
386 }
387
388 #define ROUND_UP_TO(x, y)       (((x) + (y) - 1) / (y) * (y))
389 #define ROUND_DOWN_TO(x, y)     ((x) / (y) * (y))
390
391 void intelfb_imageblit(struct fb_info *info, const struct fb_image *image)
392 {
393         struct intelfb_par *par = info->par;
394         struct drm_device *dev = par->dev;
395         struct drm_i915_private *dev_priv = dev->dev_private;
396         u32 cmd, rop_pitch_depth, tmp;
397         int nbytes, ndwords, pad;
398         u32 dst_x1, dst_y1, dst_x2, dst_y2, offset, bg, fg;
399         int dat, ix, iy, iw;
400         int i, j;
401         RING_LOCALS;
402
403         /* size in bytes of a padded scanline */
404         nbytes = ROUND_UP_TO(image->width, 16) / 8;
405
406         /* Total bytes of padded scanline data to write out. */
407         nbytes *= image->height;
408
409         /*
410         * Check if the glyph data exceeds the immediate mode limit.
411         * It would take a large font (1K pixels) to hit this limit.
412         */
413         if (nbytes > 128 || image->depth != 1)
414                 return cfb_imageblit(info, image);
415
416         /* Src data is packaged a dword (32-bit) at a time. */
417         ndwords = ROUND_UP_TO(nbytes, 4) / 4;
418
419         /*
420         * Ring has to be padded to a quad word. But because the command starts
421         with 7 bytes, pad only if there is an even number of ndwords
422         */
423         pad = !(ndwords % 2);
424
425         DRM_DEBUG("imageblit %dx%dx%d to (%d,%d)\n", image->width,
426                 image->height, image->depth, image->dx, image->dy);
427         DRM_DEBUG("nbytes: %d, ndwords: %d, pad: %d\n", nbytes, ndwords, pad);
428
429         tmp = (XY_MONO_SRC_COPY_IMM_BLT & 0xff) + ndwords;
430         cmd = (XY_MONO_SRC_COPY_IMM_BLT & ~0xff) | tmp;
431         offset = par->fb->offset;
432         dst_x1 = image->dx;
433         dst_y1 = image->dy;
434         dst_x2 = image->dx + image->width;
435         dst_y2 = image->dy + image->height;
436         rop_pitch_depth = BLT_ROP_GXCOPY | par->fb->pitch;
437
438         switch (par->fb->bits_per_pixel) {
439         case 8:
440                 rop_pitch_depth |= BLT_DEPTH_8;
441                 fg = image->fg_color;
442                 bg = image->bg_color;
443                 break;
444         case 16:
445                 rop_pitch_depth |= BLT_DEPTH_16_565;
446                 fg = par->fb->pseudo_palette[image->fg_color];
447                 bg = par->fb->pseudo_palette[image->bg_color];
448                 break;
449         case 32:
450                 rop_pitch_depth |= BLT_DEPTH_32;
451                 cmd |= XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB;
452                 fg = par->fb->pseudo_palette[image->fg_color];
453                 bg = par->fb->pseudo_palette[image->bg_color];
454                 break;
455         default:
456                 DRM_ERROR("unknown depth %d\n", par->fb->bits_per_pixel);
457                 break;
458         }
459         
460         BEGIN_LP_RING(8 + ndwords);
461         OUT_RING(cmd);
462         OUT_RING(rop_pitch_depth);
463         OUT_RING((dst_y1 << 16) | (dst_x1 & 0xffff));
464         OUT_RING((dst_y2 << 16) | (dst_x2 & 0xffff));
465         OUT_RING(offset);
466         OUT_RING(bg);
467         OUT_RING(fg);
468         ix = iy = 0;
469         iw = ROUND_UP_TO(image->width, 8) / 8;
470         while (ndwords--) {
471                 dat = 0;
472                 for (j = 0; j < 2; ++j) {
473                         for (i = 0; i < 2; ++i) {
474                                 if (ix != iw || i == 0)
475                                         dat |= image->data[iy*iw + ix++] << (i+j*2)*8;
476                         }
477                         if (ix == iw && iy != (image->height - 1)) {
478                                 ix = 0;
479                                 ++iy;
480                         }
481                 }
482                 OUT_RING(dat);
483         }
484         if (pad)
485                 OUT_RING(MI_NOOP);
486         ADVANCE_LP_RING();
487 }
488 #endif
489 static int intelfb_pan_display(struct fb_var_screeninfo *var,
490                                 struct fb_info *info)
491 {
492         struct intelfb_par *par = info->par;
493         struct drm_device *dev = par->dev;
494         struct drm_mode_set *modeset;
495         struct drm_crtc *crtc;
496         struct intel_crtc *intel_crtc;
497         int ret = 0;
498         int i;
499
500         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
501                 
502                 for (i = 0; i < par->crtc_count; i++)
503                         if (crtc->base.id == par->crtc_ids[i])
504                                 break;
505
506                 if (i == par->crtc_count)
507                         continue;
508
509                 intel_crtc = to_intel_crtc(crtc);
510                 modeset = &intel_crtc->mode_set;
511
512                 modeset->x = var->xoffset;
513                 modeset->y = var->yoffset;
514
515                 if (modeset->num_connectors) {
516                         ret = crtc->funcs->set_config(modeset);
517                   
518                         if (!ret) {
519                                 info->var.xoffset = var->xoffset;
520                                 info->var.yoffset = var->yoffset;
521                         }
522                 }
523         }
524
525         return ret;
526 }
527
528 static void intelfb_on(struct fb_info *info)
529 {
530         struct intelfb_par *par = info->par;
531         struct drm_device *dev = par->dev;
532         struct drm_crtc *crtc;
533         struct drm_encoder *encoder;
534         int i;
535
536         /*
537          * For each CRTC in this fb, find all associated encoders
538          * and turn them off, then turn off the CRTC.
539          */
540         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
541                 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
542
543                 for (i = 0; i < par->crtc_count; i++)
544                         if (crtc->base.id == par->crtc_ids[i])
545                                 break;
546
547                 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
548
549                 /* Found a CRTC on this fb, now find encoders */
550                 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
551                         if (encoder->crtc == crtc) {
552                                 struct drm_encoder_helper_funcs *encoder_funcs;
553                                 encoder_funcs = encoder->helper_private;
554                                 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
555                         }
556                 }
557         }
558 }
559
560 static void intelfb_off(struct fb_info *info, int dpms_mode)
561 {
562         struct intelfb_par *par = info->par;
563         struct drm_device *dev = par->dev;
564         struct drm_crtc *crtc;
565         struct drm_encoder *encoder;
566         int i;
567
568         /*
569          * For each CRTC in this fb, find all associated encoders
570          * and turn them off, then turn off the CRTC.
571          */
572         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
573                 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
574
575                 for (i = 0; i < par->crtc_count; i++)
576                         if (crtc->base.id == par->crtc_ids[i])
577                                 break;
578
579                 /* Found a CRTC on this fb, now find encoders */
580                 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
581                         if (encoder->crtc == crtc) {
582                                 struct drm_encoder_helper_funcs *encoder_funcs;
583                                 encoder_funcs = encoder->helper_private;
584                                 encoder_funcs->dpms(encoder, dpms_mode);
585                         }
586                 }
587                 if (dpms_mode == DRM_MODE_DPMS_OFF)
588                         crtc_funcs->dpms(crtc, dpms_mode);
589         }
590 }
591
592 int intelfb_blank(int blank, struct fb_info *info)
593 {
594         switch (blank) {
595         case FB_BLANK_UNBLANK:
596                 intelfb_on(info);
597                 break;
598         case FB_BLANK_NORMAL:
599                 intelfb_off(info, DRM_MODE_DPMS_STANDBY);
600                 break;
601         case FB_BLANK_HSYNC_SUSPEND:
602                 intelfb_off(info, DRM_MODE_DPMS_STANDBY);
603                 break;
604         case FB_BLANK_VSYNC_SUSPEND:
605                 intelfb_off(info, DRM_MODE_DPMS_SUSPEND);
606                 break;
607         case FB_BLANK_POWERDOWN:
608                 intelfb_off(info, DRM_MODE_DPMS_OFF);
609                 break;
610         }
611         return 0;
612 }
613
614 static struct fb_ops intelfb_ops = {
615         .owner = THIS_MODULE,
616         //.fb_open = intelfb_open,
617         //.fb_read = intelfb_read,
618         //.fb_write = intelfb_write,
619         //.fb_release = intelfb_release,
620         //.fb_ioctl = intelfb_ioctl,
621         .fb_check_var = intelfb_check_var,
622         .fb_set_par = intelfb_set_par,
623         .fb_setcolreg = intelfb_setcolreg,
624         .fb_fillrect = cfb_fillrect,
625         .fb_copyarea = cfb_copyarea, //intelfb_copyarea,
626         .fb_imageblit = cfb_imageblit, //intelfb_imageblit,
627         .fb_pan_display = intelfb_pan_display,
628         .fb_blank = intelfb_blank,
629 };
630
631 /**
632  * Curretly it is assumed that the old framebuffer is reused.
633  *
634  * LOCKING
635  * caller should hold the mode config lock.
636  *
637  */
638 int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc)
639 {
640         struct fb_info *info;
641         struct drm_framebuffer *fb;
642         struct drm_display_mode *mode = crtc->desired_mode;
643
644         fb = crtc->fb;
645         if (!fb)
646                 return 1;
647
648         info = fb->fbdev;
649         if (!info)
650                 return 1;
651
652         if (!mode)
653                 return 1;
654
655         info->var.xres = mode->hdisplay;
656         info->var.right_margin = mode->hsync_start - mode->hdisplay;
657         info->var.hsync_len = mode->hsync_end - mode->hsync_start;
658         info->var.left_margin = mode->htotal - mode->hsync_end;
659         info->var.yres = mode->vdisplay;
660         info->var.lower_margin = mode->vsync_start - mode->vdisplay;
661         info->var.vsync_len = mode->vsync_end - mode->vsync_start;
662         info->var.upper_margin = mode->vtotal - mode->vsync_end;
663         info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100;
664         /* avoid overflow */
665         info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;
666
667         return 0;
668 }
669 EXPORT_SYMBOL(intelfb_resize);
670
671 static struct drm_mode_set panic_mode;
672
673 int intelfb_panic(struct notifier_block *n, unsigned long ununsed,
674                   void *panic_str)
675 {
676         DRM_ERROR("panic occurred, switching back to text console\n");
677         drm_crtc_helper_set_config(&panic_mode);
678
679         return 0;
680 }
681 EXPORT_SYMBOL(intelfb_panic);
682  
683 static struct notifier_block paniced = {
684         .notifier_call = intelfb_panic,
685 };
686
687 int intelfb_create(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height, 
688                    uint32_t surface_width, uint32_t surface_height,
689                    struct intel_framebuffer **intel_fb_p)
690 {
691         struct fb_info *info;
692         struct intelfb_par *par;
693         struct drm_framebuffer *fb;
694         struct intel_framebuffer *intel_fb;
695         struct drm_mode_fb_cmd mode_cmd;
696         struct drm_gem_object *fbo = NULL;
697         struct drm_i915_gem_object *obj_priv;
698         struct device *device = &dev->pdev->dev; 
699         int size, aligned_size, ret;
700
701         mode_cmd.width = surface_width;/* crtc->desired_mode->hdisplay; */
702         mode_cmd.height = surface_height;/* crtc->desired_mode->vdisplay; */
703         
704         mode_cmd.bpp = 32;
705         mode_cmd.pitch = mode_cmd.width * ((mode_cmd.bpp + 1) / 8);
706         mode_cmd.depth = 24;
707
708         size = mode_cmd.pitch * mode_cmd.height;
709         aligned_size = ALIGN(size, PAGE_SIZE);
710         fbo = drm_gem_object_alloc(dev, aligned_size);
711         if (!fbo) {
712                 printk(KERN_ERR "failed to allocate framebuffer\n");
713                 ret = -ENOMEM;
714                 goto out;
715         }
716         obj_priv = fbo->driver_private;
717
718         mutex_lock(&dev->struct_mutex);
719         /* Flush everything out, we'll be doing GTT only from now on */
720         i915_gem_object_set_domain(fbo, I915_GEM_DOMAIN_GTT,
721                                    I915_GEM_DOMAIN_GTT);
722
723         ret = i915_gem_object_pin(fbo, PAGE_SIZE);
724         if (ret) {
725                 DRM_ERROR("failed to pin fb: %d\n", ret);
726                 goto out_unref;
727         }
728
729         i915_gem_clflush_object(fbo);
730
731         fb = intel_user_framebuffer_create(dev, NULL, &mode_cmd);
732         if (!fb) {
733                 DRM_ERROR("failed to allocate fb.\n");
734                 ret = -ENOMEM;
735                 goto out_unref;
736         }
737
738         list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
739
740         intel_fb = to_intel_framebuffer(fb);
741         *intel_fb_p = intel_fb;
742
743         intel_fb->obj = fbo;
744
745         info = framebuffer_alloc(sizeof(struct intelfb_par), device);
746         if (!info) {
747                 ret = -ENOMEM;
748                 goto out_unref;
749         }
750
751         par = info->par;
752
753         strcpy(info->fix.id, "inteldrmfb");
754         info->fix.type = FB_TYPE_PACKED_PIXELS;
755         info->fix.visual = FB_VISUAL_TRUECOLOR;
756         info->fix.type_aux = 0;
757         info->fix.xpanstep = 1; /* doing it in hw */
758         info->fix.ypanstep = 1; /* doing it in hw */
759         info->fix.ywrapstep = 0;
760         info->fix.accel = FB_ACCEL_I830;
761         info->fix.type_aux = 0;
762
763         info->flags = FBINFO_DEFAULT;
764
765         info->fbops = &intelfb_ops;
766
767         info->fix.line_length = fb->pitch;
768         info->fix.smem_start = dev->mode_config.fb_base + obj_priv->gtt_offset;
769         info->fix.smem_len = size;
770
771         info->flags = FBINFO_DEFAULT;
772
773         info->screen_base = ioremap_wc(dev->agp->base + obj_priv->gtt_offset,
774                                        size);
775         if (!info->screen_base) {
776                 ret = -ENOSPC;
777                 goto out_unref;
778         }
779         info->screen_size = size;
780
781         memset(info->screen_base, 0, size);
782
783         info->pseudo_palette = fb->pseudo_palette;
784         info->var.xres_virtual = fb->width;
785         info->var.yres_virtual = fb->height;
786         info->var.bits_per_pixel = fb->bits_per_pixel;
787         info->var.xoffset = 0;
788         info->var.yoffset = 0;
789         info->var.activate = FB_ACTIVATE_NOW;
790         info->var.height = -1;
791         info->var.width = -1;
792
793         info->var.xres = fb_width;
794         info->var.yres = fb_height;
795
796         if (IS_I9XX(dev)) {
797                 info->fix.mmio_start = pci_resource_start(dev->pdev, 0);
798                 info->fix.mmio_len = pci_resource_len(dev->pdev, 0);
799         } else {
800                 info->fix.mmio_start = pci_resource_start(dev->pdev, 1);
801                 info->fix.mmio_len = pci_resource_len(dev->pdev, 1);
802         }
803
804         info->pixmap.size = 64*1024;
805         info->pixmap.buf_align = 8;
806         info->pixmap.access_align = 32;
807         info->pixmap.flags = FB_PIXMAP_SYSTEM;
808         info->pixmap.scan_align = 1;
809
810         DRM_DEBUG("fb depth is %d\n", fb->depth);
811         DRM_DEBUG("   pitch is %d\n", fb->pitch);
812         switch(fb->depth) {
813         case 8:
814                 info->var.red.offset = 0;
815                 info->var.green.offset = 0;
816                 info->var.blue.offset = 0;
817                 info->var.red.length = 8; /* 8bit DAC */
818                 info->var.green.length = 8;
819                 info->var.blue.length = 8;
820                 info->var.transp.offset = 0;
821                 info->var.transp.length = 0;
822                 break;
823         case 15:
824                 info->var.red.offset = 10;
825                 info->var.green.offset = 5;
826                 info->var.blue.offset = 0;
827                 info->var.red.length = 5;
828                 info->var.green.length = 5;
829                 info->var.blue.length = 5;
830                 info->var.transp.offset = 15;
831                 info->var.transp.length = 1;
832                 break;
833         case 16:
834                 info->var.red.offset = 11;
835                 info->var.green.offset = 5;
836                 info->var.blue.offset = 0;
837                 info->var.red.length = 5;
838                 info->var.green.length = 6;
839                 info->var.blue.length = 5;
840                 info->var.transp.offset = 0;
841                 break;
842         case 24:
843                 info->var.red.offset = 16;
844                 info->var.green.offset = 8;
845                 info->var.blue.offset = 0;
846                 info->var.red.length = 8;
847                 info->var.green.length = 8;
848                 info->var.blue.length = 8;
849                 info->var.transp.offset = 0;
850                 info->var.transp.length = 0;
851                 break;
852         case 32:
853                 info->var.red.offset = 16;
854                 info->var.green.offset = 8;
855                 info->var.blue.offset = 0;
856                 info->var.red.length = 8;
857                 info->var.green.length = 8;
858                 info->var.blue.length = 8;
859                 info->var.transp.offset = 24;
860                 info->var.transp.length = 8;
861                 break;
862         default:
863                 break;
864         }
865
866         fb->fbdev = info;
867
868         par->intel_fb = intel_fb;
869         par->dev = dev;
870
871         /* To allow resizeing without swapping buffers */
872         printk("allocated %dx%d fb: 0x%08x, bo %p\n", intel_fb->base.width,
873                intel_fb->base.height, obj_priv->gtt_offset, fbo);
874
875         mutex_unlock(&dev->struct_mutex);
876         return 0;
877
878 out_unref:
879         drm_gem_object_unreference(fbo);
880         mutex_unlock(&dev->struct_mutex);
881 out:
882         return ret;
883 }
884
885 static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc *crtc)
886 {
887         struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
888         struct intel_framebuffer *intel_fb;
889         struct drm_framebuffer *fb;
890         struct drm_connector *connector;
891         struct fb_info *info;
892         struct intelfb_par *par;
893         struct drm_mode_set *modeset;
894         unsigned int width, height;
895         int new_fb = 0;
896         int ret, i, conn_count;
897
898         if (!drm_helper_crtc_in_use(crtc))
899                 return 0;
900
901         if (!crtc->desired_mode)
902                 return 0;
903
904         width = crtc->desired_mode->hdisplay;
905         height = crtc->desired_mode->vdisplay;
906
907         /* is there an fb bound to this crtc already */
908         if (!intel_crtc->mode_set.fb) {
909                 ret = intelfb_create(dev, width, height, width, height, &intel_fb);
910                 if (ret)
911                         return -EINVAL;
912                 new_fb = 1;
913         } else {
914                 fb = intel_crtc->mode_set.fb;
915                 intel_fb = to_intel_framebuffer(fb);
916                 if ((intel_fb->base.width < width) || (intel_fb->base.height < height))
917                         return -EINVAL;
918         }
919         
920         info = intel_fb->base.fbdev;
921         par = info->par;
922
923         modeset = &intel_crtc->mode_set;
924         modeset->fb = &intel_fb->base;
925         conn_count = 0;
926         list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
927                 if (connector->encoder)
928                         if (connector->encoder->crtc == modeset->crtc) {
929                                 modeset->connectors[conn_count] = connector;
930                                 conn_count++;
931                                 if (conn_count > INTELFB_CONN_LIMIT)
932                                         BUG();
933                         }
934         }
935         
936         for (i = conn_count; i < INTELFB_CONN_LIMIT; i++)
937                 modeset->connectors[i] = NULL;
938
939         par->crtc_ids[0] = crtc->base.id;
940
941         modeset->num_connectors = conn_count;
942         if (modeset->mode != modeset->crtc->desired_mode)
943                 modeset->mode = modeset->crtc->desired_mode;
944
945         par->crtc_count = 1;
946
947         if (new_fb) {
948                 info->var.pixclock = -1;
949                 if (register_framebuffer(info) < 0)
950                         return -EINVAL;
951         } else
952                 intelfb_set_par(info);
953
954         printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
955                info->fix.id);
956
957         /* Switch back to kernel console on panic */
958         panic_mode = *modeset;
959         atomic_notifier_chain_register(&panic_notifier_list, &paniced);
960         printk(KERN_INFO "registered panic notifier\n");
961
962         return 0;
963 }
964
965 static int intelfb_multi_fb_probe(struct drm_device *dev)
966 {
967
968         struct drm_crtc *crtc;
969         int ret = 0;
970
971         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
972                 ret = intelfb_multi_fb_probe_crtc(dev, crtc);
973                 if (ret)
974                         return ret;
975         }
976         return ret;
977 }
978
979 static int intelfb_single_fb_probe(struct drm_device *dev)
980 {
981         struct drm_crtc *crtc;
982         struct drm_connector *connector;
983         unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
984         unsigned int surface_width = 0, surface_height = 0;
985         int new_fb = 0;
986         int crtc_count = 0;
987         int ret, i, conn_count = 0;
988         struct intel_framebuffer *intel_fb;
989         struct fb_info *info;
990         struct intelfb_par *par;
991         struct drm_mode_set *modeset = NULL;
992
993         DRM_DEBUG("\n");
994         /* first up get a count of crtcs now in use and new min/maxes width/heights */
995         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
996                 if (drm_helper_crtc_in_use(crtc)) {
997                         if (crtc->desired_mode) {
998                                 if (crtc->desired_mode->hdisplay < fb_width)
999                                         fb_width = crtc->desired_mode->hdisplay;
1000                                 
1001                                 if (crtc->desired_mode->vdisplay < fb_height)
1002                                         fb_height = crtc->desired_mode->vdisplay;
1003                                 
1004                                 if (crtc->desired_mode->hdisplay > surface_width)
1005                                         surface_width = crtc->desired_mode->hdisplay;
1006                                 
1007                                 if (crtc->desired_mode->vdisplay > surface_height)
1008                                         surface_height = crtc->desired_mode->vdisplay;
1009
1010                         }
1011                 crtc_count++;
1012                 }
1013         }
1014
1015         if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
1016                 /* hmm everyone went away - assume VGA cable just fell out
1017                    and will come back later. */
1018                 return 0;
1019         }
1020
1021         /* do we have an fb already? */
1022         if (list_empty(&dev->mode_config.fb_kernel_list)) {
1023                 /* create an fb if we don't have one */
1024                 ret = intelfb_create(dev, fb_width, fb_height, surface_width, surface_height, &intel_fb);
1025                 if (ret)
1026                         return -EINVAL;
1027                 new_fb = 1;
1028         } else {
1029                 struct drm_framebuffer *fb;
1030                 fb = list_first_entry(&dev->mode_config.fb_kernel_list, struct drm_framebuffer, filp_head);
1031                 intel_fb = to_intel_framebuffer(fb);
1032
1033                 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
1034                    As really we can't resize an fbdev that is in the wild currently due to fbdev
1035                    not really being designed for the lower layers moving stuff around under it.
1036                    - so in the grand style of things - punt. */
1037                 if ((fb->width < surface_width) || (fb->height < surface_height)) {
1038                         DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
1039                         return -EINVAL;
1040                 }
1041         }
1042
1043         info = intel_fb->base.fbdev;
1044         par = info->par;
1045
1046         crtc_count = 0;
1047         /* okay we need to setup new connector sets in the crtcs */
1048         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
1049                 struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
1050                 modeset = &intel_crtc->mode_set;
1051                 modeset->fb = &intel_fb->base;
1052                 conn_count = 0;
1053                 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
1054                         if (connector->encoder)
1055                                 if(connector->encoder->crtc == modeset->crtc) {
1056                                         modeset->connectors[conn_count] = connector;
1057                                         conn_count++;
1058                                         if (conn_count > INTELFB_CONN_LIMIT)
1059                                                 BUG();
1060                                 }
1061                 }
1062
1063                 for (i = conn_count; i < INTELFB_CONN_LIMIT; i++)
1064                         modeset->connectors[i] = NULL;
1065
1066                 par->crtc_ids[crtc_count++] = crtc->base.id;
1067
1068                 modeset->num_connectors = conn_count;
1069                 if (modeset->mode != modeset->crtc->desired_mode)
1070                         modeset->mode = modeset->crtc->desired_mode;
1071         }
1072         par->crtc_count = crtc_count;
1073
1074         if (new_fb) {
1075                 info->var.pixclock = -1;
1076                 if (register_framebuffer(info) < 0)
1077                         return -EINVAL;
1078         } else
1079                 intelfb_set_par(info);
1080                 
1081         printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
1082                info->fix.id);
1083
1084         /* Switch back to kernel console on panic */
1085         panic_mode = *modeset;
1086         atomic_notifier_chain_register(&panic_notifier_list, &paniced);
1087         printk(KERN_INFO "registered panic notifier\n");
1088
1089         return 0;
1090 }
1091
1092 int intelfb_probe(struct drm_device *dev)
1093 {
1094         int ret;
1095
1096         DRM_DEBUG("\n");
1097
1098         /* something has changed in the lower levels of hell - deal with it 
1099            here */
1100
1101         /* two modes : a) 1 fb to rule all crtcs.
1102                        b) one fb per crtc.
1103            two actions 1) new connected device
1104                        2) device removed.
1105            case a/1 : if the fb surface isn't big enough - resize the surface fb.
1106                       if the fb size isn't big enough - resize fb into surface.
1107                       if everything big enough configure the new crtc/etc.
1108            case a/2 : undo the configuration
1109                       possibly resize down the fb to fit the new configuration.
1110            case b/1 : see if it is on a new crtc - setup a new fb and add it.
1111            case b/2 : teardown the new fb.
1112         */
1113
1114         /* mode a first */
1115         /* search for an fb */
1116         if (i915_fbpercrtc == 1) {
1117                 ret = intelfb_multi_fb_probe(dev);
1118         } else {
1119                 ret = intelfb_single_fb_probe(dev);
1120         }
1121
1122         return ret;
1123 }
1124 EXPORT_SYMBOL(intelfb_probe);
1125
1126 int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
1127 {
1128         struct fb_info *info;
1129         struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
1130
1131         if (!fb)
1132                 return -EINVAL;
1133
1134         info = fb->fbdev;
1135         
1136         if (info) {
1137                 unregister_framebuffer(info);
1138                 iounmap(info->screen_base);
1139                 mutex_lock(&dev->struct_mutex);
1140                 drm_gem_object_unreference(intel_fb->obj);
1141                 mutex_unlock(&dev->struct_mutex);
1142                 framebuffer_release(info);
1143         }
1144
1145         atomic_notifier_chain_unregister(&panic_notifier_list, &paniced);
1146         memset(&panic_mode, 0, sizeof(struct drm_mode_set));
1147         return 0;
1148 }
1149 EXPORT_SYMBOL(intelfb_remove);
1150 MODULE_LICENSE("GPL");