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