tridentfb: use mmio access for clock setting
[deliverable/linux.git] / drivers / video / tridentfb.c
CommitLineData
1da177e4
LT
1/*
2 * Frame buffer driver for Trident Blade and Image series
3 *
245a2c2c 4 * Copyright 2001, 2002 - Jani Monoses <jani@iv.ro>
1da177e4
LT
5 *
6 *
7 * CREDITS:(in order of appearance)
245a2c2c
KH
8 * skeletonfb.c by Geert Uytterhoeven and other fb code in drivers/video
9 * Special thanks ;) to Mattia Crivellini <tia@mclink.it>
10 * much inspired by the XFree86 4.x Trident driver sources
11 * by Alan Hourihane the FreeVGA project
12 * Francesco Salvestrini <salvestrini@users.sf.net> XP support,
13 * code, suggestions
1da177e4 14 * TODO:
245a2c2c
KH
15 * timing value tweaking so it looks good on every monitor in every mode
16 * TGUI acceleration
1da177e4
LT
17 */
18
1da177e4
LT
19#include <linux/module.h>
20#include <linux/fb.h>
21#include <linux/init.h>
22#include <linux/pci.h>
23
24#include <linux/delay.h>
10172ed6 25#include <video/vga.h>
1da177e4
LT
26#include <video/trident.h>
27
122e8ad3 28#define VERSION "0.7.9-NEWAPI"
1da177e4
LT
29
30struct tridentfb_par {
245a2c2c 31 void __iomem *io_virt; /* iospace virtual memory address */
ea8ee55c 32 u32 pseudo_pal[16];
122e8ad3 33 int chip_id;
6eed8e1e 34 int flatpanel;
d9cad04b
KH
35 void (*init_accel) (struct tridentfb_par *, int, int);
36 void (*wait_engine) (struct tridentfb_par *);
37 void (*fill_rect)
38 (struct tridentfb_par *par, u32, u32, u32, u32, u32, u32);
39 void (*copy_rect)
40 (struct tridentfb_par *par, u32, u32, u32, u32, u32, u32);
1da177e4
LT
41};
42
245a2c2c 43static unsigned char eng_oper; /* engine operation... */
1da177e4
LT
44static struct fb_ops tridentfb_ops;
45
1da177e4 46static struct fb_fix_screeninfo tridentfb_fix = {
245a2c2c 47 .id = "Trident",
1da177e4
LT
48 .type = FB_TYPE_PACKED_PIXELS,
49 .ypanstep = 1,
50 .visual = FB_VISUAL_PSEUDOCOLOR,
51 .accel = FB_ACCEL_NONE,
52};
53
1da177e4
LT
54/* defaults which are normally overriden by user values */
55
56/* video mode */
07f41e45 57static char *mode_option __devinitdata = "640x480";
6eed8e1e 58static int bpp __devinitdata = 8;
1da177e4 59
6eed8e1e 60static int noaccel __devinitdata;
1da177e4
LT
61
62static int center;
63static int stretch;
64
6eed8e1e
KH
65static int fp __devinitdata;
66static int crt __devinitdata;
1da177e4 67
6eed8e1e
KH
68static int memsize __devinitdata;
69static int memdiff __devinitdata;
1da177e4
LT
70static int nativex;
71
07f41e45
KH
72module_param(mode_option, charp, 0);
73MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
9e3f0ca8
KH
74module_param_named(mode, mode_option, charp, 0);
75MODULE_PARM_DESC(mode, "Initial video mode e.g. '648x480-8@60' (deprecated)");
1da177e4
LT
76module_param(bpp, int, 0);
77module_param(center, int, 0);
78module_param(stretch, int, 0);
79module_param(noaccel, int, 0);
80module_param(memsize, int, 0);
81module_param(memdiff, int, 0);
82module_param(nativex, int, 0);
83module_param(fp, int, 0);
6eed8e1e 84MODULE_PARM_DESC(fp, "Define if flatpanel is connected");
1da177e4 85module_param(crt, int, 0);
6eed8e1e 86MODULE_PARM_DESC(crt, "Define if CRT is connected");
1da177e4 87
e0759a5f
KH
88static int is_blade(int id)
89{
90 return (id == BLADE3D) ||
91 (id == CYBERBLADEE4) ||
92 (id == CYBERBLADEi7) ||
93 (id == CYBERBLADEi7D) ||
94 (id == CYBERBLADEi1) ||
95 (id == CYBERBLADEi1D) ||
96 (id == CYBERBLADEAi1) ||
97 (id == CYBERBLADEAi1D);
98}
99
100static int is_xp(int id)
101{
102 return (id == CYBERBLADEXPAi1) ||
103 (id == CYBERBLADEXPm8) ||
104 (id == CYBERBLADEXPm16);
105}
106
1da177e4
LT
107static int is3Dchip(int id)
108{
245a2c2c
KH
109 return ((id == BLADE3D) || (id == CYBERBLADEE4) ||
110 (id == CYBERBLADEi7) || (id == CYBERBLADEi7D) ||
111 (id == CYBER9397) || (id == CYBER9397DVD) ||
112 (id == CYBER9520) || (id == CYBER9525DVD) ||
113 (id == IMAGE975) || (id == IMAGE985) ||
114 (id == CYBERBLADEi1) || (id == CYBERBLADEi1D) ||
115 (id == CYBERBLADEAi1) || (id == CYBERBLADEAi1D) ||
116 (id == CYBERBLADEXPm8) || (id == CYBERBLADEXPm16) ||
117 (id == CYBERBLADEXPAi1));
1da177e4
LT
118}
119
120static int iscyber(int id)
121{
122 switch (id) {
245a2c2c
KH
123 case CYBER9388:
124 case CYBER9382:
125 case CYBER9385:
126 case CYBER9397:
127 case CYBER9397DVD:
128 case CYBER9520:
129 case CYBER9525DVD:
130 case CYBERBLADEE4:
131 case CYBERBLADEi7D:
132 case CYBERBLADEi1:
133 case CYBERBLADEi1D:
134 case CYBERBLADEAi1:
135 case CYBERBLADEAi1D:
136 case CYBERBLADEXPAi1:
137 return 1;
1da177e4 138
245a2c2c
KH
139 case CYBER9320:
140 case TGUI9660:
141 case IMAGE975:
142 case IMAGE985:
143 case BLADE3D:
144 case CYBERBLADEi7: /* VIA MPV4 integrated version */
145
146 default:
147 /* case CYBERBLDAEXPm8: Strange */
148 /* case CYBERBLDAEXPm16: Strange */
149 return 0;
1da177e4
LT
150 }
151}
152
306fa6f6
KH
153static inline void t_outb(struct tridentfb_par *p, u8 val, u16 reg)
154{
155 fb_writeb(val, p->io_virt + reg);
156}
1da177e4 157
306fa6f6
KH
158static inline u8 t_inb(struct tridentfb_par *p, u16 reg)
159{
160 return fb_readb(p->io_virt + reg);
161}
1da177e4 162
306fa6f6
KH
163static inline void writemmr(struct tridentfb_par *par, u16 r, u32 v)
164{
165 fb_writel(v, par->io_virt + r);
166}
167
168static inline u32 readmmr(struct tridentfb_par *par, u16 r)
169{
170 return fb_readl(par->io_virt + r);
171}
1da177e4 172
1da177e4
LT
173/*
174 * Blade specific acceleration.
175 */
176
245a2c2c 177#define point(x, y) ((y) << 16 | (x))
1da177e4
LT
178#define STA 0x2120
179#define CMD 0x2144
180#define ROP 0x2148
181#define CLR 0x2160
182#define SR1 0x2100
183#define SR2 0x2104
184#define DR1 0x2108
185#define DR2 0x210C
186
187#define ROP_S 0xCC
188
306fa6f6 189static void blade_init_accel(struct tridentfb_par *par, int pitch, int bpp)
1da177e4 190{
245a2c2c
KH
191 int v1 = (pitch >> 3) << 20;
192 int tmp = 0, v2;
1da177e4 193 switch (bpp) {
245a2c2c
KH
194 case 8:
195 tmp = 0;
196 break;
197 case 15:
198 tmp = 5;
199 break;
200 case 16:
201 tmp = 1;
202 break;
203 case 24:
204 case 32:
205 tmp = 2;
206 break;
1da177e4 207 }
245a2c2c 208 v2 = v1 | (tmp << 29);
306fa6f6
KH
209 writemmr(par, 0x21C0, v2);
210 writemmr(par, 0x21C4, v2);
211 writemmr(par, 0x21B8, v2);
212 writemmr(par, 0x21BC, v2);
213 writemmr(par, 0x21D0, v1);
214 writemmr(par, 0x21D4, v1);
215 writemmr(par, 0x21C8, v1);
216 writemmr(par, 0x21CC, v1);
217 writemmr(par, 0x216C, 0);
1da177e4
LT
218}
219
306fa6f6 220static void blade_wait_engine(struct tridentfb_par *par)
1da177e4 221{
306fa6f6 222 while (readmmr(par, STA) & 0xFA800000) ;
1da177e4
LT
223}
224
306fa6f6
KH
225static void blade_fill_rect(struct tridentfb_par *par,
226 u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)
1da177e4 227{
306fa6f6
KH
228 writemmr(par, CLR, c);
229 writemmr(par, ROP, rop ? 0x66 : ROP_S);
230 writemmr(par, CMD, 0x20000000 | 1 << 19 | 1 << 4 | 2 << 2);
1da177e4 231
306fa6f6
KH
232 writemmr(par, DR1, point(x, y));
233 writemmr(par, DR2, point(x + w - 1, y + h - 1));
1da177e4
LT
234}
235
306fa6f6
KH
236static void blade_copy_rect(struct tridentfb_par *par,
237 u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
1da177e4 238{
245a2c2c 239 u32 s1, s2, d1, d2;
1da177e4 240 int direction = 2;
245a2c2c
KH
241 s1 = point(x1, y1);
242 s2 = point(x1 + w - 1, y1 + h - 1);
243 d1 = point(x2, y2);
244 d2 = point(x2 + w - 1, y2 + h - 1);
1da177e4
LT
245
246 if ((y1 > y2) || ((y1 == y2) && (x1 > x2)))
245a2c2c 247 direction = 0;
1da177e4 248
306fa6f6
KH
249 writemmr(par, ROP, ROP_S);
250 writemmr(par, CMD, 0xE0000000 | 1 << 19 | 1 << 4 | 1 << 2 | direction);
1da177e4 251
306fa6f6
KH
252 writemmr(par, SR1, direction ? s2 : s1);
253 writemmr(par, SR2, direction ? s1 : s2);
254 writemmr(par, DR1, direction ? d2 : d1);
255 writemmr(par, DR2, direction ? d1 : d2);
1da177e4
LT
256}
257
1da177e4
LT
258/*
259 * BladeXP specific acceleration functions
260 */
261
262#define ROP_P 0xF0
245a2c2c 263#define masked_point(x, y) ((y & 0xffff)<<16|(x & 0xffff))
1da177e4 264
306fa6f6 265static void xp_init_accel(struct tridentfb_par *par, int pitch, int bpp)
1da177e4 266{
245a2c2c 267 int tmp = 0, v1;
1da177e4
LT
268 unsigned char x = 0;
269
270 switch (bpp) {
245a2c2c
KH
271 case 8:
272 x = 0;
273 break;
274 case 16:
275 x = 1;
276 break;
277 case 24:
278 x = 3;
279 break;
280 case 32:
281 x = 2;
282 break;
1da177e4
LT
283 }
284
285 switch (pitch << (bpp >> 3)) {
245a2c2c
KH
286 case 8192:
287 case 512:
288 x |= 0x00;
289 break;
290 case 1024:
291 x |= 0x04;
292 break;
293 case 2048:
294 x |= 0x08;
295 break;
296 case 4096:
297 x |= 0x0C;
298 break;
1da177e4
LT
299 }
300
306fa6f6 301 t_outb(par, x, 0x2125);
1da177e4
LT
302
303 eng_oper = x | 0x40;
304
305 switch (bpp) {
245a2c2c
KH
306 case 8:
307 tmp = 18;
308 break;
309 case 15:
310 case 16:
311 tmp = 19;
312 break;
313 case 24:
314 case 32:
315 tmp = 20;
316 break;
1da177e4
LT
317 }
318
319 v1 = pitch << tmp;
320
306fa6f6
KH
321 writemmr(par, 0x2154, v1);
322 writemmr(par, 0x2150, v1);
323 t_outb(par, 3, 0x2126);
1da177e4
LT
324}
325
306fa6f6 326static void xp_wait_engine(struct tridentfb_par *par)
1da177e4
LT
327{
328 int busy;
329 int count, timeout;
330
331 count = 0;
332 timeout = 0;
333 for (;;) {
306fa6f6 334 busy = t_inb(par, STA) & 0x80;
1da177e4
LT
335 if (busy != 0x80)
336 return;
337 count++;
338 if (count == 10000000) {
339 /* Timeout */
340 count = 9990000;
341 timeout++;
342 if (timeout == 8) {
343 /* Reset engine */
306fa6f6 344 t_outb(par, 0x00, 0x2120);
1da177e4
LT
345 return;
346 }
347 }
348 }
349}
350
306fa6f6
KH
351static void xp_fill_rect(struct tridentfb_par *par,
352 u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)
1da177e4 353{
306fa6f6
KH
354 writemmr(par, 0x2127, ROP_P);
355 writemmr(par, 0x2158, c);
356 writemmr(par, 0x2128, 0x4000);
357 writemmr(par, 0x2140, masked_point(h, w));
358 writemmr(par, 0x2138, masked_point(y, x));
359 t_outb(par, 0x01, 0x2124);
360 t_outb(par, eng_oper, 0x2125);
1da177e4
LT
361}
362
306fa6f6
KH
363static void xp_copy_rect(struct tridentfb_par *par,
364 u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
1da177e4
LT
365{
366 int direction;
245a2c2c 367 u32 x1_tmp, x2_tmp, y1_tmp, y2_tmp;
1da177e4
LT
368
369 direction = 0x0004;
245a2c2c 370
1da177e4
LT
371 if ((x1 < x2) && (y1 == y2)) {
372 direction |= 0x0200;
373 x1_tmp = x1 + w - 1;
374 x2_tmp = x2 + w - 1;
375 } else {
376 x1_tmp = x1;
377 x2_tmp = x2;
378 }
245a2c2c 379
1da177e4
LT
380 if (y1 < y2) {
381 direction |= 0x0100;
382 y1_tmp = y1 + h - 1;
383 y2_tmp = y2 + h - 1;
245a2c2c 384 } else {
1da177e4
LT
385 y1_tmp = y1;
386 y2_tmp = y2;
387 }
388
306fa6f6
KH
389 writemmr(par, 0x2128, direction);
390 t_outb(par, ROP_S, 0x2127);
391 writemmr(par, 0x213C, masked_point(y1_tmp, x1_tmp));
392 writemmr(par, 0x2138, masked_point(y2_tmp, x2_tmp));
393 writemmr(par, 0x2140, masked_point(h, w));
394 t_outb(par, 0x01, 0x2124);
1da177e4
LT
395}
396
1da177e4
LT
397/*
398 * Image specific acceleration functions
399 */
306fa6f6 400static void image_init_accel(struct tridentfb_par *par, int pitch, int bpp)
1da177e4
LT
401{
402 int tmp = 0;
245a2c2c
KH
403 switch (bpp) {
404 case 8:
405 tmp = 0;
406 break;
407 case 15:
408 tmp = 5;
409 break;
410 case 16:
411 tmp = 1;
412 break;
413 case 24:
414 case 32:
415 tmp = 2;
416 break;
1da177e4 417 }
306fa6f6
KH
418 writemmr(par, 0x2120, 0xF0000000);
419 writemmr(par, 0x2120, 0x40000000 | tmp);
420 writemmr(par, 0x2120, 0x80000000);
421 writemmr(par, 0x2144, 0x00000000);
422 writemmr(par, 0x2148, 0x00000000);
423 writemmr(par, 0x2150, 0x00000000);
424 writemmr(par, 0x2154, 0x00000000);
425 writemmr(par, 0x2120, 0x60000000 | (pitch << 16) | pitch);
426 writemmr(par, 0x216C, 0x00000000);
427 writemmr(par, 0x2170, 0x00000000);
428 writemmr(par, 0x217C, 0x00000000);
429 writemmr(par, 0x2120, 0x10000000);
430 writemmr(par, 0x2130, (2047 << 16) | 2047);
1da177e4
LT
431}
432
306fa6f6 433static void image_wait_engine(struct tridentfb_par *par)
1da177e4 434{
306fa6f6 435 while (readmmr(par, 0x2164) & 0xF0000000) ;
1da177e4
LT
436}
437
306fa6f6
KH
438static void image_fill_rect(struct tridentfb_par *par,
439 u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)
1da177e4 440{
306fa6f6
KH
441 writemmr(par, 0x2120, 0x80000000);
442 writemmr(par, 0x2120, 0x90000000 | ROP_S);
1da177e4 443
306fa6f6 444 writemmr(par, 0x2144, c);
1da177e4 445
306fa6f6
KH
446 writemmr(par, DR1, point(x, y));
447 writemmr(par, DR2, point(x + w - 1, y + h - 1));
1da177e4 448
306fa6f6 449 writemmr(par, 0x2124, 0x80000000 | 3 << 22 | 1 << 10 | 1 << 9);
1da177e4
LT
450}
451
306fa6f6
KH
452static void image_copy_rect(struct tridentfb_par *par,
453 u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
1da177e4 454{
245a2c2c 455 u32 s1, s2, d1, d2;
1da177e4 456 int direction = 2;
245a2c2c
KH
457 s1 = point(x1, y1);
458 s2 = point(x1 + w - 1, y1 + h - 1);
459 d1 = point(x2, y2);
460 d2 = point(x2 + w - 1, y2 + h - 1);
1da177e4 461
245a2c2c
KH
462 if ((y1 > y2) || ((y1 == y2) && (x1 > x2)))
463 direction = 0;
464
306fa6f6
KH
465 writemmr(par, 0x2120, 0x80000000);
466 writemmr(par, 0x2120, 0x90000000 | ROP_S);
245a2c2c 467
306fa6f6
KH
468 writemmr(par, SR1, direction ? s2 : s1);
469 writemmr(par, SR2, direction ? s1 : s2);
470 writemmr(par, DR1, direction ? d2 : d1);
471 writemmr(par, DR2, direction ? d1 : d2);
472 writemmr(par, 0x2124,
473 0x80000000 | 1 << 22 | 1 << 10 | 1 << 7 | direction);
245a2c2c 474}
1da177e4 475
1da177e4
LT
476/*
477 * Accel functions called by the upper layers
478 */
479#ifdef CONFIG_FB_TRIDENT_ACCEL
245a2c2c
KH
480static void tridentfb_fillrect(struct fb_info *info,
481 const struct fb_fillrect *fr)
1da177e4 482{
306fa6f6 483 struct tridentfb_par *par = info->par;
1da177e4 484 int bpp = info->var.bits_per_pixel;
8dad46cf 485 int col = 0;
245a2c2c 486
1da177e4 487 switch (bpp) {
245a2c2c
KH
488 default:
489 case 8:
490 col |= fr->color;
491 col |= col << 8;
492 col |= col << 16;
493 break;
494 case 16:
495 col = ((u32 *)(info->pseudo_palette))[fr->color];
496 break;
497 case 32:
498 col = ((u32 *)(info->pseudo_palette))[fr->color];
499 break;
500 }
501
d9cad04b 502 par->fill_rect(par, fr->dx, fr->dy, fr->width,
306fa6f6 503 fr->height, col, fr->rop);
d9cad04b 504 par->wait_engine(par);
1da177e4 505}
245a2c2c
KH
506static void tridentfb_copyarea(struct fb_info *info,
507 const struct fb_copyarea *ca)
1da177e4 508{
306fa6f6
KH
509 struct tridentfb_par *par = info->par;
510
d9cad04b 511 par->copy_rect(par, ca->sx, ca->sy, ca->dx, ca->dy,
306fa6f6 512 ca->width, ca->height);
d9cad04b 513 par->wait_engine(par);
1da177e4
LT
514}
515#else /* !CONFIG_FB_TRIDENT_ACCEL */
516#define tridentfb_fillrect cfb_fillrect
517#define tridentfb_copyarea cfb_copyarea
518#endif /* CONFIG_FB_TRIDENT_ACCEL */
519
520
521/*
522 * Hardware access functions
523 */
524
306fa6f6 525static inline unsigned char read3X4(struct tridentfb_par *par, int reg)
1da177e4 526{
10172ed6 527 return vga_mm_rcrt(par->io_virt, reg);
1da177e4
LT
528}
529
306fa6f6
KH
530static inline void write3X4(struct tridentfb_par *par, int reg,
531 unsigned char val)
1da177e4 532{
10172ed6 533 vga_mm_wcrt(par->io_virt, reg, val);
1da177e4
LT
534}
535
10172ed6
KH
536static inline unsigned char read3CE(struct tridentfb_par *par,
537 unsigned char reg)
1da177e4 538{
10172ed6 539 return vga_mm_rgfx(par->io_virt, reg);
1da177e4
LT
540}
541
306fa6f6
KH
542static inline void writeAttr(struct tridentfb_par *par, int reg,
543 unsigned char val)
1da177e4 544{
10172ed6
KH
545 fb_readb(par->io_virt + VGA_IS1_RC); /* flip-flop to index */
546 vga_mm_wattr(par->io_virt, reg, val);
1da177e4
LT
547}
548
306fa6f6
KH
549static inline void write3CE(struct tridentfb_par *par, int reg,
550 unsigned char val)
1da177e4 551{
10172ed6 552 vga_mm_wgfx(par->io_virt, reg, val);
1da177e4
LT
553}
554
e8ed857c 555static void enable_mmio(void)
1da177e4
LT
556{
557 /* Goto New Mode */
10172ed6 558 vga_io_rseq(0x0B);
1da177e4
LT
559
560 /* Unprotect registers */
10172ed6 561 vga_io_wseq(NewMode1, 0x80);
245a2c2c 562
1da177e4 563 /* Enable MMIO */
245a2c2c 564 outb(PCIReg, 0x3D4);
1da177e4 565 outb(inb(0x3D5) | 0x01, 0x3D5);
e8ed857c
KH
566}
567
306fa6f6 568static void disable_mmio(struct tridentfb_par *par)
e8ed857c 569{
e8ed857c 570 /* Goto New Mode */
10172ed6 571 vga_mm_rseq(par->io_virt, 0x0B);
e8ed857c
KH
572
573 /* Unprotect registers */
10172ed6 574 vga_mm_wseq(par->io_virt, NewMode1, 0x80);
e8ed857c
KH
575
576 /* Disable MMIO */
306fa6f6
KH
577 t_outb(par, PCIReg, 0x3D4);
578 t_outb(par, t_inb(par, 0x3D5) & ~0x01, 0x3D5);
1da177e4
LT
579}
580
306fa6f6
KH
581static void crtc_unlock(struct tridentfb_par *par)
582{
10172ed6
KH
583 write3X4(par, VGA_CRTC_V_SYNC_END,
584 read3X4(par, VGA_CRTC_V_SYNC_END) & 0x7F);
306fa6f6 585}
1da177e4
LT
586
587/* Return flat panel's maximum x resolution */
306fa6f6 588static int __devinit get_nativex(struct tridentfb_par *par)
1da177e4 589{
245a2c2c 590 int x, y, tmp;
1da177e4
LT
591
592 if (nativex)
593 return nativex;
594
306fa6f6 595 tmp = (read3CE(par, VertStretch) >> 4) & 3;
1da177e4
LT
596
597 switch (tmp) {
245a2c2c
KH
598 case 0:
599 x = 1280; y = 1024;
600 break;
601 case 2:
602 x = 1024; y = 768;
603 break;
604 case 3:
605 x = 800; y = 600;
606 break;
607 case 4:
608 x = 1400; y = 1050;
609 break;
610 case 1:
611 default:
612 x = 640; y = 480;
613 break;
1da177e4
LT
614 }
615
616 output("%dx%d flat panel found\n", x, y);
617 return x;
618}
619
620/* Set pitch */
306fa6f6 621static void set_lwidth(struct tridentfb_par *par, int width)
1da177e4 622{
10172ed6 623 write3X4(par, VGA_CRTC_OFFSET, width & 0xFF);
306fa6f6
KH
624 write3X4(par, AddColReg,
625 (read3X4(par, AddColReg) & 0xCF) | ((width & 0x300) >> 4));
1da177e4
LT
626}
627
628/* For resolutions smaller than FP resolution stretch */
306fa6f6 629static void screen_stretch(struct tridentfb_par *par)
1da177e4 630{
122e8ad3 631 if (par->chip_id != CYBERBLADEXPAi1)
306fa6f6 632 write3CE(par, BiosReg, 0);
245a2c2c 633 else
306fa6f6
KH
634 write3CE(par, BiosReg, 8);
635 write3CE(par, VertStretch, (read3CE(par, VertStretch) & 0x7C) | 1);
636 write3CE(par, HorStretch, (read3CE(par, HorStretch) & 0x7C) | 1);
1da177e4
LT
637}
638
639/* For resolutions smaller than FP resolution center */
306fa6f6 640static void screen_center(struct tridentfb_par *par)
1da177e4 641{
306fa6f6
KH
642 write3CE(par, VertStretch, (read3CE(par, VertStretch) & 0x7C) | 0x80);
643 write3CE(par, HorStretch, (read3CE(par, HorStretch) & 0x7C) | 0x80);
1da177e4
LT
644}
645
646/* Address of first shown pixel in display memory */
306fa6f6 647static void set_screen_start(struct tridentfb_par *par, int base)
1da177e4 648{
306fa6f6 649 u8 tmp;
10172ed6
KH
650 write3X4(par, VGA_CRTC_START_LO, base & 0xFF);
651 write3X4(par, VGA_CRTC_START_HI, (base & 0xFF00) >> 8);
306fa6f6
KH
652 tmp = read3X4(par, CRTCModuleTest) & 0xDF;
653 write3X4(par, CRTCModuleTest, tmp | ((base & 0x10000) >> 11));
654 tmp = read3X4(par, CRTHiOrd) & 0xF8;
655 write3X4(par, CRTHiOrd, tmp | ((base & 0xE0000) >> 17));
1da177e4
LT
656}
657
1da177e4 658/* Set dotclock frequency */
306fa6f6 659static void set_vclk(struct tridentfb_par *par, unsigned long freq)
1da177e4 660{
245a2c2c 661 int m, n, k;
3f275ea3 662 unsigned long f, fi, d, di;
245a2c2c 663 unsigned char lo = 0, hi = 0;
1da177e4 664
3f275ea3 665 d = 20000;
245a2c2c
KH
666 for (k = 2; k >= 0; k--)
667 for (m = 0; m < 63; m++)
668 for (n = 0; n < 128; n++) {
3f275ea3 669 fi = ((14318l * (n + 8)) / (m + 2)) >> k;
245a2c2c
KH
670 if ((di = abs(fi - freq)) < d) {
671 d = di;
672 f = fi;
673 lo = n;
674 hi = (k << 6) | m;
675 }
3f275ea3
KH
676 if (fi > freq)
677 break;
245a2c2c 678 }
122e8ad3 679 if (is3Dchip(par->chip_id)) {
10172ed6
KH
680 vga_mm_wseq(par->io_virt, ClockHigh, hi);
681 vga_mm_wseq(par->io_virt, ClockLow, lo);
1da177e4 682 } else {
c1724fec
KH
683 t_outb(par, lo, 0x43C8);
684 t_outb(par, hi, 0x43C9);
1da177e4 685 }
245a2c2c 686 debug("VCLK = %X %X\n", hi, lo);
1da177e4
LT
687}
688
689/* Set number of lines for flat panels*/
306fa6f6 690static void set_number_of_lines(struct tridentfb_par *par, int lines)
1da177e4 691{
306fa6f6 692 int tmp = read3CE(par, CyberEnhance) & 0x8F;
1da177e4
LT
693 if (lines > 1024)
694 tmp |= 0x50;
695 else if (lines > 768)
696 tmp |= 0x30;
697 else if (lines > 600)
698 tmp |= 0x20;
699 else if (lines > 480)
700 tmp |= 0x10;
306fa6f6 701 write3CE(par, CyberEnhance, tmp);
1da177e4
LT
702}
703
704/*
705 * If we see that FP is active we assume we have one.
6eed8e1e 706 * Otherwise we have a CRT display. User can override.
1da177e4 707 */
6eed8e1e 708static int __devinit is_flatpanel(struct tridentfb_par *par)
1da177e4
LT
709{
710 if (fp)
6eed8e1e 711 return 1;
122e8ad3 712 if (crt || !iscyber(par->chip_id))
6eed8e1e
KH
713 return 0;
714 return (read3CE(par, FPConfig) & 0x10) ? 1 : 0;
1da177e4
LT
715}
716
717/* Try detecting the video memory size */
306fa6f6 718static unsigned int __devinit get_memsize(struct tridentfb_par *par)
1da177e4
LT
719{
720 unsigned char tmp, tmp2;
721 unsigned int k;
722
723 /* If memory size provided by user */
724 if (memsize)
725 k = memsize * Kb;
726 else
122e8ad3 727 switch (par->chip_id) {
245a2c2c
KH
728 case CYBER9525DVD:
729 k = 2560 * Kb;
730 break;
1da177e4 731 default:
306fa6f6 732 tmp = read3X4(par, SPR) & 0x0F;
1da177e4
LT
733 switch (tmp) {
734
245a2c2c 735 case 0x01:
b614ce8b 736 k = 512 * Kb;
245a2c2c
KH
737 break;
738 case 0x02:
739 k = 6 * Mb; /* XP */
740 break;
741 case 0x03:
742 k = 1 * Mb;
743 break;
744 case 0x04:
745 k = 8 * Mb;
746 break;
747 case 0x06:
748 k = 10 * Mb; /* XP */
749 break;
750 case 0x07:
751 k = 2 * Mb;
752 break;
753 case 0x08:
754 k = 12 * Mb; /* XP */
755 break;
756 case 0x0A:
757 k = 14 * Mb; /* XP */
758 break;
759 case 0x0C:
760 k = 16 * Mb; /* XP */
761 break;
762 case 0x0E: /* XP */
763
10172ed6 764 tmp2 = vga_mm_rseq(par->io_virt, 0xC1);
245a2c2c
KH
765 switch (tmp2) {
766 case 0x00:
767 k = 20 * Mb;
768 break;
769 case 0x01:
770 k = 24 * Mb;
771 break;
772 case 0x10:
773 k = 28 * Mb;
774 break;
775 case 0x11:
776 k = 32 * Mb;
777 break;
778 default:
779 k = 1 * Mb;
780 break;
781 }
782 break;
783
784 case 0x0F:
785 k = 4 * Mb;
786 break;
787 default:
788 k = 1 * Mb;
1da177e4 789 break;
1da177e4 790 }
245a2c2c 791 }
1da177e4
LT
792
793 k -= memdiff * Kb;
245a2c2c 794 output("framebuffer size = %d Kb\n", k / Kb);
1da177e4
LT
795 return k;
796}
797
798/* See if we can handle the video mode described in var */
245a2c2c
KH
799static int tridentfb_check_var(struct fb_var_screeninfo *var,
800 struct fb_info *info)
1da177e4 801{
6eed8e1e 802 struct tridentfb_par *par = info->par;
1da177e4
LT
803 int bpp = var->bits_per_pixel;
804 debug("enter\n");
805
806 /* check color depth */
245a2c2c 807 if (bpp == 24)
1da177e4 808 bpp = var->bits_per_pixel = 32;
245a2c2c 809 /* check whether resolution fits on panel and in memory */
6eed8e1e 810 if (par->flatpanel && nativex && var->xres > nativex)
1da177e4 811 return -EINVAL;
245a2c2c 812 if (var->xres * var->yres_virtual * bpp / 8 > info->fix.smem_len)
1da177e4
LT
813 return -EINVAL;
814
815 switch (bpp) {
245a2c2c
KH
816 case 8:
817 var->red.offset = 0;
818 var->green.offset = 0;
819 var->blue.offset = 0;
820 var->red.length = 6;
821 var->green.length = 6;
822 var->blue.length = 6;
823 break;
824 case 16:
825 var->red.offset = 11;
826 var->green.offset = 5;
827 var->blue.offset = 0;
828 var->red.length = 5;
829 var->green.length = 6;
830 var->blue.length = 5;
831 break;
832 case 32:
833 var->red.offset = 16;
834 var->green.offset = 8;
835 var->blue.offset = 0;
836 var->red.length = 8;
837 var->green.length = 8;
838 var->blue.length = 8;
839 break;
840 default:
841 return -EINVAL;
1da177e4
LT
842 }
843 debug("exit\n");
844
845 return 0;
846
847}
245a2c2c 848
1da177e4
LT
849/* Pan the display */
850static int tridentfb_pan_display(struct fb_var_screeninfo *var,
245a2c2c 851 struct fb_info *info)
1da177e4 852{
306fa6f6 853 struct tridentfb_par *par = info->par;
1da177e4
LT
854 unsigned int offset;
855
856 debug("enter\n");
857 offset = (var->xoffset + (var->yoffset * var->xres))
245a2c2c 858 * var->bits_per_pixel / 32;
1da177e4
LT
859 info->var.xoffset = var->xoffset;
860 info->var.yoffset = var->yoffset;
306fa6f6 861 set_screen_start(par, offset);
1da177e4
LT
862 debug("exit\n");
863 return 0;
864}
865
306fa6f6
KH
866static void shadowmode_on(struct tridentfb_par *par)
867{
868 write3CE(par, CyberControl, read3CE(par, CyberControl) | 0x81);
869}
870
871static void shadowmode_off(struct tridentfb_par *par)
872{
873 write3CE(par, CyberControl, read3CE(par, CyberControl) & 0x7E);
874}
1da177e4
LT
875
876/* Set the hardware to the requested video mode */
877static int tridentfb_set_par(struct fb_info *info)
878{
245a2c2c
KH
879 struct tridentfb_par *par = (struct tridentfb_par *)(info->par);
880 u32 htotal, hdispend, hsyncstart, hsyncend, hblankstart, hblankend;
881 u32 vtotal, vdispend, vsyncstart, vsyncend, vblankstart, vblankend;
882 struct fb_var_screeninfo *var = &info->var;
1da177e4
LT
883 int bpp = var->bits_per_pixel;
884 unsigned char tmp;
3f275ea3
KH
885 unsigned long vclk;
886
1da177e4 887 debug("enter\n");
245a2c2c 888 hdispend = var->xres / 8 - 1;
7f762d23
KH
889 hsyncstart = (var->xres + var->right_margin) / 8 - 1;
890 hsyncend = (var->xres + var->right_margin + var->hsync_len) / 8 - 1;
891 htotal = (var->xres + var->left_margin + var->right_margin +
892 var->hsync_len) / 8 - 5;
893 hblankstart = hdispend + 2;
894 hblankend = htotal + 3;
1da177e4 895
1da177e4
LT
896 vdispend = var->yres - 1;
897 vsyncstart = var->yres + var->lower_margin;
7f762d23
KH
898 vsyncend = vsyncstart + var->vsync_len;
899 vtotal = var->upper_margin + vsyncend - 2;
900 vblankstart = vdispend + 2;
901 vblankend = vtotal;
1da177e4 902
306fa6f6
KH
903 crtc_unlock(par);
904 write3CE(par, CyberControl, 8);
1da177e4 905
6eed8e1e 906 if (par->flatpanel && var->xres < nativex) {
1da177e4
LT
907 /*
908 * on flat panels with native size larger
909 * than requested resolution decide whether
910 * we stretch or center
911 */
10172ed6 912 t_outb(par, 0xEB, VGA_MIS_W);
1da177e4 913
306fa6f6 914 shadowmode_on(par);
1da177e4 915
245a2c2c 916 if (center)
306fa6f6 917 screen_center(par);
1da177e4 918 else if (stretch)
306fa6f6 919 screen_stretch(par);
1da177e4
LT
920
921 } else {
10172ed6 922 t_outb(par, 0x2B, VGA_MIS_W);
306fa6f6 923 write3CE(par, CyberControl, 8);
1da177e4
LT
924 }
925
926 /* vertical timing values */
10172ed6
KH
927 write3X4(par, VGA_CRTC_V_TOTAL, vtotal & 0xFF);
928 write3X4(par, VGA_CRTC_V_DISP_END, vdispend & 0xFF);
929 write3X4(par, VGA_CRTC_V_SYNC_START, vsyncstart & 0xFF);
930 write3X4(par, VGA_CRTC_V_SYNC_END, (vsyncend & 0x0F));
931 write3X4(par, VGA_CRTC_V_BLANK_START, vblankstart & 0xFF);
7f762d23 932 write3X4(par, VGA_CRTC_V_BLANK_END, vblankend & 0xFF);
1da177e4
LT
933
934 /* horizontal timing values */
10172ed6
KH
935 write3X4(par, VGA_CRTC_H_TOTAL, htotal & 0xFF);
936 write3X4(par, VGA_CRTC_H_DISP, hdispend & 0xFF);
937 write3X4(par, VGA_CRTC_H_SYNC_START, hsyncstart & 0xFF);
938 write3X4(par, VGA_CRTC_H_SYNC_END,
306fa6f6 939 (hsyncend & 0x1F) | ((hblankend & 0x20) << 2));
10172ed6 940 write3X4(par, VGA_CRTC_H_BLANK_START, hblankstart & 0xFF);
7f762d23 941 write3X4(par, VGA_CRTC_H_BLANK_END, hblankend & 0x1F);
1da177e4
LT
942
943 /* higher bits of vertical timing values */
944 tmp = 0x10;
945 if (vtotal & 0x100) tmp |= 0x01;
946 if (vdispend & 0x100) tmp |= 0x02;
947 if (vsyncstart & 0x100) tmp |= 0x04;
948 if (vblankstart & 0x100) tmp |= 0x08;
949
950 if (vtotal & 0x200) tmp |= 0x20;
951 if (vdispend & 0x200) tmp |= 0x40;
952 if (vsyncstart & 0x200) tmp |= 0x80;
10172ed6 953 write3X4(par, VGA_CRTC_OVERFLOW, tmp);
1da177e4 954
7f762d23
KH
955 tmp = read3X4(par, CRTHiOrd) & 0x07;
956 tmp |= 0x08; /* line compare bit 10 */
1da177e4
LT
957 if (vtotal & 0x400) tmp |= 0x80;
958 if (vblankstart & 0x400) tmp |= 0x40;
959 if (vsyncstart & 0x400) tmp |= 0x20;
960 if (vdispend & 0x400) tmp |= 0x10;
306fa6f6 961 write3X4(par, CRTHiOrd, tmp);
1da177e4 962
7f762d23
KH
963 tmp = (htotal >> 8) & 0x01;
964 tmp |= (hdispend >> 7) & 0x02;
965 tmp |= (hsyncstart >> 5) & 0x08;
966 tmp |= (hblankstart >> 4) & 0x10;
306fa6f6 967 write3X4(par, HorizOverflow, tmp);
245a2c2c 968
1da177e4
LT
969 tmp = 0x40;
970 if (vblankstart & 0x200) tmp |= 0x20;
245a2c2c 971//FIXME if (info->var.vmode & FB_VMODE_DOUBLE) tmp |= 0x80; /* double scan for 200 line modes */
10172ed6 972 write3X4(par, VGA_CRTC_MAX_SCAN, tmp);
1da177e4 973
10172ed6
KH
974 write3X4(par, VGA_CRTC_LINE_COMPARE, 0xFF);
975 write3X4(par, VGA_CRTC_PRESET_ROW, 0);
976 write3X4(par, VGA_CRTC_MODE, 0xC3);
1da177e4 977
306fa6f6 978 write3X4(par, LinearAddReg, 0x20); /* enable linear addressing */
1da177e4 979
245a2c2c 980 tmp = (info->var.vmode & FB_VMODE_INTERLACED) ? 0x84 : 0x80;
306fa6f6
KH
981 /* enable access extended memory */
982 write3X4(par, CRTCModuleTest, tmp);
1da177e4 983
306fa6f6
KH
984 /* enable GE for text acceleration */
985 write3X4(par, GraphEngReg, 0x80);
1da177e4 986
245a2c2c 987#ifdef CONFIG_FB_TRIDENT_ACCEL
d9cad04b 988 par->init_accel(par, info->var.xres, bpp);
8dad46cf 989#endif
245a2c2c 990
1da177e4 991 switch (bpp) {
245a2c2c
KH
992 case 8:
993 tmp = 0x00;
994 break;
995 case 16:
996 tmp = 0x05;
997 break;
998 case 24:
999 tmp = 0x29;
1000 break;
1001 case 32:
1002 tmp = 0x09;
1003 break;
1da177e4
LT
1004 }
1005
306fa6f6 1006 write3X4(par, PixelBusReg, tmp);
1da177e4
LT
1007
1008 tmp = 0x10;
122e8ad3 1009 if (iscyber(par->chip_id))
245a2c2c 1010 tmp |= 0x20;
306fa6f6 1011 write3X4(par, DRAMControl, tmp); /* both IO, linear enable */
1da177e4 1012
306fa6f6
KH
1013 write3X4(par, InterfaceSel, read3X4(par, InterfaceSel) | 0x40);
1014 write3X4(par, Performance, 0x92);
1015 /* MMIO & PCI read and write burst enable */
1016 write3X4(par, PCIReg, 0x07);
1da177e4 1017
3f275ea3
KH
1018 /* convert from picoseconds to kHz */
1019 vclk = PICOS2KHZ(info->var.pixclock);
1da177e4 1020 if (bpp == 32)
3f275ea3 1021 vclk *= 2;
306fa6f6 1022 set_vclk(par, vclk);
1da177e4 1023
10172ed6
KH
1024 vga_mm_wseq(par->io_virt, 0, 3);
1025 vga_mm_wseq(par->io_virt, 1, 1); /* set char clock 8 dots wide */
306fa6f6 1026 /* enable 4 maps because needed in chain4 mode */
10172ed6
KH
1027 vga_mm_wseq(par->io_virt, 2, 0x0F);
1028 vga_mm_wseq(par->io_virt, 3, 0);
1029 vga_mm_wseq(par->io_virt, 4, 0x0E); /* memory mode enable bitmaps ?? */
1da177e4 1030
306fa6f6
KH
1031 /* divide clock by 2 if 32bpp chain4 mode display and CPU path */
1032 write3CE(par, MiscExtFunc, (bpp == 32) ? 0x1A : 0x12);
1033 write3CE(par, 0x5, 0x40); /* no CGA compat, allow 256 col */
1034 write3CE(par, 0x6, 0x05); /* graphics mode */
1035 write3CE(par, 0x7, 0x0F); /* planes? */
1da177e4 1036
122e8ad3 1037 if (par->chip_id == CYBERBLADEXPAi1) {
1da177e4 1038 /* This fixes snow-effect in 32 bpp */
10172ed6 1039 write3X4(par, VGA_CRTC_H_SYNC_START, 0x84);
1da177e4
LT
1040 }
1041
306fa6f6
KH
1042 /* graphics mode and support 256 color modes */
1043 writeAttr(par, 0x10, 0x41);
1044 writeAttr(par, 0x12, 0x0F); /* planes */
1045 writeAttr(par, 0x13, 0); /* horizontal pel panning */
1da177e4 1046
245a2c2c
KH
1047 /* colors */
1048 for (tmp = 0; tmp < 0x10; tmp++)
306fa6f6 1049 writeAttr(par, tmp, tmp);
10172ed6
KH
1050 fb_readb(par->io_virt + VGA_IS1_RC); /* flip-flop to index */
1051 t_outb(par, 0x20, VGA_ATT_W); /* enable attr */
1da177e4
LT
1052
1053 switch (bpp) {
245a2c2c
KH
1054 case 8:
1055 tmp = 0;
1056 break;
1057 case 15:
1058 tmp = 0x10;
1059 break;
1060 case 16:
1061 tmp = 0x30;
1062 break;
1063 case 24:
1064 case 32:
1065 tmp = 0xD0;
1066 break;
1da177e4
LT
1067 }
1068
10172ed6
KH
1069 t_inb(par, VGA_PEL_IW);
1070 t_inb(par, VGA_PEL_MSK);
1071 t_inb(par, VGA_PEL_MSK);
1072 t_inb(par, VGA_PEL_MSK);
1073 t_inb(par, VGA_PEL_MSK);
1074 t_outb(par, tmp, VGA_PEL_MSK);
1075 t_inb(par, VGA_PEL_IW);
1da177e4 1076
6eed8e1e 1077 if (par->flatpanel)
306fa6f6
KH
1078 set_number_of_lines(par, info->var.yres);
1079 set_lwidth(par, info->var.xres * bpp / (4 * 16));
1da177e4 1080 info->fix.visual = (bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
245a2c2c
KH
1081 info->fix.line_length = info->var.xres * (bpp >> 3);
1082 info->cmap.len = (bpp == 8) ? 256 : 16;
1da177e4
LT
1083 debug("exit\n");
1084 return 0;
1085}
1086
1087/* Set one color register */
1088static int tridentfb_setcolreg(unsigned regno, unsigned red, unsigned green,
245a2c2c
KH
1089 unsigned blue, unsigned transp,
1090 struct fb_info *info)
1da177e4
LT
1091{
1092 int bpp = info->var.bits_per_pixel;
306fa6f6 1093 struct tridentfb_par *par = info->par;
1da177e4
LT
1094
1095 if (regno >= info->cmap.len)
1096 return 1;
1097
973d9ab2 1098 if (bpp == 8) {
10172ed6
KH
1099 t_outb(par, 0xFF, VGA_PEL_MSK);
1100 t_outb(par, regno, VGA_PEL_IW);
1da177e4 1101
10172ed6
KH
1102 t_outb(par, red >> 10, VGA_PEL_D);
1103 t_outb(par, green >> 10, VGA_PEL_D);
1104 t_outb(par, blue >> 10, VGA_PEL_D);
1da177e4 1105
973d9ab2
AD
1106 } else if (regno < 16) {
1107 if (bpp == 16) { /* RGB 565 */
1108 u32 col;
1109
1110 col = (red & 0xF800) | ((green & 0xFC00) >> 5) |
1111 ((blue & 0xF800) >> 11);
1112 col |= col << 16;
1113 ((u32 *)(info->pseudo_palette))[regno] = col;
1114 } else if (bpp == 32) /* ARGB 8888 */
1115 ((u32*)info->pseudo_palette)[regno] =
245a2c2c
KH
1116 ((transp & 0xFF00) << 16) |
1117 ((red & 0xFF00) << 8) |
973d9ab2 1118 ((green & 0xFF00)) |
245a2c2c 1119 ((blue & 0xFF00) >> 8);
973d9ab2 1120 }
1da177e4 1121
245a2c2c 1122/* debug("exit\n"); */
1da177e4
LT
1123 return 0;
1124}
1125
1126/* Try blanking the screen.For flat panels it does nothing */
1127static int tridentfb_blank(int blank_mode, struct fb_info *info)
1128{
245a2c2c 1129 unsigned char PMCont, DPMSCont;
306fa6f6 1130 struct tridentfb_par *par = info->par;
1da177e4
LT
1131
1132 debug("enter\n");
6eed8e1e 1133 if (par->flatpanel)
1da177e4 1134 return 0;
306fa6f6
KH
1135 t_outb(par, 0x04, 0x83C8); /* Read DPMS Control */
1136 PMCont = t_inb(par, 0x83C6) & 0xFC;
1137 DPMSCont = read3CE(par, PowerStatus) & 0xFC;
245a2c2c 1138 switch (blank_mode) {
1da177e4
LT
1139 case FB_BLANK_UNBLANK:
1140 /* Screen: On, HSync: On, VSync: On */
1141 case FB_BLANK_NORMAL:
1142 /* Screen: Off, HSync: On, VSync: On */
1143 PMCont |= 0x03;
1144 DPMSCont |= 0x00;
1145 break;
1146 case FB_BLANK_HSYNC_SUSPEND:
1147 /* Screen: Off, HSync: Off, VSync: On */
1148 PMCont |= 0x02;
1149 DPMSCont |= 0x01;
1150 break;
1151 case FB_BLANK_VSYNC_SUSPEND:
1152 /* Screen: Off, HSync: On, VSync: Off */
1153 PMCont |= 0x02;
1154 DPMSCont |= 0x02;
1155 break;
1156 case FB_BLANK_POWERDOWN:
1157 /* Screen: Off, HSync: Off, VSync: Off */
1158 PMCont |= 0x00;
1159 DPMSCont |= 0x03;
1160 break;
245a2c2c 1161 }
1da177e4 1162
306fa6f6
KH
1163 write3CE(par, PowerStatus, DPMSCont);
1164 t_outb(par, 4, 0x83C8);
1165 t_outb(par, PMCont, 0x83C6);
1da177e4
LT
1166
1167 debug("exit\n");
1168
1169 /* let fbcon do a softblank for us */
1170 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1171}
1172
245a2c2c
KH
1173static struct fb_ops tridentfb_ops = {
1174 .owner = THIS_MODULE,
1175 .fb_setcolreg = tridentfb_setcolreg,
1176 .fb_pan_display = tridentfb_pan_display,
1177 .fb_blank = tridentfb_blank,
1178 .fb_check_var = tridentfb_check_var,
1179 .fb_set_par = tridentfb_set_par,
1180 .fb_fillrect = tridentfb_fillrect,
1181 .fb_copyarea = tridentfb_copyarea,
1182 .fb_imageblit = cfb_imageblit,
1183};
1184
e09ed099
KH
1185static int __devinit trident_pci_probe(struct pci_dev *dev,
1186 const struct pci_device_id *id)
1da177e4
LT
1187{
1188 int err;
1189 unsigned char revision;
e09ed099
KH
1190 struct fb_info *info;
1191 struct tridentfb_par *default_par;
122e8ad3
KH
1192 int defaultaccel;
1193 int chip3D;
1194 int chip_id;
1da177e4
LT
1195
1196 err = pci_enable_device(dev);
1197 if (err)
1198 return err;
1199
e09ed099
KH
1200 info = framebuffer_alloc(sizeof(struct tridentfb_par), &dev->dev);
1201 if (!info)
1202 return -ENOMEM;
1203 default_par = info->par;
1204
1da177e4
LT
1205 chip_id = id->device;
1206
245a2c2c 1207 if (chip_id == CYBERBLADEi1)
9fa68eae
KP
1208 output("*** Please do use cyblafb, Cyberblade/i1 support "
1209 "will soon be removed from tridentfb!\n");
1210
1211
1da177e4 1212 /* If PCI id is 0x9660 then further detect chip type */
245a2c2c 1213
1da177e4 1214 if (chip_id == TGUI9660) {
10172ed6 1215 revision = vga_io_rseq(RevisionID);
245a2c2c 1216
1da177e4 1217 switch (revision) {
245a2c2c
KH
1218 case 0x22:
1219 case 0x23:
1220 chip_id = CYBER9397;
1221 break;
1222 case 0x2A:
1223 chip_id = CYBER9397DVD;
1224 break;
1225 case 0x30:
1226 case 0x33:
1227 case 0x34:
1228 case 0x35:
1229 case 0x38:
1230 case 0x3A:
1231 case 0xB3:
1232 chip_id = CYBER9385;
1233 break;
1234 case 0x40 ... 0x43:
1235 chip_id = CYBER9382;
1236 break;
1237 case 0x4A:
1238 chip_id = CYBER9388;
1239 break;
1240 default:
1241 break;
1da177e4
LT
1242 }
1243 }
1244
1245 chip3D = is3Dchip(chip_id);
1da177e4
LT
1246
1247 if (is_xp(chip_id)) {
d9cad04b
KH
1248 default_par->init_accel = xp_init_accel;
1249 default_par->wait_engine = xp_wait_engine;
1250 default_par->fill_rect = xp_fill_rect;
1251 default_par->copy_rect = xp_copy_rect;
245a2c2c 1252 } else if (is_blade(chip_id)) {
d9cad04b
KH
1253 default_par->init_accel = blade_init_accel;
1254 default_par->wait_engine = blade_wait_engine;
1255 default_par->fill_rect = blade_fill_rect;
1256 default_par->copy_rect = blade_copy_rect;
1da177e4 1257 } else {
d9cad04b
KH
1258 default_par->init_accel = image_init_accel;
1259 default_par->wait_engine = image_wait_engine;
1260 default_par->fill_rect = image_fill_rect;
1261 default_par->copy_rect = image_copy_rect;
1da177e4
LT
1262 }
1263
122e8ad3
KH
1264 default_par->chip_id = chip_id;
1265
1da177e4
LT
1266 /* acceleration is on by default for 3D chips */
1267 defaultaccel = chip3D && !noaccel;
1268
1da177e4 1269 /* setup MMIO region */
245a2c2c
KH
1270 tridentfb_fix.mmio_start = pci_resource_start(dev, 1);
1271 tridentfb_fix.mmio_len = chip3D ? 0x20000 : 0x10000;
1da177e4
LT
1272
1273 if (!request_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len, "tridentfb")) {
1274 debug("request_region failed!\n");
1275 return -1;
1276 }
1277
e09ed099
KH
1278 default_par->io_virt = ioremap_nocache(tridentfb_fix.mmio_start,
1279 tridentfb_fix.mmio_len);
1da177e4 1280
e09ed099 1281 if (!default_par->io_virt) {
1da177e4 1282 debug("ioremap failed\n");
e8ed857c
KH
1283 err = -1;
1284 goto out_unmap1;
1da177e4
LT
1285 }
1286
1287 enable_mmio();
245a2c2c 1288
1da177e4 1289 /* setup framebuffer memory */
245a2c2c 1290 tridentfb_fix.smem_start = pci_resource_start(dev, 0);
e09ed099 1291 tridentfb_fix.smem_len = get_memsize(default_par);
245a2c2c 1292
1da177e4
LT
1293 if (!request_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len, "tridentfb")) {
1294 debug("request_mem_region failed!\n");
e09ed099 1295 disable_mmio(info->par);
a02f6402 1296 err = -1;
e8ed857c 1297 goto out_unmap1;
1da177e4
LT
1298 }
1299
e09ed099
KH
1300 info->screen_base = ioremap_nocache(tridentfb_fix.smem_start,
1301 tridentfb_fix.smem_len);
1da177e4 1302
e09ed099 1303 if (!info->screen_base) {
1da177e4 1304 debug("ioremap failed\n");
a02f6402 1305 err = -1;
e8ed857c 1306 goto out_unmap2;
1da177e4
LT
1307 }
1308
1309 output("%s board found\n", pci_name(dev));
6eed8e1e 1310 default_par->flatpanel = is_flatpanel(default_par);
1da177e4 1311
6eed8e1e 1312 if (default_par->flatpanel)
e09ed099 1313 nativex = get_nativex(default_par);
1da177e4 1314
e09ed099
KH
1315 info->fix = tridentfb_fix;
1316 info->fbops = &tridentfb_ops;
1da177e4
LT
1317
1318
e09ed099 1319 info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
1da177e4 1320#ifdef CONFIG_FB_TRIDENT_ACCEL
e09ed099 1321 info->flags |= FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
1da177e4 1322#endif
ea8ee55c 1323 if (!fb_find_mode(&info->var, info,
07f41e45 1324 mode_option, NULL, 0, NULL, bpp)) {
a02f6402 1325 err = -EINVAL;
e8ed857c 1326 goto out_unmap2;
a02f6402 1327 }
e09ed099 1328 err = fb_alloc_cmap(&info->cmap, 256, 0);
e8ed857c
KH
1329 if (err < 0)
1330 goto out_unmap2;
1331
d9cad04b 1332 if (defaultaccel && default_par->init_accel)
ea8ee55c 1333 info->var.accel_flags |= FB_ACCELF_TEXT;
1da177e4 1334 else
ea8ee55c
KH
1335 info->var.accel_flags &= ~FB_ACCELF_TEXT;
1336 info->var.activate |= FB_ACTIVATE_NOW;
e09ed099
KH
1337 info->device = &dev->dev;
1338 if (register_framebuffer(info) < 0) {
1da177e4 1339 printk(KERN_ERR "tridentfb: could not register Trident framebuffer\n");
e09ed099 1340 fb_dealloc_cmap(&info->cmap);
a02f6402 1341 err = -EINVAL;
e8ed857c 1342 goto out_unmap2;
1da177e4
LT
1343 }
1344 output("fb%d: %s frame buffer device %dx%d-%dbpp\n",
ea8ee55c
KH
1345 info->node, info->fix.id, info->var.xres,
1346 info->var.yres, info->var.bits_per_pixel);
e09ed099
KH
1347
1348 pci_set_drvdata(dev, info);
1da177e4 1349 return 0;
a02f6402 1350
e8ed857c 1351out_unmap2:
e09ed099
KH
1352 if (info->screen_base)
1353 iounmap(info->screen_base);
e8ed857c 1354 release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len);
e09ed099 1355 disable_mmio(info->par);
e8ed857c 1356out_unmap1:
e09ed099
KH
1357 if (default_par->io_virt)
1358 iounmap(default_par->io_virt);
e8ed857c 1359 release_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len);
e09ed099 1360 framebuffer_release(info);
a02f6402 1361 return err;
1da177e4
LT
1362}
1363
245a2c2c 1364static void __devexit trident_pci_remove(struct pci_dev *dev)
1da177e4 1365{
e09ed099
KH
1366 struct fb_info *info = pci_get_drvdata(dev);
1367 struct tridentfb_par *par = info->par;
1368
1369 unregister_framebuffer(info);
1da177e4 1370 iounmap(par->io_virt);
e09ed099 1371 iounmap(info->screen_base);
1da177e4 1372 release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len);
e8ed857c 1373 release_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len);
e09ed099
KH
1374 pci_set_drvdata(dev, NULL);
1375 framebuffer_release(info);
1da177e4
LT
1376}
1377
1378/* List of boards that we are trying to support */
1379static struct pci_device_id trident_devices[] = {
245a2c2c
KH
1380 {PCI_VENDOR_ID_TRIDENT, BLADE3D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1381 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1382 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi7D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1383 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1384 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1385 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1386 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1387 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEE4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1388 {PCI_VENDOR_ID_TRIDENT, TGUI9660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1389 {PCI_VENDOR_ID_TRIDENT, IMAGE975, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1390 {PCI_VENDOR_ID_TRIDENT, IMAGE985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1391 {PCI_VENDOR_ID_TRIDENT, CYBER9320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1392 {PCI_VENDOR_ID_TRIDENT, CYBER9388, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1393 {PCI_VENDOR_ID_TRIDENT, CYBER9520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1394 {PCI_VENDOR_ID_TRIDENT, CYBER9525DVD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1395 {PCI_VENDOR_ID_TRIDENT, CYBER9397, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1396 {PCI_VENDOR_ID_TRIDENT, CYBER9397DVD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1397 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPAi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1398 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPm8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1399 {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPm16, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1da177e4 1400 {0,}
245a2c2c
KH
1401};
1402
1403MODULE_DEVICE_TABLE(pci, trident_devices);
1da177e4
LT
1404
1405static struct pci_driver tridentfb_pci_driver = {
245a2c2c
KH
1406 .name = "tridentfb",
1407 .id_table = trident_devices,
1408 .probe = trident_pci_probe,
1409 .remove = __devexit_p(trident_pci_remove)
1da177e4
LT
1410};
1411
1412/*
1413 * Parse user specified options (`video=trident:')
1414 * example:
245a2c2c 1415 * video=trident:800x600,bpp=16,noaccel
1da177e4
LT
1416 */
1417#ifndef MODULE
07f41e45 1418static int __init tridentfb_setup(char *options)
1da177e4 1419{
245a2c2c 1420 char *opt;
1da177e4
LT
1421 if (!options || !*options)
1422 return 0;
245a2c2c
KH
1423 while ((opt = strsep(&options, ",")) != NULL) {
1424 if (!*opt)
1425 continue;
1426 if (!strncmp(opt, "noaccel", 7))
1da177e4 1427 noaccel = 1;
245a2c2c 1428 else if (!strncmp(opt, "fp", 2))
6eed8e1e 1429 fp = 1;
245a2c2c 1430 else if (!strncmp(opt, "crt", 3))
6eed8e1e 1431 fp = 0;
245a2c2c
KH
1432 else if (!strncmp(opt, "bpp=", 4))
1433 bpp = simple_strtoul(opt + 4, NULL, 0);
1434 else if (!strncmp(opt, "center", 6))
1da177e4 1435 center = 1;
245a2c2c 1436 else if (!strncmp(opt, "stretch", 7))
1da177e4 1437 stretch = 1;
245a2c2c
KH
1438 else if (!strncmp(opt, "memsize=", 8))
1439 memsize = simple_strtoul(opt + 8, NULL, 0);
1440 else if (!strncmp(opt, "memdiff=", 8))
1441 memdiff = simple_strtoul(opt + 8, NULL, 0);
1442 else if (!strncmp(opt, "nativex=", 8))
1443 nativex = simple_strtoul(opt + 8, NULL, 0);
1da177e4 1444 else
07f41e45 1445 mode_option = opt;
1da177e4
LT
1446 }
1447 return 0;
1448}
1449#endif
1450
1451static int __init tridentfb_init(void)
1452{
1453#ifndef MODULE
1454 char *option = NULL;
1455
1456 if (fb_get_options("tridentfb", &option))
1457 return -ENODEV;
1458 tridentfb_setup(option);
1459#endif
1460 output("Trident framebuffer %s initializing\n", VERSION);
1461 return pci_register_driver(&tridentfb_pci_driver);
1462}
1463
1464static void __exit tridentfb_exit(void)
1465{
1466 pci_unregister_driver(&tridentfb_pci_driver);
1467}
1468
1da177e4
LT
1469module_init(tridentfb_init);
1470module_exit(tridentfb_exit);
1471
1472MODULE_AUTHOR("Jani Monoses <jani@iv.ro>");
1473MODULE_DESCRIPTION("Framebuffer driver for Trident cards");
1474MODULE_LICENSE("GPL");
1475
This page took 0.475535 seconds and 5 git commands to generate.