xref: /linux/drivers/video/fbdev/cg14.c (revision 89755ee1d59311855b4afcf35aebddcb653b3cd5)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* cg14.c: CGFOURTEEN frame buffer driver
3  *
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) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
7  *
8  * Driver layout based loosely on tgafb.c, see that file for credits.
9  */
10 
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>
17 #include <linux/fb.h>
18 #include <linux/mm.h>
19 #include <linux/uaccess.h>
20 #include <linux/of_device.h>
21 
22 #include <asm/io.h>
23 #include <asm/fbio.h>
24 
25 #include "sbuslib.h"
26 
27 /*
28  * Local functions.
29  */
30 
31 static int cg14_setcolreg(unsigned, unsigned, unsigned, unsigned,
32 			 unsigned, struct fb_info *);
33 static int cg14_pan_display(struct fb_var_screeninfo *, struct fb_info *);
34 
35 static int cg14_sbusfb_mmap(struct fb_info *info, struct vm_area_struct *vma);
36 static int cg14_sbusfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg);
37 
38 /*
39  *  Frame buffer operations
40  */
41 
42 static const struct fb_ops cg14_ops = {
43 	.owner			= THIS_MODULE,
44 	FB_DEFAULT_SBUS_OPS(cg14),
45 	.fb_setcolreg		= cg14_setcolreg,
46 	.fb_pan_display		= cg14_pan_display,
47 };
48 
49 #define CG14_MCR_INTENABLE_SHIFT	7
50 #define CG14_MCR_INTENABLE_MASK		0x80
51 #define CG14_MCR_VIDENABLE_SHIFT	6
52 #define CG14_MCR_VIDENABLE_MASK		0x40
53 #define CG14_MCR_PIXMODE_SHIFT		4
54 #define CG14_MCR_PIXMODE_MASK		0x30
55 #define CG14_MCR_TMR_SHIFT		2
56 #define CG14_MCR_TMR_MASK		0x0c
57 #define CG14_MCR_TMENABLE_SHIFT		1
58 #define CG14_MCR_TMENABLE_MASK		0x02
59 #define CG14_MCR_RESET_SHIFT		0
60 #define CG14_MCR_RESET_MASK		0x01
61 #define CG14_REV_REVISION_SHIFT		4
62 #define CG14_REV_REVISION_MASK		0xf0
63 #define CG14_REV_IMPL_SHIFT		0
64 #define CG14_REV_IMPL_MASK		0x0f
65 #define CG14_VBR_FRAMEBASE_SHIFT	12
66 #define CG14_VBR_FRAMEBASE_MASK		0x00fff000
67 #define CG14_VMCR1_SETUP_SHIFT		0
68 #define CG14_VMCR1_SETUP_MASK		0x000001ff
69 #define CG14_VMCR1_VCONFIG_SHIFT	9
70 #define CG14_VMCR1_VCONFIG_MASK		0x00000e00
71 #define CG14_VMCR2_REFRESH_SHIFT	0
72 #define CG14_VMCR2_REFRESH_MASK		0x00000001
73 #define CG14_VMCR2_TESTROWCNT_SHIFT	1
74 #define CG14_VMCR2_TESTROWCNT_MASK	0x00000002
75 #define CG14_VMCR2_FBCONFIG_SHIFT	2
76 #define CG14_VMCR2_FBCONFIG_MASK	0x0000000c
77 #define CG14_VCR_REFRESHREQ_SHIFT	0
78 #define CG14_VCR_REFRESHREQ_MASK	0x000003ff
79 #define CG14_VCR1_REFRESHENA_SHIFT	10
80 #define CG14_VCR1_REFRESHENA_MASK	0x00000400
81 #define CG14_VCA_CAD_SHIFT		0
82 #define CG14_VCA_CAD_MASK		0x000003ff
83 #define CG14_VCA_VERS_SHIFT		10
84 #define CG14_VCA_VERS_MASK		0x00000c00
85 #define CG14_VCA_RAMSPEED_SHIFT		12
86 #define CG14_VCA_RAMSPEED_MASK		0x00001000
87 #define CG14_VCA_8MB_SHIFT		13
88 #define CG14_VCA_8MB_MASK		0x00002000
89 
90 #define CG14_MCR_PIXMODE_8		0
91 #define CG14_MCR_PIXMODE_16		2
92 #define CG14_MCR_PIXMODE_32		3
93 
94 struct cg14_regs{
95 	u8 mcr;	/* Master Control Reg */
96 	u8 ppr;	/* Packed Pixel Reg */
97 	u8 tms[2];	/* Test Mode Status Regs */
98 	u8 msr;	/* Master Status Reg */
99 	u8 fsr;	/* Fault Status Reg */
100 	u8 rev;	/* Revision & Impl */
101 	u8 ccr;	/* Clock Control Reg */
102 	u32 tmr;	/* Test Mode Read Back */
103 	u8 mod;	/* Monitor Operation Data Reg */
104 	u8 acr;	/* Aux Control */
105 	u8 xxx0[6];
106 	u16 hct;	/* Hor Counter */
107 	u16 vct;	/* Vert Counter */
108 	u16 hbs;	/* Hor Blank Start */
109 	u16 hbc;	/* Hor Blank Clear */
110 	u16 hss;	/* Hor Sync Start */
111 	u16 hsc;	/* Hor Sync Clear */
112 	u16 csc;	/* Composite Sync Clear */
113 	u16 vbs;	/* Vert Blank Start */
114 	u16 vbc;	/* Vert Blank Clear */
115 	u16 vss;	/* Vert Sync Start */
116 	u16 vsc;	/* Vert Sync Clear */
117 	u16 xcs;
118 	u16 xcc;
119 	u16 fsa;	/* Fault Status Address */
120 	u16 adr;	/* Address Registers */
121 	u8 xxx1[0xce];
122 	u8 pcg[0x100]; /* Pixel Clock Generator */
123 	u32 vbr;	/* Frame Base Row */
124 	u32 vmcr;	/* VBC Master Control */
125 	u32 vcr;	/* VBC refresh */
126 	u32 vca;	/* VBC Config */
127 };
128 
129 #define CG14_CCR_ENABLE	0x04
130 #define CG14_CCR_SELECT 0x02	/* HW/Full screen */
131 
132 struct cg14_cursor {
133 	u32 cpl0[32];	/* Enable plane 0 */
134 	u32 cpl1[32];  /* Color selection plane */
135 	u8 ccr;	/* Cursor Control Reg */
136 	u8 xxx0[3];
137 	u16 cursx;	/* Cursor x,y position */
138 	u16 cursy;	/* Cursor x,y position */
139 	u32 color0;
140 	u32 color1;
141 	u32 xxx1[0x1bc];
142 	u32 cpl0i[32];	/* Enable plane 0 autoinc */
143 	u32 cpl1i[32]; /* Color selection autoinc */
144 };
145 
146 struct cg14_dac {
147 	u8 addr;	/* Address Register */
148 	u8 xxx0[255];
149 	u8 glut;	/* Gamma table */
150 	u8 xxx1[255];
151 	u8 select;	/* Register Select */
152 	u8 xxx2[255];
153 	u8 mode;	/* Mode Register */
154 };
155 
156 struct cg14_xlut{
157 	u8 x_xlut [256];
158 	u8 x_xlutd [256];
159 	u8 xxx0[0x600];
160 	u8 x_xlut_inc [256];
161 	u8 x_xlutd_inc [256];
162 };
163 
164 /* Color look up table (clut) */
165 /* Each one of these arrays hold the color lookup table (for 256
166  * colors) for each MDI page (I assume then there should be 4 MDI
167  * pages, I still wonder what they are.  I have seen NeXTStep split
168  * the screen in four parts, while operating in 24 bits mode.  Each
169  * integer holds 4 values: alpha value (transparency channel, thanks
170  * go to John Stone (johns@umr.edu) from OpenBSD), red, green and blue
171  *
172  * I currently use the clut instead of the Xlut
173  */
174 struct cg14_clut {
175 	u32 c_clut [256];
176 	u32 c_clutd [256];    /* i wonder what the 'd' is for */
177 	u32 c_clut_inc [256];
178 	u32 c_clutd_inc [256];
179 };
180 
181 #define CG14_MMAP_ENTRIES	16
182 
183 struct cg14_par {
184 	spinlock_t		lock;
185 	struct cg14_regs	__iomem *regs;
186 	struct cg14_clut	__iomem *clut;
187 	struct cg14_cursor	__iomem *cursor;
188 
189 	u32			flags;
190 #define CG14_FLAG_BLANKED	0x00000001
191 
192 	unsigned long		iospace;
193 
194 	struct sbus_mmap_map	mmap_map[CG14_MMAP_ENTRIES];
195 
196 	int			mode;
197 	int			ramsize;
198 };
199 
200 static void __cg14_reset(struct cg14_par *par)
201 {
202 	struct cg14_regs __iomem *regs = par->regs;
203 	u8 val;
204 
205 	val = sbus_readb(&regs->mcr);
206 	val &= ~(CG14_MCR_PIXMODE_MASK);
207 	sbus_writeb(val, &regs->mcr);
208 }
209 
210 static int cg14_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
211 {
212 	struct cg14_par *par = (struct cg14_par *) info->par;
213 	unsigned long flags;
214 
215 	/* We just use this to catch switches out of
216 	 * graphics mode.
217 	 */
218 	spin_lock_irqsave(&par->lock, flags);
219 	__cg14_reset(par);
220 	spin_unlock_irqrestore(&par->lock, flags);
221 
222 	if (var->xoffset || var->yoffset || var->vmode)
223 		return -EINVAL;
224 	return 0;
225 }
226 
227 /**
228  *      cg14_setcolreg - Optional function. Sets a color register.
229  *      @regno: boolean, 0 copy local, 1 get_user() function
230  *      @red: frame buffer colormap structure
231  *      @green: The green value which can be up to 16 bits wide
232  *      @blue:  The blue value which can be up to 16 bits wide.
233  *      @transp: If supported the alpha value which can be up to 16 bits wide.
234  *      @info: frame buffer info structure
235  */
236 static int cg14_setcolreg(unsigned regno,
237 			  unsigned red, unsigned green, unsigned blue,
238 			  unsigned transp, struct fb_info *info)
239 {
240 	struct cg14_par *par = (struct cg14_par *) info->par;
241 	struct cg14_clut __iomem *clut = par->clut;
242 	unsigned long flags;
243 	u32 val;
244 
245 	if (regno >= 256)
246 		return 1;
247 
248 	red >>= 8;
249 	green >>= 8;
250 	blue >>= 8;
251 	val = (red | (green << 8) | (blue << 16));
252 
253 	spin_lock_irqsave(&par->lock, flags);
254 	sbus_writel(val, &clut->c_clut[regno]);
255 	spin_unlock_irqrestore(&par->lock, flags);
256 
257 	return 0;
258 }
259 
260 static int cg14_sbusfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
261 {
262 	struct cg14_par *par = (struct cg14_par *) info->par;
263 
264 	return sbusfb_mmap_helper(par->mmap_map,
265 				  info->fix.smem_start, info->fix.smem_len,
266 				  par->iospace, vma);
267 }
268 
269 static int cg14_sbusfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
270 {
271 	struct cg14_par *par = (struct cg14_par *) info->par;
272 	struct cg14_regs __iomem *regs = par->regs;
273 	struct mdi_cfginfo kmdi, __user *mdii;
274 	unsigned long flags;
275 	int cur_mode, mode, ret = 0;
276 
277 	switch (cmd) {
278 	case MDI_RESET:
279 		spin_lock_irqsave(&par->lock, flags);
280 		__cg14_reset(par);
281 		spin_unlock_irqrestore(&par->lock, flags);
282 		break;
283 
284 	case MDI_GET_CFGINFO:
285 		memset(&kmdi, 0, sizeof(kmdi));
286 
287 		spin_lock_irqsave(&par->lock, flags);
288 		kmdi.mdi_type = FBTYPE_MDICOLOR;
289 		kmdi.mdi_height = info->var.yres;
290 		kmdi.mdi_width = info->var.xres;
291 		kmdi.mdi_mode = par->mode;
292 		kmdi.mdi_pixfreq = 72; /* FIXME */
293 		kmdi.mdi_size = par->ramsize;
294 		spin_unlock_irqrestore(&par->lock, flags);
295 
296 		mdii = (struct mdi_cfginfo __user *) arg;
297 		if (copy_to_user(mdii, &kmdi, sizeof(kmdi)))
298 			ret = -EFAULT;
299 		break;
300 
301 	case MDI_SET_PIXELMODE:
302 		if (get_user(mode, (int __user *) arg)) {
303 			ret = -EFAULT;
304 			break;
305 		}
306 
307 		spin_lock_irqsave(&par->lock, flags);
308 		cur_mode = sbus_readb(&regs->mcr);
309 		cur_mode &= ~CG14_MCR_PIXMODE_MASK;
310 		switch(mode) {
311 		case MDI_32_PIX:
312 			cur_mode |= (CG14_MCR_PIXMODE_32 <<
313 				     CG14_MCR_PIXMODE_SHIFT);
314 			break;
315 
316 		case MDI_16_PIX:
317 			cur_mode |= (CG14_MCR_PIXMODE_16 <<
318 				     CG14_MCR_PIXMODE_SHIFT);
319 			break;
320 
321 		case MDI_8_PIX:
322 			break;
323 
324 		default:
325 			ret = -ENOSYS;
326 			break;
327 		}
328 		if (!ret) {
329 			sbus_writeb(cur_mode, &regs->mcr);
330 			par->mode = mode;
331 		}
332 		spin_unlock_irqrestore(&par->lock, flags);
333 		break;
334 
335 	default:
336 		ret = sbusfb_ioctl_helper(cmd, arg, info,
337 					  FBTYPE_MDICOLOR, 8,
338 					  info->fix.smem_len);
339 		break;
340 	}
341 
342 	return ret;
343 }
344 
345 /*
346  *  Initialisation
347  */
348 
349 static void cg14_init_fix(struct fb_info *info, int linebytes,
350 			  struct device_node *dp)
351 {
352 	snprintf(info->fix.id, sizeof(info->fix.id), "%pOFn", dp);
353 
354 	info->fix.type = FB_TYPE_PACKED_PIXELS;
355 	info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
356 
357 	info->fix.line_length = linebytes;
358 
359 	info->fix.accel = FB_ACCEL_SUN_CG14;
360 }
361 
362 static struct sbus_mmap_map __cg14_mmap_map[CG14_MMAP_ENTRIES] = {
363 	{
364 		.voff	= CG14_REGS,
365 		.poff	= 0x80000000,
366 		.size	= 0x1000
367 	},
368 	{
369 		.voff	= CG14_XLUT,
370 		.poff	= 0x80003000,
371 		.size	= 0x1000
372 	},
373 	{
374 		.voff	= CG14_CLUT1,
375 		.poff	= 0x80004000,
376 		.size	= 0x1000
377 	},
378 	{
379 		.voff	= CG14_CLUT2,
380 		.poff	= 0x80005000,
381 		.size	= 0x1000
382 	},
383 	{
384 		.voff	= CG14_CLUT3,
385 		.poff	= 0x80006000,
386 		.size	= 0x1000
387 	},
388 	{
389 		.voff	= CG3_MMAP_OFFSET - 0x7000,
390 		.poff	= 0x80000000,
391 		.size	= 0x7000
392 	},
393 	{
394 		.voff	= CG3_MMAP_OFFSET,
395 		.poff	= 0x00000000,
396 		.size	= SBUS_MMAP_FBSIZE(1)
397 	},
398 	{
399 		.voff	= MDI_CURSOR_MAP,
400 		.poff	= 0x80001000,
401 		.size	= 0x1000
402 	},
403 	{
404 		.voff	= MDI_CHUNKY_BGR_MAP,
405 		.poff	= 0x01000000,
406 		.size	= 0x400000
407 	},
408 	{
409 		.voff	= MDI_PLANAR_X16_MAP,
410 		.poff	= 0x02000000,
411 		.size	= 0x200000
412 	},
413 	{
414 		.voff	= MDI_PLANAR_C16_MAP,
415 		.poff	= 0x02800000,
416 		.size	= 0x200000
417 	},
418 	{
419 		.voff	= MDI_PLANAR_X32_MAP,
420 		.poff	= 0x03000000,
421 		.size	= 0x100000
422 	},
423 	{
424 		.voff	= MDI_PLANAR_B32_MAP,
425 		.poff	= 0x03400000,
426 		.size	= 0x100000
427 	},
428 	{
429 		.voff	= MDI_PLANAR_G32_MAP,
430 		.poff	= 0x03800000,
431 		.size	= 0x100000
432 	},
433 	{
434 		.voff	= MDI_PLANAR_R32_MAP,
435 		.poff	= 0x03c00000,
436 		.size	= 0x100000
437 	},
438 	{ .size = 0 }
439 };
440 
441 static void cg14_unmap_regs(struct platform_device *op, struct fb_info *info,
442 			    struct cg14_par *par)
443 {
444 	if (par->regs)
445 		of_iounmap(&op->resource[0],
446 			   par->regs, sizeof(struct cg14_regs));
447 	if (par->clut)
448 		of_iounmap(&op->resource[0],
449 			   par->clut, sizeof(struct cg14_clut));
450 	if (par->cursor)
451 		of_iounmap(&op->resource[0],
452 			   par->cursor, sizeof(struct cg14_cursor));
453 	if (info->screen_base)
454 		of_iounmap(&op->resource[1],
455 			   info->screen_base, info->fix.smem_len);
456 }
457 
458 static int cg14_probe(struct platform_device *op)
459 {
460 	struct device_node *dp = op->dev.of_node;
461 	struct fb_info *info;
462 	struct cg14_par *par;
463 	int is_8mb, linebytes, i, err;
464 
465 	info = framebuffer_alloc(sizeof(struct cg14_par), &op->dev);
466 
467 	err = -ENOMEM;
468 	if (!info)
469 		goto out_err;
470 	par = info->par;
471 
472 	spin_lock_init(&par->lock);
473 
474 	sbusfb_fill_var(&info->var, dp, 8);
475 	info->var.red.length = 8;
476 	info->var.green.length = 8;
477 	info->var.blue.length = 8;
478 
479 	linebytes = of_getintprop_default(dp, "linebytes",
480 					  info->var.xres);
481 	info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres);
482 
483 	if (of_node_name_eq(dp->parent, "sbus") ||
484 	    of_node_name_eq(dp->parent, "sbi")) {
485 		info->fix.smem_start = op->resource[0].start;
486 		par->iospace = op->resource[0].flags & IORESOURCE_BITS;
487 	} else {
488 		info->fix.smem_start = op->resource[1].start;
489 		par->iospace = op->resource[0].flags & IORESOURCE_BITS;
490 	}
491 
492 	par->regs = of_ioremap(&op->resource[0], 0,
493 			       sizeof(struct cg14_regs), "cg14 regs");
494 	par->clut = of_ioremap(&op->resource[0], CG14_CLUT1,
495 			       sizeof(struct cg14_clut), "cg14 clut");
496 	par->cursor = of_ioremap(&op->resource[0], CG14_CURSORREGS,
497 				 sizeof(struct cg14_cursor), "cg14 cursor");
498 
499 	info->screen_base = of_ioremap(&op->resource[1], 0,
500 				       info->fix.smem_len, "cg14 ram");
501 
502 	if (!par->regs || !par->clut || !par->cursor || !info->screen_base)
503 		goto out_unmap_regs;
504 
505 	is_8mb = (resource_size(&op->resource[1]) == (8 * 1024 * 1024));
506 
507 	BUILD_BUG_ON(sizeof(par->mmap_map) != sizeof(__cg14_mmap_map));
508 
509 	memcpy(&par->mmap_map, &__cg14_mmap_map, sizeof(par->mmap_map));
510 
511 	for (i = 0; i < CG14_MMAP_ENTRIES; i++) {
512 		struct sbus_mmap_map *map = &par->mmap_map[i];
513 
514 		if (!map->size)
515 			break;
516 		if (map->poff & 0x80000000)
517 			map->poff = (map->poff & 0x7fffffff) +
518 				(op->resource[0].start -
519 				 op->resource[1].start);
520 		if (is_8mb &&
521 		    map->size >= 0x100000 &&
522 		    map->size <= 0x400000)
523 			map->size *= 2;
524 	}
525 
526 	par->mode = MDI_8_PIX;
527 	par->ramsize = (is_8mb ? 0x800000 : 0x400000);
528 
529 	info->flags = FBINFO_HWACCEL_YPAN;
530 	info->fbops = &cg14_ops;
531 
532 	__cg14_reset(par);
533 
534 	if (fb_alloc_cmap(&info->cmap, 256, 0))
535 		goto out_unmap_regs;
536 
537 	fb_set_cmap(&info->cmap, info);
538 
539 	cg14_init_fix(info, linebytes, dp);
540 
541 	err = register_framebuffer(info);
542 	if (err < 0)
543 		goto out_dealloc_cmap;
544 
545 	dev_set_drvdata(&op->dev, info);
546 
547 	printk(KERN_INFO "%pOF: cgfourteen at %lx:%lx, %dMB\n",
548 	       dp,
549 	       par->iospace, info->fix.smem_start,
550 	       par->ramsize >> 20);
551 
552 	return 0;
553 
554 out_dealloc_cmap:
555 	fb_dealloc_cmap(&info->cmap);
556 
557 out_unmap_regs:
558 	cg14_unmap_regs(op, info, par);
559 	framebuffer_release(info);
560 
561 out_err:
562 	return err;
563 }
564 
565 static void cg14_remove(struct platform_device *op)
566 {
567 	struct fb_info *info = dev_get_drvdata(&op->dev);
568 	struct cg14_par *par = info->par;
569 
570 	unregister_framebuffer(info);
571 	fb_dealloc_cmap(&info->cmap);
572 
573 	cg14_unmap_regs(op, info, par);
574 
575 	framebuffer_release(info);
576 }
577 
578 static const struct of_device_id cg14_match[] = {
579 	{
580 		.name = "cgfourteen",
581 	},
582 	{},
583 };
584 MODULE_DEVICE_TABLE(of, cg14_match);
585 
586 static struct platform_driver cg14_driver = {
587 	.driver = {
588 		.name = "cg14",
589 		.of_match_table = cg14_match,
590 	},
591 	.probe		= cg14_probe,
592 	.remove_new	= cg14_remove,
593 };
594 
595 static int __init cg14_init(void)
596 {
597 	if (fb_get_options("cg14fb", NULL))
598 		return -ENODEV;
599 
600 	return platform_driver_register(&cg14_driver);
601 }
602 
603 static void __exit cg14_exit(void)
604 {
605 	platform_driver_unregister(&cg14_driver);
606 }
607 
608 module_init(cg14_init);
609 module_exit(cg14_exit);
610 
611 MODULE_DESCRIPTION("framebuffer driver for CGfourteen chipsets");
612 MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
613 MODULE_VERSION("2.0");
614 MODULE_LICENSE("GPL");
615