Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * OpenCores VGA/LCD 2.0 core frame buffer driver
0003  *
0004  * Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson@saunalahti.fi
0005  *
0006  * This file is licensed under the terms of the GNU General Public License
0007  * version 2.  This program is licensed "as is" without any warranty of any
0008  * kind, whether express or implied.
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 /* OCFB register defines */
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 /* Video Enable */
0035 #define OCFB_CTRL_HIE   0x00000002 /* HSync Interrupt Enable */
0036 #define OCFB_CTRL_PC    0x00000800 /* 8-bit Pseudo Color Enable*/
0037 #define OCFB_CTRL_CD8   0x00000000 /* Color Depth 8 */
0038 #define OCFB_CTRL_CD16  0x00000200 /* Color Depth 16 */
0039 #define OCFB_CTRL_CD24  0x00000400 /* Color Depth 24 */
0040 #define OCFB_CTRL_CD32  0x00000600 /* Color Depth 32 */
0041 #define OCFB_CTRL_VBL1  0x00000000 /* Burst Length 1 */
0042 #define OCFB_CTRL_VBL2  0x00000080 /* Burst Length 2 */
0043 #define OCFB_CTRL_VBL4  0x00000100 /* Burst Length 4 */
0044 #define OCFB_CTRL_VBL8  0x00000180 /* Burst Length 8 */
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     /* 640x480 @ 60 Hz, 31.5 kHz hsync */
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     /* flag indicating whether the regs are little endian accessed */
0062     int little_endian;
0063     /* Physical and virtual addresses of framebuffer */
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     /* Disable display */
0112     ocfb_writereg(fbdev, OCFB_CTRL, 0);
0113 
0114     /* Register framebuffer address */
0115     fbdev->little_endian = 0;
0116     ocfb_writereg(fbdev, OCFB_VBARA, fbdev->fb_phys);
0117 
0118     /* Detect endianess */
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     /* Horizontal timings */
0125     ocfb_writereg(fbdev, OCFB_HTIM, (var->hsync_len - 1) << 24 |
0126               (var->left_margin - 1) << 16 | (var->xres - 1));
0127 
0128     /* Vertical timings */
0129     ocfb_writereg(fbdev, OCFB_VTIM, (var->vsync_len - 1) << 24 |
0130               (var->upper_margin - 1) << 16 | (var->yres - 1));
0131 
0132     /* Total length of frame */
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;  /* enable palette */
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     /* maximum (8) VBL (video memory burst length) */
0166     bpp_config |= OCFB_CTRL_VBL8;
0167 
0168     /* Enable output */
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         /* grayscale = 0.30*R + 0.59*G + 0.11*B */
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     /* Video mode setup */
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     /* Allocate framebuffer memory */
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     /* Clear framebuffer */
0339     memset_io(fbdev->fb_virt, 0, fbsize);
0340 
0341     /* Setup and enable the framebuffer */
0342     ocfb_setupfb(fbdev);
0343 
0344     if (fbdev->little_endian)
0345         fbdev->info.flags |= FBINFO_FOREIGN_ENDIAN;
0346 
0347     /* Allocate color map */
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     /* Register framebuffer */
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     /* Disable display */
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  * Init and exit routines
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]')");