cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

leo.c (15756B)


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