xref: /openbmc/linux/drivers/video/fbdev/leo.c (revision b755c25f)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* leo.c: LEO frame buffer driver
3  *
4  * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net)
5  * Copyright (C) 1996-1999 Jakub Jelinek (jj@ultra.linux.cz)
6  * Copyright (C) 1997 Michal Rehacek (Michal.Rehacek@st.mff.cuni.cz)
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/delay.h>
16 #include <linux/init.h>
17 #include <linux/fb.h>
18 #include <linux/mm.h>
19 #include <linux/io.h>
20 #include <linux/of.h>
21 #include <linux/platform_device.h>
22 
23 #include <asm/fbio.h>
24 
25 #include "sbuslib.h"
26 
27 /*
28  * Local functions.
29  */
30 
31 static int leo_setcolreg(unsigned, unsigned, unsigned, unsigned,
32 			 unsigned, struct fb_info *);
33 static int leo_blank(int, struct fb_info *);
34 
35 static int leo_mmap(struct fb_info *, struct vm_area_struct *);
36 static int leo_ioctl(struct fb_info *, unsigned int, unsigned long);
37 static int leo_pan_display(struct fb_var_screeninfo *, struct fb_info *);
38 
39 /*
40  *  Frame buffer operations
41  */
42 
43 static const struct fb_ops leo_ops = {
44 	.owner			= THIS_MODULE,
45 	.fb_setcolreg		= leo_setcolreg,
46 	.fb_blank		= leo_blank,
47 	.fb_pan_display		= leo_pan_display,
48 	.fb_fillrect		= cfb_fillrect,
49 	.fb_copyarea		= cfb_copyarea,
50 	.fb_imageblit		= cfb_imageblit,
51 	.fb_mmap		= leo_mmap,
52 	.fb_ioctl		= leo_ioctl,
53 #ifdef CONFIG_COMPAT
54 	.fb_compat_ioctl	= sbusfb_compat_ioctl,
55 #endif
56 };
57 
58 #define LEO_OFF_LC_SS0_KRN	0x00200000UL
59 #define LEO_OFF_LC_SS0_USR	0x00201000UL
60 #define LEO_OFF_LC_SS1_KRN	0x01200000UL
61 #define LEO_OFF_LC_SS1_USR	0x01201000UL
62 #define LEO_OFF_LD_SS0		0x00400000UL
63 #define LEO_OFF_LD_SS1		0x01400000UL
64 #define LEO_OFF_LD_GBL		0x00401000UL
65 #define LEO_OFF_LX_KRN		0x00600000UL
66 #define LEO_OFF_LX_CURSOR	0x00601000UL
67 #define LEO_OFF_SS0		0x00800000UL
68 #define LEO_OFF_SS1		0x01800000UL
69 #define LEO_OFF_UNK		0x00602000UL
70 #define LEO_OFF_UNK2		0x00000000UL
71 
72 #define LEO_CUR_ENABLE		0x00000080
73 #define LEO_CUR_UPDATE		0x00000030
74 #define LEO_CUR_PROGRESS	0x00000006
75 #define LEO_CUR_UPDATECMAP	0x00000003
76 
77 #define LEO_CUR_TYPE_MASK	0x00000000
78 #define LEO_CUR_TYPE_IMAGE	0x00000020
79 #define LEO_CUR_TYPE_CMAP	0x00000050
80 
81 struct leo_cursor {
82 	u8	xxx0[16];
83 	u32	cur_type;
84 	u32	cur_misc;
85 	u32	cur_cursxy;
86 	u32	cur_data;
87 };
88 
89 #define LEO_KRN_TYPE_CLUT0	0x00001000
90 #define LEO_KRN_TYPE_CLUT1	0x00001001
91 #define LEO_KRN_TYPE_CLUT2	0x00001002
92 #define LEO_KRN_TYPE_WID	0x00001003
93 #define LEO_KRN_TYPE_UNK	0x00001006
94 #define LEO_KRN_TYPE_VIDEO	0x00002003
95 #define LEO_KRN_TYPE_CLUTDATA	0x00004000
96 #define LEO_KRN_CSR_ENABLE	0x00000008
97 #define LEO_KRN_CSR_PROGRESS	0x00000004
98 #define LEO_KRN_CSR_UNK		0x00000002
99 #define LEO_KRN_CSR_UNK2	0x00000001
100 
101 struct leo_lx_krn {
102 	u32	krn_type;
103 	u32	krn_csr;
104 	u32	krn_value;
105 };
106 
107 struct leo_lc_ss0_krn {
108 	u32 	misc;
109 	u8	xxx0[0x800-4];
110 	u32	rev;
111 };
112 
113 struct leo_lc_ss0_usr {
114 	u32	csr;
115 	u32	addrspace;
116 	u32 	fontmsk;
117 	u32	fontt;
118 	u32	extent;
119 	u32	src;
120 	u32	dst;
121 	u32	copy;
122 	u32	fill;
123 };
124 
125 struct leo_lc_ss1_krn {
126 	u8	unknown;
127 };
128 
129 struct leo_lc_ss1_usr {
130 	u8	unknown;
131 };
132 
133 struct leo_ld_ss0 {
134 	u8	xxx0[0xe00];
135 	u32	csr;
136 	u32	wid;
137 	u32	wmask;
138 	u32	widclip;
139 	u32	vclipmin;
140 	u32	vclipmax;
141 	u32	pickmin;	/* SS1 only */
142 	u32	pickmax;	/* SS1 only */
143 	u32	fg;
144 	u32	bg;
145 	u32	src;		/* Copy/Scroll (SS0 only) */
146 	u32	dst;		/* Copy/Scroll/Fill (SS0 only) */
147 	u32	extent;		/* Copy/Scroll/Fill size (SS0 only) */
148 	u32	xxx1[3];
149 	u32	setsem;		/* SS1 only */
150 	u32	clrsem;		/* SS1 only */
151 	u32	clrpick;	/* SS1 only */
152 	u32	clrdat;		/* SS1 only */
153 	u32	alpha;		/* SS1 only */
154 	u8	xxx2[0x2c];
155 	u32	winbg;
156 	u32	planemask;
157 	u32	rop;
158 	u32	z;
159 	u32	dczf;		/* SS1 only */
160 	u32	dczb;		/* SS1 only */
161 	u32	dcs;		/* SS1 only */
162 	u32	dczs;		/* SS1 only */
163 	u32	pickfb;		/* SS1 only */
164 	u32	pickbb;		/* SS1 only */
165 	u32	dcfc;		/* SS1 only */
166 	u32	forcecol;	/* SS1 only */
167 	u32	door[8];	/* SS1 only */
168 	u32	pick[5];	/* SS1 only */
169 };
170 
171 #define LEO_SS1_MISC_ENABLE	0x00000001
172 #define LEO_SS1_MISC_STEREO	0x00000002
173 struct leo_ld_ss1 {
174 	u8	xxx0[0xef4];
175 	u32	ss1_misc;
176 };
177 
178 struct leo_ld_gbl {
179 	u8	unknown;
180 };
181 
182 struct leo_par {
183 	spinlock_t		lock;
184 	struct leo_lx_krn	__iomem *lx_krn;
185 	struct leo_lc_ss0_usr	__iomem *lc_ss0_usr;
186 	struct leo_ld_ss0	__iomem *ld_ss0;
187 	struct leo_ld_ss1	__iomem *ld_ss1;
188 	struct leo_cursor	__iomem *cursor;
189 	u32			extent;
190 	u32			clut_data[256];
191 
192 	u32			flags;
193 #define LEO_FLAG_BLANKED	0x00000001
194 
195 	unsigned long		which_io;
196 };
197 
198 static void leo_wait(struct leo_lx_krn __iomem *lx_krn)
199 {
200 	int i;
201 
202 	for (i = 0;
203 	     (sbus_readl(&lx_krn->krn_csr) & LEO_KRN_CSR_PROGRESS) &&
204 	     i < 300000;
205 	     i++)
206 		udelay(1); /* Busy wait at most 0.3 sec */
207 	return;
208 }
209 
210 static void leo_switch_from_graph(struct fb_info *info)
211 {
212 	struct leo_par *par = (struct leo_par *) info->par;
213 	struct leo_ld_ss0 __iomem *ss = par->ld_ss0;
214 	struct leo_cursor __iomem *cursor = par->cursor;
215 	unsigned long flags;
216 	u32 val;
217 
218 	spin_lock_irqsave(&par->lock, flags);
219 
220 	par->extent = ((info->var.xres - 1) |
221 		       ((info->var.yres - 1) << 16));
222 
223 	sbus_writel(0xffffffff, &ss->wid);
224 	sbus_writel(0xffff, &ss->wmask);
225 	sbus_writel(0, &ss->vclipmin);
226 	sbus_writel(par->extent, &ss->vclipmax);
227 	sbus_writel(0, &ss->fg);
228 	sbus_writel(0xff000000, &ss->planemask);
229 	sbus_writel(0x310850, &ss->rop);
230 	sbus_writel(0, &ss->widclip);
231 	sbus_writel((info->var.xres-1) | ((info->var.yres-1) << 11),
232 		    &par->lc_ss0_usr->extent);
233 	sbus_writel(4, &par->lc_ss0_usr->addrspace);
234 	sbus_writel(0x80000000, &par->lc_ss0_usr->fill);
235 	sbus_writel(0, &par->lc_ss0_usr->fontt);
236 	do {
237 		val = sbus_readl(&par->lc_ss0_usr->csr);
238 	} while (val & 0x20000000);
239 
240 	/* setup screen buffer for cfb_* functions */
241 	sbus_writel(1, &ss->wid);
242 	sbus_writel(0x00ffffff, &ss->planemask);
243 	sbus_writel(0x310b90, &ss->rop);
244 	sbus_writel(0, &par->lc_ss0_usr->addrspace);
245 
246 	/* hide cursor */
247 	sbus_writel(sbus_readl(&cursor->cur_misc) & ~LEO_CUR_ENABLE, &cursor->cur_misc);
248 
249 	spin_unlock_irqrestore(&par->lock, flags);
250 }
251 
252 static int leo_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
253 {
254 	/* We just use this to catch switches out of
255 	 * graphics mode.
256 	 */
257 	leo_switch_from_graph(info);
258 
259 	if (var->xoffset || var->yoffset || var->vmode)
260 		return -EINVAL;
261 	return 0;
262 }
263 
264 /**
265  *      leo_setcolreg - Optional function. Sets a color register.
266  *      @regno: boolean, 0 copy local, 1 get_user() function
267  *      @red: frame buffer colormap structure
268  *      @green: The green value which can be up to 16 bits wide
269  *      @blue:  The blue value which can be up to 16 bits wide.
270  *      @transp: If supported the alpha value which can be up to 16 bits wide.
271  *      @info: frame buffer info structure
272  */
273 static int leo_setcolreg(unsigned regno,
274 			 unsigned red, unsigned green, unsigned blue,
275 			 unsigned transp, struct fb_info *info)
276 {
277 	struct leo_par *par = (struct leo_par *) info->par;
278 	struct leo_lx_krn __iomem *lx_krn = par->lx_krn;
279 	unsigned long flags;
280 	u32 val;
281 	int i;
282 
283 	if (regno >= 256)
284 		return 1;
285 
286 	red >>= 8;
287 	green >>= 8;
288 	blue >>= 8;
289 
290 	par->clut_data[regno] = red | (green << 8) | (blue << 16);
291 
292 	spin_lock_irqsave(&par->lock, flags);
293 
294 	leo_wait(lx_krn);
295 
296 	sbus_writel(LEO_KRN_TYPE_CLUTDATA, &lx_krn->krn_type);
297 	for (i = 0; i < 256; i++)
298 		sbus_writel(par->clut_data[i], &lx_krn->krn_value);
299 	sbus_writel(LEO_KRN_TYPE_CLUT0, &lx_krn->krn_type);
300 
301 	val = sbus_readl(&lx_krn->krn_csr);
302 	val |= (LEO_KRN_CSR_UNK | LEO_KRN_CSR_UNK2);
303 	sbus_writel(val, &lx_krn->krn_csr);
304 
305 	spin_unlock_irqrestore(&par->lock, flags);
306 
307 	return 0;
308 }
309 
310 /**
311  *      leo_blank - Optional function.  Blanks the display.
312  *      @blank: the blank mode we want.
313  *      @info: frame buffer structure that represents a single frame buffer
314  */
315 static int leo_blank(int blank, struct fb_info *info)
316 {
317 	struct leo_par *par = (struct leo_par *) info->par;
318 	struct leo_lx_krn __iomem *lx_krn = par->lx_krn;
319 	unsigned long flags;
320 	u32 val;
321 
322 	spin_lock_irqsave(&par->lock, flags);
323 
324 	switch (blank) {
325 	case FB_BLANK_UNBLANK: /* Unblanking */
326 		val = sbus_readl(&lx_krn->krn_csr);
327 		val |= LEO_KRN_CSR_ENABLE;
328 		sbus_writel(val, &lx_krn->krn_csr);
329 		par->flags &= ~LEO_FLAG_BLANKED;
330 		break;
331 
332 	case FB_BLANK_NORMAL: /* Normal blanking */
333 	case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
334 	case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
335 	case FB_BLANK_POWERDOWN: /* Poweroff */
336 		val = sbus_readl(&lx_krn->krn_csr);
337 		val &= ~LEO_KRN_CSR_ENABLE;
338 		sbus_writel(val, &lx_krn->krn_csr);
339 		par->flags |= LEO_FLAG_BLANKED;
340 		break;
341 	}
342 
343 	spin_unlock_irqrestore(&par->lock, flags);
344 
345 	return 0;
346 }
347 
348 static struct sbus_mmap_map leo_mmap_map[] = {
349 	{
350 		.voff	= LEO_SS0_MAP,
351 		.poff	= LEO_OFF_SS0,
352 		.size	= 0x800000
353 	},
354 	{
355 		.voff	= LEO_LC_SS0_USR_MAP,
356 		.poff	= LEO_OFF_LC_SS0_USR,
357 		.size	= 0x1000
358 	},
359 	{
360 		.voff	= LEO_LD_SS0_MAP,
361 		.poff	= LEO_OFF_LD_SS0,
362 		.size	= 0x1000
363 	},
364 	{
365 		.voff	= LEO_LX_CURSOR_MAP,
366 		.poff	= LEO_OFF_LX_CURSOR,
367 		.size	= 0x1000
368 	},
369 	{
370 		.voff	= LEO_SS1_MAP,
371 		.poff	= LEO_OFF_SS1,
372 		.size	= 0x800000
373 	},
374 	{
375 		.voff	= LEO_LC_SS1_USR_MAP,
376 		.poff	= LEO_OFF_LC_SS1_USR,
377 		.size	= 0x1000
378 	},
379 	{
380 		.voff	= LEO_LD_SS1_MAP,
381 		.poff	= LEO_OFF_LD_SS1,
382 		.size	= 0x1000
383 	},
384 	{
385 		.voff	= LEO_UNK_MAP,
386 		.poff	= LEO_OFF_UNK,
387 		.size	= 0x1000
388 	},
389 	{
390 		.voff	= LEO_LX_KRN_MAP,
391 		.poff	= LEO_OFF_LX_KRN,
392 		.size	= 0x1000
393 	},
394 	{
395 		.voff	= LEO_LC_SS0_KRN_MAP,
396 		.poff	= LEO_OFF_LC_SS0_KRN,
397 		.size	= 0x1000
398 	},
399 	{
400 		.voff	= LEO_LC_SS1_KRN_MAP,
401 		.poff	= LEO_OFF_LC_SS1_KRN,
402 		.size	= 0x1000
403 	},
404 	{
405 		.voff	= LEO_LD_GBL_MAP,
406 		.poff	= LEO_OFF_LD_GBL,
407 		.size	= 0x1000
408 	},
409 	{
410 		.voff	= LEO_UNK2_MAP,
411 		.poff	= LEO_OFF_UNK2,
412 		.size	= 0x100000
413 	},
414 	{ .size = 0 }
415 };
416 
417 static int leo_mmap(struct fb_info *info, struct vm_area_struct *vma)
418 {
419 	struct leo_par *par = (struct leo_par *)info->par;
420 
421 	return sbusfb_mmap_helper(leo_mmap_map,
422 				  info->fix.smem_start, info->fix.smem_len,
423 				  par->which_io, vma);
424 }
425 
426 static int leo_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
427 {
428 	return sbusfb_ioctl_helper(cmd, arg, info,
429 				   FBTYPE_SUNLEO, 32, info->fix.smem_len);
430 }
431 
432 /*
433  *  Initialisation
434  */
435 
436 static void
437 leo_init_fix(struct fb_info *info, struct device_node *dp)
438 {
439 	snprintf(info->fix.id, sizeof(info->fix.id), "%pOFn", dp);
440 
441 	info->fix.type = FB_TYPE_PACKED_PIXELS;
442 	info->fix.visual = FB_VISUAL_TRUECOLOR;
443 
444 	info->fix.line_length = 8192;
445 
446 	info->fix.accel = FB_ACCEL_SUN_LEO;
447 }
448 
449 static void leo_wid_put(struct fb_info *info, struct fb_wid_list *wl)
450 {
451 	struct leo_par *par = (struct leo_par *) info->par;
452 	struct leo_lx_krn __iomem *lx_krn = par->lx_krn;
453 	struct fb_wid_item *wi;
454 	unsigned long flags;
455 	u32 val;
456 	int i, j;
457 
458 	spin_lock_irqsave(&par->lock, flags);
459 
460 	leo_wait(lx_krn);
461 
462 	for (i = 0, wi = wl->wl_list; i < wl->wl_count; i++, wi++) {
463 		switch (wi->wi_type) {
464 		case FB_WID_DBL_8:
465 			j = (wi->wi_index & 0xf) + 0x40;
466 			break;
467 
468 		case FB_WID_DBL_24:
469 			j = wi->wi_index & 0x3f;
470 			break;
471 
472 		default:
473 			continue;
474 		}
475 		sbus_writel(0x5800 + j, &lx_krn->krn_type);
476 		sbus_writel(wi->wi_values[0], &lx_krn->krn_value);
477 	}
478 	sbus_writel(LEO_KRN_TYPE_WID, &lx_krn->krn_type);
479 
480 	val = sbus_readl(&lx_krn->krn_csr);
481 	val |= (LEO_KRN_CSR_UNK | LEO_KRN_CSR_UNK2);
482 	sbus_writel(val, &lx_krn->krn_csr);
483 
484 	spin_unlock_irqrestore(&par->lock, flags);
485 }
486 
487 static void leo_init_wids(struct fb_info *info)
488 {
489 	struct fb_wid_item wi;
490 	struct fb_wid_list wl;
491 
492 	wl.wl_count = 1;
493 	wl.wl_list = &wi;
494 	wi.wi_type = FB_WID_DBL_8;
495 	wi.wi_index = 0;
496 	wi.wi_values [0] = 0x2c0;
497 	leo_wid_put(info, &wl);
498 	wi.wi_index = 1;
499 	wi.wi_values [0] = 0x30;
500 	leo_wid_put(info, &wl);
501 	wi.wi_index = 2;
502 	wi.wi_values [0] = 0x20;
503 	leo_wid_put(info, &wl);
504 	wi.wi_type = FB_WID_DBL_24;
505 	wi.wi_index = 1;
506 	wi.wi_values [0] = 0x30;
507 	leo_wid_put(info, &wl);
508 }
509 
510 static void leo_init_hw(struct fb_info *info)
511 {
512 	struct leo_par *par = (struct leo_par *) info->par;
513 	u32 val;
514 
515 	val = sbus_readl(&par->ld_ss1->ss1_misc);
516 	val |= LEO_SS1_MISC_ENABLE;
517 	sbus_writel(val, &par->ld_ss1->ss1_misc);
518 
519 	leo_switch_from_graph(info);
520 }
521 
522 static void leo_fixup_var_rgb(struct fb_var_screeninfo *var)
523 {
524 	var->red.offset = 0;
525 	var->red.length = 8;
526 	var->green.offset = 8;
527 	var->green.length = 8;
528 	var->blue.offset = 16;
529 	var->blue.length = 8;
530 	var->transp.offset = 0;
531 	var->transp.length = 0;
532 }
533 
534 static void leo_unmap_regs(struct platform_device *op, struct fb_info *info,
535 			   struct leo_par *par)
536 {
537 	if (par->lc_ss0_usr)
538 		of_iounmap(&op->resource[0], par->lc_ss0_usr, 0x1000);
539 	if (par->ld_ss0)
540 		of_iounmap(&op->resource[0], par->ld_ss0, 0x1000);
541 	if (par->ld_ss1)
542 		of_iounmap(&op->resource[0], par->ld_ss1, 0x1000);
543 	if (par->lx_krn)
544 		of_iounmap(&op->resource[0], par->lx_krn, 0x1000);
545 	if (par->cursor)
546 		of_iounmap(&op->resource[0],
547 			   par->cursor, sizeof(struct leo_cursor));
548 	if (info->screen_base)
549 		of_iounmap(&op->resource[0], info->screen_base, 0x800000);
550 }
551 
552 static int leo_probe(struct platform_device *op)
553 {
554 	struct device_node *dp = op->dev.of_node;
555 	struct fb_info *info;
556 	struct leo_par *par;
557 	int linebytes, err;
558 
559 	info = framebuffer_alloc(sizeof(struct leo_par), &op->dev);
560 
561 	err = -ENOMEM;
562 	if (!info)
563 		goto out_err;
564 	par = info->par;
565 
566 	spin_lock_init(&par->lock);
567 
568 	info->fix.smem_start = op->resource[0].start;
569 	par->which_io = op->resource[0].flags & IORESOURCE_BITS;
570 
571 	sbusfb_fill_var(&info->var, dp, 32);
572 	leo_fixup_var_rgb(&info->var);
573 
574 	linebytes = of_getintprop_default(dp, "linebytes",
575 					  info->var.xres);
576 	info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres);
577 
578 	par->lc_ss0_usr =
579 		of_ioremap(&op->resource[0], LEO_OFF_LC_SS0_USR,
580 			   0x1000, "leolc ss0usr");
581 	par->ld_ss0 =
582 		of_ioremap(&op->resource[0], LEO_OFF_LD_SS0,
583 			   0x1000, "leold ss0");
584 	par->ld_ss1 =
585 		of_ioremap(&op->resource[0], LEO_OFF_LD_SS1,
586 			   0x1000, "leold ss1");
587 	par->lx_krn =
588 		of_ioremap(&op->resource[0], LEO_OFF_LX_KRN,
589 			   0x1000, "leolx krn");
590 	par->cursor =
591 		of_ioremap(&op->resource[0], LEO_OFF_LX_CURSOR,
592 			   sizeof(struct leo_cursor), "leolx cursor");
593 	info->screen_base =
594 		of_ioremap(&op->resource[0], LEO_OFF_SS0,
595 			   0x800000, "leo ram");
596 	if (!par->lc_ss0_usr ||
597 	    !par->ld_ss0 ||
598 	    !par->ld_ss1 ||
599 	    !par->lx_krn ||
600 	    !par->cursor ||
601 	    !info->screen_base)
602 		goto out_unmap_regs;
603 
604 	info->flags = FBINFO_DEFAULT;
605 	info->fbops = &leo_ops;
606 	info->pseudo_palette = par->clut_data;
607 
608 	leo_init_wids(info);
609 	leo_init_hw(info);
610 
611 	leo_blank(FB_BLANK_UNBLANK, info);
612 
613 	if (fb_alloc_cmap(&info->cmap, 256, 0))
614 		goto out_unmap_regs;
615 
616 	leo_init_fix(info, dp);
617 
618 	err = register_framebuffer(info);
619 	if (err < 0)
620 		goto out_dealloc_cmap;
621 
622 	dev_set_drvdata(&op->dev, info);
623 
624 	printk(KERN_INFO "%pOF: leo at %lx:%lx\n",
625 	       dp,
626 	       par->which_io, info->fix.smem_start);
627 
628 	return 0;
629 
630 out_dealloc_cmap:
631 	fb_dealloc_cmap(&info->cmap);
632 
633 out_unmap_regs:
634 	leo_unmap_regs(op, info, par);
635 	framebuffer_release(info);
636 
637 out_err:
638 	return err;
639 }
640 
641 static void leo_remove(struct platform_device *op)
642 {
643 	struct fb_info *info = dev_get_drvdata(&op->dev);
644 	struct leo_par *par = info->par;
645 
646 	unregister_framebuffer(info);
647 	fb_dealloc_cmap(&info->cmap);
648 
649 	leo_unmap_regs(op, info, par);
650 
651 	framebuffer_release(info);
652 }
653 
654 static const struct of_device_id leo_match[] = {
655 	{
656 		.name = "SUNW,leo",
657 	},
658 	{},
659 };
660 MODULE_DEVICE_TABLE(of, leo_match);
661 
662 static struct platform_driver leo_driver = {
663 	.driver = {
664 		.name = "leo",
665 		.of_match_table = leo_match,
666 	},
667 	.probe		= leo_probe,
668 	.remove_new	= leo_remove,
669 };
670 
671 static int __init leo_init(void)
672 {
673 	if (fb_get_options("leofb", NULL))
674 		return -ENODEV;
675 
676 	return platform_driver_register(&leo_driver);
677 }
678 
679 static void __exit leo_exit(void)
680 {
681 	platform_driver_unregister(&leo_driver);
682 }
683 
684 module_init(leo_init);
685 module_exit(leo_exit);
686 
687 MODULE_DESCRIPTION("framebuffer driver for LEO chipsets");
688 MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
689 MODULE_VERSION("2.0");
690 MODULE_LICENSE("GPL");
691