1/* $Id: cgsixfb.c,v 1.1.1.1 2008/10/15 03:27:04 james26_jang 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
131MODULE_LICENSE("GPL");
132
133/* The contents are unknown */
134struct cg6_tec {
135	volatile int tec_matrix;
136	volatile int tec_clip;
137	volatile int tec_vdc;
138};
139
140struct 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
155struct 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
213static 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
225static 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
231static 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
272static 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
303static 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
359static 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
486static void cg6_revc(struct display *p, int xx, int yy)
487{
488	/* Not used if hw cursor */
489}
490
491static 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
510static 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
523static 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
533static 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 */
551static 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 */
568static 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
585static 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
597static 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
609static 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
671static 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
677static 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
689static char idstring[70] __initdata = { 0 };
690
691char __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	fb->dispsw = cg6_dispsw;
746
747	fb->margins = cg6_margins;
748	fb->loadcmap = cg6_loadcmap;
749	fb->setcursor = cg6_setcursor;
750	fb->setcursormap = cg6_setcursormap;
751	fb->setcurshape = cg6_setcurshape;
752	fb->restore_palette = cg6_restore_palette;
753	fb->fill = cg6_fill;
754	fb->blank = cg6_blank;
755	fb->unblank = cg6_unblank;
756	fb->reset = cg6_reset;
757
758	fb->physbase = phys;
759	fb->mmap_map = cg6_mmap_map;
760
761	/* Initialize Brooktree DAC */
762	sbus_writel(0x04 << 24, &bt->addr);         /* color planes */
763	sbus_writel(0xff << 24, &bt->control);
764	sbus_writel(0x05 << 24, &bt->addr);
765	sbus_writel(0x00 << 24, &bt->control);
766	sbus_writel(0x06 << 24, &bt->addr);         /* overlay plane */
767	sbus_writel(0x73 << 24, &bt->control);
768	sbus_writel(0x07 << 24, &bt->addr);
769	sbus_writel(0x00 << 24, &bt->control);
770
771	conf = sbus_readl(fb->s.cg6.fhc);
772	switch(conf & CG6_FHC_CPU_MASK) {
773	case CG6_FHC_CPU_SPARC: p = "sparc"; break;
774	case CG6_FHC_CPU_68020: p = "68020"; break;
775	default: p = "i386"; break;
776	}
777
778	if (((conf >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK) >= 11) {
779		if (fix->smem_len <= 0x100000) {
780			cardtype = "TGX";
781		} else {
782			cardtype = "TGX+";
783		}
784	} else {
785		if (fix->smem_len <= 0x100000) {
786			cardtype = "GX";
787		} else {
788			cardtype = "GX+";
789		}
790	}
791
792	sprintf(idstring,
793#ifdef __sparc_v9__
794		    "cgsix at %016lx TEC Rev %x CPU %s Rev %x [%s]", phys,
795#else
796		    "cgsix at %x.%08lx TEC Rev %x CPU %s Rev %x [%s]",
797		    fb->iospace, phys,
798#endif
799		    ((sbus_readl(&fb->s.cg6.thc->thc_misc) >> CG6_THC_MISC_REV_SHIFT) &
800		     CG6_THC_MISC_REV_MASK),
801		    p, (conf >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK, cardtype);
802
803	sprintf(fb->info.modename, "CGsix [%s]", cardtype);
804	sprintf(fix->id, "CGsix [%s]", cardtype);
805
806	cg6_reset(fb);
807
808	return idstring;
809}
810