blob: 9e52794768e6110f21c701d3eaf9b7d95e478250 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
86typedef 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
99typedef 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
109struct 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
118static 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
187static void
188SETUP_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
200static void
201SETUP_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
237static void
238START_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
246static void
247WRITE_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
254static void
255FINISH_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
269static void
270SETUP_RAMDAC(struct stifb_info *fb)
271{
272 SETUP_HW(fb);
273 WRITE_WORD(0x04000000, fb, 0x1020);
274 WRITE_WORD(0xff000000, fb, 0x1028);
275}
276
277static void
278CRX24_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
290static void
291HCRX_SETUP_RAMDAC(struct stifb_info *fb)
292{
293 WRITE_WORD(0xffffffff, fb, REG_32);
294}
295#endif
296
297static void
298CRX24_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
309static void
310ENABLE_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
318static void
319CRX24_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
328static void
329ARTIST_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
437static void
438HYPER_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
460static void
461SETUP_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
470static void
471SET_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
486static void
487FINISH_ATTR_ACCESS(struct stifb_info *fb)
488{
489 SETUP_HW(fb);
490 WRITE_WORD(0x00000000, fb, REG_12);
491}
492
493static void
494elkSetupPlanes(struct stifb_info *fb)
495{
496 SETUP_RAMDAC(fb);
497 SETUP_FB(fb);
498}
499
500static void
501ngleSetupAttrPlanes(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
510static void
511rattlerSetupPlanes(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 */
533typedef 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
549static NgleLutBltCtl
550setNgleLutBltCtl(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
588static NgleLutBltCtl
589setHyperLutBltCtl(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
612static 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
646static void
647ngleDepth8_ClearImagePlanes(struct stifb_info *fb)
648{
649 /* FIXME! */
650}
651
652static void
653ngleDepth24_ClearImagePlanes(struct stifb_info *fb)
654{
655 /* FIXME! */
656}
657
658static void
659ngleResetAttrPlanes(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
710static void
711ngleClearOverlayPlanes(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
745static void
746hyperResetPlanes(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
802static void
803ngleGetDeviceRomData(struct stifb_info *fb)
804{
805#if 0
806XXX: 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 */
870static void __init
871SETUP_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
916static ssize_t
917stifb_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
949static ssize_t
950stifb_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
991static int
992stifb_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
1052static int
1053stifb_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
1081static void __init
1082stifb_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
1141static 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,
1150 .fb_cursor = soft_cursor,
1151};
1152
1153
1154/*
1155 * Initialization
1156 */
1157
1158int __init
1159stifb_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 = kmalloc(sizeof(*fb), GFP_ATOMIC);
1170 if (!fb) {
1171 printk(KERN_ERR "stifb: Could not allocate stifb structure\n");
1172 return -ENODEV;
1173 }
1174
1175 info = &fb->info;
1176
1177 /* set struct to a known state */
1178 memset(fb, 0, sizeof(*fb));
1179 fix = &info->fix;
1180 var = &info->var;
1181
1182 fb->sti = sti;
1183 /* store upper 32bits of the graphics id */
1184 fb->id = fb->sti->graphics_id[0];
1185
1186 /* only supported cards are allowed */
1187 switch (fb->id) {
1188 case CRT_ID_VISUALIZE_EG:
1189 /* look for a double buffering device like e.g. the
1190 "INTERNAL_EG_DX1024" in the RDI precisionbook laptop
1191 which won't work. The same device in non-double
1192 buffering mode returns "INTERNAL_EG_X1024". */
1193 if (strstr(sti->outptr.dev_name, "EG_DX")) {
1194 printk(KERN_WARNING
1195 "stifb: ignoring '%s'. Disable double buffering in IPL menu.\n",
1196 sti->outptr.dev_name);
1197 goto out_err0;
1198 }
1199 /* fall though */
1200 case S9000_ID_ARTIST:
1201 case S9000_ID_HCRX:
1202 case S9000_ID_TIMBER:
1203 case S9000_ID_A1659A:
1204 case S9000_ID_A1439A:
1205 break;
1206 default:
1207 printk(KERN_WARNING "stifb: '%s' (id: 0x%08x) not supported.\n",
1208 sti->outptr.dev_name, fb->id);
1209 goto out_err0;
1210 }
1211
1212 /* default to 8 bpp on most graphic chips */
1213 bpp = 8;
1214 xres = sti_onscreen_x(fb->sti);
1215 yres = sti_onscreen_y(fb->sti);
1216
1217 ngleGetDeviceRomData(fb);
1218
1219 /* get (virtual) io region base addr */
1220 fix->mmio_start = REGION_BASE(fb,2);
1221 fix->mmio_len = 0x400000;
1222
1223 /* Reject any device not in the NGLE family */
1224 switch (fb->id) {
1225 case S9000_ID_A1659A: /* CRX/A1659A */
1226 break;
1227 case S9000_ID_ELM: /* GRX, grayscale but else same as A1659A */
1228 var->grayscale = 1;
1229 fb->id = S9000_ID_A1659A;
1230 break;
1231 case S9000_ID_TIMBER: /* HP9000/710 Any (may be a grayscale device) */
1232 dev_name = fb->sti->outptr.dev_name;
1233 if (strstr(dev_name, "GRAYSCALE") ||
1234 strstr(dev_name, "Grayscale") ||
1235 strstr(dev_name, "grayscale"))
1236 var->grayscale = 1;
1237 break;
1238 case S9000_ID_TOMCAT: /* Dual CRX, behaves else like a CRX */
1239 /* FIXME: TomCat supports two heads:
1240 * fb.iobase = REGION_BASE(fb_info,3);
1241 * fb.screen_base = (void*) REGION_BASE(fb_info,2);
1242 * for now we only support the left one ! */
1243 xres = fb->ngle_rom.x_size_visible;
1244 yres = fb->ngle_rom.y_size_visible;
1245 fb->id = S9000_ID_A1659A;
1246 break;
1247 case S9000_ID_A1439A: /* CRX24/A1439A */
1248 bpp = 32;
1249 break;
1250 case S9000_ID_HCRX: /* Hyperdrive/HCRX */
1251 memset(&fb->ngle_rom, 0, sizeof(fb->ngle_rom));
1252 if ((fb->sti->regions_phys[0] & 0xfc000000) ==
1253 (fb->sti->regions_phys[2] & 0xfc000000))
1254 sti_rom_address = fb->sti->regions_phys[0];
1255 else
1256 sti_rom_address = fb->sti->regions_phys[1];
1257#ifdef __LP64__
1258 sti_rom_address |= 0xffffffff00000000;
1259#endif
1260 fb->deviceSpecificConfig = gsc_readl(sti_rom_address);
1261 if (IS_24_DEVICE(fb)) {
1262 if (bpp_pref == 8 || bpp_pref == 32)
1263 bpp = bpp_pref;
1264 else
1265 bpp = 32;
1266 } else
1267 bpp = 8;
1268 READ_WORD(fb, REG_15);
1269 SETUP_HW(fb);
1270 break;
1271 case CRT_ID_VISUALIZE_EG:
1272 case S9000_ID_ARTIST: /* Artist */
1273 break;
1274 default:
1275#ifdef FALLBACK_TO_1BPP
1276 printk(KERN_WARNING
1277 "stifb: Unsupported graphics card (id=0x%08x) "
1278 "- now trying 1bpp mode instead\n",
1279 fb->id);
1280 bpp = 1; /* default to 1 bpp */
1281 break;
1282#else
1283 printk(KERN_WARNING
1284 "stifb: Unsupported graphics card (id=0x%08x) "
1285 "- skipping.\n",
1286 fb->id);
1287 goto out_err0;
1288#endif
1289 }
1290
1291
1292 /* get framebuffer physical and virtual base addr & len (64bit ready) */
1293 fix->smem_start = F_EXTEND(fb->sti->regions_phys[1]);
1294 fix->smem_len = fb->sti->regions[1].region_desc.length * 4096;
1295
1296 fix->line_length = (fb->sti->glob_cfg->total_x * bpp) / 8;
1297 if (!fix->line_length)
1298 fix->line_length = 2048; /* default */
1299
1300 /* limit fbsize to max visible screen size */
1301 if (fix->smem_len > yres*fix->line_length)
1302 fix->smem_len = yres*fix->line_length;
1303
1304 fix->accel = FB_ACCEL_NONE;
1305
1306 switch (bpp) {
1307 case 1:
1308 fix->type = FB_TYPE_PLANES; /* well, sort of */
1309 fix->visual = FB_VISUAL_MONO10;
1310 var->red.length = var->green.length = var->blue.length = 1;
1311 break;
1312 case 8:
1313 fix->type = FB_TYPE_PACKED_PIXELS;
1314 fix->visual = FB_VISUAL_PSEUDOCOLOR;
1315 var->red.length = var->green.length = var->blue.length = 8;
1316 break;
1317 case 32:
1318 fix->type = FB_TYPE_PACKED_PIXELS;
1319 fix->visual = FB_VISUAL_TRUECOLOR;
1320 var->red.length = var->green.length = var->blue.length = var->transp.length = 8;
1321 var->blue.offset = 0;
1322 var->green.offset = 8;
1323 var->red.offset = 16;
1324 var->transp.offset = 24;
1325 break;
1326 default:
1327 break;
1328 }
1329
1330 var->xres = var->xres_virtual = xres;
1331 var->yres = var->yres_virtual = yres;
1332 var->bits_per_pixel = bpp;
1333
1334 strcpy(fix->id, "stifb");
1335 info->fbops = &stifb_ops;
1336 info->screen_base = (void*) REGION_BASE(fb,1);
1337 info->flags = FBINFO_DEFAULT;
1338 info->pseudo_palette = &fb->pseudo_palette;
1339
1340 /* This has to been done !!! */
1341 fb_alloc_cmap(&info->cmap, 256, 0);
1342 stifb_init_display(fb);
1343
1344 if (!request_mem_region(fix->smem_start, fix->smem_len, "stifb fb")) {
1345 printk(KERN_ERR "stifb: cannot reserve fb region 0x%04lx-0x%04lx\n",
1346 fix->smem_start, fix->smem_start+fix->smem_len);
1347 goto out_err1;
1348 }
1349
1350 if (!request_mem_region(fix->mmio_start, fix->mmio_len, "stifb mmio")) {
1351 printk(KERN_ERR "stifb: cannot reserve sti mmio region 0x%04lx-0x%04lx\n",
1352 fix->mmio_start, fix->mmio_start+fix->mmio_len);
1353 goto out_err2;
1354 }
1355
1356 if (register_framebuffer(&fb->info) < 0)
1357 goto out_err3;
1358
1359 sti->info = info; /* save for unregister_framebuffer() */
1360
1361 printk(KERN_INFO
1362 "fb%d: %s %dx%d-%d frame buffer device, %s, id: %04x, mmio: 0x%04lx\n",
1363 fb->info.node,
1364 fix->id,
1365 var->xres,
1366 var->yres,
1367 var->bits_per_pixel,
1368 sti->outptr.dev_name,
1369 fb->id,
1370 fix->mmio_start);
1371
1372 return 0;
1373
1374
1375out_err3:
1376 release_mem_region(fix->mmio_start, fix->mmio_len);
1377out_err2:
1378 release_mem_region(fix->smem_start, fix->smem_len);
1379out_err1:
1380 fb_dealloc_cmap(&info->cmap);
1381out_err0:
1382 kfree(fb);
1383 return -ENXIO;
1384}
1385
1386static int stifb_disabled __initdata;
1387
1388int __init
1389stifb_setup(char *options);
1390
1391int __init
1392stifb_init(void)
1393{
1394 struct sti_struct *sti;
1395 struct sti_struct *def_sti;
1396 int i;
1397
1398#ifndef MODULE
1399 char *option = NULL;
1400
1401 if (fb_get_options("stifb", &option))
1402 return -ENODEV;
1403 stifb_setup(option);
1404#endif
1405 if (stifb_disabled) {
1406 printk(KERN_INFO "stifb: disabled by \"stifb=off\" kernel parameter\n");
1407 return -ENXIO;
1408 }
1409
1410 def_sti = sti_get_rom(0);
1411 if (def_sti) {
1412 for (i = 1; i <= MAX_STI_ROMS; i++) {
1413 sti = sti_get_rom(i);
1414 if (!sti)
1415 break;
1416 if (sti == def_sti) {
1417 stifb_init_fb(sti, stifb_bpp_pref[i - 1]);
1418 break;
1419 }
1420 }
1421 }
1422
1423 for (i = 1; i <= MAX_STI_ROMS; i++) {
1424 sti = sti_get_rom(i);
1425 if (!sti)
1426 break;
1427 if (sti == def_sti)
1428 continue;
1429 stifb_init_fb(sti, stifb_bpp_pref[i - 1]);
1430 }
1431 return 0;
1432}
1433
1434/*
1435 * Cleanup
1436 */
1437
1438static void __exit
1439stifb_cleanup(void)
1440{
1441 struct sti_struct *sti;
1442 int i;
1443
1444 for (i = 1; i <= MAX_STI_ROMS; i++) {
1445 sti = sti_get_rom(i);
1446 if (!sti)
1447 break;
1448 if (sti->info) {
1449 struct fb_info *info = sti->info;
1450 unregister_framebuffer(sti->info);
1451 release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
1452 release_mem_region(info->fix.smem_start, info->fix.smem_len);
1453 fb_dealloc_cmap(&info->cmap);
1454 kfree(info);
1455 }
1456 sti->info = NULL;
1457 }
1458}
1459
1460int __init
1461stifb_setup(char *options)
1462{
1463 int i;
1464
1465 if (!options || !*options)
1466 return 0;
1467
1468 if (strncmp(options, "off", 3) == 0) {
1469 stifb_disabled = 1;
1470 options += 3;
1471 }
1472
1473 if (strncmp(options, "bpp", 3) == 0) {
1474 options += 3;
1475 for (i = 0; i < MAX_STI_ROMS; i++) {
1476 if (*options++ != ':')
1477 break;
1478 stifb_bpp_pref[i] = simple_strtoul(options, &options, 10);
1479 }
1480 }
1481 return 0;
1482}
1483
1484__setup("stifb=", stifb_setup);
1485
1486module_init(stifb_init);
1487module_exit(stifb_cleanup);
1488
1489MODULE_AUTHOR("Helge Deller <deller@gmx.de>, Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
1490MODULE_DESCRIPTION("Framebuffer driver for HP's NGLE series graphics cards in HP PARISC machines");
1491MODULE_LICENSE("GPL v2");
1492
1493MODULE_PARM(bpp, "i");
1494MODULE_PARM_DESC(mem, "Bits per pixel (default: 8)");
1495