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