OSDN Git Service

Merge branch 'master' into modesetting-101 - TTM & typedef removal
[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 "i915_drm.h"
45 #include "i915_drv.h"
46
47 struct intelfb_par {
48         struct drm_device *dev;
49         struct drm_crtc *crtc;
50 };
51
52 static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green,
53                            unsigned blue, unsigned transp,
54                            struct fb_info *info)
55 {
56         struct intelfb_par *par = info->par;
57         struct drm_framebuffer *fb = par->crtc->fb;
58         struct drm_crtc *crtc = par->crtc;
59
60         if (regno > 255)
61                 return 1;
62
63         if (fb->depth == 8) {
64                 if (crtc->funcs->gamma_set)
65                         crtc->funcs->gamma_set(crtc, red, green, blue, regno);
66                 return 0;
67         }
68
69         if (regno < 16) {
70                 switch (fb->depth) {
71                 case 15:
72                         fb->pseudo_palette[regno] = ((red & 0xf800) >>  1) |
73                                 ((green & 0xf800) >>  6) |
74                                 ((blue & 0xf800) >> 11);
75                         break;
76                 case 16:
77                         fb->pseudo_palette[regno] = (red & 0xf800) |
78                                 ((green & 0xfc00) >>  5) |
79                                 ((blue  & 0xf800) >> 11);
80                         break;
81                 case 24:
82                 case 32:
83                         fb->pseudo_palette[regno] = ((red & 0xff00) << 8) |
84                                 (green & 0xff00) |
85                                 ((blue  & 0xff00) >> 8);
86                         break;
87                 }
88         }
89
90         return 0;
91 }
92
93 static int intelfb_check_var(struct fb_var_screeninfo *var,
94                              struct fb_info *info)
95 {
96         struct intelfb_par *par = info->par;
97         struct drm_device *dev = par->dev;
98         struct drm_framebuffer *fb = par->crtc->fb;
99         struct drm_display_mode *drm_mode;
100         struct drm_output *output;
101         int depth;
102
103         if (!var->pixclock)
104                 return -EINVAL;
105
106         /* Need to resize the fb object !!! */
107         if (var->xres > fb->width || var->yres > fb->height) {
108                 DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height);
109                 DRM_ERROR("Need resizing code.\n");
110                 return -EINVAL;
111         }
112
113         switch (var->bits_per_pixel) {
114         case 16:
115                 depth = (var->green.length == 6) ? 16 : 15;
116                 break;
117         case 32:
118                 depth = (var->transp.length > 0) ? 32 : 24;
119                 break;
120         default:
121                 depth = var->bits_per_pixel;
122                 break;
123         }
124                 
125         switch (depth) {
126         case 8:
127                 var->red.offset = 0;
128                 var->green.offset = 0;
129                 var->blue.offset = 0;
130                 var->red.length = 8;
131                 var->green.length = 8;
132                 var->blue.length = 8;
133                 var->transp.length = 0;
134                 var->transp.offset = 0;
135                 break;
136         case 15:
137                 var->red.offset = 10;
138                 var->green.offset = 5;
139                 var->blue.offset = 0;
140                 var->red.length = 5;
141                 var->green.length = 5;
142                 var->blue.length = 5;
143                 var->transp.length = 1;
144                 var->transp.offset = 15;
145                 break;
146         case 16:
147                 var->red.offset = 11;
148                 var->green.offset = 6;
149                 var->blue.offset = 0;
150                 var->red.length = 5;
151                 var->green.length = 6;
152                 var->blue.length = 5;
153                 var->transp.length = 0;
154                 var->transp.offset = 0;
155                 break;
156         case 24:
157                 var->red.offset = 16;
158                 var->green.offset = 8;
159                 var->blue.offset = 0;
160                 var->red.length = 8;
161                 var->green.length = 8;
162                 var->blue.length = 8;
163                 var->transp.length = 0;
164                 var->transp.offset = 0;
165                 break;
166         case 32:
167                 var->red.offset = 16;
168                 var->green.offset = 8;
169                 var->blue.offset = 0;
170                 var->red.length = 8;
171                 var->green.length = 8;
172                 var->blue.length = 8;
173                 var->transp.length = 8;
174                 var->transp.offset = 24;
175                 break;
176         default:
177                 return -EINVAL; 
178         }
179
180 #if 0
181         /* Here we walk the output mode list and look for modes. If we haven't
182          * got it, then bail. Not very nice, so this is disabled.
183          * In the set_par code, we create our mode based on the incoming
184          * parameters. Nicer, but may not be desired by some.
185          */
186         list_for_each_entry(output, &dev->mode_config.output_list, head) {
187                 if (output->crtc == par->crtc)
188                         break;
189         }
190     
191         list_for_each_entry(drm_mode, &output->modes, head) {
192                 if (drm_mode->hdisplay == var->xres &&
193                     drm_mode->vdisplay == var->yres &&
194                     drm_mode->clock != 0)
195                         break;
196         }
197  
198         if (!drm_mode)
199                 return -EINVAL;
200 #endif
201
202         return 0;
203 }
204
205 /* this will let fbcon do the mode init */
206 /* FIXME: take mode config lock? */
207 static int intelfb_set_par(struct fb_info *info)
208 {
209         struct intelfb_par *par = info->par;
210         struct drm_framebuffer *fb = par->crtc->fb;
211         struct drm_device *dev = par->dev;
212         struct drm_display_mode *drm_mode;
213         struct fb_var_screeninfo *var = &info->var;
214
215         switch (var->bits_per_pixel) {
216         case 16:
217                 fb->depth = (var->green.length == 6) ? 16 : 15;
218                 break;
219         case 32:
220                 fb->depth = (var->transp.length > 0) ? 32 : 24;
221                 break;
222         default:
223                 fb->depth = var->bits_per_pixel;
224                 break;
225         }
226
227         fb->bits_per_pixel = var->bits_per_pixel;
228
229         info->fix.line_length = fb->pitch;
230         info->fix.smem_len = info->fix.line_length * fb->height;
231         info->fix.visual = (fb->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
232
233         info->screen_size = info->fix.smem_len; /* ??? */
234
235         /* Should we walk the output's modelist or just create our own ???
236          * For now, we create and destroy a mode based on the incoming 
237          * parameters. But there's commented out code below which scans 
238          * the output list too.
239          */
240 #if 0
241         list_for_each_entry(output, &dev->mode_config.output_list, head) {
242                 if (output->crtc == par->crtc)
243                         break;
244         }
245     
246         list_for_each_entry(drm_mode, &output->modes, head) {
247                 if (drm_mode->hdisplay == var->xres &&
248                     drm_mode->vdisplay == var->yres &&
249                     drm_mode->clock != 0)
250                         break;
251         }
252 #else
253         drm_mode = drm_mode_create(dev);
254         drm_mode->hdisplay = var->xres;
255         drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin;
256         drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len;
257         drm_mode->htotal = drm_mode->hsync_end + var->left_margin;
258         drm_mode->vdisplay = var->yres;
259         drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin;
260         drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len;
261         drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin;
262         drm_mode->clock = PICOS2KHZ(var->pixclock);
263         drm_mode->vrefresh = drm_mode_vrefresh(drm_mode);
264         drm_mode_set_name(drm_mode);
265         drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V);
266 #endif
267
268         if (!drm_crtc_set_mode(par->crtc, drm_mode, 0, 0))
269                 return -EINVAL;
270
271         /* Have to destroy our created mode if we're not searching the mode
272          * list for it.
273          */
274 #if 1 
275         drm_mode_destroy(dev, drm_mode);
276 #endif
277
278         return 0;
279 }
280
281 #if 0
282 static void intelfb_copyarea(struct fb_info *info,
283                              const struct fb_copyarea *region)
284 {
285         struct intelfb_par *par = info->par;
286         struct drm_device *dev = par->dev;
287         struct drm_i915_private *dev_priv = dev->dev_private;
288         u32 src_x1, src_y1, dst_x1, dst_y1, dst_x2, dst_y2, offset;
289         u32 cmd, rop_depth_pitch, src_pitch;
290         RING_LOCALS;
291
292         cmd = XY_SRC_COPY_BLT_CMD;
293         src_x1 = region->sx;
294         src_y1 = region->sy;
295         dst_x1 = region->dx;
296         dst_y1 = region->dy;
297         dst_x2 = region->dx + region->width;
298         dst_y2 = region->dy + region->height;
299         offset = par->fb->offset;
300         rop_depth_pitch = BLT_ROP_GXCOPY | par->fb->pitch;
301         src_pitch = par->fb->pitch;
302
303         switch (par->fb->bits_per_pixel) {
304         case 16:
305                 rop_depth_pitch |= BLT_DEPTH_16_565;
306                 break;
307         case 32:
308                 rop_depth_pitch |= BLT_DEPTH_32;
309                 cmd |= XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB;
310                 break;
311         }
312
313         BEGIN_LP_RING(8);
314         OUT_RING(cmd);
315         OUT_RING(rop_depth_pitch);
316         OUT_RING((dst_y1 << 16) | (dst_x1 & 0xffff));
317         OUT_RING((dst_y2 << 16) | (dst_x2 & 0xffff));
318         OUT_RING(offset);
319         OUT_RING((src_y1 << 16) | (src_x1 & 0xffff));
320         OUT_RING(src_pitch);
321         OUT_RING(offset);
322         ADVANCE_LP_RING();
323 }
324
325 #define ROUND_UP_TO(x, y)       (((x) + (y) - 1) / (y) * (y))
326 #define ROUND_DOWN_TO(x, y)     ((x) / (y) * (y))
327
328 void intelfb_imageblit(struct fb_info *info, const struct fb_image *image)
329 {
330         struct intelfb_par *par = info->par;
331         struct drm_device *dev = par->dev;
332         struct drm_i915_private *dev_priv = dev->dev_private;
333         u32 cmd, rop_pitch_depth, tmp;
334         int nbytes, ndwords, pad;
335         u32 dst_x1, dst_y1, dst_x2, dst_y2, offset, bg, fg;
336         int dat, ix, iy, iw;
337         int i, j;
338         RING_LOCALS;
339
340         /* size in bytes of a padded scanline */
341         nbytes = ROUND_UP_TO(image->width, 16) / 8;
342
343         /* Total bytes of padded scanline data to write out. */
344         nbytes *= image->height;
345
346         /*
347          * Check if the glyph data exceeds the immediate mode limit.
348          * It would take a large font (1K pixels) to hit this limit.
349          */
350         if (nbytes > 128 || image->depth != 1)
351                 return cfb_imageblit(info, image);
352
353         /* Src data is packaged a dword (32-bit) at a time. */
354         ndwords = ROUND_UP_TO(nbytes, 4) / 4;
355
356         /*
357          * Ring has to be padded to a quad word. But because the command starts
358            with 7 bytes, pad only if there is an even number of ndwords
359          */
360         pad = !(ndwords % 2);
361
362         DRM_DEBUG("imageblit %dx%dx%d to (%d,%d)\n", image->width,
363                   image->height, image->depth, image->dx, image->dy);
364         DRM_DEBUG("nbytes: %d, ndwords: %d, pad: %d\n", nbytes, ndwords, pad);
365
366         tmp = (XY_MONO_SRC_COPY_IMM_BLT & 0xff) + ndwords;
367         cmd = (XY_MONO_SRC_COPY_IMM_BLT & ~0xff) | tmp;
368         offset = par->fb->offset;
369         dst_x1 = image->dx;
370         dst_y1 = image->dy;
371         dst_x2 = image->dx + image->width;
372         dst_y2 = image->dy + image->height;
373         rop_pitch_depth = BLT_ROP_GXCOPY | par->fb->pitch;
374
375         switch (par->fb->bits_per_pixel) {
376         case 8:
377                 rop_pitch_depth |= BLT_DEPTH_8;
378                 fg = image->fg_color;
379                 bg = image->bg_color;
380                 break;
381         case 16:
382                 rop_pitch_depth |= BLT_DEPTH_16_565;
383                 fg = par->fb->pseudo_palette[image->fg_color];
384                 bg = par->fb->pseudo_palette[image->bg_color];
385                 break;
386         case 32:
387                 rop_pitch_depth |= BLT_DEPTH_32;
388                 cmd |= XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_RGB;
389                 fg = par->fb->pseudo_palette[image->fg_color];
390                 bg = par->fb->pseudo_palette[image->bg_color];
391                 break;
392         default:
393                 DRM_ERROR("unknown depth %d\n", par->fb->bits_per_pixel);
394                 break;
395         }
396         
397         BEGIN_LP_RING(8 + ndwords);
398         OUT_RING(cmd);
399         OUT_RING(rop_pitch_depth);
400         OUT_RING((dst_y1 << 16) | (dst_x1 & 0xffff));
401         OUT_RING((dst_y2 << 16) | (dst_x2 & 0xffff));
402         OUT_RING(offset);
403         OUT_RING(bg);
404         OUT_RING(fg);
405         ix = iy = 0;
406         iw = ROUND_UP_TO(image->width, 8) / 8;
407         while (ndwords--) {
408                 dat = 0;
409                 for (j = 0; j < 2; ++j) {
410                         for (i = 0; i < 2; ++i) {
411                                 if (ix != iw || i == 0)
412                                         dat |= image->data[iy*iw + ix++] << (i+j*2)*8;
413                         }
414                         if (ix == iw && iy != (image->height - 1)) {
415                                 ix = 0;
416                                 ++iy;
417                         }
418                 }
419                 OUT_RING(dat);
420         }
421         if (pad)
422                 OUT_RING(MI_NOOP);
423         ADVANCE_LP_RING();
424 }
425 #endif
426
427 static struct fb_ops intelfb_ops = {
428         .owner = THIS_MODULE,
429         //      .fb_open = intelfb_open,
430         //      .fb_read = intelfb_read,
431         //      .fb_write = intelfb_write,
432         //      .fb_release = intelfb_release,
433         //      .fb_ioctl = intelfb_ioctl,
434         .fb_check_var = intelfb_check_var,
435         .fb_set_par = intelfb_set_par,
436         .fb_setcolreg = intelfb_setcolreg,
437         .fb_fillrect = cfb_fillrect,
438         .fb_copyarea = cfb_copyarea, //intelfb_copyarea,
439         .fb_imageblit = cfb_imageblit, //intelfb_imageblit,
440 };
441
442 int intelfb_probe(struct drm_device *dev, struct drm_crtc *crtc)
443 {
444         struct fb_info *info;
445         struct intelfb_par *par;
446         struct device *device = &dev->pdev->dev; 
447         struct drm_framebuffer *fb;
448         struct drm_display_mode *mode = crtc->desired_mode;
449         struct drm_buffer_object *fbo = NULL;
450         int ret;
451
452         info = framebuffer_alloc(sizeof(struct intelfb_par), device);
453         if (!info){
454                 return -EINVAL;
455         }
456
457         fb = drm_framebuffer_create(dev);
458         if (!fb) {
459                 framebuffer_release(info);
460                 DRM_ERROR("failed to allocate fb.\n");
461                 return -EINVAL;
462         }
463         crtc->fb = fb;
464
465         fb->width = crtc->desired_mode->hdisplay;
466         fb->height = crtc->desired_mode->vdisplay;
467
468         fb->bits_per_pixel = 32;
469         fb->pitch = fb->width * ((fb->bits_per_pixel + 1) / 8);
470         fb->depth = 24;
471         ret = drm_buffer_object_create(dev, fb->width * fb->height * 4, 
472                                        drm_bo_type_kernel,
473                                        DRM_BO_FLAG_READ |
474                                        DRM_BO_FLAG_WRITE |
475                                        DRM_BO_FLAG_MEM_VRAM,
476                                        0, 0, 0,
477                                        &fbo);
478         if (ret || !fbo) {
479                 printk(KERN_ERR "failed to allocate framebuffer\n");
480                 drm_framebuffer_destroy(fb);
481                 framebuffer_release(info);
482                 return -EINVAL;
483         }
484
485         ret = drm_bo_set_pin(dev, fbo, 1);
486         if (ret) {
487                 printk(KERN_ERR "failed to pin framebuffer, aborting\n");
488                 drm_framebuffer_destroy(fb);
489                 framebuffer_release(info);
490                 return -EINVAL;
491         }
492
493         fb->offset = fbo->offset;
494         fb->bo = fbo;
495         printk("allocated %dx%d fb: 0x%08lx, bo %p\n", fb->width,
496                        fb->height, fbo->offset, fbo);
497
498
499         fb->fbdev = info;
500                 
501         par = info->par;
502
503         par->dev = dev;
504         par->crtc = crtc;
505
506         info->fbops = &intelfb_ops;
507
508         strcpy(info->fix.id, "intelfb");
509         info->fix.type = FB_TYPE_PACKED_PIXELS;
510         info->fix.visual = FB_VISUAL_TRUECOLOR;
511         info->fix.type_aux = 0;
512         info->fix.xpanstep = 8;
513         info->fix.ypanstep = 1;
514         info->fix.ywrapstep = 0;
515         info->fix.accel = FB_ACCEL_I830;
516         info->fix.type_aux = 0;
517         info->fix.mmio_start = 0;
518         info->fix.mmio_len = 0;
519         info->fix.line_length = fb->pitch;
520         info->fix.smem_start = fb->offset + dev->mode_config.fb_base;
521         info->fix.smem_len = info->fix.line_length * fb->height;
522
523         info->flags = FBINFO_DEFAULT;
524
525         ret = drm_mem_reg_ioremap(dev, &fb->bo->mem, &fb->virtual_base);
526         if (ret)
527                 DRM_ERROR("error mapping fb: %d\n", ret);
528
529         info->screen_base = fb->virtual_base;
530         info->screen_size = info->fix.smem_len; /* FIXME */
531         info->pseudo_palette = fb->pseudo_palette;
532         info->var.xres_virtual = fb->width;
533         info->var.yres_virtual = fb->height;
534         info->var.bits_per_pixel = fb->bits_per_pixel;
535         info->var.xoffset = 0;
536         info->var.yoffset = 0;
537         info->var.activate = FB_ACTIVATE_NOW;
538         info->var.height = -1;
539         info->var.width = -1;
540         info->var.vmode = FB_VMODE_NONINTERLACED;
541
542         info->var.xres = mode->hdisplay;
543         info->var.right_margin = mode->hsync_start - mode->hdisplay;
544         info->var.hsync_len = mode->hsync_end - mode->hsync_start;
545         info->var.left_margin = mode->htotal - mode->hsync_end;
546         info->var.yres = mode->vdisplay;
547         info->var.lower_margin = mode->vsync_start - mode->vdisplay;
548         info->var.vsync_len = mode->vsync_end - mode->vsync_start;
549         info->var.upper_margin = mode->vtotal - mode->vsync_end;
550         info->var.pixclock = 10000000 / mode->htotal * 1000 /
551                 mode->vtotal * 100;
552         /* avoid overflow */
553         info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;
554
555         info->pixmap.size = 64*1024;
556         info->pixmap.buf_align = 8;
557         info->pixmap.access_align = 32;
558         info->pixmap.flags = FB_PIXMAP_SYSTEM;
559         info->pixmap.scan_align = 1;
560
561         DRM_DEBUG("fb depth is %d\n", fb->depth);
562         DRM_DEBUG("   pitch is %d\n", fb->pitch);
563         switch(fb->depth) {
564         case 8:
565                 info->var.red.offset = 0;
566                 info->var.green.offset = 0;
567                 info->var.blue.offset = 0;
568                 info->var.red.length = 8; /* 8bit DAC */
569                 info->var.green.length = 8;
570                 info->var.blue.length = 8;
571                 info->var.transp.offset = 0;
572                 info->var.transp.length = 0;
573                 break;
574         case 15:
575                 info->var.red.offset = 10;
576                 info->var.green.offset = 5;
577                 info->var.blue.offset = 0;
578                 info->var.red.length = info->var.green.length =
579                         info->var.blue.length = 5;
580                 info->var.transp.offset = 15;
581                 info->var.transp.length = 1;
582                 break;
583         case 16:
584                 info->var.red.offset = 11;
585                 info->var.green.offset = 5;
586                 info->var.blue.offset = 0;
587                 info->var.red.length = 5;
588                 info->var.green.length = 6;
589                 info->var.blue.length = 5;
590                 info->var.transp.offset = 0;
591                 break;
592         case 24:
593                 info->var.red.offset = 16;
594                 info->var.green.offset = 8;
595                 info->var.blue.offset = 0;
596                 info->var.red.length = info->var.green.length =
597                         info->var.blue.length = 8;
598                 info->var.transp.offset = 0;
599                 info->var.transp.length = 0;
600                 break;
601         case 32:
602                 info->var.red.offset = 16;
603                 info->var.green.offset = 8;
604                 info->var.blue.offset = 0;
605                 info->var.red.length = info->var.green.length =
606                         info->var.blue.length = 8;
607                 info->var.transp.offset = 24;
608                 info->var.transp.length = 8;
609                 break;
610         default:
611                 break;
612         }
613
614         if (register_framebuffer(info) < 0)
615                 return -EINVAL;
616
617         printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
618                info->fix.id);
619         return 0;
620 }
621 EXPORT_SYMBOL(intelfb_probe);
622
623 int intelfb_remove(struct drm_device *dev, struct drm_crtc *crtc)
624 {
625         struct drm_framebuffer *fb = crtc->fb;
626         struct fb_info *info = fb->fbdev;
627         
628         if (info) {
629                 unregister_framebuffer(info);
630                 framebuffer_release(info);
631                 drm_mem_reg_iounmap(dev, &fb->bo->mem, fb->virtual_base);
632         }
633         return 0;
634 }
635 EXPORT_SYMBOL(intelfb_remove);
636 MODULE_LICENSE("GPL");