[modesetting-101] update mode count after fill_modes.
[platform/upstream/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 ? V_PHSYNC : V_NHSYNC;
267                 drm_mode->flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? V_PVSYNC : V_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 struct fb_ops intelfb_ops = {
529         .owner = THIS_MODULE,
530         //.fb_open = intelfb_open,
531         //.fb_read = intelfb_read,
532         //.fb_write = intelfb_write,
533         //.fb_release = intelfb_release,
534         //.fb_ioctl = intelfb_ioctl,
535         .fb_check_var = intelfb_check_var,
536         .fb_set_par = intelfb_set_par,
537         .fb_setcolreg = intelfb_setcolreg,
538         .fb_fillrect = cfb_fillrect,
539         .fb_copyarea = cfb_copyarea, //intelfb_copyarea,
540         .fb_imageblit = cfb_imageblit, //intelfb_imageblit,
541         .fb_pan_display = intelfb_pan_display,
542 };
543
544 /**
545  * Curretly it is assumed that the old framebuffer is reused.
546  *
547  * LOCKING
548  * caller should hold the mode config lock.
549  *
550  */
551 int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc)
552 {
553         struct fb_info *info;
554         struct drm_framebuffer *fb;
555         struct drm_display_mode *mode = crtc->desired_mode;
556
557         fb = crtc->fb;
558         if (!fb)
559                 return 1;
560
561         info = fb->fbdev;
562         if (!info)
563                 return 1;
564
565         if (!mode)
566                 return 1;
567
568         info->var.xres = mode->hdisplay;
569         info->var.right_margin = mode->hsync_start - mode->hdisplay;
570         info->var.hsync_len = mode->hsync_end - mode->hsync_start;
571         info->var.left_margin = mode->htotal - mode->hsync_end;
572         info->var.yres = mode->vdisplay;
573         info->var.lower_margin = mode->vsync_start - mode->vdisplay;
574         info->var.vsync_len = mode->vsync_end - mode->vsync_start;
575         info->var.upper_margin = mode->vtotal - mode->vsync_end;
576         info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100;
577         /* avoid overflow */
578         info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;
579
580         return 0;
581 }
582 EXPORT_SYMBOL(intelfb_resize);
583
584 static struct drm_mode_set panic_mode;
585
586 int intelfb_panic(struct notifier_block *n, unsigned long ununsed,
587                   void *panic_str)
588 {
589         DRM_ERROR("panic occurred, switching back to text console\n");
590         drm_crtc_helper_set_config(&panic_mode);
591
592         return 0;
593 }
594 EXPORT_SYMBOL(intelfb_panic);
595  
596 static struct notifier_block paniced = {
597         .notifier_call = intelfb_panic,
598 };
599
600 int intelfb_create(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height, 
601                    uint32_t surface_width, uint32_t surface_height,
602                    struct intel_framebuffer **intel_fb_p)
603 {
604         struct fb_info *info;
605         struct intelfb_par *par;
606         struct drm_framebuffer *fb;
607         struct intel_framebuffer *intel_fb;
608         struct drm_mode_fb_cmd mode_cmd;
609         struct drm_buffer_object *fbo = NULL;
610         struct device *device = &dev->pdev->dev; 
611         int ret;
612
613         mode_cmd.width = surface_width;/* crtc->desired_mode->hdisplay; */
614         mode_cmd.height = surface_height;/* crtc->desired_mode->vdisplay; */
615         
616         mode_cmd.bpp = 32;
617         mode_cmd.pitch = mode_cmd.width * ((mode_cmd.bpp + 1) / 8);
618         mode_cmd.depth = 24;
619
620         ret = drm_buffer_object_create(dev, mode_cmd.pitch * mode_cmd.height, 
621                                         drm_bo_type_kernel,
622                                         DRM_BO_FLAG_READ |
623                                         DRM_BO_FLAG_WRITE |
624                                         DRM_BO_FLAG_MEM_TT |
625                                         DRM_BO_FLAG_MEM_VRAM |
626                                         DRM_BO_FLAG_NO_EVICT,
627                                         DRM_BO_HINT_DONT_FENCE, 0, 0,
628                                         &fbo);
629         if (ret || !fbo) {
630                 printk(KERN_ERR "failed to allocate framebuffer\n");
631                 return -EINVAL;
632         }
633         
634
635         fb = intel_user_framebuffer_create(dev, NULL, &mode_cmd);
636         if (!fb) {
637                 drm_bo_usage_deref_unlocked(&fbo);
638                 DRM_ERROR("failed to allocate fb.\n");
639                 return -EINVAL;
640         }
641
642         list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
643
644         intel_fb = to_intel_framebuffer(fb);
645         *intel_fb_p = intel_fb;
646
647         intel_fb->bo = fbo;
648
649         info = framebuffer_alloc(sizeof(struct intelfb_par), device);
650         if (!info)
651                 return -EINVAL;
652
653         par = info->par;
654
655         strcpy(info->fix.id, "inteldrmfb");
656         info->fix.type = FB_TYPE_PACKED_PIXELS;
657         info->fix.visual = FB_VISUAL_TRUECOLOR;
658         info->fix.type_aux = 0;
659         info->fix.xpanstep = 1; /* doing it in hw */
660         info->fix.ypanstep = 1; /* doing it in hw */
661         info->fix.ywrapstep = 0;
662         info->fix.accel = FB_ACCEL_I830;
663         info->fix.type_aux = 0;
664
665         info->flags = FBINFO_DEFAULT;
666
667         info->fbops = &intelfb_ops;
668
669         info->fix.line_length = fb->pitch;
670         info->fix.smem_start = intel_fb->bo->offset + dev->mode_config.fb_base;
671         info->fix.smem_len = info->fix.line_length * fb->height;
672
673         info->flags = FBINFO_DEFAULT;
674
675         ret = drm_bo_kmap(intel_fb->bo, 0, intel_fb->bo->num_pages, &intel_fb->kmap);
676         if (ret)
677                 DRM_ERROR("error mapping fb: %d\n", ret);
678
679         info->screen_base = intel_fb->kmap.virtual;
680         info->screen_size = info->fix.smem_len; /* FIXME */
681
682         memset(intel_fb->kmap.virtual, 0, info->screen_size);
683
684         info->pseudo_palette = fb->pseudo_palette;
685         info->var.xres_virtual = fb->width;
686         info->var.yres_virtual = fb->height;
687         info->var.bits_per_pixel = fb->bits_per_pixel;
688         info->var.xoffset = 0;
689         info->var.yoffset = 0;
690         info->var.activate = FB_ACTIVATE_NOW;
691         info->var.height = -1;
692         info->var.width = -1;
693
694         info->var.xres = fb_width;
695         info->var.yres = fb_height;
696
697         if (IS_I9XX(dev)) {
698                 info->fix.mmio_start = pci_resource_start(dev->pdev, 0);
699                 info->fix.mmio_len = pci_resource_len(dev->pdev, 0);
700         } else {
701                 info->fix.mmio_start = pci_resource_start(dev->pdev, 1);
702                 info->fix.mmio_len = pci_resource_len(dev->pdev, 1);
703         }
704
705         info->pixmap.size = 64*1024;
706         info->pixmap.buf_align = 8;
707         info->pixmap.access_align = 32;
708         info->pixmap.flags = FB_PIXMAP_SYSTEM;
709         info->pixmap.scan_align = 1;
710
711         DRM_DEBUG("fb depth is %d\n", fb->depth);
712         DRM_DEBUG("   pitch is %d\n", fb->pitch);
713         switch(fb->depth) {
714         case 8:
715                 info->var.red.offset = 0;
716                 info->var.green.offset = 0;
717                 info->var.blue.offset = 0;
718                 info->var.red.length = 8; /* 8bit DAC */
719                 info->var.green.length = 8;
720                 info->var.blue.length = 8;
721                 info->var.transp.offset = 0;
722                 info->var.transp.length = 0;
723                 break;
724         case 15:
725                 info->var.red.offset = 10;
726                 info->var.green.offset = 5;
727                 info->var.blue.offset = 0;
728                 info->var.red.length = 5;
729                 info->var.green.length = 5;
730                 info->var.blue.length = 5;
731                 info->var.transp.offset = 15;
732                 info->var.transp.length = 1;
733                 break;
734         case 16:
735                 info->var.red.offset = 11;
736                 info->var.green.offset = 5;
737                 info->var.blue.offset = 0;
738                 info->var.red.length = 5;
739                 info->var.green.length = 6;
740                 info->var.blue.length = 5;
741                 info->var.transp.offset = 0;
742                 break;
743         case 24:
744                 info->var.red.offset = 16;
745                 info->var.green.offset = 8;
746                 info->var.blue.offset = 0;
747                 info->var.red.length = 8;
748                 info->var.green.length = 8;
749                 info->var.blue.length = 8;
750                 info->var.transp.offset = 0;
751                 info->var.transp.length = 0;
752                 break;
753         case 32:
754                 info->var.red.offset = 16;
755                 info->var.green.offset = 8;
756                 info->var.blue.offset = 0;
757                 info->var.red.length = 8;
758                 info->var.green.length = 8;
759                 info->var.blue.length = 8;
760                 info->var.transp.offset = 24;
761                 info->var.transp.length = 8;
762                 break;
763         default:
764                 break;
765         }
766
767         fb->fbdev = info;
768
769         par->intel_fb = intel_fb;
770         par->dev = dev;
771
772         /* To allow resizeing without swapping buffers */
773         printk("allocated %dx%d fb: 0x%08lx, bo %p\n", intel_fb->base.width,
774                intel_fb->base.height, intel_fb->bo->offset, fbo);
775
776         return 0;
777 }
778
779 static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc *crtc)
780 {
781         struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
782         struct intel_framebuffer *intel_fb;
783         struct drm_framebuffer *fb;
784         struct drm_connector *connector;
785         struct fb_info *info;
786         struct intelfb_par *par;
787         struct drm_mode_set *modeset;
788         unsigned int width, height;
789         int new_fb = 0;
790         int ret, i, conn_count;
791
792         if (!drm_helper_crtc_in_use(crtc))
793                 return 0;
794
795         if (!crtc->desired_mode)
796                 return 0;
797
798         width = crtc->desired_mode->hdisplay;
799         height = crtc->desired_mode->vdisplay;
800
801         /* is there an fb bound to this crtc already */
802         if (!intel_crtc->mode_set.fb) {
803                 ret = intelfb_create(dev, width, height, width, height, &intel_fb);
804                 if (ret)
805                         return -EINVAL;
806                 new_fb = 1;
807         } else {
808                 fb = intel_crtc->mode_set.fb;
809                 intel_fb = to_intel_framebuffer(fb);
810                 if ((intel_fb->base.width < width) || (intel_fb->base.height < height))
811                         return -EINVAL;
812         }
813         
814         info = intel_fb->base.fbdev;
815         par = info->par;
816
817         modeset = &intel_crtc->mode_set;
818         modeset->fb = &intel_fb->base;
819         conn_count = 0;
820         list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
821                 if (connector->encoder)
822                         if (connector->encoder->crtc == modeset->crtc) {
823                                 modeset->connectors[conn_count] = connector;
824                                 conn_count++;
825                                 if (conn_count > INTELFB_CONN_LIMIT)
826                                         BUG();
827                         }
828         }
829         
830         for (i = conn_count; i < INTELFB_CONN_LIMIT; i++)
831                 modeset->connectors[i] = NULL;
832
833         par->crtc_ids[0] = crtc->base.id;
834
835         modeset->num_connectors = conn_count;
836         if (modeset->mode != modeset->crtc->desired_mode)
837                 modeset->mode = modeset->crtc->desired_mode;
838
839         par->crtc_count = 1;
840
841         if (new_fb) {
842                 info->var.pixclock = -1;
843                 if (register_framebuffer(info) < 0)
844                         return -EINVAL;
845         } else
846                 intelfb_set_par(info);
847
848         printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
849                info->fix.id);
850
851         /* Switch back to kernel console on panic */
852         panic_mode = *modeset;
853         atomic_notifier_chain_register(&panic_notifier_list, &paniced);
854         printk(KERN_INFO "registered panic notifier\n");
855
856         return 0;
857 }
858
859 static int intelfb_multi_fb_probe(struct drm_device *dev)
860 {
861
862         struct drm_crtc *crtc;
863         int ret = 0;
864
865         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
866                 ret = intelfb_multi_fb_probe_crtc(dev, crtc);
867                 if (ret)
868                         return ret;
869         }
870         return ret;
871 }
872
873 static int intelfb_single_fb_probe(struct drm_device *dev)
874 {
875         struct drm_crtc *crtc;
876         struct drm_connector *connector;
877         unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
878         unsigned int surface_width = 0, surface_height = 0;
879         int new_fb = 0;
880         int crtc_count = 0;
881         int ret, i, conn_count = 0;
882         struct intel_framebuffer *intel_fb;
883         struct fb_info *info;
884         struct intelfb_par *par;
885         struct drm_mode_set *modeset;
886
887         DRM_DEBUG("\n");
888         /* first up get a count of crtcs now in use and new min/maxes width/heights */
889         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
890                 if (drm_helper_crtc_in_use(crtc)) {
891                         if (crtc->desired_mode) {
892                                 if (crtc->desired_mode->hdisplay < fb_width)
893                                         fb_width = crtc->desired_mode->hdisplay;
894                                 
895                                 if (crtc->desired_mode->vdisplay < fb_height)
896                                         fb_height = crtc->desired_mode->vdisplay;
897                                 
898                                 if (crtc->desired_mode->hdisplay > surface_width)
899                                         surface_width = crtc->desired_mode->hdisplay;
900                                 
901                                 if (crtc->desired_mode->vdisplay > surface_height)
902                                         surface_height = crtc->desired_mode->vdisplay;
903
904                         }
905                 crtc_count++;
906                 }
907         }
908
909         if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
910                 /* hmm everyone went away - assume VGA cable just fell out
911                    and will come back later. */
912                 return 0;
913         }
914
915         /* do we have an fb already? */
916         if (list_empty(&dev->mode_config.fb_kernel_list)) {
917                 /* create an fb if we don't have one */
918                 ret = intelfb_create(dev, fb_width, fb_height, surface_width, surface_height, &intel_fb);
919                 if (ret)
920                         return -EINVAL;
921                 new_fb = 1;
922         } else {
923                 struct drm_framebuffer *fb;
924                 fb = list_first_entry(&dev->mode_config.fb_kernel_list, struct drm_framebuffer, filp_head);
925                 intel_fb = to_intel_framebuffer(fb);
926
927                 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
928                    As really we can't resize an fbdev that is in the wild currently due to fbdev
929                    not really being designed for the lower layers moving stuff around under it.
930                    - so in the grand style of things - punt. */
931                 if ((fb->width < surface_width) || (fb->height < surface_height)) {
932                         DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
933                         return -EINVAL;
934                 }
935         }
936
937         info = intel_fb->base.fbdev;
938         par = info->par;
939
940         crtc_count = 0;
941         /* okay we need to setup new connector sets in the crtcs */
942         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
943                 struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
944                 modeset = &intel_crtc->mode_set;
945                 modeset->fb = &intel_fb->base;
946                 conn_count = 0;
947                 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
948                         if (connector->encoder)
949                                 if(connector->encoder->crtc == modeset->crtc) {
950                                         modeset->connectors[conn_count] = connector;
951                                         conn_count++;
952                                         if (conn_count > INTELFB_CONN_LIMIT)
953                                                 BUG();
954                                 }
955                 }
956
957                 for (i = conn_count; i < INTELFB_CONN_LIMIT; i++)
958                         modeset->connectors[i] = NULL;
959
960                 par->crtc_ids[crtc_count++] = crtc->base.id;
961
962                 modeset->num_connectors = conn_count;
963                 if (modeset->mode != modeset->crtc->desired_mode)
964                         modeset->mode = modeset->crtc->desired_mode;
965         }
966         par->crtc_count = crtc_count;
967
968         if (new_fb) {
969                 info->var.pixclock = -1;
970                 if (register_framebuffer(info) < 0)
971                         return -EINVAL;
972         } else
973                 intelfb_set_par(info);
974                 
975         printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
976                info->fix.id);
977
978         /* Switch back to kernel console on panic */
979         panic_mode = *modeset;
980         atomic_notifier_chain_register(&panic_notifier_list, &paniced);
981         printk(KERN_INFO "registered panic notifier\n");
982
983         return 0;
984 }
985
986 int intelfb_probe(struct drm_device *dev)
987 {
988         int ret;
989
990         DRM_DEBUG("\n");
991
992         /* something has changed in the lower levels of hell - deal with it 
993            here */
994
995         /* two modes : a) 1 fb to rule all crtcs.
996                        b) one fb per crtc.
997            two actions 1) new connected device
998                        2) device removed.
999            case a/1 : if the fb surface isn't big enough - resize the surface fb.
1000                       if the fb size isn't big enough - resize fb into surface.
1001                       if everything big enough configure the new crtc/etc.
1002            case a/2 : undo the configuration
1003                       possibly resize down the fb to fit the new configuration.
1004            case b/1 : see if it is on a new crtc - setup a new fb and add it.
1005            case b/2 : teardown the new fb.
1006         */
1007
1008         /* mode a first */
1009         /* search for an fb */
1010         if (i915_fbpercrtc == 1) {
1011                 ret = intelfb_multi_fb_probe(dev);
1012         } else {
1013                 ret = intelfb_single_fb_probe(dev);
1014         }
1015
1016         return ret;
1017 }
1018 EXPORT_SYMBOL(intelfb_probe);
1019
1020 int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
1021 {
1022         struct fb_info *info;
1023         struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
1024
1025         if (!fb)
1026                 return -EINVAL;
1027
1028         info = fb->fbdev;
1029         
1030         if (info) {
1031                 unregister_framebuffer(info);
1032                 drm_bo_kunmap(&intel_fb->kmap);
1033                 drm_bo_usage_deref_unlocked(&intel_fb->bo);
1034                 framebuffer_release(info);
1035         }
1036
1037         atomic_notifier_chain_unregister(&panic_notifier_list, &paniced);
1038         memset(&panic_mode, 0, sizeof(struct drm_mode_set));
1039         return 0;
1040 }
1041 EXPORT_SYMBOL(intelfb_remove);
1042 MODULE_LICENSE("GPL");