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