xref: /openbmc/linux/drivers/video/fbdev/p9100.c (revision b3e148d7)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* p9100.c: P9100 frame buffer driver
3  *
4  * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net)
5  * Copyright 1999 Derrick J Brashear (shadow@dementia.org)
6  *
7  * Driver layout based loosely on tgafb.c, see that file for credits.
8  */
9 
10 #include <linux/module.h>
11 #include <linux/kernel.h>
12 #include <linux/errno.h>
13 #include <linux/string.h>
14 #include <linux/delay.h>
15 #include <linux/init.h>
16 #include <linux/fb.h>
17 #include <linux/mm.h>
18 #include <linux/of.h>
19 #include <linux/platform_device.h>
20 
21 #include <asm/io.h>
22 #include <asm/fbio.h>
23 
24 #include "sbuslib.h"
25 
26 /*
27  * Local functions.
28  */
29 
30 static int p9100_setcolreg(unsigned, unsigned, unsigned, unsigned,
31 			   unsigned, struct fb_info *);
32 static int p9100_blank(int, struct fb_info *);
33 
34 static int p9100_mmap(struct fb_info *, struct vm_area_struct *);
35 static int p9100_ioctl(struct fb_info *, unsigned int, unsigned long);
36 
37 /*
38  *  Frame buffer operations
39  */
40 
41 static const struct fb_ops p9100_ops = {
42 	.owner			= THIS_MODULE,
43 	.fb_setcolreg		= p9100_setcolreg,
44 	.fb_blank		= p9100_blank,
45 	.fb_fillrect		= cfb_fillrect,
46 	.fb_copyarea		= cfb_copyarea,
47 	.fb_imageblit		= cfb_imageblit,
48 	.fb_mmap		= p9100_mmap,
49 	.fb_ioctl		= p9100_ioctl,
50 #ifdef CONFIG_COMPAT
51 	.fb_compat_ioctl	= sbusfb_compat_ioctl,
52 #endif
53 };
54 
55 /* P9100 control registers */
56 #define P9100_SYSCTL_OFF	0x0UL
57 #define P9100_VIDEOCTL_OFF	0x100UL
58 #define P9100_VRAMCTL_OFF 	0x180UL
59 #define P9100_RAMDAC_OFF 	0x200UL
60 #define P9100_VIDEOCOPROC_OFF 	0x400UL
61 
62 /* P9100 command registers */
63 #define P9100_CMD_OFF 0x0UL
64 
65 /* P9100 framebuffer memory */
66 #define P9100_FB_OFF 0x0UL
67 
68 /* 3 bits: 2=8bpp 3=16bpp 5=32bpp 7=24bpp */
69 #define SYS_CONFIG_PIXELSIZE_SHIFT 26
70 
71 #define SCREENPAINT_TIMECTL1_ENABLE_VIDEO 0x20 /* 0 = off, 1 = on */
72 
73 struct p9100_regs {
74 	/* Registers for the system control */
75 	u32 sys_base;
76 	u32 sys_config;
77 	u32 sys_intr;
78 	u32 sys_int_ena;
79 	u32 sys_alt_rd;
80 	u32 sys_alt_wr;
81 	u32 sys_xxx[58];
82 
83 	/* Registers for the video control */
84 	u32 vid_base;
85 	u32 vid_hcnt;
86 	u32 vid_htotal;
87 	u32 vid_hsync_rise;
88 	u32 vid_hblank_rise;
89 	u32 vid_hblank_fall;
90 	u32 vid_hcnt_preload;
91 	u32 vid_vcnt;
92 	u32 vid_vlen;
93 	u32 vid_vsync_rise;
94 	u32 vid_vblank_rise;
95 	u32 vid_vblank_fall;
96 	u32 vid_vcnt_preload;
97 	u32 vid_screenpaint_addr;
98 	u32 vid_screenpaint_timectl1;
99 	u32 vid_screenpaint_qsfcnt;
100 	u32 vid_screenpaint_timectl2;
101 	u32 vid_xxx[15];
102 
103 	/* Registers for the video control */
104 	u32 vram_base;
105 	u32 vram_memcfg;
106 	u32 vram_refresh_pd;
107 	u32 vram_refresh_cnt;
108 	u32 vram_raslo_max;
109 	u32 vram_raslo_cur;
110 	u32 pwrup_cfg;
111 	u32 vram_xxx[25];
112 
113 	/* Registers for IBM RGB528 Palette */
114 	u32 ramdac_cmap_wridx;
115 	u32 ramdac_palette_data;
116 	u32 ramdac_pixel_mask;
117 	u32 ramdac_palette_rdaddr;
118 	u32 ramdac_idx_lo;
119 	u32 ramdac_idx_hi;
120 	u32 ramdac_idx_data;
121 	u32 ramdac_idx_ctl;
122 	u32 ramdac_xxx[1784];
123 };
124 
125 struct p9100_cmd_parameng {
126 	u32 parameng_status;
127 	u32 parameng_bltcmd;
128 	u32 parameng_quadcmd;
129 };
130 
131 struct p9100_par {
132 	spinlock_t		lock;
133 	struct p9100_regs	__iomem *regs;
134 
135 	u32			flags;
136 #define P9100_FLAG_BLANKED	0x00000001
137 
138 	unsigned long		which_io;
139 };
140 
141 /**
142  *      p9100_setcolreg - Optional function. Sets a color register.
143  *      @regno: boolean, 0 copy local, 1 get_user() function
144  *      @red: frame buffer colormap structure
145  *      @green: The green value which can be up to 16 bits wide
146  *      @blue:  The blue value which can be up to 16 bits wide.
147  *      @transp: If supported the alpha value which can be up to 16 bits wide.
148  *      @info: frame buffer info structure
149  */
p9100_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info * info)150 static int p9100_setcolreg(unsigned regno,
151 			   unsigned red, unsigned green, unsigned blue,
152 			   unsigned transp, struct fb_info *info)
153 {
154 	struct p9100_par *par = (struct p9100_par *) info->par;
155 	struct p9100_regs __iomem *regs = par->regs;
156 	unsigned long flags;
157 
158 	if (regno >= 256)
159 		return 1;
160 
161 	red >>= 8;
162 	green >>= 8;
163 	blue >>= 8;
164 
165 	spin_lock_irqsave(&par->lock, flags);
166 
167 	sbus_writel((regno << 16), &regs->ramdac_cmap_wridx);
168 	sbus_writel((red << 16), &regs->ramdac_palette_data);
169 	sbus_writel((green << 16), &regs->ramdac_palette_data);
170 	sbus_writel((blue << 16), &regs->ramdac_palette_data);
171 
172 	spin_unlock_irqrestore(&par->lock, flags);
173 
174 	return 0;
175 }
176 
177 /**
178  *      p9100_blank - Optional function.  Blanks the display.
179  *      @blank: the blank mode we want.
180  *      @info: frame buffer structure that represents a single frame buffer
181  */
182 static int
p9100_blank(int blank,struct fb_info * info)183 p9100_blank(int blank, struct fb_info *info)
184 {
185 	struct p9100_par *par = (struct p9100_par *) info->par;
186 	struct p9100_regs __iomem *regs = par->regs;
187 	unsigned long flags;
188 	u32 val;
189 
190 	spin_lock_irqsave(&par->lock, flags);
191 
192 	switch (blank) {
193 	case FB_BLANK_UNBLANK: /* Unblanking */
194 		val = sbus_readl(&regs->vid_screenpaint_timectl1);
195 		val |= SCREENPAINT_TIMECTL1_ENABLE_VIDEO;
196 		sbus_writel(val, &regs->vid_screenpaint_timectl1);
197 		par->flags &= ~P9100_FLAG_BLANKED;
198 		break;
199 
200 	case FB_BLANK_NORMAL: /* Normal blanking */
201 	case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
202 	case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
203 	case FB_BLANK_POWERDOWN: /* Poweroff */
204 		val = sbus_readl(&regs->vid_screenpaint_timectl1);
205 		val &= ~SCREENPAINT_TIMECTL1_ENABLE_VIDEO;
206 		sbus_writel(val, &regs->vid_screenpaint_timectl1);
207 		par->flags |= P9100_FLAG_BLANKED;
208 		break;
209 	}
210 
211 	spin_unlock_irqrestore(&par->lock, flags);
212 
213 	return 0;
214 }
215 
216 static struct sbus_mmap_map p9100_mmap_map[] = {
217 	{ CG3_MMAP_OFFSET,	0,		SBUS_MMAP_FBSIZE(1) },
218 	{ 0,			0,		0		    }
219 };
220 
p9100_mmap(struct fb_info * info,struct vm_area_struct * vma)221 static int p9100_mmap(struct fb_info *info, struct vm_area_struct *vma)
222 {
223 	struct p9100_par *par = (struct p9100_par *)info->par;
224 
225 	return sbusfb_mmap_helper(p9100_mmap_map,
226 				  info->fix.smem_start, info->fix.smem_len,
227 				  par->which_io, vma);
228 }
229 
p9100_ioctl(struct fb_info * info,unsigned int cmd,unsigned long arg)230 static int p9100_ioctl(struct fb_info *info, unsigned int cmd,
231 		       unsigned long arg)
232 {
233 	/* Make it look like a cg3. */
234 	return sbusfb_ioctl_helper(cmd, arg, info,
235 				   FBTYPE_SUN3COLOR, 8, info->fix.smem_len);
236 }
237 
238 /*
239  *  Initialisation
240  */
241 
p9100_init_fix(struct fb_info * info,int linebytes,struct device_node * dp)242 static void p9100_init_fix(struct fb_info *info, int linebytes, struct device_node *dp)
243 {
244 	snprintf(info->fix.id, sizeof(info->fix.id), "%pOFn", dp);
245 
246 	info->fix.type = FB_TYPE_PACKED_PIXELS;
247 	info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
248 
249 	info->fix.line_length = linebytes;
250 
251 	info->fix.accel = FB_ACCEL_SUN_CGTHREE;
252 }
253 
p9100_probe(struct platform_device * op)254 static int p9100_probe(struct platform_device *op)
255 {
256 	struct device_node *dp = op->dev.of_node;
257 	struct fb_info *info;
258 	struct p9100_par *par;
259 	int linebytes, err;
260 
261 	info = framebuffer_alloc(sizeof(struct p9100_par), &op->dev);
262 
263 	err = -ENOMEM;
264 	if (!info)
265 		goto out_err;
266 	par = info->par;
267 
268 	spin_lock_init(&par->lock);
269 
270 	/* This is the framebuffer and the only resource apps can mmap.  */
271 	info->fix.smem_start = op->resource[2].start;
272 	par->which_io = op->resource[2].flags & IORESOURCE_BITS;
273 
274 	sbusfb_fill_var(&info->var, dp, 8);
275 	info->var.red.length = 8;
276 	info->var.green.length = 8;
277 	info->var.blue.length = 8;
278 
279 	linebytes = of_getintprop_default(dp, "linebytes", info->var.xres);
280 	info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres);
281 
282 	par->regs = of_ioremap(&op->resource[0], 0,
283 			       sizeof(struct p9100_regs), "p9100 regs");
284 	if (!par->regs)
285 		goto out_release_fb;
286 
287 	info->fbops = &p9100_ops;
288 	info->screen_base = of_ioremap(&op->resource[2], 0,
289 				       info->fix.smem_len, "p9100 ram");
290 	if (!info->screen_base)
291 		goto out_unmap_regs;
292 
293 	p9100_blank(FB_BLANK_UNBLANK, info);
294 
295 	if (fb_alloc_cmap(&info->cmap, 256, 0))
296 		goto out_unmap_screen;
297 
298 	p9100_init_fix(info, linebytes, dp);
299 
300 	err = register_framebuffer(info);
301 	if (err < 0)
302 		goto out_dealloc_cmap;
303 
304 	fb_set_cmap(&info->cmap, info);
305 
306 	dev_set_drvdata(&op->dev, info);
307 
308 	printk(KERN_INFO "%pOF: p9100 at %lx:%lx\n",
309 	       dp,
310 	       par->which_io, info->fix.smem_start);
311 
312 	return 0;
313 
314 out_dealloc_cmap:
315 	fb_dealloc_cmap(&info->cmap);
316 
317 out_unmap_screen:
318 	of_iounmap(&op->resource[2], info->screen_base, info->fix.smem_len);
319 
320 out_unmap_regs:
321 	of_iounmap(&op->resource[0], par->regs, sizeof(struct p9100_regs));
322 
323 out_release_fb:
324 	framebuffer_release(info);
325 
326 out_err:
327 	return err;
328 }
329 
p9100_remove(struct platform_device * op)330 static void p9100_remove(struct platform_device *op)
331 {
332 	struct fb_info *info = dev_get_drvdata(&op->dev);
333 	struct p9100_par *par = info->par;
334 
335 	unregister_framebuffer(info);
336 	fb_dealloc_cmap(&info->cmap);
337 
338 	of_iounmap(&op->resource[0], par->regs, sizeof(struct p9100_regs));
339 	of_iounmap(&op->resource[2], info->screen_base, info->fix.smem_len);
340 
341 	framebuffer_release(info);
342 }
343 
344 static const struct of_device_id p9100_match[] = {
345 	{
346 		.name = "p9100",
347 	},
348 	{},
349 };
350 MODULE_DEVICE_TABLE(of, p9100_match);
351 
352 static struct platform_driver p9100_driver = {
353 	.driver = {
354 		.name = "p9100",
355 		.of_match_table = p9100_match,
356 	},
357 	.probe		= p9100_probe,
358 	.remove_new	= p9100_remove,
359 };
360 
p9100_init(void)361 static int __init p9100_init(void)
362 {
363 	if (fb_get_options("p9100fb", NULL))
364 		return -ENODEV;
365 
366 	return platform_driver_register(&p9100_driver);
367 }
368 
p9100_exit(void)369 static void __exit p9100_exit(void)
370 {
371 	platform_driver_unregister(&p9100_driver);
372 }
373 
374 module_init(p9100_init);
375 module_exit(p9100_exit);
376 
377 MODULE_DESCRIPTION("framebuffer driver for P9100 chipsets");
378 MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
379 MODULE_VERSION("2.0");
380 MODULE_LICENSE("GPL");
381