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