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>
21 #include <linux/platform_device.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_sbusfb_mmap(struct fb_info
*info
, struct vm_area_struct
*vma
);
37 static int cg3_sbusfb_ioctl(struct fb_info
*info
, unsigned int cmd
, unsigned long arg
);
40 * Frame buffer operations
43 static const struct fb_ops cg3_ops
= {
45 FB_DEFAULT_SBUS_OPS(cg3
),
46 .fb_setcolreg
= cg3_setcolreg
,
47 .fb_blank
= cg3_blank
,
51 /* Control Register Constants */
52 #define CG3_CR_ENABLE_INTS 0x80
53 #define CG3_CR_ENABLE_VIDEO 0x40
54 #define CG3_CR_ENABLE_TIMING 0x20
55 #define CG3_CR_ENABLE_CURCMP 0x10
56 #define CG3_CR_XTAL_MASK 0x0c
57 #define CG3_CR_DIVISOR_MASK 0x03
59 /* Status Register Constants */
60 #define CG3_SR_PENDING_INT 0x80
61 #define CG3_SR_RES_MASK 0x70
62 #define CG3_SR_1152_900_76_A 0x40
63 #define CG3_SR_1152_900_76_B 0x60
64 #define CG3_SR_ID_MASK 0x0f
65 #define CG3_SR_ID_COLOR 0x01
66 #define CG3_SR_ID_MONO 0x02
67 #define CG3_SR_ID_MONO_ECL 0x03
93 u8 v_blank_start_high
;
98 u8 xfer_holdoff_start
;
102 /* Offset of interesting structures in the OBIO space */
103 #define CG3_REGS_OFFSET 0x400000UL
104 #define CG3_RAM_OFFSET 0x800000UL
108 struct cg3_regs __iomem
*regs
;
109 u32 sw_cmap
[((256 * 3) + 3) / 4];
112 #define CG3_FLAG_BLANKED 0x00000001
113 #define CG3_FLAG_RDI 0x00000002
115 unsigned long which_io
;
119 * cg3_setcolreg - Optional function. Sets a color register.
120 * @regno: boolean, 0 copy local, 1 get_user() function
121 * @red: frame buffer colormap structure
122 * @green: The green value which can be up to 16 bits wide
123 * @blue: The blue value which can be up to 16 bits wide.
124 * @transp: If supported the alpha value which can be up to 16 bits wide.
125 * @info: frame buffer info structure
127 * The cg3 palette is loaded with 4 color values at each time
128 * so you end up with: (rgb)(r), (gb)(rg), (b)(rgb), and so on.
129 * We keep a sw copy of the hw cmap to assist us in this esoteric
132 static int cg3_setcolreg(unsigned regno
,
133 unsigned red
, unsigned green
, unsigned blue
,
134 unsigned transp
, struct fb_info
*info
)
136 struct cg3_par
*par
= (struct cg3_par
*) info
->par
;
137 struct bt_regs __iomem
*bt
= &par
->regs
->cmap
;
150 spin_lock_irqsave(&par
->lock
, flags
);
152 p8
= (u8
*)par
->sw_cmap
+ (regno
* 3);
157 #define D4M3(x) ((((x)>>2)<<1) + ((x)>>2)) /* (x/4)*3 */
158 #define D4M4(x) ((x)&~0x3) /* (x/4)*4 */
161 p32
= &par
->sw_cmap
[D4M3(regno
)];
162 sbus_writel(D4M4(regno
), &bt
->addr
);
164 sbus_writel(*p32
++, &bt
->color_map
);
169 spin_unlock_irqrestore(&par
->lock
, flags
);
175 * cg3_blank - Optional function. Blanks the display.
176 * @blank: the blank mode we want.
177 * @info: frame buffer structure that represents a single frame buffer
179 static int cg3_blank(int blank
, struct fb_info
*info
)
181 struct cg3_par
*par
= (struct cg3_par
*) info
->par
;
182 struct cg3_regs __iomem
*regs
= par
->regs
;
186 spin_lock_irqsave(&par
->lock
, flags
);
189 case FB_BLANK_UNBLANK
: /* Unblanking */
190 val
= sbus_readb(®s
->control
);
191 val
|= CG3_CR_ENABLE_VIDEO
;
192 sbus_writeb(val
, ®s
->control
);
193 par
->flags
&= ~CG3_FLAG_BLANKED
;
196 case FB_BLANK_NORMAL
: /* Normal blanking */
197 case FB_BLANK_VSYNC_SUSPEND
: /* VESA blank (vsync off) */
198 case FB_BLANK_HSYNC_SUSPEND
: /* VESA blank (hsync off) */
199 case FB_BLANK_POWERDOWN
: /* Poweroff */
200 val
= sbus_readb(®s
->control
);
201 val
&= ~CG3_CR_ENABLE_VIDEO
;
202 sbus_writeb(val
, ®s
->control
);
203 par
->flags
|= CG3_FLAG_BLANKED
;
207 spin_unlock_irqrestore(&par
->lock
, flags
);
212 static const struct sbus_mmap_map cg3_mmap_map
[] = {
214 .voff
= CG3_MMAP_OFFSET
,
215 .poff
= CG3_RAM_OFFSET
,
216 .size
= SBUS_MMAP_FBSIZE(1)
221 static int cg3_sbusfb_mmap(struct fb_info
*info
, struct vm_area_struct
*vma
)
223 struct cg3_par
*par
= (struct cg3_par
*)info
->par
;
225 return sbusfb_mmap_helper(cg3_mmap_map
,
226 info
->fix
.smem_start
, info
->fix
.smem_len
,
231 static int cg3_sbusfb_ioctl(struct fb_info
*info
, unsigned int cmd
, unsigned long arg
)
233 return sbusfb_ioctl_helper(cmd
, arg
, info
,
234 FBTYPE_SUN3COLOR
, 8, info
->fix
.smem_len
);
241 static void cg3_init_fix(struct fb_info
*info
, int linebytes
,
242 struct device_node
*dp
)
244 snprintf(info
->fix
.id
, sizeof(info
->fix
.id
), "%pOFn", dp
);
246 info
->fix
.type
= FB_TYPE_PACKED_PIXELS
;
247 info
->fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
249 info
->fix
.line_length
= linebytes
;
251 info
->fix
.accel
= FB_ACCEL_SUN_CGTHREE
;
254 static void cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo
*var
,
255 struct device_node
*dp
)
261 params
= of_get_property(dp
, "params", NULL
);
263 ww
= simple_strtoul(params
, &p
, 10);
264 if (ww
&& *p
== 'x') {
265 hh
= simple_strtoul(p
+ 1, &p
, 10);
266 if (hh
&& *p
== '-') {
267 if (var
->xres
!= ww
||
269 var
->xres
= var
->xres_virtual
= ww
;
270 var
->yres
= var
->yres_virtual
= hh
;
277 static u8 cg3regvals_66hz
[] = { /* 1152 x 900, 66 Hz */
278 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14,
279 0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24,
280 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
284 static u8 cg3regvals_76hz
[] = { /* 1152 x 900, 76 Hz */
285 0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f,
286 0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a,
287 0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01,
291 static u8 cg3regvals_rdi
[] = { /* 640 x 480, cgRDI */
292 0x14, 0x70, 0x15, 0x20, 0x16, 0x08, 0x17, 0x10,
293 0x18, 0x06, 0x19, 0x02, 0x1a, 0x31, 0x1b, 0x51,
294 0x1c, 0x06, 0x1d, 0x0c, 0x1e, 0xff, 0x1f, 0x01,
298 static u8
*cg3_regvals
[] = {
299 cg3regvals_66hz
, cg3regvals_76hz
, cg3regvals_rdi
302 static u_char cg3_dacvals
[] = {
303 4, 0xff, 5, 0x00, 6, 0x70, 7, 0x00, 0
306 static int cg3_do_default_mode(struct cg3_par
*par
)
311 if (par
->flags
& CG3_FLAG_RDI
)
314 u8 status
= sbus_readb(&par
->regs
->status
), mon
;
315 if ((status
& CG3_SR_ID_MASK
) == CG3_SR_ID_COLOR
) {
316 mon
= status
& CG3_SR_RES_MASK
;
317 if (mon
== CG3_SR_1152_900_76_A
||
318 mon
== CG3_SR_1152_900_76_B
)
323 printk(KERN_ERR
"cgthree: can't handle SR %02x\n",
329 for (p
= cg3_regvals
[type
]; *p
; p
+= 2) {
330 u8 __iomem
*regp
= &((u8 __iomem
*)par
->regs
)[p
[0]];
331 sbus_writeb(p
[1], regp
);
333 for (p
= cg3_dacvals
; *p
; p
+= 2) {
336 regp
= (u8 __iomem
*)&par
->regs
->cmap
.addr
;
337 sbus_writeb(p
[0], regp
);
338 regp
= (u8 __iomem
*)&par
->regs
->cmap
.control
;
339 sbus_writeb(p
[1], regp
);
344 static int cg3_probe(struct platform_device
*op
)
346 struct device_node
*dp
= op
->dev
.of_node
;
347 struct fb_info
*info
;
351 info
= framebuffer_alloc(sizeof(struct cg3_par
), &op
->dev
);
358 spin_lock_init(&par
->lock
);
360 info
->fix
.smem_start
= op
->resource
[0].start
;
361 par
->which_io
= op
->resource
[0].flags
& IORESOURCE_BITS
;
363 sbusfb_fill_var(&info
->var
, dp
, 8);
364 info
->var
.red
.length
= 8;
365 info
->var
.green
.length
= 8;
366 info
->var
.blue
.length
= 8;
367 if (of_node_name_eq(dp
, "cgRDI"))
368 par
->flags
|= CG3_FLAG_RDI
;
369 if (par
->flags
& CG3_FLAG_RDI
)
370 cg3_rdi_maybe_fixup_var(&info
->var
, dp
);
372 linebytes
= of_getintprop_default(dp
, "linebytes",
374 info
->fix
.smem_len
= PAGE_ALIGN(linebytes
* info
->var
.yres
);
376 par
->regs
= of_ioremap(&op
->resource
[0], CG3_REGS_OFFSET
,
377 sizeof(struct cg3_regs
), "cg3 regs");
381 info
->fbops
= &cg3_ops
;
382 info
->screen_base
= of_ioremap(&op
->resource
[0], CG3_RAM_OFFSET
,
383 info
->fix
.smem_len
, "cg3 ram");
384 if (!info
->screen_base
)
387 cg3_blank(FB_BLANK_UNBLANK
, info
);
389 if (!of_property_present(dp
, "width")) {
390 err
= cg3_do_default_mode(par
);
392 goto out_unmap_screen
;
395 err
= fb_alloc_cmap(&info
->cmap
, 256, 0);
397 goto out_unmap_screen
;
399 fb_set_cmap(&info
->cmap
, info
);
401 cg3_init_fix(info
, linebytes
, dp
);
403 err
= register_framebuffer(info
);
405 goto out_dealloc_cmap
;
407 dev_set_drvdata(&op
->dev
, info
);
409 printk(KERN_INFO
"%pOF: cg3 at %lx:%lx\n",
410 dp
, par
->which_io
, info
->fix
.smem_start
);
415 fb_dealloc_cmap(&info
->cmap
);
418 of_iounmap(&op
->resource
[0], info
->screen_base
, info
->fix
.smem_len
);
421 of_iounmap(&op
->resource
[0], par
->regs
, sizeof(struct cg3_regs
));
424 framebuffer_release(info
);
430 static void cg3_remove(struct platform_device
*op
)
432 struct fb_info
*info
= dev_get_drvdata(&op
->dev
);
433 struct cg3_par
*par
= info
->par
;
435 unregister_framebuffer(info
);
436 fb_dealloc_cmap(&info
->cmap
);
438 of_iounmap(&op
->resource
[0], par
->regs
, sizeof(struct cg3_regs
));
439 of_iounmap(&op
->resource
[0], info
->screen_base
, info
->fix
.smem_len
);
441 framebuffer_release(info
);
444 static const struct of_device_id cg3_match
[] = {
453 MODULE_DEVICE_TABLE(of
, cg3_match
);
455 static struct platform_driver cg3_driver
= {
458 .of_match_table
= cg3_match
,
461 .remove
= cg3_remove
,
464 static int __init
cg3_init(void)
466 if (fb_get_options("cg3fb", NULL
))
469 return platform_driver_register(&cg3_driver
);
472 static void __exit
cg3_exit(void)
474 platform_driver_unregister(&cg3_driver
);
477 module_init(cg3_init
);
478 module_exit(cg3_exit
);
480 MODULE_DESCRIPTION("framebuffer driver for CGthree chipsets");
481 MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
482 MODULE_VERSION("2.0");
483 MODULE_LICENSE("GPL");