linux/drivers/video/68328fb.c
<<
>>
Prefs
   1/*
   2 *  linux/drivers/video/68328fb.c -- Low level implementation of the
   3 *                                   mc68x328 LCD frame buffer device
   4 *
   5 *      Copyright (C) 2003 Georges Menie
   6 *
   7 *  This driver assumes an already configured controller (e.g. from config.c)
   8 *  Keep the code clean of board specific initialization.
   9 *
  10 *  This code has not been tested with colors, colormap management functions
  11 *  are minimal (no colormap data written to the 68328 registers...)
  12 *
  13 *  initial version of this driver:
  14 *    Copyright (C) 1998,1999 Kenneth Albanowski <kjahds@kjahds.com>,
  15 *                            The Silver Hammer Group, Ltd.
  16 *
  17 *  this version is based on :
  18 *
  19 *  linux/drivers/video/vfb.c -- Virtual frame buffer device
  20 *
  21 *      Copyright (C) 2002 James Simmons
  22 *
  23 *      Copyright (C) 1997 Geert Uytterhoeven
  24 *
  25 *  This file is subject to the terms and conditions of the GNU General Public
  26 *  License. See the file COPYING in the main directory of this archive for
  27 *  more details.
  28 */
  29
  30#include <linux/module.h>
  31#include <linux/kernel.h>
  32#include <linux/errno.h>
  33#include <linux/string.h>
  34#include <linux/mm.h>
  35#include <linux/slab.h>
  36#include <linux/vmalloc.h>
  37#include <linux/delay.h>
  38#include <linux/interrupt.h>
  39#include <asm/uaccess.h>
  40#include <linux/fb.h>
  41#include <linux/init.h>
  42
  43#if defined(CONFIG_M68VZ328)
  44#include <asm/MC68VZ328.h>
  45#elif defined(CONFIG_M68EZ328)
  46#include <asm/MC68EZ328.h>
  47#elif defined(CONFIG_M68328)
  48#include <asm/MC68328.h>
  49#else
  50#error wrong architecture for the MC68x328 frame buffer device
  51#endif
  52
  53#if defined(CONFIG_FB_68328_INVERT)
  54#define MC68X328FB_MONO_VISUAL FB_VISUAL_MONO01
  55#else
  56#define MC68X328FB_MONO_VISUAL FB_VISUAL_MONO10
  57#endif
  58
  59static u_long videomemory;
  60static u_long videomemorysize;
  61
  62static struct fb_info fb_info;
  63static u32 mc68x328fb_pseudo_palette[16];
  64
  65static struct fb_var_screeninfo mc68x328fb_default __initdata = {
  66        .red =          { 0, 8, 0 },
  67        .green =        { 0, 8, 0 },
  68        .blue =         { 0, 8, 0 },
  69        .activate =     FB_ACTIVATE_TEST,
  70        .height =       -1,
  71        .width =        -1,
  72        .pixclock =     20000,
  73        .left_margin =  64,
  74        .right_margin = 64,
  75        .upper_margin = 32,
  76        .lower_margin = 32,
  77        .hsync_len =    64,
  78        .vsync_len =    2,
  79        .vmode =        FB_VMODE_NONINTERLACED,
  80};
  81
  82static struct fb_fix_screeninfo mc68x328fb_fix __initdata = {
  83        .id =           "68328fb",
  84        .type =         FB_TYPE_PACKED_PIXELS,
  85        .xpanstep =     1,
  86        .ypanstep =     1,
  87        .ywrapstep =    1,
  88        .accel =        FB_ACCEL_NONE,
  89};
  90
  91    /*
  92     *  Interface used by the world
  93     */
  94int mc68x328fb_init(void);
  95int mc68x328fb_setup(char *);
  96
  97static int mc68x328fb_check_var(struct fb_var_screeninfo *var,
  98                         struct fb_info *info);
  99static int mc68x328fb_set_par(struct fb_info *info);
 100static int mc68x328fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 101                         u_int transp, struct fb_info *info);
 102static int mc68x328fb_pan_display(struct fb_var_screeninfo *var,
 103                           struct fb_info *info);
 104static int mc68x328fb_mmap(struct fb_info *info, struct vm_area_struct *vma);
 105
 106static struct fb_ops mc68x328fb_ops = {
 107        .fb_check_var   = mc68x328fb_check_var,
 108        .fb_set_par     = mc68x328fb_set_par,
 109        .fb_setcolreg   = mc68x328fb_setcolreg,
 110        .fb_pan_display = mc68x328fb_pan_display,
 111        .fb_fillrect    = cfb_fillrect,
 112        .fb_copyarea    = cfb_copyarea,
 113        .fb_imageblit   = cfb_imageblit,
 114        .fb_mmap        = mc68x328fb_mmap,
 115};
 116
 117    /*
 118     *  Internal routines
 119     */
 120
 121static u_long get_line_length(int xres_virtual, int bpp)
 122{
 123        u_long length;
 124
 125        length = xres_virtual * bpp;
 126        length = (length + 31) & ~31;
 127        length >>= 3;
 128        return (length);
 129}
 130
 131    /*
 132     *  Setting the video mode has been split into two parts.
 133     *  First part, xxxfb_check_var, must not write anything
 134     *  to hardware, it should only verify and adjust var.
 135     *  This means it doesn't alter par but it does use hardware
 136     *  data from it to check this var. 
 137     */
 138
 139static int mc68x328fb_check_var(struct fb_var_screeninfo *var,
 140                         struct fb_info *info)
 141{
 142        u_long line_length;
 143
 144        /*
 145         *  FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
 146         *  as FB_VMODE_SMOOTH_XPAN is only used internally
 147         */
 148
 149        if (var->vmode & FB_VMODE_CONUPDATE) {
 150                var->vmode |= FB_VMODE_YWRAP;
 151                var->xoffset = info->var.xoffset;
 152                var->yoffset = info->var.yoffset;
 153        }
 154
 155        /*
 156         *  Some very basic checks
 157         */
 158        if (!var->xres)
 159                var->xres = 1;
 160        if (!var->yres)
 161                var->yres = 1;
 162        if (var->xres > var->xres_virtual)
 163                var->xres_virtual = var->xres;
 164        if (var->yres > var->yres_virtual)
 165                var->yres_virtual = var->yres;
 166        if (var->bits_per_pixel <= 1)
 167                var->bits_per_pixel = 1;
 168        else if (var->bits_per_pixel <= 8)
 169                var->bits_per_pixel = 8;
 170        else if (var->bits_per_pixel <= 16)
 171                var->bits_per_pixel = 16;
 172        else if (var->bits_per_pixel <= 24)
 173                var->bits_per_pixel = 24;
 174        else if (var->bits_per_pixel <= 32)
 175                var->bits_per_pixel = 32;
 176        else
 177                return -EINVAL;
 178
 179        if (var->xres_virtual < var->xoffset + var->xres)
 180                var->xres_virtual = var->xoffset + var->xres;
 181        if (var->yres_virtual < var->yoffset + var->yres)
 182                var->yres_virtual = var->yoffset + var->yres;
 183
 184        /*
 185         *  Memory limit
 186         */
 187        line_length =
 188            get_line_length(var->xres_virtual, var->bits_per_pixel);
 189        if (line_length * var->yres_virtual > videomemorysize)
 190                return -ENOMEM;
 191
 192        /*
 193         * Now that we checked it we alter var. The reason being is that the video
 194         * mode passed in might not work but slight changes to it might make it 
 195         * work. This way we let the user know what is acceptable.
 196         */
 197        switch (var->bits_per_pixel) {
 198        case 1:
 199                var->red.offset = 0;
 200                var->red.length = 1;
 201                var->green.offset = 0;
 202                var->green.length = 1;
 203                var->blue.offset = 0;
 204                var->blue.length = 1;
 205                var->transp.offset = 0;
 206                var->transp.length = 0;
 207                break;
 208        case 8:
 209                var->red.offset = 0;
 210                var->red.length = 8;
 211                var->green.offset = 0;
 212                var->green.length = 8;
 213                var->blue.offset = 0;
 214                var->blue.length = 8;
 215                var->transp.offset = 0;
 216                var->transp.length = 0;
 217                break;
 218        case 16:                /* RGBA 5551 */
 219                if (var->transp.length) {
 220                        var->red.offset = 0;
 221                        var->red.length = 5;
 222                        var->green.offset = 5;
 223                        var->green.length = 5;
 224                        var->blue.offset = 10;
 225                        var->blue.length = 5;
 226                        var->transp.offset = 15;
 227                        var->transp.length = 1;
 228                } else {        /* RGB 565 */
 229                        var->red.offset = 0;
 230                        var->red.length = 5;
 231                        var->green.offset = 5;
 232                        var->green.length = 6;
 233                        var->blue.offset = 11;
 234                        var->blue.length = 5;
 235                        var->transp.offset = 0;
 236                        var->transp.length = 0;
 237                }
 238                break;
 239        case 24:                /* RGB 888 */
 240                var->red.offset = 0;
 241                var->red.length = 8;
 242                var->green.offset = 8;
 243                var->green.length = 8;
 244                var->blue.offset = 16;
 245                var->blue.length = 8;
 246                var->transp.offset = 0;
 247                var->transp.length = 0;
 248                break;
 249        case 32:                /* RGBA 8888 */
 250                var->red.offset = 0;
 251                var->red.length = 8;
 252                var->green.offset = 8;
 253                var->green.length = 8;
 254                var->blue.offset = 16;
 255                var->blue.length = 8;
 256                var->transp.offset = 24;
 257                var->transp.length = 8;
 258                break;
 259        }
 260        var->red.msb_right = 0;
 261        var->green.msb_right = 0;
 262        var->blue.msb_right = 0;
 263        var->transp.msb_right = 0;
 264
 265        return 0;
 266}
 267
 268/* This routine actually sets the video mode. It's in here where we
 269 * the hardware state info->par and fix which can be affected by the 
 270 * change in par. For this driver it doesn't do much. 
 271 */
 272static int mc68x328fb_set_par(struct fb_info *info)
 273{
 274        info->fix.line_length = get_line_length(info->var.xres_virtual,
 275                                                info->var.bits_per_pixel);
 276        return 0;
 277}
 278
 279    /*
 280     *  Set a single color register. The values supplied are already
 281     *  rounded down to the hardware's capabilities (according to the
 282     *  entries in the var structure). Return != 0 for invalid regno.
 283     */
 284
 285static int mc68x328fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 286                         u_int transp, struct fb_info *info)
 287{
 288        if (regno >= 256)       /* no. of hw registers */
 289                return 1;
 290        /*
 291         * Program hardware... do anything you want with transp
 292         */
 293
 294        /* grayscale works only partially under directcolor */
 295        if (info->var.grayscale) {
 296                /* grayscale = 0.30*R + 0.59*G + 0.11*B */
 297                red = green = blue =
 298                    (red * 77 + green * 151 + blue * 28) >> 8;
 299        }
 300
 301        /* Directcolor:
 302         *   var->{color}.offset contains start of bitfield
 303         *   var->{color}.length contains length of bitfield
 304         *   {hardwarespecific} contains width of RAMDAC
 305         *   cmap[X] is programmed to (X << red.offset) | (X << green.offset) | (X << blue.offset)
 306         *   RAMDAC[X] is programmed to (red, green, blue)
 307         * 
 308         * Pseudocolor:
 309         *    uses offset = 0 && length = RAMDAC register width.
 310         *    var->{color}.offset is 0
 311         *    var->{color}.length contains widht of DAC
 312         *    cmap is not used
 313         *    RAMDAC[X] is programmed to (red, green, blue)
 314         * Truecolor:
 315         *    does not use DAC. Usually 3 are present.
 316         *    var->{color}.offset contains start of bitfield
 317         *    var->{color}.length contains length of bitfield
 318         *    cmap is programmed to (red << red.offset) | (green << green.offset) |
 319         *                      (blue << blue.offset) | (transp << transp.offset)
 320         *    RAMDAC does not exist
 321         */
 322#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
 323        switch (info->fix.visual) {
 324        case FB_VISUAL_TRUECOLOR:
 325        case FB_VISUAL_PSEUDOCOLOR:
 326                red = CNVT_TOHW(red, info->var.red.length);
 327                green = CNVT_TOHW(green, info->var.green.length);
 328                blue = CNVT_TOHW(blue, info->var.blue.length);
 329                transp = CNVT_TOHW(transp, info->var.transp.length);
 330                break;
 331        case FB_VISUAL_DIRECTCOLOR:
 332                red = CNVT_TOHW(red, 8);        /* expect 8 bit DAC */
 333                green = CNVT_TOHW(green, 8);
 334                blue = CNVT_TOHW(blue, 8);
 335                /* hey, there is bug in transp handling... */
 336                transp = CNVT_TOHW(transp, 8);
 337                break;
 338        }
 339#undef CNVT_TOHW
 340        /* Truecolor has hardware independent palette */
 341        if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
 342                u32 v;
 343
 344                if (regno >= 16)
 345                        return 1;
 346
 347                v = (red << info->var.red.offset) |
 348                    (green << info->var.green.offset) |
 349                    (blue << info->var.blue.offset) |
 350                    (transp << info->var.transp.offset);
 351                switch (info->var.bits_per_pixel) {
 352                case 8:
 353                        break;
 354                case 16:
 355                        ((u32 *) (info->pseudo_palette))[regno] = v;
 356                        break;
 357                case 24:
 358                case 32:
 359                        ((u32 *) (info->pseudo_palette))[regno] = v;
 360                        break;
 361                }
 362                return 0;
 363        }
 364        return 0;
 365}
 366
 367    /*
 368     *  Pan or Wrap the Display
 369     *
 370     *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
 371     */
 372
 373static int mc68x328fb_pan_display(struct fb_var_screeninfo *var,
 374                           struct fb_info *info)
 375{
 376        if (var->vmode & FB_VMODE_YWRAP) {
 377                if (var->yoffset < 0
 378                    || var->yoffset >= info->var.yres_virtual
 379                    || var->xoffset)
 380                        return -EINVAL;
 381        } else {
 382                if (var->xoffset + var->xres > info->var.xres_virtual ||
 383                    var->yoffset + var->yres > info->var.yres_virtual)
 384                        return -EINVAL;
 385        }
 386        info->var.xoffset = var->xoffset;
 387        info->var.yoffset = var->yoffset;
 388        if (var->vmode & FB_VMODE_YWRAP)
 389                info->var.vmode |= FB_VMODE_YWRAP;
 390        else
 391                info->var.vmode &= ~FB_VMODE_YWRAP;
 392        return 0;
 393}
 394
 395    /*
 396     *  Most drivers don't need their own mmap function 
 397     */
 398
 399static int mc68x328fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 400{
 401#ifndef MMU
 402        /* this is uClinux (no MMU) specific code */
 403
 404        vma->vm_flags |= VM_RESERVED;
 405        vma->vm_start = videomemory;
 406
 407        return 0;
 408#else
 409        return -EINVAL;
 410#endif
 411}
 412
 413int __init mc68x328fb_setup(char *options)
 414{
 415#if 0
 416        char *this_opt;
 417#endif
 418
 419        if (!options || !*options)
 420                return 1;
 421#if 0
 422        while ((this_opt = strsep(&options, ",")) != NULL) {
 423                if (!*this_opt)
 424                        continue;
 425                if (!strncmp(this_opt, "disable", 7))
 426                        mc68x328fb_enable = 0;
 427        }
 428#endif
 429        return 1;
 430}
 431
 432    /*
 433     *  Initialisation
 434     */
 435
 436int __init mc68x328fb_init(void)
 437{
 438#ifndef MODULE
 439        char *option = NULL;
 440
 441        if (fb_get_options("68328fb", &option))
 442                return -ENODEV;
 443        mc68x328fb_setup(option);
 444#endif
 445        /*
 446         *  initialize the default mode from the LCD controller registers
 447         */
 448        mc68x328fb_default.xres = LXMAX;
 449        mc68x328fb_default.yres = LYMAX+1;
 450        mc68x328fb_default.xres_virtual = mc68x328fb_default.xres;
 451        mc68x328fb_default.yres_virtual = mc68x328fb_default.yres;
 452        mc68x328fb_default.bits_per_pixel = 1 + (LPICF & 0x01);
 453        videomemory = LSSA;
 454        videomemorysize = (mc68x328fb_default.xres_virtual+7) / 8 *
 455                mc68x328fb_default.yres_virtual * mc68x328fb_default.bits_per_pixel;
 456
 457        fb_info.screen_base = (void *)videomemory;
 458        fb_info.fbops = &mc68x328fb_ops;
 459        fb_info.var = mc68x328fb_default;
 460        fb_info.fix = mc68x328fb_fix;
 461        fb_info.fix.smem_start = videomemory;
 462        fb_info.fix.smem_len = videomemorysize;
 463        fb_info.fix.line_length =
 464                get_line_length(mc68x328fb_default.xres_virtual, mc68x328fb_default.bits_per_pixel);
 465        fb_info.fix.visual = (mc68x328fb_default.bits_per_pixel) == 1 ?
 466                MC68X328FB_MONO_VISUAL : FB_VISUAL_PSEUDOCOLOR;
 467        if (fb_info.var.bits_per_pixel == 1) {
 468                fb_info.var.red.length = fb_info.var.green.length = fb_info.var.blue.length = 1;
 469                fb_info.var.red.offset = fb_info.var.green.offset = fb_info.var.blue.offset = 0;
 470        }
 471        fb_info.pseudo_palette = &mc68x328fb_pseudo_palette;
 472        fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
 473
 474        if (fb_alloc_cmap(&fb_info.cmap, 256, 0))
 475                return -ENOMEM;
 476
 477        if (register_framebuffer(&fb_info) < 0) {
 478                fb_dealloc_cmap(&fb_info.cmap);
 479                return -EINVAL;
 480        }
 481
 482        printk(KERN_INFO
 483                "fb%d: %s frame buffer device\n", fb_info.node, fb_info.fix.id);
 484        printk(KERN_INFO
 485                "fb%d: %dx%dx%d at 0x%08lx\n", fb_info.node,
 486                mc68x328fb_default.xres_virtual, mc68x328fb_default.yres_virtual,
 487                1 << mc68x328fb_default.bits_per_pixel, videomemory);
 488
 489        return 0;
 490}
 491
 492module_init(mc68x328fb_init);
 493
 494#ifdef MODULE
 495
 496static void __exit mc68x328fb_cleanup(void)
 497{
 498        unregister_framebuffer(&fb_info);
 499        fb_dealloc_cmap(&fb_info.cmap);
 500}
 501
 502module_exit(mc68x328fb_cleanup);
 503
 504MODULE_LICENSE("GPL");
 505#endif                          /* MODULE */
 506