551 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Driver for Aeroflex Gaisler SVGACTRL framebuffer device.4 *5 * 2011 (c) Aeroflex Gaisler AB6 *7 * Full documentation of the core can be found here:8 * https://www.gaisler.com/products/grlib/grip.pdf9 *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.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>24#include <linux/mm.h>25#include <linux/fb.h>26#include <linux/io.h>27 28struct grvga_regs {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 */36 u32 clut; /* 0x20 */37};38 39struct grvga_par {40 struct grvga_regs *regs;41 u32 color_palette[16]; /* 16 entry pseudo palette used by fbcon in true color mode */42 int clk_sel;43 int fb_alloced; /* = 1 if framebuffer is allocated in main memory */44};45 46 47static const struct fb_videomode grvga_modedb[] = {48 {49 /* 640x480 @ 60 Hz */50 NULL, 60, 640, 480, 40000, 48, 16, 39, 11, 96, 2,51 0, FB_VMODE_NONINTERLACED52 }, {53 /* 800x600 @ 60 Hz */54 NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,55 0, FB_VMODE_NONINTERLACED56 }, {57 /* 800x600 @ 72 Hz */58 NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,59 0, FB_VMODE_NONINTERLACED60 }, {61 /* 1024x768 @ 60 Hz */62 NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6,63 0, FB_VMODE_NONINTERLACED64 }65 };66 67static const struct fb_fix_screeninfo grvga_fix = {68 .id = "AG SVGACTRL",69 .type = FB_TYPE_PACKED_PIXELS,70 .visual = FB_VISUAL_PSEUDOCOLOR,71 .xpanstep = 0,72 .ypanstep = 1,73 .ywrapstep = 0,74 .accel = FB_ACCEL_NONE,75};76 77static int grvga_check_var(struct fb_var_screeninfo *var,78 struct fb_info *info)79{80 struct grvga_par *par = info->par;81 int i;82 83 if (!var->xres)84 var->xres = 1;85 if (!var->yres)86 var->yres = 1;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;95 else96 return -EINVAL;97 98 var->xres_virtual = var->xres;99 var->yres_virtual = 2*var->yres;100 101 if (info->fix.smem_len) {102 if ((var->yres_virtual*var->xres_virtual*var->bits_per_pixel/8) > info->fix.smem_len)103 return -ENOMEM;104 }105 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])109 break;110 }111 if (i <= 3)112 par->clk_sel = i;113 else114 return -EINVAL;115 116 switch (info->var.bits_per_pixel) {117 case 8: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};122 break;123 case 16: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};128 break;129 case 24:130 case 32: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};135 break;136 default:137 return -EINVAL;138 }139 140 return 0;141}142 143static int grvga_set_par(struct fb_info *info)144{145 146 u32 func = 0;147 struct grvga_par *par = info->par;148 149 __raw_writel(((info->var.yres - 1) << 16) | (info->var.xres - 1),150 &par->regs->video_length);151 152 __raw_writel((info->var.lower_margin << 16) | (info->var.right_margin),153 &par->regs->front_porch);154 155 __raw_writel((info->var.vsync_len << 16) | (info->var.hsync_len),156 &par->regs->sync_length);157 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);161 162 switch (info->var.bits_per_pixel) {163 case 8:164 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;165 func = 1;166 break;167 case 16:168 info->fix.visual = FB_VISUAL_TRUECOLOR;169 func = 2;170 break;171 case 24:172 case 32:173 info->fix.visual = FB_VISUAL_TRUECOLOR;174 func = 3;175 break;176 default:177 return -EINVAL;178 }179 180 __raw_writel((par->clk_sel << 6) | (func << 4) | 1,181 &par->regs->status);182 183 info->fix.line_length = (info->var.xres_virtual*info->var.bits_per_pixel)/8;184 return 0;185}186 187static int grvga_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info)188{189 struct grvga_par *par;190 par = info->par;191 192 if (regno >= 256) /* Size of CLUT */193 return -EINVAL;194 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;198 }199 200 201 202#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)203 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);208 209#undef CNVT_TOHW210 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,214 &par->regs->clut);215 216 /* Truecolor uses the pseudo palette */217 else if (info->fix.visual == FB_VISUAL_TRUECOLOR) {218 u32 v;219 if (regno >= 16)220 return -EINVAL;221 222 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);227 228 ((u32 *) (info->pseudo_palette))[regno] = v;229 }230 return 0;231}232 233static int grvga_pan_display(struct fb_var_screeninfo *var,234 struct fb_info *info)235{236 struct grvga_par *par = info->par;237 struct fb_fix_screeninfo *fix = &info->fix;238 u32 base_addr;239 240 if (var->xoffset != 0)241 return -EINVAL;242 243 base_addr = fix->smem_start + (var->yoffset * fix->line_length);244 base_addr &= ~3UL;245 246 /* Set framebuffer base address */247 __raw_writel(base_addr,248 &par->regs->fb_pos);249 250 return 0;251}252 253static 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,260};261 262static int grvga_parse_custom(char *options,263 struct fb_var_screeninfo *screendata)264{265 char *this_opt;266 int count = 0;267 if (!options || !*options)268 return -1;269 270 while ((this_opt = strsep(&options, " ")) != NULL) {271 if (!*this_opt)272 continue;273 274 switch (count) {275 case 0:276 screendata->pixclock = simple_strtoul(this_opt, NULL, 0);277 count++;278 break;279 case 1:280 screendata->xres = screendata->xres_virtual = simple_strtoul(this_opt, NULL, 0);281 count++;282 break;283 case 2:284 screendata->right_margin = simple_strtoul(this_opt, NULL, 0);285 count++;286 break;287 case 3:288 screendata->hsync_len = simple_strtoul(this_opt, NULL, 0);289 count++;290 break;291 case 4:292 screendata->left_margin = simple_strtoul(this_opt, NULL, 0);293 count++;294 break;295 case 5:296 screendata->yres = screendata->yres_virtual = simple_strtoul(this_opt, NULL, 0);297 count++;298 break;299 case 6:300 screendata->lower_margin = simple_strtoul(this_opt, NULL, 0);301 count++;302 break;303 case 7:304 screendata->vsync_len = simple_strtoul(this_opt, NULL, 0);305 count++;306 break;307 case 8:308 screendata->upper_margin = simple_strtoul(this_opt, NULL, 0);309 count++;310 break;311 case 9:312 screendata->bits_per_pixel = simple_strtoul(this_opt, NULL, 0);313 count++;314 break;315 default:316 return -1;317 }318 }319 screendata->activate = FB_ACTIVATE_NOW;320 screendata->vmode = FB_VMODE_NONINTERLACED;321 return 0;322}323 324static int grvga_probe(struct platform_device *dev)325{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;334 335 info = framebuffer_alloc(sizeof(struct grvga_par), &dev->dev);336 if (!info)337 return -ENOMEM;338 339 /* Expecting: "grvga: modestring, [addr:<framebuffer physical address>], [size:<framebuffer size>]340 *341 * If modestring is custom:<custom mode string> we parse the string which then contains all videoparameters342 * If address is left out, we allocate memory,343 * if size is left out we only allocate enough to support the given mode.344 */345 if (fb_get_options("grvga", &options)) {346 retval = -ENODEV;347 goto free_fb;348 }349 350 if (!options || !*options)351 options = "640x480-8@60";352 353 while (1) {354 char *this_opt = strsep(&options, ",");355 356 if (!this_opt)357 break;358 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);362 retval = -EINVAL;363 goto free_fb;364 }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);369 else370 mode_opt = this_opt;371 }372 373 par = info->par;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;379 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");383 retval = -EBUSY;384 goto free_fb;385 }386 387 par->regs = of_ioremap(&dev->resource[0], 0,388 resource_size(&dev->resource[0]),389 "grlib-svgactrl regs");390 391 if (!par->regs) {392 dev_err(&dev->dev, "failed to map registers\n");393 retval = -ENOMEM;394 goto free_fb;395 }396 397 retval = fb_alloc_cmap(&info->cmap, 256, 0);398 if (retval < 0) {399 dev_err(&dev->dev, "failed to allocate mem with fb_alloc_cmap\n");400 retval = -ENOMEM;401 goto unmap_regs;402 }403 404 if (mode_opt) {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) {408 retval = -EINVAL;409 goto dealloc_cmap;410 }411 }412 413 if (!grvga_mem_size)414 grvga_mem_size = info->var.xres_virtual * info->var.yres_virtual * info->var.bits_per_pixel/8;415 416 if (grvga_fix_addr) {417 /* Got framebuffer base address from argument list */418 419 physical_start = grvga_fix_addr;420 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");424 retval = -ENOMEM;425 goto dealloc_cmap;426 }427 428 virtual_start = (unsigned long) ioremap(physical_start, grvga_mem_size);429 430 if (!virtual_start) {431 dev_err(&dev->dev, "error mapping framebuffer memory\n");432 retval = -ENOMEM;433 goto dealloc_cmap;434 }435 } else { /* Allocate frambuffer memory */436 437 unsigned long page;438 439 virtual_start = (unsigned long) __get_free_pages(GFP_DMA,440 get_order(grvga_mem_size));441 if (!virtual_start) {442 dev_err(&dev->dev,443 "unable to allocate framebuffer memory (%lu bytes)\n",444 grvga_mem_size);445 retval = -ENOMEM;446 goto dealloc_cmap;447 }448 449 physical_start = dma_map_single(&dev->dev, (void *)virtual_start, grvga_mem_size, DMA_TO_DEVICE);450 451 /* Set page reserved so that mmap will work. This is necessary452 * since we'll be remapping normal memory.453 */454 for (page = virtual_start;455 page < PAGE_ALIGN(virtual_start + grvga_mem_size);456 page += PAGE_SIZE) {457 SetPageReserved(virt_to_page(page));458 }459 460 par->fb_alloced = 1;461 }462 463 memset((unsigned long *) virtual_start, 0, grvga_mem_size);464 465 info->screen_base = (char __iomem *) virtual_start;466 info->fix.smem_start = physical_start;467 info->fix.smem_len = grvga_mem_size;468 469 dev_set_drvdata(&dev->dev, info);470 471 dev_info(&dev->dev,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);475 476 retval = register_framebuffer(info);477 if (retval < 0) {478 dev_err(&dev->dev, "failed to register framebuffer\n");479 goto free_mem;480 }481 482 __raw_writel(physical_start, &par->regs->fb_pos);483 __raw_writel(__raw_readl(&par->regs->status) | 1, /* Enable framebuffer */484 &par->regs->status);485 486 return 0;487 488free_mem:489 if (grvga_fix_addr)490 iounmap((void *)virtual_start);491 else492 kfree((void *)virtual_start);493dealloc_cmap:494 fb_dealloc_cmap(&info->cmap);495unmap_regs:496 of_iounmap(&dev->resource[0], par->regs,497 resource_size(&dev->resource[0]));498free_fb:499 framebuffer_release(info);500 501 return retval;502}503 504static void grvga_remove(struct platform_device *device)505{506 struct fb_info *info = dev_get_drvdata(&device->dev);507 struct grvga_par *par;508 509 if (info) {510 par = info->par;511 unregister_framebuffer(info);512 fb_dealloc_cmap(&info->cmap);513 514 of_iounmap(&device->resource[0], par->regs,515 resource_size(&device->resource[0]));516 517 if (!par->fb_alloced)518 iounmap(info->screen_base);519 else520 kfree((void *)info->screen_base);521 522 framebuffer_release(info);523 }524}525 526static struct of_device_id svgactrl_of_match[] = {527 {528 .name = "GAISLER_SVGACTRL",529 },530 {531 .name = "01_063",532 },533 {},534};535MODULE_DEVICE_TABLE(of, svgactrl_of_match);536 537static struct platform_driver grvga_driver = {538 .driver = {539 .name = "grlib-svgactrl",540 .of_match_table = svgactrl_of_match,541 },542 .probe = grvga_probe,543 .remove = grvga_remove,544};545 546module_platform_driver(grvga_driver);547 548MODULE_LICENSE("GPL");549MODULE_AUTHOR("Aeroflex Gaisler");550MODULE_DESCRIPTION("Aeroflex Gaisler framebuffer device driver");551