import of ftp.dlink.com/GPL/DSMG-600_reB/ppclinux.tar.gz
[linux-2.4.21-pre4.git] / drivers / video / cgthreefb.c
1 /* $Id: cgthreefb.c,v 1.1.1.1 2005/04/11 02:50:41 jack Exp $
2  * cgthreefb.c: CGthree frame buffer driver
3  *
4  * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
5  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
6  * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
7  */
8
9 #include <linux/module.h>
10 #include <linux/sched.h>
11 #include <linux/kernel.h>
12 #include <linux/errno.h>
13 #include <linux/string.h>
14 #include <linux/mm.h>
15 #include <linux/tty.h>
16 #include <linux/slab.h>
17 #include <linux/vmalloc.h>
18 #include <linux/delay.h>
19 #include <linux/interrupt.h>
20 #include <linux/fb.h>
21 #include <linux/init.h>
22 #include <linux/selection.h>
23
24 #include <video/sbusfb.h>
25 #include <asm/io.h>
26
27 #include <video/fbcon-cfb8.h>
28
29 /* Control Register Constants */
30 #define CG3_CR_ENABLE_INTS      0x80
31 #define CG3_CR_ENABLE_VIDEO     0x40
32 #define CG3_CR_ENABLE_TIMING    0x20
33 #define CG3_CR_ENABLE_CURCMP    0x10
34 #define CG3_CR_XTAL_MASK        0x0c
35 #define CG3_CR_DIVISOR_MASK     0x03
36
37 /* Status Register Constants */
38 #define CG3_SR_PENDING_INT      0x80
39 #define CG3_SR_RES_MASK         0x70
40 #define CG3_SR_1152_900_76_A    0x40
41 #define CG3_SR_1152_900_76_B    0x60
42 #define CG3_SR_ID_MASK          0x0f
43 #define CG3_SR_ID_COLOR         0x01
44 #define CG3_SR_ID_MONO          0x02
45 #define CG3_SR_ID_MONO_ECL      0x03
46
47 MODULE_LICENSE("GPL");
48
49 enum cg3_type {
50         CG3_AT_66HZ = 0,
51         CG3_AT_76HZ,
52         CG3_RDI
53 };
54
55 struct cg3_regs {
56         struct bt_regs  cmap;
57         volatile u8     control;
58         volatile u8     status;
59         volatile u8     cursor_start;
60         volatile u8     cursor_end;
61         volatile u8     h_blank_start;
62         volatile u8     h_blank_end;
63         volatile u8     h_sync_start;
64         volatile u8     h_sync_end;
65         volatile u8     comp_sync_end;
66         volatile u8     v_blank_start_high;
67         volatile u8     v_blank_start_low;
68         volatile u8     v_blank_end;
69         volatile u8     v_sync_start;
70         volatile u8     v_sync_end;
71         volatile u8     xfer_holdoff_start;
72         volatile u8     xfer_holdoff_end;
73 };
74
75 /* Offset of interesting structures in the OBIO space */
76 #define CG3_REGS_OFFSET      0x400000UL
77 #define CG3_RAM_OFFSET       0x800000UL
78
79 static struct sbus_mmap_map cg3_mmap_map[] = {
80         { CG3_MMAP_OFFSET,      CG3_RAM_OFFSET,         SBUS_MMAP_FBSIZE(1) },
81         { 0,                    0,                      0                   }
82 };
83
84 /* The cg3 palette is loaded with 4 color values at each time  */
85 /* so you end up with: (rgb)(r), (gb)(rg), (b)(rgb), and so on */
86
87 #define D4M3(x) ((((x)>>2)<<1) + ((x)>>2))      /* (x/4)*3 */
88 #define D4M4(x) ((x)&~0x3)                      /* (x/4)*4 */
89
90 static void cg3_loadcmap (struct fb_info_sbusfb *fb, struct display *p, int index, int count)
91 {
92         struct bt_regs *bt = &fb->s.cg3.regs->cmap;
93         unsigned long flags;
94         u32 *i;
95         volatile u8 *regp;
96         int steps;
97                 
98         spin_lock_irqsave(&fb->lock, flags);
99
100         i = (((u32 *)fb->color_map) + D4M3(index));
101         steps = D4M3(index+count-1) - D4M3(index)+3;
102                                 
103         regp = (volatile u8 *)&bt->addr;
104         sbus_writeb(D4M4(index), regp);
105         while (steps--) {
106                 u32 val = *i++;
107                 sbus_writel(val, &bt->color_map);
108         }
109
110         spin_unlock_irqrestore(&fb->lock, flags);
111 }
112
113 static void cg3_blank (struct fb_info_sbusfb *fb)
114 {
115         unsigned long flags;
116         u8 tmp;
117
118         spin_lock_irqsave(&fb->lock, flags);
119         tmp = sbus_readb(&fb->s.cg3.regs->control);
120         tmp &= ~CG3_CR_ENABLE_VIDEO;
121         sbus_writeb(tmp, &fb->s.cg3.regs->control);
122         spin_unlock_irqrestore(&fb->lock, flags);
123 }
124
125 static void cg3_unblank (struct fb_info_sbusfb *fb)
126 {
127         unsigned long flags;
128         u8 tmp;
129
130         spin_lock_irqsave(&fb->lock, flags);
131         tmp = sbus_readb(&fb->s.cg3.regs->control);
132         tmp |= CG3_CR_ENABLE_VIDEO;
133         sbus_writeb(tmp, &fb->s.cg3.regs->control);
134         spin_unlock_irqrestore(&fb->lock, flags);
135 }
136
137 static void cg3_margins (struct fb_info_sbusfb *fb, struct display *p,
138                          int x_margin, int y_margin)
139 {
140         p->screen_base += (y_margin - fb->y_margin) *
141                 p->line_length + (x_margin - fb->x_margin);
142 }
143
144 static u8 cg3regvals_66hz[] __initdata = {      /* 1152 x 900, 66 Hz */
145         0x14, 0xbb,     0x15, 0x2b,     0x16, 0x04,     0x17, 0x14,
146         0x18, 0xae,     0x19, 0x03,     0x1a, 0xa8,     0x1b, 0x24,
147         0x1c, 0x01,     0x1d, 0x05,     0x1e, 0xff,     0x1f, 0x01,
148         0x10, 0x20,     0
149 };
150
151 static u8 cg3regvals_76hz[] __initdata = {      /* 1152 x 900, 76 Hz */
152         0x14, 0xb7,     0x15, 0x27,     0x16, 0x03,     0x17, 0x0f,
153         0x18, 0xae,     0x19, 0x03,     0x1a, 0xae,     0x1b, 0x2a,
154         0x1c, 0x01,     0x1d, 0x09,     0x1e, 0xff,     0x1f, 0x01,
155         0x10, 0x24,     0
156 };
157
158 static u8 cg3regvals_rdi[] __initdata = {       /* 640 x 480, cgRDI */
159         0x14, 0x70,     0x15, 0x20,     0x16, 0x08,     0x17, 0x10,
160         0x18, 0x06,     0x19, 0x02,     0x1a, 0x31,     0x1b, 0x51,
161         0x1c, 0x06,     0x1d, 0x0c,     0x1e, 0xff,     0x1f, 0x01,
162         0x10, 0x22,     0
163 };
164
165 static u8 *cg3_regvals[] __initdata = {
166         cg3regvals_66hz, cg3regvals_76hz, cg3regvals_rdi
167 };
168
169 static u_char cg3_dacvals[] __initdata = {
170         4, 0xff,        5, 0x00,        6, 0x70,        7, 0x00,        0
171 };
172
173 static char idstring[60] __initdata = { 0 };
174
175 char __init *cgthreefb_init(struct fb_info_sbusfb *fb)
176 {
177         struct fb_fix_screeninfo *fix = &fb->fix;
178         struct display *disp = &fb->disp;
179         struct fbtype *type = &fb->type;
180         struct sbus_dev *sdev = fb->sbdp;
181         unsigned long phys = sdev->reg_addrs[0].phys_addr;
182         int cgRDI = strstr(fb->sbdp->prom_name, "cgRDI") != NULL;
183
184 #ifndef FBCON_HAS_CFB8
185         return NULL;
186 #endif
187
188         if (!fb->s.cg3.regs) {
189                 fb->s.cg3.regs = (struct cg3_regs *)
190                         sbus_ioremap(&sdev->resource[0], CG3_REGS_OFFSET,
191                                      sizeof(struct cg3_regs), "cg3 regs");
192                 if (cgRDI) {
193                         char buffer[40];
194                         char *p;
195                         int ww, hh;
196                 
197                         *buffer = 0;
198                         prom_getstring (fb->prom_node, "params", buffer, sizeof(buffer));
199                         if (*buffer) {
200                                 ww = simple_strtoul (buffer, &p, 10);
201                                 if (ww && *p == 'x') {
202                                         hh = simple_strtoul (p + 1, &p, 10);
203                                         if (hh && *p == '-') {
204                                                 if (type->fb_width != ww || type->fb_height != hh) {
205                                                         type->fb_width = ww;
206                                                         type->fb_height = hh;
207                                                         return SBUSFBINIT_SIZECHANGE;
208                                                 }
209                                         }
210                                 }
211                         }
212                 }
213         }
214
215         strcpy(fb->info.modename, "CGthree");
216         strcpy(fix->id, "CGthree");
217         fix->line_length = fb->var.xres_virtual;
218         fix->accel = FB_ACCEL_SUN_CGTHREE;
219         
220         disp->scrollmode = SCROLL_YREDRAW;
221         if (!disp->screen_base) {
222                 disp->screen_base = (char *)
223                         sbus_ioremap(&sdev->resource[0], CG3_RAM_OFFSET,
224                                      type->fb_size, "cg3 ram");
225         }
226         disp->screen_base += fix->line_length * fb->y_margin + fb->x_margin;
227         fb->dispsw = fbcon_cfb8;
228
229         fb->margins = cg3_margins;
230         fb->loadcmap = cg3_loadcmap;
231         fb->blank = cg3_blank;
232         fb->unblank = cg3_unblank;
233         
234         fb->physbase = phys;
235         fb->mmap_map = cg3_mmap_map;
236         
237 #ifdef __sparc_v9__     
238         sprintf(idstring, "%s at %016lx", cgRDI ? "cgRDI" : "cgthree", phys);
239 #else
240         sprintf(idstring, "%s at %x.%08lx", cgRDI ? "cgRDI" : "cgthree", fb->iospace, phys);
241 #endif
242         
243         if (!prom_getbool(fb->prom_node, "width")) {
244                 /* Ugh, broken PROM didn't initialize us.
245                  * Let's deal with this ourselves.
246                  */
247                 enum cg3_type type;
248                 u8 *p;
249
250                 if (cgRDI)
251                         type = CG3_RDI;
252                 else {
253                         u8 status = sbus_readb(&fb->s.cg3.regs->status), mon;
254                         if ((status & CG3_SR_ID_MASK) == CG3_SR_ID_COLOR) {
255                                 mon = status & CG3_SR_RES_MASK;
256                                 if (mon == CG3_SR_1152_900_76_A ||
257                                     mon == CG3_SR_1152_900_76_B)
258                                         type = CG3_AT_76HZ;
259                                 else
260                                         type = CG3_AT_66HZ;
261                         } else {
262                                 prom_printf("cgthree: can't handle SR %02x\n",
263                                             status);
264                                 prom_halt();
265                                 return NULL; /* fool gcc. */
266                         }
267                 }
268
269                 for (p = cg3_regvals[type]; *p; p += 2) {
270                         u8 *regp = &((u8 *)fb->s.cg3.regs)[p[0]];
271                         sbus_writeb(p[1], regp);
272                 }
273                 for (p = cg3_dacvals; *p; p += 2) {
274                         volatile u8 *regp;
275
276                         regp = (volatile u8 *)&fb->s.cg3.regs->cmap.addr;
277                         sbus_writeb(p[0], regp);
278                         regp = (volatile u8 *)&fb->s.cg3.regs->cmap.control;
279                         sbus_writeb(p[1], regp);
280                 }
281         }
282
283         return idstring;
284 }