root/drivers/video/fbdev/grvga.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. grvga_check_var
  2. grvga_set_par
  3. grvga_setcolreg
  4. grvga_pan_display
  5. grvga_parse_custom
  6. grvga_probe
  7. grvga_remove

   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  * http://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 
  29 struct 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 
  40 struct 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 
  48 static 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 
  68 static 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 
  78 static 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 
 144 static 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 
 188 static 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 
 234 static 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 
 254 static 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 
 265 static 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 
 327 static 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 
 491 free_mem:
 492         if (grvga_fix_addr)
 493                 iounmap((void *)virtual_start);
 494         else
 495                 kfree((void *)virtual_start);
 496 dealloc_cmap:
 497         fb_dealloc_cmap(&info->cmap);
 498 unmap_regs:
 499         of_iounmap(&dev->resource[0], par->regs,
 500                    resource_size(&dev->resource[0]));
 501 free_fb:
 502         framebuffer_release(info);
 503 
 504         return retval;
 505 }
 506 
 507 static 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 
 531 static struct of_device_id svgactrl_of_match[] = {
 532         {
 533                 .name = "GAISLER_SVGACTRL",
 534         },
 535         {
 536                 .name = "01_063",
 537         },
 538         {},
 539 };
 540 MODULE_DEVICE_TABLE(of, svgactrl_of_match);
 541 
 542 static 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 
 551 module_platform_driver(grvga_driver);
 552 
 553 MODULE_LICENSE("GPL");
 554 MODULE_AUTHOR("Aeroflex Gaisler");
 555 MODULE_DESCRIPTION("Aeroflex Gaisler framebuffer device driver");

/* [<][>][^][v][top][bottom][index][help] */