1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Driver for Aeroflex Gaisler SVGACTRL framebuffer device.
5 * 2011 (c) Aeroflex Gaisler AB
7 * Full documentation of the core can be found here:
8 * https://www.gaisler.com/products/grlib/grip.pdf
10 * Contributors: Kristoffer Glembo <kristoffer@gaisler.com>
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>
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 */
41 struct grvga_regs
*regs
;
42 u32 color_palette
[16]; /* 16 entry pseudo palette used by fbcon in true color mode */
44 int fb_alloced
; /* = 1 if framebuffer is allocated in main memory */
48 static const struct fb_videomode grvga_modedb
[] = {
51 NULL
, 60, 640, 480, 40000, 48, 16, 39, 11, 96, 2,
52 0, FB_VMODE_NONINTERLACED
55 NULL
, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
56 0, FB_VMODE_NONINTERLACED
59 NULL
, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
60 0, FB_VMODE_NONINTERLACED
62 /* 1024x768 @ 60 Hz */
63 NULL
, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6,
64 0, FB_VMODE_NONINTERLACED
68 static const struct fb_fix_screeninfo grvga_fix
= {
70 .type
= FB_TYPE_PACKED_PIXELS
,
71 .visual
= FB_VISUAL_PSEUDOCOLOR
,
75 .accel
= FB_ACCEL_NONE
,
78 static int grvga_check_var(struct fb_var_screeninfo
*var
,
81 struct grvga_par
*par
= info
->par
;
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;
99 var
->xres_virtual
= var
->xres
;
100 var
->yres_virtual
= 2*var
->yres
;
102 if (info
->fix
.smem_len
) {
103 if ((var
->yres_virtual
*var
->xres_virtual
*var
->bits_per_pixel
/8) > info
->fix
.smem_len
)
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
])
117 switch (info
->var
.bits_per_pixel
) {
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};
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};
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};
144 static int grvga_set_par(struct fb_info
*info
)
148 struct grvga_par
*par
= info
->par
;
150 __raw_writel(((info
->var
.yres
- 1) << 16) | (info
->var
.xres
- 1),
151 &par
->regs
->video_length
);
153 __raw_writel((info
->var
.lower_margin
<< 16) | (info
->var
.right_margin
),
154 &par
->regs
->front_porch
);
156 __raw_writel((info
->var
.vsync_len
<< 16) | (info
->var
.hsync_len
),
157 &par
->regs
->sync_length
);
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
);
163 switch (info
->var
.bits_per_pixel
) {
165 info
->fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
169 info
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
174 info
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
181 __raw_writel((par
->clk_sel
<< 6) | (func
<< 4) | 1,
184 info
->fix
.line_length
= (info
->var
.xres_virtual
*info
->var
.bits_per_pixel
)/8;
188 static int grvga_setcolreg(unsigned regno
, unsigned red
, unsigned green
, unsigned blue
, unsigned transp
, struct fb_info
*info
)
190 struct grvga_par
*par
;
193 if (regno
>= 256) /* Size of CLUT */
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;
203 #define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
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
);
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
,
217 /* Truecolor uses the pseudo palette */
218 else if (info
->fix
.visual
== FB_VISUAL_TRUECOLOR
) {
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
);
229 ((u32
*) (info
->pseudo_palette
))[regno
] = v
;
234 static int grvga_pan_display(struct fb_var_screeninfo
*var
,
235 struct fb_info
*info
)
237 struct grvga_par
*par
= info
->par
;
238 struct fb_fix_screeninfo
*fix
= &info
->fix
;
241 if (var
->xoffset
!= 0)
244 base_addr
= fix
->smem_start
+ (var
->yoffset
* fix
->line_length
);
247 /* Set framebuffer base address */
248 __raw_writel(base_addr
,
254 static 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
265 static int grvga_parse_custom(char *options
,
266 struct fb_var_screeninfo
*screendata
)
270 if (!options
|| !*options
)
273 while ((this_opt
= strsep(&options
, " ")) != NULL
) {
279 screendata
->pixclock
= simple_strtoul(this_opt
, NULL
, 0);
283 screendata
->xres
= screendata
->xres_virtual
= simple_strtoul(this_opt
, NULL
, 0);
287 screendata
->right_margin
= simple_strtoul(this_opt
, NULL
, 0);
291 screendata
->hsync_len
= simple_strtoul(this_opt
, NULL
, 0);
295 screendata
->left_margin
= simple_strtoul(this_opt
, NULL
, 0);
299 screendata
->yres
= screendata
->yres_virtual
= simple_strtoul(this_opt
, NULL
, 0);
303 screendata
->lower_margin
= simple_strtoul(this_opt
, NULL
, 0);
307 screendata
->vsync_len
= simple_strtoul(this_opt
, NULL
, 0);
311 screendata
->upper_margin
= simple_strtoul(this_opt
, NULL
, 0);
315 screendata
->bits_per_pixel
= simple_strtoul(this_opt
, NULL
, 0);
322 screendata
->activate
= FB_ACTIVATE_NOW
;
323 screendata
->vmode
= FB_VMODE_NONINTERLACED
;
327 static int grvga_probe(struct platform_device
*dev
)
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
;
338 info
= framebuffer_alloc(sizeof(struct grvga_par
), &dev
->dev
);
342 /* Expecting: "grvga: modestring, [addr:<framebuffer physical address>], [size:<framebuffer size>]
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.
348 if (fb_get_options("grvga", &options
)) {
353 if (!options
|| !*options
)
354 options
= "640x480-8@60";
357 char *this_opt
= strsep(&options
, ",");
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
);
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);
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
;
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");
390 par
->regs
= of_ioremap(&dev
->resource
[0], 0,
391 resource_size(&dev
->resource
[0]),
392 "grlib-svgactrl regs");
395 dev_err(&dev
->dev
, "failed to map registers\n");
400 retval
= fb_alloc_cmap(&info
->cmap
, 256, 0);
402 dev_err(&dev
->dev
, "failed to allocate mem with fb_alloc_cmap\n");
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) {
417 grvga_mem_size
= info
->var
.xres_virtual
* info
->var
.yres_virtual
* info
->var
.bits_per_pixel
/8;
419 if (grvga_fix_addr
) {
420 /* Got framebuffer base address from argument list */
422 physical_start
= grvga_fix_addr
;
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");
431 virtual_start
= (unsigned long) ioremap(physical_start
, grvga_mem_size
);
433 if (!virtual_start
) {
434 dev_err(&dev
->dev
, "error mapping framebuffer memory\n");
438 } else { /* Allocate frambuffer memory */
442 virtual_start
= (unsigned long) __get_free_pages(GFP_DMA
,
443 get_order(grvga_mem_size
));
444 if (!virtual_start
) {
446 "unable to allocate framebuffer memory (%lu bytes)\n",
452 physical_start
= dma_map_single(&dev
->dev
, (void *)virtual_start
, grvga_mem_size
, DMA_TO_DEVICE
);
454 /* Set page reserved so that mmap will work. This is necessary
455 * since we'll be remapping normal memory.
457 for (page
= virtual_start
;
458 page
< PAGE_ALIGN(virtual_start
+ grvga_mem_size
);
460 SetPageReserved(virt_to_page(page
));
466 memset((unsigned long *) virtual_start
, 0, grvga_mem_size
);
468 info
->screen_base
= (char __iomem
*) virtual_start
;
469 info
->fix
.smem_start
= physical_start
;
470 info
->fix
.smem_len
= grvga_mem_size
;
472 dev_set_drvdata(&dev
->dev
, info
);
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
);
479 retval
= register_framebuffer(info
);
481 dev_err(&dev
->dev
, "failed to register framebuffer\n");
485 __raw_writel(physical_start
, &par
->regs
->fb_pos
);
486 __raw_writel(__raw_readl(&par
->regs
->status
) | 1, /* Enable framebuffer */
493 iounmap((void *)virtual_start
);
495 kfree((void *)virtual_start
);
497 fb_dealloc_cmap(&info
->cmap
);
499 of_iounmap(&dev
->resource
[0], par
->regs
,
500 resource_size(&dev
->resource
[0]));
502 framebuffer_release(info
);
507 static int grvga_remove(struct platform_device
*device
)
509 struct fb_info
*info
= dev_get_drvdata(&device
->dev
);
510 struct grvga_par
*par
;
514 unregister_framebuffer(info
);
515 fb_dealloc_cmap(&info
->cmap
);
517 of_iounmap(&device
->resource
[0], par
->regs
,
518 resource_size(&device
->resource
[0]));
520 if (!par
->fb_alloced
)
521 iounmap(info
->screen_base
);
523 kfree((void *)info
->screen_base
);
525 framebuffer_release(info
);
531 static struct of_device_id svgactrl_of_match
[] = {
533 .name
= "GAISLER_SVGACTRL",
540 MODULE_DEVICE_TABLE(of
, svgactrl_of_match
);
542 static struct platform_driver grvga_driver
= {
544 .name
= "grlib-svgactrl",
545 .of_match_table
= svgactrl_of_match
,
547 .probe
= grvga_probe
,
548 .remove
= grvga_remove
,
551 module_platform_driver(grvga_driver
);
553 MODULE_LICENSE("GPL");
554 MODULE_AUTHOR("Aeroflex Gaisler");
555 MODULE_DESCRIPTION("Aeroflex Gaisler framebuffer device driver");