linux/drivers/video/fbdev/hpfb.c
<<
>>
Prefs
   1/*
   2 *      HP300 Topcat framebuffer support (derived from macfb of all things)
   3 *      Phil Blundell <philb@gnu.org> 1998
   4 *      DIO-II, colour map and Catseye support by
   5 *      Kars de Jong <jongk@linux-m68k.org>, May 2004.
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/kernel.h>
  10#include <linux/errno.h>
  11#include <linux/string.h>
  12#include <linux/mm.h>
  13#include <linux/delay.h>
  14#include <linux/init.h>
  15#include <linux/fb.h>
  16#include <linux/dio.h>
  17
  18#include <asm/io.h>
  19#include <linux/uaccess.h>
  20
  21static struct fb_info fb_info = {
  22        .fix = {
  23                .id             = "HP300 ",
  24                .type           = FB_TYPE_PACKED_PIXELS,
  25                .visual         = FB_VISUAL_PSEUDOCOLOR,
  26                .accel          = FB_ACCEL_NONE,
  27        }
  28};
  29
  30static unsigned long fb_regs;
  31static unsigned char fb_bitmask;
  32
  33#define TC_NBLANK       0x4080
  34#define TC_WEN          0x4088
  35#define TC_REN          0x408c
  36#define TC_FBEN         0x4090
  37#define TC_PRR          0x40ea
  38
  39/* These defines match the X window system */
  40#define RR_CLEAR        0x0
  41#define RR_COPY         0x3
  42#define RR_NOOP         0x5
  43#define RR_XOR          0x6
  44#define RR_INVERT       0xa
  45#define RR_COPYINVERTED 0xc
  46#define RR_SET          0xf
  47
  48/* blitter regs */
  49#define BUSY            0x4044
  50#define WMRR            0x40ef
  51#define SOURCE_X        0x40f2
  52#define SOURCE_Y        0x40f6
  53#define DEST_X          0x40fa
  54#define DEST_Y          0x40fe
  55#define WHEIGHT         0x4106
  56#define WWIDTH          0x4102
  57#define WMOVE           0x409c
  58
  59static struct fb_var_screeninfo hpfb_defined = {
  60        .red            = {
  61                .length = 8,
  62        },
  63        .green          = {
  64                .length = 8,
  65        },
  66        .blue           = {
  67                .length = 8,
  68        },
  69        .activate       = FB_ACTIVATE_NOW,
  70        .height         = -1,
  71        .width          = -1,
  72        .vmode          = FB_VMODE_NONINTERLACED,
  73};
  74
  75static int hpfb_setcolreg(unsigned regno, unsigned red, unsigned green,
  76                          unsigned blue, unsigned transp,
  77                          struct fb_info *info)
  78{
  79        /* use MSBs */
  80        unsigned char _red  =red>>8;
  81        unsigned char _green=green>>8;
  82        unsigned char _blue =blue>>8;
  83        unsigned char _regno=regno;
  84
  85        /*
  86         *  Set a single color register. The values supplied are
  87         *  already rounded down to the hardware's capabilities
  88         *  (according to the entries in the `var' structure). Return
  89         *  != 0 for invalid regno.
  90         */
  91
  92        if (regno >= info->cmap.len)
  93                return 1;
  94        
  95        while (in_be16(fb_regs + 0x6002) & 0x4) udelay(1);
  96
  97        out_be16(fb_regs + 0x60ba, 0xff);
  98
  99        out_be16(fb_regs + 0x60b2, _red);
 100        out_be16(fb_regs + 0x60b4, _green);
 101        out_be16(fb_regs + 0x60b6, _blue);
 102        out_be16(fb_regs + 0x60b8, ~_regno);
 103        out_be16(fb_regs + 0x60f0, 0xff);
 104
 105        udelay(100);
 106
 107        while (in_be16(fb_regs + 0x6002) & 0x4) udelay(1);
 108        out_be16(fb_regs + 0x60b2, 0);
 109        out_be16(fb_regs + 0x60b4, 0);
 110        out_be16(fb_regs + 0x60b6, 0);
 111        out_be16(fb_regs + 0x60b8, 0);
 112
 113        return 0;
 114}
 115
 116/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
 117
 118static int hpfb_blank(int blank, struct fb_info *info)
 119{
 120        out_8(fb_regs + TC_NBLANK, (blank ? 0x00 : fb_bitmask));
 121
 122        return 0;
 123}
 124
 125static void topcat_blit(int x0, int y0, int x1, int y1, int w, int h, int rr)
 126{
 127        if (rr >= 0) {
 128                while (in_8(fb_regs + BUSY) & fb_bitmask)
 129                        ;
 130        }
 131        out_8(fb_regs + TC_FBEN, fb_bitmask);
 132        if (rr >= 0) {
 133                out_8(fb_regs + TC_WEN, fb_bitmask);
 134                out_8(fb_regs + WMRR, rr);
 135        }
 136        out_be16(fb_regs + SOURCE_X, x0);
 137        out_be16(fb_regs + SOURCE_Y, y0);
 138        out_be16(fb_regs + DEST_X, x1);
 139        out_be16(fb_regs + DEST_Y, y1);
 140        out_be16(fb_regs + WWIDTH, w);
 141        out_be16(fb_regs + WHEIGHT, h);
 142        out_8(fb_regs + WMOVE, fb_bitmask);
 143}
 144
 145static void hpfb_copyarea(struct fb_info *info, const struct fb_copyarea *area) 
 146{
 147        topcat_blit(area->sx, area->sy, area->dx, area->dy, area->width, area->height, RR_COPY);
 148}
 149
 150static void hpfb_fillrect(struct fb_info *p, const struct fb_fillrect *region)
 151{
 152        u8 clr;
 153
 154        clr = region->color & 0xff;
 155
 156        while (in_8(fb_regs + BUSY) & fb_bitmask)
 157                ;
 158
 159        /* Foreground */
 160        out_8(fb_regs + TC_WEN, fb_bitmask & clr);
 161        out_8(fb_regs + WMRR, (region->rop == ROP_COPY ? RR_SET : RR_INVERT));
 162
 163        /* Background */
 164        out_8(fb_regs + TC_WEN, fb_bitmask & ~clr);
 165        out_8(fb_regs + WMRR, (region->rop == ROP_COPY ? RR_CLEAR : RR_NOOP));
 166
 167        topcat_blit(region->dx, region->dy, region->dx, region->dy, region->width, region->height, -1);
 168}
 169
 170static int hpfb_sync(struct fb_info *info)
 171{
 172        /*
 173         * Since we also access the framebuffer directly, we have to wait
 174         * until the block mover is finished
 175         */
 176        while (in_8(fb_regs + BUSY) & fb_bitmask)
 177                ;
 178
 179        out_8(fb_regs + TC_WEN, fb_bitmask);
 180        out_8(fb_regs + TC_PRR, RR_COPY);
 181        out_8(fb_regs + TC_FBEN, fb_bitmask);
 182
 183        return 0;
 184}
 185
 186static struct fb_ops hpfb_ops = {
 187        .owner          = THIS_MODULE,
 188        .fb_setcolreg   = hpfb_setcolreg,
 189        .fb_blank       = hpfb_blank,
 190        .fb_fillrect    = hpfb_fillrect,
 191        .fb_copyarea    = hpfb_copyarea,
 192        .fb_imageblit   = cfb_imageblit,
 193        .fb_sync        = hpfb_sync,
 194};
 195
 196/* Common to all HP framebuffers */
 197#define HPFB_FBWMSB     0x05    /* Frame buffer width           */
 198#define HPFB_FBWLSB     0x07
 199#define HPFB_FBHMSB     0x09    /* Frame buffer height          */
 200#define HPFB_FBHLSB     0x0b
 201#define HPFB_DWMSB      0x0d    /* Display width                */
 202#define HPFB_DWLSB      0x0f
 203#define HPFB_DHMSB      0x11    /* Display height               */
 204#define HPFB_DHLSB      0x13
 205#define HPFB_NUMPLANES  0x5b    /* Number of colour planes      */
 206#define HPFB_FBOMSB     0x5d    /* Frame buffer offset          */
 207#define HPFB_FBOLSB     0x5f
 208
 209static int hpfb_init_one(unsigned long phys_base, unsigned long virt_base)
 210{
 211        unsigned long fboff, fb_width, fb_height, fb_start;
 212        int ret;
 213
 214        fb_regs = virt_base;
 215        fboff = (in_8(fb_regs + HPFB_FBOMSB) << 8) | in_8(fb_regs + HPFB_FBOLSB);
 216
 217        fb_info.fix.smem_start = (in_8(fb_regs + fboff) << 16);
 218
 219        if (phys_base >= DIOII_BASE) {
 220                fb_info.fix.smem_start += phys_base;
 221        }
 222
 223        if (DIO_SECID(fb_regs) != DIO_ID2_TOPCAT) {
 224                /* This is the magic incantation the HP X server uses to make Catseye boards work. */
 225                while (in_be16(fb_regs+0x4800) & 1)
 226                        ;
 227                out_be16(fb_regs+0x4800, 0);    /* Catseye status */
 228                out_be16(fb_regs+0x4510, 0);    /* VB */
 229                out_be16(fb_regs+0x4512, 0);    /* TCNTRL */
 230                out_be16(fb_regs+0x4514, 0);    /* ACNTRL */
 231                out_be16(fb_regs+0x4516, 0);    /* PNCNTRL */
 232                out_be16(fb_regs+0x4206, 0x90); /* RUG Command/Status */
 233                out_be16(fb_regs+0x60a2, 0);    /* Overlay Mask */
 234                out_be16(fb_regs+0x60bc, 0);    /* Ram Select */
 235        }
 236
 237        /*
 238         *      Fill in the available video resolution
 239         */
 240        fb_width = (in_8(fb_regs + HPFB_FBWMSB) << 8) | in_8(fb_regs + HPFB_FBWLSB);
 241        fb_info.fix.line_length = fb_width;
 242        fb_height = (in_8(fb_regs + HPFB_FBHMSB) << 8) | in_8(fb_regs + HPFB_FBHLSB);
 243        fb_info.fix.smem_len = fb_width * fb_height;
 244        fb_start = (unsigned long)ioremap_wt(fb_info.fix.smem_start,
 245                                             fb_info.fix.smem_len);
 246        hpfb_defined.xres = (in_8(fb_regs + HPFB_DWMSB) << 8) | in_8(fb_regs + HPFB_DWLSB);
 247        hpfb_defined.yres = (in_8(fb_regs + HPFB_DHMSB) << 8) | in_8(fb_regs + HPFB_DHLSB);
 248        hpfb_defined.xres_virtual = hpfb_defined.xres;
 249        hpfb_defined.yres_virtual = hpfb_defined.yres;
 250        hpfb_defined.bits_per_pixel = in_8(fb_regs + HPFB_NUMPLANES);
 251
 252        printk(KERN_INFO "hpfb: framebuffer at 0x%lx, mapped to 0x%lx, size %dk\n",
 253               fb_info.fix.smem_start, fb_start, fb_info.fix.smem_len/1024);
 254        printk(KERN_INFO "hpfb: mode is %dx%dx%d, linelength=%d\n",
 255               hpfb_defined.xres, hpfb_defined.yres, hpfb_defined.bits_per_pixel, fb_info.fix.line_length);
 256
 257        /*
 258         *      Give the hardware a bit of a prod and work out how many bits per
 259         *      pixel are supported.
 260         */
 261        out_8(fb_regs + TC_WEN, 0xff);
 262        out_8(fb_regs + TC_PRR, RR_COPY);
 263        out_8(fb_regs + TC_FBEN, 0xff);
 264        out_8(fb_start, 0xff);
 265        fb_bitmask = in_8(fb_start);
 266        out_8(fb_start, 0);
 267
 268        /*
 269         *      Enable reading/writing of all the planes.
 270         */
 271        out_8(fb_regs + TC_WEN, fb_bitmask);
 272        out_8(fb_regs + TC_PRR, RR_COPY);
 273        out_8(fb_regs + TC_REN, fb_bitmask);
 274        out_8(fb_regs + TC_FBEN, fb_bitmask);
 275
 276        /*
 277         *      Clear the screen.
 278         */
 279        topcat_blit(0, 0, 0, 0, fb_width, fb_height, RR_CLEAR);
 280
 281        /*
 282         *      Let there be consoles..
 283         */
 284        if (DIO_SECID(fb_regs) == DIO_ID2_TOPCAT)
 285                strcat(fb_info.fix.id, "Topcat");
 286        else
 287                strcat(fb_info.fix.id, "Catseye");
 288        fb_info.fbops = &hpfb_ops;
 289        fb_info.flags = FBINFO_DEFAULT;
 290        fb_info.var   = hpfb_defined;
 291        fb_info.screen_base = (char *)fb_start;
 292
 293        ret = fb_alloc_cmap(&fb_info.cmap, 1 << hpfb_defined.bits_per_pixel, 0);
 294        if (ret < 0)
 295                goto unmap_screen_base;
 296
 297        ret = register_framebuffer(&fb_info);
 298        if (ret < 0)
 299                goto dealloc_cmap;
 300
 301        fb_info(&fb_info, "%s frame buffer device\n", fb_info.fix.id);
 302
 303        return 0;
 304
 305dealloc_cmap:
 306        fb_dealloc_cmap(&fb_info.cmap);
 307
 308unmap_screen_base:
 309        if (fb_info.screen_base) {
 310                iounmap(fb_info.screen_base);
 311                fb_info.screen_base = NULL;
 312        }
 313
 314        return ret;
 315}
 316
 317/* 
 318 * Check that the secondary ID indicates that we have some hope of working with this
 319 * framebuffer.  The catseye boards are pretty much like topcats and we can muddle through.
 320 */
 321
 322#define topcat_sid_ok(x)  (((x) == DIO_ID2_LRCATSEYE) || ((x) == DIO_ID2_HRCCATSEYE)    \
 323                           || ((x) == DIO_ID2_HRMCATSEYE) || ((x) == DIO_ID2_TOPCAT))
 324
 325/* 
 326 * Initialise the framebuffer
 327 */
 328static int hpfb_dio_probe(struct dio_dev *d, const struct dio_device_id *ent)
 329{
 330        unsigned long paddr, vaddr;
 331
 332        paddr = d->resource.start;
 333        if (!request_mem_region(d->resource.start, resource_size(&d->resource), d->name))
 334                return -EBUSY;
 335
 336        if (d->scode >= DIOII_SCBASE) {
 337                vaddr = (unsigned long)ioremap(paddr, resource_size(&d->resource));
 338        } else {
 339                vaddr = paddr + DIO_VIRADDRBASE;
 340        }
 341        printk(KERN_INFO "Topcat found at DIO select code %d "
 342               "(secondary id %02x)\n", d->scode, (d->id >> 8) & 0xff);
 343        if (hpfb_init_one(paddr, vaddr)) {
 344                if (d->scode >= DIOII_SCBASE)
 345                        iounmap((void *)vaddr);
 346                return -ENOMEM;
 347        }
 348        return 0;
 349}
 350
 351static void hpfb_remove_one(struct dio_dev *d)
 352{
 353        unregister_framebuffer(&fb_info);
 354        if (d->scode >= DIOII_SCBASE)
 355                iounmap((void *)fb_regs);
 356        release_mem_region(d->resource.start, resource_size(&d->resource));
 357        fb_dealloc_cmap(&fb_info.cmap);
 358        if (fb_info.screen_base)
 359                iounmap(fb_info.screen_base);
 360}
 361
 362static struct dio_device_id hpfb_dio_tbl[] = {
 363    { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_LRCATSEYE) },
 364    { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_HRCCATSEYE) },
 365    { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_HRMCATSEYE) },
 366    { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_TOPCAT) },
 367    { 0 }
 368};
 369
 370static struct dio_driver hpfb_driver = {
 371    .name      = "hpfb",
 372    .id_table  = hpfb_dio_tbl,
 373    .probe     = hpfb_dio_probe,
 374    .remove    = hpfb_remove_one,
 375};
 376
 377int __init hpfb_init(void)
 378{
 379        unsigned int sid;
 380        unsigned char i;
 381        int err;
 382
 383        /* Topcats can be on the internal IO bus or real DIO devices.
 384         * The internal variant sits at 0x560000; it has primary
 385         * and secondary ID registers just like the DIO version.
 386         * So we merge the two detection routines.
 387         *
 388         * Perhaps this #define should be in a global header file:
 389         * I believe it's common to all internal fbs, not just topcat.
 390         */
 391#define INTFBVADDR 0xf0560000
 392#define INTFBPADDR 0x560000
 393
 394        if (!MACH_IS_HP300)
 395                return -ENODEV;
 396
 397        if (fb_get_options("hpfb", NULL))
 398                return -ENODEV;
 399
 400        err = dio_register_driver(&hpfb_driver);
 401        if (err)
 402                return err;
 403
 404        err = probe_kernel_read(&i, (unsigned char *)INTFBVADDR + DIO_IDOFF, 1);
 405
 406        if (!err && (i == DIO_ID_FBUFFER) && topcat_sid_ok(sid = DIO_SECID(INTFBVADDR))) {
 407                if (!request_mem_region(INTFBPADDR, DIO_DEVSIZE, "Internal Topcat"))
 408                        return -EBUSY;
 409                printk(KERN_INFO "Internal Topcat found (secondary id %02x)\n", sid);
 410                if (hpfb_init_one(INTFBPADDR, INTFBVADDR)) {
 411                        return -ENOMEM;
 412                }
 413        }
 414        return 0;
 415}
 416
 417void __exit hpfb_cleanup_module(void)
 418{
 419        dio_unregister_driver(&hpfb_driver);
 420}
 421
 422module_init(hpfb_init);
 423module_exit(hpfb_cleanup_module);
 424
 425MODULE_LICENSE("GPL");
 426