1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Driver for Aeroflex Gaisler SVGACTRL framebuffer device.
4 *
5 * 2011 (c) Aeroflex Gaisler AB
6 *
7 * Full documentation of the core can be found here:
8 * https://www.gaisler.com/products/grlib/grip.pdf
9 *
10 * Contributors: Kristoffer Glembo <kristoffer@gaisler.com>
11 */
12
13#include <linux/platform_device.h>
14#include <linux/dma-mapping.h>
15#include <linux/of_platform.h>
16#include <linux/of_device.h>
17#include <linux/module.h>
18#include <linux/kernel.h>
19#include <linux/string.h>
20#include <linux/delay.h>
21#include <linux/errno.h>
22#include <linux/init.h>
23#include <linux/slab.h>
24#include <linux/tty.h>
25#include <linux/mm.h>
26#include <linux/fb.h>
27#include <linux/io.h>
28
29struct grvga_regs {
30	u32 status; 		/* 0x00 */
31	u32 video_length; 	/* 0x04 */
32	u32 front_porch;	/* 0x08 */
33	u32 sync_length;	/* 0x0C */
34	u32 line_length;	/* 0x10 */
35	u32 fb_pos;		/* 0x14 */
36	u32 clk_vector[4];	/* 0x18 */
37	u32 clut;	        /* 0x20 */
38};
39
40struct grvga_par {
41	struct grvga_regs *regs;
42	u32 color_palette[16];  /* 16 entry pseudo palette used by fbcon in true color mode */
43	int clk_sel;
44	int fb_alloced;         /* = 1 if framebuffer is allocated in main memory */
45};
46
47
48static const struct fb_videomode grvga_modedb[] = {
49    {
50	/* 640x480 @ 60 Hz */
51	NULL, 60, 640, 480, 40000, 48, 16, 39, 11, 96, 2,
52	0, FB_VMODE_NONINTERLACED
53    }, {
54	/* 800x600 @ 60 Hz */
55	NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
56	0, FB_VMODE_NONINTERLACED
57    }, {
58	/* 800x600 @ 72 Hz */
59	NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
60	0, FB_VMODE_NONINTERLACED
61    }, {
62	/* 1024x768 @ 60 Hz */
63	NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6,
64	0, FB_VMODE_NONINTERLACED
65    }
66 };
67
68static const struct fb_fix_screeninfo grvga_fix = {
69	.id =		"AG SVGACTRL",
70	.type =		FB_TYPE_PACKED_PIXELS,
71	.visual =       FB_VISUAL_PSEUDOCOLOR,
72	.xpanstep =	0,
73	.ypanstep =	1,
74	.ywrapstep =	0,
75	.accel =	FB_ACCEL_NONE,
76};
77
78static int grvga_check_var(struct fb_var_screeninfo *var,
79			   struct fb_info *info)
80{
81	struct grvga_par *par = info->par;
82	int i;
83
84	if (!var->xres)
85		var->xres = 1;
86	if (!var->yres)
87		var->yres = 1;
88	if (var->bits_per_pixel <= 8)
89		var->bits_per_pixel = 8;
90	else if (var->bits_per_pixel <= 16)
91		var->bits_per_pixel = 16;
92	else if (var->bits_per_pixel <= 24)
93		var->bits_per_pixel = 24;
94	else if (var->bits_per_pixel <= 32)
95		var->bits_per_pixel = 32;
96	else
97		return -EINVAL;
98
99	var->xres_virtual = var->xres;
100	var->yres_virtual = 2*var->yres;
101
102	if (info->fix.smem_len) {
103		if ((var->yres_virtual*var->xres_virtual*var->bits_per_pixel/8) > info->fix.smem_len)
104			return -ENOMEM;
105	}
106
107	/* Which clocks that are available can be read out in these registers */
108	for (i = 0; i <= 3 ; i++) {
109		if (var->pixclock == par->regs->clk_vector[i])
110			break;
111	}
112	if (i <= 3)
113		par->clk_sel = i;
114	else
115		return -EINVAL;
116
117	switch (info->var.bits_per_pixel) {
118	case 8:
119		var->red   = (struct fb_bitfield) {0, 8, 0};      /* offset, length, msb-right */
120		var->green = (struct fb_bitfield) {0, 8, 0};
121		var->blue  = (struct fb_bitfield) {0, 8, 0};
122		var->transp = (struct fb_bitfield) {0, 0, 0};
123		break;
124	case 16:
125		var->red   = (struct fb_bitfield) {11, 5, 0};
126		var->green = (struct fb_bitfield) {5, 6, 0};
127		var->blue  = (struct fb_bitfield) {0, 5, 0};
128		var->transp = (struct fb_bitfield) {0, 0, 0};
129		break;
130	case 24:
131	case 32:
132		var->red   = (struct fb_bitfield) {16, 8, 0};
133		var->green = (struct fb_bitfield) {8, 8, 0};
134		var->blue  = (struct fb_bitfield) {0, 8, 0};
135		var->transp = (struct fb_bitfield) {24, 8, 0};
136		break;
137	default:
138		return -EINVAL;
139	}
140
141	return 0;
142}
143
144static int grvga_set_par(struct fb_info *info)
145{
146
147	u32 func = 0;
148	struct grvga_par *par = info->par;
149
150	__raw_writel(((info->var.yres - 1) << 16) | (info->var.xres - 1),
151		     &par->regs->video_length);
152
153	__raw_writel((info->var.lower_margin << 16) | (info->var.right_margin),
154		     &par->regs->front_porch);
155
156	__raw_writel((info->var.vsync_len << 16) | (info->var.hsync_len),
157		     &par->regs->sync_length);
158
159	__raw_writel(((info->var.yres + info->var.lower_margin + info->var.upper_margin + info->var.vsync_len - 1) << 16) |
160		     (info->var.xres + info->var.right_margin + info->var.left_margin + info->var.hsync_len - 1),
161		     &par->regs->line_length);
162
163	switch (info->var.bits_per_pixel) {
164	case 8:
165		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
166		func = 1;
167		break;
168	case 16:
169		info->fix.visual = FB_VISUAL_TRUECOLOR;
170		func = 2;
171		break;
172	case 24:
173	case 32:
174		info->fix.visual = FB_VISUAL_TRUECOLOR;
175		func = 3;
176		break;
177	default:
178		return -EINVAL;
179	}
180
181	__raw_writel((par->clk_sel << 6) | (func << 4) | 1,
182		     &par->regs->status);
183
184	info->fix.line_length = (info->var.xres_virtual*info->var.bits_per_pixel)/8;
185	return 0;
186}
187
188static int grvga_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info)
189{
190	struct grvga_par *par;
191	par = info->par;
192
193	if (regno >= 256)	/* Size of CLUT */
194		return -EINVAL;
195
196	if (info->var.grayscale) {
197		/* grayscale = 0.30*R + 0.59*G + 0.11*B */
198		red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
199	}
200
201
202
203#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
204
205	red    = CNVT_TOHW(red,   info->var.red.length);
206	green  = CNVT_TOHW(green, info->var.green.length);
207	blue   = CNVT_TOHW(blue,  info->var.blue.length);
208	transp = CNVT_TOHW(transp, info->var.transp.length);
209
210#undef CNVT_TOHW
211
212	/* In PSEUDOCOLOR we use the hardware CLUT */
213	if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR)
214		__raw_writel((regno << 24) | (red << 16) | (green << 8) | blue,
215			     &par->regs->clut);
216
217	/* Truecolor uses the pseudo palette */
218	else if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
219		u32 v;
220		if (regno >= 16)
221			return -EINVAL;
222
223
224		v =     (red    << info->var.red.offset)   |
225			(green  << info->var.green.offset) |
226			(blue   << info->var.blue.offset)  |
227			(transp << info->var.transp.offset);
228
229		((u32 *) (info->pseudo_palette))[regno] = v;
230	}
231	return 0;
232}
233
234static int grvga_pan_display(struct fb_var_screeninfo *var,
235			     struct fb_info *info)
236{
237	struct grvga_par *par = info->par;
238	struct fb_fix_screeninfo *fix = &info->fix;
239	u32 base_addr;
240
241	if (var->xoffset != 0)
242		return -EINVAL;
243
244	base_addr = fix->smem_start + (var->yoffset * fix->line_length);
245	base_addr &= ~3UL;
246
247	/* Set framebuffer base address  */
248	__raw_writel(base_addr,
249		     &par->regs->fb_pos);
250
251	return 0;
252}
253
254static const struct fb_ops grvga_ops = {
255	.owner          = THIS_MODULE,
256	.fb_check_var   = grvga_check_var,
257	.fb_set_par	= grvga_set_par,
258	.fb_setcolreg   = grvga_setcolreg,
259	.fb_pan_display = grvga_pan_display,
260	.fb_fillrect	= cfb_fillrect,
261	.fb_copyarea	= cfb_copyarea,
262	.fb_imageblit	= cfb_imageblit
263};
264
265static int grvga_parse_custom(char *options,
266			      struct fb_var_screeninfo *screendata)
267{
268	char *this_opt;
269	int count = 0;
270	if (!options || !*options)
271		return -1;
272
273	while ((this_opt = strsep(&options, " ")) != NULL) {
274		if (!*this_opt)
275			continue;
276
277		switch (count) {
278		case 0:
279			screendata->pixclock = simple_strtoul(this_opt, NULL, 0);
280			count++;
281			break;
282		case 1:
283			screendata->xres = screendata->xres_virtual = simple_strtoul(this_opt, NULL, 0);
284			count++;
285			break;
286		case 2:
287			screendata->right_margin = simple_strtoul(this_opt, NULL, 0);
288			count++;
289			break;
290		case 3:
291			screendata->hsync_len = simple_strtoul(this_opt, NULL, 0);
292			count++;
293			break;
294		case 4:
295			screendata->left_margin = simple_strtoul(this_opt, NULL, 0);
296			count++;
297			break;
298		case 5:
299			screendata->yres = screendata->yres_virtual = simple_strtoul(this_opt, NULL, 0);
300			count++;
301			break;
302		case 6:
303			screendata->lower_margin = simple_strtoul(this_opt, NULL, 0);
304			count++;
305			break;
306		case 7:
307			screendata->vsync_len = simple_strtoul(this_opt, NULL, 0);
308			count++;
309			break;
310		case 8:
311			screendata->upper_margin = simple_strtoul(this_opt, NULL, 0);
312			count++;
313			break;
314		case 9:
315			screendata->bits_per_pixel = simple_strtoul(this_opt, NULL, 0);
316			count++;
317			break;
318		default:
319			return -1;
320		}
321	}
322	screendata->activate  = FB_ACTIVATE_NOW;
323	screendata->vmode     = FB_VMODE_NONINTERLACED;
324	return 0;
325}
326
327static int grvga_probe(struct platform_device *dev)
328{
329	struct fb_info *info;
330	int retval = -ENOMEM;
331	unsigned long virtual_start;
332	unsigned long grvga_fix_addr = 0;
333	unsigned long physical_start = 0;
334	unsigned long grvga_mem_size = 0;
335	struct grvga_par *par = NULL;
336	char *options = NULL, *mode_opt = NULL;
337
338	info = framebuffer_alloc(sizeof(struct grvga_par), &dev->dev);
339	if (!info)
340		return -ENOMEM;
341
342	/* Expecting: "grvga: modestring, [addr:<framebuffer physical address>], [size:<framebuffer size>]
343	 *
344	 * If modestring is custom:<custom mode string> we parse the string which then contains all videoparameters
345	 * If address is left out, we allocate memory,
346	 * if size is left out we only allocate enough to support the given mode.
347	 */
348	if (fb_get_options("grvga", &options)) {
349		retval = -ENODEV;
350		goto free_fb;
351	}
352
353	if (!options || !*options)
354		options =  "640x480-8@60";
355
356	while (1) {
357		char *this_opt = strsep(&options, ",");
358
359		if (!this_opt)
360			break;
361
362		if (!strncmp(this_opt, "custom", 6)) {
363			if (grvga_parse_custom(this_opt, &info->var) < 0) {
364				dev_err(&dev->dev, "Failed to parse custom mode (%s).\n", this_opt);
365				retval = -EINVAL;
366				goto free_fb;
367			}
368		} else if (!strncmp(this_opt, "addr", 4))
369			grvga_fix_addr = simple_strtoul(this_opt + 5, NULL, 16);
370		else if (!strncmp(this_opt, "size", 4))
371			grvga_mem_size = simple_strtoul(this_opt + 5, NULL, 0);
372		else
373			mode_opt = this_opt;
374	}
375
376	par = info->par;
377	info->fbops = &grvga_ops;
378	info->fix = grvga_fix;
379	info->pseudo_palette = par->color_palette;
380	info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK | FBINFO_HWACCEL_YPAN;
381	info->fix.smem_len = grvga_mem_size;
382
383	if (!devm_request_mem_region(&dev->dev, dev->resource[0].start,
384		    resource_size(&dev->resource[0]), "grlib-svgactrl regs")) {
385		dev_err(&dev->dev, "registers already mapped\n");
386		retval = -EBUSY;
387		goto free_fb;
388	}
389
390	par->regs = of_ioremap(&dev->resource[0], 0,
391			       resource_size(&dev->resource[0]),
392			       "grlib-svgactrl regs");
393
394	if (!par->regs) {
395		dev_err(&dev->dev, "failed to map registers\n");
396		retval = -ENOMEM;
397		goto free_fb;
398	}
399
400	retval = fb_alloc_cmap(&info->cmap, 256, 0);
401	if (retval < 0) {
402		dev_err(&dev->dev, "failed to allocate mem with fb_alloc_cmap\n");
403		retval = -ENOMEM;
404		goto unmap_regs;
405	}
406
407	if (mode_opt) {
408		retval = fb_find_mode(&info->var, info, mode_opt,
409				      grvga_modedb, sizeof(grvga_modedb), &grvga_modedb[0], 8);
410		if (!retval || retval == 4) {
411			retval = -EINVAL;
412			goto dealloc_cmap;
413		}
414	}
415
416	if (!grvga_mem_size)
417		grvga_mem_size = info->var.xres_virtual * info->var.yres_virtual * info->var.bits_per_pixel/8;
418
419	if (grvga_fix_addr) {
420		/* Got framebuffer base address from argument list */
421
422		physical_start = grvga_fix_addr;
423
424		if (!devm_request_mem_region(&dev->dev, physical_start,
425					     grvga_mem_size, dev->name)) {
426			dev_err(&dev->dev, "failed to request memory region\n");
427			retval = -ENOMEM;
428			goto dealloc_cmap;
429		}
430
431		virtual_start = (unsigned long) ioremap(physical_start, grvga_mem_size);
432
433		if (!virtual_start) {
434			dev_err(&dev->dev, "error mapping framebuffer memory\n");
435			retval = -ENOMEM;
436			goto dealloc_cmap;
437		}
438	} else {	/* Allocate frambuffer memory */
439
440		unsigned long page;
441
442		virtual_start = (unsigned long) __get_free_pages(GFP_DMA,
443								 get_order(grvga_mem_size));
444		if (!virtual_start) {
445			dev_err(&dev->dev,
446				"unable to allocate framebuffer memory (%lu bytes)\n",
447				grvga_mem_size);
448			retval = -ENOMEM;
449			goto dealloc_cmap;
450		}
451
452		physical_start = dma_map_single(&dev->dev, (void *)virtual_start, grvga_mem_size, DMA_TO_DEVICE);
453
454		/* Set page reserved so that mmap will work. This is necessary
455		 * since we'll be remapping normal memory.
456		 */
457		for (page = virtual_start;
458		     page < PAGE_ALIGN(virtual_start + grvga_mem_size);
459		     page += PAGE_SIZE) {
460			SetPageReserved(virt_to_page(page));
461		}
462
463		par->fb_alloced = 1;
464	}
465
466	memset((unsigned long *) virtual_start, 0, grvga_mem_size);
467
468	info->screen_base = (char __iomem *) virtual_start;
469	info->fix.smem_start = physical_start;
470	info->fix.smem_len   = grvga_mem_size;
471
472	dev_set_drvdata(&dev->dev, info);
473
474	dev_info(&dev->dev,
475		 "Aeroflex Gaisler framebuffer device (fb%d), %dx%d-%d, using %luK of video memory @ %p\n",
476		 info->node, info->var.xres, info->var.yres, info->var.bits_per_pixel,
477		 grvga_mem_size >> 10, info->screen_base);
478
479	retval = register_framebuffer(info);
480	if (retval < 0) {
481		dev_err(&dev->dev, "failed to register framebuffer\n");
482		goto free_mem;
483	}
484
485	__raw_writel(physical_start, &par->regs->fb_pos);
486	__raw_writel(__raw_readl(&par->regs->status) | 1,  /* Enable framebuffer */
487		     &par->regs->status);
488
489	return 0;
490
491free_mem:
492	if (grvga_fix_addr)
493		iounmap((void *)virtual_start);
494	else
495		kfree((void *)virtual_start);
496dealloc_cmap:
497	fb_dealloc_cmap(&info->cmap);
498unmap_regs:
499	of_iounmap(&dev->resource[0], par->regs,
500		   resource_size(&dev->resource[0]));
501free_fb:
502	framebuffer_release(info);
503
504	return retval;
505}
506
507static int grvga_remove(struct platform_device *device)
508{
509	struct fb_info *info = dev_get_drvdata(&device->dev);
510	struct grvga_par *par;
511
512	if (info) {
513		par = info->par;
514		unregister_framebuffer(info);
515		fb_dealloc_cmap(&info->cmap);
516
517		of_iounmap(&device->resource[0], par->regs,
518			   resource_size(&device->resource[0]));
519
520		if (!par->fb_alloced)
521			iounmap(info->screen_base);
522		else
523			kfree((void *)info->screen_base);
524
525		framebuffer_release(info);
526	}
527
528	return 0;
529}
530
531static struct of_device_id svgactrl_of_match[] = {
532	{
533		.name = "GAISLER_SVGACTRL",
534	},
535	{
536		.name = "01_063",
537	},
538	{},
539};
540MODULE_DEVICE_TABLE(of, svgactrl_of_match);
541
542static struct platform_driver grvga_driver = {
543	.driver = {
544		.name = "grlib-svgactrl",
545		.of_match_table = svgactrl_of_match,
546	},
547	.probe		= grvga_probe,
548	.remove		= grvga_remove,
549};
550
551module_platform_driver(grvga_driver);
552
553MODULE_LICENSE("GPL");
554MODULE_AUTHOR("Aeroflex Gaisler");
555MODULE_DESCRIPTION("Aeroflex Gaisler framebuffer device driver");
556