]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/video/pm3fb.c
pm3fb: checkpatch fixes
[linux-2.6-omap-h63xx.git] / drivers / video / pm3fb.c
1 /*
2  *  linux/drivers/video/pm3fb.c -- 3DLabs Permedia3 frame buffer device
3  *
4  *  Copyright (C) 2001 Romain Dolbeau <romain@dolbeau.org>.
5  *
6  *  Ported to 2.6 kernel on 1 May 2007 by Krzysztof Helt <krzysztof.h1@wp.pl>
7  *      based on pm2fb.c
8  *
9  *  Based on code written by:
10  *         Sven Luther, <luther@dpt-info.u-strasbg.fr>
11  *         Alan Hourihane, <alanh@fairlite.demon.co.uk>
12  *         Russell King, <rmk@arm.linux.org.uk>
13  *  Based on linux/drivers/video/skeletonfb.c:
14  *      Copyright (C) 1997 Geert Uytterhoeven
15  *  Based on linux/driver/video/pm2fb.c:
16  *      Copyright (C) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT)
17  *      Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com)
18  *
19  *  This file is subject to the terms and conditions of the GNU General Public
20  *  License. See the file COPYING in the main directory of this archive for
21  *  more details.
22  *
23  */
24
25 #include <linux/module.h>
26 #include <linux/kernel.h>
27 #include <linux/errno.h>
28 #include <linux/string.h>
29 #include <linux/mm.h>
30 #include <linux/slab.h>
31 #include <linux/delay.h>
32 #include <linux/fb.h>
33 #include <linux/init.h>
34 #include <linux/pci.h>
35 #ifdef CONFIG_MTRR
36 #include <asm/mtrr.h>
37 #endif
38
39 #include <video/pm3fb.h>
40
41 #if !defined(CONFIG_PCI)
42 #error "Only generic PCI cards supported."
43 #endif
44
45 #undef PM3FB_MASTER_DEBUG
46 #ifdef PM3FB_MASTER_DEBUG
47 #define DPRINTK(a, b...)        \
48         printk(KERN_DEBUG "pm3fb: %s: " a, __FUNCTION__ , ## b)
49 #else
50 #define DPRINTK(a, b...)
51 #endif
52
53 #define PM3_PIXMAP_SIZE (2048 * 4)
54
55 /*
56  * Driver data
57  */
58 static char *mode_option __devinitdata;
59 static int noaccel __devinitdata;
60
61 /* mtrr option */
62 #ifdef CONFIG_MTRR
63 static int nomtrr __devinitdata;
64 #endif
65
66 /*
67  * This structure defines the hardware state of the graphics card. Normally
68  * you place this in a header file in linux/include/video. This file usually
69  * also includes register information. That allows other driver subsystems
70  * and userland applications the ability to use the same header file to
71  * avoid duplicate work and easy porting of software.
72  */
73 struct pm3_par {
74         unsigned char   __iomem *v_regs;/* virtual address of p_regs */
75         u32             video;          /* video flags before blanking */
76         u32             base;           /* screen base in 128 bits unit */
77         u32             palette[16];
78         int             mtrr_handle;
79 };
80
81 /*
82  * Here we define the default structs fb_fix_screeninfo and fb_var_screeninfo
83  * if we don't use modedb. If we do use modedb see pm3fb_init how to use it
84  * to get a fb_var_screeninfo. Otherwise define a default var as well.
85  */
86 static struct fb_fix_screeninfo pm3fb_fix __devinitdata = {
87         .id =           "Permedia3",
88         .type =         FB_TYPE_PACKED_PIXELS,
89         .visual =       FB_VISUAL_PSEUDOCOLOR,
90         .xpanstep =     1,
91         .ypanstep =     1,
92         .ywrapstep =    0,
93         .accel =        FB_ACCEL_3DLABS_PERMEDIA3,
94 };
95
96 /*
97  * Utility functions
98  */
99
100 static inline u32 PM3_READ_REG(struct pm3_par *par, s32 off)
101 {
102         return fb_readl(par->v_regs + off);
103 }
104
105 static inline void PM3_WRITE_REG(struct pm3_par *par, s32 off, u32 v)
106 {
107         fb_writel(v, par->v_regs + off);
108 }
109
110 static inline void PM3_WAIT(struct pm3_par *par, u32 n)
111 {
112         while (PM3_READ_REG(par, PM3InFIFOSpace) < n);
113 }
114
115 static inline void PM3_WRITE_DAC_REG(struct pm3_par *par, unsigned r, u8 v)
116 {
117         PM3_WAIT(par, 3);
118         PM3_WRITE_REG(par, PM3RD_IndexHigh, (r >> 8) & 0xff);
119         PM3_WRITE_REG(par, PM3RD_IndexLow, r & 0xff);
120         wmb();
121         PM3_WRITE_REG(par, PM3RD_IndexedData, v);
122         wmb();
123 }
124
125 static inline void pm3fb_set_color(struct pm3_par *par, unsigned char regno,
126                         unsigned char r, unsigned char g, unsigned char b)
127 {
128         PM3_WAIT(par, 4);
129         PM3_WRITE_REG(par, PM3RD_PaletteWriteAddress, regno);
130         wmb();
131         PM3_WRITE_REG(par, PM3RD_PaletteData, r);
132         wmb();
133         PM3_WRITE_REG(par, PM3RD_PaletteData, g);
134         wmb();
135         PM3_WRITE_REG(par, PM3RD_PaletteData, b);
136         wmb();
137 }
138
139 static void pm3fb_clear_colormap(struct pm3_par *par,
140                         unsigned char r, unsigned char g, unsigned char b)
141 {
142         int i;
143
144         for (i = 0; i < 256 ; i++)
145                 pm3fb_set_color(par, i, r, g, b);
146
147 }
148
149 /* Calculating various clock parameters */
150 static void pm3fb_calculate_clock(unsigned long reqclock,
151                                 unsigned char *prescale,
152                                 unsigned char *feedback,
153                                 unsigned char *postscale)
154 {
155         int f, pre, post;
156         unsigned long freq;
157         long freqerr = 1000;
158         long currerr;
159
160         for (f = 1; f < 256; f++) {
161                 for (pre = 1; pre < 256; pre++) {
162                         for (post = 0; post < 5; post++) {
163                                 freq = ((2*PM3_REF_CLOCK * f) >> post) / pre;
164                                 currerr = (reqclock > freq)
165                                         ? reqclock - freq
166                                         : freq - reqclock;
167                                 if (currerr < freqerr) {
168                                         freqerr = currerr;
169                                         *feedback = f;
170                                         *prescale = pre;
171                                         *postscale = post;
172                                 }
173                         }
174                 }
175         }
176 }
177
178 static inline int pm3fb_depth(const struct fb_var_screeninfo *var)
179 {
180         if (var->bits_per_pixel == 16)
181                 return var->red.length + var->green.length
182                         + var->blue.length;
183
184         return var->bits_per_pixel;
185 }
186
187 static inline int pm3fb_shift_bpp(unsigned bpp, int v)
188 {
189         switch (bpp) {
190         case 8:
191                 return (v >> 4);
192         case 16:
193                 return (v >> 3);
194         case 32:
195                 return (v >> 2);
196         }
197         DPRINTK("Unsupported depth %u\n", bpp);
198         return 0;
199 }
200
201 /* acceleration */
202 static int pm3fb_sync(struct fb_info *info)
203 {
204         struct pm3_par *par = info->par;
205
206         PM3_WAIT(par, 2);
207         PM3_WRITE_REG(par, PM3FilterMode, PM3FilterModeSync);
208         PM3_WRITE_REG(par, PM3Sync, 0);
209         mb();
210         do {
211                 while ((PM3_READ_REG(par, PM3OutFIFOWords)) == 0);
212                 rmb();
213         } while ((PM3_READ_REG(par, PM3OutputFifo)) != PM3Sync_Tag);
214
215         return 0;
216 }
217
218 static void pm3fb_init_engine(struct fb_info *info)
219 {
220         struct pm3_par *par = info->par;
221         const u32 width = (info->var.xres_virtual + 7) & ~7;
222
223         PM3_WAIT(par, 50);
224         PM3_WRITE_REG(par, PM3FilterMode, PM3FilterModeSync);
225         PM3_WRITE_REG(par, PM3StatisticMode, 0x0);
226         PM3_WRITE_REG(par, PM3DeltaMode, 0x0);
227         PM3_WRITE_REG(par, PM3RasterizerMode, 0x0);
228         PM3_WRITE_REG(par, PM3ScissorMode, 0x0);
229         PM3_WRITE_REG(par, PM3LineStippleMode, 0x0);
230         PM3_WRITE_REG(par, PM3AreaStippleMode, 0x0);
231         PM3_WRITE_REG(par, PM3GIDMode, 0x0);
232         PM3_WRITE_REG(par, PM3DepthMode, 0x0);
233         PM3_WRITE_REG(par, PM3StencilMode, 0x0);
234         PM3_WRITE_REG(par, PM3StencilData, 0x0);
235         PM3_WRITE_REG(par, PM3ColorDDAMode, 0x0);
236         PM3_WRITE_REG(par, PM3TextureCoordMode, 0x0);
237         PM3_WRITE_REG(par, PM3TextureIndexMode0, 0x0);
238         PM3_WRITE_REG(par, PM3TextureIndexMode1, 0x0);
239         PM3_WRITE_REG(par, PM3TextureReadMode, 0x0);
240         PM3_WRITE_REG(par, PM3LUTMode, 0x0);
241         PM3_WRITE_REG(par, PM3TextureFilterMode, 0x0);
242         PM3_WRITE_REG(par, PM3TextureCompositeMode, 0x0);
243         PM3_WRITE_REG(par, PM3TextureApplicationMode, 0x0);
244         PM3_WRITE_REG(par, PM3TextureCompositeColorMode1, 0x0);
245         PM3_WRITE_REG(par, PM3TextureCompositeAlphaMode1, 0x0);
246         PM3_WRITE_REG(par, PM3TextureCompositeColorMode0, 0x0);
247         PM3_WRITE_REG(par, PM3TextureCompositeAlphaMode0, 0x0);
248         PM3_WRITE_REG(par, PM3FogMode, 0x0);
249         PM3_WRITE_REG(par, PM3ChromaTestMode, 0x0);
250         PM3_WRITE_REG(par, PM3AlphaTestMode, 0x0);
251         PM3_WRITE_REG(par, PM3AntialiasMode, 0x0);
252         PM3_WRITE_REG(par, PM3YUVMode, 0x0);
253         PM3_WRITE_REG(par, PM3AlphaBlendColorMode, 0x0);
254         PM3_WRITE_REG(par, PM3AlphaBlendAlphaMode, 0x0);
255         PM3_WRITE_REG(par, PM3DitherMode, 0x0);
256         PM3_WRITE_REG(par, PM3LogicalOpMode, 0x0);
257         PM3_WRITE_REG(par, PM3RouterMode, 0x0);
258         PM3_WRITE_REG(par, PM3Window, 0x0);
259
260         PM3_WRITE_REG(par, PM3Config2D, 0x0);
261
262         PM3_WRITE_REG(par, PM3SpanColorMask, 0xffffffff);
263
264         PM3_WRITE_REG(par, PM3XBias, 0x0);
265         PM3_WRITE_REG(par, PM3YBias, 0x0);
266         PM3_WRITE_REG(par, PM3DeltaControl, 0x0);
267
268         PM3_WRITE_REG(par, PM3BitMaskPattern, 0xffffffff);
269
270         PM3_WRITE_REG(par, PM3FBDestReadEnables,
271                            PM3FBDestReadEnables_E(0xff) |
272                            PM3FBDestReadEnables_R(0xff) |
273                            PM3FBDestReadEnables_ReferenceAlpha(0xff));
274         PM3_WRITE_REG(par, PM3FBDestReadBufferAddr0, 0x0);
275         PM3_WRITE_REG(par, PM3FBDestReadBufferOffset0, 0x0);
276         PM3_WRITE_REG(par, PM3FBDestReadBufferWidth0,
277                            PM3FBDestReadBufferWidth_Width(width));
278
279         PM3_WRITE_REG(par, PM3FBDestReadMode,
280                            PM3FBDestReadMode_ReadEnable |
281                            PM3FBDestReadMode_Enable0);
282         PM3_WRITE_REG(par, PM3FBSourceReadBufferAddr, 0x0);
283         PM3_WRITE_REG(par, PM3FBSourceReadBufferOffset, 0x0);
284         PM3_WRITE_REG(par, PM3FBSourceReadBufferWidth,
285                            PM3FBSourceReadBufferWidth_Width(width));
286         PM3_WRITE_REG(par, PM3FBSourceReadMode,
287                            PM3FBSourceReadMode_Blocking |
288                            PM3FBSourceReadMode_ReadEnable);
289
290         PM3_WAIT(par, 2);
291         {
292                 /* invert bits in bitmask */
293                 unsigned long rm = 1 | (3 << 7);
294                 switch (info->var.bits_per_pixel) {
295                 case 8:
296                         PM3_WRITE_REG(par, PM3PixelSize,
297                                            PM3PixelSize_GLOBAL_8BIT);
298 #ifdef __BIG_ENDIAN
299                         rm |= 3 << 15;
300 #endif
301                         break;
302                 case 16:
303                         PM3_WRITE_REG(par, PM3PixelSize,
304                                            PM3PixelSize_GLOBAL_16BIT);
305 #ifdef __BIG_ENDIAN
306                         rm |= 2 << 15;
307 #endif
308                         break;
309                 case 32:
310                         PM3_WRITE_REG(par, PM3PixelSize,
311                                            PM3PixelSize_GLOBAL_32BIT);
312                         break;
313                 default:
314                         DPRINTK(1, "Unsupported depth %d\n",
315                                 info->var.bits_per_pixel);
316                         break;
317                 }
318                 PM3_WRITE_REG(par, PM3RasterizerMode, rm);
319         }
320
321         PM3_WAIT(par, 20);
322         PM3_WRITE_REG(par, PM3FBSoftwareWriteMask, 0xffffffff);
323         PM3_WRITE_REG(par, PM3FBHardwareWriteMask, 0xffffffff);
324         PM3_WRITE_REG(par, PM3FBWriteMode,
325                            PM3FBWriteMode_WriteEnable |
326                            PM3FBWriteMode_OpaqueSpan |
327                            PM3FBWriteMode_Enable0);
328         PM3_WRITE_REG(par, PM3FBWriteBufferAddr0, 0x0);
329         PM3_WRITE_REG(par, PM3FBWriteBufferOffset0, 0x0);
330         PM3_WRITE_REG(par, PM3FBWriteBufferWidth0,
331                            PM3FBWriteBufferWidth_Width(width));
332
333         PM3_WRITE_REG(par, PM3SizeOfFramebuffer, 0x0);
334         {
335                 /* size in lines of FB */
336                 unsigned long sofb = info->screen_size /
337                         info->fix.line_length;
338                 if (sofb > 4095)
339                         PM3_WRITE_REG(par, PM3SizeOfFramebuffer, 4095);
340                 else
341                         PM3_WRITE_REG(par, PM3SizeOfFramebuffer, sofb);
342
343                 switch (info->var.bits_per_pixel) {
344                 case 8:
345                         PM3_WRITE_REG(par, PM3DitherMode,
346                                            (1 << 10) | (2 << 3));
347                         break;
348                 case 16:
349                         PM3_WRITE_REG(par, PM3DitherMode,
350                                            (1 << 10) | (1 << 3));
351                         break;
352                 case 32:
353                         PM3_WRITE_REG(par, PM3DitherMode,
354                                            (1 << 10) | (0 << 3));
355                         break;
356                 default:
357                         DPRINTK(1, "Unsupported depth %d\n",
358                                 info->current_par->depth);
359                         break;
360                 }
361         }
362
363         PM3_WRITE_REG(par, PM3dXDom, 0x0);
364         PM3_WRITE_REG(par, PM3dXSub, 0x0);
365         PM3_WRITE_REG(par, PM3dY, 1 << 16);
366         PM3_WRITE_REG(par, PM3StartXDom, 0x0);
367         PM3_WRITE_REG(par, PM3StartXSub, 0x0);
368         PM3_WRITE_REG(par, PM3StartY, 0x0);
369         PM3_WRITE_REG(par, PM3Count, 0x0);
370
371 /* Disable LocalBuffer. better safe than sorry */
372         PM3_WRITE_REG(par, PM3LBDestReadMode, 0x0);
373         PM3_WRITE_REG(par, PM3LBDestReadEnables, 0x0);
374         PM3_WRITE_REG(par, PM3LBSourceReadMode, 0x0);
375         PM3_WRITE_REG(par, PM3LBWriteMode, 0x0);
376
377         pm3fb_sync(info);
378 }
379
380 static void pm3fb_fillrect(struct fb_info *info,
381                                 const struct fb_fillrect *region)
382 {
383         struct pm3_par *par = info->par;
384         struct fb_fillrect modded;
385         int vxres, vyres;
386         int rop;
387         u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
388                 ((u32 *)info->pseudo_palette)[region->color] : region->color;
389
390         if (info->state != FBINFO_STATE_RUNNING)
391                 return;
392         if (info->flags & FBINFO_HWACCEL_DISABLED) {
393                 cfb_fillrect(info, region);
394                 return;
395         }
396         if (region->rop == ROP_COPY )
397                 rop = PM3Config2D_ForegroundROP(0x3); /* GXcopy */
398         else
399                 rop = PM3Config2D_ForegroundROP(0x6) | /* GXxor */
400                         PM3Config2D_FBDestReadEnable;
401
402         vxres = info->var.xres_virtual;
403         vyres = info->var.yres_virtual;
404
405         memcpy(&modded, region, sizeof(struct fb_fillrect));
406
407         if (!modded.width || !modded.height ||
408             modded.dx >= vxres || modded.dy >= vyres)
409                 return;
410
411         if (modded.dx + modded.width  > vxres)
412                 modded.width  = vxres - modded.dx;
413         if (modded.dy + modded.height > vyres)
414                 modded.height = vyres - modded.dy;
415
416         if (info->var.bits_per_pixel == 8)
417                 color |= color << 8;
418         if (info->var.bits_per_pixel <= 16)
419                 color |= color << 16;
420
421         PM3_WAIT(par, 4);
422         /* ROP Ox3 is GXcopy */
423         PM3_WRITE_REG(par, PM3Config2D,
424                         PM3Config2D_UseConstantSource |
425                         PM3Config2D_ForegroundROPEnable |
426                         rop |
427                         PM3Config2D_FBWriteEnable);
428
429         PM3_WRITE_REG(par, PM3ForegroundColor, color);
430
431         PM3_WRITE_REG(par, PM3RectanglePosition,
432                         PM3RectanglePosition_XOffset(modded.dx) |
433                         PM3RectanglePosition_YOffset(modded.dy));
434
435         PM3_WRITE_REG(par, PM3Render2D,
436                       PM3Render2D_XPositive |
437                       PM3Render2D_YPositive |
438                       PM3Render2D_Operation_Normal |
439                       PM3Render2D_SpanOperation |
440                       PM3Render2D_Width(modded.width) |
441                       PM3Render2D_Height(modded.height));
442 }
443
444 static void pm3fb_copyarea(struct fb_info *info,
445                                 const struct fb_copyarea *area)
446 {
447         struct pm3_par *par = info->par;
448         struct fb_copyarea modded;
449         u32 vxres, vyres;
450         int x_align, o_x, o_y;
451
452         if (info->state != FBINFO_STATE_RUNNING)
453                 return;
454         if (info->flags & FBINFO_HWACCEL_DISABLED) {
455                 cfb_copyarea(info, area);
456                 return;
457         }
458
459         memcpy(&modded, area, sizeof(struct fb_copyarea));
460
461         vxres = info->var.xres_virtual;
462         vyres = info->var.yres_virtual;
463
464         if (!modded.width || !modded.height ||
465             modded.sx >= vxres || modded.sy >= vyres ||
466             modded.dx >= vxres || modded.dy >= vyres)
467                 return;
468
469         if (modded.sx + modded.width > vxres)
470                 modded.width = vxres - modded.sx;
471         if (modded.dx + modded.width > vxres)
472                 modded.width = vxres - modded.dx;
473         if (modded.sy + modded.height > vyres)
474                 modded.height = vyres - modded.sy;
475         if (modded.dy + modded.height > vyres)
476                 modded.height = vyres - modded.dy;
477
478         o_x = modded.sx - modded.dx;    /*(sx > dx ) ? (sx - dx) : (dx - sx); */
479         o_y = modded.sy - modded.dy;    /*(sy > dy ) ? (sy - dy) : (dy - sy); */
480
481         x_align = (modded.sx & 0x1f);
482
483         PM3_WAIT(par, 6);
484
485         PM3_WRITE_REG(par, PM3Config2D,
486                         PM3Config2D_UserScissorEnable |
487                         PM3Config2D_ForegroundROPEnable |
488                         PM3Config2D_Blocking |
489                         PM3Config2D_ForegroundROP(0x3) | /* Ox3 is GXcopy */
490                         PM3Config2D_FBWriteEnable);
491
492         PM3_WRITE_REG(par, PM3ScissorMinXY,
493                         ((modded.dy & 0x0fff) << 16) | (modded.dx & 0x0fff));
494         PM3_WRITE_REG(par, PM3ScissorMaxXY,
495                         (((modded.dy + modded.height) & 0x0fff) << 16) |
496                         ((modded.dx + modded.width) & 0x0fff));
497
498         PM3_WRITE_REG(par, PM3FBSourceReadBufferOffset,
499                         PM3FBSourceReadBufferOffset_XOffset(o_x) |
500                         PM3FBSourceReadBufferOffset_YOffset(o_y));
501
502         PM3_WRITE_REG(par, PM3RectanglePosition,
503                         PM3RectanglePosition_XOffset(modded.dx - x_align) |
504                         PM3RectanglePosition_YOffset(modded.dy));
505
506         PM3_WRITE_REG(par, PM3Render2D,
507                         ((modded.sx > modded.dx) ? PM3Render2D_XPositive : 0) |
508                         ((modded.sy > modded.dy) ? PM3Render2D_YPositive : 0) |
509                         PM3Render2D_Operation_Normal |
510                         PM3Render2D_SpanOperation |
511                         PM3Render2D_FBSourceReadEnable |
512                         PM3Render2D_Width(modded.width + x_align) |
513                         PM3Render2D_Height(modded.height));
514 }
515
516 static void pm3fb_imageblit(struct fb_info *info, const struct fb_image *image)
517 {
518         struct pm3_par *par = info->par;
519         u32 height = image->height;
520         u32 fgx, bgx;
521         const u32 *src = (const u32 *)image->data;
522
523         if (info->state != FBINFO_STATE_RUNNING)
524                 return;
525         if (info->flags & FBINFO_HWACCEL_DISABLED) {
526                 cfb_imageblit(info, image);
527                 return;
528         }
529         switch (info->fix.visual) {
530         case FB_VISUAL_PSEUDOCOLOR:
531                 fgx = image->fg_color;
532                 bgx = image->bg_color;
533                 break;
534         case FB_VISUAL_TRUECOLOR:
535         default:
536                 fgx = par->palette[image->fg_color];
537                 bgx = par->palette[image->bg_color];
538                 break;
539         }
540         if (image->depth != 1)
541                 return cfb_imageblit(info, image);
542
543         if (info->var.bits_per_pixel == 8) {
544                 fgx |= fgx << 8;
545                 bgx |= bgx << 8;
546         }
547         if (info->var.bits_per_pixel <= 16) {
548                 fgx |= fgx << 16;
549                 bgx |= bgx << 16;
550         }
551
552         PM3_WAIT(par, 7);
553
554         PM3_WRITE_REG(par, PM3ForegroundColor, fgx);
555         PM3_WRITE_REG(par, PM3BackgroundColor, bgx);
556
557         /* ROP Ox3 is GXcopy */
558         PM3_WRITE_REG(par, PM3Config2D,
559                         PM3Config2D_UserScissorEnable |
560                         PM3Config2D_UseConstantSource |
561                         PM3Config2D_ForegroundROPEnable |
562                         PM3Config2D_ForegroundROP(0x3) |
563                         PM3Config2D_OpaqueSpan |
564                         PM3Config2D_FBWriteEnable);
565         PM3_WRITE_REG(par, PM3ScissorMinXY,
566                         ((image->dy & 0x0fff) << 16) | (image->dx & 0x0fff));
567         PM3_WRITE_REG(par, PM3ScissorMaxXY,
568                         (((image->dy + image->height) & 0x0fff) << 16) |
569                         ((image->dx + image->width) & 0x0fff));
570         PM3_WRITE_REG(par, PM3RectanglePosition,
571                         PM3RectanglePosition_XOffset(image->dx) |
572                         PM3RectanglePosition_YOffset(image->dy));
573         PM3_WRITE_REG(par, PM3Render2D,
574                         PM3Render2D_XPositive |
575                         PM3Render2D_YPositive |
576                         PM3Render2D_Operation_SyncOnBitMask |
577                         PM3Render2D_SpanOperation |
578                         PM3Render2D_Width(image->width) |
579                         PM3Render2D_Height(image->height));
580
581
582         while (height--) {
583                 int width = ((image->width + 7) >> 3)
584                                 + info->pixmap.scan_align - 1;
585                 width >>= 2;
586
587                 while (width >= PM3_FIFO_SIZE) {
588                         int i = PM3_FIFO_SIZE - 1;
589
590                         PM3_WAIT(par, PM3_FIFO_SIZE);
591                         while (i--) {
592                                 PM3_WRITE_REG(par, PM3BitMaskPattern, *src);
593                                 src++;
594                         }
595                         width -= PM3_FIFO_SIZE - 1;
596                 }
597
598                 PM3_WAIT(par, width + 1);
599                 while (width--) {
600                         PM3_WRITE_REG(par, PM3BitMaskPattern, *src);
601                         src++;
602                 }
603         }
604 }
605 /* end of acceleration functions */
606
607 /* write the mode to registers */
608 static void pm3fb_write_mode(struct fb_info *info)
609 {
610         struct pm3_par *par = info->par;
611         char tempsync = 0x00;
612         char tempmisc = 0x00;
613         const u32 hsstart = info->var.right_margin;
614         const u32 hsend = hsstart + info->var.hsync_len;
615         const u32 hbend = hsend + info->var.left_margin;
616         const u32 xres = (info->var.xres + 31) & ~31;
617         const u32 htotal = xres + hbend;
618         const u32 vsstart = info->var.lower_margin;
619         const u32 vsend = vsstart + info->var.vsync_len;
620         const u32 vbend = vsend + info->var.upper_margin;
621         const u32 vtotal = info->var.yres + vbend;
622         const u32 width = (info->var.xres_virtual + 7) & ~7;
623         const unsigned bpp = info->var.bits_per_pixel;
624
625         PM3_WAIT(par, 20);
626         PM3_WRITE_REG(par, PM3MemBypassWriteMask, 0xffffffff);
627         PM3_WRITE_REG(par, PM3Aperture0, 0x00000000);
628         PM3_WRITE_REG(par, PM3Aperture1, 0x00000000);
629         PM3_WRITE_REG(par, PM3FIFODis, 0x00000007);
630
631         PM3_WRITE_REG(par, PM3HTotal,
632                            pm3fb_shift_bpp(bpp, htotal - 1));
633         PM3_WRITE_REG(par, PM3HsEnd,
634                            pm3fb_shift_bpp(bpp, hsend));
635         PM3_WRITE_REG(par, PM3HsStart,
636                            pm3fb_shift_bpp(bpp, hsstart));
637         PM3_WRITE_REG(par, PM3HbEnd,
638                            pm3fb_shift_bpp(bpp, hbend));
639         PM3_WRITE_REG(par, PM3HgEnd,
640                            pm3fb_shift_bpp(bpp, hbend));
641         PM3_WRITE_REG(par, PM3ScreenStride,
642                            pm3fb_shift_bpp(bpp, width));
643         PM3_WRITE_REG(par, PM3VTotal, vtotal - 1);
644         PM3_WRITE_REG(par, PM3VsEnd, vsend - 1);
645         PM3_WRITE_REG(par, PM3VsStart, vsstart - 1);
646         PM3_WRITE_REG(par, PM3VbEnd, vbend);
647
648         switch (bpp) {
649         case 8:
650                 PM3_WRITE_REG(par, PM3ByAperture1Mode,
651                                    PM3ByApertureMode_PIXELSIZE_8BIT);
652                 PM3_WRITE_REG(par, PM3ByAperture2Mode,
653                                    PM3ByApertureMode_PIXELSIZE_8BIT);
654                 break;
655
656         case 16:
657 #ifndef __BIG_ENDIAN
658                 PM3_WRITE_REG(par, PM3ByAperture1Mode,
659                                    PM3ByApertureMode_PIXELSIZE_16BIT);
660                 PM3_WRITE_REG(par, PM3ByAperture2Mode,
661                                    PM3ByApertureMode_PIXELSIZE_16BIT);
662 #else
663                 PM3_WRITE_REG(par, PM3ByAperture1Mode,
664                                    PM3ByApertureMode_PIXELSIZE_16BIT |
665                                    PM3ByApertureMode_BYTESWAP_BADC);
666                 PM3_WRITE_REG(par, PM3ByAperture2Mode,
667                                    PM3ByApertureMode_PIXELSIZE_16BIT |
668                                    PM3ByApertureMode_BYTESWAP_BADC);
669 #endif /* ! __BIG_ENDIAN */
670                 break;
671
672         case 32:
673 #ifndef __BIG_ENDIAN
674                 PM3_WRITE_REG(par, PM3ByAperture1Mode,
675                                    PM3ByApertureMode_PIXELSIZE_32BIT);
676                 PM3_WRITE_REG(par, PM3ByAperture2Mode,
677                                    PM3ByApertureMode_PIXELSIZE_32BIT);
678 #else
679                 PM3_WRITE_REG(par, PM3ByAperture1Mode,
680                                    PM3ByApertureMode_PIXELSIZE_32BIT |
681                                    PM3ByApertureMode_BYTESWAP_DCBA);
682                 PM3_WRITE_REG(par, PM3ByAperture2Mode,
683                                    PM3ByApertureMode_PIXELSIZE_32BIT |
684                                    PM3ByApertureMode_BYTESWAP_DCBA);
685 #endif /* ! __BIG_ENDIAN */
686                 break;
687
688         default:
689                 DPRINTK("Unsupported depth %d\n", bpp);
690                 break;
691         }
692
693         /*
694          * Oxygen VX1 - it appears that setting PM3VideoControl and
695          * then PM3RD_SyncControl to the same SYNC settings undoes
696          * any net change - they seem to xor together.  Only set the
697          * sync options in PM3RD_SyncControl.  --rmk
698          */
699         {
700                 unsigned int video = par->video;
701
702                 video &= ~(PM3VideoControl_HSYNC_MASK |
703                            PM3VideoControl_VSYNC_MASK);
704                 video |= PM3VideoControl_HSYNC_ACTIVE_HIGH |
705                          PM3VideoControl_VSYNC_ACTIVE_HIGH;
706                 PM3_WRITE_REG(par, PM3VideoControl, video);
707         }
708         PM3_WRITE_REG(par, PM3VClkCtl,
709                            (PM3_READ_REG(par, PM3VClkCtl) & 0xFFFFFFFC));
710         PM3_WRITE_REG(par, PM3ScreenBase, par->base);
711         PM3_WRITE_REG(par, PM3ChipConfig,
712                            (PM3_READ_REG(par, PM3ChipConfig) & 0xFFFFFFFD));
713
714         wmb();
715         {
716                 unsigned char uninitialized_var(m);     /* ClkPreScale */
717                 unsigned char uninitialized_var(n);     /* ClkFeedBackScale */
718                 unsigned char uninitialized_var(p);     /* ClkPostScale */
719                 unsigned long pixclock = PICOS2KHZ(info->var.pixclock);
720
721                 (void)pm3fb_calculate_clock(pixclock, &m, &n, &p);
722
723                 DPRINTK("Pixclock: %ld, Pre: %d, Feedback: %d, Post: %d\n",
724                         pixclock, (int) m, (int) n, (int) p);
725
726                 PM3_WRITE_DAC_REG(par, PM3RD_DClk0PreScale, m);
727                 PM3_WRITE_DAC_REG(par, PM3RD_DClk0FeedbackScale, n);
728                 PM3_WRITE_DAC_REG(par, PM3RD_DClk0PostScale, p);
729         }
730         /*
731            PM3_WRITE_DAC_REG(par, PM3RD_IndexControl, 0x00);
732          */
733         /*
734            PM3_SLOW_WRITE_REG(par, PM3RD_IndexControl, 0x00);
735          */
736         if ((par->video & PM3VideoControl_HSYNC_MASK) ==
737             PM3VideoControl_HSYNC_ACTIVE_HIGH)
738                 tempsync |= PM3RD_SyncControl_HSYNC_ACTIVE_HIGH;
739         if ((par->video & PM3VideoControl_VSYNC_MASK) ==
740             PM3VideoControl_VSYNC_ACTIVE_HIGH)
741                 tempsync |= PM3RD_SyncControl_VSYNC_ACTIVE_HIGH;
742
743         PM3_WRITE_DAC_REG(par, PM3RD_SyncControl, tempsync);
744         DPRINTK("PM3RD_SyncControl: %d\n", tempsync);
745
746         PM3_WRITE_DAC_REG(par, PM3RD_DACControl, 0x00);
747
748         switch (pm3fb_depth(&info->var)) {
749         case 8:
750                 PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
751                                   PM3RD_PixelSize_8_BIT_PIXELS);
752                 PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
753                                   PM3RD_ColorFormat_CI8_COLOR |
754                                   PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW);
755                 tempmisc |= PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
756                 break;
757         case 12:
758                 PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
759                                   PM3RD_PixelSize_16_BIT_PIXELS);
760                 PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
761                                   PM3RD_ColorFormat_4444_COLOR |
762                                   PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
763                                   PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
764                 tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
765                         PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
766                 break;
767         case 15:
768                 PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
769                                   PM3RD_PixelSize_16_BIT_PIXELS);
770                 PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
771                                   PM3RD_ColorFormat_5551_FRONT_COLOR |
772                                   PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
773                                   PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
774                 tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
775                         PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
776                 break;
777         case 16:
778                 PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
779                                   PM3RD_PixelSize_16_BIT_PIXELS);
780                 PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
781                                   PM3RD_ColorFormat_565_FRONT_COLOR |
782                                   PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
783                                   PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
784                 tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
785                         PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
786                 break;
787         case 32:
788                 PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
789                                   PM3RD_PixelSize_32_BIT_PIXELS);
790                 PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
791                                   PM3RD_ColorFormat_8888_COLOR |
792                                   PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW);
793                 tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
794                         PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
795                 break;
796         }
797         PM3_WRITE_DAC_REG(par, PM3RD_MiscControl, tempmisc);
798 }
799
800 /*
801  * hardware independent functions
802  */
803 static int pm3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
804 {
805         u32 lpitch;
806         unsigned bpp = var->red.length + var->green.length
807                         + var->blue.length + var->transp.length;
808
809         if (bpp != var->bits_per_pixel) {
810                 /* set predefined mode for bits_per_pixel settings */
811
812                 switch (var->bits_per_pixel) {
813                 case 8:
814                         var->red.length = 8;
815                         var->green.length = 8;
816                         var->blue.length = 8;
817                         var->red.offset = 0;
818                         var->green.offset = 0;
819                         var->blue.offset = 0;
820                         var->transp.offset = 0;
821                         var->transp.length = 0;
822                         break;
823                 case 16:
824                         var->red.length = 5;
825                         var->blue.length = 5;
826                         var->green.length = 6;
827                         var->transp.length = 0;
828                         break;
829                 case 32:
830                         var->red.length = 8;
831                         var->green.length = 8;
832                         var->blue.length = 8;
833                         var->transp.length = 8;
834                         break;
835                 default:
836                         DPRINTK("depth not supported: %u\n",
837                                 var->bits_per_pixel);
838                         return -EINVAL;
839                 }
840         }
841         /* it is assumed BGRA order */
842         if (var->bits_per_pixel > 8 ) {
843                 var->blue.offset = 0;
844                 var->green.offset = var->blue.length;
845                 var->red.offset = var->green.offset + var->green.length;
846                 var->transp.offset = var->red.offset + var->red.length;
847         }
848         var->height = -1;
849         var->width = -1;
850
851         if (var->xres != var->xres_virtual) {
852                 DPRINTK("virtual x resolution != "
853                         "physical x resolution not supported\n");
854                 return -EINVAL;
855         }
856
857         if (var->yres > var->yres_virtual) {
858                 DPRINTK("virtual y resolution < "
859                         "physical y resolution not possible\n");
860                 return -EINVAL;
861         }
862
863         if (var->xoffset) {
864                 DPRINTK("xoffset not supported\n");
865                 return -EINVAL;
866         }
867
868         if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
869                 DPRINTK("interlace not supported\n");
870                 return -EINVAL;
871         }
872
873         var->xres = (var->xres + 31) & ~31; /* could sometimes be 8 */
874         lpitch = var->xres * ((var->bits_per_pixel + 7) >> 3);
875
876         if (var->xres < 200 || var->xres > 2048) {
877                 DPRINTK("width not supported: %u\n", var->xres);
878                 return -EINVAL;
879         }
880
881         if (var->yres < 200 || var->yres > 4095) {
882                 DPRINTK("height not supported: %u\n", var->yres);
883                 return -EINVAL;
884         }
885
886         if (lpitch * var->yres_virtual > info->fix.smem_len) {
887                 DPRINTK("no memory for screen (%ux%ux%u)\n",
888                         var->xres, var->yres_virtual, var->bits_per_pixel);
889                 return -EINVAL;
890         }
891
892         if (PICOS2KHZ(var->pixclock) > PM3_MAX_PIXCLOCK) {
893                 DPRINTK("pixclock too high (%ldKHz)\n",
894                         PICOS2KHZ(var->pixclock));
895                 return -EINVAL;
896         }
897
898         var->accel_flags = 0;   /* Can't mmap if this is on */
899
900         DPRINTK("Checking graphics mode at %dx%d depth %d\n",
901                 var->xres, var->yres, var->bits_per_pixel);
902         return 0;
903 }
904
905 static int pm3fb_set_par(struct fb_info *info)
906 {
907         struct pm3_par *par = info->par;
908         const u32 xres = (info->var.xres + 31) & ~31;
909         const unsigned bpp = info->var.bits_per_pixel;
910
911         par->base = pm3fb_shift_bpp(bpp, (info->var.yoffset * xres)
912                                         + info->var.xoffset);
913         par->video = 0;
914
915         if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
916                 par->video |= PM3VideoControl_HSYNC_ACTIVE_HIGH;
917         else
918                 par->video |= PM3VideoControl_HSYNC_ACTIVE_LOW;
919
920         if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
921                 par->video |= PM3VideoControl_VSYNC_ACTIVE_HIGH;
922         else
923                 par->video |= PM3VideoControl_VSYNC_ACTIVE_LOW;
924
925         if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE)
926                 par->video |= PM3VideoControl_LINE_DOUBLE_ON;
927
928         if ((info->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW)
929                 par->video |= PM3VideoControl_ENABLE;
930         else
931                 DPRINTK("PM3Video disabled\n");
932
933         switch (bpp) {
934         case 8:
935                 par->video |= PM3VideoControl_PIXELSIZE_8BIT;
936                 break;
937         case 16:
938                 par->video |= PM3VideoControl_PIXELSIZE_16BIT;
939                 break;
940         case 32:
941                 par->video |= PM3VideoControl_PIXELSIZE_32BIT;
942                 break;
943         default:
944                 DPRINTK("Unsupported depth\n");
945                 break;
946         }
947
948         info->fix.visual =
949                 (bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
950         info->fix.line_length = ((info->var.xres_virtual + 7)  >> 3) * bpp;
951
952 /*      pm3fb_clear_memory(info, 0);*/
953         pm3fb_clear_colormap(par, 0, 0, 0);
954         PM3_WRITE_DAC_REG(par, PM3RD_CursorMode, 0);
955         pm3fb_init_engine(info);
956         pm3fb_write_mode(info);
957         return 0;
958 }
959
960 static int pm3fb_setcolreg(unsigned regno, unsigned red, unsigned green,
961                            unsigned blue, unsigned transp,
962                            struct fb_info *info)
963 {
964         struct pm3_par *par = info->par;
965
966         if (regno >= 256)  /* no. of hw registers */
967            return -EINVAL;
968
969         /* grayscale works only partially under directcolor */
970         /* grayscale = 0.30*R + 0.59*G + 0.11*B */
971         if (info->var.grayscale)
972            red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
973
974         /* Directcolor:
975          *   var->{color}.offset contains start of bitfield
976          *   var->{color}.length contains length of bitfield
977          *   {hardwarespecific} contains width of DAC
978          *   pseudo_palette[X] is programmed to (X << red.offset) |
979          *                                      (X << green.offset) |
980          *                                      (X << blue.offset)
981          *   RAMDAC[X] is programmed to (red, green, blue)
982          *   color depth = SUM(var->{color}.length)
983          *
984          * Pseudocolor:
985          *      var->{color}.offset is 0
986          *      var->{color}.length contains width of DAC or the number
987          *                      of unique colors available (color depth)
988          *      pseudo_palette is not used
989          *      RAMDAC[X] is programmed to (red, green, blue)
990          *      color depth = var->{color}.length
991          */
992
993         /*
994          * This is the point where the color is converted to something that
995          * is acceptable by the hardware.
996          */
997 #define CNVT_TOHW(val, width) ((((val) << (width)) + 0x7FFF - (val)) >> 16)
998         red = CNVT_TOHW(red, info->var.red.length);
999         green = CNVT_TOHW(green, info->var.green.length);
1000         blue = CNVT_TOHW(blue, info->var.blue.length);
1001         transp = CNVT_TOHW(transp, info->var.transp.length);
1002 #undef CNVT_TOHW
1003
1004         if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
1005         info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
1006                 u32 v;
1007
1008                 if (regno >= 16)
1009                         return -EINVAL;
1010
1011                 v = (red << info->var.red.offset) |
1012                         (green << info->var.green.offset) |
1013                         (blue << info->var.blue.offset) |
1014                         (transp << info->var.transp.offset);
1015
1016                 switch (info->var.bits_per_pixel) {
1017                 case 8:
1018                         break;
1019                 case 16:
1020                 case 32:
1021                         ((u32 *)(info->pseudo_palette))[regno] = v;
1022                         break;
1023                 }
1024                 return 0;
1025         } else if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR)
1026                 pm3fb_set_color(par, regno, red, green, blue);
1027
1028         return 0;
1029 }
1030
1031 static int pm3fb_pan_display(struct fb_var_screeninfo *var,
1032                                  struct fb_info *info)
1033 {
1034         struct pm3_par *par = info->par;
1035         const u32 xres = (var->xres + 31) & ~31;
1036
1037         par->base = pm3fb_shift_bpp(var->bits_per_pixel,
1038                                         (var->yoffset * xres)
1039                                         + var->xoffset);
1040         PM3_WAIT(par, 1);
1041         PM3_WRITE_REG(par, PM3ScreenBase, par->base);
1042         return 0;
1043 }
1044
1045 static int pm3fb_blank(int blank_mode, struct fb_info *info)
1046 {
1047         struct pm3_par *par = info->par;
1048         u32 video = par->video;
1049
1050         /*
1051          * Oxygen VX1 - it appears that setting PM3VideoControl and
1052          * then PM3RD_SyncControl to the same SYNC settings undoes
1053          * any net change - they seem to xor together.  Only set the
1054          * sync options in PM3RD_SyncControl.  --rmk
1055          */
1056         video &= ~(PM3VideoControl_HSYNC_MASK |
1057                    PM3VideoControl_VSYNC_MASK);
1058         video |= PM3VideoControl_HSYNC_ACTIVE_HIGH |
1059                  PM3VideoControl_VSYNC_ACTIVE_HIGH;
1060
1061         switch (blank_mode) {
1062         case FB_BLANK_UNBLANK:
1063                 video |= PM3VideoControl_ENABLE;
1064                 break;
1065         case FB_BLANK_NORMAL:
1066                 video &= ~PM3VideoControl_ENABLE;
1067                 break;
1068         case FB_BLANK_HSYNC_SUSPEND:
1069                 video &= ~(PM3VideoControl_HSYNC_MASK |
1070                           PM3VideoControl_BLANK_ACTIVE_LOW);
1071                 break;
1072         case FB_BLANK_VSYNC_SUSPEND:
1073                 video &= ~(PM3VideoControl_VSYNC_MASK |
1074                           PM3VideoControl_BLANK_ACTIVE_LOW);
1075                 break;
1076         case FB_BLANK_POWERDOWN:
1077                 video &= ~(PM3VideoControl_HSYNC_MASK |
1078                           PM3VideoControl_VSYNC_MASK |
1079                           PM3VideoControl_BLANK_ACTIVE_LOW);
1080                 break;
1081         default:
1082                 DPRINTK("Unsupported blanking %d\n", blank_mode);
1083                 return 1;
1084         }
1085
1086         PM3_WAIT(par, 1);
1087         PM3_WRITE_REG(par, PM3VideoControl, video);
1088         return 0;
1089 }
1090
1091         /*
1092          *  Frame buffer operations
1093          */
1094
1095 static struct fb_ops pm3fb_ops = {
1096         .owner          = THIS_MODULE,
1097         .fb_check_var   = pm3fb_check_var,
1098         .fb_set_par     = pm3fb_set_par,
1099         .fb_setcolreg   = pm3fb_setcolreg,
1100         .fb_pan_display = pm3fb_pan_display,
1101         .fb_fillrect    = pm3fb_fillrect,
1102         .fb_copyarea    = pm3fb_copyarea,
1103         .fb_imageblit   = pm3fb_imageblit,
1104         .fb_blank       = pm3fb_blank,
1105         .fb_sync        = pm3fb_sync,
1106 };
1107
1108 /* ------------------------------------------------------------------------- */
1109
1110         /*
1111          *  Initialization
1112          */
1113
1114 /* mmio register are already mapped when this function is called */
1115 /* the pm3fb_fix.smem_start is also set */
1116 static unsigned long pm3fb_size_memory(struct pm3_par *par)
1117 {
1118         unsigned long   memsize = 0;
1119         unsigned long   tempBypass, i, temp1, temp2;
1120         unsigned char   __iomem *screen_mem;
1121
1122         pm3fb_fix.smem_len = 64 * 1024l * 1024; /* request full aperture size */
1123         /* Linear frame buffer - request region and map it. */
1124         if (!request_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len,
1125                                  "pm3fb smem")) {
1126                 printk(KERN_WARNING "pm3fb: Can't reserve smem.\n");
1127                 return 0;
1128         }
1129         screen_mem =
1130                 ioremap_nocache(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
1131         if (!screen_mem) {
1132                 printk(KERN_WARNING "pm3fb: Can't ioremap smem area.\n");
1133                 release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
1134                 return 0;
1135         }
1136
1137         /* TODO: card-specific stuff, *before* accessing *any* FB memory */
1138         /* For Appian Jeronimo 2000 board second head */
1139
1140         tempBypass = PM3_READ_REG(par, PM3MemBypassWriteMask);
1141
1142         DPRINTK("PM3MemBypassWriteMask was: 0x%08lx\n", tempBypass);
1143
1144         PM3_WAIT(par, 1);
1145         PM3_WRITE_REG(par, PM3MemBypassWriteMask, 0xFFFFFFFF);
1146
1147         /* pm3 split up memory, replicates, and do a lot of
1148          * nasty stuff IMHO ;-)
1149          */
1150         for (i = 0; i < 32; i++) {
1151                 fb_writel(i * 0x00345678,
1152                           (screen_mem + (i * 1048576)));
1153                 mb();
1154                 temp1 = fb_readl((screen_mem + (i * 1048576)));
1155
1156                 /* Let's check for wrapover, write will fail at 16MB boundary */
1157                 if (temp1 == (i * 0x00345678))
1158                         memsize = i;
1159                 else
1160                         break;
1161         }
1162
1163         DPRINTK("First detect pass already got %ld MB\n", memsize + 1);
1164
1165         if (memsize + 1 == i) {
1166                 for (i = 0; i < 32; i++) {
1167                         /* Clear first 32MB ; 0 is 0, no need to byteswap */
1168                         writel(0x0000000, (screen_mem + (i * 1048576)));
1169                 }
1170                 wmb();
1171
1172                 for (i = 32; i < 64; i++) {
1173                         fb_writel(i * 0x00345678,
1174                                   (screen_mem + (i * 1048576)));
1175                         mb();
1176                         temp1 =
1177                             fb_readl((screen_mem + (i * 1048576)));
1178                         temp2 =
1179                             fb_readl((screen_mem + ((i - 32) * 1048576)));
1180                         /* different value, different RAM... */
1181                         if ((temp1 == (i * 0x00345678)) && (temp2 == 0))
1182                                 memsize = i;
1183                         else
1184                                 break;
1185                 }
1186         }
1187         DPRINTK("Second detect pass got %ld MB\n", memsize + 1);
1188
1189         PM3_WAIT(par, 1);
1190         PM3_WRITE_REG(par, PM3MemBypassWriteMask, tempBypass);
1191
1192         iounmap(screen_mem);
1193         release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
1194         memsize = 1048576 * (memsize + 1);
1195
1196         DPRINTK("Returning 0x%08lx bytes\n", memsize);
1197
1198         return memsize;
1199 }
1200
1201 static int __devinit pm3fb_probe(struct pci_dev *dev,
1202                                   const struct pci_device_id *ent)
1203 {
1204         struct fb_info *info;
1205         struct pm3_par *par;
1206         struct device *device = &dev->dev; /* for pci drivers */
1207         int err;
1208         int retval = -ENXIO;
1209
1210         err = pci_enable_device(dev);
1211         if (err) {
1212                 printk(KERN_WARNING "pm3fb: Can't enable PCI dev: %d\n", err);
1213                 return err;
1214         }
1215         /*
1216          * Dynamically allocate info and par
1217          */
1218         info = framebuffer_alloc(sizeof(struct pm3_par), device);
1219
1220         if (!info)
1221                 return -ENOMEM;
1222         par = info->par;
1223
1224         /*
1225          * Here we set the screen_base to the virtual memory address
1226          * for the framebuffer.
1227          */
1228         pm3fb_fix.mmio_start = pci_resource_start(dev, 0);
1229         pm3fb_fix.mmio_len = PM3_REGS_SIZE;
1230 #if defined(__BIG_ENDIAN)
1231         pm3fb_fix.mmio_start += PM3_REGS_SIZE;
1232         DPRINTK("Adjusting register base for big-endian.\n");
1233 #endif
1234
1235         /* Registers - request region and map it. */
1236         if (!request_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len,
1237                                  "pm3fb regbase")) {
1238                 printk(KERN_WARNING "pm3fb: Can't reserve regbase.\n");
1239                 goto err_exit_neither;
1240         }
1241         par->v_regs =
1242                 ioremap_nocache(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len);
1243         if (!par->v_regs) {
1244                 printk(KERN_WARNING "pm3fb: Can't remap %s register area.\n",
1245                         pm3fb_fix.id);
1246                 release_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len);
1247                 goto err_exit_neither;
1248         }
1249
1250         /* Linear frame buffer - request region and map it. */
1251         pm3fb_fix.smem_start = pci_resource_start(dev, 1);
1252         pm3fb_fix.smem_len = pm3fb_size_memory(par);
1253         if (!pm3fb_fix.smem_len) {
1254                 printk(KERN_WARNING "pm3fb: Can't find memory on board.\n");
1255                 goto err_exit_mmio;
1256         }
1257         if (!request_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len,
1258                                  "pm3fb smem")) {
1259                 printk(KERN_WARNING "pm3fb: Can't reserve smem.\n");
1260                 goto err_exit_mmio;
1261         }
1262         info->screen_base =
1263                 ioremap_nocache(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
1264         if (!info->screen_base) {
1265                 printk(KERN_WARNING "pm3fb: Can't ioremap smem area.\n");
1266                 release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
1267                 goto err_exit_mmio;
1268         }
1269         info->screen_size = pm3fb_fix.smem_len;
1270
1271 #ifdef CONFIG_MTRR
1272         if (!nomtrr)
1273                 par->mtrr_handle = mtrr_add(pm3fb_fix.smem_start,
1274                                                 pm3fb_fix.smem_len,
1275                                                 MTRR_TYPE_WRCOMB, 1);
1276 #endif
1277         info->fbops = &pm3fb_ops;
1278
1279         par->video = PM3_READ_REG(par, PM3VideoControl);
1280
1281         info->fix = pm3fb_fix;
1282         info->pseudo_palette = par->palette;
1283         info->flags = FBINFO_DEFAULT |
1284                         FBINFO_HWACCEL_XPAN |
1285                         FBINFO_HWACCEL_YPAN |
1286                         FBINFO_HWACCEL_COPYAREA |
1287                         FBINFO_HWACCEL_IMAGEBLIT |
1288                         FBINFO_HWACCEL_FILLRECT;
1289
1290         if (noaccel) {
1291                 printk(KERN_DEBUG "disabling acceleration\n");
1292                 info->flags |= FBINFO_HWACCEL_DISABLED;
1293         }
1294         info->pixmap.addr = kmalloc(PM3_PIXMAP_SIZE, GFP_KERNEL);
1295         if (!info->pixmap.addr) {
1296                 retval = -ENOMEM;
1297                 goto err_exit_pixmap;
1298         }
1299         info->pixmap.size = PM3_PIXMAP_SIZE;
1300         info->pixmap.buf_align = 4;
1301         info->pixmap.scan_align = 4;
1302         info->pixmap.access_align = 32;
1303         info->pixmap.flags = FB_PIXMAP_SYSTEM;
1304
1305         /*
1306          * This should give a reasonable default video mode. The following is
1307          * done when we can set a video mode.
1308          */
1309         if (!mode_option)
1310                 mode_option = "640x480@60";
1311
1312         retval = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
1313
1314         if (!retval || retval == 4) {
1315                 retval = -EINVAL;
1316                 goto err_exit_both;
1317         }
1318
1319         if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
1320                 retval = -ENOMEM;
1321                 goto err_exit_both;
1322         }
1323
1324         /*
1325          * For drivers that can...
1326          */
1327         pm3fb_check_var(&info->var, info);
1328
1329         if (register_framebuffer(info) < 0) {
1330                 retval = -EINVAL;
1331                 goto err_exit_all;
1332         }
1333         printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
1334            info->fix.id);
1335         pci_set_drvdata(dev, info);
1336         return 0;
1337
1338  err_exit_all:
1339         fb_dealloc_cmap(&info->cmap);
1340  err_exit_both:
1341         kfree(info->pixmap.addr);
1342  err_exit_pixmap:
1343         iounmap(info->screen_base);
1344         release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
1345  err_exit_mmio:
1346         iounmap(par->v_regs);
1347         release_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len);
1348  err_exit_neither:
1349         framebuffer_release(info);
1350         return retval;
1351 }
1352
1353         /*
1354          *  Cleanup
1355          */
1356 static void __devexit pm3fb_remove(struct pci_dev *dev)
1357 {
1358         struct fb_info *info = pci_get_drvdata(dev);
1359
1360         if (info) {
1361                 struct fb_fix_screeninfo *fix = &info->fix;
1362                 struct pm3_par *par = info->par;
1363
1364                 unregister_framebuffer(info);
1365                 fb_dealloc_cmap(&info->cmap);
1366
1367 #ifdef CONFIG_MTRR
1368         if (par->mtrr_handle >= 0)
1369                 mtrr_del(par->mtrr_handle, info->fix.smem_start,
1370                          info->fix.smem_len);
1371 #endif /* CONFIG_MTRR */
1372                 iounmap(info->screen_base);
1373                 release_mem_region(fix->smem_start, fix->smem_len);
1374                 iounmap(par->v_regs);
1375                 release_mem_region(fix->mmio_start, fix->mmio_len);
1376
1377                 pci_set_drvdata(dev, NULL);
1378                 kfree(info->pixmap.addr);
1379                 framebuffer_release(info);
1380         }
1381 }
1382
1383 static struct pci_device_id pm3fb_id_table[] = {
1384         { PCI_VENDOR_ID_3DLABS, 0x0a,
1385           PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
1386         { 0, }
1387 };
1388
1389 /* For PCI drivers */
1390 static struct pci_driver pm3fb_driver = {
1391         .name =         "pm3fb",
1392         .id_table =     pm3fb_id_table,
1393         .probe =        pm3fb_probe,
1394         .remove =       __devexit_p(pm3fb_remove),
1395 };
1396
1397 MODULE_DEVICE_TABLE(pci, pm3fb_id_table);
1398
1399 #ifndef MODULE
1400         /*
1401          *  Setup
1402          */
1403
1404 /*
1405  * Only necessary if your driver takes special options,
1406  * otherwise we fall back on the generic fb_setup().
1407  */
1408 static int __init pm3fb_setup(char *options)
1409 {
1410         char *this_opt;
1411
1412         /* Parse user speficied options (`video=pm3fb:') */
1413         if (!options || !*options)
1414                 return 0;
1415
1416         while ((this_opt = strsep(&options, ",")) != NULL) {
1417                 if (!*this_opt)
1418                         continue;
1419                 else if (!strncmp(this_opt, "noaccel", 7))
1420                         noaccel = 1;
1421 #ifdef CONFIG_MTRR
1422                 else if (!strncmp(this_opt, "nomtrr", 6))
1423                         nomtrr = 1;
1424 #endif
1425                 else
1426                         mode_option = this_opt;
1427         }
1428         return 0;
1429 }
1430 #endif /* MODULE */
1431
1432 static int __init pm3fb_init(void)
1433 {
1434         /*
1435          *  For kernel boot options (in 'video=pm3fb:<options>' format)
1436          */
1437 #ifndef MODULE
1438         char *option = NULL;
1439
1440         if (fb_get_options("pm3fb", &option))
1441                 return -ENODEV;
1442         pm3fb_setup(option);
1443 #endif
1444
1445         return pci_register_driver(&pm3fb_driver);
1446 }
1447
1448 #ifdef MODULE
1449 static void __exit pm3fb_exit(void)
1450 {
1451         pci_unregister_driver(&pm3fb_driver);
1452 }
1453
1454 module_exit(pm3fb_exit);
1455 #endif
1456 module_init(pm3fb_init);
1457
1458 module_param(noaccel, bool, 0);
1459 MODULE_PARM_DESC(noaccel, "Disable acceleration");
1460 #ifdef CONFIG_MTRR
1461 module_param(nomtrr, bool, 0);
1462 MODULE_PARM_DESC(nomtrr, "Disable MTRR support (0 or 1=disabled) (default=0)");
1463 #endif
1464
1465 MODULE_DESCRIPTION("Permedia3 framebuffer device driver");
1466 MODULE_LICENSE("GPL");