1 /*
2 * Permedia2 framebuffer driver.
3 * Copyright (c) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT)
4 * Copyright (c) 1999 Jakub Jelinek (jakub@redhat.com)
5 * Based on linux/drivers/video/skeletonfb.c by Geert Uytterhoeven.
6 * --------------------------------------------------------------------------
7 * $Id: pm2fb.c,v 1.163 1999/02/21 14:06:49 illo Exp $
8 * --------------------------------------------------------------------------
9 * TODO multiple boards support
10 * --------------------------------------------------------------------------
11 * This file is subject to the terms and conditions of the GNU General Public
12 * License. See the file COPYING in the main directory of this archive
13 * for more details.
14 */
15
16 #include <linux/config.h>
17 #include <linux/module.h>
18 #include <linux/kernel.h>
19 #include <linux/errno.h>
20 #include <linux/string.h>
21 #include <linux/mm.h>
22 #include <linux/tty.h>
23 #include <linux/slab.h>
24 #include <linux/vmalloc.h>
25 #include <linux/delay.h>
26 #include <linux/interrupt.h>
27 #include <linux/fb.h>
28 #include <linux/selection.h>
29 #include <linux/console.h>
30 #include <linux/init.h>
31 #include <linux/pci.h>
32 #include <asm/system.h>
33 #include <asm/io.h>
34 #include <asm/uaccess.h>
35 #include <video/fbcon.h>
36 #include <video/fbcon-cfb8.h>
37 #include <video/fbcon-cfb16.h>
38 #include <video/fbcon-cfb24.h>
39 #include <video/fbcon-cfb32.h>
40 #include "pm2fb.h"
41 #include "cvisionppc.h"
42 #ifdef __sparc__
43 #include <asm/pbm.h>
44 #include <asm/fbio.h>
45 #endif
46
47 #if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)
48 #error "The endianness of the target host has not been defined."
49 #endif
50
51 #if defined(__BIG_ENDIAN) && !defined(__sparc__)
52 #define PM2FB_BE_APERTURE
53 #endif
54
55 /* Need to debug this some more */
56 #undef PM2FB_HW_CURSOR
57
58 #if defined(CONFIG_FB_PM2_PCI) && !defined(CONFIG_PCI)
59 #undef CONFIG_FB_PM2_PCI
60 #warning "support for Permedia2 PCI boards with no generic PCI support!"
61 #endif
62
63 #undef PM2FB_MASTER_DEBUG
64 #ifdef PM2FB_MASTER_DEBUG
65 #define DPRINTK(a,b...) printk(KERN_DEBUG "pm2fb: %s: " a, __FUNCTION__ , ## b)
66 #else
67 #define DPRINTK(a,b...)
68 #endif
69
70 #define PICOS2KHZ(a) (1000000000UL/(a))
71 #define KHZ2PICOS(a) (1000000000UL/(a))
72
73 /*
74 * The _DEFINITIVE_ memory mapping/unmapping functions.
75 * This is due to the fact that they're changing soooo often...
76 */
77 #define MMAP(a,b) ioremap((unsigned long )(a), b)
78 #define UNMAP(a,b) iounmap(a)
79
80 /*
81 * The _DEFINITIVE_ memory i/o barrier functions.
82 * This is due to the fact that they're changing soooo often...
83 */
84 #define DEFW() wmb()
85 #define DEFR() rmb()
86 #define DEFRW() mb()
87
88 #ifndef MIN
89 #define MIN(a,b) ((a)<(b)?(a):(b))
90 #endif
91
92 #ifndef MAX
93 #define MAX(a,b) ((a)>(b)?(a):(b))
94 #endif
95
96 struct pm2fb_par {
97 u32 pixclock; /* pixclock in KHz */
98 u32 width; /* width of virtual screen */
99 u32 height; /* height of virtual screen */
100 u32 hsstart; /* horiz. sync start */
101 u32 hsend; /* horiz. sync end */
102 u32 hbend; /* horiz. blank end (also gate end) */
103 u32 htotal; /* total width (w/ sync & blank) */
104 u32 vsstart; /* vert. sync start */
105 u32 vsend; /* vert. sync end */
106 u32 vbend; /* vert. blank end */
107 u32 vtotal; /* total height (w/ sync & blank) */
108 u32 stride; /* screen stride */
109 u32 base; /* screen base (xoffset+yoffset) */
110 u32 depth; /* screen depth (8, 16, 24 or 32) */
111 u32 video; /* video control (hsync,vsync) */
112 };
113
114 #define OPTF_OLD_MEM (1L<<0)
115 #define OPTF_YPAN (1L<<1)
116 #define OPTF_VIRTUAL (1L<<2)
117 #define OPTF_USER (1L<<3)
118 static struct {
119 char font[40];
120 u32 flags;
121 struct pm2fb_par user_mode;
122 } pm2fb_options =
123 #ifdef __sparc__
124 /* For some reason Raptor is not happy with the low-end mode */
125 {"\0", 0L, {31499,640,480,4,20,50,209,0,3,20,499,80,0,8,121}};
126 #else
127 {"\0", 0L, {25174,640,480,4,28,40,199,9,11,45,524,80,0,8,121}};
128 #endif
129
130 static char curblink __initdata = 1;
131
132 static struct {
133 char name[16];
134 struct pm2fb_par par;
135 } user_mode[] __initdata = {
136 {"640x480-60",
137 {25174,640,480,4,28,40,199,9,11,45,524,80,0,8,121}},
138 {"640x480-72",
139 {31199,640,480,6,16,48,207,8,10,39,518,80,0,8,121}},
140 {"640x480-75",
141 {31499,640,480,4,20,50,209,0,3,20,499,80,0,8,121}},
142 {"640x480-90",
143 {39909,640,480,8,18,48,207,24,38,53,532,80,0,8,121}},
144 {"640x480-100",
145 {44899,640,480,8,40,52,211,21,33,51,530,80,0,8,121}},
146 {"800x600-56",
147 {35999,800,600,6,24,56,255,0,2,25,624,100,0,8,41}},
148 {"800x600-60",
149 {40000,800,600,10,42,64,263,0,4,28,627,100,0,8,41}},
150 {"800x600-70",
151 {44899,800,600,6,42,52,251,8,20,36,635,100,0,8,105}},
152 {"800x600-72",
153 {50000,800,600,14,44,60,259,36,42,66,665,100,0,8,41}},
154 {"800x600-75",
155 {49497,800,600,4,24,64,263,0,3,25,624,100,0,8,41}},
156 {"800x600-90",
157 {56637,800,600,2,18,48,247,7,18,35,634,100,0,8,41}},
158 {"800x600-100",
159 {67499,800,600,0,16,70,269,6,10,25,624,100,0,8,41}},
160 {"1024x768-60",
161 {64998,1024,768,6,40,80,335,2,8,38,805,128,0,8,121}},
162 {"1024x768-70",
163 {74996,1024,768,6,40,76,331,2,8,38,805,128,0,8,121}},
164 {"1024x768-72",
165 {74996,1024,768,6,40,66,321,2,8,38,805,128,0,8,121}},
166 {"1024x768-75",
167 {78932,1024,768,4,28,72,327,0,3,32,799,128,0,8,41}},
168 {"1024x768-90",
169 {100000,1024,768,0,24,72,327,20,35,77,844,128,0,8,121}},
170 {"1024x768-100",
171 {109998,1024,768,0,22,92,347,0,7,24,791,128,0,8,121}},
172 {"1024x768-illo",
173 {120322,1024,768,12,48,120,375,3,7,32,799,128,0,8,41}},
174 {"1152x864-60",
175 {80000,1152,864,16,44,76,363,5,10,52,915,144,0,8,41}},
176 {"1152x864-70",
177 {100000,1152,864,10,48,90,377,12,23,81,944,144,0,8,41}},
178 {"1152x864-75",
179 {109998,1152,864,6,42,78,365,44,52,138,1001,144,0,8,41}},
180 {"1152x864-80",
181 {109998,1152,864,4,32,72,359,29,36,94,957,144,0,8,41}},
182 {"1280x1024-60",
183 {107991,1280,1024,12,40,102,421,0,3,42,1065,160,0,8,41}},
184 {"1280x1024-70",
185 {125992,1280,1024,20,48,102,421,0,5,42,1065,160,0,8,41}},
186 {"1280x1024-74",
187 {134989,1280,1024,8,44,108,427,0,29,40,1063,160,0,8,41}},
188 {"1280x1024-75",
189 {134989,1280,1024,4,40,102,421,0,3,42,1065,160,0,8,41}},
190 {"1600x1200-60",
191 {155981,1600,1200,8,48,112,511,9,17,70,1269,200,0,8,121}},
192 {"1600x1200-66",
193 {171998,1600,1200,10,44,120,519,2,5,53,1252,200,0,8,121}},
194 {"1600x1200-76",
195 {197980,1600,1200,10,44,120,519,2,7,50,1249,200,0,8,121}},
196 {"\0", },
197 };
198
199 #ifdef CONFIG_FB_PM2_PCI
200 struct pm2pci_par {
201 u32 mem_config;
202 u32 mem_control;
203 u32 boot_address;
204 struct pci_dev* dev;
205 };
206 #endif
207
208 #define DEFAULT_CURSOR_BLINK_RATE (20)
209 #define CURSOR_DRAW_DELAY (2)
210
211 struct pm2_cursor {
212 int enable;
213 int on;
214 int vbl_cnt;
215 int blink_rate;
216 struct {
217 u16 x, y;
218 } pos, hot, size;
219 u8 color[6];
220 u8 bits[8][64];
221 u8 mask[8][64];
222 struct timer_list *timer;
223 };
224
225 static const char permedia2_name[16]="Permedia2";
226
227 static struct pm2fb_info {
228 struct fb_info_gen gen;
229 int board; /* Permedia2 board index (see
230 board_table[] below) */
231 pm2type_t type;
232 struct {
233 unsigned long fb_base; /* physical framebuffer memory base */
234 u32 fb_size; /* framebuffer memory size */
235 unsigned long rg_base; /* physical register memory base */
236 unsigned long p_fb; /* physical address of frame buffer */
237 unsigned char* v_fb; /* virtual address of frame buffer */
238 unsigned long p_regs; /* physical address of registers
239 region, must be rg_base or
240 rg_base+PM2_REGS_SIZE depending on
241 the host endianness */
242 unsigned char* v_regs; /* virtual address of p_regs */
243 } regions;
244 union { /* here, the per-board par structs */
245 #ifdef CONFIG_FB_PM2_CVPPC
246 struct cvppc_par cvppc; /* CVisionPPC data */
247 #endif
248 #ifdef CONFIG_FB_PM2_PCI
249 struct pm2pci_par pci; /* Permedia2 PCI boards data */
250 #endif
251 } board_par;
252 struct pm2fb_par current_par; /* displayed screen */
253 int current_par_valid;
254 u32 memclock; /* memclock (set by the per-board
255 init routine) */
256 struct display disp;
257 struct {
258 u8 transp;
259 u8 red;
260 u8 green;
261 u8 blue;
262 } palette[256];
263 union {
264 #ifdef FBCON_HAS_CFB16
265 u16 cmap16[16];
266 #endif
267 #ifdef FBCON_HAS_CFB24
268 u32 cmap24[16];
269 #endif
270 #ifdef FBCON_HAS_CFB32
271 u32 cmap32[16];
272 #endif
273 } cmap;
274 struct pm2_cursor *cursor;
275 } fb_info;
276
277 #ifdef CONFIG_FB_PM2_CVPPC
278 static int cvppc_detect(struct pm2fb_info*);
279 static void cvppc_init(struct pm2fb_info*);
280 #endif
281
282 #ifdef CONFIG_FB_PM2_PCI
283 static int pm2pci_detect(struct pm2fb_info*);
284 static void pm2pci_init(struct pm2fb_info*);
285 #endif
286
287 #ifdef PM2FB_HW_CURSOR
288 static void pm2fb_cursor(struct display *p, int mode, int x, int y);
289 static int pm2fb_set_font(struct display *d, int width, int height);
290 static struct pm2_cursor *pm2_init_cursor(struct pm2fb_info *fb);
291 static void pm2v_set_cursor_color(struct pm2fb_info *fb, u8 *red, u8 *green, u8 *blue);
292 static void pm2v_set_cursor_shape(struct pm2fb_info *fb);
293 static u8 cursor_color_map[2] = { 0, 0xff };
294 #else
295 #define pm2fb_cursor NULL
296 #define pm2fb_set_font NULL
297 #endif
298
299 /*
300 * Table of the supported Permedia2 based boards.
301 * Three hooks are defined for each board:
302 * detect(): should return 1 if the related board has been detected, 0
303 * otherwise. It should also fill the fields 'regions.fb_base',
304 * 'regions.fb_size', 'regions.rg_base' and 'memclock' in the
305 * passed pm2fb_info structure.
306 * init(): called immediately after the reset of the Permedia2 chip.
307 * It should reset the memory controller if needed (the MClk
308 * is set shortly afterwards by the caller).
309 * cleanup(): called after the driver has been unregistered.
310 *
311 * the init and cleanup pointers can be NULL.
312 */
313 static const struct {
314 int (*detect)(struct pm2fb_info*);
315 void (*init)(struct pm2fb_info*);
316 void (*cleanup)(struct pm2fb_info*);
317 char name[32];
318 } board_table[] = {
319 #ifdef CONFIG_FB_PM2_PCI
320 { pm2pci_detect, pm2pci_init, NULL, "Permedia2 PCI board" },
321 #endif
322 #ifdef CONFIG_FB_PM2_CVPPC
323 { cvppc_detect, cvppc_init, NULL, "CVisionPPC/BVisionPPC" },
324 #endif
325 { NULL, }
326 };
327
328 /*
329 * partial products for the supported horizontal resolutions.
330 */
331 #define PACKPP(p0,p1,p2) (((p2)<<6)|((p1)<<3)|(p0))
332 static const struct {
333 u16 width;
334 u16 pp;
335 } pp_table[] = {
336 { 32, PACKPP(1, 0, 0) }, { 64, PACKPP(1, 1, 0) },
337 { 96, PACKPP(1, 1, 1) }, { 128, PACKPP(2, 1, 1) },
338 { 160, PACKPP(2, 2, 1) }, { 192, PACKPP(2, 2, 2) },
339 { 224, PACKPP(3, 2, 1) }, { 256, PACKPP(3, 2, 2) },
340 { 288, PACKPP(3, 3, 1) }, { 320, PACKPP(3, 3, 2) },
341 { 384, PACKPP(3, 3, 3) }, { 416, PACKPP(4, 3, 1) },
342 { 448, PACKPP(4, 3, 2) }, { 512, PACKPP(4, 3, 3) },
343 { 544, PACKPP(4, 4, 1) }, { 576, PACKPP(4, 4, 2) },
344 { 640, PACKPP(4, 4, 3) }, { 768, PACKPP(4, 4, 4) },
345 { 800, PACKPP(5, 4, 1) }, { 832, PACKPP(5, 4, 2) },
346 { 896, PACKPP(5, 4, 3) }, { 1024, PACKPP(5, 4, 4) },
347 { 1056, PACKPP(5, 5, 1) }, { 1088, PACKPP(5, 5, 2) },
348 { 1152, PACKPP(5, 5, 3) }, { 1280, PACKPP(5, 5, 4) },
349 { 1536, PACKPP(5, 5, 5) }, { 1568, PACKPP(6, 5, 1) },
350 { 1600, PACKPP(6, 5, 2) }, { 1664, PACKPP(6, 5, 3) },
351 { 1792, PACKPP(6, 5, 4) }, { 2048, PACKPP(6, 5, 5) },
352 { 0, 0 } };
353
354 static void pm2fb_detect(void);
355 static int pm2fb_encode_fix(struct fb_fix_screeninfo* fix,
356 const void* par, struct fb_info_gen* info);
357 static int pm2fb_decode_var(const struct fb_var_screeninfo* var,
358 void* par, struct fb_info_gen* info);
359 static int pm2fb_encode_var(struct fb_var_screeninfo* var,
360 const void* par, struct fb_info_gen* info);
361 static void pm2fb_get_par(void* par, struct fb_info_gen* info);
362 static void pm2fb_set_par(const void* par, struct fb_info_gen* info);
363 static int pm2fb_getcolreg(unsigned regno,
364 unsigned* red, unsigned* green, unsigned* blue,
365 unsigned* transp, struct fb_info* info);
366 static int pm2fb_setcolreg(unsigned regno,
367 unsigned red, unsigned green, unsigned blue,
368 unsigned transp, struct fb_info* info);
369 static int pm2fb_blank(int blank_mode, struct fb_info_gen* info);
370 static int pm2fb_pan_display(const struct fb_var_screeninfo* var,
371 struct fb_info_gen* info);
372 static void pm2fb_set_disp(const void* par, struct display* disp,
373 struct fb_info_gen* info);
374
375 static struct fbgen_hwswitch pm2fb_hwswitch={
376 pm2fb_detect, pm2fb_encode_fix, pm2fb_decode_var,
377 pm2fb_encode_var, pm2fb_get_par, pm2fb_set_par,
378 pm2fb_getcolreg, pm2fb_setcolreg, pm2fb_pan_display,
379 pm2fb_blank, pm2fb_set_disp
380 };
381
382 static struct fb_ops pm2fb_ops={
383 owner: THIS_MODULE,
384 fb_get_fix: fbgen_get_fix,
385 fb_get_var: fbgen_get_var,
386 fb_set_var: fbgen_set_var,
387 fb_get_cmap: fbgen_get_cmap,
388 fb_set_cmap: fbgen_set_cmap,
389 fb_pan_display: fbgen_pan_display,
390 };
391
392 /***************************************************************************
393 * Begin of Permedia2 specific functions
394 ***************************************************************************/
395
RD32(unsigned char * base,s32 off)396 inline static u32 RD32(unsigned char* base, s32 off) {
397
398 return readl(base+off);
399 }
400
WR32(unsigned char * base,s32 off,u32 v)401 inline static void WR32(unsigned char* base, s32 off, u32 v) {
402
403 writel(v, base+off);
404 }
405
pm2_RD(struct pm2fb_info * p,s32 off)406 inline static u32 pm2_RD(struct pm2fb_info* p, s32 off) {
407
408 return RD32(p->regions.v_regs, off);
409 }
410
pm2_WR(struct pm2fb_info * p,s32 off,u32 v)411 inline static void pm2_WR(struct pm2fb_info* p, s32 off, u32 v) {
412
413 WR32(p->regions.v_regs, off, v);
414 }
415
pm2_RDAC_RD(struct pm2fb_info * p,s32 idx)416 inline static u32 pm2_RDAC_RD(struct pm2fb_info* p, s32 idx) {
417
418 int index = PM2R_RD_INDEXED_DATA;
419 switch (p->type) {
420 case PM2_TYPE_PERMEDIA2:
421 pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, idx);
422 break;
423 case PM2_TYPE_PERMEDIA2V:
424 pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff);
425 index = PM2VR_RD_INDEXED_DATA;
426 break;
427 }
428 DEFRW();
429 return pm2_RD(p, index);
430 }
431
pm2_RDAC_WR(struct pm2fb_info * p,s32 idx,u32 v)432 inline static void pm2_RDAC_WR(struct pm2fb_info* p, s32 idx,
433 u32 v) {
434
435 int index = PM2R_RD_INDEXED_DATA;
436 switch (p->type) {
437 case PM2_TYPE_PERMEDIA2:
438 pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, idx);
439 break;
440 case PM2_TYPE_PERMEDIA2V:
441 pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff);
442 index = PM2VR_RD_INDEXED_DATA;
443 break;
444 }
445 DEFRW();
446 pm2_WR(p, index, v);
447 }
448
pm2v_RDAC_RD(struct pm2fb_info * p,s32 idx)449 inline static u32 pm2v_RDAC_RD(struct pm2fb_info* p, s32 idx) {
450
451 pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff);
452 DEFRW();
453 return pm2_RD(p, PM2VR_RD_INDEXED_DATA);
454 }
455
pm2v_RDAC_WR(struct pm2fb_info * p,s32 idx,u32 v)456 inline static void pm2v_RDAC_WR(struct pm2fb_info* p, s32 idx,
457 u32 v) {
458
459 pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff);
460 DEFRW();
461 pm2_WR(p, PM2VR_RD_INDEXED_DATA, v);
462 }
463
464 #ifdef CONFIG_FB_PM2_FIFO_DISCONNECT
465 #define WAIT_FIFO(p,a)
466 #else
WAIT_FIFO(struct pm2fb_info * p,u32 a)467 inline static void WAIT_FIFO(struct pm2fb_info* p, u32 a) {
468
469 while(pm2_RD(p, PM2R_IN_FIFO_SPACE)<a);
470 DEFRW();
471 }
472 #endif
473
partprod(u32 xres)474 static u32 partprod(u32 xres) {
475 int i;
476
477 for (i=0; pp_table[i].width && pp_table[i].width!=xres; i++);
478 if (!pp_table[i].width)
479 DPRINTK("invalid width %u\n", xres);
480 return pp_table[i].pp;
481 }
482
to3264(u32 timing,int bpp,int is64)483 static u32 to3264(u32 timing, int bpp, int is64) {
484
485 switch (bpp) {
486 case 8:
487 timing=timing>>(2+is64);
488 break;
489 case 16:
490 timing=timing>>(1+is64);
491 break;
492 case 24:
493 timing=(timing*3)>>(2+is64);
494 break;
495 case 32:
496 if (is64)
497 timing=timing>>1;
498 break;
499 }
500 return timing;
501 }
502
from3264(u32 timing,int bpp,int is64)503 static u32 from3264(u32 timing, int bpp, int is64) {
504
505 switch (bpp) {
506 case 8:
507 timing=timing<<(2+is64);
508 break;
509 case 16:
510 timing=timing<<(1+is64);
511 break;
512 case 24:
513 timing=(timing<<(2+is64))/3;
514 break;
515 case 32:
516 if (is64)
517 timing=timing<<1;
518 break;
519 }
520 return timing;
521 }
522
pm2_mnp(u32 clk,unsigned char * mm,unsigned char * nn,unsigned char * pp)523 static void pm2_mnp(u32 clk, unsigned char* mm, unsigned char* nn,
524 unsigned char* pp) {
525 unsigned char m;
526 unsigned char n;
527 unsigned char p;
528 u32 f;
529 s32 curr;
530 s32 delta=100000;
531
532 *mm=*nn=*pp=0;
533 for (n=2; n<15; n++) {
534 for (m=2; m; m++) {
535 f=PM2_REFERENCE_CLOCK*m/n;
536 if (f>=150000 && f<=300000) {
537 for (p=0; p<5; p++, f>>=1) {
538 curr=clk>f?clk-f:f-clk;
539 if (curr<delta) {
540 delta=curr;
541 *mm=m;
542 *nn=n;
543 *pp=p;
544 }
545 }
546 }
547 }
548 }
549 }
550
pm2v_mnp(u32 clk,unsigned char * mm,unsigned char * nn,unsigned char * pp)551 static void pm2v_mnp(u32 clk, unsigned char* mm, unsigned char* nn,
552 unsigned char* pp) {
553 unsigned char m;
554 unsigned char n;
555 unsigned char p;
556 u32 f;
557 s32 delta=1000;
558
559 *mm=*nn=*pp=0;
560 for (n=1; n; n++) {
561 for (m=1; m; m++) {
562 for (p=0; p<2; p++) {
563 f=PM2_REFERENCE_CLOCK*n/(m * (1<<(p+1)));
564 if (clk>f-delta && clk<f+delta) {
565 delta=clk>f?clk-f:f-clk;
566 *mm=m;
567 *nn=n;
568 *pp=p;
569 }
570 }
571 }
572 }
573 }
574
wait_pm2(struct pm2fb_info * i)575 static void wait_pm2(struct pm2fb_info* i) {
576
577 WAIT_FIFO(i, 1);
578 pm2_WR(i, PM2R_SYNC, 0);
579 DEFRW();
580 do {
581 while (pm2_RD(i, PM2R_OUT_FIFO_WORDS)==0);
582 DEFR();
583 } while (pm2_RD(i, PM2R_OUT_FIFO)!=PM2TAG(PM2R_SYNC));
584 }
585
pm2_set_memclock(struct pm2fb_info * info,u32 clk)586 static void pm2_set_memclock(struct pm2fb_info* info, u32 clk) {
587 int i;
588 unsigned char m, n, p;
589
590 pm2_mnp(clk, &m, &n, &p);
591 WAIT_FIFO(info, 10);
592 pm2_RDAC_WR(info, PM2I_RD_MEMORY_CLOCK_3, 6);
593 DEFW();
594 pm2_RDAC_WR(info, PM2I_RD_MEMORY_CLOCK_1, m);
595 pm2_RDAC_WR(info, PM2I_RD_MEMORY_CLOCK_2, n);
596 DEFW();
597 pm2_RDAC_WR(info, PM2I_RD_MEMORY_CLOCK_3, 8|p);
598 DEFW();
599 pm2_RDAC_RD(info, PM2I_RD_MEMORY_CLOCK_STATUS);
600 DEFR();
601 for (i=256; i &&
602 !(pm2_RD(info, PM2R_RD_INDEXED_DATA)&PM2F_PLL_LOCKED); i--);
603 }
604
pm2_set_pixclock(struct pm2fb_info * info,u32 clk)605 static void pm2_set_pixclock(struct pm2fb_info* info, u32 clk) {
606 int i;
607 unsigned char m, n, p;
608
609 switch (info->type) {
610 case PM2_TYPE_PERMEDIA2:
611 pm2_mnp(clk, &m, &n, &p);
612 WAIT_FIFO(info, 10);
613 pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A3, 0);
614 DEFW();
615 pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A1, m);
616 pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A2, n);
617 DEFW();
618 pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A3, 8|p);
619 DEFW();
620 pm2_RDAC_RD(info, PM2I_RD_PIXEL_CLOCK_STATUS);
621 DEFR();
622 for (i=256; i &&
623 !(pm2_RD(info, PM2R_RD_INDEXED_DATA)&PM2F_PLL_LOCKED); i--);
624 break;
625 case PM2_TYPE_PERMEDIA2V:
626 pm2v_mnp(clk/2, &m, &n, &p);
627 WAIT_FIFO(info, 8);
628 pm2_WR(info, PM2VR_RD_INDEX_HIGH, PM2VI_RD_CLK0_PRESCALE >> 8);
629 pm2v_RDAC_WR(info, PM2VI_RD_CLK0_PRESCALE, m);
630 pm2v_RDAC_WR(info, PM2VI_RD_CLK0_FEEDBACK, n);
631 pm2v_RDAC_WR(info, PM2VI_RD_CLK0_POSTSCALE, p);
632 pm2_WR(info, PM2VR_RD_INDEX_HIGH, 0);
633 break;
634 }
635 }
636
clear_palette(struct pm2fb_info * p)637 static void clear_palette(struct pm2fb_info* p) {
638 int i=256;
639
640 WAIT_FIFO(p, 1);
641 pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, 0);
642 DEFW();
643 while (i--) {
644 WAIT_FIFO(p, 3);
645 pm2_WR(p, PM2R_RD_PALETTE_DATA, 0);
646 pm2_WR(p, PM2R_RD_PALETTE_DATA, 0);
647 pm2_WR(p, PM2R_RD_PALETTE_DATA, 0);
648 }
649 }
650
set_color(struct pm2fb_info * p,unsigned char regno,unsigned char r,unsigned char g,unsigned char b)651 static void set_color(struct pm2fb_info* p, unsigned char regno,
652 unsigned char r, unsigned char g, unsigned char b) {
653
654 WAIT_FIFO(p, 4);
655 pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, regno);
656 DEFW();
657 pm2_WR(p, PM2R_RD_PALETTE_DATA, r);
658 DEFW();
659 pm2_WR(p, PM2R_RD_PALETTE_DATA, g);
660 DEFW();
661 pm2_WR(p, PM2R_RD_PALETTE_DATA, b);
662 }
663
set_aperture(struct pm2fb_info * i,struct pm2fb_par * p)664 static void set_aperture(struct pm2fb_info* i, struct pm2fb_par* p) {
665
666 WAIT_FIFO(i, 2);
667 #ifdef __LITTLE_ENDIAN
668 pm2_WR(i, PM2R_APERTURE_ONE, 0);
669 pm2_WR(i, PM2R_APERTURE_TWO, 0);
670 #else
671 switch (p->depth) {
672 case 8:
673 case 24:
674 pm2_WR(i, PM2R_APERTURE_ONE, 0);
675 pm2_WR(i, PM2R_APERTURE_TWO, 1);
676 break;
677 case 16:
678 pm2_WR(i, PM2R_APERTURE_ONE, 2);
679 pm2_WR(i, PM2R_APERTURE_TWO, 1);
680 break;
681 case 32:
682 pm2_WR(i, PM2R_APERTURE_ONE, 1);
683 pm2_WR(i, PM2R_APERTURE_TWO, 1);
684 break;
685 }
686 #endif
687 }
688
set_screen(struct pm2fb_info * i,struct pm2fb_par * p)689 static void set_screen(struct pm2fb_info* i, struct pm2fb_par* p) {
690 u32 clrmode=0;
691 u32 txtmap=0;
692 u32 pixsize=0;
693 u32 clrformat=0;
694 u32 xres;
695 u32 video, tmp;
696
697 if (i->type == PM2_TYPE_PERMEDIA2V) {
698 WAIT_FIFO(i, 1);
699 pm2_WR(i, PM2VR_RD_INDEX_HIGH, 0);
700 }
701 xres=(p->width+31)&~31;
702 set_aperture(i, p);
703 DEFRW();
704 WAIT_FIFO(i, 27);
705 pm2_RDAC_WR(i, PM2I_RD_COLOR_KEY_CONTROL, p->depth==8?0:
706 PM2F_COLOR_KEY_TEST_OFF);
707 switch (p->depth) {
708 case 8:
709 pm2_WR(i, PM2R_FB_READ_PIXEL, 0);
710 clrformat=0x0e;
711 break;
712 case 16:
713 pm2_WR(i, PM2R_FB_READ_PIXEL, 1);
714 clrmode=PM2F_RD_TRUECOLOR|0x06;
715 txtmap=PM2F_TEXTEL_SIZE_16;
716 pixsize=1;
717 clrformat=0x70;
718 break;
719 case 32:
720 pm2_WR(i, PM2R_FB_READ_PIXEL, 2);
721 clrmode=PM2F_RD_TRUECOLOR|0x08;
722 txtmap=PM2F_TEXTEL_SIZE_32;
723 pixsize=2;
724 clrformat=0x20;
725 break;
726 case 24:
727 pm2_WR(i, PM2R_FB_READ_PIXEL, 4);
728 clrmode=PM2F_RD_TRUECOLOR|0x09;
729 txtmap=PM2F_TEXTEL_SIZE_24;
730 pixsize=4;
731 clrformat=0x20;
732 break;
733 }
734 pm2_WR(i, PM2R_SCREEN_SIZE, (p->height<<16)|p->width);
735 pm2_WR(i, PM2R_SCISSOR_MODE, PM2F_SCREEN_SCISSOR_ENABLE);
736 pm2_WR(i, PM2R_FB_WRITE_MODE, PM2F_FB_WRITE_ENABLE);
737 pm2_WR(i, PM2R_FB_READ_MODE, partprod(xres));
738 pm2_WR(i, PM2R_LB_READ_MODE, partprod(xres));
739 pm2_WR(i, PM2R_TEXTURE_MAP_FORMAT, txtmap|partprod(xres));
740 pm2_WR(i, PM2R_H_TOTAL, p->htotal);
741 pm2_WR(i, PM2R_HS_START, p->hsstart);
742 pm2_WR(i, PM2R_HS_END, p->hsend);
743 pm2_WR(i, PM2R_HG_END, p->hbend);
744 pm2_WR(i, PM2R_HB_END, p->hbend);
745 pm2_WR(i, PM2R_V_TOTAL, p->vtotal);
746 pm2_WR(i, PM2R_VS_START, p->vsstart);
747 pm2_WR(i, PM2R_VS_END, p->vsend);
748 pm2_WR(i, PM2R_VB_END, p->vbend);
749 pm2_WR(i, PM2R_SCREEN_STRIDE, p->stride);
750 DEFW();
751 pm2_WR(i, PM2R_SCREEN_BASE, p->base);
752 /* HW cursor needs /VSYNC for recognizing vert retrace */
753 video=p->video & ~(PM2F_HSYNC_ACT_LOW|PM2F_VSYNC_ACT_LOW);
754 video|=PM2F_HSYNC_ACT_HIGH|PM2F_VSYNC_ACT_HIGH;
755 switch (i->type) {
756 case PM2_TYPE_PERMEDIA2:
757 tmp = PM2F_RD_PALETTE_WIDTH_8;
758 pm2_RDAC_WR(i, PM2I_RD_COLOR_MODE, PM2F_RD_COLOR_MODE_RGB|
759 PM2F_RD_GUI_ACTIVE|clrmode);
760 if ((p->video & PM2F_HSYNC_ACT_LOW) == PM2F_HSYNC_ACT_LOW)
761 tmp |= 4; /* invert hsync */
762 if ((p->video & PM2F_HSYNC_ACT_LOW) == PM2F_HSYNC_ACT_LOW)
763 tmp |= 8; /* invert vsync */
764 pm2_RDAC_WR(i, PM2I_RD_MISC_CONTROL, tmp);
765 break;
766 case PM2_TYPE_PERMEDIA2V:
767 tmp = 0;
768 pm2v_RDAC_WR(i, PM2VI_RD_PIXEL_SIZE, pixsize);
769 pm2v_RDAC_WR(i, PM2VI_RD_COLOR_FORMAT, clrformat);
770 if ((p->video & PM2F_HSYNC_ACT_LOW) == PM2F_HSYNC_ACT_LOW)
771 tmp |= 1; /* invert hsync */
772 if ((p->video & PM2F_HSYNC_ACT_LOW) == PM2F_HSYNC_ACT_LOW)
773 tmp |= 4; /* invert vsync */
774 pm2v_RDAC_WR(i, PM2VI_RD_SYNC_CONTROL, tmp);
775 pm2v_RDAC_WR(i, PM2VI_RD_MISC_CONTROL, 1);
776 break;
777 }
778 pm2_WR(i, PM2R_VIDEO_CONTROL, video);
779 pm2_set_pixclock(i, p->pixclock);
780 };
781
782 /*
783 * copy with packed pixels (8/16bpp only).
784 */
pm2fb_pp_copy(struct pm2fb_info * i,s32 xsrc,s32 ysrc,s32 x,s32 y,s32 w,s32 h)785 static void pm2fb_pp_copy(struct pm2fb_info* i, s32 xsrc, s32 ysrc,
786 s32 x, s32 y, s32 w, s32 h) {
787 s32 scale=i->current_par.depth==8?2:1;
788 s32 offset;
789
790 if (!w || !h)
791 return;
792 WAIT_FIFO(i, 7);
793 pm2_WR(i, PM2R_CONFIG, PM2F_CONFIG_FB_WRITE_ENABLE|
794 PM2F_CONFIG_FB_PACKED_DATA|
795 PM2F_CONFIG_FB_READ_SOURCE_ENABLE);
796 pm2_WR(i, PM2R_FB_PIXEL_OFFSET, 0);
797 pm2_WR(i, PM2R_FB_SOURCE_DELTA, ((ysrc-y)&0xfff)<<16|
798 ((xsrc-x)&0xfff));
799 offset=(x&0x3)-(xsrc&0x3);
800 pm2_WR(i, PM2R_RECTANGLE_ORIGIN, (y<<16)|(x>>scale));
801 pm2_WR(i, PM2R_RECTANGLE_SIZE, (h<<16)|((w+7)>>scale));
802 pm2_WR(i, PM2R_PACKED_DATA_LIMITS, (offset<<29)|(x<<16)|(x+w));
803 DEFW();
804 pm2_WR(i, PM2R_RENDER, PM2F_RENDER_RECTANGLE|
805 (x<xsrc?PM2F_INCREASE_X:0)|
806 (y<ysrc?PM2F_INCREASE_Y:0));
807 wait_pm2(i);
808 }
809
810 /*
811 * block operation. copy=0: rectangle fill, copy=1: rectangle copy.
812 */
pm2fb_block_op(struct pm2fb_info * i,int copy,s32 xsrc,s32 ysrc,s32 x,s32 y,s32 w,s32 h,u32 color)813 static void pm2fb_block_op(struct pm2fb_info* i, int copy,
814 s32 xsrc, s32 ysrc,
815 s32 x, s32 y, s32 w, s32 h,
816 u32 color) {
817
818 if (!w || !h)
819 return;
820 WAIT_FIFO(i, 6);
821 pm2_WR(i, PM2R_CONFIG, PM2F_CONFIG_FB_WRITE_ENABLE|
822 PM2F_CONFIG_FB_READ_SOURCE_ENABLE);
823 pm2_WR(i, PM2R_FB_PIXEL_OFFSET, 0);
824 if (copy)
825 pm2_WR(i, PM2R_FB_SOURCE_DELTA, ((ysrc-y)&0xfff)<<16|
826 ((xsrc-x)&0xfff));
827 else
828 pm2_WR(i, PM2R_FB_BLOCK_COLOR, color);
829 pm2_WR(i, PM2R_RECTANGLE_ORIGIN, (y<<16)|x);
830 pm2_WR(i, PM2R_RECTANGLE_SIZE, (h<<16)|w);
831 DEFW();
832 pm2_WR(i, PM2R_RENDER, PM2F_RENDER_RECTANGLE|
833 (x<xsrc?PM2F_INCREASE_X:0)|
834 (y<ysrc?PM2F_INCREASE_Y:0)|
835 (copy?0:PM2F_RENDER_FASTFILL));
836 wait_pm2(i);
837 }
838
839 /***************************************************************************
840 * Begin of generic initialization functions
841 ***************************************************************************/
842
pm2fb_reset(struct pm2fb_info * p)843 static void pm2fb_reset(struct pm2fb_info* p) {
844
845 if (p->type == PM2_TYPE_PERMEDIA2V)
846 pm2_WR(p, PM2VR_RD_INDEX_HIGH, 0);
847 pm2_WR(p, PM2R_RESET_STATUS, 0);
848 DEFRW();
849 while (pm2_RD(p, PM2R_RESET_STATUS)&PM2F_BEING_RESET);
850 DEFRW();
851 #ifdef CONFIG_FB_PM2_FIFO_DISCONNECT
852 DPRINTK("FIFO disconnect enabled\n");
853 pm2_WR(p, PM2R_FIFO_DISCON, 1);
854 DEFRW();
855 #endif
856 if (board_table[p->board].init)
857 board_table[p->board].init(p);
858 WAIT_FIFO(p, 48);
859 pm2_WR(p, PM2R_CHIP_CONFIG, pm2_RD(p, PM2R_CHIP_CONFIG)&
860 ~(PM2F_VGA_ENABLE|PM2F_VGA_FIXED));
861 pm2_WR(p, PM2R_BYPASS_WRITE_MASK, ~(0L));
862 pm2_WR(p, PM2R_FRAMEBUFFER_WRITE_MASK, ~(0L));
863 pm2_WR(p, PM2R_FIFO_CONTROL, 0);
864 pm2_WR(p, PM2R_FILTER_MODE, PM2F_SYNCHRONIZATION);
865 pm2_WR(p, PM2R_APERTURE_ONE, 0);
866 pm2_WR(p, PM2R_APERTURE_TWO, 0);
867 pm2_WR(p, PM2R_LB_READ_FORMAT, 0);
868 pm2_WR(p, PM2R_LB_WRITE_FORMAT, 0);
869 pm2_WR(p, PM2R_LB_READ_MODE, 0);
870 pm2_WR(p, PM2R_LB_SOURCE_OFFSET, 0);
871 pm2_WR(p, PM2R_FB_SOURCE_OFFSET, 0);
872 pm2_WR(p, PM2R_FB_PIXEL_OFFSET, 0);
873 pm2_WR(p, PM2R_WINDOW_ORIGIN, 0);
874 pm2_WR(p, PM2R_FB_WINDOW_BASE, 0);
875 pm2_WR(p, PM2R_LB_WINDOW_BASE, 0);
876 pm2_WR(p, PM2R_FB_SOFT_WRITE_MASK, ~(0L));
877 pm2_WR(p, PM2R_FB_HARD_WRITE_MASK, ~(0L));
878 pm2_WR(p, PM2R_FB_READ_PIXEL, 0);
879 pm2_WR(p, PM2R_DITHER_MODE, 0);
880 pm2_WR(p, PM2R_AREA_STIPPLE_MODE, 0);
881 pm2_WR(p, PM2R_DEPTH_MODE, 0);
882 pm2_WR(p, PM2R_STENCIL_MODE, 0);
883 pm2_WR(p, PM2R_TEXTURE_ADDRESS_MODE, 0);
884 pm2_WR(p, PM2R_TEXTURE_READ_MODE, 0);
885 pm2_WR(p, PM2R_TEXEL_LUT_MODE, 0);
886 pm2_WR(p, PM2R_YUV_MODE, 0);
887 pm2_WR(p, PM2R_COLOR_DDA_MODE, 0);
888 pm2_WR(p, PM2R_TEXTURE_COLOR_MODE, 0);
889 pm2_WR(p, PM2R_FOG_MODE, 0);
890 pm2_WR(p, PM2R_ALPHA_BLEND_MODE, 0);
891 pm2_WR(p, PM2R_LOGICAL_OP_MODE, 0);
892 pm2_WR(p, PM2R_STATISTICS_MODE, 0);
893 pm2_WR(p, PM2R_SCISSOR_MODE, 0);
894 switch (p->type) {
895 case PM2_TYPE_PERMEDIA2:
896 pm2_RDAC_WR(p, PM2I_RD_MODE_CONTROL, 0); /* no overlay */
897 pm2_RDAC_WR(p, PM2I_RD_CURSOR_CONTROL, 0);
898 pm2_RDAC_WR(p, PM2I_RD_MISC_CONTROL, PM2F_RD_PALETTE_WIDTH_8);
899 break;
900 case PM2_TYPE_PERMEDIA2V:
901 pm2v_RDAC_WR(p, PM2VI_RD_MISC_CONTROL, 1); /* 8bit */
902 break;
903 }
904 pm2_RDAC_WR(p, PM2I_RD_COLOR_KEY_CONTROL, 0);
905 pm2_RDAC_WR(p, PM2I_RD_OVERLAY_KEY, 0);
906 pm2_RDAC_WR(p, PM2I_RD_RED_KEY, 0);
907 pm2_RDAC_WR(p, PM2I_RD_GREEN_KEY, 0);
908 pm2_RDAC_WR(p, PM2I_RD_BLUE_KEY, 0);
909 clear_palette(p);
910 if (p->memclock)
911 pm2_set_memclock(p, p->memclock);
912 }
913
pm2fb_conf(struct pm2fb_info * p)914 static int __init pm2fb_conf(struct pm2fb_info* p){
915
916 for (p->board=0; board_table[p->board].detect &&
917 !(board_table[p->board].detect(p)); p->board++);
918 if (!board_table[p->board].detect) {
919 DPRINTK("no board found.\n");
920 return 0;
921 }
922 DPRINTK("found board: %s\n", board_table[p->board].name);
923
924 p->regions.p_fb=p->regions.fb_base;
925 if (!request_mem_region(p->regions.p_fb, p->regions.fb_size,
926 "pm2fb")) {
927 printk (KERN_ERR "pm2fb: cannot reserve fb memory, abort\n");
928 return 0;
929 }
930 p->regions.v_fb=MMAP(p->regions.p_fb, p->regions.fb_size);
931
932 #ifndef PM2FB_BE_APERTURE
933 p->regions.p_regs=p->regions.rg_base;
934 #else
935 p->regions.p_regs=p->regions.rg_base+PM2_REGS_SIZE;
936 #endif
937 if (!request_mem_region(p->regions.p_regs, PM2_REGS_SIZE, "pm2fb")) {
938 printk (KERN_ERR "pm2fb: cannot reserve mmio memory, abort\n");
939 UNMAP(p->regions.v_fb, p->regions.fb_size);
940 return 0;
941 }
942 p->regions.v_regs=MMAP(p->regions.p_regs, PM2_REGS_SIZE);
943
944 #ifdef PM2FB_HW_CURSOR
945 p->cursor = pm2_init_cursor(p);
946 #endif
947 return 1;
948 }
949
950 /***************************************************************************
951 * Begin of per-board initialization functions
952 ***************************************************************************/
953
954 /*
955 * Phase5 CvisionPPC/BVisionPPC
956 */
957 #ifdef CONFIG_FB_PM2_CVPPC
cvppc_PCI_init(struct cvppc_par * p)958 static int cvppc_PCI_init(struct cvppc_par* p) {
959 extern u32 powerup_PCI_present;
960
961 if (!powerup_PCI_present) {
962 DPRINTK("no PCI bridge detected\n");
963 return 0;
964 }
965 if (!(p->pci_config=MMAP(CVPPC_PCI_CONFIG, 256))) {
966 DPRINTK("unable to map PCI config region\n");
967 return 0;
968 }
969 if (RD32(p->pci_config, PCI_VENDOR_ID)!=
970 ((PCI_DEVICE_ID_TI_TVP4020<<16)|PCI_VENDOR_ID_TI)) {
971 DPRINTK("bad vendorID/deviceID\n");
972 return 0;
973 }
974 if (!(p->pci_bridge=MMAP(CSPPC_PCI_BRIDGE, 256))) {
975 DPRINTK("unable to map PCI bridge\n");
976 return 0;
977 }
978 WR32(p->pci_bridge, CSPPC_BRIDGE_ENDIAN, CSPPCF_BRIDGE_BIG_ENDIAN);
979 DEFW();
980 if (pm2fb_options.flags & OPTF_OLD_MEM)
981 WR32(p->pci_config, PCI_CACHE_LINE_SIZE, 0xff00);
982 WR32(p->pci_config, PCI_BASE_ADDRESS_0, CVPPC_REGS_REGION);
983 WR32(p->pci_config, PCI_BASE_ADDRESS_1, CVPPC_FB_APERTURE_ONE);
984 WR32(p->pci_config, PCI_BASE_ADDRESS_2, CVPPC_FB_APERTURE_TWO);
985 WR32(p->pci_config, PCI_ROM_ADDRESS, CVPPC_ROM_ADDRESS);
986 DEFW();
987 WR32(p->pci_config, PCI_COMMAND, 0xef000000 |
988 PCI_COMMAND_IO |
989 PCI_COMMAND_MEMORY |
990 PCI_COMMAND_MASTER);
991 return 1;
992 }
993
cvppc_detect(struct pm2fb_info * p)994 static int __init cvppc_detect(struct pm2fb_info* p) {
995
996 if (!cvppc_PCI_init(&p->board_par.cvppc))
997 return 0;
998 p->type = PM2_TYPE_PERMEDIA2;
999 p->regions.fb_base=CVPPC_FB_APERTURE_ONE;
1000 p->regions.fb_size=CVPPC_FB_SIZE;
1001 p->regions.rg_base=CVPPC_REGS_REGION;
1002 p->memclock=CVPPC_MEMCLOCK;
1003 return 1;
1004 }
1005
cvppc_init(struct pm2fb_info * p)1006 static void cvppc_init(struct pm2fb_info* p) {
1007
1008 WAIT_FIFO(p, 3);
1009 pm2_WR(p, PM2R_MEM_CONTROL, 0);
1010 pm2_WR(p, PM2R_BOOT_ADDRESS, 0x30);
1011 DEFW();
1012 if (pm2fb_options.flags & OPTF_OLD_MEM)
1013 pm2_WR(p, PM2R_MEM_CONFIG, CVPPC_MEM_CONFIG_OLD);
1014 else
1015 pm2_WR(p, PM2R_MEM_CONFIG, CVPPC_MEM_CONFIG_NEW);
1016 }
1017 #endif /* CONFIG_FB_PM2_CVPPC */
1018
1019 /*
1020 * Generic PCI detection routines
1021 */
1022 #ifdef CONFIG_FB_PM2_PCI
1023 struct {
1024 unsigned short vendor, device;
1025 char *name;
1026 pm2type_t type;
1027 } pm2pci_cards[] __initdata = {
1028 { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TVP4020, "Texas Instruments TVP4020", PM2_TYPE_PERMEDIA2 },
1029 { PCI_VENDOR_ID_3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2, "3dLabs Permedia 2", PM2_TYPE_PERMEDIA2 },
1030 { PCI_VENDOR_ID_3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2V, "3dLabs Permedia 2v", PM2_TYPE_PERMEDIA2V },
1031 { 0, 0 }
1032 };
1033
pm2pci_detect(struct pm2fb_info * p)1034 static int __init pm2pci_detect(struct pm2fb_info* p) {
1035 struct pm2pci_par* pci=&p->board_par.pci;
1036 struct pci_dev* dev;
1037 int i;
1038 unsigned char* m;
1039 #ifdef __sparc__
1040 struct pcidev_cookie *pcp;
1041 #endif
1042
1043 memset(pci, 0, sizeof(struct pm2pci_par));
1044 if (!pci_present()) {
1045 DPRINTK("no PCI bus found.\n");
1046 return 0;
1047 }
1048 DPRINTK("scanning PCI bus for known chipsets...\n");
1049
1050 pci_for_each_dev(dev) {
1051 for (i = 0; pm2pci_cards[i].vendor; i++)
1052 if (pm2pci_cards[i].vendor == dev->vendor &&
1053 pm2pci_cards[i].device == dev->device) {
1054 pci->dev = dev;
1055 p->type = pm2pci_cards[i].type;
1056 DPRINTK("... found %s\n", pm2pci_cards[i].name);
1057 break;
1058 }
1059 if (pci->dev)
1060 break;
1061 }
1062 if (!pci->dev) {
1063 DPRINTK("no PCI board found.\n");
1064 return 0;
1065 }
1066 DPRINTK("PCI board @%08lx %08lx %08lx rom %08lx\n",
1067 pci->dev->resource[0].start,
1068 pci->dev->resource[1].start,
1069 pci->dev->resource[2].start,
1070 pci->dev->resource[PCI_ROM_RESOURCE].start);
1071 #ifdef __sparc__
1072 p->regions.rg_base= pci->dev->resource[0].start;
1073 p->regions.fb_base= pci->dev->resource[1].start;
1074 pcp = pci->dev->sysdata;
1075 /* If the user has not asked for a particular mode, lets guess */
1076 if (pcp->prom_node && !(pm2fb_options.flags & OPTF_USER)) {
1077 char timing[256], *q, *r;
1078 unsigned long w, h;
1079 int i;
1080 prom_getstring(pcp->prom_node, "timing-numbers", timing, 256);
1081 /* FIXME: Find out what the actual pixclock is and other values as well */
1082 if (timing[0]) {
1083 w = simple_strtoul(timing, &q, 0);
1084 h = 0;
1085 if (q == timing) w = 0;
1086 if (w) {
1087 for (i = 0; i < 3; i++) {
1088 for (r = q; *r && (*r < '0' || *r > '9'); r++);
1089 simple_strtoul(r, &q, 0);
1090 if (r == q) break;
1091 }
1092 if (i < 3) w = 0;
1093 }
1094 if (w) {
1095 for (r = q; *r && (*r < '0' || *r > '9'); r++);
1096 h = simple_strtoul(r, &q, 0);
1097 if (r == q) w = 0;
1098 }
1099 if (w == 640 && h == 480) w = 0;
1100 if (w) {
1101 for (i=0; user_mode[i].name[0] &&
1102 (w != user_mode[i].par.width ||
1103 h != user_mode[i].par.height); i++);
1104 if (user_mode[i].name[0])
1105 memcpy(&p->current_par, &user_mode[i].par, sizeof(user_mode[i].par));
1106 }
1107 }
1108 }
1109 #else
1110 if (pm2fb_options.flags & OPTF_VIRTUAL) {
1111 p->regions.rg_base = __pa(pci_resource_start(pci->dev, 0));
1112 p->regions.fb_base = __pa(pci_resource_start(pci->dev, 1));
1113 }
1114 else {
1115 p->regions.rg_base = pci_resource_start(pci->dev, 0);
1116 p->regions.fb_base = pci_resource_start(pci->dev, 1);
1117 }
1118 #endif
1119 #ifdef PM2FB_BE_APERTURE
1120 p->regions.rg_base += PM2_REGS_SIZE;
1121 #endif
1122 if ((m=MMAP(p->regions.rg_base, PM2_REGS_SIZE))) {
1123 pci->mem_control=RD32(m, PM2R_MEM_CONTROL);
1124 pci->boot_address=RD32(m, PM2R_BOOT_ADDRESS);
1125 pci->mem_config=RD32(m, PM2R_MEM_CONFIG);
1126 switch (pci->mem_config & PM2F_MEM_CONFIG_RAM_MASK) {
1127 case PM2F_MEM_BANKS_1:
1128 p->regions.fb_size=0x200000;
1129 break;
1130 case PM2F_MEM_BANKS_2:
1131 p->regions.fb_size=0x400000;
1132 break;
1133 case PM2F_MEM_BANKS_3:
1134 p->regions.fb_size=0x600000;
1135 break;
1136 case PM2F_MEM_BANKS_4:
1137 p->regions.fb_size=0x800000;
1138 break;
1139 }
1140 p->memclock=CVPPC_MEMCLOCK;
1141 UNMAP(m, PM2_REGS_SIZE);
1142 return 1;
1143 }
1144 DPRINTK("MMAP() failed.\n");
1145 return 0;
1146 }
1147
pm2pci_init(struct pm2fb_info * p)1148 static void pm2pci_init(struct pm2fb_info* p) {
1149 struct pm2pci_par* pci=&p->board_par.pci;
1150
1151 WAIT_FIFO(p, 3);
1152 pm2_WR(p, PM2R_MEM_CONTROL, pci->mem_control);
1153 pm2_WR(p, PM2R_BOOT_ADDRESS, pci->boot_address);
1154 DEFW();
1155 pm2_WR(p, PM2R_MEM_CONFIG, pci->mem_config);
1156 }
1157 #endif /* CONFIG_FB_PM2_PCI */
1158
1159 /***************************************************************************
1160 * Console hw acceleration
1161 ***************************************************************************/
1162
1163
pm2fb_blank(int blank_mode,struct fb_info_gen * info)1164 static int pm2fb_blank(int blank_mode, struct fb_info_gen* info) {
1165 struct pm2fb_info* i=(struct pm2fb_info* )info;
1166 u32 video;
1167
1168 if (!i->current_par_valid)
1169 return 1;
1170 video=i->current_par.video;
1171 if (blank_mode>0) {
1172 switch (blank_mode-1) {
1173 case VESA_NO_BLANKING: /* FIXME */
1174 video=video&~(PM2F_VIDEO_ENABLE);
1175 break;
1176 case VESA_HSYNC_SUSPEND:
1177 video=video&~(PM2F_HSYNC_MASK|
1178 PM2F_BLANK_LOW);
1179 break;
1180 case VESA_VSYNC_SUSPEND:
1181 video=video&~(PM2F_VSYNC_MASK|
1182 PM2F_BLANK_LOW);
1183 break;
1184 case VESA_POWERDOWN:
1185 video=video&~(PM2F_VSYNC_MASK|
1186 PM2F_HSYNC_MASK|
1187 PM2F_BLANK_LOW);
1188 break;
1189 }
1190 }
1191 WAIT_FIFO(i, 1);
1192 pm2_WR(i, PM2R_VIDEO_CONTROL, video);
1193 return 0;
1194 }
1195
pm2fb_pan_display(const struct fb_var_screeninfo * var,struct fb_info_gen * info)1196 static int pm2fb_pan_display(const struct fb_var_screeninfo* var,
1197 struct fb_info_gen* info) {
1198 struct pm2fb_info* i=(struct pm2fb_info* )info;
1199
1200 if (!i->current_par_valid)
1201 return -EINVAL;
1202 i->current_par.base=to3264(var->yoffset*i->current_par.width+
1203 var->xoffset, i->current_par.depth, 1);
1204 WAIT_FIFO(i, 1);
1205 pm2_WR(i, PM2R_SCREEN_BASE, i->current_par.base);
1206 return 0;
1207 }
1208
pm2fb_pp_bmove(struct display * p,int sy,int sx,int dy,int dx,int height,int width)1209 static void pm2fb_pp_bmove(struct display* p, int sy, int sx,
1210 int dy, int dx, int height, int width) {
1211
1212 if (fontwidthlog(p)) {
1213 sx=sx<<fontwidthlog(p);
1214 dx=dx<<fontwidthlog(p);
1215 width=width<<fontwidthlog(p);
1216 }
1217 else {
1218 sx=sx*fontwidth(p);
1219 dx=dx*fontwidth(p);
1220 width=width*fontwidth(p);
1221 }
1222 sy=sy*fontheight(p);
1223 dy=dy*fontheight(p);
1224 height=height*fontheight(p);
1225 pm2fb_pp_copy((struct pm2fb_info* )p->fb_info, sx, sy, dx,
1226 dy, width, height);
1227 }
1228
pm2fb_bmove(struct display * p,int sy,int sx,int dy,int dx,int height,int width)1229 static void pm2fb_bmove(struct display* p, int sy, int sx,
1230 int dy, int dx, int height, int width) {
1231
1232 if (fontwidthlog(p)) {
1233 sx=sx<<fontwidthlog(p);
1234 dx=dx<<fontwidthlog(p);
1235 width=width<<fontwidthlog(p);
1236 }
1237 else {
1238 sx=sx*fontwidth(p);
1239 dx=dx*fontwidth(p);
1240 width=width*fontwidth(p);
1241 }
1242 sy=sy*fontheight(p);
1243 dy=dy*fontheight(p);
1244 height=height*fontheight(p);
1245 pm2fb_block_op((struct pm2fb_info* )p->fb_info, 1, sx, sy, dx, dy,
1246 width, height, 0);
1247 }
1248
1249 #ifdef FBCON_HAS_CFB8
pm2fb_clear8(struct vc_data * conp,struct display * p,int sy,int sx,int height,int width)1250 static void pm2fb_clear8(struct vc_data* conp, struct display* p,
1251 int sy, int sx, int height, int width) {
1252 u32 c;
1253
1254 sx=sx*fontwidth(p);
1255 width=width*fontwidth(p);
1256 sy=sy*fontheight(p);
1257 height=height*fontheight(p);
1258 c=attr_bgcol_ec(p, conp);
1259 c|=c<<8;
1260 c|=c<<16;
1261 pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0, sx, sy,
1262 width, height, c);
1263 }
1264
pm2fb_clear_margins8(struct vc_data * conp,struct display * p,int bottom_only)1265 static void pm2fb_clear_margins8(struct vc_data* conp, struct display* p,
1266 int bottom_only) {
1267 u32 c;
1268 u32 sx;
1269 u32 sy;
1270
1271 c=attr_bgcol_ec(p, conp);
1272 c|=c<<8;
1273 c|=c<<16;
1274 sx=conp->vc_cols*fontwidth(p);
1275 sy=conp->vc_rows*fontheight(p);
1276 if (!bottom_only)
1277 pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0,
1278 sx, 0, (p->var.xres-sx), p->var.yres_virtual, c);
1279 pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0,
1280 0, p->var.yoffset+sy, sx, p->var.yres-sy, c);
1281 }
1282
1283 static struct display_switch pm2_cfb8 = {
1284 setup: fbcon_cfb8_setup,
1285 bmove: pm2fb_pp_bmove,
1286 #ifdef __alpha__
1287 /* Not sure why, but this works and the other does not. */
1288 /* Also, perhaps we need a separate routine to wait for the
1289 blitter to stop before doing this? */
1290 /* In addition, maybe we need to do this for 16 and 32 bit depths? */
1291 clear: fbcon_cfb8_clear,
1292 #else
1293 clear: pm2fb_clear8,
1294 #endif
1295 putc: fbcon_cfb8_putc,
1296 putcs: fbcon_cfb8_putcs,
1297 revc: fbcon_cfb8_revc,
1298 cursor: pm2fb_cursor,
1299 set_font: pm2fb_set_font,
1300 clear_margins: pm2fb_clear_margins8,
1301 fontwidthmask: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16) };
1302 #endif /* FBCON_HAS_CFB8 */
1303
1304 #ifdef FBCON_HAS_CFB16
pm2fb_clear16(struct vc_data * conp,struct display * p,int sy,int sx,int height,int width)1305 static void pm2fb_clear16(struct vc_data* conp, struct display* p,
1306 int sy, int sx, int height, int width) {
1307 u32 c;
1308
1309 sx=sx*fontwidth(p);
1310 width=width*fontwidth(p);
1311 sy=sy*fontheight(p);
1312 height=height*fontheight(p);
1313 c=((u16 *)p->dispsw_data)[attr_bgcol_ec(p, conp)];
1314 c|=c<<16;
1315 pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0, sx, sy,
1316 width, height, c);
1317 }
1318
pm2fb_clear_margins16(struct vc_data * conp,struct display * p,int bottom_only)1319 static void pm2fb_clear_margins16(struct vc_data* conp, struct display* p,
1320 int bottom_only) {
1321 u32 c;
1322 u32 sx;
1323 u32 sy;
1324
1325 c = ((u16 *)p->dispsw_data)[attr_bgcol_ec(p, conp)];
1326 c|=c<<16;
1327 sx=conp->vc_cols*fontwidth(p);
1328 sy=conp->vc_rows*fontheight(p);
1329 if (!bottom_only)
1330 pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0,
1331 sx, 0, (p->var.xres-sx), p->var.yres_virtual, c);
1332 pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0,
1333 0, p->var.yoffset+sy, sx, p->var.yres-sy, c);
1334 }
1335
1336 static struct display_switch pm2_cfb16 = {
1337 setup: fbcon_cfb16_setup,
1338 bmove: pm2fb_pp_bmove,
1339 clear: pm2fb_clear16,
1340 putc: fbcon_cfb16_putc,
1341 putcs: fbcon_cfb16_putcs,
1342 revc: fbcon_cfb16_revc,
1343 cursor: pm2fb_cursor,
1344 set_font: pm2fb_set_font,
1345 clear_margins: pm2fb_clear_margins16,
1346 fontwidthmask: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
1347 };
1348 #endif /* FBCON_HAS_CFB16 */
1349
1350 #ifdef FBCON_HAS_CFB24
1351 /*
1352 * fast fill for 24bpp works only when red==green==blue
1353 */
pm2fb_clear24(struct vc_data * conp,struct display * p,int sy,int sx,int height,int width)1354 static void pm2fb_clear24(struct vc_data* conp, struct display* p,
1355 int sy, int sx, int height, int width) {
1356 struct pm2fb_info* i=(struct pm2fb_info* )p->fb_info;
1357 u32 c;
1358
1359 c=attr_bgcol_ec(p, conp);
1360 if ( i->palette[c].red==i->palette[c].green &&
1361 i->palette[c].green==i->palette[c].blue) {
1362 c=((u32 *)p->dispsw_data)[c];
1363 c|=(c&0xff0000)<<8;
1364 sx=sx*fontwidth(p);
1365 width=width*fontwidth(p);
1366 sy=sy*fontheight(p);
1367 height=height*fontheight(p);
1368 pm2fb_block_op(i, 0, 0, 0, sx, sy, width, height, c);
1369 }
1370 else
1371 fbcon_cfb24_clear(conp, p, sy, sx, height, width);
1372
1373 }
1374
pm2fb_clear_margins24(struct vc_data * conp,struct display * p,int bottom_only)1375 static void pm2fb_clear_margins24(struct vc_data* conp, struct display* p,
1376 int bottom_only) {
1377 struct pm2fb_info* i=(struct pm2fb_info* )p->fb_info;
1378 u32 c;
1379 u32 sx;
1380 u32 sy;
1381
1382 c=attr_bgcol_ec(p, conp);
1383 if ( i->palette[c].red==i->palette[c].green &&
1384 i->palette[c].green==i->palette[c].blue) {
1385 c=((u32 *)p->dispsw_data)[c];
1386 c|=(c&0xff0000)<<8;
1387 sx=conp->vc_cols*fontwidth(p);
1388 sy=conp->vc_rows*fontheight(p);
1389 if (!bottom_only)
1390 pm2fb_block_op(i, 0, 0, 0, sx, 0, (p->var.xres-sx),
1391 p->var.yres_virtual, c);
1392 pm2fb_block_op(i, 0, 0, 0, 0, p->var.yoffset+sy,
1393 sx, p->var.yres-sy, c);
1394 }
1395 else
1396 fbcon_cfb24_clear_margins(conp, p, bottom_only);
1397
1398 }
1399
1400 static struct display_switch pm2_cfb24 = {
1401 setup: fbcon_cfb24_setup,
1402 bmove: pm2fb_bmove,
1403 clear: pm2fb_clear24,
1404 putc: fbcon_cfb24_putc,
1405 putcs: fbcon_cfb24_putcs,
1406 revc: fbcon_cfb24_revc,
1407 cursor: pm2fb_cursor,
1408 set_font: pm2fb_set_font,
1409 clear_margins: pm2fb_clear_margins24,
1410 fontwidthmask: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
1411 };
1412 #endif /* FBCON_HAS_CFB24 */
1413
1414 #ifdef FBCON_HAS_CFB32
pm2fb_clear32(struct vc_data * conp,struct display * p,int sy,int sx,int height,int width)1415 static void pm2fb_clear32(struct vc_data* conp, struct display* p,
1416 int sy, int sx, int height, int width) {
1417 u32 c;
1418
1419 sx=sx*fontwidth(p);
1420 width=width*fontwidth(p);
1421 sy=sy*fontheight(p);
1422 height=height*fontheight(p);
1423 c=((u32 *)p->dispsw_data)[attr_bgcol_ec(p, conp)];
1424 pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0, sx, sy,
1425 width, height, c);
1426 }
1427
pm2fb_clear_margins32(struct vc_data * conp,struct display * p,int bottom_only)1428 static void pm2fb_clear_margins32(struct vc_data* conp, struct display* p,
1429 int bottom_only) {
1430 u32 c;
1431 u32 sx;
1432 u32 sy;
1433
1434 c = ((u32 *)p->dispsw_data)[attr_bgcol_ec(p, conp)];
1435 sx=conp->vc_cols*fontwidth(p);
1436 sy=conp->vc_rows*fontheight(p);
1437 if (!bottom_only)
1438 pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0,
1439 sx, 0, (p->var.xres-sx), p->var.yres_virtual, c);
1440 pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0,
1441 0, p->var.yoffset+sy, sx, p->var.yres-sy, c);
1442 }
1443
1444 static struct display_switch pm2_cfb32 = {
1445 setup: fbcon_cfb32_setup,
1446 bmove: pm2fb_bmove,
1447 clear: pm2fb_clear32,
1448 putc: fbcon_cfb32_putc,
1449 putcs: fbcon_cfb32_putcs,
1450 revc: fbcon_cfb32_revc,
1451 cursor: pm2fb_cursor,
1452 set_font: pm2fb_set_font,
1453 clear_margins: pm2fb_clear_margins32,
1454 fontwidthmask: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
1455 };
1456 #endif /* FBCON_HAS_CFB32 */
1457
1458 /***************************************************************************
1459 * Framebuffer functions
1460 ***************************************************************************/
1461
pm2fb_detect(void)1462 static void pm2fb_detect(void) {}
1463
pm2fb_encode_fix(struct fb_fix_screeninfo * fix,const void * par,struct fb_info_gen * info)1464 static int pm2fb_encode_fix(struct fb_fix_screeninfo* fix,
1465 const void* par, struct fb_info_gen* info) {
1466 struct pm2fb_info* i=(struct pm2fb_info* )info;
1467 struct pm2fb_par* p=(struct pm2fb_par* )par;
1468
1469 strcpy(fix->id, permedia2_name);
1470 fix->smem_start=i->regions.p_fb;
1471 fix->smem_len=i->regions.fb_size;
1472 fix->mmio_start=i->regions.p_regs;
1473 fix->mmio_len=PM2_REGS_SIZE;
1474 fix->accel=FB_ACCEL_3DLABS_PERMEDIA2;
1475 fix->type=FB_TYPE_PACKED_PIXELS;
1476 fix->visual=p->depth==8?FB_VISUAL_PSEUDOCOLOR:FB_VISUAL_TRUECOLOR;
1477 if (i->current_par_valid)
1478 fix->line_length=i->current_par.width*(i->current_par.depth/8);
1479 else
1480 fix->line_length=0;
1481 fix->xpanstep=p->depth==24?8:64/p->depth;
1482 fix->ypanstep=1;
1483 fix->ywrapstep=0;
1484 return 0;
1485 }
1486
1487 #ifdef PM2FB_MASTER_DEBUG
pm2fb_display_var(const struct fb_var_screeninfo * var)1488 static void pm2fb_display_var(const struct fb_var_screeninfo* var) {
1489
1490 printk( KERN_DEBUG
1491 "- struct fb_var_screeninfo ---------------------------------------------------\n");
1492 printk( KERN_DEBUG
1493 "resolution: %ux%ux%u (virtual %ux%u+%u+%u)\n",
1494 var->xres, var->yres, var->bits_per_pixel,
1495 var->xres_virtual, var->yres_virtual,
1496 var->xoffset, var->yoffset);
1497 printk( KERN_DEBUG
1498 "color: %c%c "
1499 "R(%u,%u,%u), G(%u,%u,%u), B(%u,%u,%u), T(%u,%u,%u)\n",
1500 var->grayscale?'G':'C', var->nonstd?'N':'S',
1501 var->red.offset, var->red.length, var->red.msb_right,
1502 var->green.offset, var->green.length, var->green.msb_right,
1503 var->blue.offset, var->blue.length, var->blue.msb_right,
1504 var->transp.offset, var->transp.length,
1505 var->transp.msb_right);
1506 printk( KERN_DEBUG
1507 "timings: %ups (%u,%u)-(%u,%u)+%u+%u\n",
1508 var->pixclock,
1509 var->left_margin, var->upper_margin, var->right_margin,
1510 var->lower_margin, var->hsync_len, var->vsync_len);
1511 printk( KERN_DEBUG
1512 "activate %08x accel_flags %08x sync %08x vmode %08x\n",
1513 var->activate, var->accel_flags, var->sync, var->vmode);
1514 printk( KERN_DEBUG
1515 "------------------------------------------------------------------------------\n");
1516 }
1517
1518 #define pm2fb_decode_var pm2fb_wrapped_decode_var
1519 #endif
1520
pm2fb_decode_var(const struct fb_var_screeninfo * var,void * par,struct fb_info_gen * info)1521 static int pm2fb_decode_var(const struct fb_var_screeninfo* var,
1522 void* par, struct fb_info_gen* info) {
1523 struct pm2fb_info* i=(struct pm2fb_info* )info;
1524 struct pm2fb_par p;
1525 u32 xres;
1526 int data64;
1527
1528 memset(&p, 0, sizeof(struct pm2fb_par));
1529 p.width=(var->xres_virtual+7)&~7;
1530 p.height=var->yres_virtual;
1531 p.depth=(var->bits_per_pixel+7)&~7;
1532 p.depth=p.depth>32?32:p.depth;
1533 data64=p.depth>8 || i->type == PM2_TYPE_PERMEDIA2V;
1534 xres=(var->xres+31)&~31;
1535 if (p.width<xres+var->xoffset)
1536 p.width=xres+var->xoffset;
1537 if (p.height<var->yres+var->yoffset)
1538 p.height=var->yres+var->yoffset;
1539 if (!partprod(xres)) {
1540 DPRINTK("width not supported: %u\n", xres);
1541 return -EINVAL;
1542 }
1543 if (p.width>2047) {
1544 DPRINTK("virtual width not supported: %u\n", p.width);
1545 return -EINVAL;
1546 }
1547 if (var->yres<200) {
1548 DPRINTK("height not supported: %u\n",
1549 (u32 )var->yres);
1550 return -EINVAL;
1551 }
1552 if (p.height<200 || p.height>2047) {
1553 DPRINTK("virtual height not supported: %u\n", p.height);
1554 return -EINVAL;
1555 }
1556 if (p.depth>32) {
1557 DPRINTK("depth not supported: %u\n", p.depth);
1558 return -EINVAL;
1559 }
1560 if (p.width*p.height*p.depth/8>i->regions.fb_size) {
1561 DPRINTK("no memory for screen (%ux%ux%u)\n",
1562 p.width, p.height, p.depth);
1563 return -EINVAL;
1564 }
1565 p.pixclock=PICOS2KHZ(var->pixclock);
1566 if (p.pixclock>PM2_MAX_PIXCLOCK) {
1567 DPRINTK("pixclock too high (%uKHz)\n", p.pixclock);
1568 return -EINVAL;
1569 }
1570 p.hsstart=to3264(var->right_margin, p.depth, data64);
1571 p.hsend=p.hsstart+to3264(var->hsync_len, p.depth, data64);
1572 p.hbend=p.hsend+to3264(var->left_margin, p.depth, data64);
1573 p.htotal=to3264(xres, p.depth, data64)+p.hbend-1;
1574 p.vsstart=var->lower_margin?var->lower_margin-1:0; /* FIXME! */
1575 p.vsend=var->lower_margin+var->vsync_len-1;
1576 p.vbend=var->lower_margin+var->vsync_len+var->upper_margin;
1577 p.vtotal=var->yres+p.vbend-1;
1578 p.stride=to3264(p.width, p.depth, 1);
1579 p.base=to3264(var->yoffset*xres+var->xoffset, p.depth, 1);
1580 if (data64)
1581 p.video|=PM2F_DATA_64_ENABLE;
1582 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
1583 p.video|=PM2F_HSYNC_ACT_HIGH;
1584 else
1585 p.video|=PM2F_HSYNC_ACT_LOW;
1586 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
1587 p.video|=PM2F_VSYNC_ACT_HIGH;
1588 else
1589 p.video|=PM2F_VSYNC_ACT_LOW;
1590 if ((var->vmode & FB_VMODE_MASK)==FB_VMODE_INTERLACED) {
1591 DPRINTK("interlaced not supported\n");
1592 return -EINVAL;
1593 }
1594 if ((var->vmode & FB_VMODE_MASK)==FB_VMODE_DOUBLE)
1595 p.video|=PM2F_LINE_DOUBLE;
1596 if (var->activate==FB_ACTIVATE_NOW)
1597 p.video|=PM2F_VIDEO_ENABLE;
1598 *((struct pm2fb_par* )par)=p;
1599 return 0;
1600 }
1601
1602 #ifdef PM2FB_MASTER_DEBUG
1603 #undef pm2fb_decode_var
1604
pm2fb_decode_var(const struct fb_var_screeninfo * var,void * par,struct fb_info_gen * info)1605 static int pm2fb_decode_var(const struct fb_var_screeninfo* var,
1606 void* par, struct fb_info_gen* info) {
1607 int result;
1608
1609 result=pm2fb_wrapped_decode_var(var, par, info);
1610 pm2fb_display_var(var);
1611 return result;
1612 }
1613 #endif
1614
pm2fb_encode_var(struct fb_var_screeninfo * var,const void * par,struct fb_info_gen * info)1615 static int pm2fb_encode_var(struct fb_var_screeninfo* var,
1616 const void* par, struct fb_info_gen* info) {
1617 struct pm2fb_par* p=(struct pm2fb_par* )par;
1618 struct fb_var_screeninfo v;
1619 u32 base;
1620
1621 memset(&v, 0, sizeof(struct fb_var_screeninfo));
1622 v.xres_virtual=p->width;
1623 v.yres_virtual=p->height;
1624 v.xres=(p->htotal+1)-p->hbend;
1625 v.yres=(p->vtotal+1)-p->vbend;
1626 v.right_margin=p->hsstart;
1627 v.hsync_len=p->hsend-p->hsstart;
1628 v.left_margin=p->hbend-p->hsend;
1629 v.lower_margin=p->vsstart+1;
1630 v.vsync_len=p->vsend-v.lower_margin+1;
1631 v.upper_margin=p->vbend-v.lower_margin-v.vsync_len;
1632 v.bits_per_pixel=p->depth;
1633 if (p->video & PM2F_DATA_64_ENABLE) {
1634 v.xres=v.xres<<1;
1635 v.right_margin=v.right_margin<<1;
1636 v.hsync_len=v.hsync_len<<1;
1637 v.left_margin=v.left_margin<<1;
1638 }
1639 switch (p->depth) {
1640 case 8:
1641 v.red.length=v.green.length=v.blue.length=8;
1642 v.xres=v.xres<<2;
1643 v.right_margin=v.right_margin<<2;
1644 v.hsync_len=v.hsync_len<<2;
1645 v.left_margin=v.left_margin<<2;
1646 break;
1647 case 16:
1648 v.red.offset=11;
1649 v.red.length=5;
1650 v.green.offset=5;
1651 v.green.length=6;
1652 v.blue.length=5;
1653 v.xres=v.xres<<1;
1654 v.right_margin=v.right_margin<<1;
1655 v.hsync_len=v.hsync_len<<1;
1656 v.left_margin=v.left_margin<<1;
1657 break;
1658 case 32:
1659 v.transp.offset=24;
1660 v.red.offset=16;
1661 v.green.offset=8;
1662 v.red.length=v.green.length=v.blue.length=
1663 v.transp.length=8;
1664 break;
1665 case 24:
1666 v.blue.offset=16;
1667 v.green.offset=8;
1668 v.red.length=v.green.length=v.blue.length=8;
1669 v.xres=(v.xres<<2)/3;
1670 v.right_margin=(v.right_margin<<2)/3;
1671 v.hsync_len=(v.hsync_len<<2)/3;
1672 v.left_margin=(v.left_margin<<2)/3;
1673 break;
1674 }
1675 base=from3264(p->base, p->depth, 1);
1676 v.xoffset=base%v.xres;
1677 v.yoffset=base/v.xres;
1678 v.height=v.width=-1;
1679 v.pixclock=KHZ2PICOS(p->pixclock);
1680 if ((p->video & PM2F_HSYNC_MASK)==PM2F_HSYNC_ACT_HIGH)
1681 v.sync|=FB_SYNC_HOR_HIGH_ACT;
1682 if ((p->video & PM2F_VSYNC_MASK)==PM2F_VSYNC_ACT_HIGH)
1683 v.sync|=FB_SYNC_VERT_HIGH_ACT;
1684 if (p->video & PM2F_LINE_DOUBLE)
1685 v.vmode=FB_VMODE_DOUBLE;
1686 *var=v;
1687 return 0;
1688 }
1689
set_user_mode(struct pm2fb_info * i)1690 static void set_user_mode(struct pm2fb_info* i) {
1691
1692 if (pm2fb_options.flags & OPTF_YPAN) {
1693 int h = i->current_par.height;
1694 i->current_par.height=i->regions.fb_size/
1695 (i->current_par.width*i->current_par.depth/8);
1696 i->current_par.height=MIN(i->current_par.height,2047);
1697 i->current_par.height=MAX(i->current_par.height,h);
1698 }
1699 }
1700
pm2fb_get_par(void * par,struct fb_info_gen * info)1701 static void pm2fb_get_par(void* par, struct fb_info_gen* info) {
1702 struct pm2fb_info* i=(struct pm2fb_info* )info;
1703
1704 if (!i->current_par_valid) {
1705 set_user_mode(i);
1706 pm2fb_reset(i);
1707 set_screen(i, &i->current_par);
1708 i->current_par_valid=1;
1709 }
1710 *((struct pm2fb_par* )par)=i->current_par;
1711 }
1712
pm2fb_set_par(const void * par,struct fb_info_gen * info)1713 static void pm2fb_set_par(const void* par, struct fb_info_gen* info) {
1714 struct pm2fb_info* i=(struct pm2fb_info* )info;
1715 struct pm2fb_par* p;
1716
1717 p=(struct pm2fb_par* )par;
1718 if (i->current_par_valid) {
1719 i->current_par.base=p->base;
1720 if (!memcmp(p, &i->current_par, sizeof(struct pm2fb_par))) {
1721 WAIT_FIFO(i, 1);
1722 pm2_WR(i, PM2R_SCREEN_BASE, p->base);
1723 return;
1724 }
1725 }
1726 set_screen(i, p);
1727 i->current_par=*p;
1728 i->current_par_valid=1;
1729 #ifdef PM2FB_HW_CURSOR
1730 if (i->cursor) {
1731 pm2v_set_cursor_color(i, cursor_color_map, cursor_color_map, cursor_color_map);
1732 pm2v_set_cursor_shape(i);
1733 }
1734 #endif
1735 }
1736
pm2fb_getcolreg(unsigned regno,unsigned * red,unsigned * green,unsigned * blue,unsigned * transp,struct fb_info * info)1737 static int pm2fb_getcolreg(unsigned regno,
1738 unsigned* red, unsigned* green, unsigned* blue,
1739 unsigned* transp, struct fb_info* info) {
1740 struct pm2fb_info* i=(struct pm2fb_info* )info;
1741
1742 if (regno<256) {
1743 *red=i->palette[regno].red<<8|i->palette[regno].red;
1744 *green=i->palette[regno].green<<8|i->palette[regno].green;
1745 *blue=i->palette[regno].blue<<8|i->palette[regno].blue;
1746 *transp=i->palette[regno].transp<<8|i->palette[regno].transp;
1747 }
1748 return regno>255;
1749 }
1750
pm2fb_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info * info)1751 static int pm2fb_setcolreg(unsigned regno,
1752 unsigned red, unsigned green, unsigned blue,
1753 unsigned transp, struct fb_info* info) {
1754 struct pm2fb_info* i=(struct pm2fb_info* )info;
1755
1756 if (regno<16) {
1757 switch (i->current_par.depth) {
1758 #ifdef FBCON_HAS_CFB8
1759 case 8:
1760 break;
1761 #endif
1762 #ifdef FBCON_HAS_CFB16
1763 case 16:
1764 i->cmap.cmap16[regno]=
1765 ((u32 )red & 0xf800) |
1766 (((u32 )green & 0xfc00)>>5) |
1767 (((u32 )blue & 0xf800)>>11);
1768 break;
1769 #endif
1770 #ifdef FBCON_HAS_CFB24
1771 case 24:
1772 i->cmap.cmap24[regno]=
1773 (((u32 )blue & 0xff00) << 8) |
1774 ((u32 )green & 0xff00) |
1775 (((u32 )red & 0xff00) >> 8);
1776 break;
1777 #endif
1778 #ifdef FBCON_HAS_CFB32
1779 case 32:
1780 i->cmap.cmap32[regno]=
1781 (((u32 )transp & 0xff00) << 16) |
1782 (((u32 )red & 0xff00) << 8) |
1783 (((u32 )green & 0xff00)) |
1784 (((u32 )blue & 0xff00) >> 8);
1785 break;
1786 #endif
1787 default:
1788 DPRINTK("bad depth %u\n",
1789 i->current_par.depth);
1790 break;
1791 }
1792 }
1793 if (regno<256) {
1794 i->palette[regno].red=red >> 8;
1795 i->palette[regno].green=green >> 8;
1796 i->palette[regno].blue=blue >> 8;
1797 i->palette[regno].transp=transp >> 8;
1798 if (i->current_par.depth==8)
1799 set_color(i, regno, red>>8, green>>8, blue>>8);
1800 }
1801 return regno>255;
1802 }
1803
pm2fb_set_disp(const void * par,struct display * disp,struct fb_info_gen * info)1804 static void pm2fb_set_disp(const void* par, struct display* disp,
1805 struct fb_info_gen* info) {
1806 struct pm2fb_info* i=(struct pm2fb_info* )info;
1807 unsigned long flags;
1808 unsigned long depth;
1809
1810 save_flags(flags);
1811 cli();
1812 disp->screen_base = i->regions.v_fb;
1813 switch (depth=((struct pm2fb_par* )par)->depth) {
1814 #ifdef FBCON_HAS_CFB8
1815 case 8:
1816 disp->dispsw=&pm2_cfb8;
1817 break;
1818 #endif
1819 #ifdef FBCON_HAS_CFB16
1820 case 16:
1821 disp->dispsw=&pm2_cfb16;
1822 disp->dispsw_data=i->cmap.cmap16;
1823 break;
1824 #endif
1825 #ifdef FBCON_HAS_CFB24
1826 case 24:
1827 disp->dispsw=&pm2_cfb24;
1828 disp->dispsw_data=i->cmap.cmap24;
1829 break;
1830 #endif
1831 #ifdef FBCON_HAS_CFB32
1832 case 32:
1833 disp->dispsw=&pm2_cfb32;
1834 disp->dispsw_data=i->cmap.cmap32;
1835 break;
1836 #endif
1837 default:
1838 disp->dispsw=&fbcon_dummy;
1839 break;
1840 }
1841 restore_flags(flags);
1842 }
1843
1844 #ifdef PM2FB_HW_CURSOR
1845 /***************************************************************************
1846 * Hardware cursor support
1847 ***************************************************************************/
1848
1849 static u8 cursor_bits_lookup[16] = {
1850 0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54,
1851 0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55
1852 };
1853
1854 static u8 cursor_mask_lookup[16] = {
1855 0x00, 0x80, 0x20, 0xa0, 0x08, 0x88, 0x28, 0xa8,
1856 0x02, 0x82, 0x22, 0xa2, 0x0a, 0x8a, 0x2a, 0xaa
1857 };
1858
pm2v_set_cursor_color(struct pm2fb_info * fb,u8 * red,u8 * green,u8 * blue)1859 static void pm2v_set_cursor_color(struct pm2fb_info *fb, u8 *red, u8 *green, u8 *blue)
1860 {
1861 struct pm2_cursor *c = fb->cursor;
1862 int i;
1863
1864 for (i = 0; i < 2; i++) {
1865 c->color[3*i] = red[i];
1866 c->color[3*i+1] = green[i];
1867 c->color[3*i+2] = blue[i];
1868 }
1869
1870 WAIT_FIFO(fb, 14);
1871 pm2_WR(fb, PM2VR_RD_INDEX_HIGH, PM2VI_RD_CURSOR_PALETTE >> 8);
1872 for (i = 0; i < 6; i++)
1873 pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PALETTE+i, c->color[i]);
1874 pm2_WR(fb, PM2VR_RD_INDEX_HIGH, 0);
1875 }
1876
pm2v_set_cursor_shape(struct pm2fb_info * fb)1877 static void pm2v_set_cursor_shape(struct pm2fb_info *fb)
1878 {
1879 struct pm2_cursor *c = fb->cursor;
1880 u8 m, b;
1881 int i, x, y;
1882
1883 WAIT_FIFO(fb, 1);
1884 pm2_WR(fb, PM2VR_RD_INDEX_HIGH, PM2VI_RD_CURSOR_PATTERN >> 8);
1885 for (y = 0, i = 0; y < c->size.y; y++) {
1886 WAIT_FIFO(fb, 32);
1887 for (x = 0; x < c->size.x >> 3; x++) {
1888 m = c->mask[x][y];
1889 b = c->bits[x][y];
1890 pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i,
1891 cursor_mask_lookup[m >> 4] |
1892 cursor_bits_lookup[(b & m) >> 4]);
1893 pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i + 1,
1894 cursor_mask_lookup[m & 0x0f] |
1895 cursor_bits_lookup[(b & m) & 0x0f]);
1896 i+=2;
1897 }
1898 for ( ; x < 8; x++) {
1899 pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i, 0);
1900 pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i + 1, 0);
1901 i+=2;
1902 }
1903 }
1904 for (; y < 64; y++) {
1905 WAIT_FIFO(fb, 32);
1906 for (x = 0; x < 8; x++) {
1907 pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i, 0);
1908 pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i + 1, 0);
1909 i+=2;
1910 }
1911 }
1912 WAIT_FIFO(fb, 1);
1913 pm2_WR(fb, PM2VR_RD_INDEX_HIGH, 0);
1914 }
1915
pm2v_set_cursor(struct pm2fb_info * fb,int on)1916 static void pm2v_set_cursor(struct pm2fb_info *fb, int on)
1917 {
1918 struct pm2_cursor *c = fb->cursor;
1919 int x = c->pos.x;
1920
1921 if (!on) x = 4000;
1922 WAIT_FIFO(fb, 14);
1923 pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_X_LOW, x & 0xff);
1924 pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_X_HIGH, (x >> 8) & 0x0f);
1925 pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_Y_LOW, c->pos.y & 0xff);
1926 pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_Y_HIGH, (c->pos.y >> 8) & 0x0f);
1927 pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_X_HOT, c->hot.x & 0x3f);
1928 pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_Y_HOT, c->hot.y & 0x3f);
1929 pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_MODE, 0x11);
1930 }
1931
pm2_cursor_timer_handler(unsigned long dev_addr)1932 static void pm2_cursor_timer_handler(unsigned long dev_addr)
1933 {
1934 struct pm2fb_info *fb = (struct pm2fb_info *)dev_addr;
1935
1936 if (!fb->cursor->enable)
1937 goto out;
1938
1939 if (fb->cursor->vbl_cnt && --fb->cursor->vbl_cnt == 0) {
1940 fb->cursor->on ^= 1;
1941 pm2v_set_cursor(fb, fb->cursor->on);
1942 fb->cursor->vbl_cnt = fb->cursor->blink_rate;
1943 }
1944
1945 out:
1946 fb->cursor->timer->expires = jiffies + (HZ / 50);
1947 add_timer(fb->cursor->timer);
1948 }
1949
pm2fb_cursor(struct display * p,int mode,int x,int y)1950 static void pm2fb_cursor(struct display *p, int mode, int x, int y)
1951 {
1952 struct pm2fb_info *fb = (struct pm2fb_info *)p->fb_info;
1953 struct pm2_cursor *c = fb->cursor;
1954
1955 if (!c) return;
1956
1957 x *= fontwidth(p);
1958 y *= fontheight(p);
1959 if (c->pos.x == x && c->pos.y == y && (mode == CM_ERASE) == !c->enable)
1960 return;
1961
1962 c->enable = 0;
1963 if (c->on)
1964 pm2v_set_cursor(fb, 0);
1965 c->pos.x = x;
1966 c->pos.y = y;
1967
1968 switch (mode) {
1969 case CM_ERASE:
1970 c->on = 0;
1971 break;
1972
1973 case CM_DRAW:
1974 case CM_MOVE:
1975 if (c->on)
1976 pm2v_set_cursor(fb, 1);
1977 else
1978 c->vbl_cnt = CURSOR_DRAW_DELAY;
1979 c->enable = 1;
1980 break;
1981 }
1982 }
1983
pm2_init_cursor(struct pm2fb_info * fb)1984 static struct pm2_cursor * __init pm2_init_cursor(struct pm2fb_info *fb)
1985 {
1986 struct pm2_cursor *cursor;
1987
1988 if (fb->type != PM2_TYPE_PERMEDIA2V)
1989 return 0; /* FIXME: Support hw cursor everywhere */
1990
1991 cursor = kmalloc(sizeof(struct pm2_cursor), GFP_ATOMIC);
1992 if (!cursor)
1993 return 0;
1994 memset(cursor, 0, sizeof(*cursor));
1995
1996 cursor->timer = kmalloc(sizeof(*cursor->timer), GFP_KERNEL);
1997 if (!cursor->timer) {
1998 kfree(cursor);
1999 return 0;
2000 }
2001 memset(cursor->timer, 0, sizeof(*cursor->timer));
2002
2003 cursor->blink_rate = DEFAULT_CURSOR_BLINK_RATE;
2004
2005 if (curblink) {
2006 init_timer(cursor->timer);
2007 cursor->timer->expires = jiffies + (HZ / 50);
2008 cursor->timer->data = (unsigned long)fb;
2009 cursor->timer->function = pm2_cursor_timer_handler;
2010 add_timer(cursor->timer);
2011 }
2012
2013 return cursor;
2014 }
2015
pm2fb_set_font(struct display * d,int width,int height)2016 static int pm2fb_set_font(struct display *d, int width, int height)
2017 {
2018 struct pm2fb_info *fb = (struct pm2fb_info *)d->fb_info;
2019 struct pm2_cursor *c = fb->cursor;
2020 int i, j;
2021
2022 if (c) {
2023 if (!width || !height) {
2024 width = 8;
2025 height = 16;
2026 }
2027
2028 c->hot.x = 0;
2029 c->hot.y = 0;
2030 c->size.x = width;
2031 c->size.y = height;
2032
2033 memset(c->bits, 0xff, sizeof(c->bits));
2034 memset(c->mask, 0, sizeof(c->mask));
2035
2036 for (i = 0, j = width; j >= 0; j -= 8, i++) {
2037 c->mask[i][height-2] = (j >= 8) ? 0xff : (0xff << (8 - j));
2038 c->mask[i][height-1] = (j >= 8) ? 0xff : (0xff << (8 - j));
2039 }
2040
2041 pm2v_set_cursor_color(fb, cursor_color_map, cursor_color_map, cursor_color_map);
2042 pm2v_set_cursor_shape(fb);
2043 }
2044 return 1;
2045 }
2046 #endif /* PM2FB_HW_CURSOR */
2047
2048 /***************************************************************************
2049 * Begin of public functions
2050 ***************************************************************************/
2051
2052 #ifdef MODULE
pm2fb_cleanup(void)2053 static void pm2fb_cleanup(void) {
2054 struct pm2fb_info* i = &fb_info;
2055
2056 unregister_framebuffer((struct fb_info *)i);
2057 pm2fb_reset(i);
2058
2059 UNMAP(i->regions.v_fb, i->regions.fb_size);
2060 release_mem_region(i->regions.p_fb, i->regions.fb_size);
2061
2062 UNMAP(i->regions.v_regs, PM2_REGS_SIZE);
2063 release_mem_region(i->regions.p_regs, PM2_REGS_SIZE);
2064
2065 if (board_table[i->board].cleanup)
2066 board_table[i->board].cleanup(i);
2067 }
2068 #endif /* MODULE */
2069
pm2fb_init(void)2070 int __init pm2fb_init(void){
2071
2072 MOD_INC_USE_COUNT;
2073 memset(&fb_info, 0, sizeof(fb_info));
2074 memcpy(&fb_info.current_par, &pm2fb_options.user_mode, sizeof(fb_info.current_par));
2075 if (!pm2fb_conf(&fb_info)) {
2076 MOD_DEC_USE_COUNT;
2077 return -ENXIO;
2078 }
2079 pm2fb_reset(&fb_info);
2080 fb_info.disp.scrollmode=SCROLL_YNOMOVE;
2081 fb_info.gen.parsize=sizeof(struct pm2fb_par);
2082 fb_info.gen.fbhw=&pm2fb_hwswitch;
2083 strcpy(fb_info.gen.info.modename, permedia2_name);
2084 fb_info.gen.info.flags=FBINFO_FLAG_DEFAULT;
2085 fb_info.gen.info.fbops=&pm2fb_ops;
2086 fb_info.gen.info.disp=&fb_info.disp;
2087 strcpy(fb_info.gen.info.fontname, pm2fb_options.font);
2088 fb_info.gen.info.switch_con=&fbgen_switch;
2089 fb_info.gen.info.updatevar=&fbgen_update_var;
2090 fb_info.gen.info.blank=&fbgen_blank;
2091 fbgen_get_var(&fb_info.disp.var, -1, &fb_info.gen.info);
2092 fbgen_do_set_var(&fb_info.disp.var, 1, &fb_info.gen);
2093 fbgen_set_disp(-1, &fb_info.gen);
2094 fbgen_install_cmap(0, &fb_info.gen);
2095 if (register_framebuffer(&fb_info.gen.info)<0) {
2096 printk(KERN_ERR "pm2fb: unable to register.\n");
2097 MOD_DEC_USE_COUNT;
2098 return -EINVAL;
2099 }
2100 printk(KERN_INFO "fb%d: %s (%s), using %uK of video memory.\n",
2101 GET_FB_IDX(fb_info.gen.info.node),
2102 board_table[fb_info.board].name,
2103 permedia2_name,
2104 (u32 )(fb_info.regions.fb_size>>10));
2105 return 0;
2106 }
2107
pm2fb_mode_setup(char * options)2108 static void __init pm2fb_mode_setup(char* options){
2109 int i;
2110
2111 for (i=0; user_mode[i].name[0] &&
2112 strcmp(options, user_mode[i].name); i++);
2113 if (user_mode[i].name[0]) {
2114 memcpy(&pm2fb_options.user_mode, &user_mode[i].par,
2115 sizeof(pm2fb_options.user_mode));
2116 pm2fb_options.flags |= OPTF_USER;
2117 }
2118 }
2119
pm2fb_font_setup(char * options)2120 static void __init pm2fb_font_setup(char* options){
2121
2122 strncpy(pm2fb_options.font, options, sizeof(pm2fb_options.font));
2123 pm2fb_options.font[sizeof(pm2fb_options.font)-1]='\0';
2124 }
2125
pm2fb_setup(char * options)2126 int __init pm2fb_setup(char* options){
2127 char* next;
2128
2129 while (options) {
2130 if ((next=strchr(options, ',')))
2131 *(next++)='\0';
2132 if (!strncmp(options, "font:", 5))
2133 pm2fb_font_setup(options+5);
2134 else if (!strncmp(options, "mode:", 5))
2135 pm2fb_mode_setup(options+5);
2136 else if (!strcmp(options, "ypan"))
2137 pm2fb_options.flags |= OPTF_YPAN;
2138 else if (!strcmp(options, "oldmem"))
2139 pm2fb_options.flags |= OPTF_OLD_MEM;
2140 else if (!strcmp(options, "virtual"))
2141 pm2fb_options.flags |= OPTF_VIRTUAL;
2142 else if (!strcmp(options, "noblink"))
2143 curblink = 0;
2144 options=next;
2145 }
2146 return 0;
2147 }
2148
2149 /***************************************************************************
2150 * Begin of module functions
2151 ***************************************************************************/
2152
2153 #ifdef MODULE
2154
2155 MODULE_LICENSE("GPL");
2156
2157 static char *mode = NULL;
2158
2159 MODULE_PARM(mode, "s");
2160
init_module(void)2161 int __init init_module(void) {
2162
2163 if (mode) pm2fb_mode_setup(mode);
2164 return pm2fb_init();
2165 }
2166
cleanup_module(void)2167 void cleanup_module(void) {
2168
2169 pm2fb_cleanup();
2170 }
2171 #endif /* MODULE */
2172
2173 /***************************************************************************
2174 * That's all folks!
2175 ***************************************************************************/
2176