1 // SPDX-License-Identifier: GPL-2.0-only
2 /* cg3.c: CGTHREE frame buffer driver
4 * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net)
5 * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
6 * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
7 * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
9 * Driver layout based loosely on tgafb.c, see that file for credits.
12 #include <linux/module.h>
13 #include <linux/kernel.h>
14 #include <linux/errno.h>
15 #include <linux/string.h>
16 #include <linux/delay.h>
17 #include <linux/init.h>
20 #include <linux/of_device.h>
31 static int cg3_setcolreg(unsigned, unsigned, unsigned, unsigned,
32 unsigned, struct fb_info
*);
33 static int cg3_blank(int, struct fb_info
*);
35 static int cg3_mmap(struct fb_info
*, struct vm_area_struct
*);
36 static int cg3_ioctl(struct fb_info
*, unsigned int, unsigned long);
39 * Frame buffer operations
42 static const struct fb_ops cg3_ops
= {
44 .fb_setcolreg
= cg3_setcolreg
,
45 .fb_blank
= cg3_blank
,
46 .fb_fillrect
= cfb_fillrect
,
47 .fb_copyarea
= cfb_copyarea
,
48 .fb_imageblit
= cfb_imageblit
,
50 .fb_ioctl
= cg3_ioctl
,
52 .fb_compat_ioctl
= sbusfb_compat_ioctl
,
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
99 u8 v_blank_start_high
;
100 u8 v_blank_start_low
;
104 u8 xfer_holdoff_start
;
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 which_io
;
125 * cg3_setcolreg - Optional function. Sets a color register.
126 * @regno: boolean, 0 copy local, 1 get_user() function
127 * @red: frame buffer colormap structure
128 * @green: The green value which can be up to 16 bits wide
129 * @blue: The blue value which can be up to 16 bits wide.
130 * @transp: If supported the alpha value which can be up to 16 bits wide.
131 * @info: frame buffer info structure
133 * The cg3 palette is loaded with 4 color values at each time
134 * so you end up with: (rgb)(r), (gb)(rg), (b)(rgb), and so on.
135 * We keep a sw copy of the hw cmap to assist us in this esoteric
138 static int cg3_setcolreg(unsigned regno
,
139 unsigned red
, unsigned green
, unsigned blue
,
140 unsigned transp
, struct fb_info
*info
)
142 struct cg3_par
*par
= (struct cg3_par
*) info
->par
;
143 struct bt_regs __iomem
*bt
= &par
->regs
->cmap
;
156 spin_lock_irqsave(&par
->lock
, flags
);
158 p8
= (u8
*)par
->sw_cmap
+ (regno
* 3);
163 #define D4M3(x) ((((x)>>2)<<1) + ((x)>>2)) /* (x/4)*3 */
164 #define D4M4(x) ((x)&~0x3) /* (x/4)*4 */
167 p32
= &par
->sw_cmap
[D4M3(regno
)];
168 sbus_writel(D4M4(regno
), &bt
->addr
);
170 sbus_writel(*p32
++, &bt
->color_map
);
175 spin_unlock_irqrestore(&par
->lock
, flags
);
181 * cg3_blank - Optional function. Blanks the display.
182 * @blank_mode: the blank mode we want.
183 * @info: frame buffer structure that represents a single frame buffer
185 static int cg3_blank(int blank
, struct fb_info
*info
)
187 struct cg3_par
*par
= (struct cg3_par
*) info
->par
;
188 struct cg3_regs __iomem
*regs
= par
->regs
;
192 spin_lock_irqsave(&par
->lock
, flags
);
195 case FB_BLANK_UNBLANK
: /* Unblanking */
196 val
= sbus_readb(®s
->control
);
197 val
|= CG3_CR_ENABLE_VIDEO
;
198 sbus_writeb(val
, ®s
->control
);
199 par
->flags
&= ~CG3_FLAG_BLANKED
;
202 case FB_BLANK_NORMAL
: /* Normal blanking */
203 case FB_BLANK_VSYNC_SUSPEND
: /* VESA blank (vsync off) */
204 case FB_BLANK_HSYNC_SUSPEND
: /* VESA blank (hsync off) */
205 case FB_BLANK_POWERDOWN
: /* Poweroff */
206 val
= sbus_readb(®s
->control
);
207 val
&= ~CG3_CR_ENABLE_VIDEO
;
208 sbus_writeb(val
, ®s
->control
);
209 par
->flags
|= CG3_FLAG_BLANKED
;
213 spin_unlock_irqrestore(&par
->lock
, flags
);
218 static struct sbus_mmap_map cg3_mmap_map
[] = {
220 .voff
= CG3_MMAP_OFFSET
,
221 .poff
= CG3_RAM_OFFSET
,
222 .size
= SBUS_MMAP_FBSIZE(1)
227 static int cg3_mmap(struct fb_info
*info
, struct vm_area_struct
*vma
)
229 struct cg3_par
*par
= (struct cg3_par
*)info
->par
;
231 return sbusfb_mmap_helper(cg3_mmap_map
,
232 info
->fix
.smem_start
, info
->fix
.smem_len
,
237 static int cg3_ioctl(struct fb_info
*info
, unsigned int cmd
, unsigned long arg
)
239 return sbusfb_ioctl_helper(cmd
, arg
, info
,
240 FBTYPE_SUN3COLOR
, 8, info
->fix
.smem_len
);
247 static void cg3_init_fix(struct fb_info
*info
, int linebytes
,
248 struct device_node
*dp
)
250 snprintf(info
->fix
.id
, sizeof(info
->fix
.id
), "%pOFn", dp
);
252 info
->fix
.type
= FB_TYPE_PACKED_PIXELS
;
253 info
->fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
255 info
->fix
.line_length
= linebytes
;
257 info
->fix
.accel
= FB_ACCEL_SUN_CGTHREE
;
260 static void cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo
*var
,
261 struct device_node
*dp
)
267 params
= of_get_property(dp
, "params", NULL
);
269 ww
= simple_strtoul(params
, &p
, 10);
270 if (ww
&& *p
== 'x') {
271 hh
= simple_strtoul(p
+ 1, &p
, 10);
272 if (hh
&& *p
== '-') {
273 if (var
->xres
!= ww
||
275 var
->xres
= var
->xres_virtual
= ww
;
276 var
->yres
= var
->yres_virtual
= hh
;
283 static u8 cg3regvals_66hz
[] = { /* 1152 x 900, 66 Hz */
284 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14,
285 0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24,
286 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
290 static u8 cg3regvals_76hz
[] = { /* 1152 x 900, 76 Hz */
291 0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f,
292 0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a,
293 0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01,
297 static u8 cg3regvals_rdi
[] = { /* 640 x 480, cgRDI */
298 0x14, 0x70, 0x15, 0x20, 0x16, 0x08, 0x17, 0x10,
299 0x18, 0x06, 0x19, 0x02, 0x1a, 0x31, 0x1b, 0x51,
300 0x1c, 0x06, 0x1d, 0x0c, 0x1e, 0xff, 0x1f, 0x01,
304 static u8
*cg3_regvals
[] = {
305 cg3regvals_66hz
, cg3regvals_76hz
, cg3regvals_rdi
308 static u_char cg3_dacvals
[] = {
309 4, 0xff, 5, 0x00, 6, 0x70, 7, 0x00, 0
312 static int cg3_do_default_mode(struct cg3_par
*par
)
317 if (par
->flags
& CG3_FLAG_RDI
)
320 u8 status
= sbus_readb(&par
->regs
->status
), mon
;
321 if ((status
& CG3_SR_ID_MASK
) == CG3_SR_ID_COLOR
) {
322 mon
= status
& CG3_SR_RES_MASK
;
323 if (mon
== CG3_SR_1152_900_76_A
||
324 mon
== CG3_SR_1152_900_76_B
)
329 printk(KERN_ERR
"cgthree: can't handle SR %02x\n",
335 for (p
= cg3_regvals
[type
]; *p
; p
+= 2) {
336 u8 __iomem
*regp
= &((u8 __iomem
*)par
->regs
)[p
[0]];
337 sbus_writeb(p
[1], regp
);
339 for (p
= cg3_dacvals
; *p
; p
+= 2) {
342 regp
= (u8 __iomem
*)&par
->regs
->cmap
.addr
;
343 sbus_writeb(p
[0], regp
);
344 regp
= (u8 __iomem
*)&par
->regs
->cmap
.control
;
345 sbus_writeb(p
[1], regp
);
350 static int cg3_probe(struct platform_device
*op
)
352 struct device_node
*dp
= op
->dev
.of_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 (of_node_name_eq(dp
, "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 err
= fb_alloc_cmap(&info
->cmap
, 256, 0);
404 goto out_unmap_screen
;
406 fb_set_cmap(&info
->cmap
, info
);
408 cg3_init_fix(info
, linebytes
, dp
);
410 err
= register_framebuffer(info
);
412 goto out_dealloc_cmap
;
414 dev_set_drvdata(&op
->dev
, info
);
416 printk(KERN_INFO
"%pOF: cg3 at %lx:%lx\n",
417 dp
, par
->which_io
, info
->fix
.smem_start
);
422 fb_dealloc_cmap(&info
->cmap
);
425 of_iounmap(&op
->resource
[0], info
->screen_base
, info
->fix
.smem_len
);
428 of_iounmap(&op
->resource
[0], par
->regs
, sizeof(struct cg3_regs
));
431 framebuffer_release(info
);
437 static int cg3_remove(struct platform_device
*op
)
439 struct fb_info
*info
= dev_get_drvdata(&op
->dev
);
440 struct cg3_par
*par
= info
->par
;
442 unregister_framebuffer(info
);
443 fb_dealloc_cmap(&info
->cmap
);
445 of_iounmap(&op
->resource
[0], par
->regs
, sizeof(struct cg3_regs
));
446 of_iounmap(&op
->resource
[0], info
->screen_base
, info
->fix
.smem_len
);
448 framebuffer_release(info
);
453 static const struct of_device_id cg3_match
[] = {
462 MODULE_DEVICE_TABLE(of
, cg3_match
);
464 static struct platform_driver cg3_driver
= {
467 .of_match_table
= cg3_match
,
470 .remove
= cg3_remove
,
473 static int __init
cg3_init(void)
475 if (fb_get_options("cg3fb", NULL
))
478 return platform_driver_register(&cg3_driver
);
481 static void __exit
cg3_exit(void)
483 platform_driver_unregister(&cg3_driver
);
486 module_init(cg3_init
);
487 module_exit(cg3_exit
);
489 MODULE_DESCRIPTION("framebuffer driver for CGthree chipsets");
490 MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
491 MODULE_VERSION("2.0");
492 MODULE_LICENSE("GPL");