0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/delay.h>
0012 #include <linux/dma-mapping.h>
0013 #include <linux/errno.h>
0014 #include <linux/fb.h>
0015 #include <linux/init.h>
0016 #include <linux/io.h>
0017 #include <linux/kernel.h>
0018 #include <linux/mm.h>
0019 #include <linux/module.h>
0020 #include <linux/of.h>
0021 #include <linux/platform_device.h>
0022 #include <linux/string.h>
0023 #include <linux/slab.h>
0024
0025
0026 #define OCFB_CTRL 0x000
0027 #define OCFB_STAT 0x004
0028 #define OCFB_HTIM 0x008
0029 #define OCFB_VTIM 0x00c
0030 #define OCFB_HVLEN 0x010
0031 #define OCFB_VBARA 0x014
0032 #define OCFB_PALETTE 0x800
0033
0034 #define OCFB_CTRL_VEN 0x00000001
0035 #define OCFB_CTRL_HIE 0x00000002
0036 #define OCFB_CTRL_PC 0x00000800
0037 #define OCFB_CTRL_CD8 0x00000000
0038 #define OCFB_CTRL_CD16 0x00000200
0039 #define OCFB_CTRL_CD24 0x00000400
0040 #define OCFB_CTRL_CD32 0x00000600
0041 #define OCFB_CTRL_VBL1 0x00000000
0042 #define OCFB_CTRL_VBL2 0x00000080
0043 #define OCFB_CTRL_VBL4 0x00000100
0044 #define OCFB_CTRL_VBL8 0x00000180
0045
0046 #define PALETTE_SIZE 256
0047
0048 #define OCFB_NAME "OC VGA/LCD"
0049
0050 static char *mode_option;
0051
0052 static const struct fb_videomode default_mode = {
0053
0054 NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2,
0055 0, FB_VMODE_NONINTERLACED
0056 };
0057
0058 struct ocfb_dev {
0059 struct fb_info info;
0060 void __iomem *regs;
0061
0062 int little_endian;
0063
0064 dma_addr_t fb_phys;
0065 void __iomem *fb_virt;
0066 u32 pseudo_palette[PALETTE_SIZE];
0067 };
0068
0069 #ifndef MODULE
0070 static int __init ocfb_setup(char *options)
0071 {
0072 char *curr_opt;
0073
0074 if (!options || !*options)
0075 return 0;
0076
0077 while ((curr_opt = strsep(&options, ",")) != NULL) {
0078 if (!*curr_opt)
0079 continue;
0080 mode_option = curr_opt;
0081 }
0082
0083 return 0;
0084 }
0085 #endif
0086
0087 static inline u32 ocfb_readreg(struct ocfb_dev *fbdev, loff_t offset)
0088 {
0089 if (fbdev->little_endian)
0090 return ioread32(fbdev->regs + offset);
0091 else
0092 return ioread32be(fbdev->regs + offset);
0093 }
0094
0095 static void ocfb_writereg(struct ocfb_dev *fbdev, loff_t offset, u32 data)
0096 {
0097 if (fbdev->little_endian)
0098 iowrite32(data, fbdev->regs + offset);
0099 else
0100 iowrite32be(data, fbdev->regs + offset);
0101 }
0102
0103 static int ocfb_setupfb(struct ocfb_dev *fbdev)
0104 {
0105 unsigned long bpp_config;
0106 struct fb_var_screeninfo *var = &fbdev->info.var;
0107 struct device *dev = fbdev->info.device;
0108 u32 hlen;
0109 u32 vlen;
0110
0111
0112 ocfb_writereg(fbdev, OCFB_CTRL, 0);
0113
0114
0115 fbdev->little_endian = 0;
0116 ocfb_writereg(fbdev, OCFB_VBARA, fbdev->fb_phys);
0117
0118
0119 if (ocfb_readreg(fbdev, OCFB_VBARA) != fbdev->fb_phys) {
0120 fbdev->little_endian = 1;
0121 ocfb_writereg(fbdev, OCFB_VBARA, fbdev->fb_phys);
0122 }
0123
0124
0125 ocfb_writereg(fbdev, OCFB_HTIM, (var->hsync_len - 1) << 24 |
0126 (var->left_margin - 1) << 16 | (var->xres - 1));
0127
0128
0129 ocfb_writereg(fbdev, OCFB_VTIM, (var->vsync_len - 1) << 24 |
0130 (var->upper_margin - 1) << 16 | (var->yres - 1));
0131
0132
0133 hlen = var->left_margin + var->right_margin + var->hsync_len +
0134 var->xres;
0135
0136 vlen = var->upper_margin + var->lower_margin + var->vsync_len +
0137 var->yres;
0138
0139 ocfb_writereg(fbdev, OCFB_HVLEN, (hlen - 1) << 16 | (vlen - 1));
0140
0141 bpp_config = OCFB_CTRL_CD8;
0142 switch (var->bits_per_pixel) {
0143 case 8:
0144 if (!var->grayscale)
0145 bpp_config |= OCFB_CTRL_PC;
0146 break;
0147
0148 case 16:
0149 bpp_config |= OCFB_CTRL_CD16;
0150 break;
0151
0152 case 24:
0153 bpp_config |= OCFB_CTRL_CD24;
0154 break;
0155
0156 case 32:
0157 bpp_config |= OCFB_CTRL_CD32;
0158 break;
0159
0160 default:
0161 dev_err(dev, "no bpp specified\n");
0162 break;
0163 }
0164
0165
0166 bpp_config |= OCFB_CTRL_VBL8;
0167
0168
0169 ocfb_writereg(fbdev, OCFB_CTRL, (OCFB_CTRL_VEN | bpp_config));
0170
0171 return 0;
0172 }
0173
0174 static int ocfb_setcolreg(unsigned regno, unsigned red, unsigned green,
0175 unsigned blue, unsigned transp,
0176 struct fb_info *info)
0177 {
0178 struct ocfb_dev *fbdev = (struct ocfb_dev *)info->par;
0179 u32 color;
0180
0181 if (regno >= info->cmap.len) {
0182 dev_err(info->device, "regno >= cmap.len\n");
0183 return 1;
0184 }
0185
0186 if (info->var.grayscale) {
0187
0188 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
0189 }
0190
0191 red >>= (16 - info->var.red.length);
0192 green >>= (16 - info->var.green.length);
0193 blue >>= (16 - info->var.blue.length);
0194 transp >>= (16 - info->var.transp.length);
0195
0196 if (info->var.bits_per_pixel == 8 && !info->var.grayscale) {
0197 regno <<= 2;
0198 color = (red << 16) | (green << 8) | blue;
0199 ocfb_writereg(fbdev, OCFB_PALETTE + regno, color);
0200 } else {
0201 ((u32 *)(info->pseudo_palette))[regno] =
0202 (red << info->var.red.offset) |
0203 (green << info->var.green.offset) |
0204 (blue << info->var.blue.offset) |
0205 (transp << info->var.transp.offset);
0206 }
0207
0208 return 0;
0209 }
0210
0211 static int ocfb_init_fix(struct ocfb_dev *fbdev)
0212 {
0213 struct fb_var_screeninfo *var = &fbdev->info.var;
0214 struct fb_fix_screeninfo *fix = &fbdev->info.fix;
0215
0216 strcpy(fix->id, OCFB_NAME);
0217
0218 fix->line_length = var->xres * var->bits_per_pixel/8;
0219 fix->smem_len = fix->line_length * var->yres;
0220 fix->type = FB_TYPE_PACKED_PIXELS;
0221
0222 if (var->bits_per_pixel == 8 && !var->grayscale)
0223 fix->visual = FB_VISUAL_PSEUDOCOLOR;
0224 else
0225 fix->visual = FB_VISUAL_TRUECOLOR;
0226
0227 return 0;
0228 }
0229
0230 static int ocfb_init_var(struct ocfb_dev *fbdev)
0231 {
0232 struct fb_var_screeninfo *var = &fbdev->info.var;
0233
0234 var->accel_flags = FB_ACCEL_NONE;
0235 var->activate = FB_ACTIVATE_NOW;
0236 var->xres_virtual = var->xres;
0237 var->yres_virtual = var->yres;
0238
0239 switch (var->bits_per_pixel) {
0240 case 8:
0241 var->transp.offset = 0;
0242 var->transp.length = 0;
0243 var->red.offset = 0;
0244 var->red.length = 8;
0245 var->green.offset = 0;
0246 var->green.length = 8;
0247 var->blue.offset = 0;
0248 var->blue.length = 8;
0249 break;
0250
0251 case 16:
0252 var->transp.offset = 0;
0253 var->transp.length = 0;
0254 var->red.offset = 11;
0255 var->red.length = 5;
0256 var->green.offset = 5;
0257 var->green.length = 6;
0258 var->blue.offset = 0;
0259 var->blue.length = 5;
0260 break;
0261
0262 case 24:
0263 var->transp.offset = 0;
0264 var->transp.length = 0;
0265 var->red.offset = 16;
0266 var->red.length = 8;
0267 var->green.offset = 8;
0268 var->green.length = 8;
0269 var->blue.offset = 0;
0270 var->blue.length = 8;
0271 break;
0272
0273 case 32:
0274 var->transp.offset = 24;
0275 var->transp.length = 8;
0276 var->red.offset = 16;
0277 var->red.length = 8;
0278 var->green.offset = 8;
0279 var->green.length = 8;
0280 var->blue.offset = 0;
0281 var->blue.length = 8;
0282 break;
0283 }
0284
0285 return 0;
0286 }
0287
0288 static const struct fb_ops ocfb_ops = {
0289 .owner = THIS_MODULE,
0290 .fb_setcolreg = ocfb_setcolreg,
0291 .fb_fillrect = cfb_fillrect,
0292 .fb_copyarea = cfb_copyarea,
0293 .fb_imageblit = cfb_imageblit,
0294 };
0295
0296 static int ocfb_probe(struct platform_device *pdev)
0297 {
0298 int ret = 0;
0299 struct ocfb_dev *fbdev;
0300 int fbsize;
0301
0302 fbdev = devm_kzalloc(&pdev->dev, sizeof(*fbdev), GFP_KERNEL);
0303 if (!fbdev)
0304 return -ENOMEM;
0305
0306 platform_set_drvdata(pdev, fbdev);
0307
0308 fbdev->info.fbops = &ocfb_ops;
0309 fbdev->info.device = &pdev->dev;
0310 fbdev->info.par = fbdev;
0311
0312
0313 if (!fb_find_mode(&fbdev->info.var, &fbdev->info, mode_option,
0314 NULL, 0, &default_mode, 16)) {
0315 dev_err(&pdev->dev, "No valid video modes found\n");
0316 return -EINVAL;
0317 }
0318 ocfb_init_var(fbdev);
0319 ocfb_init_fix(fbdev);
0320
0321 fbdev->regs = devm_platform_ioremap_resource(pdev, 0);
0322 if (IS_ERR(fbdev->regs))
0323 return PTR_ERR(fbdev->regs);
0324
0325
0326 fbsize = fbdev->info.fix.smem_len;
0327 fbdev->fb_virt = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbsize),
0328 &fbdev->fb_phys, GFP_KERNEL);
0329 if (!fbdev->fb_virt) {
0330 dev_err(&pdev->dev,
0331 "Frame buffer memory allocation failed\n");
0332 return -ENOMEM;
0333 }
0334 fbdev->info.fix.smem_start = fbdev->fb_phys;
0335 fbdev->info.screen_base = fbdev->fb_virt;
0336 fbdev->info.pseudo_palette = fbdev->pseudo_palette;
0337
0338
0339 memset_io(fbdev->fb_virt, 0, fbsize);
0340
0341
0342 ocfb_setupfb(fbdev);
0343
0344 if (fbdev->little_endian)
0345 fbdev->info.flags |= FBINFO_FOREIGN_ENDIAN;
0346
0347
0348 ret = fb_alloc_cmap(&fbdev->info.cmap, PALETTE_SIZE, 0);
0349 if (ret) {
0350 dev_err(&pdev->dev, "Color map allocation failed\n");
0351 goto err_dma_free;
0352 }
0353
0354
0355 ret = register_framebuffer(&fbdev->info);
0356 if (ret) {
0357 dev_err(&pdev->dev, "Framebuffer registration failed\n");
0358 goto err_dealloc_cmap;
0359 }
0360
0361 return 0;
0362
0363 err_dealloc_cmap:
0364 fb_dealloc_cmap(&fbdev->info.cmap);
0365
0366 err_dma_free:
0367 dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbsize), fbdev->fb_virt,
0368 fbdev->fb_phys);
0369
0370 return ret;
0371 }
0372
0373 static int ocfb_remove(struct platform_device *pdev)
0374 {
0375 struct ocfb_dev *fbdev = platform_get_drvdata(pdev);
0376
0377 unregister_framebuffer(&fbdev->info);
0378 fb_dealloc_cmap(&fbdev->info.cmap);
0379 dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbdev->info.fix.smem_len),
0380 fbdev->fb_virt, fbdev->fb_phys);
0381
0382
0383 ocfb_writereg(fbdev, OCFB_CTRL, 0);
0384
0385 platform_set_drvdata(pdev, NULL);
0386
0387 return 0;
0388 }
0389
0390 static const struct of_device_id ocfb_match[] = {
0391 { .compatible = "opencores,ocfb", },
0392 {},
0393 };
0394 MODULE_DEVICE_TABLE(of, ocfb_match);
0395
0396 static struct platform_driver ocfb_driver = {
0397 .probe = ocfb_probe,
0398 .remove = ocfb_remove,
0399 .driver = {
0400 .name = "ocfb_fb",
0401 .of_match_table = ocfb_match,
0402 }
0403 };
0404
0405
0406
0407
0408 static int __init ocfb_init(void)
0409 {
0410 #ifndef MODULE
0411 char *option = NULL;
0412
0413 if (fb_get_options("ocfb", &option))
0414 return -ENODEV;
0415 ocfb_setup(option);
0416 #endif
0417 return platform_driver_register(&ocfb_driver);
0418 }
0419
0420 static void __exit ocfb_exit(void)
0421 {
0422 platform_driver_unregister(&ocfb_driver);
0423 }
0424
0425 module_init(ocfb_init);
0426 module_exit(ocfb_exit);
0427
0428 MODULE_AUTHOR("Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>");
0429 MODULE_DESCRIPTION("OpenCores VGA/LCD 2.0 frame buffer driver");
0430 MODULE_LICENSE("GPL v2");
0431 module_param(mode_option, charp, 0);
0432 MODULE_PARM_DESC(mode_option, "Video mode ('<xres>x<yres>[-<bpp>][@refresh]')");