linux/drivers/video/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 <asm/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 __devinit hpfb_init_one(unsigned long phys_base,
 210                                   unsigned long virt_base)
 211{
 212        unsigned long fboff, fb_width, fb_height, fb_start;
 213        int ret;
 214
 215        fb_regs = virt_base;
 216        fboff = (in_8(fb_regs + HPFB_FBOMSB) << 8) | in_8(fb_regs + HPFB_FBOLSB);
 217
 218        fb_info.fix.smem_start = (in_8(fb_regs + fboff) << 16);
 219
 220        if (phys_base >= DIOII_BASE) {
 221                fb_info.fix.smem_start += phys_base;
 222        }
 223
 224        if (DIO_SECID(fb_regs) != DIO_ID2_TOPCAT) {
 225                /* This is the magic incantation the HP X server uses to make Catseye boards work. */
 226                while (in_be16(fb_regs+0x4800) & 1)
 227                        ;
 228                out_be16(fb_regs+0x4800, 0);    /* Catseye status */
 229                out_be16(fb_regs+0x4510, 0);    /* VB */
 230                out_be16(fb_regs+0x4512, 0);    /* TCNTRL */
 231                out_be16(fb_regs+0x4514, 0);    /* ACNTRL */
 232                out_be16(fb_regs+0x4516, 0);    /* PNCNTRL */
 233                out_be16(fb_regs+0x4206, 0x90); /* RUG Command/Status */
 234                out_be16(fb_regs+0x60a2, 0);    /* Overlay Mask */
 235                out_be16(fb_regs+0x60bc, 0);    /* Ram Select */
 236        }
 237
 238        /*
 239         *      Fill in the available video resolution
 240         */
 241        fb_width = (in_8(fb_regs + HPFB_FBWMSB) << 8) | in_8(fb_regs + HPFB_FBWLSB);
 242        fb_info.fix.line_length = fb_width;
 243        fb_height = (in_8(fb_regs + HPFB_FBHMSB) << 8) | in_8(fb_regs + HPFB_FBHLSB);
 244        fb_info.fix.smem_len = fb_width * fb_height;
 245        fb_start = (unsigned long)ioremap_writethrough(fb_info.fix.smem_start,
 246                                                       fb_info.fix.smem_len);
 247        hpfb_defined.xres = (in_8(fb_regs + HPFB_DWMSB) << 8) | in_8(fb_regs + HPFB_DWLSB);
 248        hpfb_defined.yres = (in_8(fb_regs + HPFB_DHMSB) << 8) | in_8(fb_regs + HPFB_DHLSB);
 249        hpfb_defined.xres_virtual = hpfb_defined.xres;
 250        hpfb_defined.yres_virtual = hpfb_defined.yres;
 251        hpfb_defined.bits_per_pixel = in_8(fb_regs + HPFB_NUMPLANES);
 252
 253        printk(KERN_INFO "hpfb: framebuffer at 0x%lx, mapped to 0x%lx, size %dk\n",
 254               fb_info.fix.smem_start, fb_start, fb_info.fix.smem_len/1024);
 255        printk(KERN_INFO "hpfb: mode is %dx%dx%d, linelength=%d\n",
 256               hpfb_defined.xres, hpfb_defined.yres, hpfb_defined.bits_per_pixel, fb_info.fix.line_length);
 257
 258        /*
 259         *      Give the hardware a bit of a prod and work out how many bits per
 260         *      pixel are supported.
 261         */
 262        out_8(fb_regs + TC_WEN, 0xff);
 263        out_8(fb_regs + TC_PRR, RR_COPY);
 264        out_8(fb_regs + TC_FBEN, 0xff);
 265        out_8(fb_start, 0xff);
 266        fb_bitmask = in_8(fb_start);
 267        out_8(fb_start, 0);
 268
 269        /*
 270         *      Enable reading/writing of all the planes.
 271         */
 272        out_8(fb_regs + TC_WEN, fb_bitmask);
 273        out_8(fb_regs + TC_PRR, RR_COPY);
 274        out_8(fb_regs + TC_REN, fb_bitmask);
 275        out_8(fb_regs + TC_FBEN, fb_bitmask);
 276
 277        /*
 278         *      Clear the screen.
 279         */
 280        topcat_blit(0, 0, 0, 0, fb_width, fb_height, RR_CLEAR);
 281
 282        /*
 283         *      Let there be consoles..
 284         */
 285        if (DIO_SECID(fb_regs) == DIO_ID2_TOPCAT)
 286                strcat(fb_info.fix.id, "Topcat");
 287        else
 288                strcat(fb_info.fix.id, "Catseye");
 289        fb_info.fbops = &hpfb_ops;
 290        fb_info.flags = FBINFO_DEFAULT;
 291        fb_info.var   = hpfb_defined;
 292        fb_info.screen_base = (char *)fb_start;
 293
 294        ret = fb_alloc_cmap(&fb_info.cmap, 1 << hpfb_defined.bits_per_pixel, 0);
 295        if (ret < 0)
 296                goto unmap_screen_base;
 297
 298        ret = register_framebuffer(&fb_info);
 299        if (ret < 0)
 300                goto dealloc_cmap;
 301
 302        printk(KERN_INFO "fb%d: %s frame buffer device\n",
 303               fb_info.node, fb_info.fix.id);
 304
 305        return 0;
 306
 307dealloc_cmap:
 308        fb_dealloc_cmap(&fb_info.cmap);
 309
 310unmap_screen_base:
 311        if (fb_info.screen_base) {
 312                iounmap(fb_info.screen_base);
 313                fb_info.screen_base = NULL;
 314        }
 315
 316        return ret;
 317}
 318
 319/* 
 320 * Check that the secondary ID indicates that we have some hope of working with this
 321 * framebuffer.  The catseye boards are pretty much like topcats and we can muddle through.
 322 */
 323
 324#define topcat_sid_ok(x)  (((x) == DIO_ID2_LRCATSEYE) || ((x) == DIO_ID2_HRCCATSEYE)    \
 325                           || ((x) == DIO_ID2_HRMCATSEYE) || ((x) == DIO_ID2_TOPCAT))
 326
 327/* 
 328 * Initialise the framebuffer
 329 */
 330static int __devinit hpfb_dio_probe(struct dio_dev * d, const struct dio_device_id * ent)
 331{
 332        unsigned long paddr, vaddr;
 333
 334        paddr = d->resource.start;
 335        if (!request_mem_region(d->resource.start, resource_size(&d->resource), d->name))
 336                return -EBUSY;
 337
 338        if (d->scode >= DIOII_SCBASE) {
 339                vaddr = (unsigned long)ioremap(paddr, resource_size(&d->resource));
 340        } else {
 341                vaddr = paddr + DIO_VIRADDRBASE;
 342        }
 343        printk(KERN_INFO "Topcat found at DIO select code %d "
 344               "(secondary id %02x)\n", d->scode, (d->id >> 8) & 0xff);
 345        if (hpfb_init_one(paddr, vaddr)) {
 346                if (d->scode >= DIOII_SCBASE)
 347                        iounmap((void *)vaddr);
 348                return -ENOMEM;
 349        }
 350        return 0;
 351}
 352
 353static void __devexit hpfb_remove_one(struct dio_dev *d)
 354{
 355        unregister_framebuffer(&fb_info);
 356        if (d->scode >= DIOII_SCBASE)
 357                iounmap((void *)fb_regs);
 358        release_mem_region(d->resource.start, resource_size(&d->resource));
 359        fb_dealloc_cmap(&fb_info.cmap);
 360        if (fb_info.screen_base)
 361                iounmap(fb_info.screen_base);
 362}
 363
 364static struct dio_device_id hpfb_dio_tbl[] = {
 365    { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_LRCATSEYE) },
 366    { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_HRCCATSEYE) },
 367    { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_HRMCATSEYE) },
 368    { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_TOPCAT) },
 369    { 0 }
 370};
 371
 372static struct dio_driver hpfb_driver = {
 373    .name      = "hpfb",
 374    .id_table  = hpfb_dio_tbl,
 375    .probe     = hpfb_dio_probe,
 376    .remove    = __devexit_p(hpfb_remove_one),
 377};
 378
 379int __init hpfb_init(void)
 380{
 381        unsigned int sid;
 382        mm_segment_t fs;
 383        unsigned char i;
 384        int err;
 385
 386        /* Topcats can be on the internal IO bus or real DIO devices.
 387         * The internal variant sits at 0x560000; it has primary
 388         * and secondary ID registers just like the DIO version.
 389         * So we merge the two detection routines.
 390         *
 391         * Perhaps this #define should be in a global header file:
 392         * I believe it's common to all internal fbs, not just topcat.
 393         */
 394#define INTFBVADDR 0xf0560000
 395#define INTFBPADDR 0x560000
 396
 397        if (!MACH_IS_HP300)
 398                return -ENODEV;
 399
 400        if (fb_get_options("hpfb", NULL))
 401                return -ENODEV;
 402
 403        err = dio_register_driver(&hpfb_driver);
 404        if (err)
 405                return err;
 406
 407        fs = get_fs();
 408        set_fs(KERNEL_DS);
 409        err = get_user(i, (unsigned char *)INTFBVADDR + DIO_IDOFF);
 410        set_fs(fs);
 411
 412        if (!err && (i == DIO_ID_FBUFFER) && topcat_sid_ok(sid = DIO_SECID(INTFBVADDR))) {
 413                if (!request_mem_region(INTFBPADDR, DIO_DEVSIZE, "Internal Topcat"))
 414                        return -EBUSY;
 415                printk(KERN_INFO "Internal Topcat found (secondary id %02x)\n", sid);
 416                if (hpfb_init_one(INTFBPADDR, INTFBVADDR)) {
 417                        return -ENOMEM;
 418                }
 419        }
 420        return 0;
 421}
 422
 423void __exit hpfb_cleanup_module(void)
 424{
 425        dio_unregister_driver(&hpfb_driver);
 426}
 427
 428module_init(hpfb_init);
 429module_exit(hpfb_cleanup_module);
 430
 431MODULE_LICENSE("GPL");
 432
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.