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(®s->control);
198f7018c21STomi Valkeinen val |= CG3_CR_ENABLE_VIDEO;
199f7018c21STomi Valkeinen sbus_writeb(val, ®s->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(®s->control);
208f7018c21STomi Valkeinen val &= ~CG3_CR_ENABLE_VIDEO;
209f7018c21STomi Valkeinen sbus_writeb(val, ®s->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