linux/drivers/video/fbdev/cg3.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* cg3.c: CGTHREE frame buffer driver
   3 *
   4 * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net)
   5 * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
   6 * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
   7 * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
   8 *
   9 * Driver layout based loosely on tgafb.c, see that file for credits.
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/kernel.h>
  14#include <linux/errno.h>
  15#include <linux/string.h>
  16#include <linux/delay.h>
  17#include <linux/init.h>
  18#include <linux/fb.h>
  19#include <linux/mm.h>
  20#include <linux/of_device.h>
  21
  22#include <asm/io.h>
  23#include <asm/fbio.h>
  24
  25#include "sbuslib.h"
  26
  27/*
  28 * Local functions.
  29 */
  30
  31static int cg3_setcolreg(unsigned, unsigned, unsigned, unsigned,
  32                         unsigned, struct fb_info *);
  33static int cg3_blank(int, struct fb_info *);
  34
  35static int cg3_mmap(struct fb_info *, struct vm_area_struct *);
  36static int cg3_ioctl(struct fb_info *, unsigned int, unsigned long);
  37
  38/*
  39 *  Frame buffer operations
  40 */
  41
  42static const struct fb_ops cg3_ops = {
  43        .owner                  = THIS_MODULE,
  44        .fb_setcolreg           = cg3_setcolreg,
  45        .fb_blank               = cg3_blank,
  46        .fb_fillrect            = cfb_fillrect,
  47        .fb_copyarea            = cfb_copyarea,
  48        .fb_imageblit           = cfb_imageblit,
  49        .fb_mmap                = cg3_mmap,
  50        .fb_ioctl               = cg3_ioctl,
  51#ifdef CONFIG_COMPAT
  52        .fb_compat_ioctl        = sbusfb_compat_ioctl,
  53#endif
  54};
  55
  56
  57/* Control Register Constants */
  58#define CG3_CR_ENABLE_INTS      0x80
  59#define CG3_CR_ENABLE_VIDEO     0x40
  60#define CG3_CR_ENABLE_TIMING    0x20
  61#define CG3_CR_ENABLE_CURCMP    0x10
  62#define CG3_CR_XTAL_MASK        0x0c
  63#define CG3_CR_DIVISOR_MASK     0x03
  64
  65/* Status Register Constants */
  66#define CG3_SR_PENDING_INT      0x80
  67#define CG3_SR_RES_MASK         0x70
  68#define CG3_SR_1152_900_76_A    0x40
  69#define CG3_SR_1152_900_76_B    0x60
  70#define CG3_SR_ID_MASK          0x0f
  71#define CG3_SR_ID_COLOR         0x01
  72#define CG3_SR_ID_MONO          0x02
  73#define CG3_SR_ID_MONO_ECL      0x03
  74
  75enum cg3_type {
  76        CG3_AT_66HZ = 0,
  77        CG3_AT_76HZ,
  78        CG3_RDI
  79};
  80
  81struct bt_regs {
  82        u32 addr;
  83        u32 color_map;
  84        u32 control;
  85        u32 cursor;
  86};
  87
  88struct cg3_regs {
  89        struct bt_regs  cmap;
  90        u8      control;
  91        u8      status;
  92        u8      cursor_start;
  93        u8      cursor_end;
  94        u8      h_blank_start;
  95        u8      h_blank_end;
  96        u8      h_sync_start;
  97        u8      h_sync_end;
  98        u8      comp_sync_end;
  99        u8      v_blank_start_high;
 100        u8      v_blank_start_low;
 101        u8      v_blank_end;
 102        u8      v_sync_start;
 103        u8      v_sync_end;
 104        u8      xfer_holdoff_start;
 105        u8      xfer_holdoff_end;
 106};
 107
 108/* Offset of interesting structures in the OBIO space */
 109#define CG3_REGS_OFFSET      0x400000UL
 110#define CG3_RAM_OFFSET       0x800000UL
 111
 112struct cg3_par {
 113        spinlock_t              lock;
 114        struct cg3_regs         __iomem *regs;
 115        u32                     sw_cmap[((256 * 3) + 3) / 4];
 116
 117        u32                     flags;
 118#define CG3_FLAG_BLANKED        0x00000001
 119#define CG3_FLAG_RDI            0x00000002
 120
 121        unsigned long           which_io;
 122};
 123
 124/**
 125 *      cg3_setcolreg - Optional function. Sets a color register.
 126 *      @regno: boolean, 0 copy local, 1 get_user() function
 127 *      @red: frame buffer colormap structure
 128 *      @green: The green value which can be up to 16 bits wide
 129 *      @blue:  The blue value which can be up to 16 bits wide.
 130 *      @transp: If supported the alpha value which can be up to 16 bits wide.
 131 *      @info: frame buffer info structure
 132 *
 133 * The cg3 palette is loaded with 4 color values at each time
 134 * so you end up with: (rgb)(r), (gb)(rg), (b)(rgb), and so on.
 135 * We keep a sw copy of the hw cmap to assist us in this esoteric
 136 * loading procedure.
 137 */
 138static int cg3_setcolreg(unsigned regno,
 139                         unsigned red, unsigned green, unsigned blue,
 140                         unsigned transp, struct fb_info *info)
 141{
 142        struct cg3_par *par = (struct cg3_par *) info->par;
 143        struct bt_regs __iomem *bt = &par->regs->cmap;
 144        unsigned long flags;
 145        u32 *p32;
 146        u8 *p8;
 147        int count;
 148
 149        if (regno >= 256)
 150                return 1;
 151
 152        red >>= 8;
 153        green >>= 8;
 154        blue >>= 8;
 155
 156        spin_lock_irqsave(&par->lock, flags);
 157
 158        p8 = (u8 *)par->sw_cmap + (regno * 3);
 159        p8[0] = red;
 160        p8[1] = green;
 161        p8[2] = blue;
 162
 163#define D4M3(x) ((((x)>>2)<<1) + ((x)>>2))      /* (x/4)*3 */
 164#define D4M4(x) ((x)&~0x3)                      /* (x/4)*4 */
 165
 166        count = 3;
 167        p32 = &par->sw_cmap[D4M3(regno)];
 168        sbus_writel(D4M4(regno), &bt->addr);
 169        while (count--)
 170                sbus_writel(*p32++, &bt->color_map);
 171
 172#undef D4M3
 173#undef D4M4
 174
 175        spin_unlock_irqrestore(&par->lock, flags);
 176
 177        return 0;
 178}
 179
 180/**
 181 *      cg3_blank - Optional function.  Blanks the display.
 182 *      @blank: the blank mode we want.
 183 *      @info: frame buffer structure that represents a single frame buffer
 184 */
 185static int cg3_blank(int blank, struct fb_info *info)
 186{
 187        struct cg3_par *par = (struct cg3_par *) info->par;
 188        struct cg3_regs __iomem *regs = par->regs;
 189        unsigned long flags;
 190        u8 val;
 191
 192        spin_lock_irqsave(&par->lock, flags);
 193
 194        switch (blank) {
 195        case FB_BLANK_UNBLANK: /* Unblanking */
 196                val = sbus_readb(&regs->control);
 197                val |= CG3_CR_ENABLE_VIDEO;
 198                sbus_writeb(val, &regs->control);
 199                par->flags &= ~CG3_FLAG_BLANKED;
 200                break;
 201
 202        case FB_BLANK_NORMAL: /* Normal blanking */
 203        case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
 204        case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
 205        case FB_BLANK_POWERDOWN: /* Poweroff */
 206                val = sbus_readb(&regs->control);
 207                val &= ~CG3_CR_ENABLE_VIDEO;
 208                sbus_writeb(val, &regs->control);
 209                par->flags |= CG3_FLAG_BLANKED;
 210                break;
 211        }
 212
 213        spin_unlock_irqrestore(&par->lock, flags);
 214
 215        return 0;
 216}
 217
 218static struct sbus_mmap_map cg3_mmap_map[] = {
 219        {
 220                .voff   = CG3_MMAP_OFFSET,
 221                .poff   = CG3_RAM_OFFSET,
 222                .size   = SBUS_MMAP_FBSIZE(1)
 223        },
 224        { .size = 0 }
 225};
 226
 227static int cg3_mmap(struct fb_info *info, struct vm_area_struct *vma)
 228{
 229        struct cg3_par *par = (struct cg3_par *)info->par;
 230
 231        return sbusfb_mmap_helper(cg3_mmap_map,
 232                                  info->fix.smem_start, info->fix.smem_len,
 233                                  par->which_io,
 234                                  vma);
 235}
 236
 237static int cg3_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
 238{
 239        return sbusfb_ioctl_helper(cmd, arg, info,
 240                                   FBTYPE_SUN3COLOR, 8, info->fix.smem_len);
 241}
 242
 243/*
 244 *  Initialisation
 245 */
 246
 247static void cg3_init_fix(struct fb_info *info, int linebytes,
 248                         struct device_node *dp)
 249{
 250        snprintf(info->fix.id, sizeof(info->fix.id), "%pOFn", dp);
 251
 252        info->fix.type = FB_TYPE_PACKED_PIXELS;
 253        info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
 254
 255        info->fix.line_length = linebytes;
 256
 257        info->fix.accel = FB_ACCEL_SUN_CGTHREE;
 258}
 259
 260static void cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo *var,
 261                                    struct device_node *dp)
 262{
 263        const char *params;
 264        char *p;
 265        int ww, hh;
 266
 267        params = of_get_property(dp, "params", NULL);
 268        if (params) {
 269                ww = simple_strtoul(params, &p, 10);
 270                if (ww && *p == 'x') {
 271                        hh = simple_strtoul(p + 1, &p, 10);
 272                        if (hh && *p == '-') {
 273                                if (var->xres != ww ||
 274                                    var->yres != hh) {
 275                                        var->xres = var->xres_virtual = ww;
 276                                        var->yres = var->yres_virtual = hh;
 277                                }
 278                        }
 279                }
 280        }
 281}
 282
 283static u8 cg3regvals_66hz[] = { /* 1152 x 900, 66 Hz */
 284        0x14, 0xbb,     0x15, 0x2b,     0x16, 0x04,     0x17, 0x14,
 285        0x18, 0xae,     0x19, 0x03,     0x1a, 0xa8,     0x1b, 0x24,
 286        0x1c, 0x01,     0x1d, 0x05,     0x1e, 0xff,     0x1f, 0x01,
 287        0x10, 0x20,     0
 288};
 289
 290static u8 cg3regvals_76hz[] = { /* 1152 x 900, 76 Hz */
 291        0x14, 0xb7,     0x15, 0x27,     0x16, 0x03,     0x17, 0x0f,
 292        0x18, 0xae,     0x19, 0x03,     0x1a, 0xae,     0x1b, 0x2a,
 293        0x1c, 0x01,     0x1d, 0x09,     0x1e, 0xff,     0x1f, 0x01,
 294        0x10, 0x24,     0
 295};
 296
 297static u8 cg3regvals_rdi[] = {  /* 640 x 480, cgRDI */
 298        0x14, 0x70,     0x15, 0x20,     0x16, 0x08,     0x17, 0x10,
 299        0x18, 0x06,     0x19, 0x02,     0x1a, 0x31,     0x1b, 0x51,
 300        0x1c, 0x06,     0x1d, 0x0c,     0x1e, 0xff,     0x1f, 0x01,
 301        0x10, 0x22,     0
 302};
 303
 304static u8 *cg3_regvals[] = {
 305        cg3regvals_66hz, cg3regvals_76hz, cg3regvals_rdi
 306};
 307
 308static u_char cg3_dacvals[] = {
 309        4, 0xff,        5, 0x00,        6, 0x70,        7, 0x00,        0
 310};
 311
 312static int cg3_do_default_mode(struct cg3_par *par)
 313{
 314        enum cg3_type type;
 315        u8 *p;
 316
 317        if (par->flags & CG3_FLAG_RDI)
 318                type = CG3_RDI;
 319        else {
 320                u8 status = sbus_readb(&par->regs->status), mon;
 321                if ((status & CG3_SR_ID_MASK) == CG3_SR_ID_COLOR) {
 322                        mon = status & CG3_SR_RES_MASK;
 323                        if (mon == CG3_SR_1152_900_76_A ||
 324                            mon == CG3_SR_1152_900_76_B)
 325                                type = CG3_AT_76HZ;
 326                        else
 327                                type = CG3_AT_66HZ;
 328                } else {
 329                        printk(KERN_ERR "cgthree: can't handle SR %02x\n",
 330                               status);
 331                        return -EINVAL;
 332                }
 333        }
 334
 335        for (p = cg3_regvals[type]; *p; p += 2) {
 336                u8 __iomem *regp = &((u8 __iomem *)par->regs)[p[0]];
 337                sbus_writeb(p[1], regp);
 338        }
 339        for (p = cg3_dacvals; *p; p += 2) {
 340                u8 __iomem *regp;
 341
 342                regp = (u8 __iomem *)&par->regs->cmap.addr;
 343                sbus_writeb(p[0], regp);
 344                regp = (u8 __iomem *)&par->regs->cmap.control;
 345                sbus_writeb(p[1], regp);
 346        }
 347        return 0;
 348}
 349
 350static int cg3_probe(struct platform_device *op)
 351{
 352        struct device_node *dp = op->dev.of_node;
 353        struct fb_info *info;
 354        struct cg3_par *par;
 355        int linebytes, err;
 356
 357        info = framebuffer_alloc(sizeof(struct cg3_par), &op->dev);
 358
 359        err = -ENOMEM;
 360        if (!info)
 361                goto out_err;
 362        par = info->par;
 363
 364        spin_lock_init(&par->lock);
 365
 366        info->fix.smem_start = op->resource[0].start;
 367        par->which_io = op->resource[0].flags & IORESOURCE_BITS;
 368
 369        sbusfb_fill_var(&info->var, dp, 8);
 370        info->var.red.length = 8;
 371        info->var.green.length = 8;
 372        info->var.blue.length = 8;
 373        if (of_node_name_eq(dp, "cgRDI"))
 374                par->flags |= CG3_FLAG_RDI;
 375        if (par->flags & CG3_FLAG_RDI)
 376                cg3_rdi_maybe_fixup_var(&info->var, dp);
 377
 378        linebytes = of_getintprop_default(dp, "linebytes",
 379                                          info->var.xres);
 380        info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres);
 381
 382        par->regs = of_ioremap(&op->resource[0], CG3_REGS_OFFSET,
 383                               sizeof(struct cg3_regs), "cg3 regs");
 384        if (!par->regs)
 385                goto out_release_fb;
 386
 387        info->flags = FBINFO_DEFAULT;
 388        info->fbops = &cg3_ops;
 389        info->screen_base = of_ioremap(&op->resource[0], CG3_RAM_OFFSET,
 390                                       info->fix.smem_len, "cg3 ram");
 391        if (!info->screen_base)
 392                goto out_unmap_regs;
 393
 394        cg3_blank(FB_BLANK_UNBLANK, info);
 395
 396        if (!of_find_property(dp, "width", NULL)) {
 397                err = cg3_do_default_mode(par);
 398                if (err)
 399                        goto out_unmap_screen;
 400        }
 401
 402        err = fb_alloc_cmap(&info->cmap, 256, 0);
 403        if (err)
 404                goto out_unmap_screen;
 405
 406        fb_set_cmap(&info->cmap, info);
 407
 408        cg3_init_fix(info, linebytes, dp);
 409
 410        err = register_framebuffer(info);
 411        if (err < 0)
 412                goto out_dealloc_cmap;
 413
 414        dev_set_drvdata(&op->dev, info);
 415
 416        printk(KERN_INFO "%pOF: cg3 at %lx:%lx\n",
 417               dp, par->which_io, info->fix.smem_start);
 418
 419        return 0;
 420
 421out_dealloc_cmap:
 422        fb_dealloc_cmap(&info->cmap);
 423
 424out_unmap_screen:
 425        of_iounmap(&op->resource[0], info->screen_base, info->fix.smem_len);
 426
 427out_unmap_regs:
 428        of_iounmap(&op->resource[0], par->regs, sizeof(struct cg3_regs));
 429
 430out_release_fb:
 431        framebuffer_release(info);
 432
 433out_err:
 434        return err;
 435}
 436
 437static int cg3_remove(struct platform_device *op)
 438{
 439        struct fb_info *info = dev_get_drvdata(&op->dev);
 440        struct cg3_par *par = info->par;
 441
 442        unregister_framebuffer(info);
 443        fb_dealloc_cmap(&info->cmap);
 444
 445        of_iounmap(&op->resource[0], par->regs, sizeof(struct cg3_regs));
 446        of_iounmap(&op->resource[0], info->screen_base, info->fix.smem_len);
 447
 448        framebuffer_release(info);
 449
 450        return 0;
 451}
 452
 453static const struct of_device_id cg3_match[] = {
 454        {
 455                .name = "cgthree",
 456        },
 457        {
 458                .name = "cgRDI",
 459        },
 460        {},
 461};
 462MODULE_DEVICE_TABLE(of, cg3_match);
 463
 464static struct platform_driver cg3_driver = {
 465        .driver = {
 466                .name = "cg3",
 467                .of_match_table = cg3_match,
 468        },
 469        .probe          = cg3_probe,
 470        .remove         = cg3_remove,
 471};
 472
 473static int __init cg3_init(void)
 474{
 475        if (fb_get_options("cg3fb", NULL))
 476                return -ENODEV;
 477
 478        return platform_driver_register(&cg3_driver);
 479}
 480
 481static void __exit cg3_exit(void)
 482{
 483        platform_driver_unregister(&cg3_driver);
 484}
 485
 486module_init(cg3_init);
 487module_exit(cg3_exit);
 488
 489MODULE_DESCRIPTION("framebuffer driver for CGthree chipsets");
 490MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
 491MODULE_VERSION("2.0");
 492MODULE_LICENSE("GPL");
 493