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 of_device
*op
,
350 const struct of_device_id
*match
)
352 struct device_node
*dp
= op
->node
;
353 struct fb_info
*info
;
357 info
= framebuffer_alloc(sizeof(struct cg3_par
), &op
->dev
);
364 spin_lock_init(&par
->lock
);
366 info
->fix
.smem_start
= op
->resource
[0].start
;
367 par
->which_io
= op
->resource
[0].flags
& IORESOURCE_BITS
;
369 sbusfb_fill_var(&info
->var
, dp
, 8);
370 info
->var
.red
.length
= 8;
371 info
->var
.green
.length
= 8;
372 info
->var
.blue
.length
= 8;
373 if (!strcmp(dp
->name
, "cgRDI"))
374 par
->flags
|= CG3_FLAG_RDI
;
375 if (par
->flags
& CG3_FLAG_RDI
)
376 cg3_rdi_maybe_fixup_var(&info
->var
, dp
);
378 linebytes
= of_getintprop_default(dp
, "linebytes",
380 info
->fix
.smem_len
= PAGE_ALIGN(linebytes
* info
->var
.yres
);
382 par
->regs
= of_ioremap(&op
->resource
[0], CG3_REGS_OFFSET
,
383 sizeof(struct cg3_regs
), "cg3 regs");
387 info
->flags
= FBINFO_DEFAULT
;
388 info
->fbops
= &cg3_ops
;
389 info
->screen_base
= of_ioremap(&op
->resource
[0], CG3_RAM_OFFSET
,
390 info
->fix
.smem_len
, "cg3 ram");
391 if (!info
->screen_base
)
394 cg3_blank(FB_BLANK_UNBLANK
, info
);
396 if (!of_find_property(dp
, "width", NULL
)) {
397 err
= cg3_do_default_mode(par
);
399 goto out_unmap_screen
;
402 if (fb_alloc_cmap(&info
->cmap
, 256, 0))
403 goto out_unmap_screen
;
405 fb_set_cmap(&info
->cmap
, info
);
407 cg3_init_fix(info
, linebytes
, dp
);
409 err
= register_framebuffer(info
);
411 goto out_dealloc_cmap
;
413 dev_set_drvdata(&op
->dev
, info
);
415 printk(KERN_INFO
"%s: cg3 at %lx:%lx\n",
416 dp
->full_name
, par
->which_io
, info
->fix
.smem_start
);
421 fb_dealloc_cmap(&info
->cmap
);
424 of_iounmap(&op
->resource
[0], info
->screen_base
, info
->fix
.smem_len
);
427 of_iounmap(&op
->resource
[0], par
->regs
, sizeof(struct cg3_regs
));
430 framebuffer_release(info
);
436 static int __devexit
cg3_remove(struct of_device
*op
)
438 struct fb_info
*info
= dev_get_drvdata(&op
->dev
);
439 struct cg3_par
*par
= info
->par
;
441 unregister_framebuffer(info
);
442 fb_dealloc_cmap(&info
->cmap
);
444 of_iounmap(&op
->resource
[0], par
->regs
, sizeof(struct cg3_regs
));
445 of_iounmap(&op
->resource
[0], info
->screen_base
, info
->fix
.smem_len
);
447 framebuffer_release(info
);
449 dev_set_drvdata(&op
->dev
, NULL
);
454 static const struct of_device_id cg3_match
[] = {
463 MODULE_DEVICE_TABLE(of
, cg3_match
);
465 static struct of_platform_driver cg3_driver
= {
467 .match_table
= cg3_match
,
469 .remove
= __devexit_p(cg3_remove
),
472 static int __init
cg3_init(void)
474 if (fb_get_options("cg3fb", NULL
))
477 return of_register_driver(&cg3_driver
, &of_bus_type
);
480 static void __exit
cg3_exit(void)
482 of_unregister_driver(&cg3_driver
);
485 module_init(cg3_init
);
486 module_exit(cg3_exit
);
488 MODULE_DESCRIPTION("framebuffer driver for CGthree chipsets");
489 MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
490 MODULE_VERSION("2.0");
491 MODULE_LICENSE("GPL");