1 /* cg3.c: CGTHREE frame buffer driver
3 * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net)
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/delay.h>
16 #include <linux/init.h>
19 #include <linux/of_device.h>
30 static int cg3_setcolreg(unsigned, unsigned, unsigned, unsigned,
31 unsigned, struct fb_info
*);
32 static int cg3_blank(int, struct fb_info
*);
34 static int cg3_mmap(struct fb_info
*, struct vm_area_struct
*);
35 static int cg3_ioctl(struct fb_info
*, unsigned int, unsigned long);
38 * Frame buffer operations
41 static struct fb_ops cg3_ops
= {
43 .fb_setcolreg
= cg3_setcolreg
,
44 .fb_blank
= cg3_blank
,
45 .fb_fillrect
= cfb_fillrect
,
46 .fb_copyarea
= cfb_copyarea
,
47 .fb_imageblit
= cfb_imageblit
,
49 .fb_ioctl
= cg3_ioctl
,
51 .fb_compat_ioctl
= sbusfb_compat_ioctl
,
56 /* Control Register Constants */
57 #define CG3_CR_ENABLE_INTS 0x80
58 #define CG3_CR_ENABLE_VIDEO 0x40
59 #define CG3_CR_ENABLE_TIMING 0x20
60 #define CG3_CR_ENABLE_CURCMP 0x10
61 #define CG3_CR_XTAL_MASK 0x0c
62 #define CG3_CR_DIVISOR_MASK 0x03
64 /* Status Register Constants */
65 #define CG3_SR_PENDING_INT 0x80
66 #define CG3_SR_RES_MASK 0x70
67 #define CG3_SR_1152_900_76_A 0x40
68 #define CG3_SR_1152_900_76_B 0x60
69 #define CG3_SR_ID_MASK 0x0f
70 #define CG3_SR_ID_COLOR 0x01
71 #define CG3_SR_ID_MONO 0x02
72 #define CG3_SR_ID_MONO_ECL 0x03
98 u8 v_blank_start_high
;
103 u8 xfer_holdoff_start
;
107 /* Offset of interesting structures in the OBIO space */
108 #define CG3_REGS_OFFSET 0x400000UL
109 #define CG3_RAM_OFFSET 0x800000UL
113 struct cg3_regs __iomem
*regs
;
114 u32 sw_cmap
[((256 * 3) + 3) / 4];
117 #define CG3_FLAG_BLANKED 0x00000001
118 #define CG3_FLAG_RDI 0x00000002
120 unsigned long which_io
;
124 * cg3_setcolreg - Optional function. Sets a color register.
125 * @regno: boolean, 0 copy local, 1 get_user() function
126 * @red: frame buffer colormap structure
127 * @green: The green value which can be up to 16 bits wide
128 * @blue: The blue value which can be up to 16 bits wide.
129 * @transp: If supported the alpha value which can be up to 16 bits wide.
130 * @info: frame buffer info structure
132 * The cg3 palette is loaded with 4 color values at each time
133 * so you end up with: (rgb)(r), (gb)(rg), (b)(rgb), and so on.
134 * We keep a sw copy of the hw cmap to assist us in this esoteric
137 static int cg3_setcolreg(unsigned regno
,
138 unsigned red
, unsigned green
, unsigned blue
,
139 unsigned transp
, struct fb_info
*info
)
141 struct cg3_par
*par
= (struct cg3_par
*) info
->par
;
142 struct bt_regs __iomem
*bt
= &par
->regs
->cmap
;
155 spin_lock_irqsave(&par
->lock
, flags
);
157 p8
= (u8
*)par
->sw_cmap
+ (regno
* 3);
162 #define D4M3(x) ((((x)>>2)<<1) + ((x)>>2)) /* (x/4)*3 */
163 #define D4M4(x) ((x)&~0x3) /* (x/4)*4 */
166 p32
= &par
->sw_cmap
[D4M3(regno
)];
167 sbus_writel(D4M4(regno
), &bt
->addr
);
169 sbus_writel(*p32
++, &bt
->color_map
);
174 spin_unlock_irqrestore(&par
->lock
, flags
);
180 * cg3_blank - Optional function. Blanks the display.
181 * @blank_mode: the blank mode we want.
182 * @info: frame buffer structure that represents a single frame buffer
184 static int cg3_blank(int blank
, struct fb_info
*info
)
186 struct cg3_par
*par
= (struct cg3_par
*) info
->par
;
187 struct cg3_regs __iomem
*regs
= par
->regs
;
191 spin_lock_irqsave(&par
->lock
, flags
);
194 case FB_BLANK_UNBLANK
: /* Unblanking */
195 val
= sbus_readb(®s
->control
);
196 val
|= CG3_CR_ENABLE_VIDEO
;
197 sbus_writeb(val
, ®s
->control
);
198 par
->flags
&= ~CG3_FLAG_BLANKED
;
201 case FB_BLANK_NORMAL
: /* Normal blanking */
202 case FB_BLANK_VSYNC_SUSPEND
: /* VESA blank (vsync off) */
203 case FB_BLANK_HSYNC_SUSPEND
: /* VESA blank (hsync off) */
204 case FB_BLANK_POWERDOWN
: /* Poweroff */
205 val
= sbus_readb(®s
->control
);
206 val
&= ~CG3_CR_ENABLE_VIDEO
;
207 sbus_writeb(val
, ®s
->control
);
208 par
->flags
|= CG3_FLAG_BLANKED
;
212 spin_unlock_irqrestore(&par
->lock
, flags
);
217 static struct sbus_mmap_map cg3_mmap_map
[] = {
219 .voff
= CG3_MMAP_OFFSET
,
220 .poff
= CG3_RAM_OFFSET
,
221 .size
= SBUS_MMAP_FBSIZE(1)
226 static int cg3_mmap(struct fb_info
*info
, struct vm_area_struct
*vma
)
228 struct cg3_par
*par
= (struct cg3_par
*)info
->par
;
230 return sbusfb_mmap_helper(cg3_mmap_map
,
231 info
->fix
.smem_start
, info
->fix
.smem_len
,
236 static int cg3_ioctl(struct fb_info
*info
, unsigned int cmd
, unsigned long arg
)
238 return sbusfb_ioctl_helper(cmd
, arg
, info
,
239 FBTYPE_SUN3COLOR
, 8, info
->fix
.smem_len
);
246 static void __devinit
cg3_init_fix(struct fb_info
*info
, int linebytes
,
247 struct device_node
*dp
)
249 strlcpy(info
->fix
.id
, dp
->name
, sizeof(info
->fix
.id
));
251 info
->fix
.type
= FB_TYPE_PACKED_PIXELS
;
252 info
->fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
254 info
->fix
.line_length
= linebytes
;
256 info
->fix
.accel
= FB_ACCEL_SUN_CGTHREE
;
259 static void __devinit
cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo
*var
,
260 struct device_node
*dp
)
266 params
= of_get_property(dp
, "params", NULL
);
268 ww
= simple_strtoul(params
, &p
, 10);
269 if (ww
&& *p
== 'x') {
270 hh
= simple_strtoul(p
+ 1, &p
, 10);
271 if (hh
&& *p
== '-') {
272 if (var
->xres
!= ww
||
274 var
->xres
= var
->xres_virtual
= ww
;
275 var
->yres
= var
->yres_virtual
= hh
;
282 static u8 cg3regvals_66hz
[] __devinitdata
= { /* 1152 x 900, 66 Hz */
283 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14,
284 0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24,
285 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
289 static u8 cg3regvals_76hz
[] __devinitdata
= { /* 1152 x 900, 76 Hz */
290 0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f,
291 0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a,
292 0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01,
296 static u8 cg3regvals_rdi
[] __devinitdata
= { /* 640 x 480, cgRDI */
297 0x14, 0x70, 0x15, 0x20, 0x16, 0x08, 0x17, 0x10,
298 0x18, 0x06, 0x19, 0x02, 0x1a, 0x31, 0x1b, 0x51,
299 0x1c, 0x06, 0x1d, 0x0c, 0x1e, 0xff, 0x1f, 0x01,
303 static u8
*cg3_regvals
[] __devinitdata
= {
304 cg3regvals_66hz
, cg3regvals_76hz
, cg3regvals_rdi
307 static u_char cg3_dacvals
[] __devinitdata
= {
308 4, 0xff, 5, 0x00, 6, 0x70, 7, 0x00, 0
311 static int __devinit
cg3_do_default_mode(struct cg3_par
*par
)
316 if (par
->flags
& CG3_FLAG_RDI
)
319 u8 status
= sbus_readb(&par
->regs
->status
), mon
;
320 if ((status
& CG3_SR_ID_MASK
) == CG3_SR_ID_COLOR
) {
321 mon
= status
& CG3_SR_RES_MASK
;
322 if (mon
== CG3_SR_1152_900_76_A
||
323 mon
== CG3_SR_1152_900_76_B
)
328 printk(KERN_ERR
"cgthree: can't handle SR %02x\n",
334 for (p
= cg3_regvals
[type
]; *p
; p
+= 2) {
335 u8 __iomem
*regp
= &((u8 __iomem
*)par
->regs
)[p
[0]];
336 sbus_writeb(p
[1], regp
);
338 for (p
= cg3_dacvals
; *p
; p
+= 2) {
341 regp
= (u8 __iomem
*)&par
->regs
->cmap
.addr
;
342 sbus_writeb(p
[0], regp
);
343 regp
= (u8 __iomem
*)&par
->regs
->cmap
.control
;
344 sbus_writeb(p
[1], regp
);
349 static int __devinit
cg3_probe(struct platform_device
*op
)
351 struct device_node
*dp
= op
->dev
.of_node
;
352 struct fb_info
*info
;
356 info
= framebuffer_alloc(sizeof(struct cg3_par
), &op
->dev
);
363 spin_lock_init(&par
->lock
);
365 info
->fix
.smem_start
= op
->resource
[0].start
;
366 par
->which_io
= op
->resource
[0].flags
& IORESOURCE_BITS
;
368 sbusfb_fill_var(&info
->var
, dp
, 8);
369 info
->var
.red
.length
= 8;
370 info
->var
.green
.length
= 8;
371 info
->var
.blue
.length
= 8;
372 if (!strcmp(dp
->name
, "cgRDI"))
373 par
->flags
|= CG3_FLAG_RDI
;
374 if (par
->flags
& CG3_FLAG_RDI
)
375 cg3_rdi_maybe_fixup_var(&info
->var
, dp
);
377 linebytes
= of_getintprop_default(dp
, "linebytes",
379 info
->fix
.smem_len
= PAGE_ALIGN(linebytes
* info
->var
.yres
);
381 par
->regs
= of_ioremap(&op
->resource
[0], CG3_REGS_OFFSET
,
382 sizeof(struct cg3_regs
), "cg3 regs");
386 info
->flags
= FBINFO_DEFAULT
;
387 info
->fbops
= &cg3_ops
;
388 info
->screen_base
= of_ioremap(&op
->resource
[0], CG3_RAM_OFFSET
,
389 info
->fix
.smem_len
, "cg3 ram");
390 if (!info
->screen_base
)
393 cg3_blank(FB_BLANK_UNBLANK
, info
);
395 if (!of_find_property(dp
, "width", NULL
)) {
396 err
= cg3_do_default_mode(par
);
398 goto out_unmap_screen
;
401 if (fb_alloc_cmap(&info
->cmap
, 256, 0))
402 goto out_unmap_screen
;
404 fb_set_cmap(&info
->cmap
, info
);
406 cg3_init_fix(info
, linebytes
, dp
);
408 err
= register_framebuffer(info
);
410 goto out_dealloc_cmap
;
412 dev_set_drvdata(&op
->dev
, info
);
414 printk(KERN_INFO
"%s: cg3 at %lx:%lx\n",
415 dp
->full_name
, par
->which_io
, info
->fix
.smem_start
);
420 fb_dealloc_cmap(&info
->cmap
);
423 of_iounmap(&op
->resource
[0], info
->screen_base
, info
->fix
.smem_len
);
426 of_iounmap(&op
->resource
[0], par
->regs
, sizeof(struct cg3_regs
));
429 framebuffer_release(info
);
435 static int __devexit
cg3_remove(struct platform_device
*op
)
437 struct fb_info
*info
= dev_get_drvdata(&op
->dev
);
438 struct cg3_par
*par
= info
->par
;
440 unregister_framebuffer(info
);
441 fb_dealloc_cmap(&info
->cmap
);
443 of_iounmap(&op
->resource
[0], par
->regs
, sizeof(struct cg3_regs
));
444 of_iounmap(&op
->resource
[0], info
->screen_base
, info
->fix
.smem_len
);
446 framebuffer_release(info
);
448 dev_set_drvdata(&op
->dev
, NULL
);
453 static const struct of_device_id cg3_match
[] = {
462 MODULE_DEVICE_TABLE(of
, cg3_match
);
464 static struct platform_driver cg3_driver
= {
467 .owner
= THIS_MODULE
,
468 .of_match_table
= cg3_match
,
471 .remove
= __devexit_p(cg3_remove
),
474 static int __init
cg3_init(void)
476 if (fb_get_options("cg3fb", NULL
))
479 return platform_driver_register(&cg3_driver
);
482 static void __exit
cg3_exit(void)
484 platform_driver_unregister(&cg3_driver
);
487 module_init(cg3_init
);
488 module_exit(cg3_exit
);
490 MODULE_DESCRIPTION("framebuffer driver for CGthree chipsets");
491 MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
492 MODULE_VERSION("2.0");
493 MODULE_LICENSE("GPL");