1 /* $Id: cgsixfb.c,v 1.26 2001/10/16 05:44:44 davem Exp $
2 * cgsixfb.c: CGsix (GX,GXplus) 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) 1996 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 /* Offset of interesting structures in the OBIO space */
28 /*
29 * Brooktree is the video dac and is funny to program on the cg6.
30 * (it's even funnier on the cg3)
31 * The FBC could be the frame buffer control
32 * The FHC could is the frame buffer hardware control.
33 */
34 #define CG6_ROM_OFFSET 0x0UL
35 #define CG6_BROOKTREE_OFFSET 0x200000UL
36 #define CG6_DHC_OFFSET 0x240000UL
37 #define CG6_ALT_OFFSET 0x280000UL
38 #define CG6_FHC_OFFSET 0x300000UL
39 #define CG6_THC_OFFSET 0x301000UL
40 #define CG6_FBC_OFFSET 0x700000UL
41 #define CG6_TEC_OFFSET 0x701000UL
42 #define CG6_RAM_OFFSET 0x800000UL
43
44 /* FHC definitions */
45 #define CG6_FHC_FBID_SHIFT 24
46 #define CG6_FHC_FBID_MASK 255
47 #define CG6_FHC_REV_SHIFT 20
48 #define CG6_FHC_REV_MASK 15
49 #define CG6_FHC_FROP_DISABLE (1 << 19)
50 #define CG6_FHC_ROW_DISABLE (1 << 18)
51 #define CG6_FHC_SRC_DISABLE (1 << 17)
52 #define CG6_FHC_DST_DISABLE (1 << 16)
53 #define CG6_FHC_RESET (1 << 15)
54 #define CG6_FHC_LITTLE_ENDIAN (1 << 13)
55 #define CG6_FHC_RES_MASK (3 << 11)
56 #define CG6_FHC_1024 (0 << 11)
57 #define CG6_FHC_1152 (1 << 11)
58 #define CG6_FHC_1280 (2 << 11)
59 #define CG6_FHC_1600 (3 << 11)
60 #define CG6_FHC_CPU_MASK (3 << 9)
61 #define CG6_FHC_CPU_SPARC (0 << 9)
62 #define CG6_FHC_CPU_68020 (1 << 9)
63 #define CG6_FHC_CPU_386 (2 << 9)
64 #define CG6_FHC_TEST (1 << 8)
65 #define CG6_FHC_TEST_X_SHIFT 4
66 #define CG6_FHC_TEST_X_MASK 15
67 #define CG6_FHC_TEST_Y_SHIFT 0
68 #define CG6_FHC_TEST_Y_MASK 15
69
70 /* FBC mode definitions */
71 #define CG6_FBC_BLIT_IGNORE 0x00000000
72 #define CG6_FBC_BLIT_NOSRC 0x00100000
73 #define CG6_FBC_BLIT_SRC 0x00200000
74 #define CG6_FBC_BLIT_ILLEGAL 0x00300000
75 #define CG6_FBC_BLIT_MASK 0x00300000
76
77 #define CG6_FBC_VBLANK 0x00080000
78
79 #define CG6_FBC_MODE_IGNORE 0x00000000
80 #define CG6_FBC_MODE_COLOR8 0x00020000
81 #define CG6_FBC_MODE_COLOR1 0x00040000
82 #define CG6_FBC_MODE_HRMONO 0x00060000
83 #define CG6_FBC_MODE_MASK 0x00060000
84
85 #define CG6_FBC_DRAW_IGNORE 0x00000000
86 #define CG6_FBC_DRAW_RENDER 0x00008000
87 #define CG6_FBC_DRAW_PICK 0x00010000
88 #define CG6_FBC_DRAW_ILLEGAL 0x00018000
89 #define CG6_FBC_DRAW_MASK 0x00018000
90
91 #define CG6_FBC_BWRITE0_IGNORE 0x00000000
92 #define CG6_FBC_BWRITE0_ENABLE 0x00002000
93 #define CG6_FBC_BWRITE0_DISABLE 0x00004000
94 #define CG6_FBC_BWRITE0_ILLEGAL 0x00006000
95 #define CG6_FBC_BWRITE0_MASK 0x00006000
96
97 #define CG6_FBC_BWRITE1_IGNORE 0x00000000
98 #define CG6_FBC_BWRITE1_ENABLE 0x00000800
99 #define CG6_FBC_BWRITE1_DISABLE 0x00001000
100 #define CG6_FBC_BWRITE1_ILLEGAL 0x00001800
101 #define CG6_FBC_BWRITE1_MASK 0x00001800
102
103 #define CG6_FBC_BREAD_IGNORE 0x00000000
104 #define CG6_FBC_BREAD_0 0x00000200
105 #define CG6_FBC_BREAD_1 0x00000400
106 #define CG6_FBC_BREAD_ILLEGAL 0x00000600
107 #define CG6_FBC_BREAD_MASK 0x00000600
108
109 #define CG6_FBC_BDISP_IGNORE 0x00000000
110 #define CG6_FBC_BDISP_0 0x00000080
111 #define CG6_FBC_BDISP_1 0x00000100
112 #define CG6_FBC_BDISP_ILLEGAL 0x00000180
113 #define CG6_FBC_BDISP_MASK 0x00000180
114
115 #define CG6_FBC_INDEX_MOD 0x00000040
116 #define CG6_FBC_INDEX_MASK 0x00000030
117
118 /* THC definitions */
119 #define CG6_THC_MISC_REV_SHIFT 16
120 #define CG6_THC_MISC_REV_MASK 15
121 #define CG6_THC_MISC_RESET (1 << 12)
122 #define CG6_THC_MISC_VIDEO (1 << 10)
123 #define CG6_THC_MISC_SYNC (1 << 9)
124 #define CG6_THC_MISC_VSYNC (1 << 8)
125 #define CG6_THC_MISC_SYNC_ENAB (1 << 7)
126 #define CG6_THC_MISC_CURS_RES (1 << 6)
127 #define CG6_THC_MISC_INT_ENAB (1 << 5)
128 #define CG6_THC_MISC_INT (1 << 4)
129 #define CG6_THC_MISC_INIT 0x9f
130
131 MODULE_LICENSE("GPL");
132
133 /* The contents are unknown */
134 struct cg6_tec {
135 volatile int tec_matrix;
136 volatile int tec_clip;
137 volatile int tec_vdc;
138 };
139
140 struct cg6_thc {
141 uint thc_pad0[512];
142 volatile uint thc_hs; /* hsync timing */
143 volatile uint thc_hsdvs;
144 volatile uint thc_hd;
145 volatile uint thc_vs; /* vsync timing */
146 volatile uint thc_vd;
147 volatile uint thc_refresh;
148 volatile uint thc_misc;
149 uint thc_pad1[56];
150 volatile uint thc_cursxy; /* cursor x,y position (16 bits each) */
151 volatile uint thc_cursmask[32]; /* cursor mask bits */
152 volatile uint thc_cursbits[32]; /* what to show where mask enabled */
153 };
154
155 struct cg6_fbc {
156 u32 xxx0[1];
157 volatile u32 mode;
158 volatile u32 clip;
159 u32 xxx1[1];
160 volatile u32 s;
161 volatile u32 draw;
162 volatile u32 blit;
163 volatile u32 font;
164 u32 xxx2[24];
165 volatile u32 x0, y0, z0, color0;
166 volatile u32 x1, y1, z1, color1;
167 volatile u32 x2, y2, z2, color2;
168 volatile u32 x3, y3, z3, color3;
169 volatile u32 offx, offy;
170 u32 xxx3[2];
171 volatile u32 incx, incy;
172 u32 xxx4[2];
173 volatile u32 clipminx, clipminy;
174 u32 xxx5[2];
175 volatile u32 clipmaxx, clipmaxy;
176 u32 xxx6[2];
177 volatile u32 fg;
178 volatile u32 bg;
179 volatile u32 alu;
180 volatile u32 pm;
181 volatile u32 pixelm;
182 u32 xxx7[2];
183 volatile u32 patalign;
184 volatile u32 pattern[8];
185 u32 xxx8[432];
186 volatile u32 apointx, apointy, apointz;
187 u32 xxx9[1];
188 volatile u32 rpointx, rpointy, rpointz;
189 u32 xxx10[5];
190 volatile u32 pointr, pointg, pointb, pointa;
191 volatile u32 alinex, aliney, alinez;
192 u32 xxx11[1];
193 volatile u32 rlinex, rliney, rlinez;
194 u32 xxx12[5];
195 volatile u32 liner, lineg, lineb, linea;
196 volatile u32 atrix, atriy, atriz;
197 u32 xxx13[1];
198 volatile u32 rtrix, rtriy, rtriz;
199 u32 xxx14[5];
200 volatile u32 trir, trig, trib, tria;
201 volatile u32 aquadx, aquady, aquadz;
202 u32 xxx15[1];
203 volatile u32 rquadx, rquady, rquadz;
204 u32 xxx16[5];
205 volatile u32 quadr, quadg, quadb, quada;
206 volatile u32 arectx, arecty, arectz;
207 u32 xxx17[1];
208 volatile u32 rrectx, rrecty, rrectz;
209 u32 xxx18[5];
210 volatile u32 rectr, rectg, rectb, recta;
211 };
212
213 static struct sbus_mmap_map cg6_mmap_map[] = {
214 { CG6_FBC, CG6_FBC_OFFSET, PAGE_SIZE },
215 { CG6_TEC, CG6_TEC_OFFSET, PAGE_SIZE },
216 { CG6_BTREGS, CG6_BROOKTREE_OFFSET, PAGE_SIZE },
217 { CG6_FHC, CG6_FHC_OFFSET, PAGE_SIZE },
218 { CG6_THC, CG6_THC_OFFSET, PAGE_SIZE },
219 { CG6_ROM, CG6_ROM_OFFSET, 0x10000 },
220 { CG6_RAM, CG6_RAM_OFFSET, SBUS_MMAP_FBSIZE(1) },
221 { CG6_DHC, CG6_DHC_OFFSET, 0x40000 },
222 { 0, 0, 0 }
223 };
224
cg6_setup(struct display * p)225 static void cg6_setup(struct display *p)
226 {
227 p->next_line = sbusfbinfo(p->fb_info)->var.xres_virtual;
228 p->next_plane = 0;
229 }
230
cg6_clear(struct vc_data * conp,struct display * p,int sy,int sx,int height,int width)231 static void cg6_clear(struct vc_data *conp, struct display *p, int sy, int sx,
232 int height, int width)
233 {
234 struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)p->fb_info;
235 register struct cg6_fbc *fbc = fb->s.cg6.fbc;
236 unsigned long flags;
237 int x, y, w, h;
238 int i;
239
240 spin_lock_irqsave(&fb->lock, flags);
241 do {
242 i = sbus_readl(&fbc->s);
243 } while (i & 0x10000000);
244 sbus_writel(attr_bgcol_ec(p,conp), &fbc->fg);
245 sbus_writel(attr_bgcol_ec(p,conp), &fbc->bg);
246 sbus_writel(~0, &fbc->pixelm);
247 sbus_writel(0xea80ff00, &fbc->alu);
248 sbus_writel(0, &fbc->s);
249 sbus_writel(0, &fbc->clip);
250 sbus_writel(~0, &fbc->pm);
251
252 if (fontheightlog(p)) {
253 y = sy << fontheightlog(p); h = height << fontheightlog(p);
254 } else {
255 y = sy * fontheight(p); h = height * fontheight(p);
256 }
257 if (fontwidthlog(p)) {
258 x = sx << fontwidthlog(p); w = width << fontwidthlog(p);
259 } else {
260 x = sx * fontwidth(p); w = width * fontwidth(p);
261 }
262 sbus_writel(y + fb->y_margin, &fbc->arecty);
263 sbus_writel(x + fb->x_margin, &fbc->arectx);
264 sbus_writel(y + fb->y_margin + h, &fbc->arecty);
265 sbus_writel(x + fb->x_margin + w, &fbc->arectx);
266 do {
267 i = sbus_readl(&fbc->draw);
268 } while (i < 0 && (i & 0x20000000));
269 spin_unlock_irqrestore(&fb->lock, flags);
270 }
271
cg6_fill(struct fb_info_sbusfb * fb,struct display * p,int s,int count,unsigned short * boxes)272 static void cg6_fill(struct fb_info_sbusfb *fb, struct display *p, int s,
273 int count, unsigned short *boxes)
274 {
275 int i;
276 register struct cg6_fbc *fbc = fb->s.cg6.fbc;
277 unsigned long flags;
278
279 spin_lock_irqsave(&fb->lock, flags);
280 do {
281 i = sbus_readl(&fbc->s);
282 } while (i & 0x10000000);
283 sbus_writel(attr_bgcol(p,s), &fbc->fg);
284 sbus_writel(attr_bgcol(p,s), &fbc->bg);
285 sbus_writel(~0, &fbc->pixelm);
286 sbus_writel(0xea80ff00, &fbc->alu);
287 sbus_writel(0, &fbc->s);
288 sbus_writel(0, &fbc->clip);
289 sbus_writel(~0, &fbc->pm);
290 while (count-- > 0) {
291 sbus_writel(boxes[1], &fbc->arecty);
292 sbus_writel(boxes[0], &fbc->arectx);
293 sbus_writel(boxes[3], &fbc->arecty);
294 sbus_writel(boxes[2], &fbc->arectx);
295 boxes += 4;
296 do {
297 i = sbus_readl(&fbc->draw);
298 } while (i < 0 && (i & 0x20000000));
299 }
300 spin_unlock_irqrestore(&fb->lock, flags);
301 }
302
cg6_putc(struct vc_data * conp,struct display * p,int c,int yy,int xx)303 static void cg6_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx)
304 {
305 struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)p->fb_info;
306 register struct cg6_fbc *fbc = fb->s.cg6.fbc;
307 unsigned long flags;
308 int i, x, y;
309 u8 *fd;
310
311 spin_lock_irqsave(&fb->lock, flags);
312 if (fontheightlog(p)) {
313 y = fb->y_margin + (yy << fontheightlog(p));
314 i = ((c & p->charmask) << fontheightlog(p));
315 } else {
316 y = fb->y_margin + (yy * fontheight(p));
317 i = (c & p->charmask) * fontheight(p);
318 }
319 if (fontwidth(p) <= 8)
320 fd = p->fontdata + i;
321 else
322 fd = p->fontdata + (i << 1);
323 if (fontwidthlog(p))
324 x = fb->x_margin + (xx << fontwidthlog(p));
325 else
326 x = fb->x_margin + (xx * fontwidth(p));
327 do {
328 i = sbus_readl(&fbc->s);
329 } while (i & 0x10000000);
330 sbus_writel(attr_fgcol(p,c), &fbc->fg);
331 sbus_writel(attr_bgcol(p,c), &fbc->bg);
332 sbus_writel(0x140000, &fbc->mode);
333 sbus_writel(0xe880fc30, &fbc->alu);
334 sbus_writel(~0, &fbc->pixelm);
335 sbus_writel(0, &fbc->s);
336 sbus_writel(0, &fbc->clip);
337 sbus_writel(0xff, &fbc->pm);
338 sbus_writel(0, &fbc->incx);
339 sbus_writel(1, &fbc->incy);
340 sbus_writel(x, &fbc->x0);
341 sbus_writel(x + fontwidth(p) - 1, &fbc->x1);
342 sbus_writel(y, &fbc->y0);
343 if (fontwidth(p) <= 8) {
344 for (i = 0; i < fontheight(p); i++) {
345 u32 val = *fd++ << 24;
346 sbus_writel(val, &fbc->font);
347 }
348 } else {
349 for (i = 0; i < fontheight(p); i++) {
350 u32 val = *(u16 *)fd << 16;
351
352 sbus_writel(val, &fbc->font);
353 fd += 2;
354 }
355 }
356 spin_unlock_irqrestore(&fb->lock, flags);
357 }
358
cg6_putcs(struct vc_data * conp,struct display * p,const unsigned short * s,int count,int yy,int xx)359 static void cg6_putcs(struct vc_data *conp, struct display *p, const unsigned short *s,
360 int count, int yy, int xx)
361 {
362 struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)p->fb_info;
363 register struct cg6_fbc *fbc = fb->s.cg6.fbc;
364 unsigned long flags;
365 int i, x, y;
366 u8 *fd1, *fd2, *fd3, *fd4;
367 u16 c;
368
369 spin_lock_irqsave(&fb->lock, flags);
370 do {
371 i = sbus_readl(&fbc->s);
372 } while (i & 0x10000000);
373 c = scr_readw(s);
374 sbus_writel(attr_fgcol(p, c), &fbc->fg);
375 sbus_writel(attr_bgcol(p, c), &fbc->bg);
376 sbus_writel(0x140000, &fbc->mode);
377 sbus_writel(0xe880fc30, &fbc->alu);
378 sbus_writel(~0, &fbc->pixelm);
379 sbus_writel(0, &fbc->s);
380 sbus_writel(0, &fbc->clip);
381 sbus_writel(0xff, &fbc->pm);
382 x = fb->x_margin;
383 y = fb->y_margin;
384 if (fontwidthlog(p))
385 x += (xx << fontwidthlog(p));
386 else
387 x += xx * fontwidth(p);
388 if (fontheightlog(p))
389 y += (yy << fontheightlog(p));
390 else
391 y += (yy * fontheight(p));
392 if (fontwidth(p) <= 8) {
393 while (count >= 4) {
394 count -= 4;
395 sbus_writel(0, &fbc->incx);
396 sbus_writel(1, &fbc->incy);
397 sbus_writel(x, &fbc->x0);
398 sbus_writel((x += 4 * fontwidth(p)) - 1, &fbc->x1);
399 sbus_writel(y, &fbc->y0);
400 if (fontheightlog(p)) {
401 fd1 = p->fontdata + ((scr_readw(s++) & p->charmask) << fontheightlog(p));
402 fd2 = p->fontdata + ((scr_readw(s++) & p->charmask) << fontheightlog(p));
403 fd3 = p->fontdata + ((scr_readw(s++) & p->charmask) << fontheightlog(p));
404 fd4 = p->fontdata + ((scr_readw(s++) & p->charmask) << fontheightlog(p));
405 } else {
406 fd1 = p->fontdata + ((scr_readw(s++) & p->charmask) * fontheight(p));
407 fd2 = p->fontdata + ((scr_readw(s++) & p->charmask) * fontheight(p));
408 fd3 = p->fontdata + ((scr_readw(s++) & p->charmask) * fontheight(p));
409 fd4 = p->fontdata + ((scr_readw(s++) & p->charmask) * fontheight(p));
410 }
411 if (fontwidth(p) == 8) {
412 for (i = 0; i < fontheight(p); i++) {
413 u32 val = ((u32)*fd4++) |
414 ((((u32)*fd3++) |
415 ((((u32)*fd2++) |
416 (((u32)*fd1++)
417 << 8)) << 8)) << 8);
418 sbus_writel(val, &fbc->font);
419 }
420 } else {
421 for (i = 0; i < fontheight(p); i++) {
422 u32 val = (((u32)*fd4++) |
423 ((((u32)*fd3++) |
424 ((((u32)*fd2++) |
425 (((u32)*fd1++)
426 << fontwidth(p))) <<
427 fontwidth(p))) <<
428 fontwidth(p))) <<
429 (24 - 3 * fontwidth(p));
430 sbus_writel(val, &fbc->font);
431 }
432 }
433 }
434 } else {
435 while (count >= 2) {
436 count -= 2;
437 sbus_writel(0, &fbc->incx);
438 sbus_writel(1, &fbc->incy);
439 sbus_writel(x, &fbc->x0);
440 sbus_writel((x += 2 * fontwidth(p)) - 1, &fbc->x1);
441 sbus_writel(y, &fbc->y0);
442 if (fontheightlog(p)) {
443 fd1 = p->fontdata + ((scr_readw(s++) & p->charmask) << (fontheightlog(p) + 1));
444 fd2 = p->fontdata + ((scr_readw(s++) & p->charmask) << (fontheightlog(p) + 1));
445 } else {
446 fd1 = p->fontdata + (((scr_readw(s++) & p->charmask) * fontheight(p)) << 1);
447 fd2 = p->fontdata + (((scr_readw(s++) & p->charmask) * fontheight(p)) << 1);
448 }
449 for (i = 0; i < fontheight(p); i++) {
450 u32 val = ((((u32)*(u16 *)fd1) << fontwidth(p)) |
451 ((u32)*(u16 *)fd2)) << (16 - fontwidth(p));
452 sbus_writel(val, &fbc->font);
453 fd1 += 2; fd2 += 2;
454 }
455 }
456 }
457 while (count) {
458 count--;
459 sbus_writel(0, &fbc->incx);
460 sbus_writel(1, &fbc->incy);
461 sbus_writel(x, &fbc->x0);
462 sbus_writel((x += fontwidth(p)) - 1, &fbc->x1);
463 sbus_writel(y, &fbc->y0);
464 if (fontheightlog(p))
465 i = ((scr_readw(s++) & p->charmask) << fontheightlog(p));
466 else
467 i = ((scr_readw(s++) & p->charmask) * fontheight(p));
468 if (fontwidth(p) <= 8) {
469 fd1 = p->fontdata + i;
470 for (i = 0; i < fontheight(p); i++) {
471 u32 val = *fd1++ << 24;
472 sbus_writel(val, &fbc->font);
473 }
474 } else {
475 fd1 = p->fontdata + (i << 1);
476 for (i = 0; i < fontheight(p); i++) {
477 u32 val = *(u16 *)fd1 << 16;
478 sbus_writel(val, &fbc->font);
479 fd1 += 2;
480 }
481 }
482 }
483 spin_unlock_irqrestore(&fb->lock, flags);
484 }
485
cg6_revc(struct display * p,int xx,int yy)486 static void cg6_revc(struct display *p, int xx, int yy)
487 {
488 /* Not used if hw cursor */
489 }
490
cg6_loadcmap(struct fb_info_sbusfb * fb,struct display * p,int index,int count)491 static void cg6_loadcmap (struct fb_info_sbusfb *fb, struct display *p, int index, int count)
492 {
493 struct bt_regs *bt = fb->s.cg6.bt;
494 unsigned long flags;
495 int i;
496
497 spin_lock_irqsave(&fb->lock, flags);
498 sbus_writel(index << 24, &bt->addr);
499 for (i = index; count--; i++){
500 sbus_writel(fb->color_map CM(i,0) << 24,
501 &bt->color_map);
502 sbus_writel(fb->color_map CM(i,1) << 24,
503 &bt->color_map);
504 sbus_writel(fb->color_map CM(i,2) << 24,
505 &bt->color_map);
506 }
507 spin_unlock_irqrestore(&fb->lock, flags);
508 }
509
cg6_restore_palette(struct fb_info_sbusfb * fb)510 static void cg6_restore_palette (struct fb_info_sbusfb *fb)
511 {
512 struct bt_regs *bt = fb->s.cg6.bt;
513 unsigned long flags;
514
515 spin_lock_irqsave(&fb->lock, flags);
516 sbus_writel(0, &bt->addr);
517 sbus_writel(0xffffffff, &bt->color_map);
518 sbus_writel(0xffffffff, &bt->color_map);
519 sbus_writel(0xffffffff, &bt->color_map);
520 spin_unlock_irqrestore(&fb->lock, flags);
521 }
522
523 static struct display_switch cg6_dispsw __initdata = {
524 setup: cg6_setup,
525 bmove: fbcon_redraw_bmove,
526 clear: cg6_clear,
527 putc: cg6_putc,
528 putcs: cg6_putcs,
529 revc: cg6_revc,
530 fontwidthmask: FONTWIDTHRANGE(1,16) /* Allow fontwidths up to 16 */
531 };
532
cg6_setcursormap(struct fb_info_sbusfb * fb,u8 * red,u8 * green,u8 * blue)533 static void cg6_setcursormap (struct fb_info_sbusfb *fb, u8 *red, u8 *green, u8 *blue)
534 {
535 struct bt_regs *bt = fb->s.cg6.bt;
536 unsigned long flags;
537
538 spin_lock_irqsave(&fb->lock, flags);
539 sbus_writel(1 << 24, &bt->addr);
540 sbus_writel(red[0] << 24, &bt->cursor);
541 sbus_writel(green[0] << 24, &bt->cursor);
542 sbus_writel(blue[0] << 24, &bt->cursor);
543 sbus_writel(3 << 24, &bt->addr);
544 sbus_writel(red[1] << 24, &bt->cursor);
545 sbus_writel(green[1] << 24, &bt->cursor);
546 sbus_writel(blue[1] << 24, &bt->cursor);
547 spin_unlock_irqrestore(&fb->lock, flags);
548 }
549
550 /* Set cursor shape */
cg6_setcurshape(struct fb_info_sbusfb * fb)551 static void cg6_setcurshape (struct fb_info_sbusfb *fb)
552 {
553 struct cg6_thc *thc = fb->s.cg6.thc;
554 unsigned long flags;
555 int i;
556
557 spin_lock_irqsave(&fb->lock, flags);
558 for (i = 0; i < 32; i++) {
559 sbus_writel(fb->cursor.bits[0][i],
560 &thc->thc_cursmask [i]);
561 sbus_writel(fb->cursor.bits[1][i],
562 &thc->thc_cursbits [i]);
563 }
564 spin_unlock_irqrestore(&fb->lock, flags);
565 }
566
567 /* Load cursor information */
cg6_setcursor(struct fb_info_sbusfb * fb)568 static void cg6_setcursor (struct fb_info_sbusfb *fb)
569 {
570 unsigned int v;
571 unsigned long flags;
572 struct cg_cursor *c = &fb->cursor;
573
574 spin_lock_irqsave(&fb->lock, flags);
575 if (c->enable)
576 v = ((c->cpos.fbx - c->chot.fbx) << 16)
577 |((c->cpos.fby - c->chot.fby) & 0xffff);
578 else
579 /* Magic constant to turn off the cursor */
580 v = ((65536-32) << 16) | (65536-32);
581 sbus_writel(v, &fb->s.cg6.thc->thc_cursxy);
582 spin_unlock_irqrestore(&fb->lock, flags);
583 }
584
cg6_blank(struct fb_info_sbusfb * fb)585 static void cg6_blank (struct fb_info_sbusfb *fb)
586 {
587 unsigned long flags;
588 u32 tmp;
589
590 spin_lock_irqsave(&fb->lock, flags);
591 tmp = sbus_readl(&fb->s.cg6.thc->thc_misc);
592 tmp &= ~CG6_THC_MISC_VIDEO;
593 sbus_writel(tmp, &fb->s.cg6.thc->thc_misc);
594 spin_unlock_irqrestore(&fb->lock, flags);
595 }
596
cg6_unblank(struct fb_info_sbusfb * fb)597 static void cg6_unblank (struct fb_info_sbusfb *fb)
598 {
599 unsigned long flags;
600 u32 tmp;
601
602 spin_lock_irqsave(&fb->lock, flags);
603 tmp = sbus_readl(&fb->s.cg6.thc->thc_misc);
604 tmp |= CG6_THC_MISC_VIDEO;
605 sbus_writel(tmp, &fb->s.cg6.thc->thc_misc);
606 spin_unlock_irqrestore(&fb->lock, flags);
607 }
608
cg6_reset(struct fb_info_sbusfb * fb)609 static void cg6_reset (struct fb_info_sbusfb *fb)
610 {
611 unsigned int rev, conf;
612 struct cg6_tec *tec = fb->s.cg6.tec;
613 struct cg6_fbc *fbc = fb->s.cg6.fbc;
614 unsigned long flags;
615 u32 mode, tmp;
616 int i;
617
618 spin_lock_irqsave(&fb->lock, flags);
619
620 /* Turn off stuff in the Transform Engine. */
621 sbus_writel(0, &tec->tec_matrix);
622 sbus_writel(0, &tec->tec_clip);
623 sbus_writel(0, &tec->tec_vdc);
624
625 /* Take care of bugs in old revisions. */
626 rev = (sbus_readl(fb->s.cg6.fhc) >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK;
627 if (rev < 5) {
628 conf = (sbus_readl(fb->s.cg6.fhc) & CG6_FHC_RES_MASK) |
629 CG6_FHC_CPU_68020 | CG6_FHC_TEST |
630 (11 << CG6_FHC_TEST_X_SHIFT) |
631 (11 << CG6_FHC_TEST_Y_SHIFT);
632 if (rev < 2)
633 conf |= CG6_FHC_DST_DISABLE;
634 sbus_writel(conf, fb->s.cg6.fhc);
635 }
636
637 /* Set things in the FBC. Bad things appear to happen if we do
638 * back to back store/loads on the mode register, so copy it
639 * out instead. */
640 mode = sbus_readl(&fbc->mode);
641 do {
642 i = sbus_readl(&fbc->s);
643 } while (i & 0x10000000);
644 mode &= ~(CG6_FBC_BLIT_MASK | CG6_FBC_MODE_MASK |
645 CG6_FBC_DRAW_MASK | CG6_FBC_BWRITE0_MASK |
646 CG6_FBC_BWRITE1_MASK | CG6_FBC_BREAD_MASK |
647 CG6_FBC_BDISP_MASK);
648 mode |= (CG6_FBC_BLIT_SRC | CG6_FBC_MODE_COLOR8 |
649 CG6_FBC_DRAW_RENDER | CG6_FBC_BWRITE0_ENABLE |
650 CG6_FBC_BWRITE1_DISABLE | CG6_FBC_BREAD_0 |
651 CG6_FBC_BDISP_0);
652 sbus_writel(mode, &fbc->mode);
653
654 sbus_writel(0, &fbc->clip);
655 sbus_writel(0, &fbc->offx);
656 sbus_writel(0, &fbc->offy);
657 sbus_writel(0, &fbc->clipminx);
658 sbus_writel(0, &fbc->clipminy);
659 sbus_writel(fb->type.fb_width - 1, &fbc->clipmaxx);
660 sbus_writel(fb->type.fb_height - 1, &fbc->clipmaxy);
661
662 /* Enable cursor in Brooktree DAC. */
663 sbus_writel(0x06 << 24, &fb->s.cg6.bt->addr);
664 tmp = sbus_readl(&fb->s.cg6.bt->control);
665 tmp |= 0x03 << 24;
666 sbus_writel(tmp, &fb->s.cg6.bt->control);
667
668 spin_unlock_irqrestore(&fb->lock, flags);
669 }
670
cg6_margins(struct fb_info_sbusfb * fb,struct display * p,int x_margin,int y_margin)671 static void cg6_margins (struct fb_info_sbusfb *fb, struct display *p, int x_margin, int y_margin)
672 {
673 p->screen_base += (y_margin - fb->y_margin) *
674 p->line_length + (x_margin - fb->x_margin);
675 }
676
cg6_rasterimg(struct fb_info * info,int start)677 static int __init cg6_rasterimg (struct fb_info *info, int start)
678 {
679 struct fb_info_sbusfb *fb = sbusfbinfo(info);
680 register struct cg6_fbc *fbc = fb->s.cg6.fbc;
681 int i;
682
683 do {
684 i = sbus_readl(&fbc->s);
685 } while (i & 0x10000000);
686 return 0;
687 }
688
689 static char idstring[70] __initdata = { 0 };
690
cgsixfb_init(struct fb_info_sbusfb * fb)691 char __init *cgsixfb_init(struct fb_info_sbusfb *fb)
692 {
693 struct fb_fix_screeninfo *fix = &fb->fix;
694 struct fb_var_screeninfo *var = &fb->var;
695 struct display *disp = &fb->disp;
696 struct fbtype *type = &fb->type;
697 struct sbus_dev *sdev = fb->sbdp;
698 unsigned long phys = sdev->reg_addrs[0].phys_addr;
699 u32 conf;
700 char *p;
701 char *cardtype;
702 struct bt_regs *bt;
703 struct fb_ops *fbops;
704
705 fbops = kmalloc(sizeof(*fbops), GFP_KERNEL);
706 if (fbops == NULL)
707 return NULL;
708
709 *fbops = *fb->info.fbops;
710 fbops->fb_rasterimg = cg6_rasterimg;
711 fb->info.fbops = fbops;
712
713 if (prom_getbool (fb->prom_node, "dblbuf")) {
714 type->fb_size *= 4;
715 fix->smem_len *= 4;
716 }
717
718 fix->line_length = fb->var.xres_virtual;
719 fix->accel = FB_ACCEL_SUN_CGSIX;
720
721 var->accel_flags = FB_ACCELF_TEXT;
722
723 disp->scrollmode = SCROLL_YREDRAW;
724 if (!disp->screen_base) {
725 disp->screen_base = (char *)
726 sbus_ioremap(&sdev->resource[0], CG6_RAM_OFFSET,
727 type->fb_size, "cgsix ram");
728 }
729 disp->screen_base += fix->line_length * fb->y_margin + fb->x_margin;
730 fb->s.cg6.fbc = (struct cg6_fbc *)
731 sbus_ioremap(&sdev->resource[0], CG6_FBC_OFFSET,
732 4096, "cgsix fbc");
733 fb->s.cg6.tec = (struct cg6_tec *)
734 sbus_ioremap(&sdev->resource[0], CG6_TEC_OFFSET,
735 sizeof(struct cg6_tec), "cgsix tec");
736 fb->s.cg6.thc = (struct cg6_thc *)
737 sbus_ioremap(&sdev->resource[0], CG6_THC_OFFSET,
738 sizeof(struct cg6_thc), "cgsix thc");
739 fb->s.cg6.bt = bt = (struct bt_regs *)
740 sbus_ioremap(&sdev->resource[0], CG6_BROOKTREE_OFFSET,
741 sizeof(struct bt_regs), "cgsix dac");
742 fb->s.cg6.fhc = (u32 *)
743 sbus_ioremap(&sdev->resource[0], CG6_FHC_OFFSET,
744 sizeof(u32), "cgsix fhc");
745 #if 0
746 prom_printf("CG6: RES[%016lx:%016lx:%016lx]\n",
747 sdev->resource[0].start,
748 sdev->resource[0].end,
749 sdev->resource[0].flags);
750 prom_printf("CG6: fbc(%p) tec(%p) thc(%p) bt(%p) fhc(%p)\n",
751 fb->s.cg6.fbc,
752 fb->s.cg6.tec,
753 fb->s.cg6.thc,
754 fb->s.cg6.bt,
755 fb->s.cg6.fhc);
756 prom_halt();
757 #endif
758 fb->dispsw = cg6_dispsw;
759
760 fb->margins = cg6_margins;
761 fb->loadcmap = cg6_loadcmap;
762 fb->setcursor = cg6_setcursor;
763 fb->setcursormap = cg6_setcursormap;
764 fb->setcurshape = cg6_setcurshape;
765 fb->restore_palette = cg6_restore_palette;
766 fb->fill = cg6_fill;
767 fb->blank = cg6_blank;
768 fb->unblank = cg6_unblank;
769 fb->reset = cg6_reset;
770
771 fb->physbase = phys;
772 fb->mmap_map = cg6_mmap_map;
773
774 /* Initialize Brooktree DAC */
775 sbus_writel(0x04 << 24, &bt->addr); /* color planes */
776 sbus_writel(0xff << 24, &bt->control);
777 sbus_writel(0x05 << 24, &bt->addr);
778 sbus_writel(0x00 << 24, &bt->control);
779 sbus_writel(0x06 << 24, &bt->addr); /* overlay plane */
780 sbus_writel(0x73 << 24, &bt->control);
781 sbus_writel(0x07 << 24, &bt->addr);
782 sbus_writel(0x00 << 24, &bt->control);
783
784 conf = sbus_readl(fb->s.cg6.fhc);
785 switch(conf & CG6_FHC_CPU_MASK) {
786 case CG6_FHC_CPU_SPARC: p = "sparc"; break;
787 case CG6_FHC_CPU_68020: p = "68020"; break;
788 default: p = "i386"; break;
789 }
790
791 if (((conf >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK) >= 11) {
792 if (fix->smem_len <= 0x100000) {
793 cardtype = "TGX";
794 } else {
795 cardtype = "TGX+";
796 }
797 } else {
798 if (fix->smem_len <= 0x100000) {
799 cardtype = "GX";
800 } else {
801 cardtype = "GX+";
802 }
803 }
804
805 sprintf(idstring,
806 #ifdef __sparc_v9__
807 "cgsix at %016lx TEC Rev %x CPU %s Rev %x [%s]", phys,
808 #else
809 "cgsix at %x.%08lx TEC Rev %x CPU %s Rev %x [%s]",
810 fb->iospace, phys,
811 #endif
812 ((sbus_readl(&fb->s.cg6.thc->thc_misc) >> CG6_THC_MISC_REV_SHIFT) &
813 CG6_THC_MISC_REV_MASK),
814 p, (conf >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK, cardtype);
815
816 sprintf(fb->info.modename, "CGsix [%s]", cardtype);
817 sprintf(fix->id, "CGsix [%s]", cardtype);
818
819 cg6_reset(fb);
820
821 return idstring;
822 }
823