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,
54 .fb_compat_ioctl = sbusfb_compat_ioctl,
59 /* Control Register Constants */
60 #define CG3_CR_ENABLE_INTS 0x80
61 #define CG3_CR_ENABLE_VIDEO 0x40
62 #define CG3_CR_ENABLE_TIMING 0x20
63 #define CG3_CR_ENABLE_CURCMP 0x10
64 #define CG3_CR_XTAL_MASK 0x0c
65 #define CG3_CR_DIVISOR_MASK 0x03
67 /* Status Register Constants */
68 #define CG3_SR_PENDING_INT 0x80
69 #define CG3_SR_RES_MASK 0x70
70 #define CG3_SR_1152_900_76_A 0x40
71 #define CG3_SR_1152_900_76_B 0x60
72 #define CG3_SR_ID_MASK 0x0f
73 #define CG3_SR_ID_COLOR 0x01
74 #define CG3_SR_ID_MONO 0x02
75 #define CG3_SR_ID_MONO_ECL 0x03
85 volatile u32 color_map;
94 volatile u8 cursor_start;
95 volatile u8 cursor_end;
96 volatile u8 h_blank_start;
97 volatile u8 h_blank_end;
98 volatile u8 h_sync_start;
99 volatile u8 h_sync_end;
100 volatile u8 comp_sync_end;
101 volatile u8 v_blank_start_high;
102 volatile u8 v_blank_start_low;
103 volatile u8 v_blank_end;
104 volatile u8 v_sync_start;
105 volatile u8 v_sync_end;
106 volatile u8 xfer_holdoff_start;
107 volatile u8 xfer_holdoff_end;
110 /* Offset of interesting structures in the OBIO space */
111 #define CG3_REGS_OFFSET 0x400000UL
112 #define CG3_RAM_OFFSET 0x800000UL
116 struct cg3_regs __iomem *regs;
117 u32 sw_cmap[((256 * 3) + 3) / 4];
120 #define CG3_FLAG_BLANKED 0x00000001
121 #define CG3_FLAG_RDI 0x00000002
123 unsigned long physbase;
124 unsigned long fbsize;
126 struct sbus_dev *sdev;
130 * cg3_setcolreg - Optional function. Sets a color register.
131 * @regno: boolean, 0 copy local, 1 get_user() function
132 * @red: frame buffer colormap structure
133 * @green: The green value which can be up to 16 bits wide
134 * @blue: The blue value which can be up to 16 bits wide.
135 * @transp: If supported the alpha value which can be up to 16 bits wide.
136 * @info: frame buffer info structure
138 * The cg3 palette is loaded with 4 color values at each time
139 * so you end up with: (rgb)(r), (gb)(rg), (b)(rgb), and so on.
140 * We keep a sw copy of the hw cmap to assist us in this esoteric
143 static int cg3_setcolreg(unsigned regno,
144 unsigned red, unsigned green, unsigned blue,
145 unsigned transp, struct fb_info *info)
147 struct cg3_par *par = (struct cg3_par *) info->par;
148 struct bt_regs __iomem *bt = &par->regs->cmap;
161 spin_lock_irqsave(&par->lock, flags);
163 p8 = (u8 *)par->sw_cmap + (regno * 3);
168 #define D4M3(x) ((((x)>>2)<<1) + ((x)>>2)) /* (x/4)*3 */
169 #define D4M4(x) ((x)&~0x3) /* (x/4)*4 */
172 p32 = &par->sw_cmap[D4M3(regno)];
173 sbus_writel(D4M4(regno), &bt->addr);
175 sbus_writel(*p32++, &bt->color_map);
180 spin_unlock_irqrestore(&par->lock, flags);
186 * cg3_blank - Optional function. Blanks the display.
187 * @blank_mode: the blank mode we want.
188 * @info: frame buffer structure that represents a single frame buffer
191 cg3_blank(int blank, struct fb_info *info)
193 struct cg3_par *par = (struct cg3_par *) info->par;
194 struct cg3_regs __iomem *regs = par->regs;
198 spin_lock_irqsave(&par->lock, flags);
201 case FB_BLANK_UNBLANK: /* Unblanking */
202 val = sbus_readb(®s->control);
203 val |= CG3_CR_ENABLE_VIDEO;
204 sbus_writeb(val, ®s->control);
205 par->flags &= ~CG3_FLAG_BLANKED;
208 case FB_BLANK_NORMAL: /* Normal blanking */
209 case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
210 case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
211 case FB_BLANK_POWERDOWN: /* Poweroff */
212 val = sbus_readb(®s->control);
213 val &= ~CG3_CR_ENABLE_VIDEO;
214 sbus_writeb(val, ®s->control);
215 par->flags |= CG3_FLAG_BLANKED;
219 spin_unlock_irqrestore(&par->lock, flags);
224 static struct sbus_mmap_map cg3_mmap_map[] = {
226 .voff = CG3_MMAP_OFFSET,
227 .poff = CG3_RAM_OFFSET,
228 .size = SBUS_MMAP_FBSIZE(1)
233 static int cg3_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
235 struct cg3_par *par = (struct cg3_par *)info->par;
237 return sbusfb_mmap_helper(cg3_mmap_map,
238 par->physbase, par->fbsize,
239 par->sdev->reg_addrs[0].which_io,
243 static int cg3_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
244 unsigned long arg, struct fb_info *info)
246 struct cg3_par *par = (struct cg3_par *) info->par;
248 return sbusfb_ioctl_helper(cmd, arg, info,
249 FBTYPE_SUN3COLOR, 8, par->fbsize);
257 cg3_init_fix(struct fb_info *info, int linebytes)
259 struct cg3_par *par = (struct cg3_par *)info->par;
261 strlcpy(info->fix.id, par->sdev->prom_name, sizeof(info->fix.id));
263 info->fix.type = FB_TYPE_PACKED_PIXELS;
264 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
266 info->fix.line_length = linebytes;
268 info->fix.accel = FB_ACCEL_SUN_CGTHREE;
271 static void cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo *var,
272 struct sbus_dev *sdev)
279 prom_getstring(sdev->prom_node, "params", buffer, sizeof(buffer));
281 ww = simple_strtoul(buffer, &p, 10);
282 if (ww && *p == 'x') {
283 hh = simple_strtoul(p + 1, &p, 10);
284 if (hh && *p == '-') {
285 if (var->xres != ww ||
287 var->xres = var->xres_virtual = ww;
288 var->yres = var->yres_virtual = hh;
295 static u8 cg3regvals_66hz[] __initdata = { /* 1152 x 900, 66 Hz */
296 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14,
297 0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24,
298 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
302 static u8 cg3regvals_76hz[] __initdata = { /* 1152 x 900, 76 Hz */
303 0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f,
304 0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a,
305 0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01,
309 static u8 cg3regvals_rdi[] __initdata = { /* 640 x 480, cgRDI */
310 0x14, 0x70, 0x15, 0x20, 0x16, 0x08, 0x17, 0x10,
311 0x18, 0x06, 0x19, 0x02, 0x1a, 0x31, 0x1b, 0x51,
312 0x1c, 0x06, 0x1d, 0x0c, 0x1e, 0xff, 0x1f, 0x01,
316 static u8 *cg3_regvals[] __initdata = {
317 cg3regvals_66hz, cg3regvals_76hz, cg3regvals_rdi
320 static u_char cg3_dacvals[] __initdata = {
321 4, 0xff, 5, 0x00, 6, 0x70, 7, 0x00, 0
324 static void cg3_do_default_mode(struct cg3_par *par)
329 if (par->flags & CG3_FLAG_RDI)
332 u8 status = sbus_readb(&par->regs->status), mon;
333 if ((status & CG3_SR_ID_MASK) == CG3_SR_ID_COLOR) {
334 mon = status & CG3_SR_RES_MASK;
335 if (mon == CG3_SR_1152_900_76_A ||
336 mon == CG3_SR_1152_900_76_B)
341 prom_printf("cgthree: can't handle SR %02x\n",
348 for (p = cg3_regvals[type]; *p; p += 2) {
349 u8 __iomem *regp = &((u8 __iomem *)par->regs)[p[0]];
350 sbus_writeb(p[1], regp);
352 for (p = cg3_dacvals; *p; p += 2) {
353 volatile u8 __iomem *regp;
355 regp = (volatile u8 __iomem *)&par->regs->cmap.addr;
356 sbus_writeb(p[0], regp);
357 regp = (volatile u8 __iomem *)&par->regs->cmap.control;
358 sbus_writeb(p[1], regp);
365 struct list_head list;
367 static LIST_HEAD(cg3_list);
369 static void cg3_init_one(struct sbus_dev *sdev)
371 struct all_info *all;
374 all = kmalloc(sizeof(*all), GFP_KERNEL);
376 printk(KERN_ERR "cg3: Cannot allocate memory.\n");
379 memset(all, 0, sizeof(*all));
381 INIT_LIST_HEAD(&all->list);
383 spin_lock_init(&all->par.lock);
384 all->par.sdev = sdev;
386 all->par.physbase = sdev->reg_addrs[0].phys_addr;
388 sbusfb_fill_var(&all->info.var, sdev->prom_node, 8);
389 all->info.var.red.length = 8;
390 all->info.var.green.length = 8;
391 all->info.var.blue.length = 8;
392 if (!strcmp(sdev->prom_name, "cgRDI"))
393 all->par.flags |= CG3_FLAG_RDI;
394 if (all->par.flags & CG3_FLAG_RDI)
395 cg3_rdi_maybe_fixup_var(&all->info.var, sdev);
397 linebytes = prom_getintdefault(sdev->prom_node, "linebytes",
399 all->par.fbsize = PAGE_ALIGN(linebytes * all->info.var.yres);
401 all->par.regs = sbus_ioremap(&sdev->resource[0], CG3_REGS_OFFSET,
402 sizeof(struct cg3_regs), "cg3 regs");
404 all->info.flags = FBINFO_DEFAULT;
405 all->info.fbops = &cg3_ops;
406 #ifdef CONFIG_SPARC32
407 all->info.screen_base = (char __iomem *)
408 prom_getintdefault(sdev->prom_node, "address", 0);
410 if (!all->info.screen_base)
411 all->info.screen_base =
412 sbus_ioremap(&sdev->resource[0], CG3_RAM_OFFSET,
413 all->par.fbsize, "cg3 ram");
414 all->info.par = &all->par;
416 cg3_blank(0, &all->info);
418 if (!prom_getbool(sdev->prom_node, "width"))
419 cg3_do_default_mode(&all->par);
421 if (fb_alloc_cmap(&all->info.cmap, 256, 0)) {
422 printk(KERN_ERR "cg3: Could not allocate color map.\n");
426 fb_set_cmap(&all->info.cmap, &all->info);
428 cg3_init_fix(&all->info, linebytes);
430 if (register_framebuffer(&all->info) < 0) {
431 printk(KERN_ERR "cg3: Could not register framebuffer.\n");
432 fb_dealloc_cmap(&all->info.cmap);
437 list_add(&all->list, &cg3_list);
439 printk("cg3: %s at %lx:%lx\n",
441 (long) sdev->reg_addrs[0].which_io,
442 (long) sdev->reg_addrs[0].phys_addr);
445 int __init cg3_init(void)
447 struct sbus_bus *sbus;
448 struct sbus_dev *sdev;
450 if (fb_get_options("cg3fb", NULL))
453 for_all_sbusdev(sdev, sbus) {
454 if (!strcmp(sdev->prom_name, "cgthree") ||
455 !strcmp(sdev->prom_name, "cgRDI"))
462 void __exit cg3_exit(void)
464 struct list_head *pos, *tmp;
466 list_for_each_safe(pos, tmp, &cg3_list) {
467 struct all_info *all = list_entry(pos, typeof(*all), list);
469 unregister_framebuffer(&all->info);
470 fb_dealloc_cmap(&all->info.cmap);
478 /* No cmdline options yet... */
482 module_init(cg3_init);
485 module_exit(cg3_exit);
488 MODULE_DESCRIPTION("framebuffer driver for CGthree chipsets");
489 MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
490 MODULE_LICENSE("GPL");