/*
* controlfb.c -- frame buffer device for the PowerMac 'control' display
*
* Created 12 July 1998 by Dan Jacobowitz <dan@debian.org>
* Copyright (C) 1998 Dan Jacobowitz
* Copyright (C) 2001 Takashi Oe
*
* Mmap code by Michel Lanners <mlan@cpu.lu>
*
* Frame buffer structure from:
* drivers/video/chipsfb.c -- frame buffer device for
* Chips & Technologies 65550 chip.
*
* Copyright (C) 1998 Paul Mackerras
*
* This file is derived from the Powermac "chips" driver:
* Copyright (C) 1997 Fabio Riccardi.
* And from the frame buffer device for Open Firmware-initialized devices:
* Copyright (C) 1997 Geert Uytterhoeven.
*
* Hardware information from:
* control.c: Console support for PowerMac "control" display adaptor.
* Copyright (C) 1996 Paul Mackerras
*
* Updated to 2.5 framebuffer API by Ben Herrenschmidt
* <benh@kernel.crashing.org>, Paul Mackerras <paulus@samba.org>,
* and James Simmons <jsimmons@infradead.org>.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/nvram.h>
#include <linux/adb.h>
#include <linux/cuda.h>
#ifdef CONFIG_BOOTX_TEXT
#include <asm/btext.h>
#endif
#include "macmodes.h"
#include "controlfb.h"
#if !defined(CONFIG_PPC_PMAC) || !defined(CONFIG_PPC32)
#define invalid_vram_cache(addr)
#undef in_8
#undef out_8
#undef in_le32
#undef out_le32
#define in_8(addr) 0
#define out_8(addr, val) (void)(val)
#define in_le32(addr) 0
#define out_le32(addr, val) (void)(val)
#ifndef pgprot_cached_wthru
#define pgprot_cached_wthru(prot) (prot)
#endif
#else
static void invalid_vram_cache(void __force *addr)
{
eieio();
dcbf(addr);
mb();
eieio();
dcbf(addr);
mb();
}
#endif
struct fb_par_control {
int vmode, cmode;
int xres, yres;
int vxres, vyres;
int xoffset, yoffset;
int pitch;
struct control_regvals regvals;
unsigned long sync;
unsigned char ctrl;
};
#define DIRTY(z) ((x)->z != (y)->z)
#define DIRTY_CMAP(z) (memcmp(&((x)->z), &((y)->z), sizeof((y)->z)))
static inline int PAR_EQUAL(struct fb_par_control *x, struct fb_par_control *y)
{
int i, results;
results = 1;
for (i = 0; i < 3; i++)
results &= !DIRTY(regvals.clock_params[i]);
if (!results)
return 0;
for (i = 0; i < 16; i++)
results &= !DIRTY(regvals.regs[i]);
if (!results)
return 0;
return (!DIRTY(cmode) && !DIRTY(xres) && !DIRTY(yres)
&& !DIRTY(vxres) && !DIRTY(vyres));
}
struct fb_info_control {
struct fb_info info;
struct fb_par_control par;
u32 pseudo_palette[16];
struct cmap_regs __iomem *cmap_regs;
unsigned long cmap_regs_phys;
struct control_regs __iomem *control_regs;
unsigned long control_regs_phys;
unsigned long control_regs_size;
__u8 __iomem *frame_buffer;
unsigned long frame_buffer_phys;
unsigned long fb_orig_base;
unsigned long fb_orig_size;
int control_use_bank2;
unsigned long total_vram;
unsigned char vram_attr;
};
/* control register access macro */
#define CNTRL_REG(INFO,REG) (&(((INFO)->control_regs->REG).r))
/************************** Internal variables *******************************/
static struct fb_info_control *control_fb;
static int default_vmode __initdata = VMODE_NVRAM;
static int default_cmode __initdata = CMODE_NVRAM;
static int controlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int transp, struct fb_info *info)
{
struct fb_info_control *p =
container_of(info, struct fb_info_control, info);
__u8 r, g, b;
if (regno > 255)
return 1;
r = red >> 8;
g = green >> 8;
b = blue >> 8;
out_8(&p->cmap_regs->addr, regno); /* tell clut what addr to fill */
out_8(&p->cmap_regs->lut, r); /* send one color channel at */
out_8(&p->cmap_regs->lut, g); /* a time... */
out_8(&p->cmap_regs->lut, b);
if (regno < 16) {
int i;
switch (p->par.cmode) {
case CMODE_16:
p->pseudo_palette[regno] =
(regno << 10) | (regno << 5) | regno;
break;
case CMODE_32:
i = (regno << 8) | regno;
p->pseudo_palette[regno] = (i << 16) | i;
break;
}
}
return 0;
}
/******************** End of controlfb_ops implementation ******************/
static void set_control_clock(unsigned char *params)
{
#ifdef CONFIG_ADB_CUDA
struct adb_request req;
int i;
for (i = 0; i < 3; ++i) {
cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
0x50, i + 1, params[i]);
while (!req.complete)
cuda_poll();
}
#endif
}
/*
* Set screen start address according to var offset values
*/
static inline void set_screen_start(int xoffset, int yoffset,
struct fb_info_control *p)
{
struct fb_par_control *par = &p->par;
par->xoffset = xoffset;
par->yoffset = yoffset;
out_le32(CNTRL_REG(p,start_addr),
par->