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 <asm/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
82typedef 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
95typedef 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
105struct 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
114static 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
186static void
187SETUP_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
199static void
200SETUP_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
236static void
237START_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
245static void
246WRITE_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
253static void
254FINISH_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
268static void
269SETUP_RAMDAC(struct stifb_info *fb)
270{
271	SETUP_HW(fb);
272	WRITE_WORD(0x04000000, fb, 0x1020);
273	WRITE_WORD(0xff000000, fb, 0x1028);
274}
275
276static void
277CRX24_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
289static void
290HCRX_SETUP_RAMDAC(struct stifb_info *fb)
291{
292	WRITE_WORD(0xffffffff, fb, REG_32);
293}
294#endif
295
296static void
297CRX24_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
308static void
309ENABLE_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
317static void
318CRX24_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
327static void
328ARTIST_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
439static void
440HYPER_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
462static void
463SETUP_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
472static void
473SET_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
488static void
489FINISH_ATTR_ACCESS(struct stifb_info *fb)
490{
491	SETUP_HW(fb);
492	WRITE_WORD(0x00000000, fb, REG_12);
493}
494
495static void
496elkSetupPlanes(struct stifb_info *fb)
497{
498	SETUP_RAMDAC(fb);
499	SETUP_FB(fb);
500}
501
502static void
503ngleSetupAttrPlanes(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
512static void
513rattlerSetupPlanes(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		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 */
543typedef 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
559static NgleLutBltCtl
560setNgleLutBltCtl(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
598static NgleLutBltCtl
599setHyperLutBltCtl(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
622static 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
656static void
657ngleDepth8_ClearImagePlanes(struct stifb_info *fb)
658{
659	/* FIXME! */
660}
661
662static void
663ngleDepth24_ClearImagePlanes(struct stifb_info *fb)
664{
665	/* FIXME! */
666}
667
668static void
669ngleResetAttrPlanes(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
720static void
721ngleClearOverlayPlanes(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
755static void
756hyperResetPlanes(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
812static void
813ngleGetDeviceRomData(struct stifb_info *fb)
814{
815#if 0
816XXX: 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 */
880static void __init
881SETUP_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
924static int
925stifb_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
985static int
986stifb_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:	/* fall through */
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
1014static void
1015stifb_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
1044static void __init
1045stifb_init_display(struct stifb_info *fb)
1046{
1047	int id = fb->id;
1048
1049	SETUP_FB(fb);
1050
1051	/* HCRX specific initialization */
1052	SETUP_HCRX(fb);
1053
1054	/*
1055	if (id == S9000_ID_HCRX)
1056		hyperInitSprite(fb);
1057	else
1058		ngleInitSprite(fb);
1059	*/
1060
1061	/* Initialize the image planes. */
1062        switch (id) {
1063	 case S9000_ID_HCRX:
1064	    hyperResetPlanes(fb, ENABLE);
1065	    break;
1066	 case S9000_ID_A1439A:
1067	    rattlerSetupPlanes(fb);
1068	    break;
1069	 case S9000_ID_A1659A:
1070	 case S9000_ID_ARTIST:
1071	 case CRT_ID_VISUALIZE_EG:
1072	    elkSetupPlanes(fb);
1073	    break;
1074	}
1075
1076	/* Clear attribute planes on non HCRX devices. */
1077        switch (id) {
1078	 case S9000_ID_A1659A:
1079	 case S9000_ID_A1439A:
1080	    if (fb->info.var.bits_per_pixel == 32)
1081		ngleSetupAttrPlanes(fb, BUFF1_CMAP3);
1082	    else {
1083		ngleSetupAttrPlanes(fb, BUFF1_CMAP0);
1084	    }
1085	    if (id == S9000_ID_A1439A)
1086		ngleClearOverlayPlanes(fb, 0xff, 0);
1087	    break;
1088	 case S9000_ID_ARTIST:
1089	 case CRT_ID_VISUALIZE_EG:
1090	    if (fb->info.var.bits_per_pixel == 32)
1091		ngleSetupAttrPlanes(fb, BUFF1_CMAP3);
1092	    else {
1093		ngleSetupAttrPlanes(fb, ARTIST_CMAP0);
1094	    }
1095	    break;
1096	}
1097	stifb_blank(0, (struct fb_info *)fb);	/* 0=enable screen */
1098
1099	SETUP_FB(fb);
1100}
1101
1102/* ------------ Interfaces to hardware functions ------------ */
1103
1104static struct fb_ops stifb_ops = {
1105	.owner		= THIS_MODULE,
1106	.fb_setcolreg	= stifb_setcolreg,
1107	.fb_blank	= stifb_blank,
1108	.fb_fillrect	= cfb_fillrect,
1109	.fb_copyarea	= stifb_copyarea,
1110	.fb_imageblit	= cfb_imageblit,
1111};
1112
1113
1114/*
1115 *  Initialization
1116 */
1117
1118static int __init stifb_init_fb(struct sti_struct *sti, int bpp_pref)
1119{
1120	struct fb_fix_screeninfo *fix;
1121	struct fb_var_screeninfo *var;
1122	struct stifb_info *fb;
1123	struct fb_info *info;
1124	unsigned long sti_rom_address;
1125	char *dev_name;
1126	int bpp, xres, yres;
1127
1128	fb = kzalloc(sizeof(*fb), GFP_ATOMIC);
1129	if (!fb) {
1130		printk(KERN_ERR "stifb: Could not allocate stifb structure\n");
1131		return -ENODEV;
1132	}
1133
1134	info = &fb->info;
1135
1136	/* set struct to a known state */
1137	fix = &info->fix;
1138	var = &info->var;
1139
1140	fb->sti = sti;
1141	dev_name = sti->sti_data->inq_outptr.dev_name;
1142	/* store upper 32bits of the graphics id */
1143	fb->id = fb->sti->graphics_id[0];
1144
1145	/* only supported cards are allowed */
1146	switch (fb->id) {
1147	case CRT_ID_VISUALIZE_EG:
1148		/* Visualize cards can run either in "double buffer" or
1149 		  "standard" mode. Depending on the mode, the card reports
1150		  a different device name, e.g. "INTERNAL_EG_DX1024" in double
1151		  buffer mode and "INTERNAL_EG_X1024" in standard mode.
1152		  Since this driver only supports standard mode, we check
1153		  if the device name contains the string "DX" and tell the
1154		  user how to reconfigure the card. */
1155		if (strstr(dev_name, "DX")) {
1156		   printk(KERN_WARNING
1157"WARNING: stifb framebuffer driver does not support '%s' in double-buffer mode.\n"
1158"WARNING: Please disable the double-buffer mode in IPL menu (the PARISC-BIOS).\n",
1159			dev_name);
1160		   goto out_err0;
1161		}
1162		/* fall though */
1163	case S9000_ID_ARTIST:
1164	case S9000_ID_HCRX:
1165	case S9000_ID_TIMBER:
1166	case S9000_ID_A1659A:
1167	case S9000_ID_A1439A:
1168		break;
1169	default:
1170		printk(KERN_WARNING "stifb: '%s' (id: 0x%08x) not supported.\n",
1171			dev_name, fb->id);
1172		goto out_err0;
1173	}
1174
1175	/* default to 8 bpp on most graphic chips */
1176	bpp = 8;
1177	xres = sti_onscreen_x(fb->sti);
1178	yres = sti_onscreen_y(fb->sti);
1179
1180	ngleGetDeviceRomData(fb);
1181
1182	/* get (virtual) io region base addr */
1183	fix->mmio_start = REGION_BASE(fb,2);
1184	fix->mmio_len   = 0x400000;
1185
1186       	/* Reject any device not in the NGLE family */
1187	switch (fb->id) {
1188	case S9000_ID_A1659A:	/* CRX/A1659A */
1189		break;
1190	case S9000_ID_ELM:	/* GRX, grayscale but else same as A1659A */
1191		var->grayscale = 1;
1192		fb->id = S9000_ID_A1659A;
1193		break;
1194	case S9000_ID_TIMBER:	/* HP9000/710 Any (may be a grayscale device) */
1195		if (strstr(dev_name, "GRAYSCALE") ||
1196		    strstr(dev_name, "Grayscale") ||
1197		    strstr(dev_name, "grayscale"))
1198			var->grayscale = 1;
1199		break;
1200	case S9000_ID_TOMCAT:	/* Dual CRX, behaves else like a CRX */
1201		/* FIXME: TomCat supports two heads:
1202		 * fb.iobase = REGION_BASE(fb_info,3);
1203		 * fb.screen_base = ioremap_nocache(REGION_BASE(fb_info,2),xxx);
1204		 * for now we only support the left one ! */
1205		xres = fb->ngle_rom.x_size_visible;
1206		yres = fb->ngle_rom.y_size_visible;
1207		fb->id = S9000_ID_A1659A;
1208		break;
1209	case S9000_ID_A1439A:	/* CRX24/A1439A */
1210		bpp = 32;
1211		break;
1212	case S9000_ID_HCRX:	/* Hyperdrive/HCRX */
1213		memset(&fb->ngle_rom, 0, sizeof(fb->ngle_rom));
1214		if ((fb->sti->regions_phys[0] & 0xfc000000) ==
1215		    (fb->sti->regions_phys[2] & 0xfc000000))
1216			sti_rom_address = F_EXTEND(fb->sti->regions_phys[0]);
1217		else
1218			sti_rom_address = F_EXTEND(fb->sti->regions_phys[1]);
1219
1220		fb->deviceSpecificConfig = gsc_readl(sti_rom_address);
1221		if (IS_24_DEVICE(fb)) {
1222			if (bpp_pref == 8 || bpp_pref == 32)
1223				bpp = bpp_pref;
1224			else
1225				bpp = 32;
1226		} else
1227			bpp = 8;
1228		READ_WORD(fb, REG_15);
1229		SETUP_HW(fb);
1230		break;
1231	case CRT_ID_VISUALIZE_EG:
1232	case S9000_ID_ARTIST:	/* Artist */
1233		break;
1234	default:
1235#ifdef FALLBACK_TO_1BPP
1236	       	printk(KERN_WARNING
1237			"stifb: Unsupported graphics card (id=0x%08x) "
1238				"- now trying 1bpp mode instead\n",
1239			fb->id);
1240		bpp = 1;	/* default to 1 bpp */
1241		break;
1242#else
1243	       	printk(KERN_WARNING
1244			"stifb: Unsupported graphics card (id=0x%08x) "
1245				"- skipping.\n",
1246			fb->id);
1247		goto out_err0;
1248#endif
1249	}
1250
1251
1252	/* get framebuffer physical and virtual base addr & len (64bit ready) */
1253	fix->smem_start = F_EXTEND(fb->sti->regions_phys[1]);
1254	fix->smem_len = fb->sti->regions[1].region_desc.length * 4096;
1255
1256	fix->line_length = (fb->sti->glob_cfg->total_x * bpp) / 8;
1257	if (!fix->line_length)
1258		fix->line_length = 2048; /* default */
1259
1260	/* limit fbsize to max visible screen size */
1261	if (fix->smem_len > yres*fix->line_length)
1262		fix->smem_len = yres*fix->line_length;
1263
1264	fix->accel = FB_ACCEL_NONE;
1265
1266	switch (bpp) {
1267	    case 1:
1268		fix->type = FB_TYPE_PLANES;	/* well, sort of */
1269		fix->visual = FB_VISUAL_MONO10;
1270		var->red.length = var->green.length = var->blue.length = 1;
1271		break;
1272	    case 8:
1273		fix->type = FB_TYPE_PACKED_PIXELS;
1274		fix->visual = FB_VISUAL_PSEUDOCOLOR;
1275		var->red.length = var->green.length = var->blue.length = 8;
1276		break;
1277	    case 32:
1278		fix->type = FB_TYPE_PACKED_PIXELS;
1279		fix->visual = FB_VISUAL_DIRECTCOLOR;
1280		var->red.length = var->green.length = var->blue.length = var->transp.length = 8;
1281		var->blue.offset = 0;
1282		var->green.offset = 8;
1283		var->red.offset = 16;
1284		var->transp.offset = 24;
1285		break;
1286	    default:
1287		break;
1288	}
1289
1290	var->xres = var->xres_virtual = xres;
1291	var->yres = var->yres_virtual = yres;
1292	var->bits_per_pixel = bpp;
1293
1294	strcpy(fix->id, "stifb");
1295	info->fbops = &stifb_ops;
1296	info->screen_base = ioremap_nocache(REGION_BASE(fb,1), fix->smem_len);
1297	info->screen_size = fix->smem_len;
1298	info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA;
1299	info->pseudo_palette = &fb->pseudo_palette;
1300
1301	/* This has to be done !!! */
1302	if (fb_alloc_cmap(&info->cmap, NR_PALETTE, 0))
1303		goto out_err1;
1304	stifb_init_display(fb);
1305
1306	if (!request_mem_region(fix->smem_start, fix->smem_len, "stifb fb")) {
1307		printk(KERN_ERR "stifb: cannot reserve fb region 0x%04lx-0x%04lx\n",
1308				fix->smem_start, fix->smem_start+fix->smem_len);
1309		goto out_err2;
1310	}
1311
1312	if (!request_mem_region(fix->mmio_start, fix->mmio_len, "stifb mmio")) {
1313		printk(KERN_ERR "stifb: cannot reserve sti mmio region 0x%04lx-0x%04lx\n",
1314				fix->mmio_start, fix->mmio_start+fix->mmio_len);
1315		goto out_err3;
1316	}
1317
1318	if (register_framebuffer(&fb->info) < 0)
1319		goto out_err4;
1320
1321	sti->info = info; /* save for unregister_framebuffer() */
1322
1323	fb_info(&fb->info, "%s %dx%d-%d frame buffer device, %s, id: %04x, mmio: 0x%04lx\n",
1324		fix->id,
1325		var->xres,
1326		var->yres,
1327		var->bits_per_pixel,
1328		dev_name,
1329		fb->id,
1330		fix->mmio_start);
1331
1332	return 0;
1333
1334
1335out_err4:
1336	release_mem_region(fix->mmio_start, fix->mmio_len);
1337out_err3:
1338	release_mem_region(fix->smem_start, fix->smem_len);
1339out_err2:
1340	fb_dealloc_cmap(&info->cmap);
1341out_err1:
1342	iounmap(info->screen_base);
1343out_err0:
1344	kfree(fb);
1345	return -ENXIO;
1346}
1347
1348static int stifb_disabled __initdata;
1349
1350int __init
1351stifb_setup(char *options);
1352
1353static int __init stifb_init(void)
1354{
1355	struct sti_struct *sti;
1356	struct sti_struct *def_sti;
1357	int i;
1358
1359#ifndef MODULE
1360	char *option = NULL;
1361
1362	if (fb_get_options("stifb", &option))
1363		return -ENODEV;
1364	stifb_setup(option);
1365#endif
1366	if (stifb_disabled) {
1367		printk(KERN_INFO "stifb: disabled by \"stifb=off\" kernel parameter\n");
1368		return -ENXIO;
1369	}
1370
1371	def_sti = sti_get_rom(0);
1372	if (def_sti) {
1373		for (i = 1; i <= MAX_STI_ROMS; i++) {
1374			sti = sti_get_rom(i);
1375			if (!sti)
1376				break;
1377			if (sti == def_sti) {
1378				stifb_init_fb(sti, stifb_bpp_pref[i - 1]);
1379				break;
1380			}
1381		}
1382	}
1383
1384	for (i = 1; i <= MAX_STI_ROMS; i++) {
1385		sti = sti_get_rom(i);
1386		if (!sti)
1387			break;
1388		if (sti == def_sti)
1389			continue;
1390		stifb_init_fb(sti, stifb_bpp_pref[i - 1]);
1391	}
1392	return 0;
1393}
1394
1395/*
1396 *  Cleanup
1397 */
1398
1399static void __exit
1400stifb_cleanup(void)
1401{
1402	struct sti_struct *sti;
1403	int i;
1404
1405	for (i = 1; i <= MAX_STI_ROMS; i++) {
1406		sti = sti_get_rom(i);
1407		if (!sti)
1408			break;
1409		if (sti->info) {
1410			struct fb_info *info = sti->info;
1411			unregister_framebuffer(sti->info);
1412			release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
1413		        release_mem_region(info->fix.smem_start, info->fix.smem_len);
1414				if (info->screen_base)
1415					iounmap(info->screen_base);
1416		        fb_dealloc_cmap(&info->cmap);
1417		        framebuffer_release(info);
1418		}
1419		sti->info = NULL;
1420	}
1421}
1422
1423int __init
1424stifb_setup(char *options)
1425{
1426	int i;
1427
1428	if (!options || !*options)
1429		return 1;
1430
1431	if (strncmp(options, "off", 3) == 0) {
1432		stifb_disabled = 1;
1433		options += 3;
1434	}
1435
1436	if (strncmp(options, "bpp", 3) == 0) {
1437		options += 3;
1438		for (i = 0; i < MAX_STI_ROMS; i++) {
1439			if (*options++ != ':')
1440				break;
1441			stifb_bpp_pref[i] = simple_strtoul(options, &options, 10);
1442		}
1443	}
1444	return 1;
1445}
1446
1447__setup("stifb=", stifb_setup);
1448
1449module_init(stifb_init);
1450module_exit(stifb_cleanup);
1451
1452MODULE_AUTHOR("Helge Deller <deller@gmx.de>, Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
1453MODULE_DESCRIPTION("Framebuffer driver for HP's NGLE series graphics cards in HP PARISC machines");
1454MODULE_LICENSE("GPL v2");
1455