/*
* macfb.c: Generic framebuffer for Macs whose colourmaps/modes we
* don't know how to set.
*
* (c) 1999 David Huggins-Daines <dhd@debian.org>
*
* Primarily based on vesafb.c, by Gerd Knorr
* (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
*
* Also uses information and code from:
*
* The original macfb.c from Linux/mac68k 2.0, by Alan Cox, Juergen
* Mellinger, Mikael Forselius, Michael Schmitz, and others.
*
* valkyriefb.c, by Martin Costabel, Kevin Schoedel, Barry Nathan, Dan
* Jacobowitz, Paul Mackerras, Fabio Riccardi, and Geert Uytterhoeven.
*
* The VideoToolbox "Bugs" web page at
* http://rajsky.psych.nyu.edu/Tips/VideoBugs.html
*
* This code is free software. You may copy, modify, and distribute
* it subject to the terms and conditions of the GNU General Public
* License, version 2, or any later version, at your convenience.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/nubus.h>
#include <linux/init.h>
#include <linux/fb.h>
#include <asm/setup.h>
#include <asm/bootinfo.h>
#include <asm/macintosh.h>
#include <asm/io.h>
/* Common DAC base address for the LC, RBV, Valkyrie, and IIvx */
#define DAC_BASE 0x50f24000
/* Some addresses for the DAFB */
#define DAFB_BASE 0xf9800200
/* Address for the built-in Civic framebuffer in Quadra AVs */
#define CIVIC_BASE 0x50f30800
/* GSC (Gray Scale Controller) base address */
#define GSC_BASE 0x50F20000
/* CSC (Color Screen Controller) base address */
#define CSC_BASE 0x50F20000
static int (*macfb_setpalette)(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
struct fb_info *info);
static struct {
unsigned char addr;
unsigned char lut;
} __iomem *v8_brazil_cmap_regs;
static struct {
unsigned char addr;
char pad1[3]; /* word aligned */
unsigned char lut;
char pad2[3]; /* word aligned */
unsigned char cntl; /* a guess as to purpose */
} __iomem *rbv_cmap_regs;
static struct {
unsigned long reset;
unsigned long pad1[3];
unsigned char pad2[3];
unsigned char lut;
} __iomem *dafb_cmap_regs;
static struct {
unsigned char addr; /* OFFSET: 0x00 */
unsigned char pad1[15];
unsigned char lut; /* OFFSET: 0x10 */
unsigned char pad2[15];
unsigned char status; /* OFFSET: 0x20 */
unsigned char pad3[7];
unsigned long vbl_addr; /* OFFSET: 0x28 */
unsigned int status2; /* OFFSET: 0x2C */
} __iomem *civic_cmap_regs;
static struct {
char pad1[0x40];
unsigned char clut_waddr; /* 0x40 */
char pad2;
unsigned char clut_data; /* 0x42 */
char pad3[0x3];
unsigned char clut_raddr; /* 0x46 */
} __iomem *csc_cmap_regs;
/* The registers in these structs are in NuBus slot space */
struct mdc_cmap_regs {
char pad1[0x200200];
unsigned char addr;
char pad2[6];
unsigned char lut;
};
struct toby_cmap_regs {
char pad1[0x90018];
unsigned char lut; /* TFBClutWDataReg, offset 0x90018 */
char pad2[3];
unsigned char addr; /* TFBClutAddrReg, offset 0x9001C */
};
struct jet_cmap_regs {
char pad1[0xe0e000];
unsigned char addr;
unsigned char lut;
};
#define PIXEL_TO_MM(a) (((a)*10)/28) /* width in mm at 72 dpi */
static int video_slot = 0;
static struct fb_var_screeninfo macfb_defined = {
.bits_per_pixel = 8,
.activate = FB_ACTIVATE_NOW,
.width = -1,
.height = -1,
.right_margin = 32,
.upper_margin = 16,
.lower_margin = 4,
.vsync_len = 4,
.vmode = FB_VMODE_NONINTERLACED,
};
static struct fb_fix_screeninfo macfb_fix = {
.type = FB_TYPE_PACKED_PIXELS,
.accel = FB_ACCEL_NONE,
};
static struct fb_info fb_info;
static u32 pseudo_palette[16];
static int inverse;
static int vidtest;
/*
* Unlike the Valkyrie, the DAFB cannot set individual colormap
* registers. Therefore, we do what the MacOS driver does (no
* kidding!) and simply set them one by one until we hit the one we
* want.
*/
static int dafb_setpalette(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
struct fb_info *info)
{
static int lastreg = -1;
unsigned long flags;
red >>= 8;
green >>= 8;
blue >>= 8;
local_irq_save(flags);
/*
* fbdev will set an entire colourmap, but X won't. Hopefully
* this should accommodate both of them
*/
if (regno != lastreg + 1) {
int i;
/* Stab in the dark trying to reset the CLUT pointer */
nubus_writel(0, &dafb_cmap_regs->reset);
nop();
/* Loop until we get to the register we want */
for (i = 0; i < regno; i++) {
nubus_writeb(info->cmap.red[i] >> 8,
&dafb_cmap_regs->lut);
nop();
nubus_writeb(info->cmap.green[i] >> 8,
&dafb_cmap_regs->lut);
nop();
nubus_writeb(info->cmap.blue