Commit 250883f3 authored by David S. Miller's avatar David S. Miller

[FRAMEBUFFER]: Convert creatorfb to new APIs.

parent 743b21c5
......@@ -809,12 +809,12 @@ config FB_SBUS
help
Say Y if you want support for SBUS or UPA based frame buffer device.
config FB_CREATOR
bool "Creator/Creator3D support"
config FB_FFB
bool "Creator/Creator3D/Elite3D support"
depends on FB_SBUS && SPARC64
help
This is the frame buffer device driver for the Creator and Creator3D
graphics boards.
This is the frame buffer device driver for the Creator, Creator3D,
and Elite3D graphics boards.
config FB_TCX
bool "TCX (SS4/SS5 only) support"
......
......@@ -48,14 +48,6 @@ obj-$(CONFIG_FB_VGA16) += vga16fb.o cfbfillrect.o cfbcopyarea.o \
obj-$(CONFIG_FB_VIRGE) += virgefb.o
obj-$(CONFIG_FB_G364) += g364fb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o
obj-$(CONFIG_FB_FM2) += fm2fb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o
obj-$(CONFIG_FB_CREATOR) += creatorfb.o sbusfb.o
obj-$(CONFIG_FB_CGSIX) += cgsixfb.o sbusfb.o
obj-$(CONFIG_FB_BWTWO) += bwtwofb.o sbusfb.o
obj-$(CONFIG_FB_CGTHREE) += cgthreefb.o sbusfb.o
obj-$(CONFIG_FB_TCX) += tcxfb.o sbusfb.o
obj-$(CONFIG_FB_CGFOURTEEN) += cgfourteenfb.o sbusfb.o
obj-$(CONFIG_FB_P9100) += p9100fb.o sbusfb.o
obj-$(CONFIG_FB_LEO) += leofb.o sbusfb.o
obj-$(CONFIG_FB_STI) += stifb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o
obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o
obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o
......@@ -78,6 +70,18 @@ obj-$(CONFIG_FB_E1355) += epson1355fb.o
obj-$(CONFIG_FB_PVR2) += pvr2fb.o
obj-$(CONFIG_FB_VOODOO1) += sstfb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o
# One by one these are being converted over to the new APIs
#obj-$(CONFIG_FB_CREATOR) += creatorfb.o sbusfb.o
#obj-$(CONFIG_FB_CGSIX) += cgsixfb.o sbusfb.o
#obj-$(CONFIG_FB_BWTWO) += bwtwofb.o sbusfb.o
#obj-$(CONFIG_FB_CGTHREE) += cgthreefb.o sbusfb.o
#obj-$(CONFIG_FB_TCX) += tcxfb.o sbusfb.o
#obj-$(CONFIG_FB_CGFOURTEEN) += cgfourteenfb.o sbusfb.o
#obj-$(CONFIG_FB_P9100) += p9100fb.o sbusfb.o
#obj-$(CONFIG_FB_LEO) += leofb.o sbusfb.o
obj-$(CONFIG_FB_FFB) += ffb.o sbuslib.o cfbimgblt.o cfbcopyarea.o
# Files generated that shall be removed upon make clean
clean-files := promcon_tbl.c
......
......@@ -103,8 +103,6 @@ extern int hgafb_setup(char*);
extern int matroxfb_init(void);
extern int matroxfb_setup(char*);
extern int hpfb_init(void);
extern int sbusfb_init(void);
extern int sbusfb_setup(char*);
extern int control_init(void);
extern int control_setup(char*);
extern int platinum_init(void);
......@@ -146,6 +144,8 @@ extern int sstfb_init(void);
extern int sstfb_setup(char*);
extern int i810fb_init(void);
extern int i810fb_setup(char*);
extern int ffb_init(void);
extern int ffb_setup(char*);
static struct {
const char *name;
......@@ -153,13 +153,6 @@ static struct {
int (*setup)(char*);
} fb_drivers[] __initdata = {
#ifdef CONFIG_FB_SBUS
/*
* Sbusfb must be initialized _before_ other frame buffer devices that
* use PCI probing
*/
{ "sbus", sbusfb_init, sbusfb_setup },
#endif
/*
* Chipset specific drivers that use resource management
*/
......@@ -244,6 +237,9 @@ static struct {
#ifdef CONFIG_FB_STI
{ "stifb", stifb_init, stifb_setup },
#endif
#ifdef CONFIG_FB_FFB
{ "ffb", ffb_init, ffb_setup },
#endif
/*
* Generic drivers that are used as fallbacks
......
/* $Id: creatorfb.c,v 1.37 2001/10/16 05:44:44 davem Exp $
* creatorfb.c: Creator/Creator3D frame buffer driver
/* ffb.c: Creator/Elite3D frame buffer driver
*
* Copyright (C) 2003 David S. Miller (davem@redhat.com)
* Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
*
* Driver layout based loosely on tgafb.c, see that file for credits.
*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/selection.h>
#include <video/sbusfb.h>
#include <linux/fb.h>
#include <linux/mm.h>
#include <linux/timer.h>
#include <asm/io.h>
#include <asm/upa.h>
#include <asm/oplib.h>
#include "sbuslib.h"
/*
* Local functions.
*/
static int ffb_check_var(struct fb_var_screeninfo *, struct fb_info *);
static int ffb_set_par(struct fb_info *);
static int ffb_setcolreg(unsigned, unsigned, unsigned, unsigned,
unsigned, struct fb_info *);
static int ffb_blank(int, struct fb_info *);
static void ffb_init_fix(struct fb_info *);
static void ffb_imageblit(struct fb_info *, struct fb_image *);
static void ffb_fillrect(struct fb_info *, struct fb_fillrect *);
static void ffb_copyarea(struct fb_info *, struct fb_copyarea *);
static int ffb_sync(struct fb_info *);
static int ffb_mmap(struct fb_info *, struct file *, struct vm_area_struct *);
/*
* Frame buffer operations
*/
static struct fb_ops ffb_ops = {
.owner = THIS_MODULE,
.fb_check_var = ffb_check_var,
.fb_set_par = ffb_set_par,
.fb_setcolreg = ffb_setcolreg,
.fb_blank = ffb_blank,
.fb_fillrect = ffb_fillrect,
.fb_copyarea = ffb_copyarea,
.fb_imageblit = ffb_imageblit,
.fb_sync = ffb_sync,
.fb_mmap = ffb_mmap,
/* XXX Use FFB hw cursor once fb cursor API is better understood... */
.fb_cursor = soft_cursor,
};
/* Register layout and definitions */
#define FFB_SFB8R_VOFF 0x00000000
#define FFB_SFB8G_VOFF 0x00400000
#define FFB_SFB8B_VOFF 0x00800000
......@@ -132,6 +170,8 @@
#define FFB_PPC_CS_CONST 0x000003
#define FFB_ROP_NEW 0x83
#define FFB_ROP_OLD 0x85
#define FFB_ROP_NEW_XOR_OLD 0x86
#define FFB_UCSR_FIFO_MASK 0x00000fff
#define FFB_UCSR_FB_BUSY 0x01000000
......@@ -287,493 +327,529 @@ struct ffb_fbc {
volatile u32 mer;
};
static __inline__ void FFBFifo(struct fb_info_sbusfb *fb, int n)
struct ffb_dac {
volatile u32 type;
volatile u32 value;
volatile u32 type2;
volatile u32 value2;
};
struct ffb_par {
spinlock_t lock;
struct ffb_fbc *fbc;
struct ffb_dac *dac;
u32 flags;
#define FFB_FLAG_AFB 0x00000001
#define FFB_FLAG_BLANKED 0x00000002
u32 fg_cache __attribute__((aligned (8)));
u32 bg_cache;
u32 rop_cache;
int fifo_cache;
unsigned long physbase;
unsigned long fbsize;
char name[64];
int prom_node;
int prom_parent_node;
int dac_rev;
int board_type;
struct list_head list;
};
#undef FFB_DO_DEBUG_LOG
#ifdef FFB_DO_DEBUG_LOG
#define FFB_DEBUG_LOG_ENTS 32
static struct ffb_log {
int op;
#define OP_FILLRECT 1
#define OP_IMAGEBLIT 2
int depth, x, y, w, h;
} ffb_debug_log[FFB_DEBUG_LOG_ENTS];
static int ffb_debug_log_ent;
static void ffb_do_log(unsigned long unused)
{
int i;
for (i = 0; i < FFB_DEBUG_LOG_ENTS; i++) {
struct ffb_log *p = &ffb_debug_log[i];
printk("FFB_LOG: OP[%s] depth(%d) x(%d) y(%d) w(%d) h(%d)\n",
(p->op == OP_FILLRECT ? "FILLRECT" : "IMAGEBLIT"),
p->depth, p->x, p->y, p->w, p->h);
}
}
static struct timer_list ffb_log_timer =
TIMER_INITIALIZER(ffb_do_log, 0, 0);
static void ffb_log(int op, int depth, int x, int y, int w, int h)
{
if (ffb_debug_log_ent < FFB_DEBUG_LOG_ENTS) {
struct ffb_log *p = &ffb_debug_log[ffb_debug_log_ent];
if (ffb_debug_log_ent != 0 &&
p[-1].op == op && p[-1].depth == depth)
return;
p->op = op;
p->depth = depth;
p->x = x;
p->y = y;
p->w = w;
p->h = h;
if (++ffb_debug_log_ent == FFB_DEBUG_LOG_ENTS) {
ffb_log_timer.expires = jiffies + 2;
add_timer(&ffb_log_timer);
}
}
}
#else
#define ffb_log(a,b,c,d,e,f) do { } while(0)
#endif
#undef FORCE_WAIT_EVERY_ROP
static void FFBFifo(struct ffb_par *par, int n)
{
struct ffb_fbc *fbc;
int cache = fb->s.ffb.fifo_cache;
int cache = par->fifo_cache;
if (cache - n < 0) {
fbc = fb->s.ffb.fbc;
fbc = par->fbc;
do { cache = (upa_readl(&fbc->ucsr) & FFB_UCSR_FIFO_MASK) - 8;
} while (cache - n < 0);
}
fb->s.ffb.fifo_cache = cache - n;
par->fifo_cache = cache - n;
}
static __inline__ void FFBWait(struct ffb_fbc *ffb)
static void FFBWait(struct ffb_par *par)
{
struct ffb_fbc *fbc;
int limit = 10000;
fbc = par->fbc;
do {
if ((upa_readl(&ffb->ucsr) & FFB_UCSR_ALL_BUSY) == 0)
if ((upa_readl(&fbc->ucsr) & FFB_UCSR_ALL_BUSY) == 0)
break;
if ((upa_readl(&ffb->ucsr) & FFB_UCSR_ALL_ERRORS) != 0) {
upa_writel(FFB_UCSR_ALL_ERRORS, &ffb->ucsr);
if ((upa_readl(&fbc->ucsr) & FFB_UCSR_ALL_ERRORS) != 0) {
upa_writel(FFB_UCSR_ALL_ERRORS, &fbc->ucsr);
}
udelay(10);
} while(--limit > 0);
}
struct ffb_dac {
volatile u32 type;
volatile u32 value;
volatile u32 type2;
volatile u32 value2;
};
static int ffb_sync(struct fb_info *p)
{
struct ffb_par *par = (struct ffb_par *) p->par;
static struct sbus_mmap_map ffb_mmap_map[] = {
{ FFB_SFB8R_VOFF, FFB_SFB8R_POFF, 0x0400000 },
{ FFB_SFB8G_VOFF, FFB_SFB8G_POFF, 0x0400000 },
{ FFB_SFB8B_VOFF, FFB_SFB8B_POFF, 0x0400000 },
{ FFB_SFB8X_VOFF, FFB_SFB8X_POFF, 0x0400000 },
{ FFB_SFB32_VOFF, FFB_SFB32_POFF, 0x1000000 },
{ FFB_SFB64_VOFF, FFB_SFB64_POFF, 0x2000000 },
{ FFB_FBC_REGS_VOFF, FFB_FBC_REGS_POFF, 0x0002000 },
{ FFB_BM_FBC_REGS_VOFF, FFB_BM_FBC_REGS_POFF, 0x0002000 },
{ FFB_DFB8R_VOFF, FFB_DFB8R_POFF, 0x0400000 },
{ FFB_DFB8G_VOFF, FFB_DFB8G_POFF, 0x0400000 },
{ FFB_DFB8B_VOFF, FFB_DFB8B_POFF, 0x0400000 },
{ FFB_DFB8X_VOFF, FFB_DFB8X_POFF, 0x0400000 },
{ FFB_DFB24_VOFF, FFB_DFB24_POFF, 0x1000000 },
{ FFB_DFB32_VOFF, FFB_DFB32_POFF, 0x1000000 },
{ FFB_FBC_KREGS_VOFF, FFB_FBC_KREGS_POFF, 0x0002000 },
{ FFB_DAC_VOFF, FFB_DAC_POFF, 0x0002000 },
{ FFB_PROM_VOFF, FFB_PROM_POFF, 0x0010000 },
{ FFB_EXP_VOFF, FFB_EXP_POFF, 0x0002000 },
{ FFB_DFB422A_VOFF, FFB_DFB422A_POFF, 0x0800000 },
{ FFB_DFB422AD_VOFF, FFB_DFB422AD_POFF, 0x0800000 },
{ FFB_DFB24B_VOFF, FFB_DFB24B_POFF, 0x1000000 },
{ FFB_DFB422B_VOFF, FFB_DFB422B_POFF, 0x0800000 },
{ FFB_DFB422BD_VOFF, FFB_DFB422BD_POFF, 0x0800000 },
{ FFB_SFB16Z_VOFF, FFB_SFB16Z_POFF, 0x0800000 },
{ FFB_SFB8Z_VOFF, FFB_SFB8Z_POFF, 0x0800000 },
{ FFB_SFB422_VOFF, FFB_SFB422_POFF, 0x0800000 },
{ FFB_SFB422D_VOFF, FFB_SFB422D_POFF, 0x0800000 },
{ 0, 0, 0 }
};
FFBWait(par);
return 0;
}
static void ffb_setup(struct display *p)
static __inline__ void ffb_rop(struct ffb_par *par, u32 rop)
{
p->next_line = 8192;
p->next_plane = 0;
if (par->rop_cache != rop) {
FFBFifo(par, 1);
upa_writel(rop, &par->fbc->rop);
par->rop_cache = rop;
}
}
static void ffb_clear(struct vc_data *conp, struct display *p, int sy, int sx,
int height, int width)
static void ffb_switch_from_graph(struct ffb_par *par)
{
struct fb_info_sbusfb *fb = sbusfbinfo(p->fb_info);
register struct ffb_fbc *fbc = fb->s.ffb.fbc;
struct ffb_fbc *fbc = par->fbc;
unsigned long flags;
u64 yx, hw;
int fg;
spin_lock_irqsave(&fb->lock, flags);
fg = ((u32 *)p->dispsw_data)[attr_bgcol_ec(p,conp)];
if (fg != fb->s.ffb.fg_cache) {
FFBFifo(fb, 5);
upa_writel(fg, &fbc->fg);
fb->s.ffb.fg_cache = fg;
} else
FFBFifo(fb, 4);
if (fontheightlog(p)) {
yx = (u64)sy << (fontheightlog(p) + 32); hw = (u64)height << (fontheightlog(p) + 32);
} else {
yx = (u64)(sy * fontheight(p)) << 32; hw = (u64)(height * fontheight(p)) << 32;
}
if (fontwidthlog(p)) {
yx += sx << fontwidthlog(p); hw += width << fontwidthlog(p);
} else {
yx += sx * fontwidth(p); hw += width * fontwidth(p);
}
upa_writeq(yx + fb->s.ffb.yx_margin, &fbc->by);
upa_writeq(hw, &fbc->bh);
spin_unlock_irqrestore(&fb->lock, flags);
spin_lock_irqsave(&par->lock, flags);
FFBWait(par);
par->fifo_cache = 0;
FFBFifo(par, 7);
upa_writel(FFB_PPC_VCE_DISABLE|FFB_PPC_TBE_OPAQUE|
FFB_PPC_APE_DISABLE|FFB_PPC_CS_CONST,
&fbc->ppc);
upa_writel(0x2000707f, &fbc->fbc);
upa_writel(par->rop_cache, &fbc->rop);
upa_writel(0xffffffff, &fbc->pmask);
upa_writel((0 << 16) | (32 << 0), &fbc->fontinc);
upa_writel(par->fg_cache, &fbc->fg);
upa_writel(par->bg_cache, &fbc->bg);
FFBWait(par);
spin_unlock_irqrestore(&par->lock, flags);
}
static void ffb_fill(struct fb_info_sbusfb *fb, struct display *p, int s,
int count, unsigned short *boxes)
/**
* ffb_fillrect - REQUIRED function. Can use generic routines if
* non acclerated hardware and packed pixel based.
* Draws a rectangle on the screen.
*
* @info: frame buffer structure that represents a single frame buffer
* @rect: structure defining the rectagle and operation.
*/
static void ffb_fillrect(struct fb_info *info, struct fb_fillrect *rect)
{
register struct ffb_fbc *fbc = fb->s.ffb.fbc;
struct ffb_par *par = (struct ffb_par *) info->par;
struct ffb_fbc *fbc = par->fbc;
unsigned long flags;
int fg;
u32 fg;
if (rect->rop != ROP_COPY && rect->rop != ROP_XOR)
BUG();
ffb_log(OP_FILLRECT, 0, rect->dx, rect->dy, rect->width, rect->height);
fg = ((u32 *)info->pseudo_palette)[rect->color];
spin_lock_irqsave(&par->lock, flags);
spin_lock_irqsave(&fb->lock, flags);
fg = ((u32 *)p->dispsw_data)[attr_bgcol(p,s)];
if (fg != fb->s.ffb.fg_cache) {
FFBFifo(fb, 1);
if (fg != par->fg_cache) {
FFBFifo(par, 1);
upa_writel(fg, &fbc->fg);
fb->s.ffb.fg_cache = fg;
par->fg_cache = fg;
}
while (count-- > 0) {
FFBFifo(fb, 4);
upa_writel(boxes[1], &fbc->by);
upa_writel(boxes[0], &fbc->bx);
upa_writel(boxes[3] - boxes[1], &fbc->bh);
upa_writel(boxes[2] - boxes[0], &fbc->bw);
boxes += 4;
}
spin_unlock_irqrestore(&fb->lock, flags);
ffb_rop(par, (rect->rop == ROP_COPY ?
FFB_ROP_NEW :
FFB_ROP_NEW_XOR_OLD));
FFBFifo(par, 5);
upa_writel(FFB_DRAWOP_RECTANGLE, &fbc->drawop);
upa_writel(rect->dy, &fbc->by);
upa_writel(rect->dx, &fbc->bx);
upa_writel(rect->height, &fbc->bh);
upa_writel(rect->width, &fbc->bw);
#ifdef FORCE_WAIT_EVERY_ROP
FFBWait(par);
#endif
spin_unlock_irqrestore(&par->lock, flags);
}
static void ffb_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx)
/**
* ffb_copyarea - REQUIRED function. Can use generic routines if
* non acclerated hardware and packed pixel based.
* Copies on area of the screen to another area.
*
* @info: frame buffer structure that represents a single frame buffer
* @area: structure defining the source and destination.
*/
static void
ffb_copyarea(struct fb_info *info, struct fb_copyarea *area)
{
struct fb_info_sbusfb *fb = sbusfbinfo(p->fb_info);
register struct ffb_fbc *fbc = fb->s.ffb.fbc;
struct ffb_par *par = (struct ffb_par *) info->par;
struct ffb_fbc *fbc = par->fbc;
unsigned long flags;
int i, xy;
u8 *fd;
u64 fgbg;
spin_lock_irqsave(&fb->lock, flags);
if (fontheightlog(p)) {
xy = (yy << (16 + fontheightlog(p)));
i = ((c & p->charmask) << fontheightlog(p));
} else {
xy = ((yy * fontheight(p)) << 16);
i = (c & p->charmask) * fontheight(p);
if (area->dx != area->sx ||
area->dy == area->dy) {
cfb_copyarea(info, area);
return;
}
if (fontwidth(p) <= 8)
fd = p->fontdata + i;
else
fd = p->fontdata + (i << 1);
if (fontwidthlog(p))
xy += (xx << fontwidthlog(p)) + fb->s.ffb.xy_margin;
else
xy += (xx * fontwidth(p)) + fb->s.ffb.xy_margin;
fgbg = (((u64)(((u32 *)p->dispsw_data)[attr_fgcol(p,c)])) << 32) |
((u32 *)p->dispsw_data)[attr_bgcol(p,c)];
if (fgbg != *(u64 *)&fb->s.ffb.fg_cache) {
FFBFifo(fb, 2);
upa_writeq(fgbg, &fbc->fg);
*(u64 *)&fb->s.ffb.fg_cache = fgbg;
}
FFBFifo(fb, 2 + fontheight(p));
upa_writel(xy, &fbc->fontxy);
upa_writel(fontwidth(p), &fbc->fontw);
if (fontwidth(p) <= 8) {
for (i = 0; i < fontheight(p); i++) {
u32 val = *fd++ << 24;
upa_writel(val, &fbc->font);
}
} else {
for (i = 0; i < fontheight(p); i++) {
u32 val = *(u16 *)fd << 16;
spin_lock_irqsave(&par->lock, flags);
upa_writel(val, &fbc->font);
fd += 2;
}
}
spin_unlock_irqrestore(&fb->lock, flags);
ffb_rop(par, FFB_ROP_OLD);
FFBFifo(par, 7);
upa_writel(FFB_DRAWOP_VSCROLL, &fbc->drawop);
upa_writel(area->sy, &fbc->by);
upa_writel(area->sx, &fbc->bx);
upa_writel(area->dy, &fbc->dy);
upa_writel(area->dx, &fbc->dx);
upa_writel(area->height, &fbc->bh);
upa_writel(area->width, &fbc->bw);
spin_unlock_irqrestore(&par->lock, flags);
}
static void ffb_putcs(struct vc_data *conp, struct display *p, const unsigned short *s,
int count, int yy, int xx)
/**
* ffb_imageblit - REQUIRED function. Can use generic routines if
* non acclerated hardware and packed pixel based.
* Copies a image from system memory to the screen.
*
* @info: frame buffer structure that represents a single frame buffer
* @image: structure defining the image.
*/
static void ffb_imageblit(struct fb_info *info, struct fb_image *image)
{
struct fb_info_sbusfb *fb = sbusfbinfo(p->fb_info);
register struct ffb_fbc *fbc = fb->s.ffb.fbc;
struct ffb_par *par = (struct ffb_par *) info->par;
struct ffb_fbc *fbc = par->fbc;
u8 *data = (u8 *) image->data;
unsigned long flags;
int i, xy;
u8 *fd1, *fd2, *fd3, *fd4;
u16 c;
u32 fg, bg, xy;
u64 fgbg;
int i, width;
spin_lock_irqsave(&fb->lock, flags);
c = scr_readw(s);
fgbg = (((u64)(((u32 *)p->dispsw_data)[attr_fgcol(p, c)])) << 32) |
((u32 *)p->dispsw_data)[attr_bgcol(p, c)];
if (fgbg != *(u64 *)&fb->s.ffb.fg_cache) {
FFBFifo(fb, 2);
upa_writeq(fgbg, &fbc->fg);
*(u64 *)&fb->s.ffb.fg_cache = fgbg;
ffb_log(OP_IMAGEBLIT, image->depth,
image->dx, image->dy, image->width, image->height);
if (image->depth > 1) {
cfb_imageblit(info, image);
return;
}
xy = fb->s.ffb.xy_margin;
if (fontwidthlog(p))
xy += (xx << fontwidthlog(p));
else
xy += xx * fontwidth(p);
if (fontheightlog(p))
xy += (yy << (16 + fontheightlog(p)));
else
xy += ((yy * fontheight(p)) << 16);
if (fontwidth(p) <= 8) {
while (count >= 4) {
count -= 4;
FFBFifo(fb, 2 + fontheight(p));
upa_writel(4 * fontwidth(p), &fbc->fontw);
upa_writel(xy, &fbc->fontxy);
if (fontheightlog(p)) {
fd1 = p->fontdata + ((scr_readw(s++) & p->charmask) << fontheightlog(p));
fd2 = p->fontdata + ((scr_readw(s++) & p->charmask) << fontheightlog(p));
fd3 = p->fontdata + ((scr_readw(s++) & p->charmask) << fontheightlog(p));
fd4 = p->fontdata + ((scr_readw(s++) & p->charmask) << fontheightlog(p));
} else {
fd1 = p->fontdata + ((scr_readw(s++) & p->charmask) * fontheight(p));
fd2 = p->fontdata + ((scr_readw(s++) & p->charmask) * fontheight(p));
fd3 = p->fontdata + ((scr_readw(s++) & p->charmask) * fontheight(p));
fd4 = p->fontdata + ((scr_readw(s++) & p->charmask) * fontheight(p));
}
if (fontwidth(p) == 8) {
for (i = 0; i < fontheight(p); i++) {
u32 val;
val = ((u32)*fd4++) | ((((u32)*fd3++) | ((((u32)*fd2++) | (((u32)*fd1++)
<< 8)) << 8)) << 8);
upa_writel(val, &fbc->font);
}
xy += 32;
} else {
for (i = 0; i < fontheight(p); i++) {
u32 val = (((u32)*fd4++) | ((((u32)*fd3++) | ((((u32)*fd2++) | (((u32)*fd1++)
<< fontwidth(p))) << fontwidth(p))) << fontwidth(p))) << (24 - 3 * fontwidth(p));
upa_writel(val, &fbc->font);
}
xy += 4 * fontwidth(p);
}
}
} else {
while (count >= 2) {
count -= 2;
FFBFifo(fb, 2 + fontheight(p));
upa_writel(2 * fontwidth(p), &fbc->fontw);
upa_writel(xy, &fbc->fontxy);
if (fontheightlog(p)) {
fd1 = p->fontdata + ((scr_readw(s++) & p->charmask) << (fontheightlog(p) + 1));
fd2 = p->fontdata + ((scr_readw(s++) & p->charmask) << (fontheightlog(p) + 1));
} else {
fd1 = p->fontdata + (((scr_readw(s++) & p->charmask) * fontheight(p)) << 1);
fd2 = p->fontdata + (((scr_readw(s++) & p->charmask) * fontheight(p)) << 1);
}
for (i = 0; i < fontheight(p); i++) {
u32 val = ((((u32)*(u16 *)fd1) << fontwidth(p)) | ((u32)*(u16 *)fd2)) << (16 - fontwidth(p));
upa_writel(val, &fbc->font);
fd1 += 2; fd2 += 2;
}
xy += 2 * fontwidth(p);
}
fg = ((u32 *)info->pseudo_palette)[image->fg_color];
bg = ((u32 *)info->pseudo_palette)[image->bg_color];
fgbg = ((u64) fg << 32) | (u64) bg;
xy = (image->dy << 16) | image->dx;
spin_lock_irqsave(&par->lock, flags);
if (fgbg != *(u64 *)&par->fg_cache) {
FFBFifo(par, 2);
upa_writeq(fgbg, &fbc->fg);
*(u64 *)&par->fg_cache = fgbg;
}
while (count) {
count--;
FFBFifo(fb, 2 + fontheight(p));
upa_writel(fontwidth(p), &fbc->fontw);
ffb_rop(par, FFB_ROP_NEW);
for (i = 0; i < image->height; i++) {
width = image->width;
FFBFifo(par, 1);
upa_writel(xy, &fbc->fontxy);
if (fontheightlog(p))
i = ((scr_readw(s++) & p->charmask) << fontheightlog(p));
else
i = ((scr_readw(s++) & p->charmask) * fontheight(p));
if (fontwidth(p) <= 8) {
fd1 = p->fontdata + i;
for (i = 0; i < fontheight(p); i++) {
u32 val = *fd1++ << 24;
xy += (1 << 16);
upa_writel(val, &fbc->font);
}
} else {
fd1 = p->fontdata + (i << 1);
for (i = 0; i < fontheight(p); i++) {
u32 val = *(u16 *)fd1 << 16;
while (width >= 32) {
u32 val;
FFBFifo(par, 2);
upa_writel(32, &fbc->fontw);
val = ((u32)data[0] << 24) |
((u32)data[1] << 16) |
((u32)data[2] << 8) |
((u32)data[3] << 0);
upa_writel(val, &fbc->font);
data += 4;
width -= 32;
}
upa_writel(val, &fbc->font);
fd1 += 2;
if (width) {
u32 val;
FFBFifo(par, 2);
upa_writel(width, &fbc->fontw);
if (width <= 8) {
val = (u32) data[0] << 24;
data += 1;
} else if (width <= 16) {
val = ((u32) data[0] << 24) |
((u32) data[1] << 16);
data += 2;
} else {
val = ((u32) data[0] << 24) |
((u32) data[1] << 16) |
((u32) data[2] << 8);
data += 3;
}
upa_writel(val, &fbc->font);
}
xy += fontwidth(p);
}
spin_unlock_irqrestore(&fb->lock, flags);
#ifdef FORCE_WAIT_EVERY_ROP
FFBWait(par);
#endif
spin_unlock_irqrestore(&par->lock, flags);
}
static void ffb_revc(struct display *p, int xx, int yy)
static void ffb_fixup_var_rgb(struct fb_var_screeninfo *var)
{
/* Not used if hw cursor */
var->red.offset = 0;
var->red.length = 8;
var->green.offset = 8;
var->green.length = 8;
var->blue.offset = 16;
var->blue.length = 8;
var->transp.offset = 0;
var->transp.length = 0;
}
#if 0
static int ffb_blank(struct fb_info_sbusfb *fb)
/**
* ffb_check_var - Optional function. Validates a var passed in.
* @var: frame buffer variable screen structure
* @info: frame buffer structure that represents a single frame buffer
*/
static int ffb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
struct ffb_dac *dac = fb->s.ffb.dac;
unsigned long flags;
u32 tmp;
if (var->bits_per_pixel != 32)
return -EINVAL;
if (var->xres_virtual != var->xres || var->yres_virtual != var->yres)
return -EINVAL;
if (var->nonstd)
return -EINVAL;
if ((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
return -EINVAL;
if (var->xres != info->var.xres || var->yres != info->var.yres)
return -EINVAL;
ffb_fixup_var_rgb(var);
spin_lock_irqsave(&fb->lock, flags);
upa_writel(0x6000, &dac->type);
tmp = (upa_readl(&dac->value) & ~0x1);
upa_writel(0x6000, &dac->type);
upa_writel(tmp, &dac->value);
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
}
#endif
static int ffb_unblank(struct fb_info_sbusfb *fb)
/**
* ffb_set_par - Optional function. Alters the hardware state.
* @info: frame buffer structure that represents a single frame buffer
*/
static int
ffb_set_par(struct fb_info *info)
{
struct ffb_dac *dac = fb->s.ffb.dac;
unsigned long flags;
u32 tmp;
return 0;
}
/**
* ffb_setcolreg - Optional function. Sets a color register.
* @regno: boolean, 0 copy local, 1 get_user() function
* @red: frame buffer colormap structure
* @green: The green value which can be up to 16 bits wide
* @blue: The blue value which can be up to 16 bits wide.
* @transp: If supported the alpha value which can be up to 16 bits wide.
* @info: frame buffer info structure
*/
static int ffb_setcolreg(unsigned regno,
unsigned red, unsigned green, unsigned blue,
unsigned transp, struct fb_info *info)
{
u32 value;
if (regno >= 256)
return 1;
red >>= 8;
green >>= 8;
blue >>= 8;
value = (blue << 16) | (green << 8) | red;
((u32 *)info->pseudo_palette)[regno] = value;
spin_lock_irqsave(&fb->lock, flags);
upa_writel(0x6000, &dac->type);
tmp = (upa_readl(&dac->value) | 0x1);
upa_writel(0x6000, &dac->type);
upa_writel(tmp, &dac->value);
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
}
static void ffb_loadcmap (struct fb_info_sbusfb *fb, struct display *p, int index, int count)
/**
* ffb_blank - Optional function. Blanks the display.
* @blank_mode: the blank mode we want.
* @info: frame buffer structure that represents a single frame buffer
*/
static int
ffb_blank(int blank, struct fb_info *info)
{
struct ffb_dac *dac = fb->s.ffb.dac;
struct ffb_par *par = (struct ffb_par *) info->par;
struct ffb_dac *dac = par->dac;
unsigned long flags;
int i, j = count;
spin_lock_irqsave(&fb->lock, flags);
upa_writel(0x2000 | index, &dac->type);
for (i = index; j--; i++) {
u32 val;
/* Feed the colors in :)) */
val = ((fb->color_map CM(i,0))) |
((fb->color_map CM(i,1)) << 8) |
((fb->color_map CM(i,2)) << 16);
upa_writel(val, &dac->value);
u32 tmp;
spin_lock_irqsave(&par->lock, flags);
FFBWait(par);
switch (blank) {
case 0: /* Unblanking */
upa_writel(0x6000, &dac->type);
tmp = (upa_readl(&dac->value) | 0x1);
upa_writel(0x6000, &dac->type);
upa_writel(tmp, &dac->value);
par->flags &= ~FFB_FLAG_BLANKED;
break;
case 1: /* Normal blanking */
case 2: /* VESA blank (vsync off) */
case 3: /* VESA blank (hsync off) */
case 4: /* Poweroff */
upa_writel(0x6000, &dac->type);
tmp = (upa_readl(&dac->value) & ~0x1);
upa_writel(0x6000, &dac->type);
upa_writel(tmp, &dac->value);
par->flags |= FFB_FLAG_BLANKED;
break;
}
if (!p)
goto out;
for (i = index, j = count; i < 16 && j--; i++)
((u32 *)p->dispsw_data)[i] = ((fb->color_map CM(i,0))) |
((fb->color_map CM(i,1)) << 8) |
((fb->color_map CM(i,2)) << 16);
out:
spin_unlock_irqrestore(&fb->lock, flags);
spin_unlock_irqrestore(&par->lock, flags);
return 0;
}
static struct display_switch ffb_dispsw __initdata = {
.setup = ffb_setup,
.bmove = fbcon_redraw_bmove,
.clear = ffb_clear,
.putc = ffb_putc,
.putcs = ffb_putcs,
.revc = ffb_revc,
.fontwidthmask =FONTWIDTHRANGE(1,16) /* Allow fontwidths up to 16 */
static struct sbus_mmap_map ffb_mmap_map[] = {
{ FFB_SFB8R_VOFF, FFB_SFB8R_POFF, 0x0400000 },
{ FFB_SFB8G_VOFF, FFB_SFB8G_POFF, 0x0400000 },
{ FFB_SFB8B_VOFF, FFB_SFB8B_POFF, 0x0400000 },
{ FFB_SFB8X_VOFF, FFB_SFB8X_POFF, 0x0400000 },
{ FFB_SFB32_VOFF, FFB_SFB32_POFF, 0x1000000 },
{ FFB_SFB64_VOFF, FFB_SFB64_POFF, 0x2000000 },
{ FFB_FBC_REGS_VOFF, FFB_FBC_REGS_POFF, 0x0002000 },
{ FFB_BM_FBC_REGS_VOFF, FFB_BM_FBC_REGS_POFF, 0x0002000 },
{ FFB_DFB8R_VOFF, FFB_DFB8R_POFF, 0x0400000 },
{ FFB_DFB8G_VOFF, FFB_DFB8G_POFF, 0x0400000 },
{ FFB_DFB8B_VOFF, FFB_DFB8B_POFF, 0x0400000 },
{ FFB_DFB8X_VOFF, FFB_DFB8X_POFF, 0x0400000 },
{ FFB_DFB24_VOFF, FFB_DFB24_POFF, 0x1000000 },
{ FFB_DFB32_VOFF, FFB_DFB32_POFF, 0x1000000 },
{ FFB_FBC_KREGS_VOFF, FFB_FBC_KREGS_POFF, 0x0002000 },
{ FFB_DAC_VOFF, FFB_DAC_POFF, 0x0002000 },
{ FFB_PROM_VOFF, FFB_PROM_POFF, 0x0010000 },
{ FFB_EXP_VOFF, FFB_EXP_POFF, 0x0002000 },
{ FFB_DFB422A_VOFF, FFB_DFB422A_POFF, 0x0800000 },
{ FFB_DFB422AD_VOFF, FFB_DFB422AD_POFF, 0x0800000 },
{ FFB_DFB24B_VOFF, FFB_DFB24B_POFF, 0x1000000 },
{ FFB_DFB422B_VOFF, FFB_DFB422B_POFF, 0x0800000 },
{ FFB_DFB422BD_VOFF, FFB_DFB422BD_POFF, 0x0800000 },
{ FFB_SFB16Z_VOFF, FFB_SFB16Z_POFF, 0x0800000 },
{ FFB_SFB8Z_VOFF, FFB_SFB8Z_POFF, 0x0800000 },
{ FFB_SFB422_VOFF, FFB_SFB422_POFF, 0x0800000 },
{ FFB_SFB422D_VOFF, FFB_SFB422D_POFF, 0x0800000 },
{ 0, 0, 0 }
};
static void ffb_margins (struct fb_info_sbusfb *fb, struct display *p, int x_margin, int y_margin)
static int ffb_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
{
register struct ffb_fbc *fbc = fb->s.ffb.fbc;
unsigned long flags;
struct ffb_par *par = (struct ffb_par *)info->par;
spin_lock_irqsave(&fb->lock, flags);
fb->s.ffb.xy_margin = (y_margin << 16) + x_margin;
fb->s.ffb.yx_margin = (((u64)y_margin) << 32) + x_margin;
fb->info.screen_base += 8192 * (y_margin - fb->y_margin) + 4 * (x_margin - fb->x_margin);
FFBWait(fbc);
spin_unlock_irqrestore(&fb->lock, flags);
return sbusfb_mmap_helper(ffb_mmap_map,
par->physbase, par->fbsize,
0, vma);
}
static __inline__ void __ffb_curs_enable (struct fb_info_sbusfb *fb, int enable)
{
struct ffb_dac *dac = fb->s.ffb.dac;
u32 val;
upa_writel(0x100, &dac->type2);
if (fb->s.ffb.dac_rev <= 2) {
val = enable ? 3 : 0;
} else {
val = enable ? 0 : 3;
}
upa_writel(val, &dac->value2);
}
/*
* Initialisation
*/
static void ffb_setcursormap (struct fb_info_sbusfb *fb, u8 *red, u8 *green, u8 *blue)
static void
ffb_init_fix(struct fb_info *info)
{
struct ffb_dac *dac = fb->s.ffb.dac;
unsigned long flags;
spin_lock_irqsave(&fb->lock, flags);
__ffb_curs_enable (fb, 0);
upa_writel(0x102, &dac->type2);
upa_writel((red[0] | (green[0]<<8) | (blue[0]<<16)), &dac->value2);
upa_writel((red[1] | (green[1]<<8) | (blue[1]<<16)), &dac->value2);
spin_unlock_irqrestore(&fb->lock, flags);
}
struct ffb_par *par = (struct ffb_par *)info->par;
const char *ffb_type_name;
/* Set cursor shape */
static void ffb_setcurshape (struct fb_info_sbusfb *fb)
{
struct ffb_dac *dac = fb->s.ffb.dac;
unsigned long flags;
int i, j;
spin_lock_irqsave(&fb->lock, flags);
__ffb_curs_enable (fb, 0);
for (j = 0; j < 2; j++) {
u32 val = j ? 0 : 0x80;
upa_writel(val, &dac->type2);
for (i = 0; i < 0x40; i++) {
if (fb->cursor.size.fbx <= 32) {
upa_writel(fb->cursor.bits [j][i], &dac->value2);
upa_writel(0, &dac->value2);
} else {
upa_writel(fb->cursor.bits [j][2*i], &dac->value2);
upa_writel(fb->cursor.bits [j][2*i+1], &dac->value2);
}
}
}
spin_unlock_irqrestore(&fb->lock, flags);
}
if (!(par->flags & FFB_FLAG_AFB)) {
if ((par->board_type & 0x7) == 0x3)
ffb_type_name = "Creator 3D";
else
ffb_type_name = "Creator";
} else
ffb_type_name = "Elite 3D";
/* Load cursor information */
static void ffb_setcursor (struct fb_info_sbusfb *fb)
{
struct ffb_dac *dac = fb->s.ffb.dac;
struct cg_cursor *c = &fb->cursor;
unsigned long flags;
u32 val;
spin_lock_irqsave(&fb->lock, flags);
upa_writel(0x104, &dac->type2);
/* Should this be just 0x7ff??
Should I do some margin handling and setcurshape in that case? */
val = (((c->cpos.fby - c->chot.fby) & 0xffff) << 16)
|((c->cpos.fbx - c->chot.fbx) & 0xffff);
upa_writel(val, &dac->value2);
__ffb_curs_enable (fb, fb->cursor.enable);
spin_unlock_irqrestore(&fb->lock, flags);
}
strncpy(info->fix.id, ffb_type_name, sizeof(info->fix.id) - 1);
info->fix.id[sizeof(info->fix.id)-1] = 0;
static void ffb_switch_from_graph (struct fb_info_sbusfb *fb)
{
register struct ffb_fbc *fbc = fb->s.ffb.fbc;
unsigned long flags;
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_TRUECOLOR;
spin_lock_irqsave(&fb->lock, flags);
FFBWait(fbc);
fb->s.ffb.fifo_cache = 0;
FFBFifo(fb, 8);
upa_writel(FFB_PPC_VCE_DISABLE|FFB_PPC_TBE_OPAQUE|
FFB_PPC_APE_DISABLE|FFB_PPC_CS_CONST,
&fbc->ppc);
upa_writel(0x2000707f, &fbc->fbc);
upa_writel(FFB_ROP_NEW, &fbc->rop);
upa_writel(FFB_DRAWOP_RECTANGLE, &fbc->drawop);
upa_writel(0xffffffff, &fbc->pmask);
upa_writel(0x10000, &fbc->fontinc);
upa_writel(fb->s.ffb.fg_cache, &fbc->fg);
upa_writel(fb->s.ffb.bg_cache, &fbc->bg);
FFBWait(fbc);
spin_unlock_irqrestore(&fb->lock, flags);
}
/* Framebuffer length is the same regardless of resolution. */
info->fix.line_length = 8192;
static int __init ffb_rasterimg (struct fb_info *info, int start)
{
ffb_switch_from_graph (sbusfbinfo(info));
return 0;
info->fix.accel = FB_ACCEL_SUN_CREATOR;
}
static char idstring[60] __initdata = { 0 };
static int __init creator_apply_upa_parent_ranges(int parent, struct linux_prom64_registers *regs)
static int ffb_apply_upa_parent_ranges(int parent,
struct linux_prom64_registers *regs)
{
struct linux_prom64_ranges ranges[PROMREG_MAX];
char name[128];
......@@ -803,126 +879,168 @@ static int __init creator_apply_upa_parent_ranges(int parent, struct linux_prom6
return 1;
}
char __init *creatorfb_init(struct fb_info_sbusfb *fb)
struct all_info {
struct fb_info info;
struct ffb_par par;
u32 pseudo_palette[256];
struct list_head list;
};
static LIST_HEAD(ffb_list);
static void ffb_init_one(int node, int parent)
{
struct fb_fix_screeninfo *fix = &fb->info.fix;
struct fb_var_screeninfo *var = &fb->info.var;
struct display *disp = &fb->disp;
struct fbtype *type = &fb->type;
struct linux_prom64_registers regs[2*PROMREG_MAX];
int i, afb = 0;
unsigned int btype;
char name[64];
struct fb_ops *fbops;
if (prom_getproperty(fb->prom_node, "reg", (void *) regs, sizeof(regs)) <= 0)
return NULL;
if (creator_apply_upa_parent_ranges(fb->prom_parent, &regs[0]))
return NULL;
disp->dispsw_data = (void *)kmalloc(16 * sizeof(u32), GFP_KERNEL);
if (disp->dispsw_data == NULL)
return NULL;
memset(disp->dispsw_data, 0, 16 * sizeof(u32));
fbops = kmalloc(sizeof(*fbops), GFP_KERNEL);
if (fbops == NULL) {
kfree(disp->dispsw_data);
return NULL;
struct ffb_fbc *fbc;
struct ffb_dac *dac;
struct all_info *all;
if (prom_getproperty(node, "reg", (void *) regs, sizeof(regs)) <= 0) {
printk("ffb: Cannot get reg device node property.\n");
return;
}
*fbops = *fb->info.fbops;
fbops->fb_rasterimg = ffb_rasterimg;
fb->info.fbops = fbops;
prom_getstring(fb->prom_node, "name", name, sizeof(name));
if (!strcmp(name, "SUNW,afb"))
afb = 1;
btype = prom_getintdefault(fb->prom_node, "board_type", 0);
strcpy(fb->info.modename, "Creator");
if (!afb) {
if ((btype & 7) == 3)
strcpy(fix->id, "Creator 3D");
else
strcpy(fix->id, "Creator");
} else
strcpy(fix->id, "Elite 3D");
fix->visual = FB_VISUAL_TRUECOLOR;
fix->line_length = 8192;
fix->accel = FB_ACCEL_SUN_CREATOR;
var->bits_per_pixel = 32;
var->green.offset = 8;
var->blue.offset = 16;
var->accel_flags = FB_ACCELF_TEXT;
disp->scrollmode = SCROLL_YREDRAW;
fb->info.screen_base = (char *)(regs[0].phys_addr) + FFB_DFB24_POFF + 8192 * fb->y_margin + 4 * fb->x_margin;
fb->s.ffb.xy_margin = (fb->y_margin << 16) + fb->x_margin;
fb->s.ffb.yx_margin = (((u64)fb->y_margin) << 32) + fb->x_margin;
fb->s.ffb.fbc = (struct ffb_fbc *)(regs[0].phys_addr + FFB_FBC_REGS_POFF);
fb->s.ffb.dac = (struct ffb_dac *)(regs[0].phys_addr + FFB_DAC_POFF);
fb->dispsw = ffb_dispsw;
fb->margins = ffb_margins;
fb->loadcmap = ffb_loadcmap;
fb->setcursor = ffb_setcursor;
fb->setcursormap = ffb_setcursormap;
fb->setcurshape = ffb_setcurshape;
fb->switch_from_graph = ffb_switch_from_graph;
fb->fill = ffb_fill;
#if 0
/* XXX Can't enable this for now, I've seen cases
* XXX where the VC was blanked, and Xsun24 was started
* XXX via a remote login, the sunfb code did not
* XXX unblank creator when it was mmap'd for some
* XXX reason, investigate later... -DaveM
*/
fb->blank = ffb_blank;
fb->unblank = ffb_unblank;
#endif
/* If there are any read errors or fifo overflow conditions,
* clear them now.
*/
if((upa_readl(&fb->s.ffb.fbc->ucsr) & FFB_UCSR_ALL_ERRORS) != 0)
upa_writel(FFB_UCSR_ALL_ERRORS, &fb->s.ffb.fbc->ucsr);
ffb_switch_from_graph(fb);
fb->physbase = regs[0].phys_addr;
fb->mmap_map = ffb_mmap_map;
fb->cursor.hwsize.fbx = 64;
fb->cursor.hwsize.fby = 64;
type->fb_depth = 24;
upa_writel(0x8000, &fb->s.ffb.dac->type);
fb->s.ffb.dac_rev = (upa_readl(&fb->s.ffb.dac->value) >> 0x1c);
i = prom_getintdefault (fb->prom_node, "board_type", 8);
if (ffb_apply_upa_parent_ranges(parent, &regs[0])) {
printk("ffb: Cannot apply parent ranges to regs.\n");
return;
}
all = kmalloc(sizeof(*all), GFP_KERNEL);
if (!all) {
printk(KERN_ERR "ffb: Cannot allocate memory.\n");
return;
}
memset(all, 0, sizeof(*all));
INIT_LIST_HEAD(&all->list);
spin_lock_init(&all->par.lock);
all->par.fbc = (struct ffb_fbc *)(regs[0].phys_addr + FFB_FBC_REGS_POFF);
all->par.dac = (struct ffb_dac *)(regs[0].phys_addr + FFB_DAC_POFF);
all->par.rop_cache = FFB_ROP_NEW;
all->par.physbase = regs[0].phys_addr;
all->par.prom_node = node;
all->par.prom_parent_node = parent;
all->info.node = NODEV;
all->info.flags = FBINFO_FLAG_DEFAULT;
all->info.fbops = &ffb_ops;
all->info.screen_base = (char *) all->par.physbase + FFB_DFB24_POFF;
all->info.currcon = -1;
all->info.par = &all->par;
all->info.pseudo_palette = all->pseudo_palette;
sbusfb_fill_var(&all->info.var, all->par.prom_node, 32);
all->par.fbsize = PAGE_ALIGN(all->info.var.xres *
all->info.var.yres *
4);
ffb_fixup_var_rgb(&all->info.var);
all->info.var.accel_flags = FB_ACCELF_TEXT;
prom_getstring(node, "name", all->par.name, sizeof(all->par.name));
if (!strcmp(all->par.name, "SUNW,afb"))
all->par.flags |= FFB_FLAG_AFB;
all->par.board_type = prom_getintdefault(node, "board_type", 0);
fbc = all->par.fbc;
if((upa_readl(&fbc->ucsr) & FFB_UCSR_ALL_ERRORS) != 0)
upa_writel(FFB_UCSR_ALL_ERRORS, &fbc->ucsr);
ffb_switch_from_graph(&all->par);
dac = all->par.dac;
upa_writel(0x8000, &dac->type);
all->par.dac_rev = upa_readl(&dac->value) >> 0x1c;
sprintf(idstring, "%s at %016lx type %d DAC %d",
fix->id, regs[0].phys_addr, i, fb->s.ffb.dac_rev);
/* Elite3D has different DAC revision numbering, and no DAC revisions
have the reversed meaning of cursor enable */
if (afb)
fb->s.ffb.dac_rev = 10;
* have the reversed meaning of cursor enable.
*/
if (all->par.flags & FFB_FLAG_AFB)
all->par.dac_rev = 10;
/* Unblank it just to be sure. When there are multiple
* FFB/AFB cards in the system, or it is not the OBP
* chosen console, it will have video outputs off in
* the DAC.
*/
ffb_unblank(fb);
ffb_blank(0, &all->info);
return idstring;
if (fb_alloc_cmap(&all->info.cmap, 256, 0)) {
printk(KERN_ERR "ffb: Could not allocate color map.\n");
kfree(all);
return;
}
ffb_set_par(&all->info);
ffb_init_fix(&all->info);
if (register_framebuffer(&all->info) < 0) {
printk(KERN_ERR "ffb: Could not register framebuffer.\n");
fb_dealloc_cmap(&all->info.cmap);
kfree(all);
return;
}
list_add(&all->list, &ffb_list);
printk("ffb: %s at %016lx type %d DAC %d\n",
((all->par.flags & FFB_FLAG_AFB) ? "AFB" : "FFB"),
regs[0].phys_addr, all->par.board_type, all->par.dac_rev);
}
static void ffb_scan_siblings(int root)
{
int node, child;
child = prom_getchild(root);
for (node = prom_searchsiblings(child, "SUNW,ffb"); node;
node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb"))
ffb_init_one(node, root);
for (node = prom_searchsiblings(child, "SUNW,afb"); node;
node = prom_searchsiblings(prom_getsibling(node), "SUNW,afb"))
ffb_init_one(node, root);
}
int __init ffb_init(void)
{
int root;
ffb_scan_siblings(prom_root_node);
root = prom_getchild(prom_root_node);
for (root = prom_searchsiblings(root, "upa"); root;
root = prom_searchsiblings(prom_getsibling(root), "upa"))
ffb_scan_siblings(root);
return 0;
}
void __exit ffb_exit(void)
{
struct list_head *pos, *tmp;
list_for_each_safe(pos, tmp, &ffb_list) {
struct all_info *all = list_entry(pos, typeof(*all), list);
unregister_framebuffer(&all->info);
fb_dealloc_cmap(&all->info.cmap);
kfree(all);
}
}
int __init
ffb_setup(char *arg)
{
/* No cmdline options yet... */
return 0;
}
#ifdef MODULE
module_init(ffb_init);
module_exit(ffb_exit);
#endif
MODULE_DESCRIPTION("framebuffer driver for Creator/Elite3D chipsets");
MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
MODULE_LICENSE("GPL");
/*
* linux/drivers/video/sbusfb.c -- SBUS or UPA based frame buffer device
*
* Copyright (C) 1998 Jakub Jelinek
*
* This driver is partly based on the Open Firmware console driver
*
* Copyright (C) 1997 Geert Uytterhoeven
*
* and SPARC console subsystem
*
* Copyright (C) 1995 Peter Zaitcev (zaitcev@yahoo.com)
* Copyright (C) 1995-1997 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1995-1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
* Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
* Copyright (C) 1996-1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
* Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
*
* 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/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/fb.h>
#include <linux/selection.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/kd.h>
#include <linux/vt_kern.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h> /* io_remap_page_range() */
#include <video/sbusfb.h>
#define DEFAULT_CURSOR_BLINK_RATE (2*HZ/5)
#define CURSOR_SHAPE 1
#define CURSOR_BLINK 2
/*
* Interface used by the world
*/
int sbusfb_init(void);
int sbusfb_setup(char*);
static int defx_margin = -1, defy_margin = -1;
static char fontname[40] __initdata = { 0 };
static int curblink __initdata = 1;
static struct {
int depth;
int xres, yres;
int x_margin, y_margin;
} def_margins [] = {
{ 8, 1280, 1024, 64, 80 },
{ 8, 1152, 1024, 64, 80 },
{ 8, 1152, 900, 64, 18 },
{ 8, 1024, 768, 0, 0 },
{ 8, 800, 600, 16, 12 },
{ 8, 640, 480, 0, 0 },
{ 1, 1152, 900, 8, 18 },
{ 0 },
};
static int sbusfb_open(struct fb_info *info, int user);
static int sbusfb_release(struct fb_info *info, int user);
static int sbusfb_mmap(struct fb_info *info, struct file *file,
struct vm_area_struct *vma);
static int sbusfb_set_var(struct fb_var_screeninfo *var, int con,
struct fb_info *info);
static int sbusfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
struct fb_info *info);
static int sbusfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
struct fb_info *info);
static int sbusfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int transp, struct fb_info *info);
static int sbusfb_blank(int blank, struct fb_info *info);
static int sbusfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
u_long arg, int con, struct fb_info *info);
static void sbusfb_cursor(struct display *p, int mode, int x, int y);
static void sbusfb_clear_margin(struct display *p, int s);
/*
* Interface to the low level console driver
*/
static int sbusfbcon_switch(int con, struct fb_info *info);
static int sbusfbcon_updatevar(int con, struct fb_info *info);
/*
* Internal routines
*/
static int sbusfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
u_int *transp, struct fb_info *info);
static struct fb_ops sbusfb_ops = {
.owner = THIS_MODULE,
.fb_open = sbusfb_open,
.fb_release = sbusfb_release,
.fb_set_var = sbusfb_set_var,
.fb_get_cmap = sbusfb_get_cmap,
.fb_set_cmap = sbusfb_set_cmap,
.fb_setcolreg = sbusfb_setcolreg,
.fb_blank = sbusfb_blank,
.fb_ioctl = sbusfb_ioctl,
.fb_mmap = sbusfb_mmap,
};
/*
* Open/Release the frame buffer device
*/
static int sbusfb_open(struct fb_info *info, int user)
{
struct fb_info_sbusfb *fb = sbusfbinfo(info);
if (user) {
if (fb->open == 0) {
fb->mmaped = 0;
fb->vtconsole = -1;
}
fb->open++;
} else
fb->consolecnt++;
return 0;
}
static int sbusfb_release(struct fb_info *info, int user)
{
struct fb_info_sbusfb *fb = sbusfbinfo(info);
if (user) {
fb->open--;
if (fb->open == 0) {
if (fb->vtconsole != -1) {
vt_cons[fb->vtconsole]->vc_mode = KD_TEXT;
if (fb->mmaped) {
fb->graphmode--;
sbusfb_clear_margin(&fb_display[fb->vtconsole], 0);
}
}
if (fb->reset)
fb->reset(fb);
}
} else
fb->consolecnt--;
return 0;
}
static unsigned long sbusfb_mmapsize(struct fb_info_sbusfb *fb, long size)
{
if (size == SBUS_MMAP_EMPTY) return 0;
if (size >= 0) return size;
return fb->type.fb_size * (-size);
}
static int sbusfb_mmap(struct fb_info *info, struct file *file,
struct vm_area_struct *vma)
{
struct fb_info_sbusfb *fb = sbusfbinfo(info);
unsigned int size, page, r, map_size;
unsigned long map_offset = 0;
unsigned long off;
int i;
size = vma->vm_end - vma->vm_start;
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
return -EINVAL;
off = vma->vm_pgoff << PAGE_SHIFT;
/* To stop the swapper from even considering these pages */
vma->vm_flags |= (VM_SHM| VM_LOCKED);
/* Each page, see which map applies */
for (page = 0; page < size; ){
map_size = 0;
for (i = 0; fb->mmap_map[i].size; i++)
if (fb->mmap_map[i].voff == off+page) {
map_size = sbusfb_mmapsize(fb,fb->mmap_map[i].size);
#ifdef __sparc_v9__
#define POFF_MASK (PAGE_MASK|0x1UL)
#else
#define POFF_MASK (PAGE_MASK)
#endif
map_offset = (fb->physbase + fb->mmap_map[i].poff) & POFF_MASK;
break;
}
if (!map_size){
page += PAGE_SIZE;
continue;
}
if (page + map_size > size)
map_size = size - page;
r = io_remap_page_range(vma, vma->vm_start+page, map_offset, map_size, vma->vm_page_prot, fb->iospace);
if (r)
return -EAGAIN;
page += map_size;
}
vma->vm_flags |= VM_IO;
if (!fb->mmaped) {
int lastconsole = 0;
if (info->display_fg)
lastconsole = info->display_fg->vc_num;
fb->mmaped = 1;
if (fb->consolecnt && fb_display[lastconsole].fb_info == info) {
fb->vtconsole = lastconsole;
fb->graphmode++;
vt_cons [lastconsole]->vc_mode = KD_GRAPHICS;
vc_cons[lastconsole].d->vc_sw->con_cursor(vc_cons[lastconsole].d,CM_ERASE);
} else if (fb->unblank && !fb->blanked)
(*fb->unblank)(fb);
}
return 0;
}
static void sbusfb_clear_margin(struct display *p, int s)
{
struct fb_info_sbusfb *fb = sbusfbinfod(p);
if (fb->switch_from_graph)
(*fb->switch_from_graph)(fb);
if (fb->fill) {
unsigned short rects [16];
rects [0] = 0;
rects [1] = 0;
rects [2] = fb->info.var.xres_virtual;
rects [3] = fb->y_margin;
rects [4] = 0;
rects [5] = fb->y_margin;
rects [6] = fb->x_margin;
rects [7] = fb->info.var.yres_virtual;
rects [8] = fb->info.var.xres_virtual - fb->x_margin;
rects [9] = fb->y_margin;
rects [10] = fb->info.var.xres_virtual;
rects [11] = fb->info.var.yres_virtual;
rects [12] = fb->x_margin;
rects [13] = fb->info.var.yres_virtual - fb->y_margin;
rects [14] = fb->info.var.xres_virtual - fb->x_margin;
rects [15] = fb->info.var.yres_virtual;
(*fb->fill)(fb, p, s, 4, rects);
} else {
unsigned char *fb_base = fb->info.screen_base, *q;
int skip_bytes = fb->y_margin * fb->info.var.xres_virtual;
int scr_size = fb->info.var.xres_virtual
* fb->info.var.yres_virtual;
int h, he, incr, size;
he = fb->info.var.yres;
if (fb->info.var.bits_per_pixel == 1) {
fb_base -= (skip_bytes + fb->x_margin) / 8;
skip_bytes /= 8;
scr_size /= 8;
fb_memset255 (fb_base, skip_bytes - fb->x_margin / 8);
fb_memset255 (fb_base + scr_size - skip_bytes + fb->x_margin / 8, skip_bytes - fb->x_margin / 8);
incr = fb->info.var.xres_virtual / 8;
size = fb->x_margin / 8 * 2;
for (q = fb_base + skip_bytes - fb->x_margin / 8, h = 0;
h <= he; q += incr, h++)
fb_memset255 (q, size);
} else {
fb_base -= (skip_bytes + fb->x_margin);
fb_memset (fb_base, attr_bgcol(p,s), skip_bytes - fb->x_margin);
fb_memset (fb_base + scr_size - skip_bytes + fb->x_margin, attr_bgcol(p,s), skip_bytes - fb->x_margin);
incr = fb->info.var.xres_virtual;
size = fb->x_margin * 2;
for (q = fb_base + skip_bytes - fb->x_margin, h = 0;
h <= he; q += incr, h++)
fb_memset (q, attr_bgcol(p,s), size);
}
}
}
static void sbusfb_disp_setup(struct display *p)
{
struct fb_info_sbusfb *fb = sbusfbinfod(p);
if (fb->setup)
fb->setup(p);
sbusfb_clear_margin(p, 0);
}
/*
* Set the User Defined Part of the Display
*/
static int sbusfb_set_var(struct fb_var_screeninfo *var, int con,
struct fb_info *info)
{
struct display *display;
int activate = var->activate;
if(con >= 0)
display = &fb_display[con];
else
display = info->disp;
/* simple check for equality until fully implemented -E */
if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
if (display->var.xres != var->xres ||
display->var.yres != var->yres ||
display->var.xres_virtual != var->xres_virtual ||
display->var.yres_virtual != var->yres_virtual ||
display->var.bits_per_pixel != var->bits_per_pixel ||
display->var.accel_flags != var->accel_flags) {
return -EINVAL;
}
}
return 0;
}
/*
* Hardware cursor
*/
static int sbus_hw_scursor (struct fbcursor *cursor, struct fb_info_sbusfb *fb)
{
int op;
int i, bytes = 0;
struct fbcursor f;
char red[2], green[2], blue[2];
if (copy_from_user (&f, cursor, sizeof(struct fbcursor)))
return -EFAULT;
op = f.set;
if (op & FB_CUR_SETSHAPE){
if ((u32) f.size.fbx > fb->cursor.hwsize.fbx)
return -EINVAL;
if ((u32) f.size.fby > fb->cursor.hwsize.fby)
return -EINVAL;
if (f.size.fbx > 32)
bytes = f.size.fby << 3;
else
bytes = f.size.fby << 2;
}
if (op & FB_CUR_SETCMAP){
if (f.cmap.index || f.cmap.count != 2)
return -EINVAL;
if (copy_from_user (red, f.cmap.red, 2) ||
copy_from_user (green, f.cmap.green, 2) ||
copy_from_user (blue, f.cmap.blue, 2))
return -EFAULT;
}
if (op & FB_CUR_SETCMAP)
(*fb->setcursormap) (fb, red, green, blue);
if (op & FB_CUR_SETSHAPE){
u32 u;
fb->cursor.size = f.size;
memset ((void *)&fb->cursor.bits, 0, sizeof (fb->cursor.bits));
if (copy_from_user (fb->cursor.bits [0], f.mask, bytes) ||
copy_from_user (fb->cursor.bits [1], f.image, bytes))
return -EFAULT;
if (f.size.fbx <= 32) {
u = 0xffffffff << (32 - f.size.fbx);
for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
fb->cursor.bits [0][i] &= u;
fb->cursor.bits [1][i] &= fb->cursor.bits [0][i];
}
} else {
u = 0xffffffff << (64 - f.size.fbx);
for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
fb->cursor.bits [0][2*i+1] &= u;
fb->cursor.bits [1][2*i] &= fb->cursor.bits [0][2*i];
fb->cursor.bits [1][2*i+1] &= fb->cursor.bits [0][2*i+1];
}
}
(*fb->setcurshape) (fb);
}
if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){
if (op & FB_CUR_SETCUR)
fb->cursor.enable = f.enable;
if (op & FB_CUR_SETPOS)
fb->cursor.cpos = f.pos;
if (op & FB_CUR_SETHOT)
fb->cursor.chot = f.hot;
(*fb->setcursor) (fb);
}
return 0;
}
static unsigned char hw_cursor_cmap[2] = { 0, 0xff };
static void
sbusfb_cursor_timer_handler(unsigned long dev_addr)
{
struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)dev_addr;
if (!fb->setcursor) return;
if (fb->cursor.mode & CURSOR_BLINK) {
fb->cursor.enable ^= 1;
fb->setcursor(fb);
}
fb->cursor.timer.expires = jiffies + fb->cursor.blink_rate;
add_timer(&fb->cursor.timer);
}
static void sbusfb_cursor(struct display *p, int mode, int x, int y)
{
struct fb_info_sbusfb *fb = sbusfbinfod(p);
switch (mode) {
case CM_ERASE:
fb->cursor.mode &= ~CURSOR_BLINK;
fb->cursor.enable = 0;
(*fb->setcursor)(fb);
break;
case CM_MOVE:
case CM_DRAW:
if (fb->cursor.mode & CURSOR_SHAPE) {
fb->cursor.size.fbx = fontwidth(p);
fb->cursor.size.fby = fontheight(p);
fb->cursor.chot.fbx = 0;
fb->cursor.chot.fby = 0;
fb->cursor.enable = 1;
memset (fb->cursor.bits, 0, sizeof (fb->cursor.bits));
fb->cursor.bits[0][fontheight(p) - 2] = (0xffffffff << (32 - fontwidth(p)));
fb->cursor.bits[1][fontheight(p) - 2] = (0xffffffff << (32 - fontwidth(p)));
fb->cursor.bits[0][fontheight(p) - 1] = (0xffffffff << (32 - fontwidth(p)));
fb->cursor.bits[1][fontheight(p) - 1] = (0xffffffff << (32 - fontwidth(p)));
(*fb->setcursormap) (fb, hw_cursor_cmap, hw_cursor_cmap, hw_cursor_cmap);
(*fb->setcurshape) (fb);
}
fb->cursor.mode = CURSOR_BLINK;
if (fontwidthlog(p))
fb->cursor.cpos.fbx = (x << fontwidthlog(p)) + fb->x_margin;
else
fb->cursor.cpos.fbx = (x * fontwidth(p)) + fb->x_margin;
if (fontheightlog(p))
fb->cursor.cpos.fby = (y << fontheightlog(p)) + fb->y_margin;
else
fb->cursor.cpos.fby = (y * fontheight(p)) + fb->y_margin;
(*fb->setcursor)(fb);
break;
}
}
/*
* Get the Colormap
*/
static int sbusfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
struct fb_info *info)
{
if (!info->display_fg || con == info->display_fg->vc_num) /* current console? */
return fb_get_cmap(cmap, kspc, sbusfb_getcolreg, info);
else if (fb_display[con].cmap.len) /* non default colormap? */
fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
else
fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2);
return 0;
}
/*
* Set the Colormap
*/
static int sbusfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
struct fb_info *info)
{
int err;
struct display *disp;
if (con >= 0)
disp = &fb_display[con];
else
disp = info->disp;
if (!disp->cmap.len) { /* no colormap allocated? */
if ((err = fb_alloc_cmap(&disp->cmap, 1<<disp->var.bits_per_pixel, 0)))
return err;
}
if (con == info->currcon) { /* current console? */
err = fb_set_cmap(cmap, kspc, info);
if (!err) {
struct fb_info_sbusfb *fb = sbusfbinfo(info);
if (fb->loadcmap)
(*fb->loadcmap)(fb, &fb_display[con], cmap->start, cmap->len);
}
return err;
} else
fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
return 0;
}
static int sbusfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
u_long arg, int con, struct fb_info *info)
{
struct fb_info_sbusfb *fb = sbusfbinfo(info);
int i;
int lastconsole;
switch (cmd){
case FBIOGTYPE: /* return frame buffer type */
if (copy_to_user((struct fbtype *)arg, &fb->type, sizeof(struct fbtype)))
return -EFAULT;
break;
case FBIOGATTR: {
struct fbgattr *fba = (struct fbgattr *) arg;
i = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct fbgattr));
if (i) return i;
if (__put_user(fb->emulations[0], &fba->real_type) ||
__put_user(0, &fba->owner) ||
__copy_to_user(&fba->fbtype, &fb->type,
sizeof(struct fbtype)) ||
__put_user(0, &fba->sattr.flags) ||
__put_user(fb->type.fb_type, &fba->sattr.emu_type) ||
__put_user(-1, &fba->sattr.dev_specific[0]))
return -EFAULT;
for (i = 0; i < 4; i++) {
if (put_user(fb->emulations[i], &fba->emu_types[i]))
return -EFAULT;
}
break;
}
case FBIOSATTR:
i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbsattr));
if (i) return i;
return -EINVAL;
case FBIOSVIDEO:
if (fb->consolecnt) {
lastconsole = info->display_fg->vc_num;
if (vt_cons[lastconsole]->vc_mode == KD_TEXT)
break;
}
if (get_user(i, (int *)arg))
return -EFAULT;
if (i){
if (!fb->blanked || !fb->unblank)
break;
if (fb->consolecnt || (fb->open && fb->mmaped))
(*fb->unblank)(fb);
fb->blanked = 0;
} else {
if (fb->blanked || !fb->blank)
break;
(*fb->blank)(fb);
fb->blanked = 1;
}
break;
case FBIOGVIDEO:
if (put_user(fb->blanked, (int *) arg))
return -EFAULT;
break;
case FBIOGETCMAP_SPARC: {
char *rp, *gp, *bp;
int end, count, index;
struct fbcmap *cmap;
if (!fb->loadcmap)
return -EINVAL;
i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbcmap));
if (i) return i;
cmap = (struct fbcmap *) arg;
if (__get_user(count, &cmap->count) ||
__get_user(index, &cmap->index))
return -EFAULT;
if ((index < 0) || (index > 255))
return -EINVAL;
if (index + count > 256)
count = 256 - index;
if (__get_user(rp, &cmap->red) ||
__get_user(gp, &cmap->green) ||
__get_user(bp, &cmap->blue))
return -EFAULT;
if (verify_area (VERIFY_WRITE, rp, count))
return -EFAULT;
if (verify_area (VERIFY_WRITE, gp, count))
return -EFAULT;
if (verify_area (VERIFY_WRITE, bp, count))
return -EFAULT;
end = index + count;
for (i = index; i < end; i++){
if (__put_user(fb->color_map CM(i,0), rp) ||
__put_user(fb->color_map CM(i,1), gp) ||
__put_user(fb->color_map CM(i,2), bp))
return -EFAULT;
rp++; gp++; bp++;
}
(*fb->loadcmap)(fb, NULL, index, count);
break;
}
case FBIOPUTCMAP_SPARC: { /* load color map entries */
char *rp, *gp, *bp;
int end, count, index;
struct fbcmap *cmap;
if (!fb->loadcmap || !fb->color_map)
return -EINVAL;
i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbcmap));
if (i) return i;
cmap = (struct fbcmap *) arg;
if (__get_user(count, &cmap->count) ||
__get_user(index, &cmap->index))
return -EFAULT;
if ((index < 0) || (index > 255))
return -EINVAL;
if (index + count > 256)
count = 256 - index;
if (__get_user(rp, &cmap->red) ||
__get_user(gp, &cmap->green) ||
__get_user(bp, &cmap->blue))
return -EFAULT;
if (verify_area (VERIFY_READ, rp, count))
return -EFAULT;
if (verify_area (VERIFY_READ, gp, count))
return -EFAULT;
if (verify_area (VERIFY_READ, bp, count))
return -EFAULT;
end = index + count;
for (i = index; i < end; i++){
if (__get_user(fb->color_map CM(i,0), rp))
return -EFAULT;
if (__get_user(fb->color_map CM(i,1), gp))
return -EFAULT;
if (__get_user(fb->color_map CM(i,2), bp))
return -EFAULT;
rp++; gp++; bp++;
}
(*fb->loadcmap)(fb, NULL, index, count);
break;
}
case FBIOGCURMAX: {
struct fbcurpos *p = (struct fbcurpos *) arg;
if (!fb->setcursor) return -EINVAL;
if(verify_area (VERIFY_WRITE, p, sizeof (struct fbcurpos)))
return -EFAULT;
if (__put_user(fb->cursor.hwsize.fbx, &p->fbx) ||
__put_user(fb->cursor.hwsize.fby, &p->fby))
return -EFAULT;
break;
}
case FBIOSCURSOR:
if (!fb->setcursor) return -EINVAL;
if (fb->consolecnt) {
lastconsole = info->display_fg->vc_num;
if (vt_cons[lastconsole]->vc_mode == KD_TEXT)
return -EINVAL; /* Don't let graphics programs hide our nice text cursor */
fb->cursor.mode = CURSOR_SHAPE; /* Forget state of our text cursor */
}
return sbus_hw_scursor ((struct fbcursor *) arg, fb);
case FBIOSCURPOS:
if (!fb->setcursor) return -EINVAL;
/* Don't let graphics programs move our nice text cursor */
if (fb->consolecnt) {
lastconsole = info->display_fg->vc_num;
if (vt_cons[lastconsole]->vc_mode == KD_TEXT)
return -EINVAL; /* Don't let graphics programs move our nice text cursor */
}
if (copy_from_user(&fb->cursor.cpos, (void *)arg, sizeof(struct fbcurpos)))
return -EFAULT;
(*fb->setcursor) (fb);
break;
default:
if (fb->ioctl)
return fb->ioctl(fb, cmd, arg);
return -EINVAL;
}
return 0;
}
/*
* Setup: parse used options
*/
int __init sbusfb_setup(char *options)
{
char *p;
for (p = options;;) {
if (!strncmp(p, "nomargins", 9)) {
defx_margin = 0; defy_margin = 0;
} else if (!strncmp(p, "margins=", 8)) {
int i, j;
char *q;
i = simple_strtoul(p+8,&q,10);
if (i >= 0 && *q == 'x') {
j = simple_strtoul(q+1,&q,10);
if (j >= 0 && (*q == ' ' || !*q)) {
defx_margin = i; defy_margin = j;
}
}
} else if (!strncmp(p, "font=", 5)) {
int i;
for (i = 0; i < sizeof(fontname) - 1; i++)
if (p[i+5] == ' ' || !p[i+5])
break;
memcpy(fontname, p+5, i);
fontname[i] = 0;
} else if (!strncmp(p, "noblink", 7))
curblink = 0;
while (*p && *p != ' ' && *p != ',') p++;
if (*p != ',') break;
p++;
}
return 0;
}
static int sbusfbcon_switch(int con, struct fb_info *info)
{
int x_margin, y_margin;
struct fb_info_sbusfb *fb = sbusfbinfo(info);
int lastconsole;
/* Do we have to save the colormap? */
if (fb_display[info->currcon].cmap.len)
fb_get_cmap(&fb_display[info->currcon].cmap, 1, sbusfb_getcolreg, info);
if (info->display_fg) {
lastconsole = info->display_fg->vc_num;
if (lastconsole != con &&
(fontwidth(&fb_display[lastconsole]) != fontwidth(&fb_display[con]) ||
fontheight(&fb_display[lastconsole]) != fontheight(&fb_display[con])))
fb->cursor.mode |= CURSOR_SHAPE;
}
x_margin = (fb_display[con].var.xres_virtual - fb_display[con].var.xres) / 2;
y_margin = (fb_display[con].var.yres_virtual - fb_display[con].var.yres) / 2;
if (fb->margins)
fb->margins(fb, &fb_display[con], x_margin, y_margin);
if (fb->graphmode || fb->x_margin != x_margin || fb->y_margin != y_margin) {
fb->x_margin = x_margin; fb->y_margin = y_margin;
sbusfb_clear_margin(&fb_display[con], 0);
}
info->currcon = con;
/* Install new colormap */
do_install_cmap(con, info);
return 0;
}
/*
* Update the `var' structure (called by fbcon.c)
*/
static int sbusfbcon_updatevar(int con, struct fb_info *info)
{
/* Nothing */
return 0;
}
/*
* Blank the display.
*/
static int sbusfb_blank(int blank, struct fb_info *info)
{
struct fb_info_sbusfb *fb = sbusfbinfo(info);
if (blank && fb->blank)
return fb->blank(fb);
else if (!blank && fb->unblank)
return fb->unblank(fb);
return 0;
}
/*
* Read a single color register and split it into
* colors/transparent. Return != 0 for invalid regno.
*/
static int sbusfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
u_int *transp, struct fb_info *info)
{
struct fb_info_sbusfb *fb = sbusfbinfo(info);
if (!fb->color_map || regno > 255)
return 1;
*red = (fb->color_map CM(regno, 0)<<8) | fb->color_map CM(regno, 0);
*green = (fb->color_map CM(regno, 1)<<8) | fb->color_map CM(regno, 1);
*blue = (fb->color_map CM(regno, 2)<<8) | fb->color_map CM(regno, 2);
*transp = 0;
return 0;
}
/*
* Set a single color register. The values supplied are already
* rounded down to the hardware's capabilities (according to the
* entries in the var structure). Return != 0 for invalid regno.
*/
static int sbusfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int transp, struct fb_info *info)
{
struct fb_info_sbusfb *fb = sbusfbinfo(info);
if (!fb->color_map || regno > 255)
return 1;
red >>= 8;
green >>= 8;
blue >>= 8;
fb->color_map CM(regno, 0) = red;
fb->color_map CM(regno, 1) = green;
fb->color_map CM(regno, 2) = blue;
return 0;
}
static int sbusfb_set_font(struct display *p, int width, int height)
{
int margin;
int w = p->var.xres_virtual, h = p->var.yres_virtual;
int depth = p->var.bits_per_pixel;
struct fb_info_sbusfb *fb = sbusfbinfod(p);
int x_margin, y_margin;
if (depth > 8) depth = 8;
x_margin = 0;
y_margin = 0;
if (defx_margin < 0 || defy_margin < 0) {
for (margin = 0; def_margins[margin].depth; margin++)
if (w == def_margins[margin].xres &&
h == def_margins[margin].yres &&
depth == def_margins[margin].depth) {
x_margin = def_margins[margin].x_margin;
y_margin = def_margins[margin].y_margin;
break;
}
} else {
x_margin = defx_margin;
y_margin = defy_margin;
}
x_margin += ((w - 2*x_margin) % width) / 2;
y_margin += ((h - 2*y_margin) % height) / 2;
p->var.xres = w - 2*x_margin;
p->var.yres = h - 2*y_margin;
fb->cursor.mode |= CURSOR_SHAPE;
if (fb->margins)
fb->margins(fb, p, x_margin, y_margin);
if (fb->x_margin != x_margin || fb->y_margin != y_margin) {
fb->x_margin = x_margin; fb->y_margin = y_margin;
sbusfb_clear_margin(p, 0);
}
return 1;
}
void sbusfb_palette(int enter)
{
int i;
struct display *p;
for (i = 0; i < MAX_NR_CONSOLES; i++) {
p = &fb_display[i];
if (p->dispsw && p->dispsw->setup == sbusfb_disp_setup &&
p->fb_info->display_fg &&
p->fb_info->display_fg->vc_num == i) {
struct fb_info_sbusfb *fb = sbusfbinfod(p);
if (fb->restore_palette) {
if (enter)
fb->restore_palette(fb);
else if (vt_cons[i]->vc_mode != KD_GRAPHICS)
vc_cons[i].d->vc_sw->con_set_palette(vc_cons[i].d, color_table);
}
}
}
}
/*
* Initialisation
*/
extern void (*prom_palette)(int);
static void __init sbusfb_init_fb(int node, int parent, int fbtype,
struct sbus_dev *sbdp)
{
struct fb_fix_screeninfo *fix;
struct fb_var_screeninfo *var;
struct display *disp;
struct fb_info_sbusfb *fb;
struct fbtype *type;
int linebytes, w, h, depth;
char *p = NULL;
int margin;
fb = kmalloc(sizeof(struct fb_info_sbusfb), GFP_ATOMIC);
if (!fb) {
prom_printf("Could not allocate sbusfb structure\n");
return;
}
if (!prom_palette)
prom_palette = sbusfb_palette;
memset(fb, 0, sizeof(struct fb_info_sbusfb));
fix = &fb->info.fix;
var = &fb->info.var;
disp = &fb->disp;
type = &fb->type;
spin_lock_init(&fb->lock);
fb->prom_node = node;
fb->prom_parent = parent;
fb->sbdp = sbdp;
if (sbdp)
fb->iospace = sbdp->reg_addrs[0].which_io;
type->fb_type = fbtype;
memset(&fb->emulations, 0xff, sizeof(fb->emulations));
fb->emulations[0] = fbtype;
#ifdef CONFIG_SPARC32
fb->info.screen_base = (unsigned char *)prom_getintdefault(node, "address", 0);
#endif
type->fb_height = h = prom_getintdefault(node, "height", 900);
type->fb_width = w = prom_getintdefault(node, "width", 1152);
sizechange:
type->fb_depth = depth = (fbtype == FBTYPE_SUN2BW) ? 1 : 8;
linebytes = prom_getintdefault(node, "linebytes", w * depth / 8);
type->fb_size = PAGE_ALIGN((linebytes) * h);
if (defx_margin < 0 || defy_margin < 0) {
for (margin = 0; def_margins[margin].depth; margin++)
if (w == def_margins[margin].xres &&
h == def_margins[margin].yres &&
depth == def_margins[margin].depth) {
fb->x_margin = def_margins[margin].x_margin;
fb->y_margin = def_margins[margin].y_margin;
break;
}
} else {
fb->x_margin = defx_margin;
fb->y_margin = defy_margin;
}
fb->x_margin += ((w - 2*fb->x_margin) & 7) / 2;
fb->y_margin += ((h - 2*fb->y_margin) & 15) / 2;
var->xres_virtual = w;
var->yres_virtual = h;
var->xres = w - 2*fb->x_margin;
var->yres = h - 2*fb->y_margin;
var->bits_per_pixel = depth;
var->height = var->width = -1;
var->pixclock = 10000;
var->vmode = FB_VMODE_NONINTERLACED;
var->red.length = var->green.length = var->blue.length = 8;
fix->line_length = linebytes;
fix->smem_len = type->fb_size;
fix->type = FB_TYPE_PACKED_PIXELS;
fix->visual = FB_VISUAL_PSEUDOCOLOR;
fb->info.node = NODEV;
fb->info.fbops = &sbusfb_ops;
fb->info.disp = disp;
fb->info.currcon = -1;
strcpy(fb->info.fontname, fontname);
fb->info.changevar = NULL;
fb->info.switch_con = &sbusfbcon_switch;
fb->info.updatevar = &sbusfbcon_updatevar;
fb->info.flags = FBINFO_FLAG_DEFAULT;
fb->cursor.hwsize.fbx = 32;
fb->cursor.hwsize.fby = 32;
if (depth > 1 && !fb->color_map)
fb->color_map = kmalloc(256 * 3, GFP_ATOMIC);
switch(fbtype) {
#ifdef CONFIG_FB_CREATOR
case FBTYPE_CREATOR:
p = creatorfb_init(fb); break;
#endif
#ifdef CONFIG_FB_CGSIX
case FBTYPE_SUNFAST_COLOR:
p = cgsixfb_init(fb); break;
#endif
#ifdef CONFIG_FB_CGTHREE
case FBTYPE_SUN3COLOR:
p = cgthreefb_init(fb); break;
#endif
#ifdef CONFIG_FB_TCX
case FBTYPE_TCXCOLOR:
p = tcxfb_init(fb); break;
#endif
#ifdef CONFIG_FB_LEO
case FBTYPE_SUNLEO:
p = leofb_init(fb); break;
#endif
#ifdef CONFIG_FB_BWTWO
case FBTYPE_SUN2BW:
p = bwtwofb_init(fb); break;
#endif
#ifdef CONFIG_FB_CGFOURTEEN
case FBTYPE_MDICOLOR:
p = cgfourteenfb_init(fb); break;
#endif
#ifdef CONFIG_FB_P9100
case FBTYPE_P9100COLOR:
/* Temporary crock. For now we are a cg3 */
p = p9100fb_init(fb); type->fb_type = FBTYPE_SUN3COLOR; break;
#endif
}
if (!p) {
if (fb->color_map)
kfree(fb->color_map);
kfree(fb);
return;
}
if (p == SBUSFBINIT_SIZECHANGE)
goto sizechange;
disp->dispsw = &fb->dispsw;
if (fb->setcursor) {
fb->dispsw.cursor = sbusfb_cursor;
if (curblink) {
fb->cursor.blink_rate = DEFAULT_CURSOR_BLINK_RATE;
init_timer(&fb->cursor.timer);
fb->cursor.timer.expires = jiffies + fb->cursor.blink_rate;
fb->cursor.timer.data = (unsigned long)fb;
fb->cursor.timer.function = sbusfb_cursor_timer_handler;
add_timer(&fb->cursor.timer);
}
}
fb->cursor.mode = CURSOR_SHAPE;
fb->dispsw.set_font = sbusfb_set_font;
fb->setup = fb->dispsw.setup;
fb->dispsw.setup = sbusfb_disp_setup;
fb->dispsw.clear_margins = NULL;
disp->var = *var;
if (fb->blank)
disp->can_soft_blank = 1;
sbusfb_set_var(var, -1, &fb->info);
if (register_framebuffer(&fb->info) < 0) {
if (fb->color_map)
kfree(fb->color_map);
kfree(fb);
return;
}
printk(KERN_INFO "fb%d: %s\n", minor(fb->info.node), p);
}
static inline int known_card(char *name)
{
char *p;
for (p = name; *p && *p != ','; p++);
if (*p == ',') name = p + 1;
if (!strcmp(name, "cgsix") || !strcmp(name, "cgthree+"))
return FBTYPE_SUNFAST_COLOR;
if (!strcmp(name, "cgthree") || !strcmp(name, "cgRDI"))
return FBTYPE_SUN3COLOR;
if (!strcmp(name, "cgfourteen"))
return FBTYPE_MDICOLOR;
if (!strcmp(name, "leo"))
return FBTYPE_SUNLEO;
if (!strcmp(name, "bwtwo"))
return FBTYPE_SUN2BW;
if (!strcmp(name, "tcx"))
return FBTYPE_TCXCOLOR;
if (!strcmp(name, "p9100"))
return FBTYPE_P9100COLOR;
return FBTYPE_NOTYPE;
}
#ifdef CONFIG_FB_CREATOR
static void creator_fb_scan_siblings(int root)
{
int node, child;
child = prom_getchild(root);
for (node = prom_searchsiblings(child, "SUNW,ffb"); node;
node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb"))
sbusfb_init_fb(node, root, FBTYPE_CREATOR, NULL);
for (node = prom_searchsiblings(child, "SUNW,afb"); node;
node = prom_searchsiblings(prom_getsibling(node), "SUNW,afb"))
sbusfb_init_fb(node, root, FBTYPE_CREATOR, NULL);
}
static void creator_fb_scan(void)
{
int root;
creator_fb_scan_siblings(prom_root_node);
root = prom_getchild(prom_root_node);
for (root = prom_searchsiblings(root, "upa"); root;
root = prom_searchsiblings(prom_getsibling(root), "upa"))
creator_fb_scan_siblings(root);
}
#endif
int __init sbusfb_init(void)
{
int type;
struct sbus_dev *sbdp;
struct sbus_bus *sbus;
char prom_name[40];
extern int con_is_present(void);
if (!con_is_present()) return -ENXIO;
#ifdef CONFIG_FB_CREATOR
creator_fb_scan();
#endif
#ifdef CONFIG_SUN4
sbusfb_init_fb(0, 0, FBTYPE_SUN2BW, NULL);
#endif
#if defined(CONFIG_FB_CGFOURTEEN) && !defined(__sparc_v9__)
{
int root, node;
root = prom_getchild(prom_root_node);
root = prom_searchsiblings(root, "obio");
if (root &&
(node = prom_searchsiblings(prom_getchild(root), "cgfourteen"))) {
sbusfb_init_fb(node, root, FBTYPE_MDICOLOR, NULL);
}
}
#endif
if (sbus_root == NULL)
return 0;
for_all_sbusdev(sbdp, sbus) {
type = known_card(sbdp->prom_name);
if (type == FBTYPE_NOTYPE)
continue;
if (prom_getproperty(sbdp->prom_node, "emulation",
prom_name, sizeof(prom_name)) > 0) {
type = known_card(prom_name);
if (type == FBTYPE_NOTYPE)
type = known_card(sbdp->prom_name);
}
sbusfb_init_fb(sbdp->prom_node, sbdp->bus->prom_node, type, sbdp);
}
return 0;
}
MODULE_LICENSE("GPL");
/* sbuslib.c: Helper library for SBUS framebuffer drivers.
*
* Copyright (C) 2003 David S. Miller (davem@redhat.com)
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/fb.h>
#include <asm/oplib.h>
#include "sbuslib.h"
void sbusfb_fill_var(struct fb_var_screeninfo *var, int prom_node, int bpp)
{
memset(var, 0, sizeof(*var));
var->xres = prom_getintdefault(prom_node, "width", 1152);
var->yres = prom_getintdefault(prom_node, "height", 900);
var->xres_virtual = var->xres;
var->yres_virtual = var->yres;
var->bits_per_pixel = bpp;
}
EXPORT_SYMBOL(sbusfb_fill_var);
static unsigned long sbusfb_mmapsize(long size, unsigned long fbsize)
{
if (size == SBUS_MMAP_EMPTY) return 0;
if (size >= 0) return size;
return fbsize * (-size);
}
int sbusfb_mmap_helper(struct sbus_mmap_map *map,
unsigned long physbase,
unsigned long fbsize,
unsigned long iospace,
struct vm_area_struct *vma)
{
unsigned int size, page, r, map_size;
unsigned long map_offset = 0;
unsigned long off;
int i;
size = vma->vm_end - vma->vm_start;
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
return -EINVAL;
off = vma->vm_pgoff << PAGE_SHIFT;
/* To stop the swapper from even considering these pages */
vma->vm_flags |= (VM_SHM | VM_IO | VM_LOCKED);
/* Each page, see which map applies */
for (page = 0; page < size; ){
map_size = 0;
for (i = 0; map[i].size; i++)
if (map[i].voff == off+page) {
map_size = sbusfb_mmapsize(map[i].size, fbsize);
#ifdef __sparc_v9__
#define POFF_MASK (PAGE_MASK|0x1UL)
#else
#define POFF_MASK (PAGE_MASK)
#endif
map_offset = (physbase + map[i].poff) & POFF_MASK;
break;
}
if (!map_size){
page += PAGE_SIZE;
continue;
}
if (page + map_size > size)
map_size = size - page;
r = io_remap_page_range(vma,
vma->vm_start + page,
map_offset, map_size,
vma->vm_page_prot, iospace);
if (r)
return -EAGAIN;
page += map_size;
}
return 0;
}
/* sbuslib.h: SBUS fb helper library interfaces */
#ifndef _SBUSLIB_H
#define _SBUSLIB_H
struct sbus_mmap_map {
unsigned long voff;
unsigned long poff;
unsigned long size;
};
#define SBUS_MMAP_FBSIZE(n) (-n)
#define SBUS_MMAP_EMPTY 0x80000000
extern void sbusfb_fill_var(struct fb_var_screeninfo *var, int prom_node, int bpp);
struct vm_area_struct;
extern int sbusfb_mmap_helper(struct sbus_mmap_map *map,
unsigned long physbase, unsigned long fbsize,
unsigned long iospace,
struct vm_area_struct *vma);
#endif /* _SBUSLIB_H */
#include <linux/timer.h>
#include <asm/sbus.h>
#include <asm/oplib.h>
#include <asm/fbio.h>
#include <video/fbcon.h>
struct bt_regs {
volatile unsigned int addr; /* address register */
volatile unsigned int color_map; /* color map */
volatile unsigned int control; /* control register */
volatile unsigned int cursor; /* cursor map register */
};
struct fb_info_creator {
struct ffb_fbc *fbc;
struct ffb_dac *dac;
int xy_margin;
int fifo_cache;
u64 yx_margin;
int fg_cache;
int bg_cache;
int dac_rev;
};
struct fb_info_cgsix {
struct bt_regs *bt;
struct cg6_fbc *fbc;
struct cg6_thc *thc;
struct cg6_tec *tec;
volatile u32 *fhc;
};
struct fb_info_bwtwo {
struct bw2_regs *regs;
};
struct fb_info_cgthree {
struct cg3_regs *regs;
};
struct fb_info_tcx {
struct bt_regs *bt;
struct tcx_thc *thc;
struct tcx_tec *tec;
u32 *cplane;
};
struct fb_info_leo {
struct leo_lx_krn *lx_krn;
struct leo_lc_ss0_usr *lc_ss0_usr;
struct leo_ld_ss0 *ld_ss0;
struct leo_ld_ss1 *ld_ss1;
struct leo_cursor *cursor;
unsigned int extent;
};
struct fb_info_cgfourteen {
struct cg14_regs *regs;
struct cg14_cursor *cursor;
struct cg14_clut *clut;
int ramsize;
int mode;
};
struct fb_info_p9100 {
struct p9100_ctrl *ctrl;
volatile u32 *fbmem;
};
struct cg_cursor {
char enable; /* cursor is enabled */
char mode; /* cursor mode */
struct fbcurpos cpos; /* position */
struct fbcurpos chot; /* hot-spot */
struct fbcurpos size; /* size of mask & image fields */
struct fbcurpos hwsize; /* hw max size */
int bits[2][128]; /* space for mask & image bits */
char color [6]; /* cursor colors */
struct timer_list timer; /* cursor timer */
int blink_rate; /* cursor blink rate */
};
struct sbus_mmap_map {
unsigned long voff;
unsigned long poff;
unsigned long size;
};
#define SBUS_MMAP_FBSIZE(n) (-n)
#define SBUS_MMAP_EMPTY 0x80000000
struct fb_info_sbusfb {
struct fb_info info;
struct display disp;
struct display_switch dispsw;
struct fbtype type;
struct sbus_dev *sbdp;
spinlock_t lock;
int prom_node, prom_parent;
union {
struct fb_info_creator ffb;
struct fb_info_cgsix cg6;
struct fb_info_bwtwo bw2;
struct fb_info_cgthree cg3;
struct fb_info_tcx tcx;
struct fb_info_leo leo;
struct fb_info_cgfourteen cg14;
struct fb_info_p9100 p9100;
} s;
unsigned char *color_map;
struct cg_cursor cursor;
unsigned char open;
unsigned char mmaped;
unsigned char blanked;
int x_margin;
int y_margin;
int vtconsole;
int consolecnt;
int graphmode;
int emulations[4];
struct sbus_mmap_map *mmap_map;
unsigned long physbase;
int iospace;
/* Methods */
void (*setup)(struct display *);
void (*setcursor)(struct fb_info_sbusfb *);
void (*setcurshape)(struct fb_info_sbusfb *);
void (*setcursormap)(struct fb_info_sbusfb *, unsigned char *, unsigned char *, unsigned char *);
void (*loadcmap)(struct fb_info_sbusfb *, struct display *, int, int);
int (*blank)(struct fb_info_sbusfb *);
int (*unblank)(struct fb_info_sbusfb *);
void (*margins)(struct fb_info_sbusfb *, struct display *, int, int);
void (*reset)(struct fb_info_sbusfb *);
void (*fill)(struct fb_info_sbusfb *, struct display *, int, int, unsigned short *);
void (*switch_from_graph)(struct fb_info_sbusfb *);
void (*restore_palette)(struct fb_info_sbusfb *);
int (*ioctl)(struct fb_info_sbusfb *, unsigned int, unsigned long);
};
extern char *creatorfb_init(struct fb_info_sbusfb *);
extern char *cgsixfb_init(struct fb_info_sbusfb *);
extern char *cgthreefb_init(struct fb_info_sbusfb *);
extern char *tcxfb_init(struct fb_info_sbusfb *);
extern char *leofb_init(struct fb_info_sbusfb *);
extern char *bwtwofb_init(struct fb_info_sbusfb *);
extern char *cgfourteenfb_init(struct fb_info_sbusfb *);
extern char *p9100fb_init(struct fb_info_sbusfb *);
#define sbusfbinfod(disp) ((struct fb_info_sbusfb *)(disp->fb_info))
#define sbusfbinfo(info) ((struct fb_info_sbusfb *)(info))
#define CM(i, j) [3*(i)+(j)]
#define SBUSFBINIT_SIZECHANGE ((char *)-1)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment