162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* cg3.c: CGTHREE frame buffer driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net) 562306a36Sopenharmony_ci * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz) 662306a36Sopenharmony_ci * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) 762306a36Sopenharmony_ci * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Driver layout based loosely on tgafb.c, see that file for credits. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/init.h> 1862306a36Sopenharmony_ci#include <linux/fb.h> 1962306a36Sopenharmony_ci#include <linux/mm.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <asm/io.h> 2462306a36Sopenharmony_ci#include <asm/fbio.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "sbuslib.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Local functions. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int cg3_setcolreg(unsigned, unsigned, unsigned, unsigned, 3362306a36Sopenharmony_ci unsigned, struct fb_info *); 3462306a36Sopenharmony_cistatic int cg3_blank(int, struct fb_info *); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int cg3_mmap(struct fb_info *, struct vm_area_struct *); 3762306a36Sopenharmony_cistatic int cg3_ioctl(struct fb_info *, unsigned int, unsigned long); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * Frame buffer operations 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic const struct fb_ops cg3_ops = { 4462306a36Sopenharmony_ci .owner = THIS_MODULE, 4562306a36Sopenharmony_ci .fb_setcolreg = cg3_setcolreg, 4662306a36Sopenharmony_ci .fb_blank = cg3_blank, 4762306a36Sopenharmony_ci .fb_fillrect = cfb_fillrect, 4862306a36Sopenharmony_ci .fb_copyarea = cfb_copyarea, 4962306a36Sopenharmony_ci .fb_imageblit = cfb_imageblit, 5062306a36Sopenharmony_ci .fb_mmap = cg3_mmap, 5162306a36Sopenharmony_ci .fb_ioctl = cg3_ioctl, 5262306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 5362306a36Sopenharmony_ci .fb_compat_ioctl = sbusfb_compat_ioctl, 5462306a36Sopenharmony_ci#endif 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* Control Register Constants */ 5962306a36Sopenharmony_ci#define CG3_CR_ENABLE_INTS 0x80 6062306a36Sopenharmony_ci#define CG3_CR_ENABLE_VIDEO 0x40 6162306a36Sopenharmony_ci#define CG3_CR_ENABLE_TIMING 0x20 6262306a36Sopenharmony_ci#define CG3_CR_ENABLE_CURCMP 0x10 6362306a36Sopenharmony_ci#define CG3_CR_XTAL_MASK 0x0c 6462306a36Sopenharmony_ci#define CG3_CR_DIVISOR_MASK 0x03 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* Status Register Constants */ 6762306a36Sopenharmony_ci#define CG3_SR_PENDING_INT 0x80 6862306a36Sopenharmony_ci#define CG3_SR_RES_MASK 0x70 6962306a36Sopenharmony_ci#define CG3_SR_1152_900_76_A 0x40 7062306a36Sopenharmony_ci#define CG3_SR_1152_900_76_B 0x60 7162306a36Sopenharmony_ci#define CG3_SR_ID_MASK 0x0f 7262306a36Sopenharmony_ci#define CG3_SR_ID_COLOR 0x01 7362306a36Sopenharmony_ci#define CG3_SR_ID_MONO 0x02 7462306a36Sopenharmony_ci#define CG3_SR_ID_MONO_ECL 0x03 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cienum cg3_type { 7762306a36Sopenharmony_ci CG3_AT_66HZ = 0, 7862306a36Sopenharmony_ci CG3_AT_76HZ, 7962306a36Sopenharmony_ci CG3_RDI 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistruct bt_regs { 8362306a36Sopenharmony_ci u32 addr; 8462306a36Sopenharmony_ci u32 color_map; 8562306a36Sopenharmony_ci u32 control; 8662306a36Sopenharmony_ci u32 cursor; 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistruct cg3_regs { 9062306a36Sopenharmony_ci struct bt_regs cmap; 9162306a36Sopenharmony_ci u8 control; 9262306a36Sopenharmony_ci u8 status; 9362306a36Sopenharmony_ci u8 cursor_start; 9462306a36Sopenharmony_ci u8 cursor_end; 9562306a36Sopenharmony_ci u8 h_blank_start; 9662306a36Sopenharmony_ci u8 h_blank_end; 9762306a36Sopenharmony_ci u8 h_sync_start; 9862306a36Sopenharmony_ci u8 h_sync_end; 9962306a36Sopenharmony_ci u8 comp_sync_end; 10062306a36Sopenharmony_ci u8 v_blank_start_high; 10162306a36Sopenharmony_ci u8 v_blank_start_low; 10262306a36Sopenharmony_ci u8 v_blank_end; 10362306a36Sopenharmony_ci u8 v_sync_start; 10462306a36Sopenharmony_ci u8 v_sync_end; 10562306a36Sopenharmony_ci u8 xfer_holdoff_start; 10662306a36Sopenharmony_ci u8 xfer_holdoff_end; 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* Offset of interesting structures in the OBIO space */ 11062306a36Sopenharmony_ci#define CG3_REGS_OFFSET 0x400000UL 11162306a36Sopenharmony_ci#define CG3_RAM_OFFSET 0x800000UL 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistruct cg3_par { 11462306a36Sopenharmony_ci spinlock_t lock; 11562306a36Sopenharmony_ci struct cg3_regs __iomem *regs; 11662306a36Sopenharmony_ci u32 sw_cmap[((256 * 3) + 3) / 4]; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci u32 flags; 11962306a36Sopenharmony_ci#define CG3_FLAG_BLANKED 0x00000001 12062306a36Sopenharmony_ci#define CG3_FLAG_RDI 0x00000002 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci unsigned long which_io; 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/** 12662306a36Sopenharmony_ci * cg3_setcolreg - Optional function. Sets a color register. 12762306a36Sopenharmony_ci * @regno: boolean, 0 copy local, 1 get_user() function 12862306a36Sopenharmony_ci * @red: frame buffer colormap structure 12962306a36Sopenharmony_ci * @green: The green value which can be up to 16 bits wide 13062306a36Sopenharmony_ci * @blue: The blue value which can be up to 16 bits wide. 13162306a36Sopenharmony_ci * @transp: If supported the alpha value which can be up to 16 bits wide. 13262306a36Sopenharmony_ci * @info: frame buffer info structure 13362306a36Sopenharmony_ci * 13462306a36Sopenharmony_ci * The cg3 palette is loaded with 4 color values at each time 13562306a36Sopenharmony_ci * so you end up with: (rgb)(r), (gb)(rg), (b)(rgb), and so on. 13662306a36Sopenharmony_ci * We keep a sw copy of the hw cmap to assist us in this esoteric 13762306a36Sopenharmony_ci * loading procedure. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_cistatic int cg3_setcolreg(unsigned regno, 14062306a36Sopenharmony_ci unsigned red, unsigned green, unsigned blue, 14162306a36Sopenharmony_ci unsigned transp, struct fb_info *info) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct cg3_par *par = (struct cg3_par *) info->par; 14462306a36Sopenharmony_ci struct bt_regs __iomem *bt = &par->regs->cmap; 14562306a36Sopenharmony_ci unsigned long flags; 14662306a36Sopenharmony_ci u32 *p32; 14762306a36Sopenharmony_ci u8 *p8; 14862306a36Sopenharmony_ci int count; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (regno >= 256) 15162306a36Sopenharmony_ci return 1; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci red >>= 8; 15462306a36Sopenharmony_ci green >>= 8; 15562306a36Sopenharmony_ci blue >>= 8; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci spin_lock_irqsave(&par->lock, flags); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci p8 = (u8 *)par->sw_cmap + (regno * 3); 16062306a36Sopenharmony_ci p8[0] = red; 16162306a36Sopenharmony_ci p8[1] = green; 16262306a36Sopenharmony_ci p8[2] = blue; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#define D4M3(x) ((((x)>>2)<<1) + ((x)>>2)) /* (x/4)*3 */ 16562306a36Sopenharmony_ci#define D4M4(x) ((x)&~0x3) /* (x/4)*4 */ 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci count = 3; 16862306a36Sopenharmony_ci p32 = &par->sw_cmap[D4M3(regno)]; 16962306a36Sopenharmony_ci sbus_writel(D4M4(regno), &bt->addr); 17062306a36Sopenharmony_ci while (count--) 17162306a36Sopenharmony_ci sbus_writel(*p32++, &bt->color_map); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci#undef D4M3 17462306a36Sopenharmony_ci#undef D4M4 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci spin_unlock_irqrestore(&par->lock, flags); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/** 18262306a36Sopenharmony_ci * cg3_blank - Optional function. Blanks the display. 18362306a36Sopenharmony_ci * @blank: the blank mode we want. 18462306a36Sopenharmony_ci * @info: frame buffer structure that represents a single frame buffer 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_cistatic int cg3_blank(int blank, struct fb_info *info) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct cg3_par *par = (struct cg3_par *) info->par; 18962306a36Sopenharmony_ci struct cg3_regs __iomem *regs = par->regs; 19062306a36Sopenharmony_ci unsigned long flags; 19162306a36Sopenharmony_ci u8 val; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci spin_lock_irqsave(&par->lock, flags); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci switch (blank) { 19662306a36Sopenharmony_ci case FB_BLANK_UNBLANK: /* Unblanking */ 19762306a36Sopenharmony_ci val = sbus_readb(®s->control); 19862306a36Sopenharmony_ci val |= CG3_CR_ENABLE_VIDEO; 19962306a36Sopenharmony_ci sbus_writeb(val, ®s->control); 20062306a36Sopenharmony_ci par->flags &= ~CG3_FLAG_BLANKED; 20162306a36Sopenharmony_ci break; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci case FB_BLANK_NORMAL: /* Normal blanking */ 20462306a36Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */ 20562306a36Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */ 20662306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: /* Poweroff */ 20762306a36Sopenharmony_ci val = sbus_readb(®s->control); 20862306a36Sopenharmony_ci val &= ~CG3_CR_ENABLE_VIDEO; 20962306a36Sopenharmony_ci sbus_writeb(val, ®s->control); 21062306a36Sopenharmony_ci par->flags |= CG3_FLAG_BLANKED; 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci spin_unlock_irqrestore(&par->lock, flags); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic struct sbus_mmap_map cg3_mmap_map[] = { 22062306a36Sopenharmony_ci { 22162306a36Sopenharmony_ci .voff = CG3_MMAP_OFFSET, 22262306a36Sopenharmony_ci .poff = CG3_RAM_OFFSET, 22362306a36Sopenharmony_ci .size = SBUS_MMAP_FBSIZE(1) 22462306a36Sopenharmony_ci }, 22562306a36Sopenharmony_ci { .size = 0 } 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int cg3_mmap(struct fb_info *info, struct vm_area_struct *vma) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct cg3_par *par = (struct cg3_par *)info->par; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return sbusfb_mmap_helper(cg3_mmap_map, 23362306a36Sopenharmony_ci info->fix.smem_start, info->fix.smem_len, 23462306a36Sopenharmony_ci par->which_io, 23562306a36Sopenharmony_ci vma); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic int cg3_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci return sbusfb_ioctl_helper(cmd, arg, info, 24162306a36Sopenharmony_ci FBTYPE_SUN3COLOR, 8, info->fix.smem_len); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/* 24562306a36Sopenharmony_ci * Initialisation 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic void cg3_init_fix(struct fb_info *info, int linebytes, 24962306a36Sopenharmony_ci struct device_node *dp) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci snprintf(info->fix.id, sizeof(info->fix.id), "%pOFn", dp); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 25462306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci info->fix.line_length = linebytes; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci info->fix.accel = FB_ACCEL_SUN_CGTHREE; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic void cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo *var, 26262306a36Sopenharmony_ci struct device_node *dp) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci const char *params; 26562306a36Sopenharmony_ci char *p; 26662306a36Sopenharmony_ci int ww, hh; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci params = of_get_property(dp, "params", NULL); 26962306a36Sopenharmony_ci if (params) { 27062306a36Sopenharmony_ci ww = simple_strtoul(params, &p, 10); 27162306a36Sopenharmony_ci if (ww && *p == 'x') { 27262306a36Sopenharmony_ci hh = simple_strtoul(p + 1, &p, 10); 27362306a36Sopenharmony_ci if (hh && *p == '-') { 27462306a36Sopenharmony_ci if (var->xres != ww || 27562306a36Sopenharmony_ci var->yres != hh) { 27662306a36Sopenharmony_ci var->xres = var->xres_virtual = ww; 27762306a36Sopenharmony_ci var->yres = var->yres_virtual = hh; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic u8 cg3regvals_66hz[] = { /* 1152 x 900, 66 Hz */ 28562306a36Sopenharmony_ci 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14, 28662306a36Sopenharmony_ci 0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24, 28762306a36Sopenharmony_ci 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01, 28862306a36Sopenharmony_ci 0x10, 0x20, 0 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic u8 cg3regvals_76hz[] = { /* 1152 x 900, 76 Hz */ 29262306a36Sopenharmony_ci 0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f, 29362306a36Sopenharmony_ci 0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a, 29462306a36Sopenharmony_ci 0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01, 29562306a36Sopenharmony_ci 0x10, 0x24, 0 29662306a36Sopenharmony_ci}; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic u8 cg3regvals_rdi[] = { /* 640 x 480, cgRDI */ 29962306a36Sopenharmony_ci 0x14, 0x70, 0x15, 0x20, 0x16, 0x08, 0x17, 0x10, 30062306a36Sopenharmony_ci 0x18, 0x06, 0x19, 0x02, 0x1a, 0x31, 0x1b, 0x51, 30162306a36Sopenharmony_ci 0x1c, 0x06, 0x1d, 0x0c, 0x1e, 0xff, 0x1f, 0x01, 30262306a36Sopenharmony_ci 0x10, 0x22, 0 30362306a36Sopenharmony_ci}; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic u8 *cg3_regvals[] = { 30662306a36Sopenharmony_ci cg3regvals_66hz, cg3regvals_76hz, cg3regvals_rdi 30762306a36Sopenharmony_ci}; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic u_char cg3_dacvals[] = { 31062306a36Sopenharmony_ci 4, 0xff, 5, 0x00, 6, 0x70, 7, 0x00, 0 31162306a36Sopenharmony_ci}; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int cg3_do_default_mode(struct cg3_par *par) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci enum cg3_type type; 31662306a36Sopenharmony_ci u8 *p; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (par->flags & CG3_FLAG_RDI) 31962306a36Sopenharmony_ci type = CG3_RDI; 32062306a36Sopenharmony_ci else { 32162306a36Sopenharmony_ci u8 status = sbus_readb(&par->regs->status), mon; 32262306a36Sopenharmony_ci if ((status & CG3_SR_ID_MASK) == CG3_SR_ID_COLOR) { 32362306a36Sopenharmony_ci mon = status & CG3_SR_RES_MASK; 32462306a36Sopenharmony_ci if (mon == CG3_SR_1152_900_76_A || 32562306a36Sopenharmony_ci mon == CG3_SR_1152_900_76_B) 32662306a36Sopenharmony_ci type = CG3_AT_76HZ; 32762306a36Sopenharmony_ci else 32862306a36Sopenharmony_ci type = CG3_AT_66HZ; 32962306a36Sopenharmony_ci } else { 33062306a36Sopenharmony_ci printk(KERN_ERR "cgthree: can't handle SR %02x\n", 33162306a36Sopenharmony_ci status); 33262306a36Sopenharmony_ci return -EINVAL; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci for (p = cg3_regvals[type]; *p; p += 2) { 33762306a36Sopenharmony_ci u8 __iomem *regp = &((u8 __iomem *)par->regs)[p[0]]; 33862306a36Sopenharmony_ci sbus_writeb(p[1], regp); 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci for (p = cg3_dacvals; *p; p += 2) { 34162306a36Sopenharmony_ci u8 __iomem *regp; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci regp = (u8 __iomem *)&par->regs->cmap.addr; 34462306a36Sopenharmony_ci sbus_writeb(p[0], regp); 34562306a36Sopenharmony_ci regp = (u8 __iomem *)&par->regs->cmap.control; 34662306a36Sopenharmony_ci sbus_writeb(p[1], regp); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int cg3_probe(struct platform_device *op) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct device_node *dp = op->dev.of_node; 35462306a36Sopenharmony_ci struct fb_info *info; 35562306a36Sopenharmony_ci struct cg3_par *par; 35662306a36Sopenharmony_ci int linebytes, err; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(struct cg3_par), &op->dev); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci err = -ENOMEM; 36162306a36Sopenharmony_ci if (!info) 36262306a36Sopenharmony_ci goto out_err; 36362306a36Sopenharmony_ci par = info->par; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci spin_lock_init(&par->lock); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci info->fix.smem_start = op->resource[0].start; 36862306a36Sopenharmony_ci par->which_io = op->resource[0].flags & IORESOURCE_BITS; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci sbusfb_fill_var(&info->var, dp, 8); 37162306a36Sopenharmony_ci info->var.red.length = 8; 37262306a36Sopenharmony_ci info->var.green.length = 8; 37362306a36Sopenharmony_ci info->var.blue.length = 8; 37462306a36Sopenharmony_ci if (of_node_name_eq(dp, "cgRDI")) 37562306a36Sopenharmony_ci par->flags |= CG3_FLAG_RDI; 37662306a36Sopenharmony_ci if (par->flags & CG3_FLAG_RDI) 37762306a36Sopenharmony_ci cg3_rdi_maybe_fixup_var(&info->var, dp); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci linebytes = of_getintprop_default(dp, "linebytes", 38062306a36Sopenharmony_ci info->var.xres); 38162306a36Sopenharmony_ci info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci par->regs = of_ioremap(&op->resource[0], CG3_REGS_OFFSET, 38462306a36Sopenharmony_ci sizeof(struct cg3_regs), "cg3 regs"); 38562306a36Sopenharmony_ci if (!par->regs) 38662306a36Sopenharmony_ci goto out_release_fb; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci info->fbops = &cg3_ops; 38962306a36Sopenharmony_ci info->screen_base = of_ioremap(&op->resource[0], CG3_RAM_OFFSET, 39062306a36Sopenharmony_ci info->fix.smem_len, "cg3 ram"); 39162306a36Sopenharmony_ci if (!info->screen_base) 39262306a36Sopenharmony_ci goto out_unmap_regs; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci cg3_blank(FB_BLANK_UNBLANK, info); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (!of_property_present(dp, "width")) { 39762306a36Sopenharmony_ci err = cg3_do_default_mode(par); 39862306a36Sopenharmony_ci if (err) 39962306a36Sopenharmony_ci goto out_unmap_screen; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci err = fb_alloc_cmap(&info->cmap, 256, 0); 40362306a36Sopenharmony_ci if (err) 40462306a36Sopenharmony_ci goto out_unmap_screen; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci fb_set_cmap(&info->cmap, info); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci cg3_init_fix(info, linebytes, dp); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci err = register_framebuffer(info); 41162306a36Sopenharmony_ci if (err < 0) 41262306a36Sopenharmony_ci goto out_dealloc_cmap; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci dev_set_drvdata(&op->dev, info); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci printk(KERN_INFO "%pOF: cg3 at %lx:%lx\n", 41762306a36Sopenharmony_ci dp, par->which_io, info->fix.smem_start); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ciout_dealloc_cmap: 42262306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ciout_unmap_screen: 42562306a36Sopenharmony_ci of_iounmap(&op->resource[0], info->screen_base, info->fix.smem_len); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ciout_unmap_regs: 42862306a36Sopenharmony_ci of_iounmap(&op->resource[0], par->regs, sizeof(struct cg3_regs)); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ciout_release_fb: 43162306a36Sopenharmony_ci framebuffer_release(info); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ciout_err: 43462306a36Sopenharmony_ci return err; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic void cg3_remove(struct platform_device *op) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct fb_info *info = dev_get_drvdata(&op->dev); 44062306a36Sopenharmony_ci struct cg3_par *par = info->par; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci unregister_framebuffer(info); 44362306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci of_iounmap(&op->resource[0], par->regs, sizeof(struct cg3_regs)); 44662306a36Sopenharmony_ci of_iounmap(&op->resource[0], info->screen_base, info->fix.smem_len); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci framebuffer_release(info); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic const struct of_device_id cg3_match[] = { 45262306a36Sopenharmony_ci { 45362306a36Sopenharmony_ci .name = "cgthree", 45462306a36Sopenharmony_ci }, 45562306a36Sopenharmony_ci { 45662306a36Sopenharmony_ci .name = "cgRDI", 45762306a36Sopenharmony_ci }, 45862306a36Sopenharmony_ci {}, 45962306a36Sopenharmony_ci}; 46062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cg3_match); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic struct platform_driver cg3_driver = { 46362306a36Sopenharmony_ci .driver = { 46462306a36Sopenharmony_ci .name = "cg3", 46562306a36Sopenharmony_ci .of_match_table = cg3_match, 46662306a36Sopenharmony_ci }, 46762306a36Sopenharmony_ci .probe = cg3_probe, 46862306a36Sopenharmony_ci .remove_new = cg3_remove, 46962306a36Sopenharmony_ci}; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic int __init cg3_init(void) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci if (fb_get_options("cg3fb", NULL)) 47462306a36Sopenharmony_ci return -ENODEV; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return platform_driver_register(&cg3_driver); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic void __exit cg3_exit(void) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci platform_driver_unregister(&cg3_driver); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cimodule_init(cg3_init); 48562306a36Sopenharmony_cimodule_exit(cg3_exit); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ciMODULE_DESCRIPTION("framebuffer driver for CGthree chipsets"); 48862306a36Sopenharmony_ciMODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); 48962306a36Sopenharmony_ciMODULE_VERSION("2.0"); 49062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 491