1 /* cg3.c: CGTHREE frame buffer driver
3 * Copyright (C) 2003 David S. Miller (davem@redhat.com)
4 * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
5 * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
6 * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
8 * Driver layout based loosely on tgafb.c, see that file for credits.
11 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/errno.h>
14 #include <linux/string.h>
15 #include <linux/slab.h>
16 #include <linux/delay.h>
17 #include <linux/init.h>
23 #include <asm/oplib.h>
32 static int cg3_setcolreg(unsigned, unsigned, unsigned, unsigned,
33 unsigned, struct fb_info *);
34 static int cg3_blank(int, struct fb_info *);
36 static int cg3_mmap(struct fb_info *, struct file *, struct vm_area_struct *);
37 static int cg3_ioctl(struct inode *, struct file *, unsigned int,
38 unsigned long, struct fb_info *);
41 * Frame buffer operations
44 static struct fb_ops cg3_ops = {
46 .fb_setcolreg = cg3_setcolreg,
47 .fb_blank = cg3_blank,
48 .fb_fillrect = cfb_fillrect,
49 .fb_copyarea = cfb_copyarea,
50 .fb_imageblit = cfb_imageblit,
52 .fb_ioctl = cg3_ioctl,
53 .fb_cursor = soft_cursor,
57 /* Control Register Constants */
58 #define CG3_CR_ENABLE_INTS 0x80
59 #define CG3_CR_ENABLE_VIDEO 0x40
60 #define CG3_CR_ENABLE_TIMING 0x20
61 #define CG3_CR_ENABLE_CURCMP 0x10
62 #define CG3_CR_XTAL_MASK 0x0c
63 #define CG3_CR_DIVISOR_MASK 0x03
65 /* Status Register Constants */
66 #define CG3_SR_PENDING_INT 0x80
67 #define CG3_SR_RES_MASK 0x70
68 #define CG3_SR_1152_900_76_A 0x40
69 #define CG3_SR_1152_900_76_B 0x60
70 #define CG3_SR_ID_MASK 0x0f
71 #define CG3_SR_ID_COLOR 0x01
72 #define CG3_SR_ID_MONO 0x02
73 #define CG3_SR_ID_MONO_ECL 0x03
83 volatile u32 color_map;
92 volatile u8 cursor_start;
93 volatile u8 cursor_end;
94 volatile u8 h_blank_start;
95 volatile u8 h_blank_end;
96 volatile u8 h_sync_start;
97 volatile u8 h_sync_end;
98 volatile u8 comp_sync_end;
99 volatile u8 v_blank_start_high;
100 volatile u8 v_blank_start_low;
101 volatile u8 v_blank_end;
102 volatile u8 v_sync_start;
103 volatile u8 v_sync_end;
104 volatile u8 xfer_holdoff_start;
105 volatile u8 xfer_holdoff_end;
108 /* Offset of interesting structures in the OBIO space */
109 #define CG3_REGS_OFFSET 0x400000UL
110 #define CG3_RAM_OFFSET 0x800000UL
114 struct cg3_regs __iomem *regs;
115 u32 sw_cmap[((256 * 3) + 3) / 4];
118 #define CG3_FLAG_BLANKED 0x00000001
119 #define CG3_FLAG_RDI 0x00000002
121 unsigned long physbase;
122 unsigned long fbsize;
124 struct sbus_dev *sdev;
125 struct list_head list;
129 * cg3_setcolreg - Optional function. Sets a color register.
130 * @regno: boolean, 0 copy local, 1 get_user() function
131 * @red: frame buffer colormap structure
132 * @green: The green value which can be up to 16 bits wide
133 * @blue: The blue value which can be up to 16 bits wide.
134 * @transp: If supported the alpha value which can be up to 16 bits wide.
135 * @info: frame buffer info structure
137 * The cg3 palette is loaded with 4 color values at each time
138 * so you end up with: (rgb)(r), (gb)(rg), (b)(rgb), and so on.
139 * We keep a sw copy of the hw cmap to assist us in this esoteric
142 static int cg3_setcolreg(unsigned regno,
143 unsigned red, unsigned green, unsigned blue,
144 unsigned transp, struct fb_info *info)
146 struct cg3_par *par = (struct cg3_par *) info->par;
147 struct bt_regs __iomem *bt = &par->regs->cmap;
160 spin_lock_irqsave(&par->lock, flags);
162 p8 = (u8 *)par->sw_cmap + (regno * 3);
167 #define D4M3(x) ((((x)>>2)<<1) + ((x)>>2)) /* (x/4)*3 */
168 #define D4M4(x) ((x)&~0x3) /* (x/4)*4 */
171 p32 = &par->sw_cmap[D4M3(regno)];
172 sbus_writel(D4M4(regno), &bt->addr);
174 sbus_writel(*p32++, &bt->color_map);
179 spin_unlock_irqrestore(&par->lock, flags);
185 * cg3_blank - Optional function. Blanks the display.
186 * @blank_mode: the blank mode we want.
187 * @info: frame buffer structure that represents a single frame buffer
190 cg3_blank(int blank, struct fb_info *info)
192 struct cg3_par *par = (struct cg3_par *) info->par;
193 struct cg3_regs __iomem *regs = par->regs;
197 spin_lock_irqsave(&par->lock, flags);
200 case FB_BLANK_UNBLANK: /* Unblanking */
201 val = sbus_readb(®s->control);
202 val |= CG3_CR_ENABLE_VIDEO;
203 sbus_writeb(val, ®s->control);
204 par->flags &= ~CG3_FLAG_BLANKED;
207 case FB_BLANK_NORMAL: /* Normal blanking */
208 case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
209 case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
210 case FB_BLANK_POWERDOWN: /* Poweroff */
211 val = sbus_readb(®s->control);
212 val &= ~CG3_CR_ENABLE_VIDEO;
213 sbus_writeb(val, ®s->control);
214 par->flags |= CG3_FLAG_BLANKED;
218 spin_unlock_irqrestore(&par->lock, flags);
223 static struct sbus_mmap_map cg3_mmap_map[] = {
225 .voff = CG3_MMAP_OFFSET,
226 .poff = CG3_RAM_OFFSET,
227 .size = SBUS_MMAP_FBSIZE(1)
232 static int cg3_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
234 struct cg3_par *par = (struct cg3_par *)info->par;
236 return sbusfb_mmap_helper(cg3_mmap_map,
237 par->physbase, par->fbsize,
238 par->sdev->reg_addrs[0].which_io,
242 static int cg3_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
243 unsigned long arg, struct fb_info *info)
245 struct cg3_par *par = (struct cg3_par *) info->par;
247 return sbusfb_ioctl_helper(cmd, arg, info,
248 FBTYPE_SUN3COLOR, 8, par->fbsize);
256 cg3_init_fix(struct fb_info *info, int linebytes)
258 struct cg3_par *par = (struct cg3_par *)info->par;
260 strlcpy(info->fix.id, par->sdev->prom_name, sizeof(info->fix.id));
262 info->fix.type = FB_TYPE_PACKED_PIXELS;
263 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
265 info->fix.line_length = linebytes;
267 info->fix.accel = FB_ACCEL_SUN_CGTHREE;
270 static void cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo *var,
271 struct sbus_dev *sdev)
278 prom_getstring(sdev->prom_node, "params", buffer, sizeof(buffer));
280 ww = simple_strtoul(buffer, &p, 10);
281 if (ww && *p == 'x') {
282 hh = simple_strtoul(p + 1, &p, 10);
283 if (hh && *p == '-') {
284 if (var->xres != ww ||
286 var->xres = var->xres_virtual = ww;
287 var->yres = var->yres_virtual = hh;
294 static u8 cg3regvals_66hz[] __initdata = { /* 1152 x 900, 66 Hz */
295 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14,
296 0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24,
297 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
301 static u8 cg3regvals_76hz[] __initdata = { /* 1152 x 900, 76 Hz */
302 0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f,
303 0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a,
304 0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01,
308 static u8 cg3regvals_rdi[] __initdata = { /* 640 x 480, cgRDI */
309 0x14, 0x70, 0x15, 0x20, 0x16, 0x08, 0x17, 0x10,
310 0x18, 0x06, 0x19, 0x02, 0x1a, 0x31, 0x1b, 0x51,
311 0x1c, 0x06, 0x1d, 0x0c, 0x1e, 0xff, 0x1f, 0x01,
315 static u8 *cg3_regvals[] __initdata = {
316 cg3regvals_66hz, cg3regvals_76hz, cg3regvals_rdi
319 static u_char cg3_dacvals[] __initdata = {
320 4, 0xff, 5, 0x00, 6, 0x70, 7, 0x00, 0
323 static void cg3_do_default_mode(struct cg3_par *par)
328 if (par->flags & CG3_FLAG_RDI)
331 u8 status = sbus_readb(&par->regs->status), mon;
332 if ((status & CG3_SR_ID_MASK) == CG3_SR_ID_COLOR) {
333 mon = status & CG3_SR_RES_MASK;
334 if (mon == CG3_SR_1152_900_76_A ||
335 mon == CG3_SR_1152_900_76_B)
340 prom_printf("cgthree: can't handle SR %02x\n",
347 for (p = cg3_regvals[type]; *p; p += 2) {
348 u8 __iomem *regp = &((u8 __iomem *)par->regs)[p[0]];
349 sbus_writeb(p[1], regp);
351 for (p = cg3_dacvals; *p; p += 2) {
352 volatile u8 __iomem *regp;
354 regp = (volatile u8 __iomem *)&par->regs->cmap.addr;
355 sbus_writeb(p[0], regp);
356 regp = (volatile u8 __iomem *)&par->regs->cmap.control;
357 sbus_writeb(p[1], regp);
364 struct list_head list;
366 static LIST_HEAD(cg3_list);
368 static void cg3_init_one(struct sbus_dev *sdev)
370 struct all_info *all;
373 all = kmalloc(sizeof(*all), GFP_KERNEL);
375 printk(KERN_ERR "cg3: Cannot allocate memory.\n");
378 memset(all, 0, sizeof(*all));
380 INIT_LIST_HEAD(&all->list);
382 spin_lock_init(&all->par.lock);
383 all->par.sdev = sdev;
385 all->par.physbase = sdev->reg_addrs[0].phys_addr;
387 sbusfb_fill_var(&all->info.var, sdev->prom_node, 8);
388 all->info.var.red.length = 8;
389 all->info.var.green.length = 8;
390 all->info.var.blue.length = 8;
391 if (!strcmp(sdev->prom_name, "cgRDI"))
392 all->par.flags |= CG3_FLAG_RDI;
393 if (all->par.flags & CG3_FLAG_RDI)
394 cg3_rdi_maybe_fixup_var(&all->info.var, sdev);
396 linebytes = prom_getintdefault(sdev->prom_node, "linebytes",
398 all->par.fbsize = PAGE_ALIGN(linebytes * all->info.var.yres);
400 all->par.regs = sbus_ioremap(&sdev->resource[0], CG3_REGS_OFFSET,
401 sizeof(struct cg3_regs), "cg3 regs");
403 all->info.flags = FBINFO_DEFAULT;
404 all->info.fbops = &cg3_ops;
405 #ifdef CONFIG_SPARC32
406 all->info.screen_base = (char __iomem *)
407 prom_getintdefault(sdev->prom_node, "address", 0);
409 if (!all->info.screen_base)
410 all->info.screen_base =
411 sbus_ioremap(&sdev->resource[0], CG3_RAM_OFFSET,
412 all->par.fbsize, "cg3 ram");
413 all->info.par = &all->par;
415 cg3_blank(0, &all->info);
417 if (!prom_getbool(sdev->prom_node, "width"))
418 cg3_do_default_mode(&all->par);
420 if (fb_alloc_cmap(&all->info.cmap, 256, 0)) {
421 printk(KERN_ERR "cg3: Could not allocate color map.\n");
425 fb_set_cmap(&all->info.cmap, &all->info);
427 cg3_init_fix(&all->info, linebytes);
429 if (register_framebuffer(&all->info) < 0) {
430 printk(KERN_ERR "cg3: Could not register framebuffer.\n");
431 fb_dealloc_cmap(&all->info.cmap);
436 list_add(&all->list, &cg3_list);
438 printk("cg3: %s at %lx:%lx\n",
440 (long) sdev->reg_addrs[0].which_io,
441 (long) sdev->reg_addrs[0].phys_addr);
444 int __init cg3_init(void)
446 struct sbus_bus *sbus;
447 struct sbus_dev *sdev;
449 if (fb_get_options("cg3fb", NULL))
452 for_all_sbusdev(sdev, sbus) {
453 if (!strcmp(sdev->prom_name, "cgthree") ||
454 !strcmp(sdev->prom_name, "cgRDI"))
461 void __exit cg3_exit(void)
463 struct list_head *pos, *tmp;
465 list_for_each_safe(pos, tmp, &cg3_list) {
466 struct all_info *all = list_entry(pos, typeof(*all), list);
468 unregister_framebuffer(&all->info);
469 fb_dealloc_cmap(&all->info.cmap);
477 /* No cmdline options yet... */
481 module_init(cg3_init);
484 module_exit(cg3_exit);
487 MODULE_DESCRIPTION("framebuffer driver for CGthree chipsets");
488 MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
489 MODULE_LICENSE("GPL");