aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Kristiansson <stefan.kristiansson@saunalahti.fi>2014-01-13 14:38:10 -0500
committerTomi Valkeinen <tomi.valkeinen@ti.com>2014-01-17 03:57:49 -0500
commite61d05dd884728d82b864d3b5493e7861edb348f (patch)
tree8c621dbd8c13b21ace92a3394cfe2884154f1460
parentdb25295788550f6ee494d7feacfcde5d354ad62e (diff)
video: add OpenCores VGA/LCD framebuffer driver
This adds support for the VGA/LCD core available from OpenCores: http://opencores.org/project,vga_lcd The driver have been tested together with both OpenRISC and ARM (socfpga) processors. Signed-off-by: Stefan Kristiansson <stefan.kristiansson@saunalahti.fi> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
-rw-r--r--drivers/video/Kconfig16
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/ocfb.c440
3 files changed, 457 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index e6c7fb1a389b..22262a3a0e2d 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -980,6 +980,22 @@ config FB_PVR2
980 (<file:drivers/video/pvr2fb.c>). Please see the file 980 (<file:drivers/video/pvr2fb.c>). Please see the file
981 <file:Documentation/fb/pvr2fb.txt>. 981 <file:Documentation/fb/pvr2fb.txt>.
982 982
983config FB_OPENCORES
984 tristate "OpenCores VGA/LCD core 2.0 framebuffer support"
985 depends on FB
986 select FB_CFB_FILLRECT
987 select FB_CFB_COPYAREA
988 select FB_CFB_IMAGEBLIT
989 help
990 This enables support for the OpenCores VGA/LCD core.
991
992 The OpenCores VGA/LCD core is typically used together with
993 softcore CPUs (e.g. OpenRISC or Microblaze) or hard processor
994 systems (e.g. Altera socfpga or Xilinx Zynq) on FPGAs.
995
996 The source code and specification for the core is available at
997 <http://opencores.org/project,vga_lcd>
998
983config FB_S1D13XXX 999config FB_S1D13XXX
984 tristate "Epson S1D13XXX framebuffer support" 1000 tristate "Epson S1D13XXX framebuffer support"
985 depends on FB 1001 depends on FB
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index e8bae8dd4804..ae17ddf49a00 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -150,6 +150,7 @@ obj-$(CONFIG_FB_NUC900) += nuc900fb.o
150obj-$(CONFIG_FB_JZ4740) += jz4740_fb.o 150obj-$(CONFIG_FB_JZ4740) += jz4740_fb.o
151obj-$(CONFIG_FB_PUV3_UNIGFX) += fb-puv3.o 151obj-$(CONFIG_FB_PUV3_UNIGFX) += fb-puv3.o
152obj-$(CONFIG_FB_HYPERV) += hyperv_fb.o 152obj-$(CONFIG_FB_HYPERV) += hyperv_fb.o
153obj-$(CONFIG_FB_OPENCORES) += ocfb.o
153 154
154# Platform or fallback drivers go here 155# Platform or fallback drivers go here
155obj-$(CONFIG_FB_UVESA) += uvesafb.o 156obj-$(CONFIG_FB_UVESA) += uvesafb.o
diff --git a/drivers/video/ocfb.c b/drivers/video/ocfb.c
new file mode 100644
index 000000000000..7f9dc9bec309
--- /dev/null
+++ b/drivers/video/ocfb.c
@@ -0,0 +1,440 @@
1/*
2 * OpenCores VGA/LCD 2.0 core frame buffer driver
3 *
4 * Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson@saunalahti.fi
5 *
6 * This file is licensed under the terms of the GNU General Public License
7 * version 2. This program is licensed "as is" without any warranty of any
8 * kind, whether express or implied.
9 */
10
11#include <linux/delay.h>
12#include <linux/dma-mapping.h>
13#include <linux/errno.h>
14#include <linux/fb.h>
15#include <linux/init.h>
16#include <linux/io.h>
17#include <linux/kernel.h>
18#include <linux/mm.h>
19#include <linux/module.h>
20#include <linux/of.h>
21#include <linux/platform_device.h>
22#include <linux/string.h>
23#include <linux/slab.h>
24
25/* OCFB register defines */
26#define OCFB_CTRL 0x000
27#define OCFB_STAT 0x004
28#define OCFB_HTIM 0x008
29#define OCFB_VTIM 0x00c
30#define OCFB_HVLEN 0x010
31#define OCFB_VBARA 0x014
32#define OCFB_PALETTE 0x800
33
34#define OCFB_CTRL_VEN 0x00000001 /* Video Enable */
35#define OCFB_CTRL_HIE 0x00000002 /* HSync Interrupt Enable */
36#define OCFB_CTRL_PC 0x00000800 /* 8-bit Pseudo Color Enable*/
37#define OCFB_CTRL_CD8 0x00000000 /* Color Depth 8 */
38#define OCFB_CTRL_CD16 0x00000200 /* Color Depth 16 */
39#define OCFB_CTRL_CD24 0x00000400 /* Color Depth 24 */
40#define OCFB_CTRL_CD32 0x00000600 /* Color Depth 32 */
41#define OCFB_CTRL_VBL1 0x00000000 /* Burst Length 1 */
42#define OCFB_CTRL_VBL2 0x00000080 /* Burst Length 2 */
43#define OCFB_CTRL_VBL4 0x00000100 /* Burst Length 4 */
44#define OCFB_CTRL_VBL8 0x00000180 /* Burst Length 8 */
45
46#define PALETTE_SIZE 256
47
48#define OCFB_NAME "OC VGA/LCD"
49
50static char *mode_option;
51
52static const struct fb_videomode default_mode = {
53 /* 640x480 @ 60 Hz, 31.5 kHz hsync */
54 NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2,
55 0, FB_VMODE_NONINTERLACED
56};
57
58struct ocfb_dev {
59 struct fb_info info;
60 void __iomem *regs;
61 /* flag indicating whether the regs are little endian accessed */
62 int little_endian;
63 /* Physical and virtual addresses of framebuffer */
64 phys_addr_t fb_phys;
65 void __iomem *fb_virt;
66 u32 pseudo_palette[PALETTE_SIZE];
67};
68
69#ifndef MODULE
70static int __init ocfb_setup(char *options)
71{
72 char *curr_opt;
73
74 if (!options || !*options)
75 return 0;
76
77 while ((curr_opt = strsep(&options, ",")) != NULL) {
78 if (!*curr_opt)
79 continue;
80 mode_option = curr_opt;
81 }
82
83 return 0;
84}
85#endif
86
87static inline u32 ocfb_readreg(struct ocfb_dev *fbdev, loff_t offset)
88{
89 if (fbdev->little_endian)
90 return ioread32(fbdev->regs + offset);
91 else
92 return ioread32be(fbdev->regs + offset);
93}
94
95static void ocfb_writereg(struct ocfb_dev *fbdev, loff_t offset, u32 data)
96{
97 if (fbdev->little_endian)
98 iowrite32(data, fbdev->regs + offset);
99 else
100 iowrite32be(data, fbdev->regs + offset);
101}
102
103static int ocfb_setupfb(struct ocfb_dev *fbdev)
104{
105 unsigned long bpp_config;
106 struct fb_var_screeninfo *var = &fbdev->info.var;
107 struct device *dev = fbdev->info.device;
108 u32 hlen;
109 u32 vlen;
110
111 /* Disable display */
112 ocfb_writereg(fbdev, OCFB_CTRL, 0);
113
114 /* Register framebuffer address */
115 fbdev->little_endian = 0;
116 ocfb_writereg(fbdev, OCFB_VBARA, fbdev->fb_phys);
117
118 /* Detect endianess */
119 if (ocfb_readreg(fbdev, OCFB_VBARA) != fbdev->fb_phys) {
120 fbdev->little_endian = 1;
121 ocfb_writereg(fbdev, OCFB_VBARA, fbdev->fb_phys);
122 }
123
124 /* Horizontal timings */
125 ocfb_writereg(fbdev, OCFB_HTIM, (var->hsync_len - 1) << 24 |
126 (var->right_margin - 1) << 16 | (var->xres - 1));
127
128 /* Vertical timings */
129 ocfb_writereg(fbdev, OCFB_VTIM, (var->vsync_len - 1) << 24 |
130 (var->lower_margin - 1) << 16 | (var->yres - 1));
131
132 /* Total length of frame */
133 hlen = var->left_margin + var->right_margin + var->hsync_len +
134 var->xres;
135
136 vlen = var->upper_margin + var->lower_margin + var->vsync_len +
137 var->yres;
138
139 ocfb_writereg(fbdev, OCFB_HVLEN, (hlen - 1) << 16 | (vlen - 1));
140
141 bpp_config = OCFB_CTRL_CD8;
142 switch (var->bits_per_pixel) {
143 case 8:
144 if (!var->grayscale)
145 bpp_config |= OCFB_CTRL_PC; /* enable palette */
146 break;
147
148 case 16:
149 bpp_config |= OCFB_CTRL_CD16;
150 break;
151
152 case 24:
153 bpp_config |= OCFB_CTRL_CD24;
154 break;
155
156 case 32:
157 bpp_config |= OCFB_CTRL_CD32;
158 break;
159
160 default:
161 dev_err(dev, "no bpp specified\n");
162 break;
163 }
164
165 /* maximum (8) VBL (video memory burst length) */
166 bpp_config |= OCFB_CTRL_VBL8;
167
168 /* Enable output */
169 ocfb_writereg(fbdev, OCFB_CTRL, (OCFB_CTRL_VEN | bpp_config));
170
171 return 0;
172}
173
174static int ocfb_setcolreg(unsigned regno, unsigned red, unsigned green,
175 unsigned blue, unsigned transp,
176 struct fb_info *info)
177{
178 struct ocfb_dev *fbdev = (struct ocfb_dev *)info->par;
179 u32 color;
180
181 if (regno >= info->cmap.len) {
182 dev_err(info->device, "regno >= cmap.len\n");
183 return 1;
184 }
185
186 if (info->var.grayscale) {
187 /* grayscale = 0.30*R + 0.59*G + 0.11*B */
188 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
189 }
190
191 red >>= (16 - info->var.red.length);
192 green >>= (16 - info->var.green.length);
193 blue >>= (16 - info->var.blue.length);
194 transp >>= (16 - info->var.transp.length);
195
196 if (info->var.bits_per_pixel == 8 && !info->var.grayscale) {
197 regno <<= 2;
198 color = (red << 16) | (green << 8) | blue;
199 ocfb_writereg(fbdev, OCFB_PALETTE + regno, color);
200 } else {
201 ((u32 *)(info->pseudo_palette))[regno] =
202 (red << info->var.red.offset) |
203 (green << info->var.green.offset) |
204 (blue << info->var.blue.offset) |
205 (transp << info->var.transp.offset);
206 }
207
208 return 0;
209}
210
211static int ocfb_init_fix(struct ocfb_dev *fbdev)
212{
213 struct fb_var_screeninfo *var = &fbdev->info.var;
214 struct fb_fix_screeninfo *fix = &fbdev->info.fix;
215
216 strcpy(fix->id, OCFB_NAME);
217
218 fix->line_length = var->xres * var->bits_per_pixel/8;
219 fix->smem_len = fix->line_length * var->yres;
220 fix->type = FB_TYPE_PACKED_PIXELS;
221
222 if (var->bits_per_pixel == 8 && !var->grayscale)
223 fix->visual = FB_VISUAL_PSEUDOCOLOR;
224 else
225 fix->visual = FB_VISUAL_TRUECOLOR;
226
227 return 0;
228}
229
230static int ocfb_init_var(struct ocfb_dev *fbdev)
231{
232 struct fb_var_screeninfo *var = &fbdev->info.var;
233
234 var->accel_flags = FB_ACCEL_NONE;
235 var->activate = FB_ACTIVATE_NOW;
236 var->xres_virtual = var->xres;
237 var->yres_virtual = var->yres;
238
239 switch (var->bits_per_pixel) {
240 case 8:
241 var->transp.offset = 0;
242 var->transp.length = 0;
243 var->red.offset = 0;
244 var->red.length = 8;
245 var->green.offset = 0;
246 var->green.length = 8;
247 var->blue.offset = 0;
248 var->blue.length = 8;
249 break;
250
251 case 16:
252 var->transp.offset = 0;
253 var->transp.length = 0;
254 var->red.offset = 11;
255 var->red.length = 5;
256 var->green.offset = 5;
257 var->green.length = 6;
258 var->blue.offset = 0;
259 var->blue.length = 5;
260 break;
261
262 case 24:
263 var->transp.offset = 0;
264 var->transp.length = 0;
265 var->red.offset = 16;
266 var->red.length = 8;
267 var->green.offset = 8;
268 var->green.length = 8;
269 var->blue.offset = 0;
270 var->blue.length = 8;
271 break;
272
273 case 32:
274 var->transp.offset = 24;
275 var->transp.length = 8;
276 var->red.offset = 16;
277 var->red.length = 8;
278 var->green.offset = 8;
279 var->green.length = 8;
280 var->blue.offset = 0;
281 var->blue.length = 8;
282 break;
283 }
284
285 return 0;
286}
287
288static struct fb_ops ocfb_ops = {
289 .owner = THIS_MODULE,
290 .fb_setcolreg = ocfb_setcolreg,
291 .fb_fillrect = cfb_fillrect,
292 .fb_copyarea = cfb_copyarea,
293 .fb_imageblit = cfb_imageblit,
294};
295
296static int ocfb_probe(struct platform_device *pdev)
297{
298 int ret = 0;
299 struct ocfb_dev *fbdev;
300 struct resource *res;
301 int fbsize;
302
303 fbdev = devm_kzalloc(&pdev->dev, sizeof(*fbdev), GFP_KERNEL);
304 if (!fbdev)
305 return -ENOMEM;
306
307 platform_set_drvdata(pdev, fbdev);
308
309 fbdev->info.fbops = &ocfb_ops;
310 fbdev->info.device = &pdev->dev;
311 fbdev->info.par = fbdev;
312
313 /* Video mode setup */
314 if (!fb_find_mode(&fbdev->info.var, &fbdev->info, mode_option,
315 NULL, 0, &default_mode, 16)) {
316 dev_err(&pdev->dev, "No valid video modes found\n");
317 return -EINVAL;
318 }
319 ocfb_init_var(fbdev);
320 ocfb_init_fix(fbdev);
321
322 /* Request I/O resource */
323 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
324 if (!res) {
325 dev_err(&pdev->dev, "I/O resource request failed\n");
326 return -ENXIO;
327 }
328 res->flags &= ~IORESOURCE_CACHEABLE;
329 fbdev->regs = devm_ioremap_resource(&pdev->dev, res);
330 if (IS_ERR(fbdev->regs))
331 return PTR_ERR(fbdev->regs);
332
333 /* Allocate framebuffer memory */
334 fbsize = fbdev->info.fix.smem_len;
335 fbdev->fb_virt = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbsize),
336 &fbdev->fb_phys, GFP_KERNEL);
337 if (!fbdev->fb_virt) {
338 dev_err(&pdev->dev,
339 "Frame buffer memory allocation failed\n");
340 return -ENOMEM;
341 }
342 fbdev->info.fix.smem_start = fbdev->fb_phys;
343 fbdev->info.screen_base = fbdev->fb_virt;
344 fbdev->info.pseudo_palette = fbdev->pseudo_palette;
345
346 /* Clear framebuffer */
347 memset_io(fbdev->fb_virt, 0, fbsize);
348
349 /* Setup and enable the framebuffer */
350 ocfb_setupfb(fbdev);
351
352 if (fbdev->little_endian)
353 fbdev->info.flags |= FBINFO_FOREIGN_ENDIAN;
354
355 /* Allocate color map */
356 ret = fb_alloc_cmap(&fbdev->info.cmap, PALETTE_SIZE, 0);
357 if (ret) {
358 dev_err(&pdev->dev, "Color map allocation failed\n");
359 goto err_dma_free;
360 }
361
362 /* Register framebuffer */
363 ret = register_framebuffer(&fbdev->info);
364 if (ret) {
365 dev_err(&pdev->dev, "Framebuffer registration failed\n");
366 goto err_dealloc_cmap;
367 }
368
369 return 0;
370
371err_dealloc_cmap:
372 fb_dealloc_cmap(&fbdev->info.cmap);
373
374err_dma_free:
375 dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbsize), fbdev->fb_virt,
376 fbdev->fb_phys);
377
378 return ret;
379}
380
381static int ocfb_remove(struct platform_device *pdev)
382{
383 struct ocfb_dev *fbdev = platform_get_drvdata(pdev);
384
385 unregister_framebuffer(&fbdev->info);
386 fb_dealloc_cmap(&fbdev->info.cmap);
387 dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbdev->info.fix.smem_len),
388 fbdev->fb_virt, fbdev->fb_phys);
389
390 /* Disable display */
391 ocfb_writereg(fbdev, OCFB_CTRL, 0);
392
393 platform_set_drvdata(pdev, NULL);
394
395 return 0;
396}
397
398static struct of_device_id ocfb_match[] = {
399 { .compatible = "opencores,ocfb", },
400 {},
401};
402MODULE_DEVICE_TABLE(of, ocfb_match);
403
404static struct platform_driver ocfb_driver = {
405 .probe = ocfb_probe,
406 .remove = ocfb_remove,
407 .driver = {
408 .name = "ocfb_fb",
409 .of_match_table = ocfb_match,
410 }
411};
412
413/*
414 * Init and exit routines
415 */
416static int __init ocfb_init(void)
417{
418#ifndef MODULE
419 char *option = NULL;
420
421 if (fb_get_options("ocfb", &option))
422 return -ENODEV;
423 ocfb_setup(option);
424#endif
425 return platform_driver_register(&ocfb_driver);
426}
427
428static void __exit ocfb_exit(void)
429{
430 platform_driver_unregister(&ocfb_driver);
431}
432
433module_init(ocfb_init);
434module_exit(ocfb_exit);
435
436MODULE_AUTHOR("Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>");
437MODULE_DESCRIPTION("OpenCores VGA/LCD 2.0 frame buffer driver");
438MODULE_LICENSE("GPL v2");
439module_param(mode_option, charp, 0);
440MODULE_PARM_DESC(mode_option, "Video mode ('<xres>x<yres>[-<bpp>][@refresh]')");