1/* p9100.c: P9100 frame buffer driver 2 * 3 * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net) 4 * Copyright 1999 Derrick J Brashear (shadow@dementia.org) 5 * 6 * Driver layout based loosely on tgafb.c, see that file for credits. 7 */ 8 9#include <linux/module.h> 10#include <linux/kernel.h> 11#include <linux/errno.h> 12#include <linux/string.h> 13#include <linux/delay.h> 14#include <linux/init.h> 15#include <linux/fb.h> 16#include <linux/mm.h> 17#include <linux/of_device.h> 18 19#include <asm/io.h> 20#include <asm/fbio.h> 21 22#include "sbuslib.h" 23 24/* 25 * Local functions. 26 */ 27 28static int p9100_setcolreg(unsigned, unsigned, unsigned, unsigned, 29 unsigned, struct fb_info *); 30static int p9100_blank(int, struct fb_info *); 31 32static int p9100_mmap(struct fb_info *, struct vm_area_struct *); 33static int p9100_ioctl(struct fb_info *, unsigned int, unsigned long); 34 35/* 36 * Frame buffer operations 37 */ 38 39static struct fb_ops p9100_ops = { 40 .owner = THIS_MODULE, 41 .fb_setcolreg = p9100_setcolreg, 42 .fb_blank = p9100_blank, 43 .fb_fillrect = cfb_fillrect, 44 .fb_copyarea = cfb_copyarea, 45 .fb_imageblit = cfb_imageblit, 46 .fb_mmap = p9100_mmap, 47 .fb_ioctl = p9100_ioctl, 48#ifdef CONFIG_COMPAT 49 .fb_compat_ioctl = sbusfb_compat_ioctl, 50#endif 51}; 52 53/* P9100 control registers */ 54#define P9100_SYSCTL_OFF 0x0UL 55#define P9100_VIDEOCTL_OFF 0x100UL 56#define P9100_VRAMCTL_OFF 0x180UL 57#define P9100_RAMDAC_OFF 0x200UL 58#define P9100_VIDEOCOPROC_OFF 0x400UL 59 60/* P9100 command registers */ 61#define P9100_CMD_OFF 0x0UL 62 63/* P9100 framebuffer memory */ 64#define P9100_FB_OFF 0x0UL 65 66/* 3 bits: 2=8bpp 3=16bpp 5=32bpp 7=24bpp */ 67#define SYS_CONFIG_PIXELSIZE_SHIFT 26 68 69#define SCREENPAINT_TIMECTL1_ENABLE_VIDEO 0x20 /* 0 = off, 1 = on */ 70 71struct p9100_regs { 72 /* Registers for the system control */ 73 u32 sys_base; 74 u32 sys_config; 75 u32 sys_intr; 76 u32 sys_int_ena; 77 u32 sys_alt_rd; 78 u32 sys_alt_wr; 79 u32 sys_xxx[58]; 80 81 /* Registers for the video control */ 82 u32 vid_base; 83 u32 vid_hcnt; 84 u32 vid_htotal; 85 u32 vid_hsync_rise; 86 u32 vid_hblank_rise; 87 u32 vid_hblank_fall; 88 u32 vid_hcnt_preload; 89 u32 vid_vcnt; 90 u32 vid_vlen; 91 u32 vid_vsync_rise; 92 u32 vid_vblank_rise; 93 u32 vid_vblank_fall; 94 u32 vid_vcnt_preload; 95 u32 vid_screenpaint_addr; 96 u32 vid_screenpaint_timectl1; 97 u32 vid_screenpaint_qsfcnt; 98 u32 vid_screenpaint_timectl2; 99 u32 vid_xxx[15]; 100 101 /* Registers for the video control */ 102 u32 vram_base; 103 u32 vram_memcfg; 104 u32 vram_refresh_pd; 105 u32 vram_refresh_cnt; 106 u32 vram_raslo_max; 107 u32 vram_raslo_cur; 108 u32 pwrup_cfg; 109 u32 vram_xxx[25]; 110 111 /* Registers for IBM RGB528 Palette */ 112 u32 ramdac_cmap_wridx; 113 u32 ramdac_palette_data; 114 u32 ramdac_pixel_mask; 115 u32 ramdac_palette_rdaddr; 116 u32 ramdac_idx_lo; 117 u32 ramdac_idx_hi; 118 u32 ramdac_idx_data; 119 u32 ramdac_idx_ctl; 120 u32 ramdac_xxx[1784]; 121}; 122 123struct p9100_cmd_parameng { 124 u32 parameng_status; 125 u32 parameng_bltcmd; 126 u32 parameng_quadcmd; 127}; 128 129struct p9100_par { 130 spinlock_t lock; 131 struct p9100_regs __iomem *regs; 132 133 u32 flags; 134#define P9100_FLAG_BLANKED 0x00000001 135 136 unsigned long which_io; 137}; 138 139/** 140 * p9100_setcolreg - Optional function. Sets a color register. 141 * @regno: boolean, 0 copy local, 1 get_user() function 142 * @red: frame buffer colormap structure 143 * @green: The green value which can be up to 16 bits wide 144 * @blue: The blue value which can be up to 16 bits wide. 145 * @transp: If supported the alpha value which can be up to 16 bits wide. 146 * @info: frame buffer info structure 147 */ 148static int p9100_setcolreg(unsigned regno, 149 unsigned red, unsigned green, unsigned blue, 150 unsigned transp, struct fb_info *info) 151{ 152 struct p9100_par *par = (struct p9100_par *) info->par; 153 struct p9100_regs __iomem *regs = par->regs; 154 unsigned long flags; 155 156 if (regno >= 256) 157 return 1; 158 159 red >>= 8; 160 green >>= 8; 161 blue >>= 8; 162 163 spin_lock_irqsave(&par->lock, flags); 164 165 sbus_writel((regno << 16), ®s->ramdac_cmap_wridx); 166 sbus_writel((red << 16), ®s->ramdac_palette_data); 167 sbus_writel((green << 16), ®s->ramdac_palette_data); 168 sbus_writel((blue << 16), ®s->ramdac_palette_data); 169 170 spin_unlock_irqrestore(&par->lock, flags); 171 172 return 0; 173} 174 175/** 176 * p9100_blank - Optional function. Blanks the display. 177 * @blank_mode: the blank mode we want. 178 * @info: frame buffer structure that represents a single frame buffer 179 */ 180static int 181p9100_blank(int blank, struct fb_info *info) 182{ 183 struct p9100_par *par = (struct p9100_par *) info->par; 184 struct p9100_regs __iomem *regs = par->regs; 185 unsigned long flags; 186 u32 val; 187 188 spin_lock_irqsave(&par->lock, flags); 189 190 switch (blank) { 191 case FB_BLANK_UNBLANK: /* Unblanking */ 192 val = sbus_readl(®s->vid_screenpaint_timectl1); 193 val |= SCREENPAINT_TIMECTL1_ENABLE_VIDEO; 194 sbus_writel(val, ®s->vid_screenpaint_timectl1); 195 par->flags &= ~P9100_FLAG_BLANKED; 196 break; 197 198 case FB_BLANK_NORMAL: /* Normal blanking */ 199 case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */ 200 case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */ 201 case FB_BLANK_POWERDOWN: /* Poweroff */ 202 val = sbus_readl(®s->vid_screenpaint_timectl1); 203 val &= ~SCREENPAINT_TIMECTL1_ENABLE_VIDEO; 204 sbus_writel(val, ®s->vid_screenpaint_timectl1); 205 par->flags |= P9100_FLAG_BLANKED; 206 break; 207 } 208 209 spin_unlock_irqrestore(&par->lock, flags); 210 211 return 0; 212} 213 214static struct sbus_mmap_map p9100_mmap_map[] = { 215 { CG3_MMAP_OFFSET, 0, SBUS_MMAP_FBSIZE(1) }, 216 { 0, 0, 0 } 217}; 218 219static int p9100_mmap(struct fb_info *info, struct vm_area_struct *vma) 220{ 221 struct p9100_par *par = (struct p9100_par *)info->par; 222 223 return sbusfb_mmap_helper(p9100_mmap_map, 224 info->fix.smem_start, info->fix.smem_len, 225 par->which_io, vma); 226} 227 228static int p9100_ioctl(struct fb_info *info, unsigned int cmd, 229 unsigned long arg) 230{ 231 /* Make it look like a cg3. */ 232 return sbusfb_ioctl_helper(cmd, arg, info, 233 FBTYPE_SUN3COLOR, 8, info->fix.smem_len); 234} 235 236/* 237 * Initialisation 238 */ 239 240static void p9100_init_fix(struct fb_info *info, int linebytes, struct device_node *dp) 241{ 242 strlcpy(info->fix.id, dp->name, sizeof(info->fix.id)); 243 244 info->fix.type = FB_TYPE_PACKED_PIXELS; 245 info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 246 247 info->fix.line_length = linebytes; 248 249 info->fix.accel = FB_ACCEL_SUN_CGTHREE; 250} 251 252static int p9100_probe(struct platform_device *op) 253{ 254 struct device_node *dp = op->dev.of_node; 255 struct fb_info *info; 256 struct p9100_par *par; 257 int linebytes, err; 258 259 info = framebuffer_alloc(sizeof(struct p9100_par), &op->dev); 260 261 err = -ENOMEM; 262 if (!info) 263 goto out_err; 264 par = info->par; 265 266 spin_lock_init(&par->lock); 267 268 /* This is the framebuffer and the only resource apps can mmap. */ 269 info->fix.smem_start = op->resource[2].start; 270 par->which_io = op->resource[2].flags & IORESOURCE_BITS; 271 272 sbusfb_fill_var(&info->var, dp, 8); 273 info->var.red.length = 8; 274 info->var.green.length = 8; 275 info->var.blue.length = 8; 276 277 linebytes = of_getintprop_default(dp, "linebytes", info->var.xres); 278 info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres); 279 280 par->regs = of_ioremap(&op->resource[0], 0, 281 sizeof(struct p9100_regs), "p9100 regs"); 282 if (!par->regs) 283 goto out_release_fb; 284 285 info->flags = FBINFO_DEFAULT; 286 info->fbops = &p9100_ops; 287 info->screen_base = of_ioremap(&op->resource[2], 0, 288 info->fix.smem_len, "p9100 ram"); 289 if (!info->screen_base) 290 goto out_unmap_regs; 291 292 p9100_blank(FB_BLANK_UNBLANK, info); 293 294 if (fb_alloc_cmap(&info->cmap, 256, 0)) 295 goto out_unmap_screen; 296 297 p9100_init_fix(info, linebytes, dp); 298 299 err = register_framebuffer(info); 300 if (err < 0) 301 goto out_dealloc_cmap; 302 303 fb_set_cmap(&info->cmap, info); 304 305 dev_set_drvdata(&op->dev, info); 306 307 printk(KERN_INFO "%s: p9100 at %lx:%lx\n", 308 dp->full_name, 309 par->which_io, info->fix.smem_start); 310 311 return 0; 312 313out_dealloc_cmap: 314 fb_dealloc_cmap(&info->cmap); 315 316out_unmap_screen: 317 of_iounmap(&op->resource[2], info->screen_base, info->fix.smem_len); 318 319out_unmap_regs: 320 of_iounmap(&op->resource[0], par->regs, sizeof(struct p9100_regs)); 321 322out_release_fb: 323 framebuffer_release(info); 324 325out_err: 326 return err; 327} 328 329static int p9100_remove(struct platform_device *op) 330{ 331 struct fb_info *info = dev_get_drvdata(&op->dev); 332 struct p9100_par *par = info->par; 333 334 unregister_framebuffer(info); 335 fb_dealloc_cmap(&info->cmap); 336 337 of_iounmap(&op->resource[0], par->regs, sizeof(struct p9100_regs)); 338 of_iounmap(&op->resource[2], info->screen_base, info->fix.smem_len); 339 340 framebuffer_release(info); 341 342 return 0; 343} 344 345static const struct of_device_id p9100_match[] = { 346 { 347 .name = "p9100", 348 }, 349 {}, 350}; 351MODULE_DEVICE_TABLE(of, p9100_match); 352 353static struct platform_driver p9100_driver = { 354 .driver = { 355 .name = "p9100", 356 .of_match_table = p9100_match, 357 }, 358 .probe = p9100_probe, 359 .remove = p9100_remove, 360}; 361 362static int __init p9100_init(void) 363{ 364 if (fb_get_options("p9100fb", NULL)) 365 return -ENODEV; 366 367 return platform_driver_register(&p9100_driver); 368} 369 370static void __exit p9100_exit(void) 371{ 372 platform_driver_unregister(&p9100_driver); 373} 374 375module_init(p9100_init); 376module_exit(p9100_exit); 377 378MODULE_DESCRIPTION("framebuffer driver for P9100 chipsets"); 379MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); 380MODULE_VERSION("2.0"); 381MODULE_LICENSE("GPL"); 382