Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* leo.c: LEO frame buffer driver
0003  *
0004  * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net)
0005  * Copyright (C) 1996-1999 Jakub Jelinek (jj@ultra.linux.cz)
0006  * Copyright (C) 1997 Michal Rehacek (Michal.Rehacek@st.mff.cuni.cz)
0007  *
0008  * Driver layout based loosely on tgafb.c, see that file for credits.
0009  */
0010 
0011 #include <linux/module.h>
0012 #include <linux/kernel.h>
0013 #include <linux/errno.h>
0014 #include <linux/string.h>
0015 #include <linux/delay.h>
0016 #include <linux/init.h>
0017 #include <linux/fb.h>
0018 #include <linux/mm.h>
0019 #include <linux/of_device.h>
0020 #include <linux/io.h>
0021 
0022 #include <asm/fbio.h>
0023 
0024 #include "sbuslib.h"
0025 
0026 /*
0027  * Local functions.
0028  */
0029 
0030 static int leo_setcolreg(unsigned, unsigned, unsigned, unsigned,
0031              unsigned, struct fb_info *);
0032 static int leo_blank(int, struct fb_info *);
0033 
0034 static int leo_mmap(struct fb_info *, struct vm_area_struct *);
0035 static int leo_ioctl(struct fb_info *, unsigned int, unsigned long);
0036 static int leo_pan_display(struct fb_var_screeninfo *, struct fb_info *);
0037 
0038 /*
0039  *  Frame buffer operations
0040  */
0041 
0042 static const struct fb_ops leo_ops = {
0043     .owner          = THIS_MODULE,
0044     .fb_setcolreg       = leo_setcolreg,
0045     .fb_blank       = leo_blank,
0046     .fb_pan_display     = leo_pan_display,
0047     .fb_fillrect        = cfb_fillrect,
0048     .fb_copyarea        = cfb_copyarea,
0049     .fb_imageblit       = cfb_imageblit,
0050     .fb_mmap        = leo_mmap,
0051     .fb_ioctl       = leo_ioctl,
0052 #ifdef CONFIG_COMPAT
0053     .fb_compat_ioctl    = sbusfb_compat_ioctl,
0054 #endif
0055 };
0056 
0057 #define LEO_OFF_LC_SS0_KRN  0x00200000UL
0058 #define LEO_OFF_LC_SS0_USR  0x00201000UL
0059 #define LEO_OFF_LC_SS1_KRN  0x01200000UL
0060 #define LEO_OFF_LC_SS1_USR  0x01201000UL
0061 #define LEO_OFF_LD_SS0      0x00400000UL
0062 #define LEO_OFF_LD_SS1      0x01400000UL
0063 #define LEO_OFF_LD_GBL      0x00401000UL
0064 #define LEO_OFF_LX_KRN      0x00600000UL
0065 #define LEO_OFF_LX_CURSOR   0x00601000UL
0066 #define LEO_OFF_SS0     0x00800000UL
0067 #define LEO_OFF_SS1     0x01800000UL
0068 #define LEO_OFF_UNK     0x00602000UL
0069 #define LEO_OFF_UNK2        0x00000000UL
0070 
0071 #define LEO_CUR_ENABLE      0x00000080
0072 #define LEO_CUR_UPDATE      0x00000030
0073 #define LEO_CUR_PROGRESS    0x00000006
0074 #define LEO_CUR_UPDATECMAP  0x00000003
0075 
0076 #define LEO_CUR_TYPE_MASK   0x00000000
0077 #define LEO_CUR_TYPE_IMAGE  0x00000020
0078 #define LEO_CUR_TYPE_CMAP   0x00000050
0079 
0080 struct leo_cursor {
0081     u8  xxx0[16];
0082     u32 cur_type;
0083     u32 cur_misc;
0084     u32 cur_cursxy;
0085     u32 cur_data;
0086 };
0087 
0088 #define LEO_KRN_TYPE_CLUT0  0x00001000
0089 #define LEO_KRN_TYPE_CLUT1  0x00001001
0090 #define LEO_KRN_TYPE_CLUT2  0x00001002
0091 #define LEO_KRN_TYPE_WID    0x00001003
0092 #define LEO_KRN_TYPE_UNK    0x00001006
0093 #define LEO_KRN_TYPE_VIDEO  0x00002003
0094 #define LEO_KRN_TYPE_CLUTDATA   0x00004000
0095 #define LEO_KRN_CSR_ENABLE  0x00000008
0096 #define LEO_KRN_CSR_PROGRESS    0x00000004
0097 #define LEO_KRN_CSR_UNK     0x00000002
0098 #define LEO_KRN_CSR_UNK2    0x00000001
0099 
0100 struct leo_lx_krn {
0101     u32 krn_type;
0102     u32 krn_csr;
0103     u32 krn_value;
0104 };
0105 
0106 struct leo_lc_ss0_krn {
0107     u32     misc;
0108     u8  xxx0[0x800-4];
0109     u32 rev;
0110 };
0111 
0112 struct leo_lc_ss0_usr {
0113     u32 csr;
0114     u32 addrspace;
0115     u32     fontmsk;
0116     u32 fontt;
0117     u32 extent;
0118     u32 src;
0119     u32 dst;
0120     u32 copy;
0121     u32 fill;
0122 };
0123 
0124 struct leo_lc_ss1_krn {
0125     u8  unknown;
0126 };
0127 
0128 struct leo_lc_ss1_usr {
0129     u8  unknown;
0130 };
0131 
0132 struct leo_ld_ss0 {
0133     u8  xxx0[0xe00];
0134     u32 csr;
0135     u32 wid;
0136     u32 wmask;
0137     u32 widclip;
0138     u32 vclipmin;
0139     u32 vclipmax;
0140     u32 pickmin;    /* SS1 only */
0141     u32 pickmax;    /* SS1 only */
0142     u32 fg;
0143     u32 bg;
0144     u32 src;        /* Copy/Scroll (SS0 only) */
0145     u32 dst;        /* Copy/Scroll/Fill (SS0 only) */
0146     u32 extent;     /* Copy/Scroll/Fill size (SS0 only) */
0147     u32 xxx1[3];
0148     u32 setsem;     /* SS1 only */
0149     u32 clrsem;     /* SS1 only */
0150     u32 clrpick;    /* SS1 only */
0151     u32 clrdat;     /* SS1 only */
0152     u32 alpha;      /* SS1 only */
0153     u8  xxx2[0x2c];
0154     u32 winbg;
0155     u32 planemask;
0156     u32 rop;
0157     u32 z;
0158     u32 dczf;       /* SS1 only */
0159     u32 dczb;       /* SS1 only */
0160     u32 dcs;        /* SS1 only */
0161     u32 dczs;       /* SS1 only */
0162     u32 pickfb;     /* SS1 only */
0163     u32 pickbb;     /* SS1 only */
0164     u32 dcfc;       /* SS1 only */
0165     u32 forcecol;   /* SS1 only */
0166     u32 door[8];    /* SS1 only */
0167     u32 pick[5];    /* SS1 only */
0168 };
0169 
0170 #define LEO_SS1_MISC_ENABLE 0x00000001
0171 #define LEO_SS1_MISC_STEREO 0x00000002
0172 struct leo_ld_ss1 {
0173     u8  xxx0[0xef4];
0174     u32 ss1_misc;
0175 };
0176 
0177 struct leo_ld_gbl {
0178     u8  unknown;
0179 };
0180 
0181 struct leo_par {
0182     spinlock_t      lock;
0183     struct leo_lx_krn   __iomem *lx_krn;
0184     struct leo_lc_ss0_usr   __iomem *lc_ss0_usr;
0185     struct leo_ld_ss0   __iomem *ld_ss0;
0186     struct leo_ld_ss1   __iomem *ld_ss1;
0187     struct leo_cursor   __iomem *cursor;
0188     u32         extent;
0189     u32         clut_data[256];
0190 
0191     u32         flags;
0192 #define LEO_FLAG_BLANKED    0x00000001
0193 
0194     unsigned long       which_io;
0195 };
0196 
0197 static void leo_wait(struct leo_lx_krn __iomem *lx_krn)
0198 {
0199     int i;
0200 
0201     for (i = 0;
0202          (sbus_readl(&lx_krn->krn_csr) & LEO_KRN_CSR_PROGRESS) &&
0203          i < 300000;
0204          i++)
0205         udelay(1); /* Busy wait at most 0.3 sec */
0206     return;
0207 }
0208 
0209 static void leo_switch_from_graph(struct fb_info *info)
0210 {
0211     struct leo_par *par = (struct leo_par *) info->par;
0212     struct leo_ld_ss0 __iomem *ss = par->ld_ss0;
0213     struct leo_cursor __iomem *cursor = par->cursor;
0214     unsigned long flags;
0215     u32 val;
0216 
0217     spin_lock_irqsave(&par->lock, flags);
0218 
0219     par->extent = ((info->var.xres - 1) |
0220                ((info->var.yres - 1) << 16));
0221 
0222     sbus_writel(0xffffffff, &ss->wid);
0223     sbus_writel(0xffff, &ss->wmask);
0224     sbus_writel(0, &ss->vclipmin);
0225     sbus_writel(par->extent, &ss->vclipmax);
0226     sbus_writel(0, &ss->fg);
0227     sbus_writel(0xff000000, &ss->planemask);
0228     sbus_writel(0x310850, &ss->rop);
0229     sbus_writel(0, &ss->widclip);
0230     sbus_writel((info->var.xres-1) | ((info->var.yres-1) << 11),
0231             &par->lc_ss0_usr->extent);
0232     sbus_writel(4, &par->lc_ss0_usr->addrspace);
0233     sbus_writel(0x80000000, &par->lc_ss0_usr->fill);
0234     sbus_writel(0, &par->lc_ss0_usr->fontt);
0235     do {
0236         val = sbus_readl(&par->lc_ss0_usr->csr);
0237     } while (val & 0x20000000);
0238 
0239     /* setup screen buffer for cfb_* functions */
0240     sbus_writel(1, &ss->wid);
0241     sbus_writel(0x00ffffff, &ss->planemask);
0242     sbus_writel(0x310b90, &ss->rop);
0243     sbus_writel(0, &par->lc_ss0_usr->addrspace);
0244 
0245     /* hide cursor */
0246     sbus_writel(sbus_readl(&cursor->cur_misc) & ~LEO_CUR_ENABLE, &cursor->cur_misc);
0247 
0248     spin_unlock_irqrestore(&par->lock, flags);
0249 }
0250 
0251 static int leo_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
0252 {
0253     /* We just use this to catch switches out of
0254      * graphics mode.
0255      */
0256     leo_switch_from_graph(info);
0257 
0258     if (var->xoffset || var->yoffset || var->vmode)
0259         return -EINVAL;
0260     return 0;
0261 }
0262 
0263 /**
0264  *      leo_setcolreg - Optional function. Sets a color register.
0265  *      @regno: boolean, 0 copy local, 1 get_user() function
0266  *      @red: frame buffer colormap structure
0267  *      @green: The green value which can be up to 16 bits wide
0268  *      @blue:  The blue value which can be up to 16 bits wide.
0269  *      @transp: If supported the alpha value which can be up to 16 bits wide.
0270  *      @info: frame buffer info structure
0271  */
0272 static int leo_setcolreg(unsigned regno,
0273              unsigned red, unsigned green, unsigned blue,
0274              unsigned transp, struct fb_info *info)
0275 {
0276     struct leo_par *par = (struct leo_par *) info->par;
0277     struct leo_lx_krn __iomem *lx_krn = par->lx_krn;
0278     unsigned long flags;
0279     u32 val;
0280     int i;
0281 
0282     if (regno >= 256)
0283         return 1;
0284 
0285     red >>= 8;
0286     green >>= 8;
0287     blue >>= 8;
0288 
0289     par->clut_data[regno] = red | (green << 8) | (blue << 16);
0290 
0291     spin_lock_irqsave(&par->lock, flags);
0292 
0293     leo_wait(lx_krn);
0294 
0295     sbus_writel(LEO_KRN_TYPE_CLUTDATA, &lx_krn->krn_type);
0296     for (i = 0; i < 256; i++)
0297         sbus_writel(par->clut_data[i], &lx_krn->krn_value);
0298     sbus_writel(LEO_KRN_TYPE_CLUT0, &lx_krn->krn_type);
0299 
0300     val = sbus_readl(&lx_krn->krn_csr);
0301     val |= (LEO_KRN_CSR_UNK | LEO_KRN_CSR_UNK2);
0302     sbus_writel(val, &lx_krn->krn_csr);
0303 
0304     spin_unlock_irqrestore(&par->lock, flags);
0305 
0306     return 0;
0307 }
0308 
0309 /**
0310  *      leo_blank - Optional function.  Blanks the display.
0311  *      @blank: the blank mode we want.
0312  *      @info: frame buffer structure that represents a single frame buffer
0313  */
0314 static int leo_blank(int blank, struct fb_info *info)
0315 {
0316     struct leo_par *par = (struct leo_par *) info->par;
0317     struct leo_lx_krn __iomem *lx_krn = par->lx_krn;
0318     unsigned long flags;
0319     u32 val;
0320 
0321     spin_lock_irqsave(&par->lock, flags);
0322 
0323     switch (blank) {
0324     case FB_BLANK_UNBLANK: /* Unblanking */
0325         val = sbus_readl(&lx_krn->krn_csr);
0326         val |= LEO_KRN_CSR_ENABLE;
0327         sbus_writel(val, &lx_krn->krn_csr);
0328         par->flags &= ~LEO_FLAG_BLANKED;
0329         break;
0330 
0331     case FB_BLANK_NORMAL: /* Normal blanking */
0332     case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
0333     case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
0334     case FB_BLANK_POWERDOWN: /* Poweroff */
0335         val = sbus_readl(&lx_krn->krn_csr);
0336         val &= ~LEO_KRN_CSR_ENABLE;
0337         sbus_writel(val, &lx_krn->krn_csr);
0338         par->flags |= LEO_FLAG_BLANKED;
0339         break;
0340     }
0341 
0342     spin_unlock_irqrestore(&par->lock, flags);
0343 
0344     return 0;
0345 }
0346 
0347 static struct sbus_mmap_map leo_mmap_map[] = {
0348     {
0349         .voff   = LEO_SS0_MAP,
0350         .poff   = LEO_OFF_SS0,
0351         .size   = 0x800000
0352     },
0353     {
0354         .voff   = LEO_LC_SS0_USR_MAP,
0355         .poff   = LEO_OFF_LC_SS0_USR,
0356         .size   = 0x1000
0357     },
0358     {
0359         .voff   = LEO_LD_SS0_MAP,
0360         .poff   = LEO_OFF_LD_SS0,
0361         .size   = 0x1000
0362     },
0363     {
0364         .voff   = LEO_LX_CURSOR_MAP,
0365         .poff   = LEO_OFF_LX_CURSOR,
0366         .size   = 0x1000
0367     },
0368     {
0369         .voff   = LEO_SS1_MAP,
0370         .poff   = LEO_OFF_SS1,
0371         .size   = 0x800000
0372     },
0373     {
0374         .voff   = LEO_LC_SS1_USR_MAP,
0375         .poff   = LEO_OFF_LC_SS1_USR,
0376         .size   = 0x1000
0377     },
0378     {
0379         .voff   = LEO_LD_SS1_MAP,
0380         .poff   = LEO_OFF_LD_SS1,
0381         .size   = 0x1000
0382     },
0383     {
0384         .voff   = LEO_UNK_MAP,
0385         .poff   = LEO_OFF_UNK,
0386         .size   = 0x1000
0387     },
0388     {
0389         .voff   = LEO_LX_KRN_MAP,
0390         .poff   = LEO_OFF_LX_KRN,
0391         .size   = 0x1000
0392     },
0393     {
0394         .voff   = LEO_LC_SS0_KRN_MAP,
0395         .poff   = LEO_OFF_LC_SS0_KRN,
0396         .size   = 0x1000
0397     },
0398     {
0399         .voff   = LEO_LC_SS1_KRN_MAP,
0400         .poff   = LEO_OFF_LC_SS1_KRN,
0401         .size   = 0x1000
0402     },
0403     {
0404         .voff   = LEO_LD_GBL_MAP,
0405         .poff   = LEO_OFF_LD_GBL,
0406         .size   = 0x1000
0407     },
0408     {
0409         .voff   = LEO_UNK2_MAP,
0410         .poff   = LEO_OFF_UNK2,
0411         .size   = 0x100000
0412     },
0413     { .size = 0 }
0414 };
0415 
0416 static int leo_mmap(struct fb_info *info, struct vm_area_struct *vma)
0417 {
0418     struct leo_par *par = (struct leo_par *)info->par;
0419 
0420     return sbusfb_mmap_helper(leo_mmap_map,
0421                   info->fix.smem_start, info->fix.smem_len,
0422                   par->which_io, vma);
0423 }
0424 
0425 static int leo_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
0426 {
0427     return sbusfb_ioctl_helper(cmd, arg, info,
0428                    FBTYPE_SUNLEO, 32, info->fix.smem_len);
0429 }
0430 
0431 /*
0432  *  Initialisation
0433  */
0434 
0435 static void
0436 leo_init_fix(struct fb_info *info, struct device_node *dp)
0437 {
0438     snprintf(info->fix.id, sizeof(info->fix.id), "%pOFn", dp);
0439 
0440     info->fix.type = FB_TYPE_PACKED_PIXELS;
0441     info->fix.visual = FB_VISUAL_TRUECOLOR;
0442 
0443     info->fix.line_length = 8192;
0444 
0445     info->fix.accel = FB_ACCEL_SUN_LEO;
0446 }
0447 
0448 static void leo_wid_put(struct fb_info *info, struct fb_wid_list *wl)
0449 {
0450     struct leo_par *par = (struct leo_par *) info->par;
0451     struct leo_lx_krn __iomem *lx_krn = par->lx_krn;
0452     struct fb_wid_item *wi;
0453     unsigned long flags;
0454     u32 val;
0455     int i, j;
0456 
0457     spin_lock_irqsave(&par->lock, flags);
0458 
0459     leo_wait(lx_krn);
0460 
0461     for (i = 0, wi = wl->wl_list; i < wl->wl_count; i++, wi++) {
0462         switch (wi->wi_type) {
0463         case FB_WID_DBL_8:
0464             j = (wi->wi_index & 0xf) + 0x40;
0465             break;
0466 
0467         case FB_WID_DBL_24:
0468             j = wi->wi_index & 0x3f;
0469             break;
0470 
0471         default:
0472             continue;
0473         }
0474         sbus_writel(0x5800 + j, &lx_krn->krn_type);
0475         sbus_writel(wi->wi_values[0], &lx_krn->krn_value);
0476     }
0477     sbus_writel(LEO_KRN_TYPE_WID, &lx_krn->krn_type);
0478 
0479     val = sbus_readl(&lx_krn->krn_csr);
0480     val |= (LEO_KRN_CSR_UNK | LEO_KRN_CSR_UNK2);
0481     sbus_writel(val, &lx_krn->krn_csr);
0482 
0483     spin_unlock_irqrestore(&par->lock, flags);
0484 }
0485 
0486 static void leo_init_wids(struct fb_info *info)
0487 {
0488     struct fb_wid_item wi;
0489     struct fb_wid_list wl;
0490 
0491     wl.wl_count = 1;
0492     wl.wl_list = &wi;
0493     wi.wi_type = FB_WID_DBL_8;
0494     wi.wi_index = 0;
0495     wi.wi_values [0] = 0x2c0;
0496     leo_wid_put(info, &wl);
0497     wi.wi_index = 1;
0498     wi.wi_values [0] = 0x30;
0499     leo_wid_put(info, &wl);
0500     wi.wi_index = 2;
0501     wi.wi_values [0] = 0x20;
0502     leo_wid_put(info, &wl);
0503     wi.wi_type = FB_WID_DBL_24;
0504     wi.wi_index = 1;
0505     wi.wi_values [0] = 0x30;
0506     leo_wid_put(info, &wl);
0507 }
0508 
0509 static void leo_init_hw(struct fb_info *info)
0510 {
0511     struct leo_par *par = (struct leo_par *) info->par;
0512     u32 val;
0513 
0514     val = sbus_readl(&par->ld_ss1->ss1_misc);
0515     val |= LEO_SS1_MISC_ENABLE;
0516     sbus_writel(val, &par->ld_ss1->ss1_misc);
0517 
0518     leo_switch_from_graph(info);
0519 }
0520 
0521 static void leo_fixup_var_rgb(struct fb_var_screeninfo *var)
0522 {
0523     var->red.offset = 0;
0524     var->red.length = 8;
0525     var->green.offset = 8;
0526     var->green.length = 8;
0527     var->blue.offset = 16;
0528     var->blue.length = 8;
0529     var->transp.offset = 0;
0530     var->transp.length = 0;
0531 }
0532 
0533 static void leo_unmap_regs(struct platform_device *op, struct fb_info *info,
0534                struct leo_par *par)
0535 {
0536     if (par->lc_ss0_usr)
0537         of_iounmap(&op->resource[0], par->lc_ss0_usr, 0x1000);
0538     if (par->ld_ss0)
0539         of_iounmap(&op->resource[0], par->ld_ss0, 0x1000);
0540     if (par->ld_ss1)
0541         of_iounmap(&op->resource[0], par->ld_ss1, 0x1000);
0542     if (par->lx_krn)
0543         of_iounmap(&op->resource[0], par->lx_krn, 0x1000);
0544     if (par->cursor)
0545         of_iounmap(&op->resource[0],
0546                par->cursor, sizeof(struct leo_cursor));
0547     if (info->screen_base)
0548         of_iounmap(&op->resource[0], info->screen_base, 0x800000);
0549 }
0550 
0551 static int leo_probe(struct platform_device *op)
0552 {
0553     struct device_node *dp = op->dev.of_node;
0554     struct fb_info *info;
0555     struct leo_par *par;
0556     int linebytes, err;
0557 
0558     info = framebuffer_alloc(sizeof(struct leo_par), &op->dev);
0559 
0560     err = -ENOMEM;
0561     if (!info)
0562         goto out_err;
0563     par = info->par;
0564 
0565     spin_lock_init(&par->lock);
0566 
0567     info->fix.smem_start = op->resource[0].start;
0568     par->which_io = op->resource[0].flags & IORESOURCE_BITS;
0569 
0570     sbusfb_fill_var(&info->var, dp, 32);
0571     leo_fixup_var_rgb(&info->var);
0572 
0573     linebytes = of_getintprop_default(dp, "linebytes",
0574                       info->var.xres);
0575     info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres);
0576 
0577     par->lc_ss0_usr =
0578         of_ioremap(&op->resource[0], LEO_OFF_LC_SS0_USR,
0579                0x1000, "leolc ss0usr");
0580     par->ld_ss0 =
0581         of_ioremap(&op->resource[0], LEO_OFF_LD_SS0,
0582                0x1000, "leold ss0");
0583     par->ld_ss1 =
0584         of_ioremap(&op->resource[0], LEO_OFF_LD_SS1,
0585                0x1000, "leold ss1");
0586     par->lx_krn =
0587         of_ioremap(&op->resource[0], LEO_OFF_LX_KRN,
0588                0x1000, "leolx krn");
0589     par->cursor =
0590         of_ioremap(&op->resource[0], LEO_OFF_LX_CURSOR,
0591                sizeof(struct leo_cursor), "leolx cursor");
0592     info->screen_base =
0593         of_ioremap(&op->resource[0], LEO_OFF_SS0,
0594                0x800000, "leo ram");
0595     if (!par->lc_ss0_usr ||
0596         !par->ld_ss0 ||
0597         !par->ld_ss1 ||
0598         !par->lx_krn ||
0599         !par->cursor ||
0600         !info->screen_base)
0601         goto out_unmap_regs;
0602 
0603     info->flags = FBINFO_DEFAULT;
0604     info->fbops = &leo_ops;
0605     info->pseudo_palette = par->clut_data;
0606 
0607     leo_init_wids(info);
0608     leo_init_hw(info);
0609 
0610     leo_blank(FB_BLANK_UNBLANK, info);
0611 
0612     if (fb_alloc_cmap(&info->cmap, 256, 0))
0613         goto out_unmap_regs;
0614 
0615     leo_init_fix(info, dp);
0616 
0617     err = register_framebuffer(info);
0618     if (err < 0)
0619         goto out_dealloc_cmap;
0620 
0621     dev_set_drvdata(&op->dev, info);
0622 
0623     printk(KERN_INFO "%pOF: leo at %lx:%lx\n",
0624            dp,
0625            par->which_io, info->fix.smem_start);
0626 
0627     return 0;
0628 
0629 out_dealloc_cmap:
0630     fb_dealloc_cmap(&info->cmap);
0631 
0632 out_unmap_regs:
0633     leo_unmap_regs(op, info, par);
0634     framebuffer_release(info);
0635 
0636 out_err:
0637     return err;
0638 }
0639 
0640 static int leo_remove(struct platform_device *op)
0641 {
0642     struct fb_info *info = dev_get_drvdata(&op->dev);
0643     struct leo_par *par = info->par;
0644 
0645     unregister_framebuffer(info);
0646     fb_dealloc_cmap(&info->cmap);
0647 
0648     leo_unmap_regs(op, info, par);
0649 
0650     framebuffer_release(info);
0651 
0652     return 0;
0653 }
0654 
0655 static const struct of_device_id leo_match[] = {
0656     {
0657         .name = "SUNW,leo",
0658     },
0659     {},
0660 };
0661 MODULE_DEVICE_TABLE(of, leo_match);
0662 
0663 static struct platform_driver leo_driver = {
0664     .driver = {
0665         .name = "leo",
0666         .of_match_table = leo_match,
0667     },
0668     .probe      = leo_probe,
0669     .remove     = leo_remove,
0670 };
0671 
0672 static int __init leo_init(void)
0673 {
0674     if (fb_get_options("leofb", NULL))
0675         return -ENODEV;
0676 
0677     return platform_driver_register(&leo_driver);
0678 }
0679 
0680 static void __exit leo_exit(void)
0681 {
0682     platform_driver_unregister(&leo_driver);
0683 }
0684 
0685 module_init(leo_init);
0686 module_exit(leo_exit);
0687 
0688 MODULE_DESCRIPTION("framebuffer driver for LEO chipsets");
0689 MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
0690 MODULE_VERSION("2.0");
0691 MODULE_LICENSE("GPL");