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>
16 #include <linux/module.h>
17 #include <linux/kernel.h>
18 #include <linux/string.h>
19 #include <linux/delay.h>
20 #include <linux/errno.h>
21 #include <linux/init.h>
22 #include <linux/slab.h>
23 #include <linux/tty.h>
29 u32 status
; /* 0x00 */
30 u32 video_length
; /* 0x04 */
31 u32 front_porch
; /* 0x08 */
32 u32 sync_length
; /* 0x0C */
33 u32 line_length
; /* 0x10 */
34 u32 fb_pos
; /* 0x14 */
35 u32 clk_vector
[4]; /* 0x18 */
40 struct grvga_regs
*regs
;
41 u32 color_palette
[16]; /* 16 entry pseudo palette used by fbcon in true color mode */
43 int fb_alloced
; /* = 1 if framebuffer is allocated in main memory */
47 static const struct fb_videomode grvga_modedb
[] = {
50 NULL
, 60, 640, 480, 40000, 48, 16, 39, 11, 96, 2,
51 0, FB_VMODE_NONINTERLACED
54 NULL
, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
55 0, FB_VMODE_NONINTERLACED
58 NULL
, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
59 0, FB_VMODE_NONINTERLACED
61 /* 1024x768 @ 60 Hz */
62 NULL
, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6,
63 0, FB_VMODE_NONINTERLACED
67 static const struct fb_fix_screeninfo grvga_fix
= {
69 .type
= FB_TYPE_PACKED_PIXELS
,
70 .visual
= FB_VISUAL_PSEUDOCOLOR
,
74 .accel
= FB_ACCEL_NONE
,
77 static int grvga_check_var(struct fb_var_screeninfo
*var
,
80 struct grvga_par
*par
= info
->par
;
87 if (var
->bits_per_pixel
<= 8)
88 var
->bits_per_pixel
= 8;
89 else if (var
->bits_per_pixel
<= 16)
90 var
->bits_per_pixel
= 16;
91 else if (var
->bits_per_pixel
<= 24)
92 var
->bits_per_pixel
= 24;
93 else if (var
->bits_per_pixel
<= 32)
94 var
->bits_per_pixel
= 32;
98 var
->xres_virtual
= var
->xres
;
99 var
->yres_virtual
= 2*var
->yres
;
101 if (info
->fix
.smem_len
) {
102 if ((var
->yres_virtual
*var
->xres_virtual
*var
->bits_per_pixel
/8) > info
->fix
.smem_len
)
106 /* Which clocks that are available can be read out in these registers */
107 for (i
= 0; i
<= 3 ; i
++) {
108 if (var
->pixclock
== par
->regs
->clk_vector
[i
])
116 switch (info
->var
.bits_per_pixel
) {
118 var
->red
= (struct fb_bitfield
) {0, 8, 0}; /* offset, length, msb-right */
119 var
->green
= (struct fb_bitfield
) {0, 8, 0};
120 var
->blue
= (struct fb_bitfield
) {0, 8, 0};
121 var
->transp
= (struct fb_bitfield
) {0, 0, 0};
124 var
->red
= (struct fb_bitfield
) {11, 5, 0};
125 var
->green
= (struct fb_bitfield
) {5, 6, 0};
126 var
->blue
= (struct fb_bitfield
) {0, 5, 0};
127 var
->transp
= (struct fb_bitfield
) {0, 0, 0};
131 var
->red
= (struct fb_bitfield
) {16, 8, 0};
132 var
->green
= (struct fb_bitfield
) {8, 8, 0};
133 var
->blue
= (struct fb_bitfield
) {0, 8, 0};
134 var
->transp
= (struct fb_bitfield
) {24, 8, 0};
143 static int grvga_set_par(struct fb_info
*info
)
147 struct grvga_par
*par
= info
->par
;
149 __raw_writel(((info
->var
.yres
- 1) << 16) | (info
->var
.xres
- 1),
150 &par
->regs
->video_length
);
152 __raw_writel((info
->var
.lower_margin
<< 16) | (info
->var
.right_margin
),
153 &par
->regs
->front_porch
);
155 __raw_writel((info
->var
.vsync_len
<< 16) | (info
->var
.hsync_len
),
156 &par
->regs
->sync_length
);
158 __raw_writel(((info
->var
.yres
+ info
->var
.lower_margin
+ info
->var
.upper_margin
+ info
->var
.vsync_len
- 1) << 16) |
159 (info
->var
.xres
+ info
->var
.right_margin
+ info
->var
.left_margin
+ info
->var
.hsync_len
- 1),
160 &par
->regs
->line_length
);
162 switch (info
->var
.bits_per_pixel
) {
164 info
->fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
168 info
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
173 info
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
180 __raw_writel((par
->clk_sel
<< 6) | (func
<< 4) | 1,
183 info
->fix
.line_length
= (info
->var
.xres_virtual
*info
->var
.bits_per_pixel
)/8;
187 static int grvga_setcolreg(unsigned regno
, unsigned red
, unsigned green
, unsigned blue
, unsigned transp
, struct fb_info
*info
)
189 struct grvga_par
*par
;
192 if (regno
>= 256) /* Size of CLUT */
195 if (info
->var
.grayscale
) {
196 /* grayscale = 0.30*R + 0.59*G + 0.11*B */
197 red
= green
= blue
= (red
* 77 + green
* 151 + blue
* 28) >> 8;
202 #define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
204 red
= CNVT_TOHW(red
, info
->var
.red
.length
);
205 green
= CNVT_TOHW(green
, info
->var
.green
.length
);
206 blue
= CNVT_TOHW(blue
, info
->var
.blue
.length
);
207 transp
= CNVT_TOHW(transp
, info
->var
.transp
.length
);
211 /* In PSEUDOCOLOR we use the hardware CLUT */
212 if (info
->fix
.visual
== FB_VISUAL_PSEUDOCOLOR
)
213 __raw_writel((regno
<< 24) | (red
<< 16) | (green
<< 8) | blue
,
216 /* Truecolor uses the pseudo palette */
217 else if (info
->fix
.visual
== FB_VISUAL_TRUECOLOR
) {
223 v
= (red
<< info
->var
.red
.offset
) |
224 (green
<< info
->var
.green
.offset
) |
225 (blue
<< info
->var
.blue
.offset
) |
226 (transp
<< info
->var
.transp
.offset
);
228 ((u32
*) (info
->pseudo_palette
))[regno
] = v
;
233 static int grvga_pan_display(struct fb_var_screeninfo
*var
,
234 struct fb_info
*info
)
236 struct grvga_par
*par
= info
->par
;
237 struct fb_fix_screeninfo
*fix
= &info
->fix
;
240 if (var
->xoffset
!= 0)
243 base_addr
= fix
->smem_start
+ (var
->yoffset
* fix
->line_length
);
246 /* Set framebuffer base address */
247 __raw_writel(base_addr
,
253 static const struct fb_ops grvga_ops
= {
254 .owner
= THIS_MODULE
,
255 FB_DEFAULT_IOMEM_OPS
,
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
,
262 static int grvga_parse_custom(char *options
,
263 struct fb_var_screeninfo
*screendata
)
267 if (!options
|| !*options
)
270 while ((this_opt
= strsep(&options
, " ")) != NULL
) {
276 screendata
->pixclock
= simple_strtoul(this_opt
, NULL
, 0);
280 screendata
->xres
= screendata
->xres_virtual
= simple_strtoul(this_opt
, NULL
, 0);
284 screendata
->right_margin
= simple_strtoul(this_opt
, NULL
, 0);
288 screendata
->hsync_len
= simple_strtoul(this_opt
, NULL
, 0);
292 screendata
->left_margin
= simple_strtoul(this_opt
, NULL
, 0);
296 screendata
->yres
= screendata
->yres_virtual
= simple_strtoul(this_opt
, NULL
, 0);
300 screendata
->lower_margin
= simple_strtoul(this_opt
, NULL
, 0);
304 screendata
->vsync_len
= simple_strtoul(this_opt
, NULL
, 0);
308 screendata
->upper_margin
= simple_strtoul(this_opt
, NULL
, 0);
312 screendata
->bits_per_pixel
= simple_strtoul(this_opt
, NULL
, 0);
319 screendata
->activate
= FB_ACTIVATE_NOW
;
320 screendata
->vmode
= FB_VMODE_NONINTERLACED
;
324 static int grvga_probe(struct platform_device
*dev
)
326 struct fb_info
*info
;
327 int retval
= -ENOMEM
;
328 unsigned long virtual_start
;
329 unsigned long grvga_fix_addr
= 0;
330 unsigned long physical_start
= 0;
331 unsigned long grvga_mem_size
= 0;
332 struct grvga_par
*par
= NULL
;
333 char *options
= NULL
, *mode_opt
= NULL
;
335 info
= framebuffer_alloc(sizeof(struct grvga_par
), &dev
->dev
);
339 /* Expecting: "grvga: modestring, [addr:<framebuffer physical address>], [size:<framebuffer size>]
341 * If modestring is custom:<custom mode string> we parse the string which then contains all videoparameters
342 * If address is left out, we allocate memory,
343 * if size is left out we only allocate enough to support the given mode.
345 if (fb_get_options("grvga", &options
)) {
350 if (!options
|| !*options
)
351 options
= "640x480-8@60";
354 char *this_opt
= strsep(&options
, ",");
359 if (!strncmp(this_opt
, "custom", 6)) {
360 if (grvga_parse_custom(this_opt
, &info
->var
) < 0) {
361 dev_err(&dev
->dev
, "Failed to parse custom mode (%s).\n", this_opt
);
365 } else if (!strncmp(this_opt
, "addr", 4))
366 grvga_fix_addr
= simple_strtoul(this_opt
+ 5, NULL
, 16);
367 else if (!strncmp(this_opt
, "size", 4))
368 grvga_mem_size
= simple_strtoul(this_opt
+ 5, NULL
, 0);
374 info
->fbops
= &grvga_ops
;
375 info
->fix
= grvga_fix
;
376 info
->pseudo_palette
= par
->color_palette
;
377 info
->flags
= FBINFO_PARTIAL_PAN_OK
| FBINFO_HWACCEL_YPAN
;
378 info
->fix
.smem_len
= grvga_mem_size
;
380 if (!devm_request_mem_region(&dev
->dev
, dev
->resource
[0].start
,
381 resource_size(&dev
->resource
[0]), "grlib-svgactrl regs")) {
382 dev_err(&dev
->dev
, "registers already mapped\n");
387 par
->regs
= of_ioremap(&dev
->resource
[0], 0,
388 resource_size(&dev
->resource
[0]),
389 "grlib-svgactrl regs");
392 dev_err(&dev
->dev
, "failed to map registers\n");
397 retval
= fb_alloc_cmap(&info
->cmap
, 256, 0);
399 dev_err(&dev
->dev
, "failed to allocate mem with fb_alloc_cmap\n");
405 retval
= fb_find_mode(&info
->var
, info
, mode_opt
,
406 grvga_modedb
, sizeof(grvga_modedb
), &grvga_modedb
[0], 8);
407 if (!retval
|| retval
== 4) {
414 grvga_mem_size
= info
->var
.xres_virtual
* info
->var
.yres_virtual
* info
->var
.bits_per_pixel
/8;
416 if (grvga_fix_addr
) {
417 /* Got framebuffer base address from argument list */
419 physical_start
= grvga_fix_addr
;
421 if (!devm_request_mem_region(&dev
->dev
, physical_start
,
422 grvga_mem_size
, dev
->name
)) {
423 dev_err(&dev
->dev
, "failed to request memory region\n");
428 virtual_start
= (unsigned long) ioremap(physical_start
, grvga_mem_size
);
430 if (!virtual_start
) {
431 dev_err(&dev
->dev
, "error mapping framebuffer memory\n");
435 } else { /* Allocate frambuffer memory */
439 virtual_start
= (unsigned long) __get_free_pages(GFP_DMA
,
440 get_order(grvga_mem_size
));
441 if (!virtual_start
) {
443 "unable to allocate framebuffer memory (%lu bytes)\n",
449 physical_start
= dma_map_single(&dev
->dev
, (void *)virtual_start
, grvga_mem_size
, DMA_TO_DEVICE
);
451 /* Set page reserved so that mmap will work. This is necessary
452 * since we'll be remapping normal memory.
454 for (page
= virtual_start
;
455 page
< PAGE_ALIGN(virtual_start
+ grvga_mem_size
);
457 SetPageReserved(virt_to_page(page
));
463 memset((unsigned long *) virtual_start
, 0, grvga_mem_size
);
465 info
->screen_base
= (char __iomem
*) virtual_start
;
466 info
->fix
.smem_start
= physical_start
;
467 info
->fix
.smem_len
= grvga_mem_size
;
469 dev_set_drvdata(&dev
->dev
, info
);
472 "Aeroflex Gaisler framebuffer device (fb%d), %dx%d-%d, using %luK of video memory @ %p\n",
473 info
->node
, info
->var
.xres
, info
->var
.yres
, info
->var
.bits_per_pixel
,
474 grvga_mem_size
>> 10, info
->screen_base
);
476 retval
= register_framebuffer(info
);
478 dev_err(&dev
->dev
, "failed to register framebuffer\n");
482 __raw_writel(physical_start
, &par
->regs
->fb_pos
);
483 __raw_writel(__raw_readl(&par
->regs
->status
) | 1, /* Enable framebuffer */
490 iounmap((void *)virtual_start
);
492 kfree((void *)virtual_start
);
494 fb_dealloc_cmap(&info
->cmap
);
496 of_iounmap(&dev
->resource
[0], par
->regs
,
497 resource_size(&dev
->resource
[0]));
499 framebuffer_release(info
);
504 static void grvga_remove(struct platform_device
*device
)
506 struct fb_info
*info
= dev_get_drvdata(&device
->dev
);
507 struct grvga_par
*par
;
511 unregister_framebuffer(info
);
512 fb_dealloc_cmap(&info
->cmap
);
514 of_iounmap(&device
->resource
[0], par
->regs
,
515 resource_size(&device
->resource
[0]));
517 if (!par
->fb_alloced
)
518 iounmap(info
->screen_base
);
520 kfree((void *)info
->screen_base
);
522 framebuffer_release(info
);
526 static struct of_device_id svgactrl_of_match
[] = {
528 .name
= "GAISLER_SVGACTRL",
535 MODULE_DEVICE_TABLE(of
, svgactrl_of_match
);
537 static struct platform_driver grvga_driver
= {
539 .name
= "grlib-svgactrl",
540 .of_match_table
= svgactrl_of_match
,
542 .probe
= grvga_probe
,
543 .remove
= grvga_remove
,
546 module_platform_driver(grvga_driver
);
548 MODULE_LICENSE("GPL");
549 MODULE_AUTHOR("Aeroflex Gaisler");
550 MODULE_DESCRIPTION("Aeroflex Gaisler framebuffer device driver");