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