linux/drivers/video/fbdev/valkyriefb.c
<<
>>
Prefs
   1/*
   2 *  valkyriefb.c -- frame buffer device for the PowerMac 'valkyrie' display
   3 *
   4 *  Created 8 August 1998 by 
   5 *  Martin Costabel <costabel@wanadoo.fr> and Kevin Schoedel
   6 *
   7 *  Vmode-switching changes and vmode 15/17 modifications created 29 August
   8 *  1998 by Barry K. Nathan <barryn@pobox.com>.
   9 *
  10 *  Ported to m68k Macintosh by David Huggins-Daines <dhd@debian.org>
  11 *
  12 *  Derived directly from:
  13 *
  14 *   controlfb.c -- frame buffer device for the PowerMac 'control' display
  15 *   Copyright (C) 1998 Dan Jacobowitz <dan@debian.org>
  16 *
  17 *   pmc-valkyrie.c -- Console support for PowerMac "valkyrie" display adaptor.
  18 *   Copyright (C) 1997 Paul Mackerras.
  19 *
  20 *  and indirectly:
  21 *
  22 *  Frame buffer structure from:
  23 *    drivers/video/chipsfb.c -- frame buffer device for
  24 *    Chips & Technologies 65550 chip.
  25 *
  26 *    Copyright (C) 1998 Paul Mackerras
  27 *
  28 *    This file is derived from the Powermac "chips" driver:
  29 *    Copyright (C) 1997 Fabio Riccardi.
  30 *    And from the frame buffer device for Open Firmware-initialized devices:
  31 *    Copyright (C) 1997 Geert Uytterhoeven.
  32 *
  33 *  Hardware information from:
  34 *    control.c: Console support for PowerMac "control" display adaptor.
  35 *    Copyright (C) 1996 Paul Mackerras
  36 *
  37 *  This file is subject to the terms and conditions of the GNU General Public
  38 *  License. See the file COPYING in the main directory of this archive for
  39 *  more details.
  40 */
  41
  42#include <linux/module.h>
  43#include <linux/kernel.h>
  44#include <linux/errno.h>
  45#include <linux/string.h>
  46#include <linux/mm.h>
  47#include <linux/slab.h>
  48#include <linux/vmalloc.h>
  49#include <linux/delay.h>
  50#include <linux/interrupt.h>
  51#include <linux/fb.h>
  52#include <linux/selection.h>
  53#include <linux/init.h>
  54#include <linux/nvram.h>
  55#include <linux/adb.h>
  56#include <linux/cuda.h>
  57#ifdef CONFIG_MAC
  58#include <asm/macintosh.h>
  59#else
  60#include <asm/prom.h>
  61#endif
  62
  63#include "macmodes.h"
  64#include "valkyriefb.h"
  65
  66static int default_vmode = VMODE_NVRAM;
  67static int default_cmode = CMODE_NVRAM;
  68
  69struct fb_par_valkyrie {
  70        int     vmode, cmode;
  71        int     xres, yres;
  72        int     vxres, vyres;
  73        struct valkyrie_regvals *init;
  74};
  75
  76struct fb_info_valkyrie {
  77        struct fb_info          info;
  78        struct fb_par_valkyrie  par;
  79        struct cmap_regs        __iomem *cmap_regs;
  80        unsigned long           cmap_regs_phys;
  81        
  82        struct valkyrie_regs    __iomem *valkyrie_regs;
  83        unsigned long           valkyrie_regs_phys;
  84        
  85        __u8                    __iomem *frame_buffer;
  86        unsigned long           frame_buffer_phys;
  87        
  88        int                     sense;
  89        unsigned long           total_vram;
  90
  91        u32                     pseudo_palette[16];
  92};
  93
  94/*
  95 * Exported functions
  96 */
  97int valkyriefb_init(void);
  98int valkyriefb_setup(char*);
  99
 100static int valkyriefb_check_var(struct fb_var_screeninfo *var,
 101                                struct fb_info *info);
 102static int valkyriefb_set_par(struct fb_info *info);
 103static int valkyriefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 104                             u_int transp, struct fb_info *info);
 105static int valkyriefb_blank(int blank_mode, struct fb_info *info);
 106
 107static int read_valkyrie_sense(struct fb_info_valkyrie *p);
 108static void set_valkyrie_clock(unsigned char *params);
 109static int valkyrie_var_to_par(struct fb_var_screeninfo *var,
 110        struct fb_par_valkyrie *par, const struct fb_info *fb_info);
 111
 112static int valkyrie_init_info(struct fb_info *info, struct fb_info_valkyrie *p);
 113static void valkyrie_par_to_fix(struct fb_par_valkyrie *par, struct fb_fix_screeninfo *fix);
 114static void valkyrie_init_fix(struct fb_fix_screeninfo *fix, struct fb_info_valkyrie *p);
 115
 116static struct fb_ops valkyriefb_ops = {
 117        .owner =        THIS_MODULE,
 118        .fb_check_var = valkyriefb_check_var,
 119        .fb_set_par =   valkyriefb_set_par,
 120        .fb_setcolreg = valkyriefb_setcolreg,
 121        .fb_blank =     valkyriefb_blank,
 122        .fb_fillrect    = cfb_fillrect,
 123        .fb_copyarea    = cfb_copyarea,
 124        .fb_imageblit   = cfb_imageblit,
 125};
 126
 127/* Sets the video mode according to info->var */
 128static int valkyriefb_set_par(struct fb_info *info)
 129{
 130        struct fb_info_valkyrie *p =
 131                container_of(info, struct fb_info_valkyrie, info);
 132        volatile struct valkyrie_regs __iomem *valkyrie_regs = p->valkyrie_regs;
 133        struct fb_par_valkyrie *par = info->par;
 134        struct valkyrie_regvals *init;
 135        int err;
 136
 137        if ((err = valkyrie_var_to_par(&info->var, par, info)))
 138                return err;
 139
 140        valkyrie_par_to_fix(par, &info->fix);
 141
 142        /* Reset the valkyrie */
 143        out_8(&valkyrie_regs->status.r, 0);
 144        udelay(100);
 145
 146        /* Initialize display timing registers */
 147        init = par->init;
 148        out_8(&valkyrie_regs->mode.r, init->mode | 0x80);
 149        out_8(&valkyrie_regs->depth.r, par->cmode + 3);
 150        set_valkyrie_clock(init->clock_params);
 151        udelay(100);
 152
 153        /* Turn on display */
 154        out_8(&valkyrie_regs->mode.r, init->mode);
 155
 156        return 0;
 157}
 158
 159static inline int valkyrie_par_to_var(struct fb_par_valkyrie *par,
 160                                      struct fb_var_screeninfo *var)
 161{
 162        return mac_vmode_to_var(par->vmode, par->cmode, var);
 163}
 164
 165static int
 166valkyriefb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
 167{
 168        int err;
 169        struct fb_par_valkyrie par;
 170
 171        if ((err = valkyrie_var_to_par(var, &par, info)))
 172                return err;
 173        valkyrie_par_to_var(&par, var);
 174        return 0;
 175}
 176
 177/*
 178 *  Blank the screen if blank_mode != 0, else unblank. If blank_mode == NULL
 179 *  then the caller blanks by setting the CLUT (Color Look Up Table) to all
 180 *  black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
 181 *  to e.g. a video mode which doesn't support it. Implements VESA suspend
 182 *  and powerdown modes on hardware that supports disabling hsync/vsync:
 183 *    blank_mode == 2: suspend vsync
 184 *    blank_mode == 3: suspend hsync
 185 *    blank_mode == 4: powerdown
 186 */
 187static int valkyriefb_blank(int blank_mode, struct fb_info *info)
 188{
 189        struct fb_info_valkyrie *p =
 190                container_of(info, struct fb_info_valkyrie, info);
 191        struct fb_par_valkyrie *par = info->par;
 192        struct valkyrie_regvals *init = par->init;
 193
 194        if (init == NULL)
 195                return 1;
 196
 197        switch (blank_mode) {
 198        case FB_BLANK_UNBLANK:                  /* unblank */
 199                out_8(&p->valkyrie_regs->mode.r, init->mode);
 200                break;
 201        case FB_BLANK_NORMAL:
 202                return 1;       /* get caller to set CLUT to all black */
 203        case FB_BLANK_VSYNC_SUSPEND:
 204        case FB_BLANK_HSYNC_SUSPEND:
 205                /*
 206                 * [kps] Value extracted from MacOS. I don't know
 207                 * whether this bit disables hsync or vsync, or
 208                 * whether the hardware can do the other as well.
 209                 */
 210                out_8(&p->valkyrie_regs->mode.r, init->mode | 0x40);
 211                break;
 212        case FB_BLANK_POWERDOWN:
 213                out_8(&p->valkyrie_regs->mode.r, 0x66);
 214                break;
 215        }
 216        return 0;
 217}
 218
 219static int valkyriefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 220                             u_int transp, struct fb_info *info)
 221{
 222        struct fb_info_valkyrie *p =
 223                container_of(info, struct fb_info_valkyrie, info);
 224        volatile struct cmap_regs __iomem *cmap_regs = p->cmap_regs;
 225        struct fb_par_valkyrie *par = info->par;
 226
 227        if (regno > 255)
 228                return 1;
 229        red >>= 8;
 230        green >>= 8;
 231        blue >>= 8;
 232
 233        /* tell clut which address to fill */
 234        out_8(&p->cmap_regs->addr, regno);
 235        udelay(1);
 236        /* send one color channel at a time */
 237        out_8(&cmap_regs->lut, red);
 238        out_8(&cmap_regs->lut, green);
 239        out_8(&cmap_regs->lut, blue);
 240
 241        if (regno < 16 && par->cmode == CMODE_16)
 242                ((u32 *)info->pseudo_palette)[regno] =
 243                        (regno << 10) | (regno << 5) | regno;
 244
 245        return 0;
 246}
 247
 248static inline int valkyrie_vram_reqd(int video_mode, int color_mode)
 249{
 250        int pitch;
 251        struct valkyrie_regvals *init = valkyrie_reg_init[video_mode-1];
 252        
 253        if ((pitch = init->pitch[color_mode]) == 0)
 254                pitch = 2 * init->pitch[0];
 255        return init->vres * pitch;
 256}
 257
 258static void set_valkyrie_clock(unsigned char *params)
 259{
 260#ifdef CONFIG_ADB_CUDA
 261        struct adb_request req;
 262        int i;
 263
 264        for (i = 0; i < 3; ++i) {
 265                cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
 266                             0x50, i + 1, params[i]);
 267                while (!req.complete)
 268                        cuda_poll();
 269        }
 270#endif
 271}
 272
 273static void __init valkyrie_choose_mode(struct fb_info_valkyrie *p)
 274{
 275        p->sense = read_valkyrie_sense(p);
 276        printk(KERN_INFO "Monitor sense value = 0x%x\n", p->sense);
 277
 278        /* Try to pick a video mode out of NVRAM if we have one. */
 279#ifdef CONFIG_PPC_PMAC
 280        if (IS_REACHABLE(CONFIG_NVRAM) && default_vmode == VMODE_NVRAM)
 281                default_vmode = nvram_read_byte(NV_VMODE);
 282#endif
 283        if (default_vmode <= 0 || default_vmode > VMODE_MAX ||
 284            !valkyrie_reg_init[default_vmode - 1]) {
 285                default_vmode = mac_map_monitor_sense(p->sense);
 286                if (!valkyrie_reg_init[default_vmode - 1])
 287                        default_vmode = VMODE_640_480_67;
 288        }
 289
 290#ifdef CONFIG_PPC_PMAC
 291        if (IS_REACHABLE(CONFIG_NVRAM) && default_cmode == CMODE_NVRAM)
 292                default_cmode = nvram_read_byte(NV_CMODE);
 293#endif
 294        /*
 295         * Reduce the pixel size if we don't have enough VRAM or bandwidth.
 296         */
 297        if (default_cmode < CMODE_8 || default_cmode > CMODE_16
 298            || valkyrie_reg_init[default_vmode-1]->pitch[default_cmode] == 0
 299            || valkyrie_vram_reqd(default_vmode, default_cmode) > p->total_vram)
 300                default_cmode = CMODE_8;
 301
 302        printk(KERN_INFO "using video mode %d and color mode %d.\n",
 303               default_vmode, default_cmode);
 304}
 305
 306int __init valkyriefb_init(void)
 307{
 308        struct fb_info_valkyrie *p;
 309        unsigned long frame_buffer_phys, cmap_regs_phys;
 310        int err;
 311        char *option = NULL;
 312
 313        if (fb_get_options("valkyriefb", &option))
 314                return -ENODEV;
 315        valkyriefb_setup(option);
 316
 317#ifdef CONFIG_MAC
 318        if (!MACH_IS_MAC)
 319                return -ENODEV;
 320        if (!(mac_bi_data.id == MAC_MODEL_Q630
 321              /* I'm not sure about this one */
 322            || mac_bi_data.id == MAC_MODEL_P588))
 323                return -ENODEV;
 324
 325        /* Hardcoded addresses... welcome to 68k Macintosh country :-) */
 326        frame_buffer_phys = 0xf9000000;
 327        cmap_regs_phys = 0x50f24000;
 328#else /* ppc (!CONFIG_MAC) */
 329        {
 330                struct device_node *dp;
 331                struct resource r;
 332
 333                dp = of_find_node_by_name(NULL, "valkyrie");
 334                if (dp == 0)
 335                        return 0;
 336
 337                if (of_address_to_resource(dp, 0, &r)) {
 338                        printk(KERN_ERR "can't find address for valkyrie\n");
 339                        return 0;
 340                }
 341
 342                frame_buffer_phys = r.start;
 343                cmap_regs_phys = r.start + 0x304000;
 344        }
 345#endif /* ppc (!CONFIG_MAC) */
 346
 347        p = kzalloc(sizeof(*p), GFP_ATOMIC);
 348        if (p == 0)
 349                return -ENOMEM;
 350
 351        /* Map in frame buffer and registers */
 352        if (!request_mem_region(frame_buffer_phys, 0x100000, "valkyriefb")) {
 353                kfree(p);
 354                return 0;
 355        }
 356        p->total_vram = 0x100000;
 357        p->frame_buffer_phys = frame_buffer_phys;
 358#ifdef CONFIG_MAC
 359        p->frame_buffer = ioremap_nocache(frame_buffer_phys, p->total_vram);
 360#else
 361        p->frame_buffer = ioremap_wt(frame_buffer_phys, p->total_vram);
 362#endif
 363        p->cmap_regs_phys = cmap_regs_phys;
 364        p->cmap_regs = ioremap(p->cmap_regs_phys, 0x1000);
 365        p->valkyrie_regs_phys = cmap_regs_phys+0x6000;
 366        p->valkyrie_regs = ioremap(p->valkyrie_regs_phys, 0x1000);
 367        err = -ENOMEM;
 368        if (p->frame_buffer == NULL || p->cmap_regs == NULL
 369            || p->valkyrie_regs == NULL) {
 370                printk(KERN_ERR "valkyriefb: couldn't map resources\n");
 371                goto out_free;
 372        }
 373
 374        valkyrie_choose_mode(p);
 375        mac_vmode_to_var(default_vmode, default_cmode, &p->info.var);
 376        err = valkyrie_init_info(&p->info, p);
 377        if (err < 0)
 378                goto out_free;
 379        valkyrie_init_fix(&p->info.fix, p);
 380        if (valkyriefb_set_par(&p->info))
 381                /* "can't happen" */
 382                printk(KERN_ERR "valkyriefb: can't set default video mode\n");
 383
 384        if ((err = register_framebuffer(&p->info)) != 0)
 385                goto out_cmap_free;
 386
 387        fb_info(&p->info, "valkyrie frame buffer device\n");
 388        return 0;
 389
 390 out_cmap_free:
 391        fb_dealloc_cmap(&p->info.cmap);
 392 out_free:
 393        if (p->frame_buffer)
 394                iounmap(p->frame_buffer);
 395        if (p->cmap_regs)
 396                iounmap(p->cmap_regs);
 397        if (p->valkyrie_regs)
 398                iounmap(p->valkyrie_regs);
 399        kfree(p);
 400        return err;
 401}
 402
 403/*
 404 * Get the monitor sense value.
 405 */
 406static int read_valkyrie_sense(struct fb_info_valkyrie *p)
 407{
 408        int sense, in;
 409
 410        out_8(&p->valkyrie_regs->msense.r, 0);   /* release all lines */
 411        __delay(20000);
 412        sense = ((in = in_8(&p->valkyrie_regs->msense.r)) & 0x70) << 4;
 413        /* drive each sense line low in turn and collect the other 2 */
 414        out_8(&p->valkyrie_regs->msense.r, 4);   /* drive A low */
 415        __delay(20000);
 416        sense |= ((in = in_8(&p->valkyrie_regs->msense.r)) & 0x30);
 417        out_8(&p->valkyrie_regs->msense.r, 2);   /* drive B low */
 418        __delay(20000);
 419        sense |= ((in = in_8(&p->valkyrie_regs->msense.r)) & 0x40) >> 3;
 420        sense |= (in & 0x10) >> 2;
 421        out_8(&p->valkyrie_regs->msense.r, 1);   /* drive C low */
 422        __delay(20000);
 423        sense |= ((in = in_8(&p->valkyrie_regs->msense.r)) & 0x60) >> 5;
 424
 425        out_8(&p->valkyrie_regs->msense.r, 7);
 426
 427        return sense;
 428}
 429
 430/*
 431 * This routine takes a user-supplied var,
 432 * and picks the best vmode/cmode from it.
 433 */
 434
 435/* [bkn] I did a major overhaul of this function.
 436 *
 437 * Much of the old code was "swiped by jonh from atyfb.c". Because
 438 * macmodes has mac_var_to_vmode, I felt that it would be better to
 439 * rework this function to use that, instead of reinventing the wheel to
 440 * add support for vmode 17. This was reinforced by the fact that
 441 * the previously swiped atyfb.c code is no longer there.
 442 *
 443 * So, I swiped and adapted platinum_var_to_par (from platinumfb.c), replacing
 444 * most, but not all, of the old code in the process. One side benefit of
 445 * swiping the platinumfb code is that we now have more comprehensible error
 446 * messages when a vmode/cmode switch fails. (Most of the error messages are
 447 * platinumfb.c, but I added two of my own, and I also changed some commas
 448 * into colons to make the messages more consistent with other Linux error
 449 * messages.) In addition, I think the new code *might* fix some vmode-
 450 * switching oddities, but I'm not sure.
 451 *
 452 * There may be some more opportunities for cleanup in here, but this is a
 453 * good start...
 454 */
 455
 456static int valkyrie_var_to_par(struct fb_var_screeninfo *var,
 457        struct fb_par_valkyrie *par, const struct fb_info *fb_info)
 458{
 459        int vmode, cmode;
 460        struct valkyrie_regvals *init;
 461        struct fb_info_valkyrie *p =
 462                container_of(fb_info, struct fb_info_valkyrie, info);
 463
 464        if (mac_var_to_vmode(var, &vmode, &cmode) != 0) {
 465                printk(KERN_ERR "valkyriefb: can't do %dx%dx%d.\n",
 466                       var->xres, var->yres, var->bits_per_pixel);
 467                return -EINVAL;
 468        }
 469
 470        /* Check if we know about the wanted video mode */
 471        if (vmode < 1 || vmode > VMODE_MAX || !valkyrie_reg_init[vmode-1]) {
 472                printk(KERN_ERR "valkyriefb: vmode %d not valid.\n", vmode);
 473                return -EINVAL;
 474        }
 475        
 476        if (cmode != CMODE_8 && cmode != CMODE_16) {
 477                printk(KERN_ERR "valkyriefb: cmode %d not valid.\n", cmode);
 478                return -EINVAL;
 479        }
 480
 481        if (var->xres_virtual > var->xres || var->yres_virtual > var->yres
 482            || var->xoffset != 0 || var->yoffset != 0) {
 483                return -EINVAL;
 484        }
 485
 486        init = valkyrie_reg_init[vmode-1];
 487        if (init->pitch[cmode] == 0) {
 488                printk(KERN_ERR "valkyriefb: vmode %d does not support "
 489                       "cmode %d.\n", vmode, cmode);
 490                return -EINVAL;
 491        }
 492
 493        if (valkyrie_vram_reqd(vmode, cmode) > p->total_vram) {
 494                printk(KERN_ERR "valkyriefb: not enough ram for vmode %d, "
 495                       "cmode %d.\n", vmode, cmode);
 496                return -EINVAL;
 497        }
 498
 499        par->vmode = vmode;
 500        par->cmode = cmode;
 501        par->init = init;
 502        par->xres = var->xres;
 503        par->yres = var->yres;
 504        par->vxres = par->xres;
 505        par->vyres = par->yres;
 506
 507        return 0;
 508}
 509
 510static void valkyrie_init_fix(struct fb_fix_screeninfo *fix, struct fb_info_valkyrie *p)
 511{
 512        memset(fix, 0, sizeof(*fix));
 513        strcpy(fix->id, "valkyrie");
 514        fix->mmio_start = p->valkyrie_regs_phys;
 515        fix->mmio_len = sizeof(struct valkyrie_regs);
 516        fix->type = FB_TYPE_PACKED_PIXELS;
 517        fix->smem_start = p->frame_buffer_phys + 0x1000;
 518        fix->smem_len = p->total_vram;
 519
 520        fix->type_aux = 0;
 521        fix->ywrapstep = 0;
 522        fix->ypanstep = 0;
 523        fix->xpanstep = 0;
 524        
 525}
 526
 527/* Fix must already be inited above */
 528static void valkyrie_par_to_fix(struct fb_par_valkyrie *par,
 529        struct fb_fix_screeninfo *fix)
 530{
 531        fix->smem_len = valkyrie_vram_reqd(par->vmode, par->cmode);
 532        fix->visual = (par->cmode == CMODE_8) ?
 533                FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
 534        fix->line_length = par->vxres << par->cmode;
 535                /* ywrapstep, xpanstep, ypanstep */
 536}
 537
 538static int __init valkyrie_init_info(struct fb_info *info,
 539                struct fb_info_valkyrie *p)
 540{
 541        info->fbops = &valkyriefb_ops;
 542        info->screen_base = p->frame_buffer + 0x1000;
 543        info->flags = FBINFO_DEFAULT;
 544        info->pseudo_palette = p->pseudo_palette;
 545        info->par = &p->par;
 546        return fb_alloc_cmap(&info->cmap, 256, 0);
 547}
 548
 549
 550/*
 551 * Parse user specified options (`video=valkyriefb:')
 552 */
 553int __init valkyriefb_setup(char *options)
 554{
 555        char *this_opt;
 556
 557        if (!options || !*options)
 558                return 0;
 559
 560        while ((this_opt = strsep(&options, ",")) != NULL) {
 561                if (!strncmp(this_opt, "vmode:", 6)) {
 562                        int vmode = simple_strtoul(this_opt+6, NULL, 0);
 563                        if (vmode > 0 && vmode <= VMODE_MAX)
 564                                default_vmode = vmode;
 565                }
 566                else if (!strncmp(this_opt, "cmode:", 6)) {
 567                        int depth = simple_strtoul(this_opt+6, NULL, 0);
 568                        switch (depth) {
 569                        case 8:
 570                                default_cmode = CMODE_8;
 571                                break;
 572                        case 15:
 573                        case 16:
 574                                default_cmode = CMODE_16;
 575                                break;
 576                        }
 577                }
 578        }
 579        return 0;
 580}
 581
 582module_init(valkyriefb_init);
 583MODULE_LICENSE("GPL");
 584