2 * OpenCores VGA/LCD 2.0 core frame buffer driver
4 * Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson@saunalahti.fi
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.
11 #include <linux/delay.h>
12 #include <linux/dma-mapping.h>
13 #include <linux/errno.h>
15 #include <linux/init.h>
17 #include <linux/kernel.h>
19 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/string.h>
23 #include <linux/slab.h>
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
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 */
46 #define PALETTE_SIZE 256
48 #define OCFB_NAME "OC VGA/LCD"
50 static char *mode_option
;
52 static 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
61 /* flag indicating whether the regs are little endian accessed */
63 /* Physical and virtual addresses of framebuffer */
65 void __iomem
*fb_virt
;
66 u32 pseudo_palette
[PALETTE_SIZE
];
70 static int __init
ocfb_setup(char *options
)
74 if (!options
|| !*options
)
77 while ((curr_opt
= strsep(&options
, ",")) != NULL
) {
80 mode_option
= curr_opt
;
87 static inline u32
ocfb_readreg(struct ocfb_dev
*fbdev
, loff_t offset
)
89 if (fbdev
->little_endian
)
90 return ioread32(fbdev
->regs
+ offset
);
92 return ioread32be(fbdev
->regs
+ offset
);
95 static void ocfb_writereg(struct ocfb_dev
*fbdev
, loff_t offset
, u32 data
)
97 if (fbdev
->little_endian
)
98 iowrite32(data
, fbdev
->regs
+ offset
);
100 iowrite32be(data
, fbdev
->regs
+ offset
);
103 static int ocfb_setupfb(struct ocfb_dev
*fbdev
)
105 unsigned long bpp_config
;
106 struct fb_var_screeninfo
*var
= &fbdev
->info
.var
;
107 struct device
*dev
= fbdev
->info
.device
;
111 /* Disable display */
112 ocfb_writereg(fbdev
, OCFB_CTRL
, 0);
114 /* Register framebuffer address */
115 fbdev
->little_endian
= 0;
116 ocfb_writereg(fbdev
, OCFB_VBARA
, fbdev
->fb_phys
);
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
);
124 /* Horizontal timings */
125 ocfb_writereg(fbdev
, OCFB_HTIM
, (var
->hsync_len
- 1) << 24 |
126 (var
->left_margin
- 1) << 16 | (var
->xres
- 1));
128 /* Vertical timings */
129 ocfb_writereg(fbdev
, OCFB_VTIM
, (var
->vsync_len
- 1) << 24 |
130 (var
->upper_margin
- 1) << 16 | (var
->yres
- 1));
132 /* Total length of frame */
133 hlen
= var
->left_margin
+ var
->right_margin
+ var
->hsync_len
+
136 vlen
= var
->upper_margin
+ var
->lower_margin
+ var
->vsync_len
+
139 ocfb_writereg(fbdev
, OCFB_HVLEN
, (hlen
- 1) << 16 | (vlen
- 1));
141 bpp_config
= OCFB_CTRL_CD8
;
142 switch (var
->bits_per_pixel
) {
145 bpp_config
|= OCFB_CTRL_PC
; /* enable palette */
149 bpp_config
|= OCFB_CTRL_CD16
;
153 bpp_config
|= OCFB_CTRL_CD24
;
157 bpp_config
|= OCFB_CTRL_CD32
;
161 dev_err(dev
, "no bpp specified\n");
165 /* maximum (8) VBL (video memory burst length) */
166 bpp_config
|= OCFB_CTRL_VBL8
;
169 ocfb_writereg(fbdev
, OCFB_CTRL
, (OCFB_CTRL_VEN
| bpp_config
));
174 static int ocfb_setcolreg(unsigned regno
, unsigned red
, unsigned green
,
175 unsigned blue
, unsigned transp
,
176 struct fb_info
*info
)
178 struct ocfb_dev
*fbdev
= (struct ocfb_dev
*)info
->par
;
181 if (regno
>= info
->cmap
.len
) {
182 dev_err(info
->device
, "regno >= cmap.len\n");
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;
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
);
196 if (info
->var
.bits_per_pixel
== 8 && !info
->var
.grayscale
) {
198 color
= (red
<< 16) | (green
<< 8) | blue
;
199 ocfb_writereg(fbdev
, OCFB_PALETTE
+ regno
, color
);
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
);
211 static int ocfb_init_fix(struct ocfb_dev
*fbdev
)
213 struct fb_var_screeninfo
*var
= &fbdev
->info
.var
;
214 struct fb_fix_screeninfo
*fix
= &fbdev
->info
.fix
;
216 strcpy(fix
->id
, OCFB_NAME
);
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
;
222 if (var
->bits_per_pixel
== 8 && !var
->grayscale
)
223 fix
->visual
= FB_VISUAL_PSEUDOCOLOR
;
225 fix
->visual
= FB_VISUAL_TRUECOLOR
;
230 static int ocfb_init_var(struct ocfb_dev
*fbdev
)
232 struct fb_var_screeninfo
*var
= &fbdev
->info
.var
;
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
;
239 switch (var
->bits_per_pixel
) {
241 var
->transp
.offset
= 0;
242 var
->transp
.length
= 0;
245 var
->green
.offset
= 0;
246 var
->green
.length
= 8;
247 var
->blue
.offset
= 0;
248 var
->blue
.length
= 8;
252 var
->transp
.offset
= 0;
253 var
->transp
.length
= 0;
254 var
->red
.offset
= 11;
256 var
->green
.offset
= 5;
257 var
->green
.length
= 6;
258 var
->blue
.offset
= 0;
259 var
->blue
.length
= 5;
263 var
->transp
.offset
= 0;
264 var
->transp
.length
= 0;
265 var
->red
.offset
= 16;
267 var
->green
.offset
= 8;
268 var
->green
.length
= 8;
269 var
->blue
.offset
= 0;
270 var
->blue
.length
= 8;
274 var
->transp
.offset
= 24;
275 var
->transp
.length
= 8;
276 var
->red
.offset
= 16;
278 var
->green
.offset
= 8;
279 var
->green
.length
= 8;
280 var
->blue
.offset
= 0;
281 var
->blue
.length
= 8;
288 static 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
,
296 static int ocfb_probe(struct platform_device
*pdev
)
299 struct ocfb_dev
*fbdev
;
300 struct resource
*res
;
303 fbdev
= devm_kzalloc(&pdev
->dev
, sizeof(*fbdev
), GFP_KERNEL
);
307 platform_set_drvdata(pdev
, fbdev
);
309 fbdev
->info
.fbops
= &ocfb_ops
;
310 fbdev
->info
.device
= &pdev
->dev
;
311 fbdev
->info
.par
= fbdev
;
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");
319 ocfb_init_var(fbdev
);
320 ocfb_init_fix(fbdev
);
322 /* Request I/O resource */
323 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
325 dev_err(&pdev
->dev
, "I/O resource request failed\n");
328 fbdev
->regs
= devm_ioremap_resource(&pdev
->dev
, res
);
329 if (IS_ERR(fbdev
->regs
))
330 return PTR_ERR(fbdev
->regs
);
332 /* Allocate framebuffer memory */
333 fbsize
= fbdev
->info
.fix
.smem_len
;
334 fbdev
->fb_virt
= dma_alloc_coherent(&pdev
->dev
, PAGE_ALIGN(fbsize
),
335 &fbdev
->fb_phys
, GFP_KERNEL
);
336 if (!fbdev
->fb_virt
) {
338 "Frame buffer memory allocation failed\n");
341 fbdev
->info
.fix
.smem_start
= fbdev
->fb_phys
;
342 fbdev
->info
.screen_base
= fbdev
->fb_virt
;
343 fbdev
->info
.pseudo_palette
= fbdev
->pseudo_palette
;
345 /* Clear framebuffer */
346 memset_io(fbdev
->fb_virt
, 0, fbsize
);
348 /* Setup and enable the framebuffer */
351 if (fbdev
->little_endian
)
352 fbdev
->info
.flags
|= FBINFO_FOREIGN_ENDIAN
;
354 /* Allocate color map */
355 ret
= fb_alloc_cmap(&fbdev
->info
.cmap
, PALETTE_SIZE
, 0);
357 dev_err(&pdev
->dev
, "Color map allocation failed\n");
361 /* Register framebuffer */
362 ret
= register_framebuffer(&fbdev
->info
);
364 dev_err(&pdev
->dev
, "Framebuffer registration failed\n");
365 goto err_dealloc_cmap
;
371 fb_dealloc_cmap(&fbdev
->info
.cmap
);
374 dma_free_coherent(&pdev
->dev
, PAGE_ALIGN(fbsize
), fbdev
->fb_virt
,
380 static int ocfb_remove(struct platform_device
*pdev
)
382 struct ocfb_dev
*fbdev
= platform_get_drvdata(pdev
);
384 unregister_framebuffer(&fbdev
->info
);
385 fb_dealloc_cmap(&fbdev
->info
.cmap
);
386 dma_free_coherent(&pdev
->dev
, PAGE_ALIGN(fbdev
->info
.fix
.smem_len
),
387 fbdev
->fb_virt
, fbdev
->fb_phys
);
389 /* Disable display */
390 ocfb_writereg(fbdev
, OCFB_CTRL
, 0);
392 platform_set_drvdata(pdev
, NULL
);
397 static struct of_device_id ocfb_match
[] = {
398 { .compatible
= "opencores,ocfb", },
401 MODULE_DEVICE_TABLE(of
, ocfb_match
);
403 static struct platform_driver ocfb_driver
= {
405 .remove
= ocfb_remove
,
408 .of_match_table
= ocfb_match
,
413 * Init and exit routines
415 static int __init
ocfb_init(void)
420 if (fb_get_options("ocfb", &option
))
424 return platform_driver_register(&ocfb_driver
);
427 static void __exit
ocfb_exit(void)
429 platform_driver_unregister(&ocfb_driver
);
432 module_init(ocfb_init
);
433 module_exit(ocfb_exit
);
435 MODULE_AUTHOR("Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>");
436 MODULE_DESCRIPTION("OpenCores VGA/LCD 2.0 frame buffer driver");
437 MODULE_LICENSE("GPL v2");
438 module_param(mode_option
, charp
, 0);
439 MODULE_PARM_DESC(mode_option
, "Video mode ('<xres>x<yres>[-<bpp>][@refresh]')");