ocfb.c (10354B)
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 dma_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->left_margin - 1) << 16 | (var->xres - 1)); 127 128 /* Vertical timings */ 129 ocfb_writereg(fbdev, OCFB_VTIM, (var->vsync_len - 1) << 24 | 130 (var->upper_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 const 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 int fbsize; 301 302 fbdev = devm_kzalloc(&pdev->dev, sizeof(*fbdev), GFP_KERNEL); 303 if (!fbdev) 304 return -ENOMEM; 305 306 platform_set_drvdata(pdev, fbdev); 307 308 fbdev->info.fbops = &ocfb_ops; 309 fbdev->info.device = &pdev->dev; 310 fbdev->info.par = fbdev; 311 312 /* Video mode setup */ 313 if (!fb_find_mode(&fbdev->info.var, &fbdev->info, mode_option, 314 NULL, 0, &default_mode, 16)) { 315 dev_err(&pdev->dev, "No valid video modes found\n"); 316 return -EINVAL; 317 } 318 ocfb_init_var(fbdev); 319 ocfb_init_fix(fbdev); 320 321 fbdev->regs = devm_platform_ioremap_resource(pdev, 0); 322 if (IS_ERR(fbdev->regs)) 323 return PTR_ERR(fbdev->regs); 324 325 /* Allocate framebuffer memory */ 326 fbsize = fbdev->info.fix.smem_len; 327 fbdev->fb_virt = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbsize), 328 &fbdev->fb_phys, GFP_KERNEL); 329 if (!fbdev->fb_virt) { 330 dev_err(&pdev->dev, 331 "Frame buffer memory allocation failed\n"); 332 return -ENOMEM; 333 } 334 fbdev->info.fix.smem_start = fbdev->fb_phys; 335 fbdev->info.screen_base = fbdev->fb_virt; 336 fbdev->info.pseudo_palette = fbdev->pseudo_palette; 337 338 /* Clear framebuffer */ 339 memset_io(fbdev->fb_virt, 0, fbsize); 340 341 /* Setup and enable the framebuffer */ 342 ocfb_setupfb(fbdev); 343 344 if (fbdev->little_endian) 345 fbdev->info.flags |= FBINFO_FOREIGN_ENDIAN; 346 347 /* Allocate color map */ 348 ret = fb_alloc_cmap(&fbdev->info.cmap, PALETTE_SIZE, 0); 349 if (ret) { 350 dev_err(&pdev->dev, "Color map allocation failed\n"); 351 goto err_dma_free; 352 } 353 354 /* Register framebuffer */ 355 ret = register_framebuffer(&fbdev->info); 356 if (ret) { 357 dev_err(&pdev->dev, "Framebuffer registration failed\n"); 358 goto err_dealloc_cmap; 359 } 360 361 return 0; 362 363err_dealloc_cmap: 364 fb_dealloc_cmap(&fbdev->info.cmap); 365 366err_dma_free: 367 dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbsize), fbdev->fb_virt, 368 fbdev->fb_phys); 369 370 return ret; 371} 372 373static int ocfb_remove(struct platform_device *pdev) 374{ 375 struct ocfb_dev *fbdev = platform_get_drvdata(pdev); 376 377 unregister_framebuffer(&fbdev->info); 378 fb_dealloc_cmap(&fbdev->info.cmap); 379 dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbdev->info.fix.smem_len), 380 fbdev->fb_virt, fbdev->fb_phys); 381 382 /* Disable display */ 383 ocfb_writereg(fbdev, OCFB_CTRL, 0); 384 385 platform_set_drvdata(pdev, NULL); 386 387 return 0; 388} 389 390static const struct of_device_id ocfb_match[] = { 391 { .compatible = "opencores,ocfb", }, 392 {}, 393}; 394MODULE_DEVICE_TABLE(of, ocfb_match); 395 396static struct platform_driver ocfb_driver = { 397 .probe = ocfb_probe, 398 .remove = ocfb_remove, 399 .driver = { 400 .name = "ocfb_fb", 401 .of_match_table = ocfb_match, 402 } 403}; 404 405/* 406 * Init and exit routines 407 */ 408static int __init ocfb_init(void) 409{ 410#ifndef MODULE 411 char *option = NULL; 412 413 if (fb_get_options("ocfb", &option)) 414 return -ENODEV; 415 ocfb_setup(option); 416#endif 417 return platform_driver_register(&ocfb_driver); 418} 419 420static void __exit ocfb_exit(void) 421{ 422 platform_driver_unregister(&ocfb_driver); 423} 424 425module_init(ocfb_init); 426module_exit(ocfb_exit); 427 428MODULE_AUTHOR("Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>"); 429MODULE_DESCRIPTION("OpenCores VGA/LCD 2.0 frame buffer driver"); 430MODULE_LICENSE("GPL v2"); 431module_param(mode_option, charp, 0); 432MODULE_PARM_DESC(mode_option, "Video mode ('<xres>x<yres>[-<bpp>][@refresh]')");