xref: /openbmc/linux/drivers/video/fbdev/cg3.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f7018c21STomi Valkeinen /* cg3.c: CGTHREE frame buffer driver
3f7018c21STomi Valkeinen  *
4f7018c21STomi Valkeinen  * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net)
5f7018c21STomi Valkeinen  * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
6f7018c21STomi Valkeinen  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
7f7018c21STomi Valkeinen  * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
8f7018c21STomi Valkeinen  *
9f7018c21STomi Valkeinen  * Driver layout based loosely on tgafb.c, see that file for credits.
10f7018c21STomi Valkeinen  */
11f7018c21STomi Valkeinen 
12f7018c21STomi Valkeinen #include <linux/module.h>
13f7018c21STomi Valkeinen #include <linux/kernel.h>
14f7018c21STomi Valkeinen #include <linux/errno.h>
15f7018c21STomi Valkeinen #include <linux/string.h>
16f7018c21STomi Valkeinen #include <linux/delay.h>
17f7018c21STomi Valkeinen #include <linux/init.h>
18f7018c21STomi Valkeinen #include <linux/fb.h>
19f7018c21STomi Valkeinen #include <linux/mm.h>
20*e8812acbSRob Herring #include <linux/of.h>
21*e8812acbSRob Herring #include <linux/platform_device.h>
22f7018c21STomi Valkeinen 
23f7018c21STomi Valkeinen #include <asm/io.h>
24f7018c21STomi Valkeinen #include <asm/fbio.h>
25f7018c21STomi Valkeinen 
26f7018c21STomi Valkeinen #include "sbuslib.h"
27f7018c21STomi Valkeinen 
28f7018c21STomi Valkeinen /*
29f7018c21STomi Valkeinen  * Local functions.
30f7018c21STomi Valkeinen  */
31f7018c21STomi Valkeinen 
32f7018c21STomi Valkeinen static int cg3_setcolreg(unsigned, unsigned, unsigned, unsigned,
33f7018c21STomi Valkeinen 			 unsigned, struct fb_info *);
34f7018c21STomi Valkeinen static int cg3_blank(int, struct fb_info *);
35f7018c21STomi Valkeinen 
36f7018c21STomi Valkeinen static int cg3_mmap(struct fb_info *, struct vm_area_struct *);
37f7018c21STomi Valkeinen static int cg3_ioctl(struct fb_info *, unsigned int, unsigned long);
38f7018c21STomi Valkeinen 
39f7018c21STomi Valkeinen /*
40f7018c21STomi Valkeinen  *  Frame buffer operations
41f7018c21STomi Valkeinen  */
42f7018c21STomi Valkeinen 
438a48ac33SJani Nikula static const struct fb_ops cg3_ops = {
44f7018c21STomi Valkeinen 	.owner			= THIS_MODULE,
45f7018c21STomi Valkeinen 	.fb_setcolreg		= cg3_setcolreg,
46f7018c21STomi Valkeinen 	.fb_blank		= cg3_blank,
47f7018c21STomi Valkeinen 	.fb_fillrect		= cfb_fillrect,
48f7018c21STomi Valkeinen 	.fb_copyarea		= cfb_copyarea,
49f7018c21STomi Valkeinen 	.fb_imageblit		= cfb_imageblit,
50f7018c21STomi Valkeinen 	.fb_mmap		= cg3_mmap,
51f7018c21STomi Valkeinen 	.fb_ioctl		= cg3_ioctl,
52f7018c21STomi Valkeinen #ifdef CONFIG_COMPAT
53f7018c21STomi Valkeinen 	.fb_compat_ioctl	= sbusfb_compat_ioctl,
54f7018c21STomi Valkeinen #endif
55f7018c21STomi Valkeinen };
56f7018c21STomi Valkeinen 
57f7018c21STomi Valkeinen 
58f7018c21STomi Valkeinen /* Control Register Constants */
59f7018c21STomi Valkeinen #define CG3_CR_ENABLE_INTS      0x80
60f7018c21STomi Valkeinen #define CG3_CR_ENABLE_VIDEO     0x40
61f7018c21STomi Valkeinen #define CG3_CR_ENABLE_TIMING    0x20
62f7018c21STomi Valkeinen #define CG3_CR_ENABLE_CURCMP    0x10
63f7018c21STomi Valkeinen #define CG3_CR_XTAL_MASK        0x0c
64f7018c21STomi Valkeinen #define CG3_CR_DIVISOR_MASK     0x03
65f7018c21STomi Valkeinen 
66f7018c21STomi Valkeinen /* Status Register Constants */
67f7018c21STomi Valkeinen #define CG3_SR_PENDING_INT      0x80
68f7018c21STomi Valkeinen #define CG3_SR_RES_MASK         0x70
69f7018c21STomi Valkeinen #define CG3_SR_1152_900_76_A    0x40
70f7018c21STomi Valkeinen #define CG3_SR_1152_900_76_B    0x60
71f7018c21STomi Valkeinen #define CG3_SR_ID_MASK          0x0f
72f7018c21STomi Valkeinen #define CG3_SR_ID_COLOR         0x01
73f7018c21STomi Valkeinen #define CG3_SR_ID_MONO          0x02
74f7018c21STomi Valkeinen #define CG3_SR_ID_MONO_ECL      0x03
75f7018c21STomi Valkeinen 
76f7018c21STomi Valkeinen enum cg3_type {
77f7018c21STomi Valkeinen 	CG3_AT_66HZ = 0,
78f7018c21STomi Valkeinen 	CG3_AT_76HZ,
79f7018c21STomi Valkeinen 	CG3_RDI
80f7018c21STomi Valkeinen };
81f7018c21STomi Valkeinen 
82f7018c21STomi Valkeinen struct bt_regs {
83f7018c21STomi Valkeinen 	u32 addr;
84f7018c21STomi Valkeinen 	u32 color_map;
85f7018c21STomi Valkeinen 	u32 control;
86f7018c21STomi Valkeinen 	u32 cursor;
87f7018c21STomi Valkeinen };
88f7018c21STomi Valkeinen 
89f7018c21STomi Valkeinen struct cg3_regs {
90f7018c21STomi Valkeinen 	struct bt_regs	cmap;
91f7018c21STomi Valkeinen 	u8	control;
92f7018c21STomi Valkeinen 	u8	status;
93f7018c21STomi Valkeinen 	u8	cursor_start;
94f7018c21STomi Valkeinen 	u8	cursor_end;
95f7018c21STomi Valkeinen 	u8	h_blank_start;
96f7018c21STomi Valkeinen 	u8	h_blank_end;
97f7018c21STomi Valkeinen 	u8	h_sync_start;
98f7018c21STomi Valkeinen 	u8	h_sync_end;
99f7018c21STomi Valkeinen 	u8	comp_sync_end;
100f7018c21STomi Valkeinen 	u8	v_blank_start_high;
101f7018c21STomi Valkeinen 	u8	v_blank_start_low;
102f7018c21STomi Valkeinen 	u8	v_blank_end;
103f7018c21STomi Valkeinen 	u8	v_sync_start;
104f7018c21STomi Valkeinen 	u8	v_sync_end;
105f7018c21STomi Valkeinen 	u8	xfer_holdoff_start;
106f7018c21STomi Valkeinen 	u8	xfer_holdoff_end;
107f7018c21STomi Valkeinen };
108f7018c21STomi Valkeinen 
109f7018c21STomi Valkeinen /* Offset of interesting structures in the OBIO space */
110f7018c21STomi Valkeinen #define CG3_REGS_OFFSET	     0x400000UL
111f7018c21STomi Valkeinen #define CG3_RAM_OFFSET	     0x800000UL
112f7018c21STomi Valkeinen 
113f7018c21STomi Valkeinen struct cg3_par {
114f7018c21STomi Valkeinen 	spinlock_t		lock;
115f7018c21STomi Valkeinen 	struct cg3_regs		__iomem *regs;
116f7018c21STomi Valkeinen 	u32			sw_cmap[((256 * 3) + 3) / 4];
117f7018c21STomi Valkeinen 
118f7018c21STomi Valkeinen 	u32			flags;
119f7018c21STomi Valkeinen #define CG3_FLAG_BLANKED	0x00000001
120f7018c21STomi Valkeinen #define CG3_FLAG_RDI		0x00000002
121f7018c21STomi Valkeinen 
122f7018c21STomi Valkeinen 	unsigned long		which_io;
123f7018c21STomi Valkeinen };
124f7018c21STomi Valkeinen 
125f7018c21STomi Valkeinen /**
126f7018c21STomi Valkeinen  *      cg3_setcolreg - Optional function. Sets a color register.
127f7018c21STomi Valkeinen  *      @regno: boolean, 0 copy local, 1 get_user() function
128f7018c21STomi Valkeinen  *      @red: frame buffer colormap structure
129f7018c21STomi Valkeinen  *      @green: The green value which can be up to 16 bits wide
130f7018c21STomi Valkeinen  *      @blue:  The blue value which can be up to 16 bits wide.
131f7018c21STomi Valkeinen  *      @transp: If supported the alpha value which can be up to 16 bits wide.
132f7018c21STomi Valkeinen  *      @info: frame buffer info structure
133f7018c21STomi Valkeinen  *
134f7018c21STomi Valkeinen  * The cg3 palette is loaded with 4 color values at each time
135f7018c21STomi Valkeinen  * so you end up with: (rgb)(r), (gb)(rg), (b)(rgb), and so on.
136f7018c21STomi Valkeinen  * We keep a sw copy of the hw cmap to assist us in this esoteric
137f7018c21STomi Valkeinen  * loading procedure.
138f7018c21STomi Valkeinen  */
cg3_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info * info)139f7018c21STomi Valkeinen static int cg3_setcolreg(unsigned regno,
140f7018c21STomi Valkeinen 			 unsigned red, unsigned green, unsigned blue,
141f7018c21STomi Valkeinen 			 unsigned transp, struct fb_info *info)
142f7018c21STomi Valkeinen {
143f7018c21STomi Valkeinen 	struct cg3_par *par = (struct cg3_par *) info->par;
144f7018c21STomi Valkeinen 	struct bt_regs __iomem *bt = &par->regs->cmap;
145f7018c21STomi Valkeinen 	unsigned long flags;
146f7018c21STomi Valkeinen 	u32 *p32;
147f7018c21STomi Valkeinen 	u8 *p8;
148f7018c21STomi Valkeinen 	int count;
149f7018c21STomi Valkeinen 
150f7018c21STomi Valkeinen 	if (regno >= 256)
151f7018c21STomi Valkeinen 		return 1;
152f7018c21STomi Valkeinen 
153f7018c21STomi Valkeinen 	red >>= 8;
154f7018c21STomi Valkeinen 	green >>= 8;
155f7018c21STomi Valkeinen 	blue >>= 8;
156f7018c21STomi Valkeinen 
157f7018c21STomi Valkeinen 	spin_lock_irqsave(&par->lock, flags);
158f7018c21STomi Valkeinen 
159f7018c21STomi Valkeinen 	p8 = (u8 *)par->sw_cmap + (regno * 3);
160f7018c21STomi Valkeinen 	p8[0] = red;
161f7018c21STomi Valkeinen 	p8[1] = green;
162f7018c21STomi Valkeinen 	p8[2] = blue;
163f7018c21STomi Valkeinen 
164f7018c21STomi Valkeinen #define D4M3(x) ((((x)>>2)<<1) + ((x)>>2))      /* (x/4)*3 */
165f7018c21STomi Valkeinen #define D4M4(x) ((x)&~0x3)                      /* (x/4)*4 */
166f7018c21STomi Valkeinen 
167f7018c21STomi Valkeinen 	count = 3;
168f7018c21STomi Valkeinen 	p32 = &par->sw_cmap[D4M3(regno)];
169f7018c21STomi Valkeinen 	sbus_writel(D4M4(regno), &bt->addr);
170f7018c21STomi Valkeinen 	while (count--)
171f7018c21STomi Valkeinen 		sbus_writel(*p32++, &bt->color_map);
172f7018c21STomi Valkeinen 
173f7018c21STomi Valkeinen #undef D4M3
174f7018c21STomi Valkeinen #undef D4M4
175f7018c21STomi Valkeinen 
176f7018c21STomi Valkeinen 	spin_unlock_irqrestore(&par->lock, flags);
177f7018c21STomi Valkeinen 
178f7018c21STomi Valkeinen 	return 0;
179f7018c21STomi Valkeinen }
180f7018c21STomi Valkeinen 
181f7018c21STomi Valkeinen /**
182f7018c21STomi Valkeinen  *      cg3_blank - Optional function.  Blanks the display.
1833ccdcdf4SSam Ravnborg  *      @blank: the blank mode we want.
184f7018c21STomi Valkeinen  *      @info: frame buffer structure that represents a single frame buffer
185f7018c21STomi Valkeinen  */
cg3_blank(int blank,struct fb_info * info)186f7018c21STomi Valkeinen static int cg3_blank(int blank, struct fb_info *info)
187f7018c21STomi Valkeinen {
188f7018c21STomi Valkeinen 	struct cg3_par *par = (struct cg3_par *) info->par;
189f7018c21STomi Valkeinen 	struct cg3_regs __iomem *regs = par->regs;
190f7018c21STomi Valkeinen 	unsigned long flags;
191f7018c21STomi Valkeinen 	u8 val;
192f7018c21STomi Valkeinen 
193f7018c21STomi Valkeinen 	spin_lock_irqsave(&par->lock, flags);
194f7018c21STomi Valkeinen 
195f7018c21STomi Valkeinen 	switch (blank) {
196f7018c21STomi Valkeinen 	case FB_BLANK_UNBLANK: /* Unblanking */
197f7018c21STomi Valkeinen 		val = sbus_readb(&regs->control);
198f7018c21STomi Valkeinen 		val |= CG3_CR_ENABLE_VIDEO;
199f7018c21STomi Valkeinen 		sbus_writeb(val, &regs->control);
200f7018c21STomi Valkeinen 		par->flags &= ~CG3_FLAG_BLANKED;
201f7018c21STomi Valkeinen 		break;
202f7018c21STomi Valkeinen 
203f7018c21STomi Valkeinen 	case FB_BLANK_NORMAL: /* Normal blanking */
204f7018c21STomi Valkeinen 	case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
205f7018c21STomi Valkeinen 	case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
206f7018c21STomi Valkeinen 	case FB_BLANK_POWERDOWN: /* Poweroff */
207f7018c21STomi Valkeinen 		val = sbus_readb(&regs->control);
208f7018c21STomi Valkeinen 		val &= ~CG3_CR_ENABLE_VIDEO;
209f7018c21STomi Valkeinen 		sbus_writeb(val, &regs->control);
210f7018c21STomi Valkeinen 		par->flags |= CG3_FLAG_BLANKED;
211f7018c21STomi Valkeinen 		break;
212f7018c21STomi Valkeinen 	}
213f7018c21STomi Valkeinen 
214f7018c21STomi Valkeinen 	spin_unlock_irqrestore(&par->lock, flags);
215f7018c21STomi Valkeinen 
216f7018c21STomi Valkeinen 	return 0;
217f7018c21STomi Valkeinen }
218f7018c21STomi Valkeinen 
219f7018c21STomi Valkeinen static struct sbus_mmap_map cg3_mmap_map[] = {
220f7018c21STomi Valkeinen 	{
221f7018c21STomi Valkeinen 		.voff	= CG3_MMAP_OFFSET,
222f7018c21STomi Valkeinen 		.poff	= CG3_RAM_OFFSET,
223f7018c21STomi Valkeinen 		.size	= SBUS_MMAP_FBSIZE(1)
224f7018c21STomi Valkeinen 	},
225f7018c21STomi Valkeinen 	{ .size = 0 }
226f7018c21STomi Valkeinen };
227f7018c21STomi Valkeinen 
cg3_mmap(struct fb_info * info,struct vm_area_struct * vma)228f7018c21STomi Valkeinen static int cg3_mmap(struct fb_info *info, struct vm_area_struct *vma)
229f7018c21STomi Valkeinen {
230f7018c21STomi Valkeinen 	struct cg3_par *par = (struct cg3_par *)info->par;
231f7018c21STomi Valkeinen 
232f7018c21STomi Valkeinen 	return sbusfb_mmap_helper(cg3_mmap_map,
233f7018c21STomi Valkeinen 				  info->fix.smem_start, info->fix.smem_len,
234f7018c21STomi Valkeinen 				  par->which_io,
235f7018c21STomi Valkeinen 				  vma);
236f7018c21STomi Valkeinen }
237f7018c21STomi Valkeinen 
cg3_ioctl(struct fb_info * info,unsigned int cmd,unsigned long arg)238f7018c21STomi Valkeinen static int cg3_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
239f7018c21STomi Valkeinen {
240f7018c21STomi Valkeinen 	return sbusfb_ioctl_helper(cmd, arg, info,
241f7018c21STomi Valkeinen 				   FBTYPE_SUN3COLOR, 8, info->fix.smem_len);
242f7018c21STomi Valkeinen }
243f7018c21STomi Valkeinen 
244f7018c21STomi Valkeinen /*
245f7018c21STomi Valkeinen  *  Initialisation
246f7018c21STomi Valkeinen  */
247f7018c21STomi Valkeinen 
cg3_init_fix(struct fb_info * info,int linebytes,struct device_node * dp)248f7018c21STomi Valkeinen static void cg3_init_fix(struct fb_info *info, int linebytes,
249f7018c21STomi Valkeinen 			 struct device_node *dp)
250f7018c21STomi Valkeinen {
2515c63e407SRob Herring 	snprintf(info->fix.id, sizeof(info->fix.id), "%pOFn", dp);
252f7018c21STomi Valkeinen 
253f7018c21STomi Valkeinen 	info->fix.type = FB_TYPE_PACKED_PIXELS;
254f7018c21STomi Valkeinen 	info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
255f7018c21STomi Valkeinen 
256f7018c21STomi Valkeinen 	info->fix.line_length = linebytes;
257f7018c21STomi Valkeinen 
258f7018c21STomi Valkeinen 	info->fix.accel = FB_ACCEL_SUN_CGTHREE;
259f7018c21STomi Valkeinen }
260f7018c21STomi Valkeinen 
cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo * var,struct device_node * dp)261f7018c21STomi Valkeinen static void cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo *var,
262f7018c21STomi Valkeinen 				    struct device_node *dp)
263f7018c21STomi Valkeinen {
264f7018c21STomi Valkeinen 	const char *params;
265f7018c21STomi Valkeinen 	char *p;
266f7018c21STomi Valkeinen 	int ww, hh;
267f7018c21STomi Valkeinen 
268f7018c21STomi Valkeinen 	params = of_get_property(dp, "params", NULL);
269f7018c21STomi Valkeinen 	if (params) {
270f7018c21STomi Valkeinen 		ww = simple_strtoul(params, &p, 10);
271f7018c21STomi Valkeinen 		if (ww && *p == 'x') {
272f7018c21STomi Valkeinen 			hh = simple_strtoul(p + 1, &p, 10);
273f7018c21STomi Valkeinen 			if (hh && *p == '-') {
274f7018c21STomi Valkeinen 				if (var->xres != ww ||
275f7018c21STomi Valkeinen 				    var->yres != hh) {
276f7018c21STomi Valkeinen 					var->xres = var->xres_virtual = ww;
277f7018c21STomi Valkeinen 					var->yres = var->yres_virtual = hh;
278f7018c21STomi Valkeinen 				}
279f7018c21STomi Valkeinen 			}
280f7018c21STomi Valkeinen 		}
281f7018c21STomi Valkeinen 	}
282f7018c21STomi Valkeinen }
283f7018c21STomi Valkeinen 
284f7018c21STomi Valkeinen static u8 cg3regvals_66hz[] = {	/* 1152 x 900, 66 Hz */
285f7018c21STomi Valkeinen 	0x14, 0xbb,	0x15, 0x2b,	0x16, 0x04,	0x17, 0x14,
286f7018c21STomi Valkeinen 	0x18, 0xae,	0x19, 0x03,	0x1a, 0xa8,	0x1b, 0x24,
287f7018c21STomi Valkeinen 	0x1c, 0x01,	0x1d, 0x05,	0x1e, 0xff,	0x1f, 0x01,
288f7018c21STomi Valkeinen 	0x10, 0x20,	0
289f7018c21STomi Valkeinen };
290f7018c21STomi Valkeinen 
291f7018c21STomi Valkeinen static u8 cg3regvals_76hz[] = {	/* 1152 x 900, 76 Hz */
292f7018c21STomi Valkeinen 	0x14, 0xb7,	0x15, 0x27,	0x16, 0x03,	0x17, 0x0f,
293f7018c21STomi Valkeinen 	0x18, 0xae,	0x19, 0x03,	0x1a, 0xae,	0x1b, 0x2a,
294f7018c21STomi Valkeinen 	0x1c, 0x01,	0x1d, 0x09,	0x1e, 0xff,	0x1f, 0x01,
295f7018c21STomi Valkeinen 	0x10, 0x24,	0
296f7018c21STomi Valkeinen };
297f7018c21STomi Valkeinen 
298f7018c21STomi Valkeinen static u8 cg3regvals_rdi[] = {	/* 640 x 480, cgRDI */
299f7018c21STomi Valkeinen 	0x14, 0x70,	0x15, 0x20,	0x16, 0x08,	0x17, 0x10,
300f7018c21STomi Valkeinen 	0x18, 0x06,	0x19, 0x02,	0x1a, 0x31,	0x1b, 0x51,
301f7018c21STomi Valkeinen 	0x1c, 0x06,	0x1d, 0x0c,	0x1e, 0xff,	0x1f, 0x01,
302f7018c21STomi Valkeinen 	0x10, 0x22,	0
303f7018c21STomi Valkeinen };
304f7018c21STomi Valkeinen 
305f7018c21STomi Valkeinen static u8 *cg3_regvals[] = {
306f7018c21STomi Valkeinen 	cg3regvals_66hz, cg3regvals_76hz, cg3regvals_rdi
307f7018c21STomi Valkeinen };
308f7018c21STomi Valkeinen 
309f7018c21STomi Valkeinen static u_char cg3_dacvals[] = {
310f7018c21STomi Valkeinen 	4, 0xff,	5, 0x00,	6, 0x70,	7, 0x00,	0
311f7018c21STomi Valkeinen };
312f7018c21STomi Valkeinen 
cg3_do_default_mode(struct cg3_par * par)313f7018c21STomi Valkeinen static int cg3_do_default_mode(struct cg3_par *par)
314f7018c21STomi Valkeinen {
315f7018c21STomi Valkeinen 	enum cg3_type type;
316f7018c21STomi Valkeinen 	u8 *p;
317f7018c21STomi Valkeinen 
318f7018c21STomi Valkeinen 	if (par->flags & CG3_FLAG_RDI)
319f7018c21STomi Valkeinen 		type = CG3_RDI;
320f7018c21STomi Valkeinen 	else {
321f7018c21STomi Valkeinen 		u8 status = sbus_readb(&par->regs->status), mon;
322f7018c21STomi Valkeinen 		if ((status & CG3_SR_ID_MASK) == CG3_SR_ID_COLOR) {
323f7018c21STomi Valkeinen 			mon = status & CG3_SR_RES_MASK;
324f7018c21STomi Valkeinen 			if (mon == CG3_SR_1152_900_76_A ||
325f7018c21STomi Valkeinen 			    mon == CG3_SR_1152_900_76_B)
326f7018c21STomi Valkeinen 				type = CG3_AT_76HZ;
327f7018c21STomi Valkeinen 			else
328f7018c21STomi Valkeinen 				type = CG3_AT_66HZ;
329f7018c21STomi Valkeinen 		} else {
330f7018c21STomi Valkeinen 			printk(KERN_ERR "cgthree: can't handle SR %02x\n",
331f7018c21STomi Valkeinen 			       status);
332f7018c21STomi Valkeinen 			return -EINVAL;
333f7018c21STomi Valkeinen 		}
334f7018c21STomi Valkeinen 	}
335f7018c21STomi Valkeinen 
336f7018c21STomi Valkeinen 	for (p = cg3_regvals[type]; *p; p += 2) {
337f7018c21STomi Valkeinen 		u8 __iomem *regp = &((u8 __iomem *)par->regs)[p[0]];
338f7018c21STomi Valkeinen 		sbus_writeb(p[1], regp);
339f7018c21STomi Valkeinen 	}
340f7018c21STomi Valkeinen 	for (p = cg3_dacvals; *p; p += 2) {
341f7018c21STomi Valkeinen 		u8 __iomem *regp;
342f7018c21STomi Valkeinen 
343f7018c21STomi Valkeinen 		regp = (u8 __iomem *)&par->regs->cmap.addr;
344f7018c21STomi Valkeinen 		sbus_writeb(p[0], regp);
345f7018c21STomi Valkeinen 		regp = (u8 __iomem *)&par->regs->cmap.control;
346f7018c21STomi Valkeinen 		sbus_writeb(p[1], regp);
347f7018c21STomi Valkeinen 	}
348f7018c21STomi Valkeinen 	return 0;
349f7018c21STomi Valkeinen }
350f7018c21STomi Valkeinen 
cg3_probe(struct platform_device * op)351f7018c21STomi Valkeinen static int cg3_probe(struct platform_device *op)
352f7018c21STomi Valkeinen {
353f7018c21STomi Valkeinen 	struct device_node *dp = op->dev.of_node;
354f7018c21STomi Valkeinen 	struct fb_info *info;
355f7018c21STomi Valkeinen 	struct cg3_par *par;
356f7018c21STomi Valkeinen 	int linebytes, err;
357f7018c21STomi Valkeinen 
358f7018c21STomi Valkeinen 	info = framebuffer_alloc(sizeof(struct cg3_par), &op->dev);
359f7018c21STomi Valkeinen 
360f7018c21STomi Valkeinen 	err = -ENOMEM;
361f7018c21STomi Valkeinen 	if (!info)
362f7018c21STomi Valkeinen 		goto out_err;
363f7018c21STomi Valkeinen 	par = info->par;
364f7018c21STomi Valkeinen 
365f7018c21STomi Valkeinen 	spin_lock_init(&par->lock);
366f7018c21STomi Valkeinen 
367f7018c21STomi Valkeinen 	info->fix.smem_start = op->resource[0].start;
368f7018c21STomi Valkeinen 	par->which_io = op->resource[0].flags & IORESOURCE_BITS;
369f7018c21STomi Valkeinen 
370f7018c21STomi Valkeinen 	sbusfb_fill_var(&info->var, dp, 8);
371f7018c21STomi Valkeinen 	info->var.red.length = 8;
372f7018c21STomi Valkeinen 	info->var.green.length = 8;
373f7018c21STomi Valkeinen 	info->var.blue.length = 8;
374a6f13af4SRob Herring 	if (of_node_name_eq(dp, "cgRDI"))
375f7018c21STomi Valkeinen 		par->flags |= CG3_FLAG_RDI;
376f7018c21STomi Valkeinen 	if (par->flags & CG3_FLAG_RDI)
377f7018c21STomi Valkeinen 		cg3_rdi_maybe_fixup_var(&info->var, dp);
378f7018c21STomi Valkeinen 
379f7018c21STomi Valkeinen 	linebytes = of_getintprop_default(dp, "linebytes",
380f7018c21STomi Valkeinen 					  info->var.xres);
381f7018c21STomi Valkeinen 	info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres);
382f7018c21STomi Valkeinen 
383f7018c21STomi Valkeinen 	par->regs = of_ioremap(&op->resource[0], CG3_REGS_OFFSET,
384f7018c21STomi Valkeinen 			       sizeof(struct cg3_regs), "cg3 regs");
385f7018c21STomi Valkeinen 	if (!par->regs)
386f7018c21STomi Valkeinen 		goto out_release_fb;
387f7018c21STomi Valkeinen 
388f7018c21STomi Valkeinen 	info->fbops = &cg3_ops;
389f7018c21STomi Valkeinen 	info->screen_base = of_ioremap(&op->resource[0], CG3_RAM_OFFSET,
390f7018c21STomi Valkeinen 				       info->fix.smem_len, "cg3 ram");
391f7018c21STomi Valkeinen 	if (!info->screen_base)
392f7018c21STomi Valkeinen 		goto out_unmap_regs;
393f7018c21STomi Valkeinen 
394f7018c21STomi Valkeinen 	cg3_blank(FB_BLANK_UNBLANK, info);
395f7018c21STomi Valkeinen 
39629413f05SRob Herring 	if (!of_property_present(dp, "width")) {
397f7018c21STomi Valkeinen 		err = cg3_do_default_mode(par);
398f7018c21STomi Valkeinen 		if (err)
399f7018c21STomi Valkeinen 			goto out_unmap_screen;
400f7018c21STomi Valkeinen 	}
401f7018c21STomi Valkeinen 
402f7018c21STomi Valkeinen 	err = fb_alloc_cmap(&info->cmap, 256, 0);
403f7018c21STomi Valkeinen 	if (err)
404f7018c21STomi Valkeinen 		goto out_unmap_screen;
405f7018c21STomi Valkeinen 
406f7018c21STomi Valkeinen 	fb_set_cmap(&info->cmap, info);
407f7018c21STomi Valkeinen 
408f7018c21STomi Valkeinen 	cg3_init_fix(info, linebytes, dp);
409f7018c21STomi Valkeinen 
410f7018c21STomi Valkeinen 	err = register_framebuffer(info);
411f7018c21STomi Valkeinen 	if (err < 0)
412f7018c21STomi Valkeinen 		goto out_dealloc_cmap;
413f7018c21STomi Valkeinen 
414f7018c21STomi Valkeinen 	dev_set_drvdata(&op->dev, info);
415f7018c21STomi Valkeinen 
4166d7e6533SRob Herring 	printk(KERN_INFO "%pOF: cg3 at %lx:%lx\n",
4176d7e6533SRob Herring 	       dp, par->which_io, info->fix.smem_start);
418f7018c21STomi Valkeinen 
419f7018c21STomi Valkeinen 	return 0;
420f7018c21STomi Valkeinen 
421f7018c21STomi Valkeinen out_dealloc_cmap:
422f7018c21STomi Valkeinen 	fb_dealloc_cmap(&info->cmap);
423f7018c21STomi Valkeinen 
424f7018c21STomi Valkeinen out_unmap_screen:
425f7018c21STomi Valkeinen 	of_iounmap(&op->resource[0], info->screen_base, info->fix.smem_len);
426f7018c21STomi Valkeinen 
427f7018c21STomi Valkeinen out_unmap_regs:
428f7018c21STomi Valkeinen 	of_iounmap(&op->resource[0], par->regs, sizeof(struct cg3_regs));
429f7018c21STomi Valkeinen 
430f7018c21STomi Valkeinen out_release_fb:
431f7018c21STomi Valkeinen 	framebuffer_release(info);
432f7018c21STomi Valkeinen 
433f7018c21STomi Valkeinen out_err:
434f7018c21STomi Valkeinen 	return err;
435f7018c21STomi Valkeinen }
436f7018c21STomi Valkeinen 
cg3_remove(struct platform_device * op)437dd65e6f3SUwe Kleine-König static void cg3_remove(struct platform_device *op)
438f7018c21STomi Valkeinen {
439f7018c21STomi Valkeinen 	struct fb_info *info = dev_get_drvdata(&op->dev);
440f7018c21STomi Valkeinen 	struct cg3_par *par = info->par;
441f7018c21STomi Valkeinen 
442f7018c21STomi Valkeinen 	unregister_framebuffer(info);
443f7018c21STomi Valkeinen 	fb_dealloc_cmap(&info->cmap);
444f7018c21STomi Valkeinen 
445f7018c21STomi Valkeinen 	of_iounmap(&op->resource[0], par->regs, sizeof(struct cg3_regs));
446f7018c21STomi Valkeinen 	of_iounmap(&op->resource[0], info->screen_base, info->fix.smem_len);
447f7018c21STomi Valkeinen 
448f7018c21STomi Valkeinen 	framebuffer_release(info);
449f7018c21STomi Valkeinen }
450f7018c21STomi Valkeinen 
451f7018c21STomi Valkeinen static const struct of_device_id cg3_match[] = {
452f7018c21STomi Valkeinen 	{
453f7018c21STomi Valkeinen 		.name = "cgthree",
454f7018c21STomi Valkeinen 	},
455f7018c21STomi Valkeinen 	{
456f7018c21STomi Valkeinen 		.name = "cgRDI",
457f7018c21STomi Valkeinen 	},
458f7018c21STomi Valkeinen 	{},
459f7018c21STomi Valkeinen };
460f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(of, cg3_match);
461f7018c21STomi Valkeinen 
462f7018c21STomi Valkeinen static struct platform_driver cg3_driver = {
463f7018c21STomi Valkeinen 	.driver = {
464f7018c21STomi Valkeinen 		.name = "cg3",
465f7018c21STomi Valkeinen 		.of_match_table = cg3_match,
466f7018c21STomi Valkeinen 	},
467f7018c21STomi Valkeinen 	.probe		= cg3_probe,
468dd65e6f3SUwe Kleine-König 	.remove_new	= cg3_remove,
469f7018c21STomi Valkeinen };
470f7018c21STomi Valkeinen 
cg3_init(void)471f7018c21STomi Valkeinen static int __init cg3_init(void)
472f7018c21STomi Valkeinen {
473f7018c21STomi Valkeinen 	if (fb_get_options("cg3fb", NULL))
474f7018c21STomi Valkeinen 		return -ENODEV;
475f7018c21STomi Valkeinen 
476f7018c21STomi Valkeinen 	return platform_driver_register(&cg3_driver);
477f7018c21STomi Valkeinen }
478f7018c21STomi Valkeinen 
cg3_exit(void)479f7018c21STomi Valkeinen static void __exit cg3_exit(void)
480f7018c21STomi Valkeinen {
481f7018c21STomi Valkeinen 	platform_driver_unregister(&cg3_driver);
482f7018c21STomi Valkeinen }
483f7018c21STomi Valkeinen 
484f7018c21STomi Valkeinen module_init(cg3_init);
485f7018c21STomi Valkeinen module_exit(cg3_exit);
486f7018c21STomi Valkeinen 
487f7018c21STomi Valkeinen MODULE_DESCRIPTION("framebuffer driver for CGthree chipsets");
488f7018c21STomi Valkeinen MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
489f7018c21STomi Valkeinen MODULE_VERSION("2.0");
490f7018c21STomi Valkeinen MODULE_LICENSE("GPL");
491