OSDN Git Service

radeon: fix fence race condition hopefully
[android-x86/external-libdrm.git] / linux-core / drm_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_crtc.h"
43
44 struct drmfb_par {
45         struct drm_device *dev;
46         struct drm_crtc *crtc;
47 };
48
49 static int drmfb_setcolreg(unsigned regno, unsigned red, unsigned green,
50                            unsigned blue, unsigned transp,
51                            struct fb_info *info)
52 {
53         struct drmfb_par *par = info->par;
54         struct drm_framebuffer *fb = par->crtc->fb;
55         struct drm_crtc *crtc = par->crtc;
56
57         if (regno > 255)
58                 return 1;
59
60         if (fb->depth == 8) {
61                 return 0;
62         }
63         
64         if (regno < 16) {
65                 switch (fb->depth) {
66                 case 15:
67                         fb->pseudo_palette[regno] = ((red & 0xf800) >>  1) |
68                                 ((green & 0xf800) >>  6) |
69                                 ((blue & 0xf800) >> 11);
70                         break;
71                 case 16:
72                         fb->pseudo_palette[regno] = (red & 0xf800) |
73                                 ((green & 0xfc00) >>  5) |
74                                 ((blue  & 0xf800) >> 11);
75                         break;
76                 case 24:
77                 case 32:
78                         fb->pseudo_palette[regno] = ((red & 0xff00) << 8) |
79                                 (green & 0xff00) |
80                                 ((blue  & 0xff00) >> 8);
81                         break;
82                 }
83         }
84
85         return 0;
86 }
87
88 static int drmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
89 {
90         struct drmfb_par *par = info->par;
91         struct drm_device *dev = par->dev;
92         struct drm_framebuffer *fb = par->crtc->fb;
93         struct drm_display_mode *drm_mode;
94         struct drm_output *output;
95         int depth;
96
97         if (!var->pixclock)
98                 return -EINVAL;
99
100         /* Need to resize the fb object !!! */
101         if (var->xres > fb->width || var->yres > fb->height) {
102                 DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height);
103                 DRM_ERROR("Need resizing code.\n");
104                 return -EINVAL;
105         }
106
107         switch (var->bits_per_pixel) {
108         case 16:
109                 depth = (var->green.length == 6) ? 16 : 15;
110                 break;
111         case 32:
112                 depth = (var->transp.length > 0) ? 32 : 24;
113                 break;
114         default:
115                 depth = var->bits_per_pixel;
116                 break;
117         }
118                 
119         switch (depth) {
120         case 8:
121                 var->red.offset = 0;
122                 var->green.offset = 0;
123                 var->blue.offset = 0;
124                 var->red.length = 8;
125                 var->green.length = 8;
126                 var->blue.length = 8;
127                 var->transp.length = 0;
128                 var->transp.offset = 0;
129                 break;
130         case 15:
131                 var->red.offset = 10;
132                 var->green.offset = 5;
133                 var->blue.offset = 0;
134                 var->red.length = 5;
135                 var->green.length = 5;
136                 var->blue.length = 5;
137                 var->transp.length = 1;
138                 var->transp.offset = 15;
139                 break;
140         case 16:
141                 var->red.offset = 11;
142                 var->green.offset = 6;
143                 var->blue.offset = 0;
144                 var->red.length = 5;
145                 var->green.length = 6;
146                 var->blue.length = 5;
147                 var->transp.length = 0;
148                 var->transp.offset = 0;
149                 break;
150         case 24:
151                 var->red.offset = 16;
152                 var->green.offset = 8;
153                 var->blue.offset = 0;
154                 var->red.length = 8;
155                 var->green.length = 8;
156                 var->blue.length = 8;
157                 var->transp.length = 0;
158                 var->transp.offset = 0;
159                 break;
160         case 32:
161                 var->red.offset = 16;
162                 var->green.offset = 8;
163                 var->blue.offset = 0;
164                 var->red.length = 8;
165                 var->green.length = 8;
166                 var->blue.length = 8;
167                 var->transp.length = 8;
168                 var->transp.offset = 24;
169                 break;
170         default:
171                 return -EINVAL; 
172         }
173
174 #if 0
175         /* Here we walk the output mode list and look for modes. If we haven't
176          * got it, then bail. Not very nice, so this is disabled.
177          * In the set_par code, we create our mode based on the incoming
178          * parameters. Nicer, but may not be desired by some.
179          */
180         list_for_each_entry(output, &dev->mode_config.output_list, head) {
181                 if (output->crtc == par->crtc)
182                         break;
183         }
184     
185         list_for_each_entry(drm_mode, &output->modes, head) {
186                 if (drm_mode->hdisplay == var->xres &&
187                     drm_mode->vdisplay == var->yres &&
188                     drm_mode->clock != 0)
189                     break;
190         }
191
192         if (!drm_mode)
193                 return -EINVAL;
194 #endif
195
196         return 0;
197 }
198
199 /* this will let fbcon do the mode init */
200 static int drmfb_set_par(struct fb_info *info)
201 {
202         struct drmfb_par *par = info->par;
203         struct drm_framebuffer *fb = par->crtc->fb;
204         struct drm_device *dev = par->dev;
205         struct drm_display_mode *drm_mode;
206         struct fb_var_screeninfo *var = &info->var;
207         struct drm_output *output;
208
209         switch (var->bits_per_pixel) {
210         case 16:
211                 fb->depth = (var->green.length == 6) ? 16 : 15;
212                 break;
213         case 32:
214                 fb->depth = (var->transp.length > 0) ? 32 : 24;
215                 break;
216         default:
217                 fb->depth = var->bits_per_pixel;
218                 break;
219         }
220
221         fb->bits_per_pixel = var->bits_per_pixel;
222
223         info->fix.line_length = fb->pitch;
224         info->fix.smem_len = info->fix.line_length * fb->height;
225         info->fix.visual = (fb->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
226
227         info->screen_size = info->fix.smem_len; /* ??? */
228
229         /* Should we walk the output's modelist or just create our own ???
230          * For now, we create and destroy a mode based on the incoming 
231          * parameters. But there's commented out code below which scans 
232          * the output list too.
233          */
234 #if 0
235         list_for_each_entry(output, &dev->mode_config.output_list, head) {
236                 if (output->crtc == par->crtc)
237                         break;
238         }
239     
240         list_for_each_entry(drm_mode, &output->modes, head) {
241                 if (drm_mode->hdisplay == var->xres &&
242                     drm_mode->vdisplay == var->yres &&
243                     drm_mode->clock != 0)
244                     break;
245         }
246 #else
247         drm_mode = drm_mode_create(dev);
248         drm_mode->hdisplay = var->xres;
249         drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin;
250         drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len;
251         drm_mode->htotal = drm_mode->hsync_end + var->left_margin;
252         drm_mode->vdisplay = var->yres;
253         drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin;
254         drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len;
255         drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin;
256         drm_mode->clock = PICOS2KHZ(var->pixclock);
257         drm_mode->vrefresh = drm_mode_vrefresh(drm_mode);
258         drm_mode_set_name(drm_mode);
259         drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V);
260 #endif
261
262         if (!drm_crtc_set_mode(par->crtc, drm_mode, 0, 0))
263                 return -EINVAL;
264
265         /* Have to destroy our created mode if we're not searching the mode
266          * list for it.
267          */
268 #if 1 
269         drm_mode_destroy(dev, drm_mode);
270 #endif
271
272         return 0;
273 }
274
275 static struct fb_ops drmfb_ops = {
276         .owner = THIS_MODULE,
277         //      .fb_open = drmfb_open,
278         //      .fb_read = drmfb_read,
279         //      .fb_write = drmfb_write,
280         //      .fb_release = drmfb_release,
281         //      .fb_ioctl = drmfb_ioctl,
282         .fb_check_var = drmfb_check_var,
283         .fb_set_par = drmfb_set_par,
284         .fb_setcolreg = drmfb_setcolreg,
285         .fb_fillrect = cfb_fillrect,
286         .fb_copyarea = cfb_copyarea,
287         .fb_imageblit = cfb_imageblit,
288 };
289
290 int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc)
291 {
292         struct fb_info *info;
293         struct drm_framebuffer *fb = crtc->fb;
294         struct drmfb_par *par;
295         struct device *device = &dev->pdev->dev; 
296         struct drm_display_mode *mode = crtc->desired_mode;
297         int ret;
298
299         info = framebuffer_alloc(sizeof(struct drmfb_par), device);
300         if (!info)
301                 return -ENOMEM;
302
303         fb->fbdev = info;
304                 
305         par = info->par;
306
307         par->dev = dev;
308         par->crtc = crtc;
309
310         info->fbops = &drmfb_ops;
311
312         strcpy(info->fix.id, "drmfb");
313         info->fix.type = FB_TYPE_PACKED_PIXELS;
314         info->fix.visual = FB_VISUAL_TRUECOLOR;
315         info->fix.accel = FB_ACCEL_NONE;
316         info->fix.type_aux = 0;
317         info->fix.mmio_start = 0;
318         info->fix.mmio_len = 0;
319         info->fix.line_length = fb->pitch;
320         info->fix.smem_start = fb->offset + dev->mode_config.fb_base;
321         info->fix.smem_len = info->fix.line_length * fb->height;
322
323         info->flags = FBINFO_DEFAULT;
324
325         ret = drm_mem_reg_ioremap(dev, &fb->bo->mem, &fb->virtual_base);
326         if (ret)
327                 DRM_ERROR("error mapping fb: %d\n", ret);
328
329         info->screen_base = fb->virtual_base;
330         info->screen_size = info->fix.smem_len; /* ??? */
331         info->pseudo_palette = fb->pseudo_palette;
332         info->var.xres_virtual = fb->width;
333         info->var.yres_virtual = fb->height;
334         info->var.bits_per_pixel = fb->bits_per_pixel;
335         info->var.xoffset = 0;
336         info->var.yoffset = 0;
337         info->var.activate = FB_ACTIVATE_NOW;
338         info->var.height = -1;
339         info->var.width = -1;
340         info->var.vmode = FB_VMODE_NONINTERLACED;
341
342         info->var.xres = mode->hdisplay;
343         info->var.right_margin = mode->hsync_start - mode->hdisplay;
344         info->var.hsync_len = mode->hsync_end - mode->hsync_start;
345         info->var.left_margin = mode->htotal - mode->hsync_end;
346         info->var.yres = mode->vdisplay;
347         info->var.lower_margin = mode->vsync_start - mode->vdisplay;
348         info->var.vsync_len = mode->vsync_end - mode->vsync_start;
349         info->var.upper_margin = mode->vtotal - mode->vsync_end;
350         info->var.pixclock = 10000000 / mode->htotal * 1000 /
351                                 mode->vtotal * 100;
352         /* avoid overflow */
353         info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;
354
355         DRM_DEBUG("fb depth is %d\n", fb->depth);
356         switch(fb->depth) {
357         case 8:
358                 info->var.red.offset = 0;
359                 info->var.green.offset = 0;
360                 info->var.blue.offset = 0;
361                 info->var.red.length = 8; /* 8bit DAC */
362                 info->var.green.length = 8;
363                 info->var.blue.length = 8;
364                 info->var.transp.offset = 0;
365                 info->var.transp.length = 0;
366                 break;
367         case 15:
368                 info->var.red.offset = 10;
369                 info->var.green.offset = 5;
370                 info->var.blue.offset = 0;
371                 info->var.red.length = info->var.green.length =
372                         info->var.blue.length = 5;
373                 info->var.transp.offset = 15;
374                 info->var.transp.length = 1;
375                 break;
376         case 16:
377                 info->var.red.offset = 11;
378                 info->var.green.offset = 5;
379                 info->var.blue.offset = 0;
380                 info->var.red.length = 5;
381                 info->var.green.length = 6;
382                 info->var.blue.length = 5;
383                 info->var.transp.offset = 0;
384                 info->var.transp.length = 0;
385                 break;
386         case 24:
387                 info->var.red.offset = 16;
388                 info->var.green.offset = 8;
389                 info->var.blue.offset = 0;
390                 info->var.red.length = info->var.green.length =
391                         info->var.blue.length = 8;
392                 info->var.transp.offset = 0;
393                 info->var.transp.length = 0;
394                 break;
395         case 32:
396                 info->var.red.offset = 16;
397                 info->var.green.offset = 8;
398                 info->var.blue.offset = 0;
399                 info->var.red.length = info->var.green.length =
400                         info->var.blue.length = 8;
401                 info->var.transp.offset = 24;
402                 info->var.transp.length = 8;
403                 break;
404         default:
405                 break;
406         }
407
408         if (register_framebuffer(info) < 0) {
409                 unregister_framebuffer(info);
410                 return -EINVAL;
411         }
412
413         printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
414                info->fix.id);
415         return 0;
416 }
417 EXPORT_SYMBOL(drmfb_probe);
418
419 int drmfb_remove(struct drm_device *dev, struct drm_crtc *crtc)
420 {
421         struct fb_info *info = fb->fbdev;
422         struct drm_framebuffer *fb = crtc->fb;
423         
424         if (info) {
425                 drm_mem_reg_iounmap(dev, &fb->bo->mem, fb->virtual_base);
426                 unregister_framebuffer(info);
427                 framebuffer_release(info);
428         }
429         return 0;
430 }
431 EXPORT_SYMBOL(drmfb_remove);
432 MODULE_LICENSE("GPL");