Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  *  linux/drivers/video/68328fb.c -- Low level implementation of the
0003  *                                   mc68x328 LCD frame buffer device
0004  *
0005  *  Copyright (C) 2003 Georges Menie
0006  *
0007  *  This driver assumes an already configured controller (e.g. from config.c)
0008  *  Keep the code clean of board specific initialization.
0009  *
0010  *  This code has not been tested with colors, colormap management functions
0011  *  are minimal (no colormap data written to the 68328 registers...)
0012  *
0013  *  initial version of this driver:
0014  *    Copyright (C) 1998,1999 Kenneth Albanowski <kjahds@kjahds.com>,
0015  *                            The Silver Hammer Group, Ltd.
0016  *
0017  *  this version is based on :
0018  *
0019  *  linux/drivers/video/vfb.c -- Virtual frame buffer device
0020  *
0021  *      Copyright (C) 2002 James Simmons
0022  *
0023  *  Copyright (C) 1997 Geert Uytterhoeven
0024  *
0025  *  This file is subject to the terms and conditions of the GNU General Public
0026  *  License. See the file COPYING in the main directory of this archive for
0027  *  more details.
0028  */
0029 
0030 #include <linux/module.h>
0031 #include <linux/kernel.h>
0032 #include <linux/errno.h>
0033 #include <linux/string.h>
0034 #include <linux/mm.h>
0035 #include <linux/vmalloc.h>
0036 #include <linux/delay.h>
0037 #include <linux/interrupt.h>
0038 #include <linux/uaccess.h>
0039 #include <linux/fb.h>
0040 #include <linux/init.h>
0041 
0042 #if defined(CONFIG_M68VZ328)
0043 #include <asm/MC68VZ328.h>
0044 #elif defined(CONFIG_M68EZ328)
0045 #include <asm/MC68EZ328.h>
0046 #elif defined(CONFIG_M68328)
0047 #include <asm/MC68328.h>
0048 #else
0049 #error wrong architecture for the MC68x328 frame buffer device
0050 #endif
0051 
0052 static u_long videomemory;
0053 static u_long videomemorysize;
0054 
0055 static struct fb_info fb_info;
0056 static u32 mc68x328fb_pseudo_palette[16];
0057 
0058 static struct fb_var_screeninfo mc68x328fb_default __initdata = {
0059     .red =      { 0, 8, 0 },
0060         .green =    { 0, 8, 0 },
0061         .blue =     { 0, 8, 0 },
0062         .activate = FB_ACTIVATE_TEST,
0063         .height =   -1,
0064         .width =    -1,
0065         .pixclock = 20000,
0066         .left_margin =  64,
0067         .right_margin = 64,
0068         .upper_margin = 32,
0069         .lower_margin = 32,
0070         .hsync_len =    64,
0071         .vsync_len =    2,
0072         .vmode =    FB_VMODE_NONINTERLACED,
0073 };
0074 
0075 static const struct fb_fix_screeninfo mc68x328fb_fix __initconst = {
0076     .id =       "68328fb",
0077     .type =     FB_TYPE_PACKED_PIXELS,
0078     .xpanstep = 1,
0079     .ypanstep = 1,
0080     .ywrapstep =    1,
0081     .accel =    FB_ACCEL_NONE,
0082 };
0083 
0084     /*
0085      *  Interface used by the world
0086      */
0087 static int mc68x328fb_check_var(struct fb_var_screeninfo *var,
0088              struct fb_info *info);
0089 static int mc68x328fb_set_par(struct fb_info *info);
0090 static int mc68x328fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
0091              u_int transp, struct fb_info *info);
0092 static int mc68x328fb_pan_display(struct fb_var_screeninfo *var,
0093                struct fb_info *info);
0094 static int mc68x328fb_mmap(struct fb_info *info, struct vm_area_struct *vma);
0095 
0096 static const struct fb_ops mc68x328fb_ops = {
0097     .fb_check_var   = mc68x328fb_check_var,
0098     .fb_set_par = mc68x328fb_set_par,
0099     .fb_setcolreg   = mc68x328fb_setcolreg,
0100     .fb_pan_display = mc68x328fb_pan_display,
0101     .fb_fillrect    = cfb_fillrect,
0102     .fb_copyarea    = cfb_copyarea,
0103     .fb_imageblit   = cfb_imageblit,
0104     .fb_mmap    = mc68x328fb_mmap,
0105 };
0106 
0107     /*
0108      *  Internal routines
0109      */
0110 
0111 static u_long get_line_length(int xres_virtual, int bpp)
0112 {
0113     u_long length;
0114 
0115     length = xres_virtual * bpp;
0116     length = (length + 31) & ~31;
0117     length >>= 3;
0118     return (length);
0119 }
0120 
0121     /*
0122      *  Setting the video mode has been split into two parts.
0123      *  First part, xxxfb_check_var, must not write anything
0124      *  to hardware, it should only verify and adjust var.
0125      *  This means it doesn't alter par but it does use hardware
0126      *  data from it to check this var. 
0127      */
0128 
0129 static int mc68x328fb_check_var(struct fb_var_screeninfo *var,
0130              struct fb_info *info)
0131 {
0132     u_long line_length;
0133 
0134     /*
0135      *  FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
0136      *  as FB_VMODE_SMOOTH_XPAN is only used internally
0137      */
0138 
0139     if (var->vmode & FB_VMODE_CONUPDATE) {
0140         var->vmode |= FB_VMODE_YWRAP;
0141         var->xoffset = info->var.xoffset;
0142         var->yoffset = info->var.yoffset;
0143     }
0144 
0145     /*
0146      *  Some very basic checks
0147      */
0148     if (!var->xres)
0149         var->xres = 1;
0150     if (!var->yres)
0151         var->yres = 1;
0152     if (var->xres > var->xres_virtual)
0153         var->xres_virtual = var->xres;
0154     if (var->yres > var->yres_virtual)
0155         var->yres_virtual = var->yres;
0156     if (var->bits_per_pixel <= 1)
0157         var->bits_per_pixel = 1;
0158     else if (var->bits_per_pixel <= 8)
0159         var->bits_per_pixel = 8;
0160     else if (var->bits_per_pixel <= 16)
0161         var->bits_per_pixel = 16;
0162     else if (var->bits_per_pixel <= 24)
0163         var->bits_per_pixel = 24;
0164     else if (var->bits_per_pixel <= 32)
0165         var->bits_per_pixel = 32;
0166     else
0167         return -EINVAL;
0168 
0169     if (var->xres_virtual < var->xoffset + var->xres)
0170         var->xres_virtual = var->xoffset + var->xres;
0171     if (var->yres_virtual < var->yoffset + var->yres)
0172         var->yres_virtual = var->yoffset + var->yres;
0173 
0174     /*
0175      *  Memory limit
0176      */
0177     line_length =
0178         get_line_length(var->xres_virtual, var->bits_per_pixel);
0179     if (line_length * var->yres_virtual > videomemorysize)
0180         return -ENOMEM;
0181 
0182     /*
0183      * Now that we checked it we alter var. The reason being is that the video
0184      * mode passed in might not work but slight changes to it might make it 
0185      * work. This way we let the user know what is acceptable.
0186      */
0187     switch (var->bits_per_pixel) {
0188     case 1:
0189         var->red.offset = 0;
0190         var->red.length = 1;
0191         var->green.offset = 0;
0192         var->green.length = 1;
0193         var->blue.offset = 0;
0194         var->blue.length = 1;
0195         var->transp.offset = 0;
0196         var->transp.length = 0;
0197         break;
0198     case 8:
0199         var->red.offset = 0;
0200         var->red.length = 8;
0201         var->green.offset = 0;
0202         var->green.length = 8;
0203         var->blue.offset = 0;
0204         var->blue.length = 8;
0205         var->transp.offset = 0;
0206         var->transp.length = 0;
0207         break;
0208     case 16:        /* RGBA 5551 */
0209         if (var->transp.length) {
0210             var->red.offset = 0;
0211             var->red.length = 5;
0212             var->green.offset = 5;
0213             var->green.length = 5;
0214             var->blue.offset = 10;
0215             var->blue.length = 5;
0216             var->transp.offset = 15;
0217             var->transp.length = 1;
0218         } else {    /* RGB 565 */
0219             var->red.offset = 0;
0220             var->red.length = 5;
0221             var->green.offset = 5;
0222             var->green.length = 6;
0223             var->blue.offset = 11;
0224             var->blue.length = 5;
0225             var->transp.offset = 0;
0226             var->transp.length = 0;
0227         }
0228         break;
0229     case 24:        /* RGB 888 */
0230         var->red.offset = 0;
0231         var->red.length = 8;
0232         var->green.offset = 8;
0233         var->green.length = 8;
0234         var->blue.offset = 16;
0235         var->blue.length = 8;
0236         var->transp.offset = 0;
0237         var->transp.length = 0;
0238         break;
0239     case 32:        /* RGBA 8888 */
0240         var->red.offset = 0;
0241         var->red.length = 8;
0242         var->green.offset = 8;
0243         var->green.length = 8;
0244         var->blue.offset = 16;
0245         var->blue.length = 8;
0246         var->transp.offset = 24;
0247         var->transp.length = 8;
0248         break;
0249     }
0250     var->red.msb_right = 0;
0251     var->green.msb_right = 0;
0252     var->blue.msb_right = 0;
0253     var->transp.msb_right = 0;
0254 
0255     return 0;
0256 }
0257 
0258 /* This routine actually sets the video mode. It's in here where we
0259  * the hardware state info->par and fix which can be affected by the 
0260  * change in par. For this driver it doesn't do much. 
0261  */
0262 static int mc68x328fb_set_par(struct fb_info *info)
0263 {
0264     info->fix.line_length = get_line_length(info->var.xres_virtual,
0265                         info->var.bits_per_pixel);
0266     return 0;
0267 }
0268 
0269     /*
0270      *  Set a single color register. The values supplied are already
0271      *  rounded down to the hardware's capabilities (according to the
0272      *  entries in the var structure). Return != 0 for invalid regno.
0273      */
0274 
0275 static int mc68x328fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
0276              u_int transp, struct fb_info *info)
0277 {
0278     if (regno >= 256)   /* no. of hw registers */
0279         return 1;
0280     /*
0281      * Program hardware... do anything you want with transp
0282      */
0283 
0284     /* grayscale works only partially under directcolor */
0285     if (info->var.grayscale) {
0286         /* grayscale = 0.30*R + 0.59*G + 0.11*B */
0287         red = green = blue =
0288             (red * 77 + green * 151 + blue * 28) >> 8;
0289     }
0290 
0291     /* Directcolor:
0292      *   var->{color}.offset contains start of bitfield
0293      *   var->{color}.length contains length of bitfield
0294      *   {hardwarespecific} contains width of RAMDAC
0295      *   cmap[X] is programmed to (X << red.offset) | (X << green.offset) | (X << blue.offset)
0296      *   RAMDAC[X] is programmed to (red, green, blue)
0297      * 
0298      * Pseudocolor:
0299      *    uses offset = 0 && length = RAMDAC register width.
0300      *    var->{color}.offset is 0
0301      *    var->{color}.length contains width of DAC
0302      *    cmap is not used
0303      *    RAMDAC[X] is programmed to (red, green, blue)
0304      * Truecolor:
0305      *    does not use DAC. Usually 3 are present.
0306      *    var->{color}.offset contains start of bitfield
0307      *    var->{color}.length contains length of bitfield
0308      *    cmap is programmed to (red << red.offset) | (green << green.offset) |
0309      *                      (blue << blue.offset) | (transp << transp.offset)
0310      *    RAMDAC does not exist
0311      */
0312 #define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
0313     switch (info->fix.visual) {
0314     case FB_VISUAL_TRUECOLOR:
0315     case FB_VISUAL_PSEUDOCOLOR:
0316         red = CNVT_TOHW(red, info->var.red.length);
0317         green = CNVT_TOHW(green, info->var.green.length);
0318         blue = CNVT_TOHW(blue, info->var.blue.length);
0319         transp = CNVT_TOHW(transp, info->var.transp.length);
0320         break;
0321     case FB_VISUAL_DIRECTCOLOR:
0322         red = CNVT_TOHW(red, 8);    /* expect 8 bit DAC */
0323         green = CNVT_TOHW(green, 8);
0324         blue = CNVT_TOHW(blue, 8);
0325         /* hey, there is bug in transp handling... */
0326         transp = CNVT_TOHW(transp, 8);
0327         break;
0328     }
0329 #undef CNVT_TOHW
0330     /* Truecolor has hardware independent palette */
0331     if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
0332         u32 v;
0333 
0334         if (regno >= 16)
0335             return 1;
0336 
0337         v = (red << info->var.red.offset) |
0338             (green << info->var.green.offset) |
0339             (blue << info->var.blue.offset) |
0340             (transp << info->var.transp.offset);
0341         switch (info->var.bits_per_pixel) {
0342         case 8:
0343             break;
0344         case 16:
0345             ((u32 *) (info->pseudo_palette))[regno] = v;
0346             break;
0347         case 24:
0348         case 32:
0349             ((u32 *) (info->pseudo_palette))[regno] = v;
0350             break;
0351         }
0352         return 0;
0353     }
0354     return 0;
0355 }
0356 
0357     /*
0358      *  Pan or Wrap the Display
0359      *
0360      *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
0361      */
0362 
0363 static int mc68x328fb_pan_display(struct fb_var_screeninfo *var,
0364                struct fb_info *info)
0365 {
0366     if (var->vmode & FB_VMODE_YWRAP) {
0367         if (var->yoffset < 0
0368             || var->yoffset >= info->var.yres_virtual
0369             || var->xoffset)
0370             return -EINVAL;
0371     } else {
0372         if (var->xoffset + info->var.xres > info->var.xres_virtual ||
0373             var->yoffset + info->var.yres > info->var.yres_virtual)
0374             return -EINVAL;
0375     }
0376     info->var.xoffset = var->xoffset;
0377     info->var.yoffset = var->yoffset;
0378     if (var->vmode & FB_VMODE_YWRAP)
0379         info->var.vmode |= FB_VMODE_YWRAP;
0380     else
0381         info->var.vmode &= ~FB_VMODE_YWRAP;
0382     return 0;
0383 }
0384 
0385     /*
0386      *  Most drivers don't need their own mmap function 
0387      */
0388 
0389 static int mc68x328fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
0390 {
0391 #ifndef MMU
0392     /* this is uClinux (no MMU) specific code */
0393 
0394     vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
0395     vma->vm_start = videomemory;
0396 
0397     return 0;
0398 #else
0399     return -EINVAL;
0400 #endif
0401 }
0402 
0403 static int __init mc68x328fb_setup(char *options)
0404 {
0405     if (!options || !*options)
0406         return 1;
0407     return 1;
0408 }
0409 
0410     /*
0411      *  Initialisation
0412      */
0413 
0414 static int __init mc68x328fb_init(void)
0415 {
0416 #ifndef MODULE
0417     char *option = NULL;
0418 
0419     if (fb_get_options("68328fb", &option))
0420         return -ENODEV;
0421     mc68x328fb_setup(option);
0422 #endif
0423     /*
0424      *  initialize the default mode from the LCD controller registers
0425      */
0426     mc68x328fb_default.xres = LXMAX;
0427     mc68x328fb_default.yres = LYMAX+1;
0428     mc68x328fb_default.xres_virtual = mc68x328fb_default.xres;
0429     mc68x328fb_default.yres_virtual = mc68x328fb_default.yres;
0430     mc68x328fb_default.bits_per_pixel = 1 + (LPICF & 0x01);
0431     videomemory = LSSA;
0432     videomemorysize = (mc68x328fb_default.xres_virtual+7) / 8 *
0433         mc68x328fb_default.yres_virtual * mc68x328fb_default.bits_per_pixel;
0434 
0435     fb_info.screen_base = (void *)videomemory;
0436     fb_info.fbops = &mc68x328fb_ops;
0437     fb_info.var = mc68x328fb_default;
0438     fb_info.fix = mc68x328fb_fix;
0439     fb_info.fix.smem_start = videomemory;
0440     fb_info.fix.smem_len = videomemorysize;
0441     fb_info.fix.line_length =
0442         get_line_length(mc68x328fb_default.xres_virtual, mc68x328fb_default.bits_per_pixel);
0443     fb_info.fix.visual = (mc68x328fb_default.bits_per_pixel) == 1 ?
0444         FB_VISUAL_MONO10 : FB_VISUAL_PSEUDOCOLOR;
0445     if (fb_info.var.bits_per_pixel == 1) {
0446         fb_info.var.red.length = fb_info.var.green.length = fb_info.var.blue.length = 1;
0447         fb_info.var.red.offset = fb_info.var.green.offset = fb_info.var.blue.offset = 0;
0448     }
0449     fb_info.pseudo_palette = &mc68x328fb_pseudo_palette;
0450     fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
0451 
0452     if (fb_alloc_cmap(&fb_info.cmap, 256, 0))
0453         return -ENOMEM;
0454 
0455     if (register_framebuffer(&fb_info) < 0) {
0456         fb_dealloc_cmap(&fb_info.cmap);
0457         return -EINVAL;
0458     }
0459 
0460     fb_info(&fb_info, "%s frame buffer device\n", fb_info.fix.id);
0461     fb_info(&fb_info, "%dx%dx%d at 0x%08lx\n",
0462         mc68x328fb_default.xres_virtual,
0463         mc68x328fb_default.yres_virtual,
0464         1 << mc68x328fb_default.bits_per_pixel, videomemory);
0465 
0466     return 0;
0467 }
0468 
0469 module_init(mc68x328fb_init);
0470 
0471 #ifdef MODULE
0472 
0473 static void __exit mc68x328fb_cleanup(void)
0474 {
0475     unregister_framebuffer(&fb_info);
0476     fb_dealloc_cmap(&fb_info.cmap);
0477 }
0478 
0479 module_exit(mc68x328fb_cleanup);
0480 
0481 MODULE_LICENSE("GPL");
0482 #endif              /* MODULE */