root/drivers/video/fbdev/hpfb.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hpfb_setcolreg
  2. hpfb_blank
  3. topcat_blit
  4. hpfb_copyarea
  5. hpfb_fillrect
  6. hpfb_sync
  7. hpfb_init_one
  8. hpfb_dio_probe
  9. hpfb_remove_one
  10. hpfb_init
  11. hpfb_cleanup_module

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

/* [<][>][^][v][top][bottom][index][help] */