1/* cg6.c: CGSIX (GX, GXplus, TGX) frame buffer driver
2 *
3 * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net)
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 * Driver layout based loosely on tgafb.c, see that file for credits.
9 */
10
11#include <linux/module.h>
12#include <linux/kernel.h>
13#include <linux/errno.h>
14#include <linux/string.h>
15#include <linux/slab.h>
16#include <linux/delay.h>
17#include <linux/init.h>
18#include <linux/fb.h>
19#include <linux/mm.h>
20
21#include <asm/io.h>
22#include <asm/prom.h>
23#include <asm/of_device.h>
24#include <asm/fbio.h>
25
26#include "sbuslib.h"
27
28/*
29 * Local functions.
30 */
31
32static int cg6_setcolreg(unsigned, unsigned, unsigned, unsigned,
33			 unsigned, struct fb_info *);
34static int cg6_blank(int, struct fb_info *);
35
36static void cg6_imageblit(struct fb_info *, const struct fb_image *);
37static void cg6_fillrect(struct fb_info *, const struct fb_fillrect *);
38static int cg6_sync(struct fb_info *);
39static int cg6_mmap(struct fb_info *, struct vm_area_struct *);
40static int cg6_ioctl(struct fb_info *, unsigned int, unsigned long);
41
42/*
43 *  Frame buffer operations
44 */
45
46static struct fb_ops cg6_ops = {
47	.owner			= THIS_MODULE,
48	.fb_setcolreg		= cg6_setcolreg,
49	.fb_blank		= cg6_blank,
50	.fb_fillrect		= cg6_fillrect,
51	.fb_copyarea		= cfb_copyarea,
52	.fb_imageblit		= cg6_imageblit,
53	.fb_sync		= cg6_sync,
54	.fb_mmap		= cg6_mmap,
55	.fb_ioctl		= cg6_ioctl,
56#ifdef CONFIG_COMPAT
57	.fb_compat_ioctl	= sbusfb_compat_ioctl,
58#endif
59};
60
61/* Offset of interesting structures in the OBIO space */
62/*
63 * Brooktree is the video dac and is funny to program on the cg6.
64 * (it's even funnier on the cg3)
65 * The FBC could be the frame buffer control
66 * The FHC could is the frame buffer hardware control.
67 */
68#define CG6_ROM_OFFSET       0x0UL
69#define CG6_BROOKTREE_OFFSET 0x200000UL
70#define CG6_DHC_OFFSET       0x240000UL
71#define CG6_ALT_OFFSET       0x280000UL
72#define CG6_FHC_OFFSET       0x300000UL
73#define CG6_THC_OFFSET       0x301000UL
74#define CG6_FBC_OFFSET       0x700000UL
75#define CG6_TEC_OFFSET       0x701000UL
76#define CG6_RAM_OFFSET       0x800000UL
77
78/* FHC definitions */
79#define CG6_FHC_FBID_SHIFT           24
80#define CG6_FHC_FBID_MASK            255
81#define CG6_FHC_REV_SHIFT            20
82#define CG6_FHC_REV_MASK             15
83#define CG6_FHC_FROP_DISABLE         (1 << 19)
84#define CG6_FHC_ROW_DISABLE          (1 << 18)
85#define CG6_FHC_SRC_DISABLE          (1 << 17)
86#define CG6_FHC_DST_DISABLE          (1 << 16)
87#define CG6_FHC_RESET                (1 << 15)
88#define CG6_FHC_LITTLE_ENDIAN        (1 << 13)
89#define CG6_FHC_RES_MASK             (3 << 11)
90#define CG6_FHC_1024                 (0 << 11)
91#define CG6_FHC_1152                 (1 << 11)
92#define CG6_FHC_1280                 (2 << 11)
93#define CG6_FHC_1600                 (3 << 11)
94#define CG6_FHC_CPU_MASK             (3 << 9)
95#define CG6_FHC_CPU_SPARC            (0 << 9)
96#define CG6_FHC_CPU_68020            (1 << 9)
97#define CG6_FHC_CPU_386              (2 << 9)
98#define CG6_FHC_TEST		     (1 << 8)
99#define CG6_FHC_TEST_X_SHIFT	     4
100#define CG6_FHC_TEST_X_MASK	     15
101#define CG6_FHC_TEST_Y_SHIFT	     0
102#define CG6_FHC_TEST_Y_MASK	     15
103
104/* FBC mode definitions */
105#define CG6_FBC_BLIT_IGNORE		0x00000000
106#define CG6_FBC_BLIT_NOSRC		0x00100000
107#define CG6_FBC_BLIT_SRC		0x00200000
108#define CG6_FBC_BLIT_ILLEGAL		0x00300000
109#define CG6_FBC_BLIT_MASK		0x00300000
110
111#define CG6_FBC_VBLANK			0x00080000
112
113#define CG6_FBC_MODE_IGNORE		0x00000000
114#define CG6_FBC_MODE_COLOR8		0x00020000
115#define CG6_FBC_MODE_COLOR1		0x00040000
116#define CG6_FBC_MODE_HRMONO		0x00060000
117#define CG6_FBC_MODE_MASK		0x00060000
118
119#define CG6_FBC_DRAW_IGNORE		0x00000000
120#define CG6_FBC_DRAW_RENDER		0x00008000
121#define CG6_FBC_DRAW_PICK		0x00010000
122#define CG6_FBC_DRAW_ILLEGAL		0x00018000
123#define CG6_FBC_DRAW_MASK		0x00018000
124
125#define CG6_FBC_BWRITE0_IGNORE		0x00000000
126#define CG6_FBC_BWRITE0_ENABLE		0x00002000
127#define CG6_FBC_BWRITE0_DISABLE		0x00004000
128#define CG6_FBC_BWRITE0_ILLEGAL		0x00006000
129#define CG6_FBC_BWRITE0_MASK		0x00006000
130
131#define CG6_FBC_BWRITE1_IGNORE		0x00000000
132#define CG6_FBC_BWRITE1_ENABLE		0x00000800
133#define CG6_FBC_BWRITE1_DISABLE		0x00001000
134#define CG6_FBC_BWRITE1_ILLEGAL		0x00001800
135#define CG6_FBC_BWRITE1_MASK		0x00001800
136
137#define CG6_FBC_BREAD_IGNORE		0x00000000
138#define CG6_FBC_BREAD_0			0x00000200
139#define CG6_FBC_BREAD_1			0x00000400
140#define CG6_FBC_BREAD_ILLEGAL		0x00000600
141#define CG6_FBC_BREAD_MASK		0x00000600
142
143#define CG6_FBC_BDISP_IGNORE		0x00000000
144#define CG6_FBC_BDISP_0			0x00000080
145#define CG6_FBC_BDISP_1			0x00000100
146#define CG6_FBC_BDISP_ILLEGAL		0x00000180
147#define CG6_FBC_BDISP_MASK		0x00000180
148
149#define CG6_FBC_INDEX_MOD		0x00000040
150#define CG6_FBC_INDEX_MASK		0x00000030
151
152/* THC definitions */
153#define CG6_THC_MISC_REV_SHIFT       16
154#define CG6_THC_MISC_REV_MASK        15
155#define CG6_THC_MISC_RESET           (1 << 12)
156#define CG6_THC_MISC_VIDEO           (1 << 10)
157#define CG6_THC_MISC_SYNC            (1 << 9)
158#define CG6_THC_MISC_VSYNC           (1 << 8)
159#define CG6_THC_MISC_SYNC_ENAB       (1 << 7)
160#define CG6_THC_MISC_CURS_RES        (1 << 6)
161#define CG6_THC_MISC_INT_ENAB        (1 << 5)
162#define CG6_THC_MISC_INT             (1 << 4)
163#define CG6_THC_MISC_INIT            0x9f
164
165/* The contents are unknown */
166struct cg6_tec {
167	int tec_matrix;
168	int tec_clip;
169	int tec_vdc;
170};
171
172struct cg6_thc {
173        u32 thc_pad0[512];
174	u32 thc_hs;		/* hsync timing */
175	u32 thc_hsdvs;
176	u32 thc_hd;
177	u32 thc_vs;		/* vsync timing */
178	u32 thc_vd;
179	u32 thc_refresh;
180	u32 thc_misc;
181	u32 thc_pad1[56];
182	u32 thc_cursxy;	/* cursor x,y position (16 bits each) */
183	u32 thc_cursmask[32];	/* cursor mask bits */
184	u32 thc_cursbits[32];	/* what to show where mask enabled */
185};
186
187struct cg6_fbc {
188	u32	xxx0[1];
189	u32	mode;
190	u32	clip;
191	u32	xxx1[1];
192	u32	s;
193	u32	draw;
194	u32	blit;
195	u32	font;
196	u32	xxx2[24];
197	u32	x0, y0, z0, color0;
198	u32	x1, y1, z1, color1;
199	u32	x2, y2, z2, color2;
200	u32	x3, y3, z3, color3;
201	u32	offx, offy;
202	u32	xxx3[2];
203	u32	incx, incy;
204	u32	xxx4[2];
205	u32	clipminx, clipminy;
206	u32	xxx5[2];
207	u32	clipmaxx, clipmaxy;
208	u32	xxx6[2];
209	u32	fg;
210	u32	bg;
211	u32	alu;
212	u32	pm;
213	u32	pixelm;
214	u32	xxx7[2];
215	u32	patalign;
216	u32	pattern[8];
217	u32	xxx8[432];
218	u32	apointx, apointy, apointz;
219	u32	xxx9[1];
220	u32	rpointx, rpointy, rpointz;
221	u32	xxx10[5];
222	u32	pointr, pointg, pointb, pointa;
223	u32	alinex, aliney, alinez;
224	u32	xxx11[1];
225	u32	rlinex, rliney, rlinez;
226	u32	xxx12[5];
227	u32	liner, lineg, lineb, linea;
228	u32	atrix, atriy, atriz;
229	u32	xxx13[1];
230	u32	rtrix, rtriy, rtriz;
231	u32	xxx14[5];
232	u32	trir, trig, trib, tria;
233	u32	aquadx, aquady, aquadz;
234	u32	xxx15[1];
235	u32	rquadx, rquady, rquadz;
236	u32	xxx16[5];
237	u32	quadr, quadg, quadb, quada;
238	u32	arectx, arecty, arectz;
239	u32	xxx17[1];
240	u32	rrectx, rrecty, rrectz;
241	u32	xxx18[5];
242	u32	rectr, rectg, rectb, recta;
243};
244
245struct bt_regs {
246	u32 addr;
247	u32 color_map;
248	u32 control;
249	u32 cursor;
250};
251
252struct cg6_par {
253	spinlock_t		lock;
254	struct bt_regs		__iomem *bt;
255	struct cg6_fbc		__iomem *fbc;
256	struct cg6_thc		__iomem *thc;
257	struct cg6_tec		__iomem *tec;
258	u32			__iomem *fhc;
259
260	u32			flags;
261#define CG6_FLAG_BLANKED	0x00000001
262
263	unsigned long		physbase;
264	unsigned long		which_io;
265	unsigned long		fbsize;
266};
267
268static int cg6_sync(struct fb_info *info)
269{
270	struct cg6_par *par = (struct cg6_par *) info->par;
271	struct cg6_fbc __iomem *fbc = par->fbc;
272	int limit = 10000;
273
274	do {
275		if (!(sbus_readl(&fbc->s) & 0x10000000))
276			break;
277		udelay(10);
278	} while (--limit > 0);
279
280	return 0;
281}
282
283/**
284 *      cg6_fillrect - REQUIRED function. Can use generic routines if
285 *                     non acclerated hardware and packed pixel based.
286 *                     Draws a rectangle on the screen.
287 *
288 *      @info: frame buffer structure that represents a single frame buffer
289 *      @rect: structure defining the rectagle and operation.
290 */
291static void cg6_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
292{
293	struct cg6_par *par = (struct cg6_par *) info->par;
294	struct cg6_fbc __iomem *fbc = par->fbc;
295	unsigned long flags;
296	s32 val;
297
298
299	spin_lock_irqsave(&par->lock, flags);
300	cg6_sync(info);
301	sbus_writel(rect->color, &fbc->fg);
302	sbus_writel(~(u32)0, &fbc->pixelm);
303	sbus_writel(0xea80ff00, &fbc->alu);
304	sbus_writel(0, &fbc->s);
305	sbus_writel(0, &fbc->clip);
306	sbus_writel(~(u32)0, &fbc->pm);
307	sbus_writel(rect->dy, &fbc->arecty);
308	sbus_writel(rect->dx, &fbc->arectx);
309	sbus_writel(rect->dy + rect->height, &fbc->arecty);
310	sbus_writel(rect->dx + rect->width, &fbc->arectx);
311	do {
312		val = sbus_readl(&fbc->draw);
313	} while (val < 0 && (val & 0x20000000));
314	spin_unlock_irqrestore(&par->lock, flags);
315}
316
317/**
318 *      cg6_imageblit - REQUIRED function. Can use generic routines if
319 *                      non acclerated hardware and packed pixel based.
320 *                      Copies a image from system memory to the screen.
321 *
322 *      @info: frame buffer structure that represents a single frame buffer
323 *      @image: structure defining the image.
324 */
325static void cg6_imageblit(struct fb_info *info, const struct fb_image *image)
326{
327	struct cg6_par *par = (struct cg6_par *) info->par;
328	struct cg6_fbc __iomem *fbc = par->fbc;
329	const u8 *data = image->data;
330	unsigned long flags;
331	u32 x, y;
332	int i, width;
333
334	if (image->depth > 1) {
335		cfb_imageblit(info, image);
336		return;
337	}
338
339	spin_lock_irqsave(&par->lock, flags);
340
341	cg6_sync(info);
342
343	sbus_writel(image->fg_color, &fbc->fg);
344	sbus_writel(image->bg_color, &fbc->bg);
345	sbus_writel(0x140000, &fbc->mode);
346	sbus_writel(0xe880fc30, &fbc->alu);
347	sbus_writel(~(u32)0, &fbc->pixelm);
348	sbus_writel(0, &fbc->s);
349	sbus_writel(0, &fbc->clip);
350	sbus_writel(0xff, &fbc->pm);
351	sbus_writel(32, &fbc->incx);
352	sbus_writel(0, &fbc->incy);
353
354	x = image->dx;
355	y = image->dy;
356	for (i = 0; i < image->height; i++) {
357		width = image->width;
358
359		while (width >= 32) {
360			u32 val;
361
362			sbus_writel(y, &fbc->y0);
363			sbus_writel(x, &fbc->x0);
364			sbus_writel(x + 32 - 1, &fbc->x1);
365
366			val = ((u32)data[0] << 24) |
367			      ((u32)data[1] << 16) |
368			      ((u32)data[2] <<  8) |
369			      ((u32)data[3] <<  0);
370			sbus_writel(val, &fbc->font);
371
372			data += 4;
373			x += 32;
374			width -= 32;
375		}
376		if (width) {
377			u32 val;
378
379			sbus_writel(y, &fbc->y0);
380			sbus_writel(x, &fbc->x0);
381			sbus_writel(x + width - 1, &fbc->x1);
382			if (width <= 8) {
383				val = (u32) data[0] << 24;
384				data += 1;
385			} else if (width <= 16) {
386				val = ((u32) data[0] << 24) |
387				      ((u32) data[1] << 16);
388				data += 2;
389			} else {
390				val = ((u32) data[0] << 24) |
391				      ((u32) data[1] << 16) |
392				      ((u32) data[2] <<  8);
393				data += 3;
394			}
395			sbus_writel(val, &fbc->font);
396		}
397
398		y += 1;
399		x = image->dx;
400	}
401
402	spin_unlock_irqrestore(&par->lock, flags);
403}
404
405/**
406 *      cg6_setcolreg - Optional function. Sets a color register.
407 *      @regno: boolean, 0 copy local, 1 get_user() function
408 *      @red: frame buffer colormap structure
409 *      @green: The green value which can be up to 16 bits wide
410 *      @blue:  The blue value which can be up to 16 bits wide.
411 *      @transp: If supported the alpha value which can be up to 16 bits wide.
412 *      @info: frame buffer info structure
413 */
414static int cg6_setcolreg(unsigned regno,
415			 unsigned red, unsigned green, unsigned blue,
416			 unsigned transp, struct fb_info *info)
417{
418	struct cg6_par *par = (struct cg6_par *) info->par;
419	struct bt_regs __iomem *bt = par->bt;
420	unsigned long flags;
421
422	if (regno >= 256)
423		return 1;
424
425	red >>= 8;
426	green >>= 8;
427	blue >>= 8;
428
429	spin_lock_irqsave(&par->lock, flags);
430
431	sbus_writel((u32)regno << 24, &bt->addr);
432	sbus_writel((u32)red << 24, &bt->color_map);
433	sbus_writel((u32)green << 24, &bt->color_map);
434	sbus_writel((u32)blue << 24, &bt->color_map);
435
436	spin_unlock_irqrestore(&par->lock, flags);
437
438	return 0;
439}
440
441/**
442 *      cg6_blank - Optional function.  Blanks the display.
443 *      @blank_mode: the blank mode we want.
444 *      @info: frame buffer structure that represents a single frame buffer
445 */
446static int
447cg6_blank(int blank, struct fb_info *info)
448{
449	struct cg6_par *par = (struct cg6_par *) info->par;
450	struct cg6_thc __iomem *thc = par->thc;
451	unsigned long flags;
452	u32 val;
453
454	spin_lock_irqsave(&par->lock, flags);
455
456	switch (blank) {
457	case FB_BLANK_UNBLANK: /* Unblanking */
458		val = sbus_readl(&thc->thc_misc);
459		val |= CG6_THC_MISC_VIDEO;
460		sbus_writel(val, &thc->thc_misc);
461		par->flags &= ~CG6_FLAG_BLANKED;
462		break;
463
464	case FB_BLANK_NORMAL: /* Normal blanking */
465	case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
466	case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
467	case FB_BLANK_POWERDOWN: /* Poweroff */
468		val = sbus_readl(&thc->thc_misc);
469		val &= ~CG6_THC_MISC_VIDEO;
470		sbus_writel(val, &thc->thc_misc);
471		par->flags |= CG6_FLAG_BLANKED;
472		break;
473	}
474
475	spin_unlock_irqrestore(&par->lock, flags);
476
477	return 0;
478}
479
480static struct sbus_mmap_map cg6_mmap_map[] = {
481	{
482		.voff	= CG6_FBC,
483		.poff	= CG6_FBC_OFFSET,
484		.size	= PAGE_SIZE
485	},
486	{
487		.voff	= CG6_TEC,
488		.poff	= CG6_TEC_OFFSET,
489		.size	= PAGE_SIZE
490	},
491	{
492		.voff	= CG6_BTREGS,
493		.poff	= CG6_BROOKTREE_OFFSET,
494		.size	= PAGE_SIZE
495	},
496	{
497		.voff	= CG6_FHC,
498		.poff	= CG6_FHC_OFFSET,
499		.size	= PAGE_SIZE
500	},
501	{
502		.voff	= CG6_THC,
503		.poff	= CG6_THC_OFFSET,
504		.size	= PAGE_SIZE
505	},
506	{
507		.voff	= CG6_ROM,
508		.poff	= CG6_ROM_OFFSET,
509		.size	= 0x10000
510	},
511	{
512		.voff	= CG6_RAM,
513		.poff	= CG6_RAM_OFFSET,
514		.size	= SBUS_MMAP_FBSIZE(1)
515	},
516	{
517		.voff	= CG6_DHC,
518		.poff	= CG6_DHC_OFFSET,
519		.size	= 0x40000
520	},
521	{ .size	= 0 }
522};
523
524static int cg6_mmap(struct fb_info *info, struct vm_area_struct *vma)
525{
526	struct cg6_par *par = (struct cg6_par *)info->par;
527
528	return sbusfb_mmap_helper(cg6_mmap_map,
529				  par->physbase, par->fbsize,
530				  par->which_io, vma);
531}
532
533static int cg6_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
534{
535	struct cg6_par *par = (struct cg6_par *) info->par;
536
537	return sbusfb_ioctl_helper(cmd, arg, info,
538				   FBTYPE_SUNFAST_COLOR, 8, par->fbsize);
539}
540
541/*
542 *  Initialisation
543 */
544
545static void
546cg6_init_fix(struct fb_info *info, int linebytes)
547{
548	struct cg6_par *par = (struct cg6_par *)info->par;
549	const char *cg6_cpu_name, *cg6_card_name;
550	u32 conf;
551
552	conf = sbus_readl(par->fhc);
553	switch(conf & CG6_FHC_CPU_MASK) {
554	case CG6_FHC_CPU_SPARC:
555		cg6_cpu_name = "sparc";
556		break;
557	case CG6_FHC_CPU_68020:
558		cg6_cpu_name = "68020";
559		break;
560	default:
561		cg6_cpu_name = "i386";
562		break;
563	};
564	if (((conf >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK) >= 11) {
565		if (par->fbsize <= 0x100000) {
566			cg6_card_name = "TGX";
567		} else {
568			cg6_card_name = "TGX+";
569		}
570	} else {
571		if (par->fbsize <= 0x100000) {
572			cg6_card_name = "GX";
573		} else {
574			cg6_card_name = "GX+";
575		}
576	}
577
578	sprintf(info->fix.id, "%s %s", cg6_card_name, cg6_cpu_name);
579	info->fix.id[sizeof(info->fix.id)-1] = 0;
580
581	info->fix.type = FB_TYPE_PACKED_PIXELS;
582	info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
583
584	info->fix.line_length = linebytes;
585
586	info->fix.accel = FB_ACCEL_SUN_CGSIX;
587}
588
589/* Initialize Brooktree DAC */
590static void cg6_bt_init(struct cg6_par *par)
591{
592	struct bt_regs __iomem *bt = par->bt;
593
594	sbus_writel(0x04 << 24, &bt->addr);         /* color planes */
595	sbus_writel(0xff << 24, &bt->control);
596	sbus_writel(0x05 << 24, &bt->addr);
597	sbus_writel(0x00 << 24, &bt->control);
598	sbus_writel(0x06 << 24, &bt->addr);         /* overlay plane */
599	sbus_writel(0x73 << 24, &bt->control);
600	sbus_writel(0x07 << 24, &bt->addr);
601	sbus_writel(0x00 << 24, &bt->control);
602}
603
604static void cg6_chip_init(struct fb_info *info)
605{
606	struct cg6_par *par = (struct cg6_par *) info->par;
607	struct cg6_tec __iomem *tec = par->tec;
608	struct cg6_fbc __iomem *fbc = par->fbc;
609	u32 rev, conf, mode;
610	int i;
611
612	/* Turn off stuff in the Transform Engine. */
613	sbus_writel(0, &tec->tec_matrix);
614	sbus_writel(0, &tec->tec_clip);
615	sbus_writel(0, &tec->tec_vdc);
616
617	/* Take care of bugs in old revisions. */
618	rev = (sbus_readl(par->fhc) >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK;
619	if (rev < 5) {
620		conf = (sbus_readl(par->fhc) & CG6_FHC_RES_MASK) |
621			CG6_FHC_CPU_68020 | CG6_FHC_TEST |
622			(11 << CG6_FHC_TEST_X_SHIFT) |
623			(11 << CG6_FHC_TEST_Y_SHIFT);
624		if (rev < 2)
625			conf |= CG6_FHC_DST_DISABLE;
626		sbus_writel(conf, par->fhc);
627	}
628
629	/* Set things in the FBC. Bad things appear to happen if we do
630	 * back to back store/loads on the mode register, so copy it
631	 * out instead. */
632	mode = sbus_readl(&fbc->mode);
633	do {
634		i = sbus_readl(&fbc->s);
635	} while (i & 0x10000000);
636	mode &= ~(CG6_FBC_BLIT_MASK | CG6_FBC_MODE_MASK |
637		       CG6_FBC_DRAW_MASK | CG6_FBC_BWRITE0_MASK |
638		       CG6_FBC_BWRITE1_MASK | CG6_FBC_BREAD_MASK |
639		       CG6_FBC_BDISP_MASK);
640	mode |= (CG6_FBC_BLIT_SRC | CG6_FBC_MODE_COLOR8 |
641		      CG6_FBC_DRAW_RENDER | CG6_FBC_BWRITE0_ENABLE |
642		      CG6_FBC_BWRITE1_DISABLE | CG6_FBC_BREAD_0 |
643		      CG6_FBC_BDISP_0);
644	sbus_writel(mode, &fbc->mode);
645
646	sbus_writel(0, &fbc->clip);
647	sbus_writel(0, &fbc->offx);
648	sbus_writel(0, &fbc->offy);
649	sbus_writel(0, &fbc->clipminx);
650	sbus_writel(0, &fbc->clipminy);
651	sbus_writel(info->var.xres - 1, &fbc->clipmaxx);
652	sbus_writel(info->var.yres - 1, &fbc->clipmaxy);
653}
654
655struct all_info {
656	struct fb_info info;
657	struct cg6_par par;
658};
659
660static void cg6_unmap_regs(struct of_device *op, struct all_info *all)
661{
662	if (all->par.fbc)
663		of_iounmap(&op->resource[0], all->par.fbc, 4096);
664	if (all->par.tec)
665		of_iounmap(&op->resource[0],
666			   all->par.tec, sizeof(struct cg6_tec));
667	if (all->par.thc)
668		of_iounmap(&op->resource[0],
669			   all->par.thc, sizeof(struct cg6_thc));
670	if (all->par.bt)
671		of_iounmap(&op->resource[0],
672			   all->par.bt, sizeof(struct bt_regs));
673	if (all->par.fhc)
674		of_iounmap(&op->resource[0],
675			   all->par.fhc, sizeof(u32));
676
677	if (all->info.screen_base)
678		of_iounmap(&op->resource[0],
679			   all->info.screen_base, all->par.fbsize);
680}
681
682static int __devinit cg6_init_one(struct of_device *op)
683{
684	struct device_node *dp = op->node;
685	struct all_info *all;
686	int linebytes, err;
687
688	all = kzalloc(sizeof(*all), GFP_KERNEL);
689	if (!all)
690		return -ENOMEM;
691
692	spin_lock_init(&all->par.lock);
693
694	all->par.physbase = op->resource[0].start;
695	all->par.which_io = op->resource[0].flags & IORESOURCE_BITS;
696
697	sbusfb_fill_var(&all->info.var, dp->node, 8);
698	all->info.var.red.length = 8;
699	all->info.var.green.length = 8;
700	all->info.var.blue.length = 8;
701
702	linebytes = of_getintprop_default(dp, "linebytes",
703					  all->info.var.xres);
704	all->par.fbsize = PAGE_ALIGN(linebytes * all->info.var.yres);
705	if (of_find_property(dp, "dblbuf", NULL))
706		all->par.fbsize *= 4;
707
708	all->par.fbc = of_ioremap(&op->resource[0], CG6_FBC_OFFSET,
709				  4096, "cgsix fbc");
710	all->par.tec = of_ioremap(&op->resource[0], CG6_TEC_OFFSET,
711				  sizeof(struct cg6_tec), "cgsix tec");
712	all->par.thc = of_ioremap(&op->resource[0], CG6_THC_OFFSET,
713				  sizeof(struct cg6_thc), "cgsix thc");
714	all->par.bt = of_ioremap(&op->resource[0], CG6_BROOKTREE_OFFSET,
715				 sizeof(struct bt_regs), "cgsix dac");
716	all->par.fhc = of_ioremap(&op->resource[0], CG6_FHC_OFFSET,
717				  sizeof(u32), "cgsix fhc");
718
719	all->info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_IMAGEBLIT |
720                          FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
721	all->info.fbops = &cg6_ops;
722
723	all->info.screen_base =  of_ioremap(&op->resource[0], CG6_RAM_OFFSET,
724					    all->par.fbsize, "cgsix ram");
725	if (!all->par.fbc || !all->par.tec || !all->par.thc ||
726	    !all->par.bt || !all->par.fhc || !all->info.screen_base) {
727		cg6_unmap_regs(op, all);
728		kfree(all);
729		return -ENOMEM;
730	}
731
732	all->info.par = &all->par;
733
734	all->info.var.accel_flags = FB_ACCELF_TEXT;
735
736	cg6_bt_init(&all->par);
737	cg6_chip_init(&all->info);
738	cg6_blank(0, &all->info);
739
740	if (fb_alloc_cmap(&all->info.cmap, 256, 0)) {
741		cg6_unmap_regs(op, all);
742		kfree(all);
743		return -ENOMEM;
744	}
745
746	fb_set_cmap(&all->info.cmap, &all->info);
747	cg6_init_fix(&all->info, linebytes);
748
749	err = register_framebuffer(&all->info);
750	if (err < 0) {
751		cg6_unmap_regs(op, all);
752		fb_dealloc_cmap(&all->info.cmap);
753		kfree(all);
754		return err;
755	}
756
757	dev_set_drvdata(&op->dev, all);
758
759	printk("%s: CGsix [%s] at %lx:%lx\n",
760	       dp->full_name,
761	       all->info.fix.id,
762	       all->par.which_io, all->par.physbase);
763
764	return 0;
765}
766
767static int __devinit cg6_probe(struct of_device *dev, const struct of_device_id *match)
768{
769	struct of_device *op = to_of_device(&dev->dev);
770
771	return cg6_init_one(op);
772}
773
774static int __devexit cg6_remove(struct of_device *op)
775{
776	struct all_info *all = dev_get_drvdata(&op->dev);
777
778	unregister_framebuffer(&all->info);
779	fb_dealloc_cmap(&all->info.cmap);
780
781	cg6_unmap_regs(op, all);
782
783	kfree(all);
784
785	dev_set_drvdata(&op->dev, NULL);
786
787	return 0;
788}
789
790static struct of_device_id cg6_match[] = {
791	{
792		.name = "cgsix",
793	},
794	{
795		.name = "cgthree+",
796	},
797	{},
798};
799MODULE_DEVICE_TABLE(of, cg6_match);
800
801static struct of_platform_driver cg6_driver = {
802	.name		= "cg6",
803	.match_table	= cg6_match,
804	.probe		= cg6_probe,
805	.remove		= __devexit_p(cg6_remove),
806};
807
808static int __init cg6_init(void)
809{
810	if (fb_get_options("cg6fb", NULL))
811		return -ENODEV;
812
813	return of_register_driver(&cg6_driver, &of_bus_type);
814}
815
816static void __exit cg6_exit(void)
817{
818	of_unregister_driver(&cg6_driver);
819}
820
821module_init(cg6_init);
822module_exit(cg6_exit);
823
824MODULE_DESCRIPTION("framebuffer driver for CGsix chipsets");
825MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
826MODULE_VERSION("2.0");
827MODULE_LICENSE("GPL");
828