OSDN Git Service

radeon: make buffer swap for older drivers work again on GEM
[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         ret = i915_gem_object_pin(fbo, PAGE_SIZE);
720         if (ret) {
721                 DRM_ERROR("failed to pin fb: %d\n", ret);
722                 goto out_unref;
723         }
724
725         fb = intel_user_framebuffer_create(dev, NULL, &mode_cmd);
726         if (!fb) {
727                 DRM_ERROR("failed to allocate fb.\n");
728                 ret = -ENOMEM;
729                 goto out_unref;
730         }
731
732         list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
733
734         intel_fb = to_intel_framebuffer(fb);
735         *intel_fb_p = intel_fb;
736
737         intel_fb->obj = fbo;
738
739         info = framebuffer_alloc(sizeof(struct intelfb_par), device);
740         if (!info) {
741                 ret = -ENOMEM;
742                 goto out_unref;
743         }
744
745         par = info->par;
746
747         strcpy(info->fix.id, "inteldrmfb");
748         info->fix.type = FB_TYPE_PACKED_PIXELS;
749         info->fix.visual = FB_VISUAL_TRUECOLOR;
750         info->fix.type_aux = 0;
751         info->fix.xpanstep = 1; /* doing it in hw */
752         info->fix.ypanstep = 1; /* doing it in hw */
753         info->fix.ywrapstep = 0;
754         info->fix.accel = FB_ACCEL_I830;
755         info->fix.type_aux = 0;
756
757         info->flags = FBINFO_DEFAULT;
758
759         info->fbops = &intelfb_ops;
760
761         info->fix.line_length = fb->pitch;
762         info->fix.smem_start = dev->mode_config.fb_base + obj_priv->gtt_offset;
763         info->fix.smem_len = size;
764
765         info->flags = FBINFO_DEFAULT;
766
767         info->screen_base = ioremap_wc(dev->agp->base + obj_priv->gtt_offset,
768                                        size);
769         if (!info->screen_base) {
770                 ret = -ENOSPC;
771                 goto out_unref;
772         }
773         info->screen_size = size;
774
775         memset(info->screen_base, 0, size);
776
777         info->pseudo_palette = fb->pseudo_palette;
778         info->var.xres_virtual = fb->width;
779         info->var.yres_virtual = fb->height;
780         info->var.bits_per_pixel = fb->bits_per_pixel;
781         info->var.xoffset = 0;
782         info->var.yoffset = 0;
783         info->var.activate = FB_ACTIVATE_NOW;
784         info->var.height = -1;
785         info->var.width = -1;
786
787         info->var.xres = fb_width;
788         info->var.yres = fb_height;
789
790         if (IS_I9XX(dev)) {
791                 info->fix.mmio_start = pci_resource_start(dev->pdev, 0);
792                 info->fix.mmio_len = pci_resource_len(dev->pdev, 0);
793         } else {
794                 info->fix.mmio_start = pci_resource_start(dev->pdev, 1);
795                 info->fix.mmio_len = pci_resource_len(dev->pdev, 1);
796         }
797
798         info->pixmap.size = 64*1024;
799         info->pixmap.buf_align = 8;
800         info->pixmap.access_align = 32;
801         info->pixmap.flags = FB_PIXMAP_SYSTEM;
802         info->pixmap.scan_align = 1;
803
804         DRM_DEBUG("fb depth is %d\n", fb->depth);
805         DRM_DEBUG("   pitch is %d\n", fb->pitch);
806         switch(fb->depth) {
807         case 8:
808                 info->var.red.offset = 0;
809                 info->var.green.offset = 0;
810                 info->var.blue.offset = 0;
811                 info->var.red.length = 8; /* 8bit DAC */
812                 info->var.green.length = 8;
813                 info->var.blue.length = 8;
814                 info->var.transp.offset = 0;
815                 info->var.transp.length = 0;
816                 break;
817         case 15:
818                 info->var.red.offset = 10;
819                 info->var.green.offset = 5;
820                 info->var.blue.offset = 0;
821                 info->var.red.length = 5;
822                 info->var.green.length = 5;
823                 info->var.blue.length = 5;
824                 info->var.transp.offset = 15;
825                 info->var.transp.length = 1;
826                 break;
827         case 16:
828                 info->var.red.offset = 11;
829                 info->var.green.offset = 5;
830                 info->var.blue.offset = 0;
831                 info->var.red.length = 5;
832                 info->var.green.length = 6;
833                 info->var.blue.length = 5;
834                 info->var.transp.offset = 0;
835                 break;
836         case 24:
837                 info->var.red.offset = 16;
838                 info->var.green.offset = 8;
839                 info->var.blue.offset = 0;
840                 info->var.red.length = 8;
841                 info->var.green.length = 8;
842                 info->var.blue.length = 8;
843                 info->var.transp.offset = 0;
844                 info->var.transp.length = 0;
845                 break;
846         case 32:
847                 info->var.red.offset = 16;
848                 info->var.green.offset = 8;
849                 info->var.blue.offset = 0;
850                 info->var.red.length = 8;
851                 info->var.green.length = 8;
852                 info->var.blue.length = 8;
853                 info->var.transp.offset = 24;
854                 info->var.transp.length = 8;
855                 break;
856         default:
857                 break;
858         }
859
860         fb->fbdev = info;
861
862         par->intel_fb = intel_fb;
863         par->dev = dev;
864
865         /* To allow resizeing without swapping buffers */
866         printk("allocated %dx%d fb: 0x%08x, bo %p\n", intel_fb->base.width,
867                intel_fb->base.height, obj_priv->gtt_offset, fbo);
868
869         mutex_unlock(&dev->struct_mutex);
870         return 0;
871
872 out_unref:
873         drm_gem_object_unreference(fbo);
874         mutex_unlock(&dev->struct_mutex);
875 out:
876         return ret;
877 }
878
879 static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc *crtc)
880 {
881         struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
882         struct intel_framebuffer *intel_fb;
883         struct drm_framebuffer *fb;
884         struct drm_connector *connector;
885         struct fb_info *info;
886         struct intelfb_par *par;
887         struct drm_mode_set *modeset;
888         unsigned int width, height;
889         int new_fb = 0;
890         int ret, i, conn_count;
891
892         if (!drm_helper_crtc_in_use(crtc))
893                 return 0;
894
895         if (!crtc->desired_mode)
896                 return 0;
897
898         width = crtc->desired_mode->hdisplay;
899         height = crtc->desired_mode->vdisplay;
900
901         /* is there an fb bound to this crtc already */
902         if (!intel_crtc->mode_set.fb) {
903                 ret = intelfb_create(dev, width, height, width, height, &intel_fb);
904                 if (ret)
905                         return -EINVAL;
906                 new_fb = 1;
907         } else {
908                 fb = intel_crtc->mode_set.fb;
909                 intel_fb = to_intel_framebuffer(fb);
910                 if ((intel_fb->base.width < width) || (intel_fb->base.height < height))
911                         return -EINVAL;
912         }
913         
914         info = intel_fb->base.fbdev;
915         par = info->par;
916
917         modeset = &intel_crtc->mode_set;
918         modeset->fb = &intel_fb->base;
919         conn_count = 0;
920         list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
921                 if (connector->encoder)
922                         if (connector->encoder->crtc == modeset->crtc) {
923                                 modeset->connectors[conn_count] = connector;
924                                 conn_count++;
925                                 if (conn_count > INTELFB_CONN_LIMIT)
926                                         BUG();
927                         }
928         }
929         
930         for (i = conn_count; i < INTELFB_CONN_LIMIT; i++)
931                 modeset->connectors[i] = NULL;
932
933         par->crtc_ids[0] = crtc->base.id;
934
935         modeset->num_connectors = conn_count;
936         if (modeset->mode != modeset->crtc->desired_mode)
937                 modeset->mode = modeset->crtc->desired_mode;
938
939         par->crtc_count = 1;
940
941         if (new_fb) {
942                 info->var.pixclock = -1;
943                 if (register_framebuffer(info) < 0)
944                         return -EINVAL;
945         } else
946                 intelfb_set_par(info);
947
948         printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
949                info->fix.id);
950
951         /* Switch back to kernel console on panic */
952         panic_mode = *modeset;
953         atomic_notifier_chain_register(&panic_notifier_list, &paniced);
954         printk(KERN_INFO "registered panic notifier\n");
955
956         return 0;
957 }
958
959 static int intelfb_multi_fb_probe(struct drm_device *dev)
960 {
961
962         struct drm_crtc *crtc;
963         int ret = 0;
964
965         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
966                 ret = intelfb_multi_fb_probe_crtc(dev, crtc);
967                 if (ret)
968                         return ret;
969         }
970         return ret;
971 }
972
973 static int intelfb_single_fb_probe(struct drm_device *dev)
974 {
975         struct drm_crtc *crtc;
976         struct drm_connector *connector;
977         unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
978         unsigned int surface_width = 0, surface_height = 0;
979         int new_fb = 0;
980         int crtc_count = 0;
981         int ret, i, conn_count = 0;
982         struct intel_framebuffer *intel_fb;
983         struct fb_info *info;
984         struct intelfb_par *par;
985         struct drm_mode_set *modeset = NULL;
986
987         DRM_DEBUG("\n");
988         /* first up get a count of crtcs now in use and new min/maxes width/heights */
989         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
990                 if (drm_helper_crtc_in_use(crtc)) {
991                         if (crtc->desired_mode) {
992                                 if (crtc->desired_mode->hdisplay < fb_width)
993                                         fb_width = crtc->desired_mode->hdisplay;
994                                 
995                                 if (crtc->desired_mode->vdisplay < fb_height)
996                                         fb_height = crtc->desired_mode->vdisplay;
997                                 
998                                 if (crtc->desired_mode->hdisplay > surface_width)
999                                         surface_width = crtc->desired_mode->hdisplay;
1000                                 
1001                                 if (crtc->desired_mode->vdisplay > surface_height)
1002                                         surface_height = crtc->desired_mode->vdisplay;
1003
1004                         }
1005                 crtc_count++;
1006                 }
1007         }
1008
1009         if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
1010                 /* hmm everyone went away - assume VGA cable just fell out
1011                    and will come back later. */
1012                 return 0;
1013         }
1014
1015         /* do we have an fb already? */
1016         if (list_empty(&dev->mode_config.fb_kernel_list)) {
1017                 /* create an fb if we don't have one */
1018                 ret = intelfb_create(dev, fb_width, fb_height, surface_width, surface_height, &intel_fb);
1019                 if (ret)
1020                         return -EINVAL;
1021                 new_fb = 1;
1022         } else {
1023                 struct drm_framebuffer *fb;
1024                 fb = list_first_entry(&dev->mode_config.fb_kernel_list, struct drm_framebuffer, filp_head);
1025                 intel_fb = to_intel_framebuffer(fb);
1026
1027                 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
1028                    As really we can't resize an fbdev that is in the wild currently due to fbdev
1029                    not really being designed for the lower layers moving stuff around under it.
1030                    - so in the grand style of things - punt. */
1031                 if ((fb->width < surface_width) || (fb->height < surface_height)) {
1032                         DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
1033                         return -EINVAL;
1034                 }
1035         }
1036
1037         info = intel_fb->base.fbdev;
1038         par = info->par;
1039
1040         crtc_count = 0;
1041         /* okay we need to setup new connector sets in the crtcs */
1042         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
1043                 struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
1044                 modeset = &intel_crtc->mode_set;
1045                 modeset->fb = &intel_fb->base;
1046                 conn_count = 0;
1047                 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
1048                         if (connector->encoder)
1049                                 if(connector->encoder->crtc == modeset->crtc) {
1050                                         modeset->connectors[conn_count] = connector;
1051                                         conn_count++;
1052                                         if (conn_count > INTELFB_CONN_LIMIT)
1053                                                 BUG();
1054                                 }
1055                 }
1056
1057                 for (i = conn_count; i < INTELFB_CONN_LIMIT; i++)
1058                         modeset->connectors[i] = NULL;
1059
1060                 par->crtc_ids[crtc_count++] = crtc->base.id;
1061
1062                 modeset->num_connectors = conn_count;
1063                 if (modeset->mode != modeset->crtc->desired_mode)
1064                         modeset->mode = modeset->crtc->desired_mode;
1065         }
1066         par->crtc_count = crtc_count;
1067
1068         if (new_fb) {
1069                 info->var.pixclock = -1;
1070                 if (register_framebuffer(info) < 0)
1071                         return -EINVAL;
1072         } else
1073                 intelfb_set_par(info);
1074                 
1075         printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
1076                info->fix.id);
1077
1078         /* Switch back to kernel console on panic */
1079         panic_mode = *modeset;
1080         atomic_notifier_chain_register(&panic_notifier_list, &paniced);
1081         printk(KERN_INFO "registered panic notifier\n");
1082
1083         return 0;
1084 }
1085
1086 int intelfb_probe(struct drm_device *dev)
1087 {
1088         int ret;
1089
1090         DRM_DEBUG("\n");
1091
1092         /* something has changed in the lower levels of hell - deal with it 
1093            here */
1094
1095         /* two modes : a) 1 fb to rule all crtcs.
1096                        b) one fb per crtc.
1097            two actions 1) new connected device
1098                        2) device removed.
1099            case a/1 : if the fb surface isn't big enough - resize the surface fb.
1100                       if the fb size isn't big enough - resize fb into surface.
1101                       if everything big enough configure the new crtc/etc.
1102            case a/2 : undo the configuration
1103                       possibly resize down the fb to fit the new configuration.
1104            case b/1 : see if it is on a new crtc - setup a new fb and add it.
1105            case b/2 : teardown the new fb.
1106         */
1107
1108         /* mode a first */
1109         /* search for an fb */
1110         if (i915_fbpercrtc == 1) {
1111                 ret = intelfb_multi_fb_probe(dev);
1112         } else {
1113                 ret = intelfb_single_fb_probe(dev);
1114         }
1115
1116         return ret;
1117 }
1118 EXPORT_SYMBOL(intelfb_probe);
1119
1120 int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
1121 {
1122         struct fb_info *info;
1123         struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
1124
1125         if (!fb)
1126                 return -EINVAL;
1127
1128         info = fb->fbdev;
1129         
1130         if (info) {
1131                 unregister_framebuffer(info);
1132                 iounmap(info->screen_base);
1133                 mutex_lock(&dev->struct_mutex);
1134                 drm_gem_object_unreference(intel_fb->obj);
1135                 mutex_unlock(&dev->struct_mutex);
1136                 framebuffer_release(info);
1137         }
1138
1139         atomic_notifier_chain_unregister(&panic_notifier_list, &paniced);
1140         memset(&panic_mode, 0, sizeof(struct drm_mode_set));
1141         return 0;
1142 }
1143 EXPORT_SYMBOL(intelfb_remove);
1144 MODULE_LICENSE("GPL");