diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/video/tdfxfb.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/video/tdfxfb.c')
-rw-r--r-- | drivers/video/tdfxfb.c | 1366 |
1 files changed, 1366 insertions, 0 deletions
diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c new file mode 100644 index 000000000000..c34ba39b6f7e --- /dev/null +++ b/drivers/video/tdfxfb.c | |||
@@ -0,0 +1,1366 @@ | |||
1 | /* | ||
2 | * | ||
3 | * tdfxfb.c | ||
4 | * | ||
5 | * Author: Hannu Mallat <hmallat@cc.hut.fi> | ||
6 | * | ||
7 | * Copyright © 1999 Hannu Mallat | ||
8 | * All rights reserved | ||
9 | * | ||
10 | * Created : Thu Sep 23 18:17:43 1999, hmallat | ||
11 | * Last modified: Tue Nov 2 21:19:47 1999, hmallat | ||
12 | * | ||
13 | * Lots of the information here comes from the Daryll Strauss' Banshee | ||
14 | * patches to the XF86 server, and the rest comes from the 3dfx | ||
15 | * Banshee specification. I'm very much indebted to Daryll for his | ||
16 | * work on the X server. | ||
17 | * | ||
18 | * Voodoo3 support was contributed Harold Oga. Lots of additions | ||
19 | * (proper acceleration, 24 bpp, hardware cursor) and bug fixes by Attila | ||
20 | * Kesmarki. Thanks guys! | ||
21 | * | ||
22 | * Voodoo1 and Voodoo2 support aren't relevant to this driver as they | ||
23 | * behave very differently from the Voodoo3/4/5. For anyone wanting to | ||
24 | * use frame buffer on the Voodoo1/2, see the sstfb driver (which is | ||
25 | * located at http://www.sourceforge.net/projects/sstfb). | ||
26 | * | ||
27 | * While I _am_ grateful to 3Dfx for releasing the specs for Banshee, | ||
28 | * I do wish the next version is a bit more complete. Without the XF86 | ||
29 | * patches I couldn't have gotten even this far... for instance, the | ||
30 | * extensions to the VGA register set go completely unmentioned in the | ||
31 | * spec! Also, lots of references are made to the 'SST core', but no | ||
32 | * spec is publicly available, AFAIK. | ||
33 | * | ||
34 | * The structure of this driver comes pretty much from the Permedia | ||
35 | * driver by Ilario Nardinocchi, which in turn is based on skeletonfb. | ||
36 | * | ||
37 | * TODO: | ||
38 | * - support for 16/32 bpp needs fixing (funky bootup penguin) | ||
39 | * - multihead support (basically need to support an array of fb_infos) | ||
40 | * - support other architectures (PPC, Alpha); does the fact that the VGA | ||
41 | * core can be accessed only thru I/O (not memory mapped) complicate | ||
42 | * things? | ||
43 | * | ||
44 | * Version history: | ||
45 | * | ||
46 | * 0.1.4 (released 2002-05-28) ported over to new fbdev api by James Simmons | ||
47 | * | ||
48 | * 0.1.3 (released 1999-11-02) added Attila's panning support, code | ||
49 | * reorg, hwcursor address page size alignment | ||
50 | * (for mmaping both frame buffer and regs), | ||
51 | * and my changes to get rid of hardcoded | ||
52 | * VGA i/o register locations (uses PCI | ||
53 | * configuration info now) | ||
54 | * 0.1.2 (released 1999-10-19) added Attila Kesmarki's bug fixes and | ||
55 | * improvements | ||
56 | * 0.1.1 (released 1999-10-07) added Voodoo3 support by Harold Oga. | ||
57 | * 0.1.0 (released 1999-10-06) initial version | ||
58 | * | ||
59 | */ | ||
60 | |||
61 | #include <linux/config.h> | ||
62 | #include <linux/module.h> | ||
63 | #include <linux/kernel.h> | ||
64 | #include <linux/errno.h> | ||
65 | #include <linux/string.h> | ||
66 | #include <linux/mm.h> | ||
67 | #include <linux/tty.h> | ||
68 | #include <linux/slab.h> | ||
69 | #include <linux/delay.h> | ||
70 | #include <linux/interrupt.h> | ||
71 | #include <linux/fb.h> | ||
72 | #include <linux/init.h> | ||
73 | #include <linux/pci.h> | ||
74 | #include <linux/nvram.h> | ||
75 | #include <asm/io.h> | ||
76 | #include <linux/timer.h> | ||
77 | #include <linux/spinlock.h> | ||
78 | |||
79 | #include <video/tdfx.h> | ||
80 | |||
81 | #undef TDFXFB_DEBUG | ||
82 | #ifdef TDFXFB_DEBUG | ||
83 | #define DPRINTK(a,b...) printk(KERN_DEBUG "fb: %s: " a, __FUNCTION__ , ## b) | ||
84 | #else | ||
85 | #define DPRINTK(a,b...) | ||
86 | #endif | ||
87 | |||
88 | #define BANSHEE_MAX_PIXCLOCK 270000 | ||
89 | #define VOODOO3_MAX_PIXCLOCK 300000 | ||
90 | #define VOODOO5_MAX_PIXCLOCK 350000 | ||
91 | |||
92 | static struct fb_fix_screeninfo tdfx_fix __devinitdata = { | ||
93 | .id = "3Dfx", | ||
94 | .type = FB_TYPE_PACKED_PIXELS, | ||
95 | .visual = FB_VISUAL_PSEUDOCOLOR, | ||
96 | .ypanstep = 1, | ||
97 | .ywrapstep = 1, | ||
98 | .accel = FB_ACCEL_3DFX_BANSHEE | ||
99 | }; | ||
100 | |||
101 | static struct fb_var_screeninfo tdfx_var __devinitdata = { | ||
102 | /* "640x480, 8 bpp @ 60 Hz */ | ||
103 | .xres = 640, | ||
104 | .yres = 480, | ||
105 | .xres_virtual = 640, | ||
106 | .yres_virtual = 1024, | ||
107 | .bits_per_pixel =8, | ||
108 | .red = {0, 8, 0}, | ||
109 | .blue = {0, 8, 0}, | ||
110 | .green = {0, 8, 0}, | ||
111 | .activate = FB_ACTIVATE_NOW, | ||
112 | .height = -1, | ||
113 | .width = -1, | ||
114 | .accel_flags = FB_ACCELF_TEXT, | ||
115 | .pixclock = 39722, | ||
116 | .left_margin = 40, | ||
117 | .right_margin = 24, | ||
118 | .upper_margin = 32, | ||
119 | .lower_margin = 11, | ||
120 | .hsync_len = 96, | ||
121 | .vsync_len = 2, | ||
122 | .vmode = FB_VMODE_NONINTERLACED | ||
123 | }; | ||
124 | |||
125 | /* | ||
126 | * PCI driver prototypes | ||
127 | */ | ||
128 | static int __devinit tdfxfb_probe(struct pci_dev *pdev, | ||
129 | const struct pci_device_id *id); | ||
130 | static void __devexit tdfxfb_remove(struct pci_dev *pdev); | ||
131 | |||
132 | static struct pci_device_id tdfxfb_id_table[] = { | ||
133 | { PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE, | ||
134 | PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16, | ||
135 | 0xff0000, 0 }, | ||
136 | { PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3, | ||
137 | PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16, | ||
138 | 0xff0000, 0 }, | ||
139 | { PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO5, | ||
140 | PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16, | ||
141 | 0xff0000, 0 }, | ||
142 | { 0, } | ||
143 | }; | ||
144 | |||
145 | static struct pci_driver tdfxfb_driver = { | ||
146 | .name = "tdfxfb", | ||
147 | .id_table = tdfxfb_id_table, | ||
148 | .probe = tdfxfb_probe, | ||
149 | .remove = __devexit_p(tdfxfb_remove), | ||
150 | }; | ||
151 | |||
152 | MODULE_DEVICE_TABLE(pci, tdfxfb_id_table); | ||
153 | |||
154 | /* | ||
155 | * Frame buffer device API | ||
156 | */ | ||
157 | static int tdfxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb); | ||
158 | static int tdfxfb_set_par(struct fb_info *info); | ||
159 | static int tdfxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | ||
160 | u_int transp, struct fb_info *info); | ||
161 | static int tdfxfb_blank(int blank, struct fb_info *info); | ||
162 | static int tdfxfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info); | ||
163 | static int banshee_wait_idle(struct fb_info *info); | ||
164 | #ifdef CONFIG_FB_3DFX_ACCEL | ||
165 | static void tdfxfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect); | ||
166 | static void tdfxfb_copyarea(struct fb_info *info, const struct fb_copyarea *area); | ||
167 | static void tdfxfb_imageblit(struct fb_info *info, const struct fb_image *image); | ||
168 | #endif /* CONFIG_FB_3DFX_ACCEL */ | ||
169 | |||
170 | static struct fb_ops tdfxfb_ops = { | ||
171 | .owner = THIS_MODULE, | ||
172 | .fb_check_var = tdfxfb_check_var, | ||
173 | .fb_set_par = tdfxfb_set_par, | ||
174 | .fb_setcolreg = tdfxfb_setcolreg, | ||
175 | .fb_blank = tdfxfb_blank, | ||
176 | .fb_pan_display = tdfxfb_pan_display, | ||
177 | .fb_sync = banshee_wait_idle, | ||
178 | #ifdef CONFIG_FB_3DFX_ACCEL | ||
179 | .fb_fillrect = tdfxfb_fillrect, | ||
180 | .fb_copyarea = tdfxfb_copyarea, | ||
181 | .fb_imageblit = tdfxfb_imageblit, | ||
182 | #else | ||
183 | .fb_fillrect = cfb_fillrect, | ||
184 | .fb_copyarea = cfb_copyarea, | ||
185 | .fb_imageblit = cfb_imageblit, | ||
186 | #endif | ||
187 | .fb_cursor = soft_cursor, | ||
188 | }; | ||
189 | |||
190 | /* | ||
191 | * do_xxx: Hardware-specific functions | ||
192 | */ | ||
193 | static u32 do_calc_pll(int freq, int *freq_out); | ||
194 | static void do_write_regs(struct fb_info *info, struct banshee_reg *reg); | ||
195 | static unsigned long do_lfb_size(struct tdfx_par *par, unsigned short); | ||
196 | |||
197 | /* | ||
198 | * Driver data | ||
199 | */ | ||
200 | static int nopan = 0; | ||
201 | static int nowrap = 1; // not implemented (yet) | ||
202 | static char *mode_option __devinitdata = NULL; | ||
203 | |||
204 | /* ------------------------------------------------------------------------- | ||
205 | * Hardware-specific funcions | ||
206 | * ------------------------------------------------------------------------- */ | ||
207 | |||
208 | #ifdef VGA_REG_IO | ||
209 | static inline u8 vga_inb(struct tdfx_par *par, u32 reg) { return inb(reg); } | ||
210 | |||
211 | static inline void vga_outb(struct tdfx_par *par, u32 reg, u8 val) { outb(val, reg); } | ||
212 | #else | ||
213 | static inline u8 vga_inb(struct tdfx_par *par, u32 reg) { | ||
214 | return inb(par->iobase + reg - 0x300); | ||
215 | } | ||
216 | static inline void vga_outb(struct tdfx_par *par, u32 reg, u8 val) { | ||
217 | outb(val, par->iobase + reg - 0x300); | ||
218 | } | ||
219 | #endif | ||
220 | |||
221 | static inline void gra_outb(struct tdfx_par *par, u32 idx, u8 val) { | ||
222 | vga_outb(par, GRA_I, idx); vga_outb(par, GRA_D, val); | ||
223 | } | ||
224 | |||
225 | static inline void seq_outb(struct tdfx_par *par, u32 idx, u8 val) { | ||
226 | vga_outb(par, SEQ_I, idx); vga_outb(par, SEQ_D, val); | ||
227 | } | ||
228 | |||
229 | static inline u8 seq_inb(struct tdfx_par *par, u32 idx) { | ||
230 | vga_outb(par, SEQ_I, idx); return vga_inb(par, SEQ_D); | ||
231 | } | ||
232 | |||
233 | static inline void crt_outb(struct tdfx_par *par, u32 idx, u8 val) { | ||
234 | vga_outb(par, CRT_I, idx); vga_outb(par, CRT_D, val); | ||
235 | } | ||
236 | |||
237 | static inline u8 crt_inb(struct tdfx_par *par, u32 idx) { | ||
238 | vga_outb(par, CRT_I, idx); return vga_inb(par, CRT_D); | ||
239 | } | ||
240 | |||
241 | static inline void att_outb(struct tdfx_par *par, u32 idx, u8 val) | ||
242 | { | ||
243 | unsigned char tmp; | ||
244 | |||
245 | tmp = vga_inb(par, IS1_R); | ||
246 | vga_outb(par, ATT_IW, idx); | ||
247 | vga_outb(par, ATT_IW, val); | ||
248 | } | ||
249 | |||
250 | static inline void vga_disable_video(struct tdfx_par *par) | ||
251 | { | ||
252 | unsigned char s; | ||
253 | |||
254 | s = seq_inb(par, 0x01) | 0x20; | ||
255 | seq_outb(par, 0x00, 0x01); | ||
256 | seq_outb(par, 0x01, s); | ||
257 | seq_outb(par, 0x00, 0x03); | ||
258 | } | ||
259 | |||
260 | static inline void vga_enable_video(struct tdfx_par *par) | ||
261 | { | ||
262 | unsigned char s; | ||
263 | |||
264 | s = seq_inb(par, 0x01) & 0xdf; | ||
265 | seq_outb(par, 0x00, 0x01); | ||
266 | seq_outb(par, 0x01, s); | ||
267 | seq_outb(par, 0x00, 0x03); | ||
268 | } | ||
269 | |||
270 | static inline void vga_enable_palette(struct tdfx_par *par) | ||
271 | { | ||
272 | vga_inb(par, IS1_R); | ||
273 | vga_outb(par, ATT_IW, 0x20); | ||
274 | } | ||
275 | |||
276 | static inline u32 tdfx_inl(struct tdfx_par *par, unsigned int reg) | ||
277 | { | ||
278 | return readl(par->regbase_virt + reg); | ||
279 | } | ||
280 | |||
281 | static inline void tdfx_outl(struct tdfx_par *par, unsigned int reg, u32 val) | ||
282 | { | ||
283 | writel(val, par->regbase_virt + reg); | ||
284 | } | ||
285 | |||
286 | static inline void banshee_make_room(struct tdfx_par *par, int size) | ||
287 | { | ||
288 | /* Note: The Voodoo3's onboard FIFO has 32 slots. This loop | ||
289 | * won't quit if you ask for more. */ | ||
290 | while((tdfx_inl(par, STATUS) & 0x1f) < size-1); | ||
291 | } | ||
292 | |||
293 | static int banshee_wait_idle(struct fb_info *info) | ||
294 | { | ||
295 | struct tdfx_par *par = (struct tdfx_par *) info->par; | ||
296 | int i = 0; | ||
297 | |||
298 | banshee_make_room(par, 1); | ||
299 | tdfx_outl(par, COMMAND_3D, COMMAND_3D_NOP); | ||
300 | |||
301 | while(1) { | ||
302 | i = (tdfx_inl(par, STATUS) & STATUS_BUSY) ? 0 : i + 1; | ||
303 | if(i == 3) break; | ||
304 | } | ||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | /* | ||
309 | * Set the color of a palette entry in 8bpp mode | ||
310 | */ | ||
311 | static inline void do_setpalentry(struct tdfx_par *par, unsigned regno, u32 c) | ||
312 | { | ||
313 | banshee_make_room(par, 2); | ||
314 | tdfx_outl(par, DACADDR, regno); | ||
315 | tdfx_outl(par, DACDATA, c); | ||
316 | } | ||
317 | |||
318 | static u32 do_calc_pll(int freq, int* freq_out) | ||
319 | { | ||
320 | int m, n, k, best_m, best_n, best_k, f_cur, best_error; | ||
321 | int fref = 14318; | ||
322 | |||
323 | /* this really could be done with more intelligence -- | ||
324 | 255*63*4 = 64260 iterations is silly */ | ||
325 | best_error = freq; | ||
326 | best_n = best_m = best_k = 0; | ||
327 | for (n = 1; n < 256; n++) { | ||
328 | for (m = 1; m < 64; m++) { | ||
329 | for (k = 0; k < 4; k++) { | ||
330 | f_cur = fref*(n + 2)/(m + 2)/(1 << k); | ||
331 | if (abs(f_cur - freq) < best_error) { | ||
332 | best_error = abs(f_cur-freq); | ||
333 | best_n = n; | ||
334 | best_m = m; | ||
335 | best_k = k; | ||
336 | } | ||
337 | } | ||
338 | } | ||
339 | } | ||
340 | n = best_n; | ||
341 | m = best_m; | ||
342 | k = best_k; | ||
343 | *freq_out = fref*(n + 2)/(m + 2)/(1 << k); | ||
344 | return (n << 8) | (m << 2) | k; | ||
345 | } | ||
346 | |||
347 | static void do_write_regs(struct fb_info *info, struct banshee_reg* reg) | ||
348 | { | ||
349 | struct tdfx_par *par = (struct tdfx_par *) info->par; | ||
350 | int i; | ||
351 | |||
352 | banshee_wait_idle(info); | ||
353 | |||
354 | tdfx_outl(par, MISCINIT1, tdfx_inl(par, MISCINIT1) | 0x01); | ||
355 | |||
356 | crt_outb(par, 0x11, crt_inb(par, 0x11) & 0x7f); /* CRT unprotect */ | ||
357 | |||
358 | banshee_make_room(par, 3); | ||
359 | tdfx_outl(par, VGAINIT1, reg->vgainit1 & 0x001FFFFF); | ||
360 | tdfx_outl(par, VIDPROCCFG, reg->vidcfg & ~0x00000001); | ||
361 | #if 0 | ||
362 | tdfx_outl(par, PLLCTRL1, reg->mempll); | ||
363 | tdfx_outl(par, PLLCTRL2, reg->gfxpll); | ||
364 | #endif | ||
365 | tdfx_outl(par, PLLCTRL0, reg->vidpll); | ||
366 | |||
367 | vga_outb(par, MISC_W, reg->misc[0x00] | 0x01); | ||
368 | |||
369 | for (i = 0; i < 5; i++) | ||
370 | seq_outb(par, i, reg->seq[i]); | ||
371 | |||
372 | for (i = 0; i < 25; i++) | ||
373 | crt_outb(par, i, reg->crt[i]); | ||
374 | |||
375 | for (i = 0; i < 9; i++) | ||
376 | gra_outb(par, i, reg->gra[i]); | ||
377 | |||
378 | for (i = 0; i < 21; i++) | ||
379 | att_outb(par, i, reg->att[i]); | ||
380 | |||
381 | crt_outb(par, 0x1a, reg->ext[0]); | ||
382 | crt_outb(par, 0x1b, reg->ext[1]); | ||
383 | |||
384 | vga_enable_palette(par); | ||
385 | vga_enable_video(par); | ||
386 | |||
387 | banshee_make_room(par, 11); | ||
388 | tdfx_outl(par, VGAINIT0, reg->vgainit0); | ||
389 | tdfx_outl(par, DACMODE, reg->dacmode); | ||
390 | tdfx_outl(par, VIDDESKSTRIDE, reg->stride); | ||
391 | tdfx_outl(par, HWCURPATADDR, 0); | ||
392 | |||
393 | tdfx_outl(par, VIDSCREENSIZE,reg->screensize); | ||
394 | tdfx_outl(par, VIDDESKSTART, reg->startaddr); | ||
395 | tdfx_outl(par, VIDPROCCFG, reg->vidcfg); | ||
396 | tdfx_outl(par, VGAINIT1, reg->vgainit1); | ||
397 | tdfx_outl(par, MISCINIT0, reg->miscinit0); | ||
398 | |||
399 | banshee_make_room(par, 8); | ||
400 | tdfx_outl(par, SRCBASE, reg->srcbase); | ||
401 | tdfx_outl(par, DSTBASE, reg->dstbase); | ||
402 | tdfx_outl(par, COMMANDEXTRA_2D, 0); | ||
403 | tdfx_outl(par, CLIP0MIN, 0); | ||
404 | tdfx_outl(par, CLIP0MAX, 0x0fff0fff); | ||
405 | tdfx_outl(par, CLIP1MIN, 0); | ||
406 | tdfx_outl(par, CLIP1MAX, 0x0fff0fff); | ||
407 | tdfx_outl(par, SRCXY, 0); | ||
408 | |||
409 | banshee_wait_idle(info); | ||
410 | } | ||
411 | |||
412 | static unsigned long do_lfb_size(struct tdfx_par *par, unsigned short dev_id) | ||
413 | { | ||
414 | u32 draminit0 = 0; | ||
415 | u32 draminit1 = 0; | ||
416 | u32 miscinit1 = 0; | ||
417 | u32 lfbsize = 0; | ||
418 | int sgram_p = 0; | ||
419 | |||
420 | draminit0 = tdfx_inl(par, DRAMINIT0); | ||
421 | draminit1 = tdfx_inl(par, DRAMINIT1); | ||
422 | |||
423 | if ((dev_id == PCI_DEVICE_ID_3DFX_BANSHEE) || | ||
424 | (dev_id == PCI_DEVICE_ID_3DFX_VOODOO3)) { | ||
425 | sgram_p = (draminit1 & DRAMINIT1_MEM_SDRAM) ? 0 : 1; | ||
426 | |||
427 | lfbsize = sgram_p ? | ||
428 | (((draminit0 & DRAMINIT0_SGRAM_NUM) ? 2 : 1) * | ||
429 | ((draminit0 & DRAMINIT0_SGRAM_TYPE) ? 8 : 4) * 1024 * 1024) : | ||
430 | 16 * 1024 * 1024; | ||
431 | } else { | ||
432 | /* Voodoo4/5 */ | ||
433 | u32 chips, psize, banks; | ||
434 | |||
435 | chips = ((draminit0 & (1 << 26)) == 0) ? 4 : 8; | ||
436 | psize = 1 << ((draminit0 & 0x38000000) >> 28); | ||
437 | banks = ((draminit0 & (1 << 30)) == 0) ? 2 : 4; | ||
438 | lfbsize = chips * psize * banks; | ||
439 | lfbsize <<= 20; | ||
440 | } | ||
441 | /* disable block writes for SDRAM (why?) */ | ||
442 | miscinit1 = tdfx_inl(par, MISCINIT1); | ||
443 | miscinit1 |= sgram_p ? 0 : MISCINIT1_2DBLOCK_DIS; | ||
444 | miscinit1 |= MISCINIT1_CLUT_INV; | ||
445 | |||
446 | banshee_make_room(par, 1); | ||
447 | tdfx_outl(par, MISCINIT1, miscinit1); | ||
448 | return lfbsize; | ||
449 | } | ||
450 | |||
451 | /* ------------------------------------------------------------------------- */ | ||
452 | |||
453 | static int tdfxfb_check_var(struct fb_var_screeninfo *var,struct fb_info *info) | ||
454 | { | ||
455 | struct tdfx_par *par = (struct tdfx_par *) info->par; | ||
456 | u32 lpitch; | ||
457 | |||
458 | if (var->bits_per_pixel != 8 && var->bits_per_pixel != 16 && | ||
459 | var->bits_per_pixel != 24 && var->bits_per_pixel != 32) { | ||
460 | DPRINTK("depth not supported: %u\n", var->bits_per_pixel); | ||
461 | return -EINVAL; | ||
462 | } | ||
463 | |||
464 | if (var->xres != var->xres_virtual) | ||
465 | var->xres_virtual = var->xres; | ||
466 | |||
467 | if (var->yres > var->yres_virtual) | ||
468 | var->yres_virtual = var->yres; | ||
469 | |||
470 | if (var->xoffset) { | ||
471 | DPRINTK("xoffset not supported\n"); | ||
472 | return -EINVAL; | ||
473 | } | ||
474 | |||
475 | /* Banshee doesn't support interlace, but Voodoo4/5 and probably Voodoo3 do. */ | ||
476 | /* no direct information about device id now? use max_pixclock for this... */ | ||
477 | if (((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) && | ||
478 | (par->max_pixclock < VOODOO3_MAX_PIXCLOCK)) { | ||
479 | DPRINTK("interlace not supported\n"); | ||
480 | return -EINVAL; | ||
481 | } | ||
482 | |||
483 | var->xres = (var->xres + 15) & ~15; /* could sometimes be 8 */ | ||
484 | lpitch = var->xres * ((var->bits_per_pixel + 7)>>3); | ||
485 | |||
486 | if (var->xres < 320 || var->xres > 2048) { | ||
487 | DPRINTK("width not supported: %u\n", var->xres); | ||
488 | return -EINVAL; | ||
489 | } | ||
490 | |||
491 | if (var->yres < 200 || var->yres > 2048) { | ||
492 | DPRINTK("height not supported: %u\n", var->yres); | ||
493 | return -EINVAL; | ||
494 | } | ||
495 | |||
496 | if (lpitch * var->yres_virtual > info->fix.smem_len) { | ||
497 | var->yres_virtual = info->fix.smem_len/lpitch; | ||
498 | if (var->yres_virtual < var->yres) { | ||
499 | DPRINTK("no memory for screen (%ux%ux%u)\n", | ||
500 | var->xres, var->yres_virtual, var->bits_per_pixel); | ||
501 | return -EINVAL; | ||
502 | } | ||
503 | } | ||
504 | |||
505 | if (PICOS2KHZ(var->pixclock) > par->max_pixclock) { | ||
506 | DPRINTK("pixclock too high (%ldKHz)\n",PICOS2KHZ(var->pixclock)); | ||
507 | return -EINVAL; | ||
508 | } | ||
509 | |||
510 | switch(var->bits_per_pixel) { | ||
511 | case 8: | ||
512 | var->red.length = var->green.length = var->blue.length = 8; | ||
513 | break; | ||
514 | case 16: | ||
515 | var->red.offset = 11; | ||
516 | var->red.length = 5; | ||
517 | var->green.offset = 5; | ||
518 | var->green.length = 6; | ||
519 | var->blue.offset = 0; | ||
520 | var->blue.length = 5; | ||
521 | break; | ||
522 | case 24: | ||
523 | var->red.offset=16; | ||
524 | var->green.offset=8; | ||
525 | var->blue.offset=0; | ||
526 | var->red.length = var->green.length = var->blue.length = 8; | ||
527 | case 32: | ||
528 | var->red.offset = 16; | ||
529 | var->green.offset = 8; | ||
530 | var->blue.offset = 0; | ||
531 | var->red.length = var->green.length = var->blue.length = 8; | ||
532 | break; | ||
533 | } | ||
534 | var->height = var->width = -1; | ||
535 | |||
536 | var->accel_flags = FB_ACCELF_TEXT; | ||
537 | |||
538 | DPRINTK("Checking graphics mode at %dx%d depth %d\n", var->xres, var->yres, var->bits_per_pixel); | ||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | static int tdfxfb_set_par(struct fb_info *info) | ||
543 | { | ||
544 | struct tdfx_par *par = (struct tdfx_par *) info->par; | ||
545 | u32 hdispend, hsyncsta, hsyncend, htotal; | ||
546 | u32 hd, hs, he, ht, hbs, hbe; | ||
547 | u32 vd, vs, ve, vt, vbs, vbe; | ||
548 | struct banshee_reg reg; | ||
549 | int fout, freq; | ||
550 | u32 wd, cpp; | ||
551 | |||
552 | par->baseline = 0; | ||
553 | |||
554 | memset(®, 0, sizeof(reg)); | ||
555 | cpp = (info->var.bits_per_pixel + 7)/8; | ||
556 | |||
557 | reg.vidcfg = VIDCFG_VIDPROC_ENABLE | VIDCFG_DESK_ENABLE | VIDCFG_CURS_X11 | ((cpp - 1) << VIDCFG_PIXFMT_SHIFT) | (cpp != 1 ? VIDCFG_CLUT_BYPASS : 0); | ||
558 | |||
559 | /* PLL settings */ | ||
560 | freq = PICOS2KHZ(info->var.pixclock); | ||
561 | |||
562 | reg.dacmode = 0; | ||
563 | reg.vidcfg &= ~VIDCFG_2X; | ||
564 | |||
565 | hdispend = info->var.xres; | ||
566 | hsyncsta = hdispend + info->var.right_margin; | ||
567 | hsyncend = hsyncsta + info->var.hsync_len; | ||
568 | htotal = hsyncend + info->var.left_margin; | ||
569 | |||
570 | if (freq > par->max_pixclock/2) { | ||
571 | freq = freq > par->max_pixclock ? par->max_pixclock : freq; | ||
572 | reg.dacmode |= DACMODE_2X; | ||
573 | reg.vidcfg |= VIDCFG_2X; | ||
574 | hdispend >>= 1; | ||
575 | hsyncsta >>= 1; | ||
576 | hsyncend >>= 1; | ||
577 | htotal >>= 1; | ||
578 | } | ||
579 | |||
580 | hd = wd = (hdispend >> 3) - 1; | ||
581 | hs = (hsyncsta >> 3) - 1; | ||
582 | he = (hsyncend >> 3) - 1; | ||
583 | ht = (htotal >> 3) - 1; | ||
584 | hbs = hd; | ||
585 | hbe = ht; | ||
586 | |||
587 | if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) { | ||
588 | vbs = vd = (info->var.yres << 1) - 1; | ||
589 | vs = vd + (info->var.lower_margin << 1); | ||
590 | ve = vs + (info->var.vsync_len << 1); | ||
591 | vbe = vt = ve + (info->var.upper_margin << 1) - 1; | ||
592 | } else { | ||
593 | vbs = vd = info->var.yres - 1; | ||
594 | vs = vd + info->var.lower_margin; | ||
595 | ve = vs + info->var.vsync_len; | ||
596 | vbe = vt = ve + info->var.upper_margin - 1; | ||
597 | } | ||
598 | |||
599 | /* this is all pretty standard VGA register stuffing */ | ||
600 | reg.misc[0x00] = 0x0f | | ||
601 | (info->var.xres < 400 ? 0xa0 : | ||
602 | info->var.xres < 480 ? 0x60 : | ||
603 | info->var.xres < 768 ? 0xe0 : 0x20); | ||
604 | |||
605 | reg.gra[0x00] = 0x00; | ||
606 | reg.gra[0x01] = 0x00; | ||
607 | reg.gra[0x02] = 0x00; | ||
608 | reg.gra[0x03] = 0x00; | ||
609 | reg.gra[0x04] = 0x00; | ||
610 | reg.gra[0x05] = 0x40; | ||
611 | reg.gra[0x06] = 0x05; | ||
612 | reg.gra[0x07] = 0x0f; | ||
613 | reg.gra[0x08] = 0xff; | ||
614 | |||
615 | reg.att[0x00] = 0x00; | ||
616 | reg.att[0x01] = 0x01; | ||
617 | reg.att[0x02] = 0x02; | ||
618 | reg.att[0x03] = 0x03; | ||
619 | reg.att[0x04] = 0x04; | ||
620 | reg.att[0x05] = 0x05; | ||
621 | reg.att[0x06] = 0x06; | ||
622 | reg.att[0x07] = 0x07; | ||
623 | reg.att[0x08] = 0x08; | ||
624 | reg.att[0x09] = 0x09; | ||
625 | reg.att[0x0a] = 0x0a; | ||
626 | reg.att[0x0b] = 0x0b; | ||
627 | reg.att[0x0c] = 0x0c; | ||
628 | reg.att[0x0d] = 0x0d; | ||
629 | reg.att[0x0e] = 0x0e; | ||
630 | reg.att[0x0f] = 0x0f; | ||
631 | reg.att[0x10] = 0x41; | ||
632 | reg.att[0x11] = 0x00; | ||
633 | reg.att[0x12] = 0x0f; | ||
634 | reg.att[0x13] = 0x00; | ||
635 | reg.att[0x14] = 0x00; | ||
636 | |||
637 | reg.seq[0x00] = 0x03; | ||
638 | reg.seq[0x01] = 0x01; /* fixme: clkdiv2? */ | ||
639 | reg.seq[0x02] = 0x0f; | ||
640 | reg.seq[0x03] = 0x00; | ||
641 | reg.seq[0x04] = 0x0e; | ||
642 | |||
643 | reg.crt[0x00] = ht - 4; | ||
644 | reg.crt[0x01] = hd; | ||
645 | reg.crt[0x02] = hbs; | ||
646 | reg.crt[0x03] = 0x80 | (hbe & 0x1f); | ||
647 | reg.crt[0x04] = hs; | ||
648 | reg.crt[0x05] = ((hbe & 0x20) << 2) | (he & 0x1f); | ||
649 | reg.crt[0x06] = vt; | ||
650 | reg.crt[0x07] = ((vs & 0x200) >> 2) | | ||
651 | ((vd & 0x200) >> 3) | | ||
652 | ((vt & 0x200) >> 4) | 0x10 | | ||
653 | ((vbs & 0x100) >> 5) | | ||
654 | ((vs & 0x100) >> 6) | | ||
655 | ((vd & 0x100) >> 7) | | ||
656 | ((vt & 0x100) >> 8); | ||
657 | reg.crt[0x08] = 0x00; | ||
658 | reg.crt[0x09] = 0x40 | ((vbs & 0x200) >> 4); | ||
659 | reg.crt[0x0a] = 0x00; | ||
660 | reg.crt[0x0b] = 0x00; | ||
661 | reg.crt[0x0c] = 0x00; | ||
662 | reg.crt[0x0d] = 0x00; | ||
663 | reg.crt[0x0e] = 0x00; | ||
664 | reg.crt[0x0f] = 0x00; | ||
665 | reg.crt[0x10] = vs; | ||
666 | reg.crt[0x11] = (ve & 0x0f) | 0x20; | ||
667 | reg.crt[0x12] = vd; | ||
668 | reg.crt[0x13] = wd; | ||
669 | reg.crt[0x14] = 0x00; | ||
670 | reg.crt[0x15] = vbs; | ||
671 | reg.crt[0x16] = vbe + 1; | ||
672 | reg.crt[0x17] = 0xc3; | ||
673 | reg.crt[0x18] = 0xff; | ||
674 | |||
675 | /* Banshee's nonvga stuff */ | ||
676 | reg.ext[0x00] = (((ht & 0x100) >> 8) | | ||
677 | ((hd & 0x100) >> 6) | | ||
678 | ((hbs & 0x100) >> 4) | | ||
679 | ((hbe & 0x40) >> 1) | | ||
680 | ((hs & 0x100) >> 2) | | ||
681 | ((he & 0x20) << 2)); | ||
682 | reg.ext[0x01] = (((vt & 0x400) >> 10) | | ||
683 | ((vd & 0x400) >> 8) | | ||
684 | ((vbs & 0x400) >> 6) | | ||
685 | ((vbe & 0x400) >> 4)); | ||
686 | |||
687 | reg.vgainit0 = VGAINIT0_8BIT_DAC | | ||
688 | VGAINIT0_EXT_ENABLE | | ||
689 | VGAINIT0_WAKEUP_3C3 | | ||
690 | VGAINIT0_ALT_READBACK | | ||
691 | VGAINIT0_EXTSHIFTOUT; | ||
692 | reg.vgainit1 = tdfx_inl(par, VGAINIT1) & 0x1fffff; | ||
693 | |||
694 | reg.cursloc = 0; | ||
695 | |||
696 | reg.cursc0 = 0; | ||
697 | reg.cursc1 = 0xffffff; | ||
698 | |||
699 | reg.stride = info->var.xres * cpp; | ||
700 | reg.startaddr = par->baseline * reg.stride; | ||
701 | reg.srcbase = reg.startaddr; | ||
702 | reg.dstbase = reg.startaddr; | ||
703 | |||
704 | /* PLL settings */ | ||
705 | freq = PICOS2KHZ(info->var.pixclock); | ||
706 | |||
707 | reg.dacmode &= ~DACMODE_2X; | ||
708 | reg.vidcfg &= ~VIDCFG_2X; | ||
709 | if (freq > par->max_pixclock/2) { | ||
710 | freq = freq > par->max_pixclock ? par->max_pixclock : freq; | ||
711 | reg.dacmode |= DACMODE_2X; | ||
712 | reg.vidcfg |= VIDCFG_2X; | ||
713 | } | ||
714 | reg.vidpll = do_calc_pll(freq, &fout); | ||
715 | #if 0 | ||
716 | reg.mempll = do_calc_pll(..., &fout); | ||
717 | reg.gfxpll = do_calc_pll(..., &fout); | ||
718 | #endif | ||
719 | |||
720 | if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) { | ||
721 | reg.screensize = info->var.xres | (info->var.yres << 13); | ||
722 | reg.vidcfg |= VIDCFG_HALF_MODE; | ||
723 | reg.crt[0x09] |= 0x80; | ||
724 | } else { | ||
725 | reg.screensize = info->var.xres | (info->var.yres << 12); | ||
726 | reg.vidcfg &= ~VIDCFG_HALF_MODE; | ||
727 | } | ||
728 | if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) | ||
729 | reg.vidcfg |= VIDCFG_INTERLACE; | ||
730 | reg.miscinit0 = tdfx_inl(par, MISCINIT0); | ||
731 | |||
732 | #if defined(__BIG_ENDIAN) | ||
733 | switch (info->var.bits_per_pixel) { | ||
734 | case 8: | ||
735 | case 24: | ||
736 | reg.miscinit0 &= ~(1 << 30); | ||
737 | reg.miscinit0 &= ~(1 << 31); | ||
738 | break; | ||
739 | case 16: | ||
740 | reg.miscinit0 |= (1 << 30); | ||
741 | reg.miscinit0 |= (1 << 31); | ||
742 | break; | ||
743 | case 32: | ||
744 | reg.miscinit0 |= (1 << 30); | ||
745 | reg.miscinit0 &= ~(1 << 31); | ||
746 | break; | ||
747 | } | ||
748 | #endif | ||
749 | do_write_regs(info, ®); | ||
750 | |||
751 | /* Now change fb_fix_screeninfo according to changes in par */ | ||
752 | info->fix.line_length = info->var.xres * ((info->var.bits_per_pixel + 7)>>3); | ||
753 | info->fix.visual = (info->var.bits_per_pixel == 8) | ||
754 | ? FB_VISUAL_PSEUDOCOLOR | ||
755 | : FB_VISUAL_TRUECOLOR; | ||
756 | DPRINTK("Graphics mode is now set at %dx%d depth %d\n", info->var.xres, info->var.yres, info->var.bits_per_pixel); | ||
757 | return 0; | ||
758 | } | ||
759 | |||
760 | /* A handy macro shamelessly pinched from matroxfb */ | ||
761 | #define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16) | ||
762 | |||
763 | static int tdfxfb_setcolreg(unsigned regno, unsigned red, unsigned green, | ||
764 | unsigned blue,unsigned transp,struct fb_info *info) | ||
765 | { | ||
766 | struct tdfx_par *par = (struct tdfx_par *) info->par; | ||
767 | u32 rgbcol; | ||
768 | |||
769 | if (regno >= info->cmap.len || regno > 255) return 1; | ||
770 | |||
771 | switch (info->fix.visual) { | ||
772 | case FB_VISUAL_PSEUDOCOLOR: | ||
773 | rgbcol =(((u32)red & 0xff00) << 8) | | ||
774 | (((u32)green & 0xff00) << 0) | | ||
775 | (((u32)blue & 0xff00) >> 8); | ||
776 | do_setpalentry(par, regno, rgbcol); | ||
777 | break; | ||
778 | /* Truecolor has no hardware color palettes. */ | ||
779 | case FB_VISUAL_TRUECOLOR: | ||
780 | rgbcol = (CNVT_TOHW( red, info->var.red.length) << info->var.red.offset) | | ||
781 | (CNVT_TOHW( green, info->var.green.length) << info->var.green.offset) | | ||
782 | (CNVT_TOHW( blue, info->var.blue.length) << info->var.blue.offset) | | ||
783 | (CNVT_TOHW( transp, info->var.transp.length) << info->var.transp.offset); | ||
784 | ((u32*)(info->pseudo_palette))[regno] = rgbcol; | ||
785 | break; | ||
786 | default: | ||
787 | DPRINTK("bad depth %u\n", info->var.bits_per_pixel); | ||
788 | break; | ||
789 | } | ||
790 | return 0; | ||
791 | } | ||
792 | |||
793 | /* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ | ||
794 | static int tdfxfb_blank(int blank, struct fb_info *info) | ||
795 | { | ||
796 | struct tdfx_par *par = (struct tdfx_par *) info->par; | ||
797 | u32 dacmode, state = 0, vgablank = 0; | ||
798 | |||
799 | dacmode = tdfx_inl(par, DACMODE); | ||
800 | |||
801 | switch (blank) { | ||
802 | case FB_BLANK_UNBLANK: /* Screen: On; HSync: On, VSync: On */ | ||
803 | state = 0; | ||
804 | vgablank = 0; | ||
805 | break; | ||
806 | case FB_BLANK_NORMAL: /* Screen: Off; HSync: On, VSync: On */ | ||
807 | state = 0; | ||
808 | vgablank = 1; | ||
809 | break; | ||
810 | case FB_BLANK_VSYNC_SUSPEND: /* Screen: Off; HSync: On, VSync: Off */ | ||
811 | state = BIT(3); | ||
812 | vgablank = 1; | ||
813 | break; | ||
814 | case FB_BLANK_HSYNC_SUSPEND: /* Screen: Off; HSync: Off, VSync: On */ | ||
815 | state = BIT(1); | ||
816 | vgablank = 1; | ||
817 | break; | ||
818 | case FB_BLANK_POWERDOWN: /* Screen: Off; HSync: Off, VSync: Off */ | ||
819 | state = BIT(1) | BIT(3); | ||
820 | vgablank = 1; | ||
821 | break; | ||
822 | } | ||
823 | |||
824 | dacmode &= ~(BIT(1) | BIT(3)); | ||
825 | dacmode |= state; | ||
826 | banshee_make_room(par, 1); | ||
827 | tdfx_outl(par, DACMODE, dacmode); | ||
828 | if (vgablank) | ||
829 | vga_disable_video(par); | ||
830 | else | ||
831 | vga_enable_video(par); | ||
832 | return 0; | ||
833 | } | ||
834 | |||
835 | /* | ||
836 | * Set the starting position of the visible screen to var->yoffset | ||
837 | */ | ||
838 | static int tdfxfb_pan_display(struct fb_var_screeninfo *var, | ||
839 | struct fb_info *info) | ||
840 | { | ||
841 | struct tdfx_par *par = (struct tdfx_par *) info->par; | ||
842 | u32 addr; | ||
843 | |||
844 | if (nopan || var->xoffset || (var->yoffset > var->yres_virtual)) | ||
845 | return -EINVAL; | ||
846 | if ((var->yoffset + var->yres > var->yres_virtual && nowrap)) | ||
847 | return -EINVAL; | ||
848 | |||
849 | addr = var->yoffset * info->fix.line_length; | ||
850 | banshee_make_room(par, 1); | ||
851 | tdfx_outl(par, VIDDESKSTART, addr); | ||
852 | |||
853 | info->var.xoffset = var->xoffset; | ||
854 | info->var.yoffset = var->yoffset; | ||
855 | return 0; | ||
856 | } | ||
857 | |||
858 | #ifdef CONFIG_FB_3DFX_ACCEL | ||
859 | /* | ||
860 | * FillRect 2D command (solidfill or invert (via ROP_XOR)) | ||
861 | */ | ||
862 | static void tdfxfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) | ||
863 | { | ||
864 | struct tdfx_par *par = (struct tdfx_par *) info->par; | ||
865 | u32 bpp = info->var.bits_per_pixel; | ||
866 | u32 stride = info->fix.line_length; | ||
867 | u32 fmt= stride | ((bpp+((bpp==8) ? 0 : 8)) << 13); | ||
868 | int tdfx_rop; | ||
869 | |||
870 | if (rect->rop == ROP_COPY) | ||
871 | tdfx_rop = TDFX_ROP_COPY; | ||
872 | else | ||
873 | tdfx_rop = TDFX_ROP_XOR; | ||
874 | |||
875 | banshee_make_room(par, 5); | ||
876 | tdfx_outl(par, DSTFORMAT, fmt); | ||
877 | if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR) { | ||
878 | tdfx_outl(par, COLORFORE, rect->color); | ||
879 | } else { /* FB_VISUAL_TRUECOLOR */ | ||
880 | tdfx_outl(par, COLORFORE, ((u32*)(info->pseudo_palette))[rect->color]); | ||
881 | } | ||
882 | tdfx_outl(par, COMMAND_2D, COMMAND_2D_FILLRECT | (tdfx_rop << 24)); | ||
883 | tdfx_outl(par, DSTSIZE, rect->width | (rect->height << 16)); | ||
884 | tdfx_outl(par, LAUNCH_2D, rect->dx | (rect->dy << 16)); | ||
885 | } | ||
886 | |||
887 | /* | ||
888 | * Screen-to-Screen BitBlt 2D command (for the bmove fb op.) | ||
889 | */ | ||
890 | static void tdfxfb_copyarea(struct fb_info *info, const struct fb_copyarea *area) | ||
891 | { | ||
892 | struct tdfx_par *par = (struct tdfx_par *) info->par; | ||
893 | u32 sx = area->sx, sy = area->sy, dx = area->dx, dy = area->dy; | ||
894 | u32 bpp = info->var.bits_per_pixel; | ||
895 | u32 stride = info->fix.line_length; | ||
896 | u32 blitcmd = COMMAND_2D_S2S_BITBLT | (TDFX_ROP_COPY << 24); | ||
897 | u32 fmt = stride | ((bpp+((bpp==8) ? 0 : 8)) << 13); | ||
898 | |||
899 | if (area->sx <= area->dx) { | ||
900 | //-X | ||
901 | blitcmd |= BIT(14); | ||
902 | sx += area->width - 1; | ||
903 | dx += area->width - 1; | ||
904 | } | ||
905 | if (area->sy <= area->dy) { | ||
906 | //-Y | ||
907 | blitcmd |= BIT(15); | ||
908 | sy += area->height - 1; | ||
909 | dy += area->height - 1; | ||
910 | } | ||
911 | |||
912 | banshee_make_room(par, 6); | ||
913 | |||
914 | tdfx_outl(par, SRCFORMAT, fmt); | ||
915 | tdfx_outl(par, DSTFORMAT, fmt); | ||
916 | tdfx_outl(par, COMMAND_2D, blitcmd); | ||
917 | tdfx_outl(par, DSTSIZE, area->width | (area->height << 16)); | ||
918 | tdfx_outl(par, DSTXY, dx | (dy << 16)); | ||
919 | tdfx_outl(par, LAUNCH_2D, sx | (sy << 16)); | ||
920 | } | ||
921 | |||
922 | static void tdfxfb_imageblit(struct fb_info *info, const struct fb_image *image) | ||
923 | { | ||
924 | struct tdfx_par *par = (struct tdfx_par *) info->par; | ||
925 | int size = image->height * ((image->width * image->depth + 7)>>3); | ||
926 | int fifo_free; | ||
927 | int i, stride = info->fix.line_length; | ||
928 | u32 bpp = info->var.bits_per_pixel; | ||
929 | u32 dstfmt = stride | ((bpp+((bpp==8) ? 0 : 8)) << 13); | ||
930 | u8 *chardata = (u8 *) image->data; | ||
931 | u32 srcfmt; | ||
932 | |||
933 | if (image->depth != 1) { | ||
934 | //banshee_make_room(par, 6 + ((size + 3) >> 2)); | ||
935 | //srcfmt = stride | ((bpp+((bpp==8) ? 0 : 8)) << 13) | 0x400000; | ||
936 | cfb_imageblit(info, image); | ||
937 | return; | ||
938 | } else { | ||
939 | banshee_make_room(par, 8); | ||
940 | switch (info->fix.visual) { | ||
941 | case FB_VISUAL_PSEUDOCOLOR: | ||
942 | tdfx_outl(par, COLORFORE, image->fg_color); | ||
943 | tdfx_outl(par, COLORBACK, image->bg_color); | ||
944 | break; | ||
945 | case FB_VISUAL_TRUECOLOR: | ||
946 | default: | ||
947 | tdfx_outl(par, COLORFORE, ((u32*)(info->pseudo_palette))[image->fg_color]); | ||
948 | tdfx_outl(par, COLORBACK, ((u32*)(info->pseudo_palette))[image->bg_color]); | ||
949 | } | ||
950 | #ifdef __BIG_ENDIAN | ||
951 | srcfmt = 0x400000 | BIT(20); | ||
952 | #else | ||
953 | srcfmt = 0x400000; | ||
954 | #endif | ||
955 | } | ||
956 | |||
957 | tdfx_outl(par, SRCXY, 0); | ||
958 | tdfx_outl(par, DSTXY, image->dx | (image->dy << 16)); | ||
959 | tdfx_outl(par, COMMAND_2D, COMMAND_2D_H2S_BITBLT | (TDFX_ROP_COPY << 24)); | ||
960 | tdfx_outl(par, SRCFORMAT, srcfmt); | ||
961 | tdfx_outl(par, DSTFORMAT, dstfmt); | ||
962 | tdfx_outl(par, DSTSIZE, image->width | (image->height << 16)); | ||
963 | |||
964 | /* A count of how many free FIFO entries we've requested. | ||
965 | * When this goes negative, we need to request more. */ | ||
966 | fifo_free = 0; | ||
967 | |||
968 | /* Send four bytes at a time of data */ | ||
969 | for (i = (size >> 2) ; i > 0; i--) { | ||
970 | if(--fifo_free < 0) { | ||
971 | fifo_free=31; | ||
972 | banshee_make_room(par,fifo_free); | ||
973 | } | ||
974 | tdfx_outl(par, LAUNCH_2D,*(u32*)chardata); | ||
975 | chardata += 4; | ||
976 | } | ||
977 | |||
978 | /* Send the leftovers now */ | ||
979 | banshee_make_room(par,3); | ||
980 | i = size%4; | ||
981 | switch (i) { | ||
982 | case 0: break; | ||
983 | case 1: tdfx_outl(par, LAUNCH_2D,*chardata); break; | ||
984 | case 2: tdfx_outl(par, LAUNCH_2D,*(u16*)chardata); break; | ||
985 | case 3: tdfx_outl(par, LAUNCH_2D,*(u16*)chardata | ((chardata[3]) << 24)); break; | ||
986 | } | ||
987 | } | ||
988 | #endif /* CONFIG_FB_3DFX_ACCEL */ | ||
989 | |||
990 | #ifdef TDFX_HARDWARE_CURSOR | ||
991 | static int tdfxfb_cursor(struct fb_info *info, struct fb_cursor *cursor) | ||
992 | { | ||
993 | struct tdfx_par *par = (struct tdfx_par *) info->par; | ||
994 | unsigned long flags; | ||
995 | |||
996 | /* | ||
997 | * If the cursor is not be changed this means either we want the | ||
998 | * current cursor state (if enable is set) or we want to query what | ||
999 | * we can do with the cursor (if enable is not set) | ||
1000 | */ | ||
1001 | if (!cursor->set) return 0; | ||
1002 | |||
1003 | /* Too large of a cursor :-( */ | ||
1004 | if (cursor->image.width > 64 || cursor->image.height > 64) | ||
1005 | return -ENXIO; | ||
1006 | |||
1007 | /* | ||
1008 | * If we are going to be changing things we should disable | ||
1009 | * the cursor first | ||
1010 | */ | ||
1011 | if (info->cursor.enable) { | ||
1012 | spin_lock_irqsave(&par->DAClock, flags); | ||
1013 | info->cursor.enable = 0; | ||
1014 | del_timer(&(par->hwcursor.timer)); | ||
1015 | tdfx_outl(par, VIDPROCCFG, par->hwcursor.disable); | ||
1016 | spin_unlock_irqrestore(&par->DAClock, flags); | ||
1017 | } | ||
1018 | |||
1019 | /* Disable the Cursor */ | ||
1020 | if ((cursor->set && FB_CUR_SETCUR) && !cursor->enable) | ||
1021 | return 0; | ||
1022 | |||
1023 | /* fix cursor color - XFree86 forgets to restore it properly */ | ||
1024 | if (cursor->set && FB_CUR_SETCMAP) { | ||
1025 | struct fb_cmap cmap = cursor->image.cmap; | ||
1026 | unsigned long bg_color, fg_color; | ||
1027 | |||
1028 | cmap.len = 2; /* Voodoo 3+ only support 2 color cursors */ | ||
1029 | fg_color = ((cmap.red[cmap.start] << 16) | | ||
1030 | (cmap.green[cmap.start] << 8) | | ||
1031 | (cmap.blue[cmap.start])); | ||
1032 | bg_color = ((cmap.red[cmap.start+1] << 16) | | ||
1033 | (cmap.green[cmap.start+1] << 8) | | ||
1034 | (cmap.blue[cmap.start+1])); | ||
1035 | fb_copy_cmap(&cmap, &info->cursor.image.cmap); | ||
1036 | spin_lock_irqsave(&par->DAClock, flags); | ||
1037 | banshee_make_room(par, 2); | ||
1038 | tdfx_outl(par, HWCURC0, bg_color); | ||
1039 | tdfx_outl(par, HWCURC1, fg_color); | ||
1040 | spin_unlock_irqrestore(&par->DAClock, flags); | ||
1041 | } | ||
1042 | |||
1043 | if (cursor->set && FB_CUR_SETPOS) { | ||
1044 | int x, y; | ||
1045 | |||
1046 | x = cursor->image.dx; | ||
1047 | y = cursor->image.dy; | ||
1048 | y -= info->var.yoffset; | ||
1049 | info->cursor.image.dx = x; | ||
1050 | info->cursor.image.dy = y; | ||
1051 | x += 63; | ||
1052 | y += 63; | ||
1053 | spin_lock_irqsave(&par->DAClock, flags); | ||
1054 | banshee_make_room(par, 1); | ||
1055 | tdfx_outl(par, HWCURLOC, (y << 16) + x); | ||
1056 | spin_unlock_irqrestore(&par->DAClock, flags); | ||
1057 | } | ||
1058 | |||
1059 | /* Not supported so we fake it */ | ||
1060 | if (cursor->set && FB_CUR_SETHOT) { | ||
1061 | info->cursor.hot.x = cursor->hot.x; | ||
1062 | info->cursor.hot.y = cursor->hot.y; | ||
1063 | } | ||
1064 | |||
1065 | if (cursor->set && FB_CUR_SETSHAPE) { | ||
1066 | /* | ||
1067 | * Voodoo 3 and above cards use 2 monochrome cursor patterns. | ||
1068 | * The reason is so the card can fetch 8 words at a time | ||
1069 | * and are stored on chip for use for the next 8 scanlines. | ||
1070 | * This reduces the number of times for access to draw the | ||
1071 | * cursor for each screen refresh. | ||
1072 | * Each pattern is a bitmap of 64 bit wide and 64 bit high | ||
1073 | * (total of 8192 bits or 1024 Kbytes). The two patterns are | ||
1074 | * stored in such a way that pattern 0 always resides in the | ||
1075 | * lower half (least significant 64 bits) of a 128 bit word | ||
1076 | * and pattern 1 the upper half. If you examine the data of | ||
1077 | * the cursor image the graphics card uses then from the | ||
1078 | * begining you see line one of pattern 0, line one of | ||
1079 | * pattern 1, line two of pattern 0, line two of pattern 1, | ||
1080 | * etc etc. The linear stride for the cursor is always 16 bytes | ||
1081 | * (128 bits) which is the maximum cursor width times two for | ||
1082 | * the two monochrome patterns. | ||
1083 | */ | ||
1084 | u8 *cursorbase = (u8 *) info->cursor.image.data; | ||
1085 | char *bitmap = (char *)cursor->image.data; | ||
1086 | char *mask = (char *) cursor->mask; | ||
1087 | int i, j, k, h = 0; | ||
1088 | |||
1089 | for (i = 0; i < 64; i++) { | ||
1090 | if (i < cursor->image.height) { | ||
1091 | j = (cursor->image.width + 7) >> 3; | ||
1092 | k = 8 - j; | ||
1093 | |||
1094 | for (;j > 0; j--) { | ||
1095 | /* Pattern 0. Copy the cursor bitmap to it */ | ||
1096 | fb_writeb(*bitmap, cursorbase + h); | ||
1097 | bitmap++; | ||
1098 | /* Pattern 1. Copy the cursor mask to it */ | ||
1099 | fb_writeb(*mask, cursorbase + h + 8); | ||
1100 | mask++; | ||
1101 | h++; | ||
1102 | } | ||
1103 | for (;k > 0; k--) { | ||
1104 | fb_writeb(0, cursorbase + h); | ||
1105 | fb_writeb(~0, cursorbase + h + 8); | ||
1106 | h++; | ||
1107 | } | ||
1108 | } else { | ||
1109 | fb_writel(0, cursorbase + h); | ||
1110 | fb_writel(0, cursorbase + h + 4); | ||
1111 | fb_writel(~0, cursorbase + h + 8); | ||
1112 | fb_writel(~0, cursorbase + h + 12); | ||
1113 | h += 16; | ||
1114 | } | ||
1115 | } | ||
1116 | } | ||
1117 | /* Turn the cursor on */ | ||
1118 | cursor->enable = 1; | ||
1119 | info->cursor = *cursor; | ||
1120 | mod_timer(&par->hwcursor.timer, jiffies+HZ/2); | ||
1121 | spin_lock_irqsave(&par->DAClock, flags); | ||
1122 | banshee_make_room(par, 1); | ||
1123 | tdfx_outl(par, VIDPROCCFG, par->hwcursor.enable); | ||
1124 | spin_unlock_irqrestore(&par->DAClock, flags); | ||
1125 | return 0; | ||
1126 | } | ||
1127 | #endif | ||
1128 | |||
1129 | /** | ||
1130 | * tdfxfb_probe - Device Initializiation | ||
1131 | * | ||
1132 | * @pdev: PCI Device to initialize | ||
1133 | * @id: PCI Device ID | ||
1134 | * | ||
1135 | * Initializes and allocates resources for PCI device @pdev. | ||
1136 | * | ||
1137 | */ | ||
1138 | static int __devinit tdfxfb_probe(struct pci_dev *pdev, | ||
1139 | const struct pci_device_id *id) | ||
1140 | { | ||
1141 | struct tdfx_par *default_par; | ||
1142 | struct fb_info *info; | ||
1143 | int size, err, lpitch; | ||
1144 | |||
1145 | if ((err = pci_enable_device(pdev))) { | ||
1146 | printk(KERN_WARNING "tdfxfb: Can't enable pdev: %d\n", err); | ||
1147 | return err; | ||
1148 | } | ||
1149 | |||
1150 | size = sizeof(struct tdfx_par)+256*sizeof(u32); | ||
1151 | |||
1152 | info = framebuffer_alloc(size, &pdev->dev); | ||
1153 | |||
1154 | if (!info) return -ENOMEM; | ||
1155 | |||
1156 | default_par = info->par; | ||
1157 | |||
1158 | /* Configure the default fb_fix_screeninfo first */ | ||
1159 | switch (pdev->device) { | ||
1160 | case PCI_DEVICE_ID_3DFX_BANSHEE: | ||
1161 | strcat(tdfx_fix.id, " Banshee"); | ||
1162 | default_par->max_pixclock = BANSHEE_MAX_PIXCLOCK; | ||
1163 | break; | ||
1164 | case PCI_DEVICE_ID_3DFX_VOODOO3: | ||
1165 | strcat(tdfx_fix.id, " Voodoo3"); | ||
1166 | default_par->max_pixclock = VOODOO3_MAX_PIXCLOCK; | ||
1167 | break; | ||
1168 | case PCI_DEVICE_ID_3DFX_VOODOO5: | ||
1169 | strcat(tdfx_fix.id, " Voodoo5"); | ||
1170 | default_par->max_pixclock = VOODOO5_MAX_PIXCLOCK; | ||
1171 | break; | ||
1172 | } | ||
1173 | |||
1174 | tdfx_fix.mmio_start = pci_resource_start(pdev, 0); | ||
1175 | tdfx_fix.mmio_len = pci_resource_len(pdev, 0); | ||
1176 | default_par->regbase_virt = ioremap_nocache(tdfx_fix.mmio_start, tdfx_fix.mmio_len); | ||
1177 | if (!default_par->regbase_virt) { | ||
1178 | printk("fb: Can't remap %s register area.\n", tdfx_fix.id); | ||
1179 | goto out_err; | ||
1180 | } | ||
1181 | |||
1182 | if (!request_mem_region(pci_resource_start(pdev, 0), | ||
1183 | pci_resource_len(pdev, 0), "tdfx regbase")) { | ||
1184 | printk(KERN_WARNING "tdfxfb: Can't reserve regbase\n"); | ||
1185 | goto out_err; | ||
1186 | } | ||
1187 | |||
1188 | tdfx_fix.smem_start = pci_resource_start(pdev, 1); | ||
1189 | if (!(tdfx_fix.smem_len = do_lfb_size(default_par, pdev->device))) { | ||
1190 | printk("fb: Can't count %s memory.\n", tdfx_fix.id); | ||
1191 | release_mem_region(pci_resource_start(pdev, 0), | ||
1192 | pci_resource_len(pdev, 0)); | ||
1193 | goto out_err; | ||
1194 | } | ||
1195 | |||
1196 | if (!request_mem_region(pci_resource_start(pdev, 1), | ||
1197 | pci_resource_len(pdev, 1), "tdfx smem")) { | ||
1198 | printk(KERN_WARNING "tdfxfb: Can't reserve smem\n"); | ||
1199 | release_mem_region(pci_resource_start(pdev, 0), | ||
1200 | pci_resource_len(pdev, 0)); | ||
1201 | goto out_err; | ||
1202 | } | ||
1203 | |||
1204 | info->screen_base = ioremap_nocache(tdfx_fix.smem_start, | ||
1205 | tdfx_fix.smem_len); | ||
1206 | if (!info->screen_base) { | ||
1207 | printk("fb: Can't remap %s framebuffer.\n", tdfx_fix.id); | ||
1208 | release_mem_region(pci_resource_start(pdev, 1), | ||
1209 | pci_resource_len(pdev, 1)); | ||
1210 | release_mem_region(pci_resource_start(pdev, 0), | ||
1211 | pci_resource_len(pdev, 0)); | ||
1212 | goto out_err; | ||
1213 | } | ||
1214 | |||
1215 | default_par->iobase = pci_resource_start(pdev, 2); | ||
1216 | |||
1217 | if (!request_region(pci_resource_start(pdev, 2), | ||
1218 | pci_resource_len(pdev, 2), "tdfx iobase")) { | ||
1219 | printk(KERN_WARNING "tdfxfb: Can't reserve iobase\n"); | ||
1220 | release_mem_region(pci_resource_start(pdev, 1), | ||
1221 | pci_resource_len(pdev, 1)); | ||
1222 | release_mem_region(pci_resource_start(pdev, 0), | ||
1223 | pci_resource_len(pdev, 0)); | ||
1224 | goto out_err; | ||
1225 | } | ||
1226 | |||
1227 | printk("fb: %s memory = %dK\n", tdfx_fix.id, tdfx_fix.smem_len >> 10); | ||
1228 | |||
1229 | tdfx_fix.ypanstep = nopan ? 0 : 1; | ||
1230 | tdfx_fix.ywrapstep = nowrap ? 0 : 1; | ||
1231 | |||
1232 | info->fbops = &tdfxfb_ops; | ||
1233 | info->fix = tdfx_fix; | ||
1234 | info->pseudo_palette = (void *)(default_par + 1); | ||
1235 | info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; | ||
1236 | #ifdef CONFIG_FB_3DFX_ACCEL | ||
1237 | info->flags |= FBINFO_HWACCEL_FILLRECT | | ||
1238 | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_IMAGEBLIT; | ||
1239 | #endif | ||
1240 | |||
1241 | if (!mode_option) | ||
1242 | mode_option = "640x480@60"; | ||
1243 | |||
1244 | err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8); | ||
1245 | if (!err || err == 4) | ||
1246 | info->var = tdfx_var; | ||
1247 | |||
1248 | /* maximize virtual vertical length */ | ||
1249 | lpitch = info->var.xres_virtual * ((info->var.bits_per_pixel + 7) >> 3); | ||
1250 | info->var.yres_virtual = info->fix.smem_len/lpitch; | ||
1251 | if (info->var.yres_virtual < info->var.yres) | ||
1252 | goto out_err; | ||
1253 | |||
1254 | #ifdef CONFIG_FB_3DFX_ACCEL | ||
1255 | /* | ||
1256 | * FIXME: Limit var->yres_virtual to 4096 because of screen artifacts | ||
1257 | * during scrolling. This is only present if 2D acceleration is | ||
1258 | * enabled. | ||
1259 | */ | ||
1260 | if (info->var.yres_virtual > 4096) | ||
1261 | info->var.yres_virtual = 4096; | ||
1262 | #endif /* CONFIG_FB_3DFX_ACCEL */ | ||
1263 | |||
1264 | if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { | ||
1265 | printk(KERN_WARNING "tdfxfb: Can't allocate color map\n"); | ||
1266 | goto out_err; | ||
1267 | } | ||
1268 | |||
1269 | if (register_framebuffer(info) < 0) { | ||
1270 | printk("tdfxfb: can't register framebuffer\n"); | ||
1271 | fb_dealloc_cmap(&info->cmap); | ||
1272 | goto out_err; | ||
1273 | } | ||
1274 | /* | ||
1275 | * Our driver data | ||
1276 | */ | ||
1277 | pci_set_drvdata(pdev, info); | ||
1278 | return 0; | ||
1279 | |||
1280 | out_err: | ||
1281 | /* | ||
1282 | * Cleanup after anything that was remapped/allocated. | ||
1283 | */ | ||
1284 | if (default_par->regbase_virt) | ||
1285 | iounmap(default_par->regbase_virt); | ||
1286 | if (info->screen_base) | ||
1287 | iounmap(info->screen_base); | ||
1288 | framebuffer_release(info); | ||
1289 | return -ENXIO; | ||
1290 | } | ||
1291 | |||
1292 | #ifndef MODULE | ||
1293 | void tdfxfb_setup(char *options) | ||
1294 | { | ||
1295 | char* this_opt; | ||
1296 | |||
1297 | if (!options || !*options) | ||
1298 | return; | ||
1299 | |||
1300 | while ((this_opt = strsep(&options, ",")) != NULL) { | ||
1301 | if (!*this_opt) | ||
1302 | continue; | ||
1303 | if(!strcmp(this_opt, "nopan")) { | ||
1304 | nopan = 1; | ||
1305 | } else if(!strcmp(this_opt, "nowrap")) { | ||
1306 | nowrap = 1; | ||
1307 | } else { | ||
1308 | mode_option = this_opt; | ||
1309 | } | ||
1310 | } | ||
1311 | } | ||
1312 | #endif | ||
1313 | |||
1314 | /** | ||
1315 | * tdfxfb_remove - Device removal | ||
1316 | * | ||
1317 | * @pdev: PCI Device to cleanup | ||
1318 | * | ||
1319 | * Releases all resources allocated during the course of the driver's | ||
1320 | * lifetime for the PCI device @pdev. | ||
1321 | * | ||
1322 | */ | ||
1323 | static void __devexit tdfxfb_remove(struct pci_dev *pdev) | ||
1324 | { | ||
1325 | struct fb_info *info = pci_get_drvdata(pdev); | ||
1326 | struct tdfx_par *par = (struct tdfx_par *) info->par; | ||
1327 | |||
1328 | unregister_framebuffer(info); | ||
1329 | iounmap(par->regbase_virt); | ||
1330 | iounmap(info->screen_base); | ||
1331 | |||
1332 | /* Clean up after reserved regions */ | ||
1333 | release_region(pci_resource_start(pdev, 2), | ||
1334 | pci_resource_len(pdev, 2)); | ||
1335 | release_mem_region(pci_resource_start(pdev, 1), | ||
1336 | pci_resource_len(pdev, 1)); | ||
1337 | release_mem_region(pci_resource_start(pdev, 0), | ||
1338 | pci_resource_len(pdev, 0)); | ||
1339 | pci_set_drvdata(pdev, NULL); | ||
1340 | framebuffer_release(info); | ||
1341 | } | ||
1342 | |||
1343 | static int __init tdfxfb_init(void) | ||
1344 | { | ||
1345 | #ifndef MODULE | ||
1346 | char *option = NULL; | ||
1347 | |||
1348 | if (fb_get_options("tdfxfb", &option)) | ||
1349 | return -ENODEV; | ||
1350 | |||
1351 | tdfxfb_setup(option); | ||
1352 | #endif | ||
1353 | return pci_register_driver(&tdfxfb_driver); | ||
1354 | } | ||
1355 | |||
1356 | static void __exit tdfxfb_exit(void) | ||
1357 | { | ||
1358 | pci_unregister_driver(&tdfxfb_driver); | ||
1359 | } | ||
1360 | |||
1361 | MODULE_AUTHOR("Hannu Mallat <hmallat@cc.hut.fi>"); | ||
1362 | MODULE_DESCRIPTION("3Dfx framebuffer device driver"); | ||
1363 | MODULE_LICENSE("GPL"); | ||
1364 | |||
1365 | module_init(tdfxfb_init); | ||
1366 | module_exit(tdfxfb_exit); | ||