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