Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/drivers/video/stifb.c - | |
3 | * Low level Frame buffer driver for HP workstations with | |
4 | * STI (standard text interface) video firmware. | |
5 | * | |
6 | * Copyright (C) 2001-2004 Helge Deller <deller@gmx.de> | |
7 | * Portions Copyright (C) 2001 Thomas Bogendoerfer <tsbogend@alpha.franken.de> | |
8 | * | |
9 | * Based on: | |
10 | * - linux/drivers/video/artistfb.c -- Artist frame buffer driver | |
11 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> | |
12 | * - based on skeletonfb, which was | |
13 | * Created 28 Dec 1997 by Geert Uytterhoeven | |
14 | * - HP Xhp cfb-based X11 window driver for XFree86 | |
15 | * (c)Copyright 1992 Hewlett-Packard Co. | |
16 | * | |
17 | * | |
18 | * The following graphics display devices (NGLE family) are supported by this driver: | |
19 | * | |
20 | * HPA4070A known as "HCRX", a 1280x1024 color device with 8 planes | |
21 | * HPA4071A known as "HCRX24", a 1280x1024 color device with 24 planes, | |
22 | * optionally available with a hardware accelerator as HPA4071A_Z | |
23 | * HPA1659A known as "CRX", a 1280x1024 color device with 8 planes | |
24 | * HPA1439A known as "CRX24", a 1280x1024 color device with 24 planes, | |
25 | * optionally available with a hardware accelerator. | |
26 | * HPA1924A known as "GRX", a 1280x1024 grayscale device with 8 planes | |
27 | * HPA2269A known as "Dual CRX", a 1280x1024 color device with 8 planes, | |
28 | * implements support for two displays on a single graphics card. | |
29 | * HP710C internal graphics support optionally available on the HP9000s710 SPU, | |
30 | * supports 1280x1024 color displays with 8 planes. | |
31 | * HP710G same as HP710C, 1280x1024 grayscale only | |
32 | * HP710L same as HP710C, 1024x768 color only | |
33 | * HP712 internal graphics support on HP9000s712 SPU, supports 640x480, | |
34 | * 1024x768 or 1280x1024 color displays on 8 planes (Artist) | |
35 | * | |
36 | * This file is subject to the terms and conditions of the GNU General Public | |
37 | * License. See the file COPYING in the main directory of this archive | |
38 | * for more details. | |
39 | */ | |
40 | ||
41 | /* TODO: | |
42 | * - 1bpp mode is completely untested | |
43 | * - add support for h/w acceleration | |
44 | * - add hardware cursor | |
45 | * - automatically disable double buffering (e.g. on RDI precisionbook laptop) | |
46 | */ | |
47 | ||
48 | ||
49 | /* on supported graphic devices you may: | |
50 | * #define FALLBACK_TO_1BPP to fall back to 1 bpp, or | |
51 | * #undef FALLBACK_TO_1BPP to reject support for unsupported cards */ | |
52 | #undef FALLBACK_TO_1BPP | |
53 | ||
54 | #undef DEBUG_STIFB_REGS /* debug sti register accesses */ | |
55 | ||
56 | ||
57 | #include <linux/config.h> | |
58 | #include <linux/module.h> | |
59 | #include <linux/kernel.h> | |
60 | #include <linux/errno.h> | |
61 | #include <linux/string.h> | |
62 | #include <linux/mm.h> | |
63 | #include <linux/slab.h> | |
64 | #include <linux/delay.h> | |
65 | #include <linux/fb.h> | |
66 | #include <linux/init.h> | |
67 | #include <linux/ioport.h> | |
68 | #include <linux/pci.h> | |
69 | ||
70 | #include <asm/grfioctl.h> /* for HP-UX compatibility */ | |
71 | #include <asm/uaccess.h> | |
72 | ||
73 | #include "sticore.h" | |
74 | ||
75 | /* REGION_BASE(fb_info, index) returns the virtual address for region <index> */ | |
76 | #ifdef __LP64__ | |
77 | #define REGION_BASE(fb_info, index) \ | |
78 | (fb_info->sti->glob_cfg->region_ptrs[index] | 0xffffffff00000000) | |
79 | #else | |
80 | #define REGION_BASE(fb_info, index) \ | |
81 | fb_info->sti->glob_cfg->region_ptrs[index] | |
82 | #endif | |
83 | ||
84 | #define NGLEDEVDEPROM_CRT_REGION 1 | |
85 | ||
86 | typedef struct { | |
87 | __s32 video_config_reg; | |
88 | __s32 misc_video_start; | |
89 | __s32 horiz_timing_fmt; | |
90 | __s32 serr_timing_fmt; | |
91 | __s32 vert_timing_fmt; | |
92 | __s32 horiz_state; | |
93 | __s32 vert_state; | |
94 | __s32 vtg_state_elements; | |
95 | __s32 pipeline_delay; | |
96 | __s32 misc_video_end; | |
97 | } video_setup_t; | |
98 | ||
99 | typedef struct { | |
100 | __s16 sizeof_ngle_data; | |
101 | __s16 x_size_visible; /* visible screen dim in pixels */ | |
102 | __s16 y_size_visible; | |
103 | __s16 pad2[15]; | |
104 | __s16 cursor_pipeline_delay; | |
105 | __s16 video_interleaves; | |
106 | __s32 pad3[11]; | |
107 | } ngle_rom_t; | |
108 | ||
109 | struct stifb_info { | |
110 | struct fb_info info; | |
111 | unsigned int id; | |
112 | ngle_rom_t ngle_rom; | |
113 | struct sti_struct *sti; | |
114 | int deviceSpecificConfig; | |
115 | u32 pseudo_palette[256]; | |
116 | }; | |
117 | ||
118 | static int __initdata stifb_bpp_pref[MAX_STI_ROMS]; | |
119 | ||
120 | /* ------------------- chipset specific functions -------------------------- */ | |
121 | ||
122 | /* offsets to graphic-chip internal registers */ | |
123 | ||
124 | #define REG_1 0x000118 | |
125 | #define REG_2 0x000480 | |
126 | #define REG_3 0x0004a0 | |
127 | #define REG_4 0x000600 | |
128 | #define REG_6 0x000800 | |
129 | #define REG_8 0x000820 | |
130 | #define REG_9 0x000a04 | |
131 | #define REG_10 0x018000 | |
132 | #define REG_11 0x018004 | |
133 | #define REG_12 0x01800c | |
134 | #define REG_13 0x018018 | |
135 | #define REG_14 0x01801c | |
136 | #define REG_15 0x200000 | |
137 | #define REG_15b0 0x200000 | |
138 | #define REG_16b1 0x200005 | |
139 | #define REG_16b3 0x200007 | |
140 | #define REG_21 0x200218 | |
141 | #define REG_22 0x0005a0 | |
142 | #define REG_23 0x0005c0 | |
143 | #define REG_26 0x200118 | |
144 | #define REG_27 0x200308 | |
145 | #define REG_32 0x21003c | |
146 | #define REG_33 0x210040 | |
147 | #define REG_34 0x200008 | |
148 | #define REG_35 0x018010 | |
149 | #define REG_38 0x210020 | |
150 | #define REG_39 0x210120 | |
151 | #define REG_40 0x210130 | |
152 | #define REG_42 0x210028 | |
153 | #define REG_43 0x21002c | |
154 | #define REG_44 0x210030 | |
155 | #define REG_45 0x210034 | |
156 | ||
157 | #define READ_BYTE(fb,reg) gsc_readb((fb)->info.fix.mmio_start + (reg)) | |
158 | #define READ_WORD(fb,reg) gsc_readl((fb)->info.fix.mmio_start + (reg)) | |
159 | ||
160 | ||
161 | #ifndef DEBUG_STIFB_REGS | |
162 | # define DEBUG_OFF() | |
163 | # define DEBUG_ON() | |
164 | # define WRITE_BYTE(value,fb,reg) gsc_writeb((value),(fb)->info.fix.mmio_start + (reg)) | |
165 | # define WRITE_WORD(value,fb,reg) gsc_writel((value),(fb)->info.fix.mmio_start + (reg)) | |
166 | #else | |
167 | static int debug_on = 1; | |
168 | # define DEBUG_OFF() debug_on=0 | |
169 | # define DEBUG_ON() debug_on=1 | |
170 | # define WRITE_BYTE(value,fb,reg) do { if (debug_on) \ | |
171 | printk(KERN_DEBUG "%30s: WRITE_BYTE(0x%06x) = 0x%02x (old=0x%02x)\n", \ | |
172 | __FUNCTION__, reg, value, READ_BYTE(fb,reg)); \ | |
173 | gsc_writeb((value),(fb)->info.fix.mmio_start + (reg)); } while (0) | |
174 | # define WRITE_WORD(value,fb,reg) do { if (debug_on) \ | |
175 | printk(KERN_DEBUG "%30s: WRITE_WORD(0x%06x) = 0x%08x (old=0x%08x)\n", \ | |
176 | __FUNCTION__, reg, value, READ_WORD(fb,reg)); \ | |
177 | gsc_writel((value),(fb)->info.fix.mmio_start + (reg)); } while (0) | |
178 | #endif /* DEBUG_STIFB_REGS */ | |
179 | ||
180 | ||
181 | #define ENABLE 1 /* for enabling/disabling screen */ | |
182 | #define DISABLE 0 | |
183 | ||
184 | #define NGLE_LOCK(fb_info) do { } while (0) | |
185 | #define NGLE_UNLOCK(fb_info) do { } while (0) | |
186 | ||
187 | static void | |
188 | SETUP_HW(struct stifb_info *fb) | |
189 | { | |
190 | char stat; | |
191 | ||
192 | do { | |
193 | stat = READ_BYTE(fb, REG_15b0); | |
194 | if (!stat) | |
195 | stat = READ_BYTE(fb, REG_15b0); | |
196 | } while (stat); | |
197 | } | |
198 | ||
199 | ||
200 | static void | |
201 | SETUP_FB(struct stifb_info *fb) | |
202 | { | |
203 | unsigned int reg10_value = 0; | |
204 | ||
205 | SETUP_HW(fb); | |
206 | switch (fb->id) | |
207 | { | |
208 | case CRT_ID_VISUALIZE_EG: | |
209 | case S9000_ID_ARTIST: | |
210 | case S9000_ID_A1659A: | |
211 | reg10_value = 0x13601000; | |
212 | break; | |
213 | case S9000_ID_A1439A: | |
214 | if (fb->info.var.bits_per_pixel == 32) | |
215 | reg10_value = 0xBBA0A000; | |
216 | else | |
217 | reg10_value = 0x13601000; | |
218 | break; | |
219 | case S9000_ID_HCRX: | |
220 | if (fb->info.var.bits_per_pixel == 32) | |
221 | reg10_value = 0xBBA0A000; | |
222 | else | |
223 | reg10_value = 0x13602000; | |
224 | break; | |
225 | case S9000_ID_TIMBER: | |
226 | case CRX24_OVERLAY_PLANES: | |
227 | reg10_value = 0x13602000; | |
228 | break; | |
229 | } | |
230 | if (reg10_value) | |
231 | WRITE_WORD(reg10_value, fb, REG_10); | |
232 | WRITE_WORD(0x83000300, fb, REG_14); | |
233 | SETUP_HW(fb); | |
234 | WRITE_BYTE(1, fb, REG_16b1); | |
235 | } | |
236 | ||
237 | static void | |
238 | START_IMAGE_COLORMAP_ACCESS(struct stifb_info *fb) | |
239 | { | |
240 | SETUP_HW(fb); | |
241 | WRITE_WORD(0xBBE0F000, fb, REG_10); | |
242 | WRITE_WORD(0x03000300, fb, REG_14); | |
243 | WRITE_WORD(~0, fb, REG_13); | |
244 | } | |
245 | ||
246 | static void | |
247 | WRITE_IMAGE_COLOR(struct stifb_info *fb, int index, int color) | |
248 | { | |
249 | SETUP_HW(fb); | |
250 | WRITE_WORD(((0x100+index)<<2), fb, REG_3); | |
251 | WRITE_WORD(color, fb, REG_4); | |
252 | } | |
253 | ||
254 | static void | |
255 | FINISH_IMAGE_COLORMAP_ACCESS(struct stifb_info *fb) | |
256 | { | |
257 | WRITE_WORD(0x400, fb, REG_2); | |
258 | if (fb->info.var.bits_per_pixel == 32) { | |
259 | WRITE_WORD(0x83000100, fb, REG_1); | |
260 | } else { | |
261 | if (fb->id == S9000_ID_ARTIST || fb->id == CRT_ID_VISUALIZE_EG) | |
262 | WRITE_WORD(0x80000100, fb, REG_26); | |
263 | else | |
264 | WRITE_WORD(0x80000100, fb, REG_1); | |
265 | } | |
266 | SETUP_FB(fb); | |
267 | } | |
268 | ||
269 | static void | |
270 | SETUP_RAMDAC(struct stifb_info *fb) | |
271 | { | |
272 | SETUP_HW(fb); | |
273 | WRITE_WORD(0x04000000, fb, 0x1020); | |
274 | WRITE_WORD(0xff000000, fb, 0x1028); | |
275 | } | |
276 | ||
277 | static void | |
278 | CRX24_SETUP_RAMDAC(struct stifb_info *fb) | |
279 | { | |
280 | SETUP_HW(fb); | |
281 | WRITE_WORD(0x04000000, fb, 0x1000); | |
282 | WRITE_WORD(0x02000000, fb, 0x1004); | |
283 | WRITE_WORD(0xff000000, fb, 0x1008); | |
284 | WRITE_WORD(0x05000000, fb, 0x1000); | |
285 | WRITE_WORD(0x02000000, fb, 0x1004); | |
286 | WRITE_WORD(0x03000000, fb, 0x1008); | |
287 | } | |
288 | ||
289 | #if 0 | |
290 | static void | |
291 | HCRX_SETUP_RAMDAC(struct stifb_info *fb) | |
292 | { | |
293 | WRITE_WORD(0xffffffff, fb, REG_32); | |
294 | } | |
295 | #endif | |
296 | ||
297 | static void | |
298 | CRX24_SET_OVLY_MASK(struct stifb_info *fb) | |
299 | { | |
300 | SETUP_HW(fb); | |
301 | WRITE_WORD(0x13a02000, fb, REG_11); | |
302 | WRITE_WORD(0x03000300, fb, REG_14); | |
303 | WRITE_WORD(0x000017f0, fb, REG_3); | |
304 | WRITE_WORD(0xffffffff, fb, REG_13); | |
305 | WRITE_WORD(0xffffffff, fb, REG_22); | |
306 | WRITE_WORD(0x00000000, fb, REG_23); | |
307 | } | |
308 | ||
309 | static void | |
310 | ENABLE_DISABLE_DISPLAY(struct stifb_info *fb, int enable) | |
311 | { | |
312 | unsigned int value = enable ? 0x43000000 : 0x03000000; | |
313 | SETUP_HW(fb); | |
314 | WRITE_WORD(0x06000000, fb, 0x1030); | |
315 | WRITE_WORD(value, fb, 0x1038); | |
316 | } | |
317 | ||
318 | static void | |
319 | CRX24_ENABLE_DISABLE_DISPLAY(struct stifb_info *fb, int enable) | |
320 | { | |
321 | unsigned int value = enable ? 0x10000000 : 0x30000000; | |
322 | SETUP_HW(fb); | |
323 | WRITE_WORD(0x01000000, fb, 0x1000); | |
324 | WRITE_WORD(0x02000000, fb, 0x1004); | |
325 | WRITE_WORD(value, fb, 0x1008); | |
326 | } | |
327 | ||
328 | static void | |
329 | ARTIST_ENABLE_DISABLE_DISPLAY(struct stifb_info *fb, int enable) | |
330 | { | |
331 | u32 DregsMiscVideo = REG_21; | |
332 | u32 DregsMiscCtl = REG_27; | |
333 | ||
334 | SETUP_HW(fb); | |
335 | if (enable) { | |
336 | WRITE_WORD(READ_WORD(fb, DregsMiscVideo) | 0x0A000000, fb, DregsMiscVideo); | |
337 | WRITE_WORD(READ_WORD(fb, DregsMiscCtl) | 0x00800000, fb, DregsMiscCtl); | |
338 | } else { | |
339 | WRITE_WORD(READ_WORD(fb, DregsMiscVideo) & ~0x0A000000, fb, DregsMiscVideo); | |
340 | WRITE_WORD(READ_WORD(fb, DregsMiscCtl) & ~0x00800000, fb, DregsMiscCtl); | |
341 | } | |
342 | } | |
343 | ||
344 | #define GET_ROMTABLE_INDEX(fb) \ | |
345 | (READ_BYTE(fb, REG_16b3) - 1) | |
346 | ||
347 | #define HYPER_CONFIG_PLANES_24 0x00000100 | |
348 | ||
349 | #define IS_24_DEVICE(fb) \ | |
350 | (fb->deviceSpecificConfig & HYPER_CONFIG_PLANES_24) | |
351 | ||
352 | #define IS_888_DEVICE(fb) \ | |
353 | (!(IS_24_DEVICE(fb))) | |
354 | ||
355 | #define GET_FIFO_SLOTS(fb, cnt, numslots) \ | |
356 | { while (cnt < numslots) \ | |
357 | cnt = READ_WORD(fb, REG_34); \ | |
358 | cnt -= numslots; \ | |
359 | } | |
360 | ||
361 | #define IndexedDcd 0 /* Pixel data is indexed (pseudo) color */ | |
362 | #define Otc04 2 /* Pixels in each longword transfer (4) */ | |
363 | #define Otc32 5 /* Pixels in each longword transfer (32) */ | |
364 | #define Ots08 3 /* Each pixel is size (8)d transfer (1) */ | |
365 | #define OtsIndirect 6 /* Each bit goes through FG/BG color(8) */ | |
366 | #define AddrLong 5 /* FB address is Long aligned (pixel) */ | |
367 | #define BINovly 0x2 /* 8 bit overlay */ | |
368 | #define BINapp0I 0x0 /* Application Buffer 0, Indexed */ | |
369 | #define BINapp1I 0x1 /* Application Buffer 1, Indexed */ | |
370 | #define BINapp0F8 0xa /* Application Buffer 0, Fractional 8-8-8 */ | |
371 | #define BINattr 0xd /* Attribute Bitmap */ | |
372 | #define RopSrc 0x3 | |
373 | #define BitmapExtent08 3 /* Each write hits ( 8) bits in depth */ | |
374 | #define BitmapExtent32 5 /* Each write hits (32) bits in depth */ | |
375 | #define DataDynamic 0 /* Data register reloaded by direct access */ | |
376 | #define MaskDynamic 1 /* Mask register reloaded by direct access */ | |
377 | #define MaskOtc 0 /* Mask contains Object Count valid bits */ | |
378 | ||
379 | #define MaskAddrOffset(offset) (offset) | |
380 | #define StaticReg(en) (en) | |
381 | #define BGx(en) (en) | |
382 | #define FGx(en) (en) | |
383 | ||
384 | #define BAJustPoint(offset) (offset) | |
385 | #define BAIndexBase(base) (base) | |
386 | #define BA(F,C,S,A,J,B,I) \ | |
387 | (((F)<<31)|((C)<<27)|((S)<<24)|((A)<<21)|((J)<<16)|((B)<<12)|(I)) | |
388 | ||
389 | #define IBOvals(R,M,X,S,D,L,B,F) \ | |
390 | (((R)<<8)|((M)<<16)|((X)<<24)|((S)<<29)|((D)<<28)|((L)<<31)|((B)<<1)|(F)) | |
391 | ||
392 | #define NGLE_QUICK_SET_IMAGE_BITMAP_OP(fb, val) \ | |
393 | WRITE_WORD(val, fb, REG_14) | |
394 | ||
395 | #define NGLE_QUICK_SET_DST_BM_ACCESS(fb, val) \ | |
396 | WRITE_WORD(val, fb, REG_11) | |
397 | ||
398 | #define NGLE_QUICK_SET_CTL_PLN_REG(fb, val) \ | |
399 | WRITE_WORD(val, fb, REG_12) | |
400 | ||
401 | #define NGLE_REALLY_SET_IMAGE_PLANEMASK(fb, plnmsk32) \ | |
402 | WRITE_WORD(plnmsk32, fb, REG_13) | |
403 | ||
404 | #define NGLE_REALLY_SET_IMAGE_FG_COLOR(fb, fg32) \ | |
405 | WRITE_WORD(fg32, fb, REG_35) | |
406 | ||
407 | #define NGLE_SET_TRANSFERDATA(fb, val) \ | |
408 | WRITE_WORD(val, fb, REG_8) | |
409 | ||
410 | #define NGLE_SET_DSTXY(fb, val) \ | |
411 | WRITE_WORD(val, fb, REG_6) | |
412 | ||
413 | #define NGLE_LONG_FB_ADDRESS(fbaddrbase, x, y) ( \ | |
414 | (u32) (fbaddrbase) + \ | |
415 | ( (unsigned int) ( (y) << 13 ) | \ | |
416 | (unsigned int) ( (x) << 2 ) ) \ | |
417 | ) | |
418 | ||
419 | #define NGLE_BINC_SET_DSTADDR(fb, addr) \ | |
420 | WRITE_WORD(addr, fb, REG_3) | |
421 | ||
422 | #define NGLE_BINC_SET_SRCADDR(fb, addr) \ | |
423 | WRITE_WORD(addr, fb, REG_2) | |
424 | ||
425 | #define NGLE_BINC_SET_DSTMASK(fb, mask) \ | |
426 | WRITE_WORD(mask, fb, REG_22) | |
427 | ||
428 | #define NGLE_BINC_WRITE32(fb, data32) \ | |
429 | WRITE_WORD(data32, fb, REG_23) | |
430 | ||
431 | #define START_COLORMAPLOAD(fb, cmapBltCtlData32) \ | |
432 | WRITE_WORD((cmapBltCtlData32), fb, REG_38) | |
433 | ||
434 | #define SET_LENXY_START_RECFILL(fb, lenxy) \ | |
435 | WRITE_WORD(lenxy, fb, REG_9) | |
436 | ||
437 | static void | |
438 | HYPER_ENABLE_DISABLE_DISPLAY(struct stifb_info *fb, int enable) | |
439 | { | |
440 | u32 DregsHypMiscVideo = REG_33; | |
441 | unsigned int value; | |
442 | SETUP_HW(fb); | |
443 | value = READ_WORD(fb, DregsHypMiscVideo); | |
444 | if (enable) | |
445 | value |= 0x0A000000; | |
446 | else | |
447 | value &= ~0x0A000000; | |
448 | WRITE_WORD(value, fb, DregsHypMiscVideo); | |
449 | } | |
450 | ||
451 | ||
452 | /* BufferNumbers used by SETUP_ATTR_ACCESS() */ | |
453 | #define BUFF0_CMAP0 0x00001e02 | |
454 | #define BUFF1_CMAP0 0x02001e02 | |
455 | #define BUFF1_CMAP3 0x0c001e02 | |
456 | #define ARTIST_CMAP0 0x00000102 | |
457 | #define HYPER_CMAP8 0x00000100 | |
458 | #define HYPER_CMAP24 0x00000800 | |
459 | ||
460 | static void | |
461 | SETUP_ATTR_ACCESS(struct stifb_info *fb, unsigned BufferNumber) | |
462 | { | |
463 | SETUP_HW(fb); | |
464 | WRITE_WORD(0x2EA0D000, fb, REG_11); | |
465 | WRITE_WORD(0x23000302, fb, REG_14); | |
466 | WRITE_WORD(BufferNumber, fb, REG_12); | |
467 | WRITE_WORD(0xffffffff, fb, REG_8); | |
468 | } | |
469 | ||
470 | static void | |
471 | SET_ATTR_SIZE(struct stifb_info *fb, int width, int height) | |
472 | { | |
473 | /* REG_6 seems to have special values when run on a | |
474 | RDI precisionbook parisc laptop (INTERNAL_EG_DX1024 or | |
475 | INTERNAL_EG_X1024). The values are: | |
476 | 0x2f0: internal (LCD) & external display enabled | |
477 | 0x2a0: external display only | |
478 | 0x000: zero on standard artist graphic cards | |
479 | */ | |
480 | WRITE_WORD(0x00000000, fb, REG_6); | |
481 | WRITE_WORD((width<<16) | height, fb, REG_9); | |
482 | WRITE_WORD(0x05000000, fb, REG_6); | |
483 | WRITE_WORD(0x00040001, fb, REG_9); | |
484 | } | |
485 | ||
486 | static void | |
487 | FINISH_ATTR_ACCESS(struct stifb_info *fb) | |
488 | { | |
489 | SETUP_HW(fb); | |
490 | WRITE_WORD(0x00000000, fb, REG_12); | |
491 | } | |
492 | ||
493 | static void | |
494 | elkSetupPlanes(struct stifb_info *fb) | |
495 | { | |
496 | SETUP_RAMDAC(fb); | |
497 | SETUP_FB(fb); | |
498 | } | |
499 | ||
500 | static void | |
501 | ngleSetupAttrPlanes(struct stifb_info *fb, int BufferNumber) | |
502 | { | |
503 | SETUP_ATTR_ACCESS(fb, BufferNumber); | |
504 | SET_ATTR_SIZE(fb, fb->info.var.xres, fb->info.var.yres); | |
505 | FINISH_ATTR_ACCESS(fb); | |
506 | SETUP_FB(fb); | |
507 | } | |
508 | ||
509 | ||
510 | static void | |
511 | rattlerSetupPlanes(struct stifb_info *fb) | |
512 | { | |
513 | CRX24_SETUP_RAMDAC(fb); | |
514 | ||
515 | /* replacement for: SETUP_FB(fb, CRX24_OVERLAY_PLANES); */ | |
516 | WRITE_WORD(0x83000300, fb, REG_14); | |
517 | SETUP_HW(fb); | |
518 | WRITE_BYTE(1, fb, REG_16b1); | |
519 | ||
520 | fb_memset(fb->info.fix.smem_start, 0xff, | |
521 | fb->info.var.yres*fb->info.fix.line_length); | |
522 | ||
523 | CRX24_SET_OVLY_MASK(fb); | |
524 | SETUP_FB(fb); | |
525 | } | |
526 | ||
527 | ||
528 | #define HYPER_CMAP_TYPE 0 | |
529 | #define NGLE_CMAP_INDEXED0_TYPE 0 | |
530 | #define NGLE_CMAP_OVERLAY_TYPE 3 | |
531 | ||
532 | /* typedef of LUT (Colormap) BLT Control Register */ | |
533 | typedef union /* Note assumption that fields are packed left-to-right */ | |
534 | { u32 all; | |
535 | struct | |
536 | { | |
537 | unsigned enable : 1; | |
538 | unsigned waitBlank : 1; | |
539 | unsigned reserved1 : 4; | |
540 | unsigned lutOffset : 10; /* Within destination LUT */ | |
541 | unsigned lutType : 2; /* Cursor, image, overlay */ | |
542 | unsigned reserved2 : 4; | |
543 | unsigned length : 10; | |
544 | } fields; | |
545 | } NgleLutBltCtl; | |
546 | ||
547 | ||
548 | #if 0 | |
549 | static NgleLutBltCtl | |
550 | setNgleLutBltCtl(struct stifb_info *fb, int offsetWithinLut, int length) | |
551 | { | |
552 | NgleLutBltCtl lutBltCtl; | |
553 | ||
554 | /* set enable, zero reserved fields */ | |
555 | lutBltCtl.all = 0x80000000; | |
556 | lutBltCtl.fields.length = length; | |
557 | ||
558 | switch (fb->id) | |
559 | { | |
560 | case S9000_ID_A1439A: /* CRX24 */ | |
561 | if (fb->var.bits_per_pixel == 8) { | |
562 | lutBltCtl.fields.lutType = NGLE_CMAP_OVERLAY_TYPE; | |
563 | lutBltCtl.fields.lutOffset = 0; | |
564 | } else { | |
565 | lutBltCtl.fields.lutType = NGLE_CMAP_INDEXED0_TYPE; | |
566 | lutBltCtl.fields.lutOffset = 0 * 256; | |
567 | } | |
568 | break; | |
569 | ||
570 | case S9000_ID_ARTIST: | |
571 | lutBltCtl.fields.lutType = NGLE_CMAP_INDEXED0_TYPE; | |
572 | lutBltCtl.fields.lutOffset = 0 * 256; | |
573 | break; | |
574 | ||
575 | default: | |
576 | lutBltCtl.fields.lutType = NGLE_CMAP_INDEXED0_TYPE; | |
577 | lutBltCtl.fields.lutOffset = 0; | |
578 | break; | |
579 | } | |
580 | ||
581 | /* Offset points to start of LUT. Adjust for within LUT */ | |
582 | lutBltCtl.fields.lutOffset += offsetWithinLut; | |
583 | ||
584 | return lutBltCtl; | |
585 | } | |
586 | #endif | |
587 | ||
588 | static NgleLutBltCtl | |
589 | setHyperLutBltCtl(struct stifb_info *fb, int offsetWithinLut, int length) | |
590 | { | |
591 | NgleLutBltCtl lutBltCtl; | |
592 | ||
593 | /* set enable, zero reserved fields */ | |
594 | lutBltCtl.all = 0x80000000; | |
595 | ||
596 | lutBltCtl.fields.length = length; | |
597 | lutBltCtl.fields.lutType = HYPER_CMAP_TYPE; | |
598 | ||
599 | /* Expect lutIndex to be 0 or 1 for image cmaps, 2 or 3 for overlay cmaps */ | |
600 | if (fb->info.var.bits_per_pixel == 8) | |
601 | lutBltCtl.fields.lutOffset = 2 * 256; | |
602 | else | |
603 | lutBltCtl.fields.lutOffset = 0 * 256; | |
604 | ||
605 | /* Offset points to start of LUT. Adjust for within LUT */ | |
606 | lutBltCtl.fields.lutOffset += offsetWithinLut; | |
607 | ||
608 | return lutBltCtl; | |
609 | } | |
610 | ||
611 | ||
612 | static void hyperUndoITE(struct stifb_info *fb) | |
613 | { | |
614 | int nFreeFifoSlots = 0; | |
615 | u32 fbAddr; | |
616 | ||
617 | NGLE_LOCK(fb); | |
618 | ||
619 | GET_FIFO_SLOTS(fb, nFreeFifoSlots, 1); | |
620 | WRITE_WORD(0xffffffff, fb, REG_32); | |
621 | ||
622 | /* Write overlay transparency mask so only entry 255 is transparent */ | |
623 | ||
624 | /* Hardware setup for full-depth write to "magic" location */ | |
625 | GET_FIFO_SLOTS(fb, nFreeFifoSlots, 7); | |
626 | NGLE_QUICK_SET_DST_BM_ACCESS(fb, | |
627 | BA(IndexedDcd, Otc04, Ots08, AddrLong, | |
628 | BAJustPoint(0), BINovly, BAIndexBase(0))); | |
629 | NGLE_QUICK_SET_IMAGE_BITMAP_OP(fb, | |
630 | IBOvals(RopSrc, MaskAddrOffset(0), | |
631 | BitmapExtent08, StaticReg(0), | |
632 | DataDynamic, MaskOtc, BGx(0), FGx(0))); | |
633 | ||
634 | /* Now prepare to write to the "magic" location */ | |
635 | fbAddr = NGLE_LONG_FB_ADDRESS(0, 1532, 0); | |
636 | NGLE_BINC_SET_DSTADDR(fb, fbAddr); | |
637 | NGLE_REALLY_SET_IMAGE_PLANEMASK(fb, 0xffffff); | |
638 | NGLE_BINC_SET_DSTMASK(fb, 0xffffffff); | |
639 | ||
640 | /* Finally, write a zero to clear the mask */ | |
641 | NGLE_BINC_WRITE32(fb, 0); | |
642 | ||
643 | NGLE_UNLOCK(fb); | |
644 | } | |
645 | ||
646 | static void | |
647 | ngleDepth8_ClearImagePlanes(struct stifb_info *fb) | |
648 | { | |
649 | /* FIXME! */ | |
650 | } | |
651 | ||
652 | static void | |
653 | ngleDepth24_ClearImagePlanes(struct stifb_info *fb) | |
654 | { | |
655 | /* FIXME! */ | |
656 | } | |
657 | ||
658 | static void | |
659 | ngleResetAttrPlanes(struct stifb_info *fb, unsigned int ctlPlaneReg) | |
660 | { | |
661 | int nFreeFifoSlots = 0; | |
662 | u32 packed_dst; | |
663 | u32 packed_len; | |
664 | ||
665 | NGLE_LOCK(fb); | |
666 | ||
667 | GET_FIFO_SLOTS(fb, nFreeFifoSlots, 4); | |
668 | NGLE_QUICK_SET_DST_BM_ACCESS(fb, | |
669 | BA(IndexedDcd, Otc32, OtsIndirect, | |
670 | AddrLong, BAJustPoint(0), | |
671 | BINattr, BAIndexBase(0))); | |
672 | NGLE_QUICK_SET_CTL_PLN_REG(fb, ctlPlaneReg); | |
673 | NGLE_SET_TRANSFERDATA(fb, 0xffffffff); | |
674 | ||
675 | NGLE_QUICK_SET_IMAGE_BITMAP_OP(fb, | |
676 | IBOvals(RopSrc, MaskAddrOffset(0), | |
677 | BitmapExtent08, StaticReg(1), | |
678 | DataDynamic, MaskOtc, | |
679 | BGx(0), FGx(0))); | |
680 | packed_dst = 0; | |
681 | packed_len = (fb->info.var.xres << 16) | fb->info.var.yres; | |
682 | GET_FIFO_SLOTS(fb, nFreeFifoSlots, 2); | |
683 | NGLE_SET_DSTXY(fb, packed_dst); | |
684 | SET_LENXY_START_RECFILL(fb, packed_len); | |
685 | ||
686 | /* | |
687 | * In order to work around an ELK hardware problem (Buffy doesn't | |
688 | * always flush it's buffers when writing to the attribute | |
689 | * planes), at least 4 pixels must be written to the attribute | |
690 | * planes starting at (X == 1280) and (Y != to the last Y written | |
691 | * by BIF): | |
692 | */ | |
693 | ||
694 | if (fb->id == S9000_ID_A1659A) { /* ELK_DEVICE_ID */ | |
695 | /* It's safe to use scanline zero: */ | |
696 | packed_dst = (1280 << 16); | |
697 | GET_FIFO_SLOTS(fb, nFreeFifoSlots, 2); | |
698 | NGLE_SET_DSTXY(fb, packed_dst); | |
699 | packed_len = (4 << 16) | 1; | |
700 | SET_LENXY_START_RECFILL(fb, packed_len); | |
701 | } /* ELK Hardware Kludge */ | |
702 | ||
703 | /**** Finally, set the Control Plane Register back to zero: ****/ | |
704 | GET_FIFO_SLOTS(fb, nFreeFifoSlots, 1); | |
705 | NGLE_QUICK_SET_CTL_PLN_REG(fb, 0); | |
706 | ||
707 | NGLE_UNLOCK(fb); | |
708 | } | |
709 | ||
710 | static void | |
711 | ngleClearOverlayPlanes(struct stifb_info *fb, int mask, int data) | |
712 | { | |
713 | int nFreeFifoSlots = 0; | |
714 | u32 packed_dst; | |
715 | u32 packed_len; | |
716 | ||
717 | NGLE_LOCK(fb); | |
718 | ||
719 | /* Hardware setup */ | |
720 | GET_FIFO_SLOTS(fb, nFreeFifoSlots, 8); | |
721 | NGLE_QUICK_SET_DST_BM_ACCESS(fb, | |
722 | BA(IndexedDcd, Otc04, Ots08, AddrLong, | |
723 | BAJustPoint(0), BINovly, BAIndexBase(0))); | |
724 | ||
725 | NGLE_SET_TRANSFERDATA(fb, 0xffffffff); /* Write foreground color */ | |
726 | ||
727 | NGLE_REALLY_SET_IMAGE_FG_COLOR(fb, data); | |
728 | NGLE_REALLY_SET_IMAGE_PLANEMASK(fb, mask); | |
729 | ||
730 | packed_dst = 0; | |
731 | packed_len = (fb->info.var.xres << 16) | fb->info.var.yres; | |
732 | NGLE_SET_DSTXY(fb, packed_dst); | |
733 | ||
734 | /* Write zeroes to overlay planes */ | |
735 | NGLE_QUICK_SET_IMAGE_BITMAP_OP(fb, | |
736 | IBOvals(RopSrc, MaskAddrOffset(0), | |
737 | BitmapExtent08, StaticReg(0), | |
738 | DataDynamic, MaskOtc, BGx(0), FGx(0))); | |
739 | ||
740 | SET_LENXY_START_RECFILL(fb, packed_len); | |
741 | ||
742 | NGLE_UNLOCK(fb); | |
743 | } | |
744 | ||
745 | static void | |
746 | hyperResetPlanes(struct stifb_info *fb, int enable) | |
747 | { | |
748 | unsigned int controlPlaneReg; | |
749 | ||
750 | NGLE_LOCK(fb); | |
751 | ||
752 | if (IS_24_DEVICE(fb)) | |
753 | if (fb->info.var.bits_per_pixel == 32) | |
754 | controlPlaneReg = 0x04000F00; | |
755 | else | |
756 | controlPlaneReg = 0x00000F00; /* 0x00000800 should be enought, but lets clear all 4 bits */ | |
757 | else | |
758 | controlPlaneReg = 0x00000F00; /* 0x00000100 should be enought, but lets clear all 4 bits */ | |
759 | ||
760 | switch (enable) { | |
761 | case ENABLE: | |
762 | /* clear screen */ | |
763 | if (IS_24_DEVICE(fb)) | |
764 | ngleDepth24_ClearImagePlanes(fb); | |
765 | else | |
766 | ngleDepth8_ClearImagePlanes(fb); | |
767 | ||
768 | /* Paint attribute planes for default case. | |
769 | * On Hyperdrive, this means all windows using overlay cmap 0. */ | |
770 | ngleResetAttrPlanes(fb, controlPlaneReg); | |
771 | ||
772 | /* clear overlay planes */ | |
773 | ngleClearOverlayPlanes(fb, 0xff, 255); | |
774 | ||
775 | /************************************************** | |
776 | ** Also need to counteract ITE settings | |
777 | **************************************************/ | |
778 | hyperUndoITE(fb); | |
779 | break; | |
780 | ||
781 | case DISABLE: | |
782 | /* clear screen */ | |
783 | if (IS_24_DEVICE(fb)) | |
784 | ngleDepth24_ClearImagePlanes(fb); | |
785 | else | |
786 | ngleDepth8_ClearImagePlanes(fb); | |
787 | ngleResetAttrPlanes(fb, controlPlaneReg); | |
788 | ngleClearOverlayPlanes(fb, 0xff, 0); | |
789 | break; | |
790 | ||
791 | case -1: /* RESET */ | |
792 | hyperUndoITE(fb); | |
793 | ngleResetAttrPlanes(fb, controlPlaneReg); | |
794 | break; | |
795 | } | |
796 | ||
797 | NGLE_UNLOCK(fb); | |
798 | } | |
799 | ||
800 | /* Return pointer to in-memory structure holding ELK device-dependent ROM values. */ | |
801 | ||
802 | static void | |
803 | ngleGetDeviceRomData(struct stifb_info *fb) | |
804 | { | |
805 | #if 0 | |
806 | XXX: FIXME: !!! | |
807 | int *pBytePerLongDevDepData;/* data byte == LSB */ | |
808 | int *pRomTable; | |
809 | NgleDevRomData *pPackedDevRomData; | |
810 | int sizePackedDevRomData = sizeof(*pPackedDevRomData); | |
811 | char *pCard8; | |
812 | int i; | |
813 | char *mapOrigin = NULL; | |
814 | ||
815 | int romTableIdx; | |
816 | ||
817 | pPackedDevRomData = fb->ngle_rom; | |
818 | ||
819 | SETUP_HW(fb); | |
820 | if (fb->id == S9000_ID_ARTIST) { | |
821 | pPackedDevRomData->cursor_pipeline_delay = 4; | |
822 | pPackedDevRomData->video_interleaves = 4; | |
823 | } else { | |
824 | /* Get pointer to unpacked byte/long data in ROM */ | |
825 | pBytePerLongDevDepData = fb->sti->regions[NGLEDEVDEPROM_CRT_REGION]; | |
826 | ||
827 | /* Tomcat supports several resolutions: 1280x1024, 1024x768, 640x480 */ | |
828 | if (fb->id == S9000_ID_TOMCAT) | |
829 | { | |
830 | /* jump to the correct ROM table */ | |
831 | GET_ROMTABLE_INDEX(romTableIdx); | |
832 | while (romTableIdx > 0) | |
833 | { | |
834 | pCard8 = (Card8 *) pPackedDevRomData; | |
835 | pRomTable = pBytePerLongDevDepData; | |
836 | /* Pack every fourth byte from ROM into structure */ | |
837 | for (i = 0; i < sizePackedDevRomData; i++) | |
838 | { | |
839 | *pCard8++ = (Card8) (*pRomTable++); | |
840 | } | |
841 | ||
842 | pBytePerLongDevDepData = (Card32 *) | |
843 | ((Card8 *) pBytePerLongDevDepData + | |
844 | pPackedDevRomData->sizeof_ngle_data); | |
845 | ||
846 | romTableIdx--; | |
847 | } | |
848 | } | |
849 | ||
850 | pCard8 = (Card8 *) pPackedDevRomData; | |
851 | ||
852 | /* Pack every fourth byte from ROM into structure */ | |
853 | for (i = 0; i < sizePackedDevRomData; i++) | |
854 | { | |
855 | *pCard8++ = (Card8) (*pBytePerLongDevDepData++); | |
856 | } | |
857 | } | |
858 | ||
859 | SETUP_FB(fb); | |
860 | #endif | |
861 | } | |
862 | ||
863 | ||
864 | #define HYPERBOWL_MODE_FOR_8_OVER_88_LUT0_NO_TRANSPARENCIES 4 | |
865 | #define HYPERBOWL_MODE01_8_24_LUT0_TRANSPARENT_LUT1_OPAQUE 8 | |
866 | #define HYPERBOWL_MODE01_8_24_LUT0_OPAQUE_LUT1_OPAQUE 10 | |
867 | #define HYPERBOWL_MODE2_8_24 15 | |
868 | ||
869 | /* HCRX specific boot-time initialization */ | |
870 | static void __init | |
871 | SETUP_HCRX(struct stifb_info *fb) | |
872 | { | |
873 | int hyperbowl; | |
874 | int nFreeFifoSlots = 0; | |
875 | ||
876 | if (fb->id != S9000_ID_HCRX) | |
877 | return; | |
878 | ||
879 | /* Initialize Hyperbowl registers */ | |
880 | GET_FIFO_SLOTS(fb, nFreeFifoSlots, 7); | |
881 | ||
882 | if (IS_24_DEVICE(fb)) { | |
883 | hyperbowl = (fb->info.var.bits_per_pixel == 32) ? | |
884 | HYPERBOWL_MODE01_8_24_LUT0_TRANSPARENT_LUT1_OPAQUE : | |
885 | HYPERBOWL_MODE01_8_24_LUT0_OPAQUE_LUT1_OPAQUE; | |
886 | ||
887 | /* First write to Hyperbowl must happen twice (bug) */ | |
888 | WRITE_WORD(hyperbowl, fb, REG_40); | |
889 | WRITE_WORD(hyperbowl, fb, REG_40); | |
890 | ||
891 | WRITE_WORD(HYPERBOWL_MODE2_8_24, fb, REG_39); | |
892 | ||
893 | WRITE_WORD(0x014c0148, fb, REG_42); /* Set lut 0 to be the direct color */ | |
894 | WRITE_WORD(0x404c4048, fb, REG_43); | |
895 | WRITE_WORD(0x034c0348, fb, REG_44); | |
896 | WRITE_WORD(0x444c4448, fb, REG_45); | |
897 | } else { | |
898 | hyperbowl = HYPERBOWL_MODE_FOR_8_OVER_88_LUT0_NO_TRANSPARENCIES; | |
899 | ||
900 | /* First write to Hyperbowl must happen twice (bug) */ | |
901 | WRITE_WORD(hyperbowl, fb, REG_40); | |
902 | WRITE_WORD(hyperbowl, fb, REG_40); | |
903 | ||
904 | WRITE_WORD(0x00000000, fb, REG_42); | |
905 | WRITE_WORD(0x00000000, fb, REG_43); | |
906 | WRITE_WORD(0x00000000, fb, REG_44); | |
907 | WRITE_WORD(0x444c4048, fb, REG_45); | |
908 | } | |
909 | } | |
910 | ||
911 | ||
912 | /* ------------------- driver specific functions --------------------------- */ | |
913 | ||
914 | #define TMPBUFLEN 2048 | |
915 | ||
916 | static ssize_t | |
917 | stifb_read(struct file *file, char *buf, size_t count, loff_t *ppos) | |
918 | { | |
919 | unsigned long p = *ppos; | |
920 | struct inode *inode = file->f_dentry->d_inode; | |
921 | int fbidx = iminor(inode); | |
922 | struct fb_info *info = registered_fb[fbidx]; | |
923 | char tmpbuf[TMPBUFLEN]; | |
924 | ||
925 | if (!info || ! info->screen_base) | |
926 | return -ENODEV; | |
927 | ||
928 | if (p >= info->fix.smem_len) | |
929 | return 0; | |
930 | if (count >= info->fix.smem_len) | |
931 | count = info->fix.smem_len; | |
932 | if (count + p > info->fix.smem_len) | |
933 | count = info->fix.smem_len - p; | |
934 | if (count > sizeof(tmpbuf)) | |
935 | count = sizeof(tmpbuf); | |
936 | if (count) { | |
937 | char *base_addr; | |
938 | ||
939 | base_addr = info->screen_base; | |
940 | memcpy_fromio(&tmpbuf, base_addr+p, count); | |
941 | count -= copy_to_user(buf, &tmpbuf, count); | |
942 | if (!count) | |
943 | return -EFAULT; | |
944 | *ppos += count; | |
945 | } | |
946 | return count; | |
947 | } | |
948 | ||
949 | static ssize_t | |
950 | stifb_write(struct file *file, const char *buf, size_t count, loff_t *ppos) | |
951 | { | |
952 | struct inode *inode = file->f_dentry->d_inode; | |
953 | int fbidx = iminor(inode); | |
954 | struct fb_info *info = registered_fb[fbidx]; | |
955 | unsigned long p = *ppos; | |
956 | size_t c; | |
957 | int err; | |
958 | char tmpbuf[TMPBUFLEN]; | |
959 | ||
960 | if (!info || !info->screen_base) | |
961 | return -ENODEV; | |
962 | ||
963 | if (p > info->fix.smem_len) | |
964 | return -ENOSPC; | |
965 | if (count >= info->fix.smem_len) | |
966 | count = info->fix.smem_len; | |
967 | err = 0; | |
968 | if (count + p > info->fix.smem_len) { | |
969 | count = info->fix.smem_len - p; | |
970 | err = -ENOSPC; | |
971 | } | |
972 | ||
973 | p += (unsigned long)info->screen_base; | |
974 | c = count; | |
975 | while (c) { | |
976 | int len = c > sizeof(tmpbuf) ? sizeof(tmpbuf) : c; | |
977 | err = -EFAULT; | |
978 | if (copy_from_user(&tmpbuf, buf, len)) | |
979 | break; | |
980 | memcpy_toio(p, &tmpbuf, len); | |
981 | c -= len; | |
982 | p += len; | |
983 | buf += len; | |
984 | *ppos += len; | |
985 | } | |
986 | if (count-c) | |
987 | return (count-c); | |
988 | return err; | |
989 | } | |
990 | ||
991 | static int | |
992 | stifb_setcolreg(u_int regno, u_int red, u_int green, | |
993 | u_int blue, u_int transp, struct fb_info *info) | |
994 | { | |
995 | struct stifb_info *fb = (struct stifb_info *) info; | |
996 | u32 color; | |
997 | ||
998 | if (regno >= 256) /* no. of hw registers */ | |
999 | return 1; | |
1000 | ||
1001 | red >>= 8; | |
1002 | green >>= 8; | |
1003 | blue >>= 8; | |
1004 | ||
1005 | DEBUG_OFF(); | |
1006 | ||
1007 | START_IMAGE_COLORMAP_ACCESS(fb); | |
1008 | ||
1009 | if (fb->info.var.grayscale) { | |
1010 | /* gray = 0.30*R + 0.59*G + 0.11*B */ | |
1011 | color = ((red * 77) + | |
1012 | (green * 151) + | |
1013 | (blue * 28)) >> 8; | |
1014 | } else { | |
1015 | color = ((red << 16) | | |
1016 | (green << 8) | | |
1017 | (blue)); | |
1018 | } | |
1019 | ||
1020 | if (info->var.bits_per_pixel == 32) { | |
1021 | ((u32 *)(info->pseudo_palette))[regno] = | |
1022 | (red << info->var.red.offset) | | |
1023 | (green << info->var.green.offset) | | |
1024 | (blue << info->var.blue.offset); | |
1025 | } else { | |
1026 | ((u32 *)(info->pseudo_palette))[regno] = regno; | |
1027 | } | |
1028 | ||
1029 | WRITE_IMAGE_COLOR(fb, regno, color); | |
1030 | ||
1031 | if (fb->id == S9000_ID_HCRX) { | |
1032 | NgleLutBltCtl lutBltCtl; | |
1033 | ||
1034 | lutBltCtl = setHyperLutBltCtl(fb, | |
1035 | 0, /* Offset w/i LUT */ | |
1036 | 256); /* Load entire LUT */ | |
1037 | NGLE_BINC_SET_SRCADDR(fb, | |
1038 | NGLE_LONG_FB_ADDRESS(0, 0x100, 0)); | |
1039 | /* 0x100 is same as used in WRITE_IMAGE_COLOR() */ | |
1040 | START_COLORMAPLOAD(fb, lutBltCtl.all); | |
1041 | SETUP_FB(fb); | |
1042 | } else { | |
1043 | /* cleanup colormap hardware */ | |
1044 | FINISH_IMAGE_COLORMAP_ACCESS(fb); | |
1045 | } | |
1046 | ||
1047 | DEBUG_ON(); | |
1048 | ||
1049 | return 0; | |
1050 | } | |
1051 | ||
1052 | static int | |
1053 | stifb_blank(int blank_mode, struct fb_info *info) | |
1054 | { | |
1055 | struct stifb_info *fb = (struct stifb_info *) info; | |
1056 | int enable = (blank_mode == 0) ? ENABLE : DISABLE; | |
1057 | ||
1058 | switch (fb->id) { | |
1059 | case S9000_ID_A1439A: | |
1060 | CRX24_ENABLE_DISABLE_DISPLAY(fb, enable); | |
1061 | break; | |
1062 | case CRT_ID_VISUALIZE_EG: | |
1063 | case S9000_ID_ARTIST: | |
1064 | ARTIST_ENABLE_DISABLE_DISPLAY(fb, enable); | |
1065 | break; | |
1066 | case S9000_ID_HCRX: | |
1067 | HYPER_ENABLE_DISABLE_DISPLAY(fb, enable); | |
1068 | break; | |
1069 | case S9000_ID_A1659A:; /* fall through */ | |
1070 | case S9000_ID_TIMBER:; | |
1071 | case CRX24_OVERLAY_PLANES:; | |
1072 | default: | |
1073 | ENABLE_DISABLE_DISPLAY(fb, enable); | |
1074 | break; | |
1075 | } | |
1076 | ||
1077 | SETUP_FB(fb); | |
1078 | return 0; | |
1079 | } | |
1080 | ||
1081 | static void __init | |
1082 | stifb_init_display(struct stifb_info *fb) | |
1083 | { | |
1084 | int id = fb->id; | |
1085 | ||
1086 | SETUP_FB(fb); | |
1087 | ||
1088 | /* HCRX specific initialization */ | |
1089 | SETUP_HCRX(fb); | |
1090 | ||
1091 | /* | |
1092 | if (id == S9000_ID_HCRX) | |
1093 | hyperInitSprite(fb); | |
1094 | else | |
1095 | ngleInitSprite(fb); | |
1096 | */ | |
1097 | ||
1098 | /* Initialize the image planes. */ | |
1099 | switch (id) { | |
1100 | case S9000_ID_HCRX: | |
1101 | hyperResetPlanes(fb, ENABLE); | |
1102 | break; | |
1103 | case S9000_ID_A1439A: | |
1104 | rattlerSetupPlanes(fb); | |
1105 | break; | |
1106 | case S9000_ID_A1659A: | |
1107 | case S9000_ID_ARTIST: | |
1108 | case CRT_ID_VISUALIZE_EG: | |
1109 | elkSetupPlanes(fb); | |
1110 | break; | |
1111 | } | |
1112 | ||
1113 | /* Clear attribute planes on non HCRX devices. */ | |
1114 | switch (id) { | |
1115 | case S9000_ID_A1659A: | |
1116 | case S9000_ID_A1439A: | |
1117 | if (fb->info.var.bits_per_pixel == 32) | |
1118 | ngleSetupAttrPlanes(fb, BUFF1_CMAP3); | |
1119 | else { | |
1120 | ngleSetupAttrPlanes(fb, BUFF1_CMAP0); | |
1121 | } | |
1122 | if (id == S9000_ID_A1439A) | |
1123 | ngleClearOverlayPlanes(fb, 0xff, 0); | |
1124 | break; | |
1125 | case S9000_ID_ARTIST: | |
1126 | case CRT_ID_VISUALIZE_EG: | |
1127 | if (fb->info.var.bits_per_pixel == 32) | |
1128 | ngleSetupAttrPlanes(fb, BUFF1_CMAP3); | |
1129 | else { | |
1130 | ngleSetupAttrPlanes(fb, ARTIST_CMAP0); | |
1131 | } | |
1132 | break; | |
1133 | } | |
1134 | stifb_blank(0, (struct fb_info *)fb); /* 0=enable screen */ | |
1135 | ||
1136 | SETUP_FB(fb); | |
1137 | } | |
1138 | ||
1139 | /* ------------ Interfaces to hardware functions ------------ */ | |
1140 | ||
1141 | static struct fb_ops stifb_ops = { | |
1142 | .owner = THIS_MODULE, | |
1143 | .fb_read = stifb_read, | |
1144 | .fb_write = stifb_write, | |
1145 | .fb_setcolreg = stifb_setcolreg, | |
1146 | .fb_blank = stifb_blank, | |
1147 | .fb_fillrect = cfb_fillrect, | |
1148 | .fb_copyarea = cfb_copyarea, | |
1149 | .fb_imageblit = cfb_imageblit, | |
1da177e4 LT |
1150 | }; |
1151 | ||
1152 | ||
1153 | /* | |
1154 | * Initialization | |
1155 | */ | |
1156 | ||
1157 | int __init | |
1158 | stifb_init_fb(struct sti_struct *sti, int bpp_pref) | |
1159 | { | |
1160 | struct fb_fix_screeninfo *fix; | |
1161 | struct fb_var_screeninfo *var; | |
1162 | struct stifb_info *fb; | |
1163 | struct fb_info *info; | |
1164 | unsigned long sti_rom_address; | |
1165 | char *dev_name; | |
1166 | int bpp, xres, yres; | |
1167 | ||
1168 | fb = kmalloc(sizeof(*fb), GFP_ATOMIC); | |
1169 | if (!fb) { | |
1170 | printk(KERN_ERR "stifb: Could not allocate stifb structure\n"); | |
1171 | return -ENODEV; | |
1172 | } | |
1173 | ||
1174 | info = &fb->info; | |
1175 | ||
1176 | /* set struct to a known state */ | |
1177 | memset(fb, 0, sizeof(*fb)); | |
1178 | fix = &info->fix; | |
1179 | var = &info->var; | |
1180 | ||
1181 | fb->sti = sti; | |
1182 | /* store upper 32bits of the graphics id */ | |
1183 | fb->id = fb->sti->graphics_id[0]; | |
1184 | ||
1185 | /* only supported cards are allowed */ | |
1186 | switch (fb->id) { | |
1187 | case CRT_ID_VISUALIZE_EG: | |
1188 | /* look for a double buffering device like e.g. the | |
1189 | "INTERNAL_EG_DX1024" in the RDI precisionbook laptop | |
1190 | which won't work. The same device in non-double | |
1191 | buffering mode returns "INTERNAL_EG_X1024". */ | |
1192 | if (strstr(sti->outptr.dev_name, "EG_DX")) { | |
1193 | printk(KERN_WARNING | |
1194 | "stifb: ignoring '%s'. Disable double buffering in IPL menu.\n", | |
1195 | sti->outptr.dev_name); | |
1196 | goto out_err0; | |
1197 | } | |
1198 | /* fall though */ | |
1199 | case S9000_ID_ARTIST: | |
1200 | case S9000_ID_HCRX: | |
1201 | case S9000_ID_TIMBER: | |
1202 | case S9000_ID_A1659A: | |
1203 | case S9000_ID_A1439A: | |
1204 | break; | |
1205 | default: | |
1206 | printk(KERN_WARNING "stifb: '%s' (id: 0x%08x) not supported.\n", | |
1207 | sti->outptr.dev_name, fb->id); | |
1208 | goto out_err0; | |
1209 | } | |
1210 | ||
1211 | /* default to 8 bpp on most graphic chips */ | |
1212 | bpp = 8; | |
1213 | xres = sti_onscreen_x(fb->sti); | |
1214 | yres = sti_onscreen_y(fb->sti); | |
1215 | ||
1216 | ngleGetDeviceRomData(fb); | |
1217 | ||
1218 | /* get (virtual) io region base addr */ | |
1219 | fix->mmio_start = REGION_BASE(fb,2); | |
1220 | fix->mmio_len = 0x400000; | |
1221 | ||
1222 | /* Reject any device not in the NGLE family */ | |
1223 | switch (fb->id) { | |
1224 | case S9000_ID_A1659A: /* CRX/A1659A */ | |
1225 | break; | |
1226 | case S9000_ID_ELM: /* GRX, grayscale but else same as A1659A */ | |
1227 | var->grayscale = 1; | |
1228 | fb->id = S9000_ID_A1659A; | |
1229 | break; | |
1230 | case S9000_ID_TIMBER: /* HP9000/710 Any (may be a grayscale device) */ | |
1231 | dev_name = fb->sti->outptr.dev_name; | |
1232 | if (strstr(dev_name, "GRAYSCALE") || | |
1233 | strstr(dev_name, "Grayscale") || | |
1234 | strstr(dev_name, "grayscale")) | |
1235 | var->grayscale = 1; | |
1236 | break; | |
1237 | case S9000_ID_TOMCAT: /* Dual CRX, behaves else like a CRX */ | |
1238 | /* FIXME: TomCat supports two heads: | |
1239 | * fb.iobase = REGION_BASE(fb_info,3); | |
1240 | * fb.screen_base = (void*) REGION_BASE(fb_info,2); | |
1241 | * for now we only support the left one ! */ | |
1242 | xres = fb->ngle_rom.x_size_visible; | |
1243 | yres = fb->ngle_rom.y_size_visible; | |
1244 | fb->id = S9000_ID_A1659A; | |
1245 | break; | |
1246 | case S9000_ID_A1439A: /* CRX24/A1439A */ | |
1247 | bpp = 32; | |
1248 | break; | |
1249 | case S9000_ID_HCRX: /* Hyperdrive/HCRX */ | |
1250 | memset(&fb->ngle_rom, 0, sizeof(fb->ngle_rom)); | |
1251 | if ((fb->sti->regions_phys[0] & 0xfc000000) == | |
1252 | (fb->sti->regions_phys[2] & 0xfc000000)) | |
1253 | sti_rom_address = fb->sti->regions_phys[0]; | |
1254 | else | |
1255 | sti_rom_address = fb->sti->regions_phys[1]; | |
1256 | #ifdef __LP64__ | |
1257 | sti_rom_address |= 0xffffffff00000000; | |
1258 | #endif | |
1259 | fb->deviceSpecificConfig = gsc_readl(sti_rom_address); | |
1260 | if (IS_24_DEVICE(fb)) { | |
1261 | if (bpp_pref == 8 || bpp_pref == 32) | |
1262 | bpp = bpp_pref; | |
1263 | else | |
1264 | bpp = 32; | |
1265 | } else | |
1266 | bpp = 8; | |
1267 | READ_WORD(fb, REG_15); | |
1268 | SETUP_HW(fb); | |
1269 | break; | |
1270 | case CRT_ID_VISUALIZE_EG: | |
1271 | case S9000_ID_ARTIST: /* Artist */ | |
1272 | break; | |
1273 | default: | |
1274 | #ifdef FALLBACK_TO_1BPP | |
1275 | printk(KERN_WARNING | |
1276 | "stifb: Unsupported graphics card (id=0x%08x) " | |
1277 | "- now trying 1bpp mode instead\n", | |
1278 | fb->id); | |
1279 | bpp = 1; /* default to 1 bpp */ | |
1280 | break; | |
1281 | #else | |
1282 | printk(KERN_WARNING | |
1283 | "stifb: Unsupported graphics card (id=0x%08x) " | |
1284 | "- skipping.\n", | |
1285 | fb->id); | |
1286 | goto out_err0; | |
1287 | #endif | |
1288 | } | |
1289 | ||
1290 | ||
1291 | /* get framebuffer physical and virtual base addr & len (64bit ready) */ | |
1292 | fix->smem_start = F_EXTEND(fb->sti->regions_phys[1]); | |
1293 | fix->smem_len = fb->sti->regions[1].region_desc.length * 4096; | |
1294 | ||
1295 | fix->line_length = (fb->sti->glob_cfg->total_x * bpp) / 8; | |
1296 | if (!fix->line_length) | |
1297 | fix->line_length = 2048; /* default */ | |
1298 | ||
1299 | /* limit fbsize to max visible screen size */ | |
1300 | if (fix->smem_len > yres*fix->line_length) | |
1301 | fix->smem_len = yres*fix->line_length; | |
1302 | ||
1303 | fix->accel = FB_ACCEL_NONE; | |
1304 | ||
1305 | switch (bpp) { | |
1306 | case 1: | |
1307 | fix->type = FB_TYPE_PLANES; /* well, sort of */ | |
1308 | fix->visual = FB_VISUAL_MONO10; | |
1309 | var->red.length = var->green.length = var->blue.length = 1; | |
1310 | break; | |
1311 | case 8: | |
1312 | fix->type = FB_TYPE_PACKED_PIXELS; | |
1313 | fix->visual = FB_VISUAL_PSEUDOCOLOR; | |
1314 | var->red.length = var->green.length = var->blue.length = 8; | |
1315 | break; | |
1316 | case 32: | |
1317 | fix->type = FB_TYPE_PACKED_PIXELS; | |
1318 | fix->visual = FB_VISUAL_TRUECOLOR; | |
1319 | var->red.length = var->green.length = var->blue.length = var->transp.length = 8; | |
1320 | var->blue.offset = 0; | |
1321 | var->green.offset = 8; | |
1322 | var->red.offset = 16; | |
1323 | var->transp.offset = 24; | |
1324 | break; | |
1325 | default: | |
1326 | break; | |
1327 | } | |
1328 | ||
1329 | var->xres = var->xres_virtual = xres; | |
1330 | var->yres = var->yres_virtual = yres; | |
1331 | var->bits_per_pixel = bpp; | |
1332 | ||
1333 | strcpy(fix->id, "stifb"); | |
1334 | info->fbops = &stifb_ops; | |
1335 | info->screen_base = (void*) REGION_BASE(fb,1); | |
1336 | info->flags = FBINFO_DEFAULT; | |
1337 | info->pseudo_palette = &fb->pseudo_palette; | |
1338 | ||
1339 | /* This has to been done !!! */ | |
1340 | fb_alloc_cmap(&info->cmap, 256, 0); | |
1341 | stifb_init_display(fb); | |
1342 | ||
1343 | if (!request_mem_region(fix->smem_start, fix->smem_len, "stifb fb")) { | |
1344 | printk(KERN_ERR "stifb: cannot reserve fb region 0x%04lx-0x%04lx\n", | |
1345 | fix->smem_start, fix->smem_start+fix->smem_len); | |
1346 | goto out_err1; | |
1347 | } | |
1348 | ||
1349 | if (!request_mem_region(fix->mmio_start, fix->mmio_len, "stifb mmio")) { | |
1350 | printk(KERN_ERR "stifb: cannot reserve sti mmio region 0x%04lx-0x%04lx\n", | |
1351 | fix->mmio_start, fix->mmio_start+fix->mmio_len); | |
1352 | goto out_err2; | |
1353 | } | |
1354 | ||
1355 | if (register_framebuffer(&fb->info) < 0) | |
1356 | goto out_err3; | |
1357 | ||
1358 | sti->info = info; /* save for unregister_framebuffer() */ | |
1359 | ||
1360 | printk(KERN_INFO | |
1361 | "fb%d: %s %dx%d-%d frame buffer device, %s, id: %04x, mmio: 0x%04lx\n", | |
1362 | fb->info.node, | |
1363 | fix->id, | |
1364 | var->xres, | |
1365 | var->yres, | |
1366 | var->bits_per_pixel, | |
1367 | sti->outptr.dev_name, | |
1368 | fb->id, | |
1369 | fix->mmio_start); | |
1370 | ||
1371 | return 0; | |
1372 | ||
1373 | ||
1374 | out_err3: | |
1375 | release_mem_region(fix->mmio_start, fix->mmio_len); | |
1376 | out_err2: | |
1377 | release_mem_region(fix->smem_start, fix->smem_len); | |
1378 | out_err1: | |
1379 | fb_dealloc_cmap(&info->cmap); | |
1380 | out_err0: | |
1381 | kfree(fb); | |
1382 | return -ENXIO; | |
1383 | } | |
1384 | ||
1385 | static int stifb_disabled __initdata; | |
1386 | ||
1387 | int __init | |
1388 | stifb_setup(char *options); | |
1389 | ||
1390 | int __init | |
1391 | stifb_init(void) | |
1392 | { | |
1393 | struct sti_struct *sti; | |
1394 | struct sti_struct *def_sti; | |
1395 | int i; | |
1396 | ||
1397 | #ifndef MODULE | |
1398 | char *option = NULL; | |
1399 | ||
1400 | if (fb_get_options("stifb", &option)) | |
1401 | return -ENODEV; | |
1402 | stifb_setup(option); | |
1403 | #endif | |
1404 | if (stifb_disabled) { | |
1405 | printk(KERN_INFO "stifb: disabled by \"stifb=off\" kernel parameter\n"); | |
1406 | return -ENXIO; | |
1407 | } | |
1408 | ||
1409 | def_sti = sti_get_rom(0); | |
1410 | if (def_sti) { | |
1411 | for (i = 1; i <= MAX_STI_ROMS; i++) { | |
1412 | sti = sti_get_rom(i); | |
1413 | if (!sti) | |
1414 | break; | |
1415 | if (sti == def_sti) { | |
1416 | stifb_init_fb(sti, stifb_bpp_pref[i - 1]); | |
1417 | break; | |
1418 | } | |
1419 | } | |
1420 | } | |
1421 | ||
1422 | for (i = 1; i <= MAX_STI_ROMS; i++) { | |
1423 | sti = sti_get_rom(i); | |
1424 | if (!sti) | |
1425 | break; | |
1426 | if (sti == def_sti) | |
1427 | continue; | |
1428 | stifb_init_fb(sti, stifb_bpp_pref[i - 1]); | |
1429 | } | |
1430 | return 0; | |
1431 | } | |
1432 | ||
1433 | /* | |
1434 | * Cleanup | |
1435 | */ | |
1436 | ||
1437 | static void __exit | |
1438 | stifb_cleanup(void) | |
1439 | { | |
1440 | struct sti_struct *sti; | |
1441 | int i; | |
1442 | ||
1443 | for (i = 1; i <= MAX_STI_ROMS; i++) { | |
1444 | sti = sti_get_rom(i); | |
1445 | if (!sti) | |
1446 | break; | |
1447 | if (sti->info) { | |
1448 | struct fb_info *info = sti->info; | |
1449 | unregister_framebuffer(sti->info); | |
1450 | release_mem_region(info->fix.mmio_start, info->fix.mmio_len); | |
1451 | release_mem_region(info->fix.smem_start, info->fix.smem_len); | |
1452 | fb_dealloc_cmap(&info->cmap); | |
1453 | kfree(info); | |
1454 | } | |
1455 | sti->info = NULL; | |
1456 | } | |
1457 | } | |
1458 | ||
1459 | int __init | |
1460 | stifb_setup(char *options) | |
1461 | { | |
1462 | int i; | |
1463 | ||
1464 | if (!options || !*options) | |
1465 | return 0; | |
1466 | ||
1467 | if (strncmp(options, "off", 3) == 0) { | |
1468 | stifb_disabled = 1; | |
1469 | options += 3; | |
1470 | } | |
1471 | ||
1472 | if (strncmp(options, "bpp", 3) == 0) { | |
1473 | options += 3; | |
1474 | for (i = 0; i < MAX_STI_ROMS; i++) { | |
1475 | if (*options++ != ':') | |
1476 | break; | |
1477 | stifb_bpp_pref[i] = simple_strtoul(options, &options, 10); | |
1478 | } | |
1479 | } | |
1480 | return 0; | |
1481 | } | |
1482 | ||
1483 | __setup("stifb=", stifb_setup); | |
1484 | ||
1485 | module_init(stifb_init); | |
1486 | module_exit(stifb_cleanup); | |
1487 | ||
1488 | MODULE_AUTHOR("Helge Deller <deller@gmx.de>, Thomas Bogendoerfer <tsbogend@alpha.franken.de>"); | |
1489 | MODULE_DESCRIPTION("Framebuffer driver for HP's NGLE series graphics cards in HP PARISC machines"); | |
1490 | MODULE_LICENSE("GPL v2"); | |
1491 | ||
1492 | MODULE_PARM(bpp, "i"); | |
1493 | MODULE_PARM_DESC(mem, "Bits per pixel (default: 8)"); | |
1494 |