Commit e30b0dc2 authored by David S. Miller's avatar David S. Miller

Merge nuts.ninka.net:/home/davem/src/BK/sparcwork-2.5

into nuts.ninka.net:/home/davem/src/BK/sparc-2.5
parents ab6671e9 e7f92f43
......@@ -15,12 +15,6 @@ CONFIG_EXPERIMENTAL=y
CONFIG_SYSVIPC=y
# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_SYSCTL=y
# CONFIG_LOG_BUF_SHIFT_17 is not set
# CONFIG_LOG_BUF_SHIFT_16 is not set
CONFIG_LOG_BUF_SHIFT_15=y
# CONFIG_LOG_BUF_SHIFT_14 is not set
# CONFIG_LOG_BUF_SHIFT_13 is not set
# CONFIG_LOG_BUF_SHIFT_12 is not set
CONFIG_LOG_BUF_SHIFT=15
#
......@@ -30,6 +24,7 @@ CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_OBSOLETE_MODPARM=y
# CONFIG_MODVERSIONS is not set
CONFIG_KMOD=y
#
......@@ -45,6 +40,7 @@ CONFIG_SMP=y
CONFIG_NR_CPUS=4
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_PROC_INTF=y
CONFIG_CPU_FREQ_TABLE=y
CONFIG_US3_FREQ=m
CONFIG_SPARC64=y
CONFIG_HOTPLUG=y
......@@ -89,15 +85,52 @@ CONFIG_WATCHDOG_RIO=m
#
# Graphics support
#
# CONFIG_FB is not set
CONFIG_FB=y
# CONFIG_FB_CLGEN is not set
# CONFIG_FB_PM2 is not set
# CONFIG_FB_CYBER2000 is not set
# CONFIG_FB_IMSTT is not set
# CONFIG_FB_BW2 is not set
# CONFIG_FB_CG3 is not set
CONFIG_FB_CG6=y
# CONFIG_FB_RIVA is not set
# CONFIG_FB_MATROX is not set
# CONFIG_FB_RADEON is not set
# CONFIG_FB_ATY128 is not set
# CONFIG_FB_ATY is not set
# CONFIG_FB_SIS is not set
# CONFIG_FB_NEOMAGIC is not set
# CONFIG_FB_3DFX is not set
# CONFIG_FB_VOODOO1 is not set
# CONFIG_FB_TRIDENT is not set
# CONFIG_FB_PM3 is not set
CONFIG_FB_SBUS=y
CONFIG_FB_FFB=y
# CONFIG_FB_TCX is not set
# CONFIG_FB_CG14 is not set
# CONFIG_FB_P9100 is not set
# CONFIG_FB_LEO is not set
# CONFIG_FB_PCI is not set
# CONFIG_FB_VIRTUAL is not set
#
# Console display driver support
#
# CONFIG_VGA_CONSOLE is not set
# CONFIG_MDA_CONSOLE is not set
CONFIG_PROM_CONSOLE=y
# CONFIG_PROM_CONSOLE is not set
CONFIG_DUMMY_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_PCI_CONSOLE=y
# CONFIG_FBCON_ADVANCED is not set
CONFIG_FONT_SUN8x16=y
# CONFIG_FONT_SUN12x22 is not set
CONFIG_FONTS=y
# CONFIG_FONT_8x8 is not set
# CONFIG_FONT_8x16 is not set
# CONFIG_FONT_6x11 is not set
# CONFIG_FONT_PEARL_8x8 is not set
# CONFIG_FONT_ACORN_8x8 is not set
#
# Serial drivers
......@@ -110,7 +143,7 @@ CONFIG_DUMMY_CONSOLE=y
CONFIG_SERIAL_SUNCORE=y
CONFIG_SERIAL_SUNZILOG=y
CONFIG_SERIAL_SUNSU=y
# CONFIG_SERIAL_SUNSAB is not set
CONFIG_SERIAL_SUNSAB=m
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
......@@ -190,7 +223,7 @@ CONFIG_BLK_DEV_ADMA=y
CONFIG_BLK_DEV_ALI15X3=y
# CONFIG_WDC_ALI15X3 is not set
# CONFIG_BLK_DEV_AMD74XX is not set
CONFIG_BLK_DEV_CMD64X=y
# CONFIG_BLK_DEV_CMD64X is not set
# CONFIG_BLK_DEV_TRIFLEX is not set
# CONFIG_BLK_DEV_CY82C693 is not set
# CONFIG_BLK_DEV_CS5520 is not set
......@@ -198,7 +231,7 @@ CONFIG_BLK_DEV_CMD64X=y
# CONFIG_BLK_DEV_HPT366 is not set
# CONFIG_BLK_DEV_SC1200 is not set
# CONFIG_BLK_DEV_PIIX is not set
CONFIG_BLK_DEV_NS87415=y
# CONFIG_BLK_DEV_NS87415 is not set
# CONFIG_BLK_DEV_OPTI621 is not set
# CONFIG_BLK_DEV_PDC202XX_OLD is not set
# CONFIG_BLK_DEV_PDC202XX_NEW is not set
......@@ -221,9 +254,9 @@ CONFIG_SCSI=y
#
CONFIG_BLK_DEV_SD=y
CONFIG_SD_EXTRA_DEVS=40
CONFIG_CHR_DEV_ST=y
CONFIG_CHR_DEV_ST=m
CONFIG_CHR_DEV_OSST=m
CONFIG_BLK_DEV_SR=y
CONFIG_BLK_DEV_SR=m
CONFIG_BLK_DEV_SR_VENDOR=y
CONFIG_SR_EXTRA_DEVS=2
CONFIG_CHR_DEV_SG=m
......@@ -320,11 +353,12 @@ CONFIG_INET_AH=y
CONFIG_INET_ESP=y
CONFIG_XFRM_USER=m
CONFIG_IPV6=m
CONFIG_IPV6_PRIVACY=y
#
# SCTP Configuration (EXPERIMENTAL)
#
CONFIG_IPV6_SCTP__=y
CONFIG_IPV6_SCTP__=m
CONFIG_IP_SCTP=m
# CONFIG_SCTP_ADLER32 is not set
# CONFIG_SCTP_DBG_MSG is not set
......@@ -395,6 +429,7 @@ CONFIG_TUN=m
# Ethernet (10 or 100Mbit)
#
CONFIG_NET_ETHERNET=y
# CONFIG_MII is not set
CONFIG_SUNLANCE=y
CONFIG_HAPPYMEAL=y
CONFIG_SUNBMAC=m
......@@ -402,6 +437,7 @@ CONFIG_SUNQE=m
CONFIG_SUNGEM=y
CONFIG_NET_VENDOR_3COM=y
CONFIG_VORTEX=m
CONFIG_TYPHOON=m
#
# Tulip family network device support
......@@ -419,9 +455,11 @@ CONFIG_NET_PCI=y
CONFIG_PCNET32=m
# CONFIG_AMD8111_ETH is not set
CONFIG_ADAPTEC_STARFIRE=m
CONFIG_ADAPTEC_STARFIRE_NAPI=y
CONFIG_B44=m
CONFIG_DGRS=m
CONFIG_EEPRO100=m
# CONFIG_EEPRO100_PIO is not set
CONFIG_E100=m
CONFIG_FEALNX=m
CONFIG_NATSEMI=m
......@@ -940,6 +978,7 @@ CONFIG_BT_USB_ZERO_PACKET=y
CONFIG_BT_HCIUART=m
CONFIG_BT_HCIUART_H4=y
CONFIG_BT_HCIUART_BCSP=y
CONFIG_BT_HCIUART_BCSP_TXCRC=y
CONFIG_BT_HCIVHCI=m
#
......
......@@ -114,6 +114,8 @@ extern void _do_write_unlock(rwlock_t *rw);
extern unsigned long phys_base;
extern unsigned long pfn_base;
extern unsigned int sys_call_table[];
/* used by various drivers */
#ifdef CONFIG_SMP
#ifndef CONFIG_DEBUG_SPINLOCK
......@@ -374,3 +376,6 @@ EXPORT_SYMBOL(do_BUG);
/* for ns8703 */
EXPORT_SYMBOL(ns87303_lock);
/* for solaris compat module */
EXPORT_SYMBOL_GPL(sys_call_table);
......@@ -16,6 +16,7 @@
#include <asm/signal.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/thread_info.h>
#include "conv.h"
......
......@@ -520,7 +520,7 @@ static void happy_meal_tcvr_write(struct happy_meal *hp,
ASD(("happy_meal_tcvr_write: reg=0x%02x value=%04x\n", reg, value));
/* Welcome to Sun Microsystems, can I take your order please? */
if (!hp->happy_flags & HFLAG_FENABLE)
if (!(hp->happy_flags & HFLAG_FENABLE))
return happy_meal_bb_write(hp, tregs, reg, value);
/* Would you like fries with that? */
......
......@@ -376,19 +376,19 @@ config FB_SUN3
bool "Sun3 framebuffer support"
depends on FB && (SUN3 || SUN3X)
config FB_BWTWO
config FB_BW2
bool "BWtwo support"
depends on FB && ((SPARC32 || SPARC64) && FB_SBUS || (SUN3 || SUN3X) && FB_SUN3)
help
This is the frame buffer device driver for the BWtwo frame buffer.
config FB_CGTHREE
config FB_CG3
bool "CGthree support"
depends on FB && ((SPARC32 || SPARC64) && FB_SBUS || (SUN3 || SUN3X) && FB_SUN3)
help
This is the frame buffer device driver for the CGthree frame buffer.
config FB_CGSIX
config FB_CG6
bool "CGsix (GX,TurboGX) support"
depends on FB && ((SPARC32 || SPARC64) && FB_SBUS || (SUN3 || SUN3X) && FB_SUN3)
help
......@@ -809,30 +809,30 @@ 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"
depends on FB_SBUS && SPARC32
depends on FB_SBUS
help
This is the frame buffer device driver for the TCX 24/8bit frame
buffer.
config FB_CGFOURTEEN
config FB_CG14
bool "CGfourteen (SX) support"
depends on FB_SBUS && SPARC32
depends on FB_SBUS
help
This is the frame buffer device driver for the CGfourteen frame
buffer on Desktop SPARCsystems with the SX graphics option.
config FB_P9100
bool "P9100 (Sparcbook 3 only) support"
depends on FB_SBUS && SPARC32
depends on FB_SBUS
help
This is the frame buffer device driver for the P9100 card
supported on Sparcbook 3 machines.
......
......@@ -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,22 @@ 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_LEO) += leofb.o sbusfb.o
obj-$(CONFIG_FB_FFB) += ffb.o sbuslib.o cfbimgblt.o cfbcopyarea.o
obj-$(CONFIG_FB_CG6) += cg6.o sbuslib.o cfbimgblt.o cfbcopyarea.o
obj-$(CONFIG_FB_CG3) += cg3.o sbuslib.o cfbimgblt.o cfbcopyarea.o \
cfbfillrect.o
obj-$(CONFIG_FB_BW2) += bw2.o sbuslib.o cfbimgblt.o cfbcopyarea.o \
cfbfillrect.o
obj-$(CONFIG_FB_CG14) += cg14.o sbuslib.o cfbimgblt.o cfbcopyarea.o \
cfbfillrect.o
obj-$(CONFIG_FB_P9100) += p9100.o sbuslib.o cfbimgblt.o cfbcopyarea.o \
cfbfillrect.o
obj-$(CONFIG_FB_TCX) += tcx.o sbuslib.o cfbimgblt.o cfbcopyarea.o \
cfbfillrect.o
# Files generated that shall be removed upon make clean
clean-files := promcon_tbl.c
......
/* bw2.c: BWTWO frame buffer driver
*
* Copyright (C) 2003 David S. Miller (davem@redhat.com)
* Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*
* Driver layout based loosely on tgafb.c, see that file for credits.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fb.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <asm/sbus.h>
#include <asm/oplib.h>
#include <asm/fbio.h>
#ifdef CONFIG_SPARC32
#include <asm/sun4paddr.h>
#endif
#include "sbuslib.h"
/*
* Local functions.
*/
static int bw2_check_var(struct fb_var_screeninfo *, struct fb_info *);
static int bw2_set_par(struct fb_info *);
static int bw2_blank(int, struct fb_info *);
static int bw2_mmap(struct fb_info *, struct file *, struct vm_area_struct *);
/*
* Frame buffer operations
*/
static struct fb_ops bw2_ops = {
.owner = THIS_MODULE,
.fb_check_var = bw2_check_var,
.fb_set_par = bw2_set_par,
.fb_blank = bw2_blank,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_mmap = bw2_mmap,
.fb_cursor = soft_cursor,
};
/* OBio addresses for the bwtwo registers */
#define BWTWO_REGISTER_OFFSET 0x400000
struct bt_regs {
volatile u32 addr;
volatile u32 color_map;
volatile u32 control;
volatile u32 cursor;
};
struct bw2_regs {
struct bt_regs cmap;
volatile u8 control;
volatile u8 status;
volatile u8 cursor_start;
volatile u8 cursor_end;
volatile u8 h_blank_start;
volatile u8 h_blank_end;
volatile u8 h_sync_start;
volatile u8 h_sync_end;
volatile u8 comp_sync_end;
volatile u8 v_blank_start_high;
volatile u8 v_blank_start_low;
volatile u8 v_blank_end;
volatile u8 v_sync_start;
volatile u8 v_sync_end;
volatile u8 xfer_holdoff_start;
volatile u8 xfer_holdoff_end;
};
/* Status Register Constants */
#define BWTWO_SR_RES_MASK 0x70
#define BWTWO_SR_1600_1280 0x50
#define BWTWO_SR_1152_900_76_A 0x40
#define BWTWO_SR_1152_900_76_B 0x60
#define BWTWO_SR_ID_MASK 0x0f
#define BWTWO_SR_ID_MONO 0x02
#define BWTWO_SR_ID_MONO_ECL 0x03
#define BWTWO_SR_ID_MSYNC 0x04
#define BWTWO_SR_ID_NOCONN 0x0a
/* Control Register Constants */
#define BWTWO_CTL_ENABLE_INTS 0x80
#define BWTWO_CTL_ENABLE_VIDEO 0x40
#define BWTWO_CTL_ENABLE_TIMING 0x20
#define BWTWO_CTL_ENABLE_CURCMP 0x10
#define BWTWO_CTL_XTAL_MASK 0x0C
#define BWTWO_CTL_DIVISOR_MASK 0x03
/* Status Register Constants */
#define BWTWO_STAT_PENDING_INT 0x80
#define BWTWO_STAT_MSENSE_MASK 0x70
#define BWTWO_STAT_ID_MASK 0x0f
struct bw2_par {
spinlock_t lock;
struct bw2_regs *regs;
u32 flags;
#define BW2_FLAG_BLANKED 0x00000001
unsigned long physbase;
unsigned long fbsize;
struct sbus_dev *sdev;
struct list_head list;
};
/**
* bw2_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 bw2_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
if (var->bits_per_pixel != 8)
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;
return 0;
}
/**
* bw2_set_par - Optional function. Alters the hardware state.
* @info: frame buffer structure that represents a single frame buffer
*/
static int
bw2_set_par(struct fb_info *info)
{
return 0;
}
/**
* bw2_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
bw2_blank(int blank, struct fb_info *info)
{
struct bw2_par *par = (struct bw2_par *) info->par;
struct bw2_regs *regs = par->regs;
unsigned long flags;
u8 val;
spin_lock_irqsave(&par->lock, flags);
switch (blank) {
case 0: /* Unblanking */
val = sbus_readb(&regs->control);
val |= BWTWO_CTL_ENABLE_VIDEO;
sbus_writeb(val, &regs->control);
par->flags &= ~BW2_FLAG_BLANKED;
break;
case 1: /* Normal blanking */
case 2: /* VESA blank (vsync off) */
case 3: /* VESA blank (hsync off) */
case 4: /* Poweroff */
val = sbus_readb(&regs->control);
val &= ~BWTWO_CTL_ENABLE_VIDEO;
sbus_writeb(val, &regs->control);
par->flags |= BW2_FLAG_BLANKED;
break;
}
spin_unlock_irqrestore(&par->lock, flags);
return 0;
}
static struct sbus_mmap_map bw2_mmap_map[] = {
{ 0, 0, SBUS_MMAP_FBSIZE(1) },
{ 0, 0, 0 }
};
static int bw2_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
{
struct bw2_par *par = (struct bw2_par *)info->par;
return sbusfb_mmap_helper(bw2_mmap_map,
par->physbase, par->fbsize,
(par->sdev ?
par->sdev->reg_addrs[0].which_io :
0),
vma);
}
/*
* Initialisation
*/
static void
bw2_init_fix(struct fb_info *info, int linebytes)
{
strncpy(info->fix.id, "bwtwo", sizeof(info->fix.id) - 1);
info->fix.id[sizeof(info->fix.id)-1] = 0;
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_MONO01;
info->fix.line_length = linebytes;
info->fix.accel = FB_ACCEL_SUN_BWTWO;
}
static u8 bw2regs_1600[] __initdata = {
0x14, 0x8b, 0x15, 0x28, 0x16, 0x03, 0x17, 0x13,
0x18, 0x7b, 0x19, 0x05, 0x1a, 0x34, 0x1b, 0x2e,
0x1c, 0x00, 0x1d, 0x0a, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x21, 0
};
static u8 bw2regs_ecl[] __initdata = {
0x14, 0x65, 0x15, 0x1e, 0x16, 0x04, 0x17, 0x0c,
0x18, 0x5e, 0x19, 0x03, 0x1a, 0xa7, 0x1b, 0x23,
0x1c, 0x00, 0x1d, 0x08, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x20, 0
};
static u8 bw2regs_analog[] __initdata = {
0x14, 0xbb, 0x15, 0x2b, 0x16, 0x03, 0x17, 0x13,
0x18, 0xb0, 0x19, 0x03, 0x1a, 0xa6, 0x1b, 0x22,
0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x20, 0
};
static u8 bw2regs_76hz[] __initdata = {
0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f,
0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a,
0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x24, 0
};
static u8 bw2regs_66hz[] __initdata = {
0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14,
0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24,
0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x20, 0
};
static void bw2_do_default_mode(struct bw2_par *par, struct fb_info *info,
int *linebytes)
{
u8 status, mon;
u8 *p;
status = sbus_readb(&par->regs->status);
mon = status & BWTWO_SR_RES_MASK;
switch (status & BWTWO_SR_ID_MASK) {
case BWTWO_SR_ID_MONO_ECL:
if (mon == BWTWO_SR_1600_1280) {
p = bw2regs_1600;
info->var.xres = info->var.xres_virtual = 1600;
info->var.yres = info->var.yres_virtual = 1280;
*linebytes = 1600 / 8;
} else
p = bw2regs_ecl;
break;
case BWTWO_SR_ID_MONO:
p = bw2regs_analog;
break;
case BWTWO_SR_ID_MSYNC:
if (mon == BWTWO_SR_1152_900_76_A ||
mon == BWTWO_SR_1152_900_76_B)
p = bw2regs_76hz;
else
p = bw2regs_66hz;
break;
case BWTWO_SR_ID_NOCONN:
return;
default:
prom_printf("bw2: can't handle SR %02x\n",
status);
prom_halt();
}
for ( ; *p; p += 2) {
u8 *regp = &((u8 *)par->regs)[p[0]];
sbus_writeb(p[1], regp);
}
}
struct all_info {
struct fb_info info;
struct bw2_par par;
struct list_head list;
};
static LIST_HEAD(bw2_list);
static void bw2_init_one(struct sbus_dev *sdev)
{
struct all_info *all;
struct resource *resp;
#ifdef CONFIG_SUN4
struct resource res;
#endif
int linebytes;
all = kmalloc(sizeof(*all), GFP_KERNEL);
if (!all) {
printk(KERN_ERR "bw2: Cannot allocate memory.\n");
return;
}
memset(all, 0, sizeof(*all));
INIT_LIST_HEAD(&all->list);
spin_lock_init(&all->par.lock);
all->par.sdev = sdev;
#ifdef CONFIG_SUN4
if (!sdev) {
all->par.physbase = sun4_bwtwo_physaddr;
res.start = sun4_bwtwo_physaddr;
res.end = res.start + BWTWO_REGISTER_OFFSET + sizeof(struct bw2_regs) - 1;
res.flags = IORESOURCE_IO;
resp = &res;
all->info.var.xres = all->info.var.xres_virtual = 1152;
all->info.var.yres = all->info.var.yres_virtual = 900;
all->info.bits_per_pixel = 1;
linebytes = 1152 / 8;
} else
#else
{
if (!sdev)
BUG();
all->par.physbase = sdev->reg_addrs[0].phys_addr;
resp = &sdev->resource[0];
sbusfb_fill_var(&all->info.var, (sdev ? sdev->prom_node : 0), 1);
linebytes = prom_getintdefault(sdev->prom_node, "linebytes",
all->info.var.xres);
}
#endif
all->par.regs = (struct bw2_regs *)
sbus_ioremap(resp, BWTWO_REGISTER_OFFSET,
sizeof(struct bw2_regs), "bw2 regs");
if (sdev && !prom_getbool(sdev->prom_node, "width"))
bw2_do_default_mode(&all->par, &all->info, &linebytes);
all->par.fbsize = PAGE_ALIGN(linebytes * all->info.var.yres);
all->info.node = NODEV;
all->info.flags = FBINFO_FLAG_DEFAULT;
all->info.fbops = &bw2_ops;
#if defined(CONFIG_SPARC32)
if (sdev)
all->info.screen_base = (char *)
prom_getintdefault(sdev->prom_node, "address", 0);
#endif
if (!all->info.screen_base)
all->info.screen_base = (char *)
sbus_ioremap(resp, 0, all->par.fbsize, "bw2 ram");
all->info.currcon = -1;
all->info.par = &all->par;
bw2_blank(0, &all->info);
bw2_set_par(&all->info);
bw2_init_fix(&all->info, linebytes);
if (register_framebuffer(&all->info) < 0) {
printk(KERN_ERR "bw2: Could not register framebuffer.\n");
kfree(all);
return;
}
list_add(&all->list, &bw2_list);
printk("bw2: bwtwo at %lx:%lx\n",
(long) (sdev ? sdev->reg_addrs[0].which_io : 0),
(long) all->par.physbase);
}
int __init bw2_init(void)
{
struct sbus_bus *sbus;
struct sbus_dev *sdev;
#ifdef CONFIG_SUN4
bw2_init_one(NULL);
#endif
for_all_sbusdev(sdev, sbus) {
if (!strcmp(sdev->prom_name, "bwtwo"))
bw2_init_one(sdev);
}
return 0;
}
void __exit bw2_exit(void)
{
struct list_head *pos, *tmp;
list_for_each_safe(pos, tmp, &bw2_list) {
struct all_info *all = list_entry(pos, typeof(*all), list);
unregister_framebuffer(&all->info);
kfree(all);
}
}
int __init
bw2_setup(char *arg)
{
/* No cmdline options yet... */
return 0;
}
#ifdef MODULE
module_init(bw2_init);
module_exit(bw2_exit);
#endif
MODULE_DESCRIPTION("framebuffer driver for BWTWO chipsets");
MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
MODULE_LICENSE("GPL");
/* $Id: bwtwofb.c,v 1.15 2001/09/19 00:04:33 davem Exp $
* bwtwofb.c: BWtwo frame buffer driver
*
* Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz)
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
* Copyright (C) 1998 Pavel Machek (pavel@ucw.cz)
*/
#include <linux/config.h>
#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 <asm/io.h>
#if !defined(__sparc_v9__) && !defined(__mc68000__)
#include <asm/sun4paddr.h>
#endif
#include <video/fbcon-mfb.h>
/* OBio addresses for the bwtwo registers */
#define BWTWO_REGISTER_OFFSET 0x400000
struct bw2_regs {
struct bt_regs bt;
volatile u8 control;
volatile u8 status;
volatile u8 cursor_start;
volatile u8 cursor_end;
volatile u8 h_blank_start;
volatile u8 h_blank_end;
volatile u8 h_sync_start;
volatile u8 h_sync_end;
volatile u8 comp_sync_end;
volatile u8 v_blank_start_high;
volatile u8 v_blank_start_low;
volatile u8 v_blank_end;
volatile u8 v_sync_start;
volatile u8 v_sync_end;
volatile u8 xfer_holdoff_start;
volatile u8 xfer_holdoff_end;
};
/* Status Register Constants */
#define BWTWO_SR_RES_MASK 0x70
#define BWTWO_SR_1600_1280 0x50
#define BWTWO_SR_1152_900_76_A 0x40
#define BWTWO_SR_1152_900_76_B 0x60
#define BWTWO_SR_ID_MASK 0x0f
#define BWTWO_SR_ID_MONO 0x02
#define BWTWO_SR_ID_MONO_ECL 0x03
#define BWTWO_SR_ID_MSYNC 0x04
#define BWTWO_SR_ID_NOCONN 0x0a
/* Control Register Constants */
#define BWTWO_CTL_ENABLE_INTS 0x80
#define BWTWO_CTL_ENABLE_VIDEO 0x40
#define BWTWO_CTL_ENABLE_TIMING 0x20
#define BWTWO_CTL_ENABLE_CURCMP 0x10
#define BWTWO_CTL_XTAL_MASK 0x0C
#define BWTWO_CTL_DIVISOR_MASK 0x03
/* Status Register Constants */
#define BWTWO_STAT_PENDING_INT 0x80
#define BWTWO_STAT_MSENSE_MASK 0x70
#define BWTWO_STAT_ID_MASK 0x0f
static struct sbus_mmap_map bw2_mmap_map[] = {
{ 0, 0, SBUS_MMAP_FBSIZE(1) },
{ 0, 0, 0 }
};
static int bw2_blank (struct fb_info_sbusfb *fb)
{
unsigned long flags;
u8 tmp;
spin_lock_irqsave(&fb->lock, flags);
tmp = sbus_readb(&fb->s.bw2.regs->control);
tmp &= ~BWTWO_CTL_ENABLE_VIDEO;
sbus_writeb(tmp, &fb->s.bw2.regs->control);
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
}
static int bw2_unblank (struct fb_info_sbusfb *fb)
{
unsigned long flags;
u8 tmp;
spin_lock_irqsave(&fb->lock, flags);
tmp = sbus_readb(&fb->s.bw2.regs->control);
tmp |= BWTWO_CTL_ENABLE_VIDEO;
sbus_writeb(tmp, &fb->s.bw2.regs->control);
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
}
static void bw2_margins (struct fb_info_sbusfb *fb, struct display *p,
int x_margin, int y_margin)
{
fb->info.screen_base += (y_margin - fb->y_margin) *
p->fb_info->fix.line_length + ((x_margin - fb->x_margin) >> 3);
}
static u8 bw2regs_1600[] __initdata = {
0x14, 0x8b, 0x15, 0x28, 0x16, 0x03, 0x17, 0x13,
0x18, 0x7b, 0x19, 0x05, 0x1a, 0x34, 0x1b, 0x2e,
0x1c, 0x00, 0x1d, 0x0a, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x21, 0
};
static u8 bw2regs_ecl[] __initdata = {
0x14, 0x65, 0x15, 0x1e, 0x16, 0x04, 0x17, 0x0c,
0x18, 0x5e, 0x19, 0x03, 0x1a, 0xa7, 0x1b, 0x23,
0x1c, 0x00, 0x1d, 0x08, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x20, 0
};
static u8 bw2regs_analog[] __initdata = {
0x14, 0xbb, 0x15, 0x2b, 0x16, 0x03, 0x17, 0x13,
0x18, 0xb0, 0x19, 0x03, 0x1a, 0xa6, 0x1b, 0x22,
0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x20, 0
};
static u8 bw2regs_76hz[] __initdata = {
0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f,
0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a,
0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x24, 0
};
static u8 bw2regs_66hz[] __initdata = {
0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14,
0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24,
0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x20, 0
};
static char idstring[60] __initdata = { 0 };
char __init *bwtwofb_init(struct fb_info_sbusfb *fb)
{
struct fb_fix_screeninfo *fix = &fb->info.fix;
struct display *disp = &fb->disp;
struct fbtype *type = &fb->type;
#ifdef CONFIG_SUN4
unsigned long phys = sun4_bwtwo_physaddr;
struct resource res;
#else
unsigned long phys = fb->sbdp->reg_addrs[0].phys_addr;
#endif
struct resource *resp;
unsigned int vaddr;
#ifndef FBCON_HAS_MFB
return NULL;
#endif
#ifdef CONFIG_SUN4
res.start = phys;
res.end = res.start + BWTWO_REGISTER_OFFSET + sizeof(struct bw2_regs) - 1;
res.flags = IORESOURCE_IO | (fb->iospace & 0xff);
resp = &res;
#else
resp = &fb->sbdp->resource[0];
#endif
if (!fb->s.bw2.regs) {
fb->s.bw2.regs = (struct bw2_regs *)
sbus_ioremap(resp, BWTWO_REGISTER_OFFSET,
sizeof(struct bw2_regs), "bw2 regs");
if ((!ARCH_SUN4) && (!prom_getbool(fb->prom_node, "width"))) {
/* Ugh, broken PROM didn't initialize us.
* Let's deal with this ourselves.
*/
u8 status, mon;
u8 *p;
int sizechange = 0;
status = sbus_readb(&fb->s.bw2.regs->status);
mon = status & BWTWO_SR_RES_MASK;
switch (status & BWTWO_SR_ID_MASK) {
case BWTWO_SR_ID_MONO_ECL:
if (mon == BWTWO_SR_1600_1280) {
p = bw2regs_1600;
fb->type.fb_width = 1600;
fb->type.fb_height = 1280;
sizechange = 1;
} else
p = bw2regs_ecl;
break;
case BWTWO_SR_ID_MONO:
p = bw2regs_analog;
break;
case BWTWO_SR_ID_MSYNC:
if (mon == BWTWO_SR_1152_900_76_A ||
mon == BWTWO_SR_1152_900_76_B)
p = bw2regs_76hz;
else
p = bw2regs_66hz;
break;
case BWTWO_SR_ID_NOCONN:
return NULL;
default:
#ifndef CONFIG_FB_SUN3
prom_printf("bw2: can't handle SR %02x\n",
status);
prom_halt();
#endif
return NULL; /* fool gcc. */
}
for ( ; *p; p += 2) {
u8 *regp = &((u8 *)fb->s.bw2.regs)[p[0]];
sbus_writeb(p[1], regp);
}
}
}
strcpy(fb->info.modename, "BWtwo");
strcpy(fix->id, "BWtwo");
fix->line_length = fb->info.var.xres_virtual >> 3;
fix->accel = FB_ACCEL_SUN_BWTWO;
disp->scrollmode = SCROLL_YREDRAW;
disp->inverse = 1;
if (!fb->info.screen_base) {
fb->info.screen_base = (char *)
sbus_ioremap(resp, 0, type->fb_size, "bw2 ram");
}
fb->info.screen_base += fix->line_length * fb->y_margin + (fb->x_margin >> 3);
fb->dispsw = fbcon_mfb;
fix->visual = FB_VISUAL_MONO01;
#ifndef CONFIG_SUN4
fb->blank = bw2_blank;
fb->unblank = bw2_unblank;
prom_getproperty(fb->sbdp->prom_node, "address",
(char *)&vaddr, sizeof(vaddr));
fb->physbase = __get_phys((unsigned long)vaddr);
#endif
fb->margins = bw2_margins;
fb->mmap_map = bw2_mmap_map;
#ifdef __sparc_v9__
sprintf(idstring, "bwtwo at %016lx", phys);
#else
sprintf(idstring, "bwtwo at %x.%08lx", fb->iospace, phys);
#endif
return idstring;
}
MODULE_LICENSE("GPL");
......@@ -65,14 +65,14 @@ static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
// Single word
if (last)
first &= last;
FB_WRITEL((*src & first) | (FB_READL(dst) & ~first),
FB_WRITEL((FB_READL(src) & first) | (FB_READL(dst) & ~first),
dst);
} else {
// Multiple destination words
// Leading bits
if (first) {
FB_WRITEL((*src & first) | (FB_READL(dst) &
FB_WRITEL((FB_READL(src) & first) | (FB_READL(dst) &
~first), dst);
dst++;
src++;
......@@ -82,21 +82,21 @@ static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
// Main chunk
n /= BITS_PER_LONG;
while (n >= 8) {
FB_WRITEL(*src++, dst++);
FB_WRITEL(*src++, dst++);
FB_WRITEL(*src++, dst++);
FB_WRITEL(*src++, dst++);
FB_WRITEL(*src++, dst++);
FB_WRITEL(*src++, dst++);
FB_WRITEL(*src++, dst++);
FB_WRITEL(*src++, dst++);
FB_WRITEL(FB_READL(src++), dst++);
FB_WRITEL(FB_READL(src++), dst++);
FB_WRITEL(FB_READL(src++), dst++);
FB_WRITEL(FB_READL(src++), dst++);
FB_WRITEL(FB_READL(src++), dst++);
FB_WRITEL(FB_READL(src++), dst++);
FB_WRITEL(FB_READL(src++), dst++);
FB_WRITEL(FB_READL(src++), dst++);
n -= 8;
}
while (n--)
FB_WRITEL(*src++, dst++);
FB_WRITEL(FB_READL(src++), dst++);
// Trailing bits
if (last)
FB_WRITEL((*src & last) | (FB_READL(dst) &
FB_WRITEL((FB_READL(src) & last) | (FB_READL(dst) &
~last), dst);
}
} else {
......@@ -111,22 +111,22 @@ static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
first &= last;
if (shift > 0) {
// Single source word
FB_WRITEL(((*src >> right) & first) |
FB_WRITEL(((FB_READL(src) >> right) & first) |
(FB_READL(dst) & ~first), dst);
} else if (src_idx+n <= BITS_PER_LONG) {
// Single source word
FB_WRITEL(((*src << left) & first) |
FB_WRITEL(((FB_READL(src) << left) & first) |
(FB_READL(dst) & ~first), dst);
} else {
// 2 source words
d0 = *src++;
d1 = *src;
d0 = FB_READL(src++);
d1 = FB_READL(src);
FB_WRITEL(((d0<<left | d1>>right) & first) |
(FB_READL(dst) & ~first), dst);
}
} else {
// Multiple destination words
d0 = *src++;
d0 = FB_READL(src++);
// Leading bits
if (shift > 0) {
// Single source word
......@@ -136,7 +136,7 @@ static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
n -= BITS_PER_LONG-dst_idx;
} else {
// 2 source words
d1 = *src++;
d1 = FB_READL(src++);
FB_WRITEL(((d0<<left | d1>>right) & first) |
(FB_READL(dst) & ~first), dst);
d0 = d1;
......@@ -148,22 +148,22 @@ static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
m = n % BITS_PER_LONG;
n /= BITS_PER_LONG;
while (n >= 4) {
d1 = *src++;
d1 = FB_READL(src++);
FB_WRITEL(d0 << left | d1 >> right, dst++);
d0 = d1;
d1 = *src++;
d1 = FB_READL(src++);
FB_WRITEL(d0 << left | d1 >> right, dst++);
d0 = d1;
d1 = *src++;
d1 = FB_READL(src++);
FB_WRITEL(d0 << left | d1 >> right, dst++);
d0 = d1;
d1 = *src++;
d1 = FB_READL(src++);
FB_WRITEL(d0 << left | d1 >> right, dst++);
d0 = d1;
n -= 4;
}
while (n--) {
d1 = *src++;
d1 = FB_READL(src++);
FB_WRITEL(d0 << left | d1 >> right, dst++);
d0 = d1;
}
......@@ -177,7 +177,7 @@ static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
dst);
} else {
// 2 source words
d1 = *src;
d1 = FB_READL(src);
FB_WRITEL(((d0<<left | d1>>right) &
last) | (FB_READL(dst) &
~last), dst);
......@@ -220,13 +220,13 @@ static void bitcpy_rev(unsigned long *dst, int dst_idx,
// Single word
if (last)
first &= last;
FB_WRITEL((*src & first) | (FB_READL(dst) & ~first),
FB_WRITEL((FB_READL(src) & first) | (FB_READL(dst) & ~first),
dst);
} else {
// Multiple destination words
// Leading bits
if (first) {
FB_WRITEL((*src & first) | (FB_READL(dst) &
FB_WRITEL((FB_READL(src) & first) | (FB_READL(dst) &
~first), dst);
dst--;
src--;
......@@ -236,22 +236,22 @@ static void bitcpy_rev(unsigned long *dst, int dst_idx,
// Main chunk
n /= BITS_PER_LONG;
while (n >= 8) {
FB_WRITEL(*src--, dst--);
FB_WRITEL(*src--, dst--);
FB_WRITEL(*src--, dst--);
FB_WRITEL(*src--, dst--);
FB_WRITEL(*src--, dst--);
FB_WRITEL(*src--, dst--);
FB_WRITEL(*src--, dst--);
FB_WRITEL(*src--, dst--);
FB_WRITEL(FB_READL(src--), dst--);
FB_WRITEL(FB_READL(src--), dst--);
FB_WRITEL(FB_READL(src--), dst--);
FB_WRITEL(FB_READL(src--), dst--);
FB_WRITEL(FB_READL(src--), dst--);
FB_WRITEL(FB_READL(src--), dst--);
FB_WRITEL(FB_READL(src--), dst--);
FB_WRITEL(FB_READL(src--), dst--);
n -= 8;
}
while (n--)
FB_WRITEL(*src--, dst--);
FB_WRITEL(FB_READL(src--), dst--);
// Trailing bits
if (last)
FB_WRITEL((*src & last) | (FB_READL(dst) &
FB_WRITEL((FB_READL(src) & last) | (FB_READL(dst) &
~last), dst);
}
} else {
......@@ -266,22 +266,22 @@ static void bitcpy_rev(unsigned long *dst, int dst_idx,
first &= last;
if (shift < 0) {
// Single source word
FB_WRITEL((*src << left & first) |
FB_WRITEL((FB_READL(src) << left & first) |
(FB_READL(dst) & ~first), dst);
} else if (1+(unsigned long)src_idx >= n) {
// Single source word
FB_WRITEL(((*src >> right) & first) |
FB_WRITEL(((FB_READL(src) >> right) & first) |
(FB_READL(dst) & ~first), dst);
} else {
// 2 source words
d0 = *src--;
d1 = *src;
d0 = FB_READL(src--);
d1 = FB_READL(src);
FB_WRITEL(((d0>>right | d1<<left) & first) |
(FB_READL(dst) & ~first), dst);
}
} else {
// Multiple destination words
d0 = *src--;
d0 = FB_READL(src--);
// Leading bits
if (shift < 0) {
// Single source word
......@@ -291,7 +291,7 @@ static void bitcpy_rev(unsigned long *dst, int dst_idx,
n -= dst_idx+1;
} else {
// 2 source words
d1 = *src--;
d1 = FB_READL(src--);
FB_WRITEL(((d0>>right | d1<<left) & first) |
(FB_READL(dst) & ~first), dst);
d0 = d1;
......@@ -303,22 +303,22 @@ static void bitcpy_rev(unsigned long *dst, int dst_idx,
m = n % BITS_PER_LONG;
n /= BITS_PER_LONG;
while (n >= 4) {
d1 = *src--;
d1 = FB_READL(src--);
FB_WRITEL(d0 >> right | d1 << left, dst--);
d0 = d1;
d1 = *src--;
d1 = FB_READL(src--);
FB_WRITEL(d0 >> right | d1 << left, dst--);
d0 = d1;
d1 = *src--;
d1 = FB_READL(src--);
FB_WRITEL(d0 >> right | d1 << left, dst--);
d0 = d1;
d1 = *src--;
d1 = FB_READL(src--);
FB_WRITEL(d0 >> right | d1 << left, dst--);
d0 = d1;
n -= 4;
}
while (n--) {
d1 = *src--;
d1 = FB_READL(src--);
FB_WRITEL(d0 >> right | d1 << left, dst--);
d0 = d1;
}
......@@ -332,7 +332,7 @@ static void bitcpy_rev(unsigned long *dst, int dst_idx,
dst);
} else {
// 2 source words
d1 = *src;
d1 = FB_READL(src);
FB_WRITEL(((d0>>right | d1<<left) &
last) | (FB_READL(dst) &
~last), dst);
......@@ -410,8 +410,8 @@ void cfb_copyarea(struct fb_info *p, struct fb_copyarea *area)
dst_idx &= (BYTES_PER_LONG-1);
src += src_idx >> SHIFT_PER_LONG;
src_idx &= (BYTES_PER_LONG-1);
bitcpy_rev((unsigned long*)dst, dst_idx,
(unsigned long *)src, src_idx,
bitcpy_rev(dst, dst_idx,
src, src_idx,
area->width*p->var.bits_per_pixel);
}
} else {
......@@ -420,8 +420,8 @@ void cfb_copyarea(struct fb_info *p, struct fb_copyarea *area)
dst_idx &= (BYTES_PER_LONG-1);
src += src_idx >> SHIFT_PER_LONG;
src_idx &= (BYTES_PER_LONG-1);
bitcpy((unsigned long*)dst, dst_idx,
(unsigned long *)src, src_idx,
bitcpy(dst, dst_idx,
src, src_idx,
area->width*p->var.bits_per_pixel);
dst_idx += next_line*8;
src_idx += next_line*8;
......
......@@ -73,16 +73,11 @@ static u32 cfb_tab32[] = {
0x00000000, 0xffffffff
};
#if BITS_PER_LONG == 32
#define FB_WRITEL fb_writel
#define FB_READL fb_readl
#else
#define FB_WRITEL fb_writeq
#define FB_READL fb_readq
#endif
#if defined (__BIG_ENDIAN)
#define LEFT_POS(bpp) (BITS_PER_LONG - bpp)
#define LEFT_POS(bpp) (32 - bpp)
#define NEXT_POS(pos, bpp) ((pos) -= (bpp))
#define SHIFT_HIGH(val, bits) ((val) >> (bits))
#define SHIFT_LOW(val, bits) ((val) << (bits))
......@@ -94,26 +89,25 @@ static u32 cfb_tab32[] = {
#endif
static inline void color_imageblit(struct fb_image *image, struct fb_info *p,
u8 *dst1, unsigned long start_index,
unsigned long pitch_index)
u8 *dst1, u32 start_index,
u32 pitch_index)
{
/* Draw the penguin */
unsigned long *dst, *dst2, color = 0, val, shift;
u32 *dst, *dst2, color = 0, val, shift;
int i, n, bpp = p->var.bits_per_pixel;
unsigned long null_bits = BITS_PER_LONG - bpp;
u32 null_bits = 32 - bpp;
u32 *palette = (u32 *) p->pseudo_palette;
u8 *src = image->data;
dst2 = (unsigned long *) dst1;
dst2 = (u32 *) dst1;
for (i = image->height; i--; ) {
n = image->width;
dst = (unsigned long *) dst1;
dst = (u32 *) dst1;
shift = 0;
val = 0;
if (start_index) {
unsigned long start_mask = ~(SHIFT_HIGH(~0UL,
start_index));
u32 start_mask = ~(SHIFT_HIGH(~(u32)0, start_index));
val = FB_READL(dst) & start_mask;
shift = start_index;
}
......@@ -129,14 +123,14 @@ static inline void color_imageblit(struct fb_image *image, struct fb_info *p,
FB_WRITEL(val, dst++);
val = (shift == null_bits) ? 0 :
SHIFT_LOW(color,BITS_PER_LONG - shift);
SHIFT_LOW(color, 32 - shift);
}
shift += bpp;
shift &= (BITS_PER_LONG - 1);
shift &= (32 - 1);
src++;
}
if (shift) {
unsigned long end_mask = SHIFT_HIGH(~0UL, shift);
u32 end_mask = SHIFT_HIGH(~(u32)0, shift);
FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
}
......@@ -144,40 +138,39 @@ static inline void color_imageblit(struct fb_image *image, struct fb_info *p,
if (pitch_index) {
dst2 += p->fix.line_length;
dst1 = (char *) dst2;
(unsigned long) dst1 &= ~(sizeof(unsigned long) - 1);
(unsigned long) dst1 &= ~(sizeof(u32) - 1);
start_index += pitch_index;
start_index &= BITS_PER_LONG - 1;
start_index &= 32 - 1;
}
}
}
static inline void slow_imageblit(struct fb_image *image, struct fb_info *p,
u8 *dst1, unsigned long fgcolor,
unsigned long bgcolor,
unsigned long start_index,
unsigned long pitch_index)
u8 *dst1, u32 fgcolor,
u32 bgcolor,
u32 start_index,
u32 pitch_index)
{
unsigned long shift, color = 0, bpp = p->var.bits_per_pixel;
unsigned long *dst, *dst2, val, pitch = p->fix.line_length;
unsigned long null_bits = BITS_PER_LONG - bpp;
unsigned long spitch = (image->width+7)/8;
u32 shift, color = 0, bpp = p->var.bits_per_pixel;
u32 *dst, *dst2, val, pitch = p->fix.line_length;
u32 null_bits = 32 - bpp;
u32 spitch = (image->width+7)/8;
u8 *src = image->data, *s;
unsigned long i, j, l;
u32 i, j, l;
dst2 = (unsigned long *) dst1;
dst2 = (u32 *) dst1;
for (i = image->height; i--; ) {
shift = val = 0;
l = 8;
j = image->width;
dst = (unsigned long *) dst1;
dst = (u32 *) dst1;
s = src;
/* write leading bits */
if (start_index) {
unsigned long start_mask = ~(SHIFT_HIGH(~0UL,
start_index));
u32 start_mask = ~(SHIFT_HIGH(~(u32)0, start_index));
val = FB_READL(dst) & start_mask;
shift = start_index;
}
......@@ -192,16 +185,16 @@ static inline void slow_imageblit(struct fb_image *image, struct fb_info *p,
if (shift >= null_bits) {
FB_WRITEL(val, dst++);
val = (shift == null_bits) ? 0 :
SHIFT_LOW(color,BITS_PER_LONG - shift);
SHIFT_LOW(color,32 - shift);
}
shift += bpp;
shift &= (BITS_PER_LONG - 1);
shift &= (32 - 1);
if (!l) { l = 8; s++; };
}
/* write trailing bits */
if (shift) {
unsigned long end_mask = SHIFT_HIGH(~0UL, shift);
u32 end_mask = SHIFT_HIGH(~(u32)0, shift);
FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
}
......@@ -211,10 +204,10 @@ static inline void slow_imageblit(struct fb_image *image, struct fb_info *p,
if (pitch_index) {
dst2 += pitch;
dst1 = (char *) dst2;
(unsigned long) dst1 &= ~(sizeof(unsigned long) - 1);
(unsigned long) dst1 &= ~(sizeof(u32) - 1);
start_index += pitch_index;
start_index &= BITS_PER_LONG - 1;
start_index &= 32 - 1;
}
}
......@@ -229,14 +222,14 @@ static inline void slow_imageblit(struct fb_image *image, struct fb_info *p,
* beginning and end of a scanline is dword aligned
*/
static inline void fast_imageblit(struct fb_image *image, struct fb_info *p,
u8 *dst1, unsigned long fgcolor,
unsigned long bgcolor)
u8 *dst1, u32 fgcolor,
u32 bgcolor)
{
unsigned long fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
unsigned long ppw = BITS_PER_LONG/bpp, spitch = (image->width + 7)/8;
unsigned long bit_mask, end_mask, eorx, shift;
u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
u32 ppw = 32/bpp, spitch = (image->width + 7)/8;
u32 bit_mask, end_mask, eorx, shift;
char *s = image->data, *src;
unsigned long *dst;
u32 *dst;
u32 *tab = NULL;
int i, j, k;
......@@ -264,7 +257,7 @@ static inline void fast_imageblit(struct fb_image *image, struct fb_info *p,
k = image->width/ppw;
for (i = image->height; i--; ) {
dst = (unsigned long *) dst1, shift = 8; src = s;
dst = (u32 *) dst1, shift = 8; src = s;
for (j = k; j--; ) {
shift -= ppw;
......@@ -279,8 +272,8 @@ static inline void fast_imageblit(struct fb_image *image, struct fb_info *p,
void cfb_imageblit(struct fb_info *p, struct fb_image *image)
{
unsigned long fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
unsigned long bpl = sizeof(unsigned long), bpp = p->var.bits_per_pixel;
u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
int x2, y2, vxres, vyres;
u8 *dst1;
......@@ -304,7 +297,7 @@ void cfb_imageblit(struct fb_info *p, struct fb_image *image)
image->height = y2 - image->dy;
bitstart = (image->dy * p->fix.line_length * 8) + (image->dx * bpp);
start_index = bitstart & (BITS_PER_LONG - 1);
start_index = bitstart & (32 - 1);
pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
bitstart /= 8;
......@@ -324,8 +317,8 @@ void cfb_imageblit(struct fb_info *p, struct fb_image *image)
bgcolor = image->bg_color;
}
if (BITS_PER_LONG % bpp == 0 && !start_index && !pitch_index &&
((image->width & (BITS_PER_LONG/bpp-1)) == 0) &&
if (32 % bpp == 0 && !start_index && !pitch_index &&
((image->width & (32/bpp-1)) == 0) &&
bpp >= 8 && bpp <= 32)
fast_imageblit(image, p, dst1, fgcolor, bgcolor);
else
......
/* $Id: cgfourteenfb.c,v 1.11 2001/09/19 00:04:33 davem Exp $
* cgfourteenfb.c: CGfourteen frame buffer driver
/* cg14.c: CGFOURTEEN frame buffer driver
*
* Copyright (C) 2003 David S. Miller (davem@redhat.com)
* Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
* Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
*
* 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 <linux/fb.h>
#include <linux/mm.h>
#include <video/sbusfb.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
#include <asm/sbus.h>
#include <asm/oplib.h>
#include <asm/fbio.h>
#include "sbuslib.h"
/*
* Local functions.
*/
static int cg14_check_var(struct fb_var_screeninfo *, struct fb_info *);
static int cg14_set_par(struct fb_info *);
static int cg14_setcolreg(unsigned, unsigned, unsigned, unsigned,
unsigned, struct fb_info *);
#include <video/fbcon-cfb8.h>
static int cg14_mmap(struct fb_info *, struct file *, struct vm_area_struct *);
static int cg14_ioctl(struct inode *, struct file *, unsigned int,
unsigned long, struct fb_info *);
/*
* Frame buffer operations
*/
static struct fb_ops cg14_ops = {
.owner = THIS_MODULE,
.fb_check_var = cg14_check_var,
.fb_set_par = cg14_set_par,
.fb_setcolreg = cg14_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_mmap = cg14_mmap,
.fb_ioctl = cg14_ioctl,
.fb_cursor = soft_cursor,
};
#define CG14_MCR_INTENABLE_SHIFT 7
#define CG14_MCR_INTENABLE_MASK 0x80
......@@ -72,8 +99,6 @@
#define CG14_MCR_PIXMODE_16 2
#define CG14_MCR_PIXMODE_32 3
MODULE_LICENSE("GPL");
struct cg14_regs{
volatile u8 mcr; /* Master Control Reg */
volatile u8 ppr; /* Packed Pixel Reg */
......@@ -161,285 +186,412 @@ struct cg14_clut {
u32 c_clutd_inc [256];
};
static struct sbus_mmap_map cg14_mmap_map[] __initdata = {
{ CG14_REGS, 0x80000000, 0x1000 },
{ CG14_XLUT, 0x80003000, 0x1000 },
{ CG14_CLUT1, 0x80004000, 0x1000 },
{ CG14_CLUT2, 0x80005000, 0x1000 },
{ CG14_CLUT3, 0x80006000, 0x1000 },
{ CG3_MMAP_OFFSET -
0x7000, 0x80000000, 0x7000 },
{ CG3_MMAP_OFFSET, 0x00000000, SBUS_MMAP_FBSIZE(1) },
{ MDI_CURSOR_MAP, 0x80001000, 0x1000 },
{ MDI_CHUNKY_BGR_MAP, 0x01000000, 0x400000 },
{ MDI_PLANAR_X16_MAP, 0x02000000, 0x200000 },
{ MDI_PLANAR_C16_MAP, 0x02800000, 0x200000 },
{ MDI_PLANAR_X32_MAP, 0x03000000, 0x100000 },
{ MDI_PLANAR_B32_MAP, 0x03400000, 0x100000 },
{ MDI_PLANAR_G32_MAP, 0x03800000, 0x100000 },
{ MDI_PLANAR_R32_MAP, 0x03c00000, 0x100000 },
{ 0, 0, 0 }
};
#define CG14_MMAP_ENTRIES 16
static void cg14_loadcmap (struct fb_info_sbusfb *fb, struct display *p,
int index, int count)
{
struct cg14_clut *clut = fb->s.cg14.clut;
unsigned long flags;
struct cg14_par {
spinlock_t lock;
struct cg14_regs *regs;
struct cg14_clut *clut;
struct cg14_cursor *cursor;
spin_lock_irqsave(&fb->lock, flags);
for (; count--; index++) {
u32 val;
u32 flags;
#define CG14_FLAG_BLANKED 0x00000001
val = ((fb->color_map CM(index,2) << 16) |
(fb->color_map CM(index,1) << 8) |
(fb->color_map CM(index,0)));
sbus_writel(val, &clut->c_clut[index]);
}
spin_unlock_irqrestore(&fb->lock, flags);
}
unsigned long physbase;
unsigned long iospace;
unsigned long fbsize;
static void cg14_margins (struct fb_info_sbusfb *fb, struct display *p,
int x_margin, int y_margin)
{
fb->info.screen_base += (y_margin - fb->y_margin) *
fb->info.fix.line_length + (x_margin - fb->x_margin);
}
static void cg14_setcursormap (struct fb_info_sbusfb *fb, u8 *red, u8 *green, u8 *blue)
{
struct cg14_cursor *cur = fb->s.cg14.cursor;
unsigned long flags;
struct sbus_mmap_map mmap_map[CG14_MMAP_ENTRIES];
spin_lock_irqsave(&fb->lock, flags);
sbus_writel(((red[0]) | (green[0] << 8) | (blue[0] << 16)), &cur->color0);
sbus_writel(((red[1]) | (green[1] << 8) | (blue[1] << 16)), &cur->color1);
spin_unlock_irqrestore(&fb->lock, flags);
}
int mode;
int ramsize;
struct sbus_dev *sdev;
struct list_head list;
};
/* Set cursor shape */
static void cg14_setcurshape (struct fb_info_sbusfb *fb)
static void __cg14_reset(struct cg14_par *par)
{
struct cg14_cursor *cur = fb->s.cg14.cursor;
unsigned long flags;
int i;
struct cg14_regs *regs = par->regs;
u8 val;
spin_lock_irqsave(&fb->lock, flags);
for (i = 0; i < 32; i++){
sbus_writel(fb->cursor.bits[0][i], &cur->cpl0[i]);
sbus_writel(fb->cursor.bits[1][i], &cur->cpl1[i]);
}
spin_unlock_irqrestore(&fb->lock, flags);
val = sbus_readb(&regs->mcr);
val &= ~(CG14_MCR_PIXMODE_MASK);
sbus_writeb(val, &regs->mcr);
}
/* Load cursor information */
static void cg14_setcursor (struct fb_info_sbusfb *fb)
/**
* cg14_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 cg14_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
struct cg_cursor *c = &fb->cursor;
struct cg14_cursor *cur = fb->s.cg14.cursor;
unsigned long flags;
if (var->bits_per_pixel != 8)
return -EINVAL;
spin_lock_irqsave(&fb->lock, flags);
if (c->enable) {
u8 tmp = sbus_readb(&cur->ccr);
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;
tmp |= CG14_CCR_ENABLE;
sbus_writeb(tmp, &cur->ccr);
} else {
u8 tmp = sbus_readb(&cur->ccr);
if (var->xres != info->var.xres || var->yres != info->var.yres)
return -EINVAL;
tmp &= ~CG14_CCR_ENABLE;
sbus_writeb(tmp, &cur->ccr);
}
sbus_writew(((c->cpos.fbx - c->chot.fbx) & 0xfff), &cur->cursx);
sbus_writew(((c->cpos.fby - c->chot.fby) & 0xfff), &cur->cursy);
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
}
static void cg14_switch_from_graph (struct fb_info_sbusfb *fb)
/**
* cg14_set_par - Optional function. Alters the hardware state.
* @info: frame buffer structure that represents a single frame buffer
*/
static int
cg14_set_par(struct fb_info *info)
{
return 0;
}
/**
* cg14_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 cg14_setcolreg(unsigned regno,
unsigned red, unsigned green, unsigned blue,
unsigned transp, struct fb_info *info)
{
struct cg14_par *par = (struct cg14_par *) info->par;
struct cg14_clut *clut = par->clut;
unsigned long flags;
u32 val;
spin_lock_irqsave(&fb->lock, flags);
if (regno >= 256)
return 1;
/* Set the 8-bpp mode */
if (fb->open && fb->mmaped){
volatile char *mcr = &fb->s.cg14.regs->mcr;
char tmp;
val = (red | (green << 8) | (blue << 16));
fb->s.cg14.mode = 8;
tmp = sbus_readb(mcr);
tmp &= ~(CG14_MCR_PIXMODE_MASK);
sbus_writeb(tmp, mcr);
}
spin_unlock_irqrestore(&fb->lock, flags);
spin_lock_irqsave(&par->lock, flags);
sbus_writel(val, &clut->c_clut[regno]);
spin_unlock_irqrestore(&par->lock, flags);
return 0;
}
static void cg14_reset (struct fb_info_sbusfb *fb)
static int cg14_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
{
volatile char *mcr = &fb->s.cg14.regs->mcr;
unsigned long flags;
char tmp;
struct cg14_par *par = (struct cg14_par *) info->par;
spin_lock_irqsave(&fb->lock, flags);
tmp = sbus_readb(mcr);
tmp &= ~(CG14_MCR_PIXMODE_MASK);
sbus_writeb(tmp, mcr);
spin_unlock_irqrestore(&fb->lock, flags);
return sbusfb_mmap_helper(par->mmap_map,
par->physbase, par->fbsize,
par->iospace, vma);
}
static int cg14_ioctl (struct fb_info_sbusfb *fb, unsigned int cmd, unsigned long arg)
static int cg14_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg, struct fb_info *info)
{
volatile char *mcr = &fb->s.cg14.regs->mcr;
struct mdi_cfginfo *mdii;
struct cg14_par *par = (struct cg14_par *) info->par;
struct cg14_regs *regs = par->regs;
struct mdi_cfginfo kmdi, *mdii;
unsigned long flags;
int mode, ret = 0;
char tmp;
int cur_mode, mode, ret = 0;
switch (cmd) {
case MDI_RESET:
spin_lock_irqsave(&fb->lock, flags);
tmp = sbus_readb(mcr);
tmp &= ~CG14_MCR_PIXMODE_MASK;
sbus_writeb(tmp, mcr);
spin_unlock_irqrestore(&fb->lock, flags);
spin_lock_irqsave(&par->lock, flags);
__cg14_reset(par);
spin_unlock_irqrestore(&par->lock, flags);
break;
case MDI_GET_CFGINFO:
mdii = (struct mdi_cfginfo *)arg;
if (put_user(FBTYPE_MDICOLOR, &mdii->mdi_type) ||
__put_user(fb->type.fb_height, &mdii->mdi_height) ||
__put_user(fb->type.fb_width, &mdii->mdi_width) ||
__put_user(fb->s.cg14.mode, &mdii->mdi_mode) ||
__put_user(72, &mdii->mdi_pixfreq) || /* FIXME */
__put_user(fb->s.cg14.ramsize, &mdii->mdi_size))
return -EFAULT;
memset(&kmdi, 0, sizeof(kmdi));
spin_lock_irqsave(&par->lock, flags);
kmdi.mdi_type = FBTYPE_MDICOLOR;
kmdi.mdi_height = info->var.yres;
kmdi.mdi_width = info->var.xres;
kmdi.mdi_mode = par->mode;
kmdi.mdi_pixfreq = 72; /* FIXME */
kmdi.mdi_size = par->ramsize;
spin_unlock_irqrestore(&par->lock, flags);
mdii = (struct mdi_cfginfo *) arg;
if (copy_to_user(mdii, &kmdi, sizeof(kmdi)))
ret = -EFAULT;
break;
case MDI_SET_PIXELMODE:
if (get_user(mode, (int *)arg))
return -EFAULT;
if (get_user(mode, (int *) arg)) {
ret = -EFAULT;
break;
}
spin_lock_irqsave(&fb->lock, flags);
tmp = sbus_readb(mcr);
switch (mode){
spin_lock_irqsave(&par->lock, flags);
cur_mode = sbus_readb(&regs->mcr);
cur_mode &= ~CG14_MCR_PIXMODE_MASK;
switch(mode) {
case MDI_32_PIX:
tmp = (tmp & ~CG14_MCR_PIXMODE_MASK) |
(CG14_MCR_PIXMODE_32 << CG14_MCR_PIXMODE_SHIFT);
cur_mode |= (CG14_MCR_PIXMODE_32 <<
CG14_MCR_PIXMODE_SHIFT);
break;
case MDI_16_PIX:
tmp = (tmp & ~CG14_MCR_PIXMODE_MASK) | 0x20;
cur_mode |= 0x20;
break;
case MDI_8_PIX:
tmp = (tmp & ~CG14_MCR_PIXMODE_MASK);
break;
default:
ret = -ENOSYS;
break;
};
if (ret == 0) {
sbus_writeb(tmp, mcr);
fb->s.cg14.mode = mode;
if (!ret) {
sbus_writeb(cur_mode, &regs->mcr);
par->mode = mode;
}
spin_unlock_irqrestore(&fb->lock, flags);
spin_unlock_irqrestore(&par->lock, flags);
break;
default:
ret = -EINVAL;
break;
};
return ret;
}
static unsigned long __init get_phys(unsigned long addr)
{
return __get_phys(addr);
}
/*
* Initialisation
*/
static int __init get_iospace(unsigned long addr)
static void cg14_init_fix(struct fb_info *info, int linebytes)
{
return __get_iospace(addr);
struct cg14_par *par = (struct cg14_par *)info->par;
strncpy(info->fix.id, par->sdev->prom_name, sizeof(info->fix.id) - 1);
info->fix.id[sizeof(info->fix.id)-1] = 0;
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_TRUECOLOR;
info->fix.line_length = linebytes;
info->fix.accel = FB_ACCEL_SUN_CG14;
}
static char idstring[60] __initdata = { 0 };
static struct sbus_mmap_map __cg14_mmap_map[CG14_MMAP_ENTRIES] __initdata = {
{ CG14_REGS, 0x80000000, 0x1000 },
{ CG14_XLUT, 0x80003000, 0x1000 },
{ CG14_CLUT1, 0x80004000, 0x1000 },
{ CG14_CLUT2, 0x80005000, 0x1000 },
{ CG14_CLUT3, 0x80006000, 0x1000 },
{ CG3_MMAP_OFFSET -
0x7000, 0x80000000, 0x7000 },
{ CG3_MMAP_OFFSET, 0x00000000, SBUS_MMAP_FBSIZE(1) },
{ MDI_CURSOR_MAP, 0x80001000, 0x1000 },
{ MDI_CHUNKY_BGR_MAP, 0x01000000, 0x400000 },
{ MDI_PLANAR_X16_MAP, 0x02000000, 0x200000 },
{ MDI_PLANAR_C16_MAP, 0x02800000, 0x200000 },
{ MDI_PLANAR_X32_MAP, 0x03000000, 0x100000 },
{ MDI_PLANAR_B32_MAP, 0x03400000, 0x100000 },
{ MDI_PLANAR_G32_MAP, 0x03800000, 0x100000 },
{ MDI_PLANAR_R32_MAP, 0x03c00000, 0x100000 },
{ 0, 0, 0 }
};
struct all_info {
struct fb_info info;
struct cg14_par par;
struct list_head list;
};
static LIST_HEAD(cg14_list);
char __init *cgfourteenfb_init(struct fb_info_sbusfb *fb)
static void cg14_init_one(struct sbus_dev *sdev, int node, int parent_node)
{
struct fb_fix_screeninfo *fix = &fb->info.fix;
struct display *disp = &fb->disp;
struct fbtype *type = &fb->type;
unsigned long rphys, phys;
struct all_info *all;
unsigned long phys, rphys;
u32 bases[6];
int is_8mb, i;
int is_8mb, linebytes, i;
#ifndef FBCON_HAS_CFB8
return NULL;
#endif
prom_getproperty (fb->prom_node, "address", (char *) &bases[0], 8);
if (!sdev) {
prom_getproperty(node, "address",
(char *) &bases[0], sizeof(bases));
if (!bases[0]) {
printk("cg14 not mmaped\n");
return NULL;
printk(KERN_ERR "cg14: Device is not mapped.\n");
return;
}
if (get_iospace(bases[0]) != get_iospace(bases[1])) {
printk("Ugh. cg14 iospaces don't match\n");
return NULL;
if (__get_iospace(bases[0]) != __get_iospace(bases[1])) {
printk(KERN_ERR "cg14: I/O spaces don't match.\n");
return;
}
fb->physbase = phys = get_phys(bases[1]);
rphys = get_phys(bases[0]);
fb->iospace = get_iospace(bases[0]);
fb->s.cg14.regs = (struct cg14_regs *)(unsigned long)bases[0];
fb->s.cg14.clut = (void *)((unsigned long)bases[0]+CG14_CLUT1);
fb->s.cg14.cursor = (void *)((unsigned long)bases[0]+CG14_CURSORREGS);
fb->info.screen_base = (char *)bases[1];
/* CG14_VCA_8MB_MASK is not correctly set on the 501-2482
* VSIMM, so we read the memory size from the PROM
*/
prom_getproperty(fb->prom_node, "reg", (char *) &bases[0], 24);
is_8mb = bases[5] == 0x800000;
fb->mmap_map = kmalloc(sizeof(cg14_mmap_map), GFP_KERNEL);
if (!fb->mmap_map)
return NULL;
for (i = 0; ; i++) {
fb->mmap_map[i].voff = cg14_mmap_map[i].voff;
fb->mmap_map[i].poff = (cg14_mmap_map[i].poff & 0x80000000) ?
(cg14_mmap_map[i].poff & 0x7fffffff) + rphys - phys :
cg14_mmap_map[i].poff;
fb->mmap_map[i].size = cg14_mmap_map[i].size;
if (is_8mb && fb->mmap_map[i].size >= 0x100000 &&
fb->mmap_map[i].size <= 0x400000)
fb->mmap_map[i].size <<= 1;
if (!cg14_mmap_map[i].size)
}
all = kmalloc(sizeof(*all), GFP_KERNEL);
if (!all) {
printk(KERN_ERR "cg14: Cannot allocate memory.\n");
return;
}
memset(all, 0, sizeof(*all));
INIT_LIST_HEAD(&all->list);
spin_lock_init(&all->par.lock);
sbusfb_fill_var(&all->info.var, node, 8);
linebytes = prom_getintdefault(sdev->prom_node, "linebytes",
all->info.var.xres);
all->par.fbsize = PAGE_ALIGN(linebytes * all->info.var.yres);
all->par.sdev = sdev;
if (sdev) {
rphys = sdev->reg_addrs[0].phys_addr;
all->par.physbase = phys = sdev->reg_addrs[1].phys_addr;
all->par.iospace = sdev->reg_addrs[0].which_io;
all->par.regs = (struct cg14_regs *)
sbus_ioremap(&sdev->resource[0], 0,
sizeof(struct cg14_regs),
"cg14 regs");
all->par.clut = (struct cg14_clut *)
sbus_ioremap(&sdev->resource[0], CG14_CLUT1,
sizeof(struct cg14_clut),
"cg14 clut");
all->par.cursor = (struct cg14_cursor *)
sbus_ioremap(&sdev->resource[0], CG14_CURSORREGS,
sizeof(struct cg14_cursor),
"cg14 cursor");
all->info.screen_base = (char *)
sbus_ioremap(&sdev->resource[1], 0,
all->par.fbsize, "cg14 ram");
} else {
rphys = __get_phys(bases[0]);
all->par.physbase = phys = __get_phys(bases[1]);
all->par.iospace = __get_iospace(bases[0]);
all->par.regs = (struct cg14_regs *)(unsigned long)bases[0];
all->par.clut = (struct cg14_clut *)((unsigned long)bases[0] +
CG14_CLUT1);
all->par.cursor =
(struct cg14_cursor *)((unsigned long)bases[0] +
CG14_CURSORREGS);
all->info.screen_base = (char *)(unsigned long)bases[1];
}
prom_getproperty(node, "reg", (char *) &bases[0], sizeof(bases));
is_8mb = (bases[5] == 0x800000);
if (sizeof(all->par.mmap_map) != sizeof(__cg14_mmap_map)) {
extern void __cg14_mmap_sized_wrongly(void);
__cg14_mmap_sized_wrongly();
}
memcpy(&all->par.mmap_map, &__cg14_mmap_map, sizeof(all->par.mmap_map));
for (i = 0; i < CG14_MMAP_ENTRIES; i++) {
struct sbus_mmap_map *map = &all->par.mmap_map[i];
if (!map->size)
break;
if (map->poff & 0x80000000)
map->poff = (map->poff & 0x7fffffff) + rphys - phys;
if (is_8mb &&
map->size >= 0x100000 &&
map->size <= 0x400000)
map->size *= 2;
}
strcpy(fb->info.modename, "CGfourteen");
strcpy(fix->id, "CGfourteen");
fix->line_length = fb->info.var.xres_virtual;
fix->accel = FB_ACCEL_SUN_CG14;
all->par.mode = MDI_8_PIX;
all->par.ramsize = (is_8mb ? 0x800000 : 0x400000);
disp->scrollmode = SCROLL_YREDRAW;
fb->info.screen_base += fix->line_length * fb->y_margin + fb->x_margin;
fb->dispsw = fbcon_cfb8;
all->info.node = NODEV;
all->info.flags = FBINFO_FLAG_DEFAULT;
all->info.fbops = &cg14_ops;
all->info.currcon = -1;
all->info.par = &all->par;
type->fb_depth = 24;
fb->emulations[1] = FBTYPE_SUN3COLOR;
__cg14_reset(&all->par);
fb->margins = cg14_margins;
fb->loadcmap = cg14_loadcmap;
fb->setcursor = cg14_setcursor;
fb->setcursormap = cg14_setcursormap;
fb->setcurshape = cg14_setcurshape;
fb->reset = cg14_reset;
fb->switch_from_graph = cg14_switch_from_graph;
fb->ioctl = cg14_ioctl;
if (fb_alloc_cmap(&all->info.cmap, 256, 0)) {
printk(KERN_ERR "cg14: Could not allocate color map.\n");
kfree(all);
return;
}
cg14_set_par(&all->info);
cg14_init_fix(&all->info, linebytes);
fb->s.cg14.mode = 8;
fb->s.cg14.ramsize = (is_8mb) ? 0x800000 : 0x400000;
if (register_framebuffer(&all->info) < 0) {
printk(KERN_ERR "cg14: Could not register framebuffer.\n");
fb_dealloc_cmap(&all->info.cmap);
kfree(all);
return;
}
cg14_reset(fb);
list_add(&all->list, &cg14_list);
sprintf(idstring, "cgfourteen at %x.%08lx, %dMB, rev=%d, impl=%d", fb->iospace, phys,
is_8mb ? 8 : 4, fb->s.cg14.regs->rev >> 4, fb->s.cg14.regs->rev & 0xf);
printk("cg14: cgfourteen at %lx:%lx\n",
all->par.physbase, all->par.iospace);
return idstring;
}
int __init cg14_init(void)
{
struct sbus_bus *sbus;
struct sbus_dev *sdev;
#ifdef CONFIG_SPARC32
{
int root, node;
root = prom_getchild(prom_root_node);
root = prom_searchsiblings(root, "obio");
if (root) {
node = prom_searchsiblings(prom_getchild(root),
"cgfourteen");
if (node)
cg14_init_one(NULL, node, root);
}
}
#endif
for_all_sbusdev(sdev, sbus) {
if (!strcmp(sdev->prom_name, "cgfourteen"))
cg14_init_one(sdev, sdev->prom_node, sbus->prom_node);
}
return 0;
}
void __exit cg14_exit(void)
{
struct list_head *pos, *tmp;
list_for_each_safe(pos, tmp, &cg14_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
cg14_setup(char *arg)
{
/* No cmdline options yet... */
return 0;
}
#ifdef MODULE
module_init(cg14_init);
module_exit(cg14_exit);
#endif
MODULE_DESCRIPTION("framebuffer driver for CGfourteen chipsets");
MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
MODULE_LICENSE("GPL");
/* cg3.c: CGTHREE frame buffer driver
*
* Copyright (C) 2003 David S. Miller (davem@redhat.com)
* Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*
* Driver layout based loosely on tgafb.c, see that file for credits.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fb.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <asm/sbus.h>
#include <asm/oplib.h>
#include <asm/fbio.h>
#include "sbuslib.h"
/*
* Local functions.
*/
static int cg3_check_var(struct fb_var_screeninfo *, struct fb_info *);
static int cg3_set_par(struct fb_info *);
static int cg3_setcolreg(unsigned, unsigned, unsigned, unsigned,
unsigned, struct fb_info *);
static int cg3_blank(int, struct fb_info *);
static int cg3_mmap(struct fb_info *, struct file *, struct vm_area_struct *);
/*
* Frame buffer operations
*/
static struct fb_ops cg3_ops = {
.owner = THIS_MODULE,
.fb_check_var = cg3_check_var,
.fb_set_par = cg3_set_par,
.fb_setcolreg = cg3_setcolreg,
.fb_blank = cg3_blank,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_mmap = cg3_mmap,
.fb_cursor = soft_cursor,
};
/* Control Register Constants */
#define CG3_CR_ENABLE_INTS 0x80
#define CG3_CR_ENABLE_VIDEO 0x40
#define CG3_CR_ENABLE_TIMING 0x20
#define CG3_CR_ENABLE_CURCMP 0x10
#define CG3_CR_XTAL_MASK 0x0c
#define CG3_CR_DIVISOR_MASK 0x03
/* Status Register Constants */
#define CG3_SR_PENDING_INT 0x80
#define CG3_SR_RES_MASK 0x70
#define CG3_SR_1152_900_76_A 0x40
#define CG3_SR_1152_900_76_B 0x60
#define CG3_SR_ID_MASK 0x0f
#define CG3_SR_ID_COLOR 0x01
#define CG3_SR_ID_MONO 0x02
#define CG3_SR_ID_MONO_ECL 0x03
enum cg3_type {
CG3_AT_66HZ = 0,
CG3_AT_76HZ,
CG3_RDI
};
struct bt_regs {
volatile u32 addr;
volatile u32 color_map;
volatile u32 control;
volatile u32 cursor;
};
struct cg3_regs {
struct bt_regs cmap;
volatile u8 control;
volatile u8 status;
volatile u8 cursor_start;
volatile u8 cursor_end;
volatile u8 h_blank_start;
volatile u8 h_blank_end;
volatile u8 h_sync_start;
volatile u8 h_sync_end;
volatile u8 comp_sync_end;
volatile u8 v_blank_start_high;
volatile u8 v_blank_start_low;
volatile u8 v_blank_end;
volatile u8 v_sync_start;
volatile u8 v_sync_end;
volatile u8 xfer_holdoff_start;
volatile u8 xfer_holdoff_end;
};
/* Offset of interesting structures in the OBIO space */
#define CG3_REGS_OFFSET 0x400000UL
#define CG3_RAM_OFFSET 0x800000UL
struct cg3_par {
spinlock_t lock;
struct cg3_regs *regs;
u32 sw_cmap[((256 * 3) + 3) / 4];
u32 flags;
#define CG3_FLAG_BLANKED 0x00000001
#define CG3_FLAG_RDI 0x00000002
unsigned long physbase;
unsigned long fbsize;
struct sbus_dev *sdev;
struct list_head list;
};
/**
* cg3_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 cg3_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
if (var->bits_per_pixel != 8)
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;
return 0;
}
/**
* cg3_set_par - Optional function. Alters the hardware state.
* @info: frame buffer structure that represents a single frame buffer
*/
static int
cg3_set_par(struct fb_info *info)
{
return 0;
}
/**
* cg3_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
*
* The cg3 palette is loaded with 4 color values at each time
* so you end up with: (rgb)(r), (gb)(rg), (b)(rgb), and so on.
* We keep a sw copy of the hw cmap to assist us in this esoteric
* loading procedure.
*/
static int cg3_setcolreg(unsigned regno,
unsigned red, unsigned green, unsigned blue,
unsigned transp, struct fb_info *info)
{
struct cg3_par *par = (struct cg3_par *) info->par;
struct bt_regs *bt = &par->regs->cmap;
unsigned long flags;
u32 *p32;
u8 *p8;
int count;
if (regno >= 256)
return 1;
red >>= 8;
green >>= 8;
blue >>= 8;
spin_lock_irqsave(&par->lock, flags);
p8 = (u8 *)par->sw_cmap + (regno * 3);
p8[0] = red;
p8[1] = green;
p8[2] = blue;
#define D4M3(x) ((((x)>>2)<<1) + ((x)>>2)) /* (x/4)*3 */
#define D4M4(x) ((x)&~0x3) /* (x/4)*4 */
count = 3;
p32 = &par->sw_cmap[D4M3(regno)];
sbus_writel(D4M4(regno), &bt->addr);
while (count--)
sbus_writel(*p32++, &bt->color_map);
#undef D4M3
#undef D4M4
spin_unlock_irqrestore(&par->lock, flags);
return 0;
}
/**
* cg3_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
cg3_blank(int blank, struct fb_info *info)
{
struct cg3_par *par = (struct cg3_par *) info->par;
struct cg3_regs *regs = par->regs;
unsigned long flags;
u8 val;
spin_lock_irqsave(&par->lock, flags);
switch (blank) {
case 0: /* Unblanking */
val = sbus_readl(&regs->control);
val |= CG3_CR_ENABLE_VIDEO;
sbus_writel(val, &regs->control);
par->flags &= ~CG3_FLAG_BLANKED;
break;
case 1: /* Normal blanking */
case 2: /* VESA blank (vsync off) */
case 3: /* VESA blank (hsync off) */
case 4: /* Poweroff */
val = sbus_readl(&regs->control);
val |= CG3_CR_ENABLE_VIDEO;
sbus_writel(val, &regs->control);
par->flags |= CG3_FLAG_BLANKED;
break;
}
spin_unlock_irqrestore(&par->lock, flags);
return 0;
}
static struct sbus_mmap_map cg3_mmap_map[] = {
{ CG3_MMAP_OFFSET, CG3_RAM_OFFSET, SBUS_MMAP_FBSIZE(1) },
{ 0, 0, 0 }
};
static int cg3_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
{
struct cg3_par *par = (struct cg3_par *)info->par;
return sbusfb_mmap_helper(cg3_mmap_map,
par->physbase, par->fbsize,
par->sdev->reg_addrs[0].which_io,
vma);
}
/*
* Initialisation
*/
static void
cg3_init_fix(struct fb_info *info, int linebytes)
{
struct cg3_par *par = (struct cg3_par *)info->par;
strncpy(info->fix.id, par->sdev->prom_name, sizeof(info->fix.id) - 1);
info->fix.id[sizeof(info->fix.id)-1] = 0;
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
info->fix.line_length = linebytes;
info->fix.accel = FB_ACCEL_SUN_CGTHREE;
}
static void cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo *var,
struct sbus_dev *sdev)
{
char buffer[40];
char *p;
int ww, hh;
*buffer = 0;
prom_getstring(sdev->prom_node, "params", buffer, sizeof(buffer));
if (*buffer) {
ww = simple_strtoul(buffer, &p, 10);
if (ww && *p == 'x') {
hh = simple_strtoul(p + 1, &p, 10);
if (hh && *p == '-') {
if (var->xres != ww ||
var->yres != hh) {
var->xres = var->xres_virtual = ww;
var->yres = var->yres_virtual = hh;
}
}
}
}
}
static u8 cg3regvals_66hz[] __initdata = { /* 1152 x 900, 66 Hz */
0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14,
0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24,
0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x20, 0
};
static u8 cg3regvals_76hz[] __initdata = { /* 1152 x 900, 76 Hz */
0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f,
0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a,
0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x24, 0
};
static u8 cg3regvals_rdi[] __initdata = { /* 640 x 480, cgRDI */
0x14, 0x70, 0x15, 0x20, 0x16, 0x08, 0x17, 0x10,
0x18, 0x06, 0x19, 0x02, 0x1a, 0x31, 0x1b, 0x51,
0x1c, 0x06, 0x1d, 0x0c, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x22, 0
};
static u8 *cg3_regvals[] __initdata = {
cg3regvals_66hz, cg3regvals_76hz, cg3regvals_rdi
};
static u_char cg3_dacvals[] __initdata = {
4, 0xff, 5, 0x00, 6, 0x70, 7, 0x00, 0
};
static void cg3_do_default_mode(struct cg3_par *par)
{
enum cg3_type type;
u8 *p;
if (par->flags & CG3_FLAG_RDI)
type = CG3_RDI;
else {
u8 status = sbus_readb(&par->regs->status), mon;
if ((status & CG3_SR_ID_MASK) == CG3_SR_ID_COLOR) {
mon = status & CG3_SR_RES_MASK;
if (mon == CG3_SR_1152_900_76_A ||
mon == CG3_SR_1152_900_76_B)
type = CG3_AT_76HZ;
else
type = CG3_AT_66HZ;
} else {
prom_printf("cgthree: can't handle SR %02x\n",
status);
prom_halt();
return;
}
}
for (p = cg3_regvals[type]; *p; p += 2) {
u8 *regp = &((u8 *)par->regs)[p[0]];
sbus_writeb(p[1], regp);
}
for (p = cg3_dacvals; *p; p += 2) {
volatile u8 *regp;
regp = (volatile u8 *)&par->regs->cmap.addr;
sbus_writeb(p[0], regp);
regp = (volatile u8 *)&par->regs->cmap.control;
sbus_writeb(p[1], regp);
}
}
struct all_info {
struct fb_info info;
struct cg3_par par;
struct list_head list;
};
static LIST_HEAD(cg3_list);
static void cg3_init_one(struct sbus_dev *sdev)
{
struct all_info *all;
int linebytes;
all = kmalloc(sizeof(*all), GFP_KERNEL);
if (!all) {
printk(KERN_ERR "cg3: Cannot allocate memory.\n");
return;
}
memset(all, 0, sizeof(*all));
INIT_LIST_HEAD(&all->list);
spin_lock_init(&all->par.lock);
all->par.sdev = sdev;
all->par.physbase = sdev->reg_addrs[0].phys_addr;
sbusfb_fill_var(&all->info.var, sdev->prom_node, 8);
if (!strcmp(sdev->prom_name, "cgRDI"))
all->par.flags |= CG3_FLAG_RDI;
if (all->par.flags & CG3_FLAG_RDI)
cg3_rdi_maybe_fixup_var(&all->info.var, sdev);
linebytes = prom_getintdefault(sdev->prom_node, "linebytes",
all->info.var.xres);
all->par.fbsize = PAGE_ALIGN(linebytes * all->info.var.yres);
all->par.regs = (struct cg3_regs *)
sbus_ioremap(&sdev->resource[0], CG3_REGS_OFFSET,
sizeof(struct cg3_regs), "cg3 regs");
all->info.node = NODEV;
all->info.flags = FBINFO_FLAG_DEFAULT;
all->info.fbops = &cg3_ops;
#ifdef CONFIG_SPARC32
all->info.screen_base = (char *)
prom_getintdefault(sdev->prom_node, "address", 0);
#endif
if (!all->info.screen_base)
all->info.screen_base = (char *)
sbus_ioremap(&sdev->resource[0], CG3_RAM_OFFSET,
all->par.fbsize, "cg3 ram");
all->info.currcon = -1;
all->info.par = &all->par;
cg3_blank(0, &all->info);
if (!prom_getbool(sdev->prom_node, "width"))
cg3_do_default_mode(&all->par);
if (fb_alloc_cmap(&all->info.cmap, 256, 0)) {
printk(KERN_ERR "cg3: Could not allocate color map.\n");
kfree(all);
return;
}
cg3_set_par(&all->info);
cg3_init_fix(&all->info, linebytes);
if (register_framebuffer(&all->info) < 0) {
printk(KERN_ERR "cg3: Could not register framebuffer.\n");
fb_dealloc_cmap(&all->info.cmap);
kfree(all);
return;
}
list_add(&all->list, &cg3_list);
printk("cg3: %s at %lx:%lx\n",
sdev->prom_name,
(long) sdev->reg_addrs[0].which_io,
(long) sdev->reg_addrs[0].phys_addr);
}
int __init cg3_init(void)
{
struct sbus_bus *sbus;
struct sbus_dev *sdev;
for_all_sbusdev(sdev, sbus) {
if (!strcmp(sdev->prom_name, "cgthree") ||
!strcmp(sdev->prom_name, "cgRDI"))
cg3_init_one(sdev);
}
return 0;
}
void __exit cg3_exit(void)
{
struct list_head *pos, *tmp;
list_for_each_safe(pos, tmp, &cg3_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
cg3_setup(char *arg)
{
/* No cmdline options yet... */
return 0;
}
#ifdef MODULE
module_init(cg3_init);
module_exit(cg3_exit);
#endif
MODULE_DESCRIPTION("framebuffer driver for CGthree chipsets");
MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
MODULE_LICENSE("GPL");
/* $Id: cgsixfb.c,v 1.26 2001/10/16 05:44:44 davem Exp $
* cgsixfb.c: CGsix (GX,GXplus) frame buffer driver
/* cg6.c: CGSIX (GX, GXplus, TGX) frame buffer driver
*
* Copyright (C) 2003 David S. Miller (davem@redhat.com)
* Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
* Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
*
* 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 <linux/fb.h>
#include <linux/mm.h>
#include <video/sbusfb.h>
#include <asm/io.h>
#include <asm/sbus.h>
#include <asm/oplib.h>
#include <asm/fbio.h>
#include "sbuslib.h"
/*
* Local functions.
*/
static int cg6_check_var(struct fb_var_screeninfo *, struct fb_info *);
static int cg6_set_par(struct fb_info *);
static int cg6_setcolreg(unsigned, unsigned, unsigned, unsigned,
unsigned, struct fb_info *);
static int cg6_blank(int, struct fb_info *);
static void cg6_imageblit(struct fb_info *, struct fb_image *);
static void cg6_fillrect(struct fb_info *, struct fb_fillrect *);
static int cg6_sync(struct fb_info *);
static int cg6_mmap(struct fb_info *, struct file *, struct vm_area_struct *);
/*
* Frame buffer operations
*/
static struct fb_ops cg6_ops = {
.owner = THIS_MODULE,
.fb_check_var = cg6_check_var,
.fb_set_par = cg6_set_par,
.fb_setcolreg = cg6_setcolreg,
.fb_blank = cg6_blank,
.fb_fillrect = cg6_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cg6_imageblit,
.fb_sync = cg6_sync,
.fb_mmap = cg6_mmap,
.fb_cursor = soft_cursor,
};
/* Offset of interesting structures in the OBIO space */
/*
......@@ -128,8 +162,6 @@
#define CG6_THC_MISC_INT (1 << 4)
#define CG6_THC_MISC_INIT 0x9f
MODULE_LICENSE("GPL");
/* The contents are unknown */
struct cg6_tec {
volatile int tec_matrix;
......@@ -210,430 +242,385 @@ struct cg6_fbc {
volatile u32 rectr, rectg, rectb, recta;
};
static struct sbus_mmap_map cg6_mmap_map[] = {
{ CG6_FBC, CG6_FBC_OFFSET, PAGE_SIZE },
{ CG6_TEC, CG6_TEC_OFFSET, PAGE_SIZE },
{ CG6_BTREGS, CG6_BROOKTREE_OFFSET, PAGE_SIZE },
{ CG6_FHC, CG6_FHC_OFFSET, PAGE_SIZE },
{ CG6_THC, CG6_THC_OFFSET, PAGE_SIZE },
{ CG6_ROM, CG6_ROM_OFFSET, 0x10000 },
{ CG6_RAM, CG6_RAM_OFFSET, SBUS_MMAP_FBSIZE(1) },
{ CG6_DHC, CG6_DHC_OFFSET, 0x40000 },
{ 0, 0, 0 }
struct bt_regs {
volatile u32 addr;
volatile u32 color_map;
volatile u32 control;
volatile u32 cursor;
};
static void cg6_setup(struct display *p)
{
p->next_line = p->fb_info->var.xres_virtual;
p->next_plane = 0;
}
struct cg6_par {
spinlock_t lock;
struct bt_regs *bt;
struct cg6_fbc *fbc;
struct cg6_thc *thc;
struct cg6_tec *tec;
volatile u32 *fhc;
u32 flags;
#define CG6_FLAG_BLANKED 0x00000001
unsigned long physbase;
unsigned long fbsize;
struct sbus_dev *sdev;
struct list_head list;
};
static void cg6_clear(struct vc_data *conp, struct display *p, int sy, int sx,
int height, int width)
static int cg6_sync(struct fb_info *info)
{
struct fb_info_sbusfb *fb = sbusfbinfo(p->fb_info);
register struct cg6_fbc *fbc = fb->s.cg6.fbc;
unsigned long flags;
int x, y, w, h;
int i;
struct cg6_par *par = (struct cg6_par *) info->par;
struct cg6_fbc *fbc = par->fbc;
int limit = 10000;
spin_lock_irqsave(&fb->lock, flags);
do {
i = sbus_readl(&fbc->s);
} while (i & 0x10000000);
sbus_writel(attr_bgcol_ec(p,conp), &fbc->fg);
sbus_writel(attr_bgcol_ec(p,conp), &fbc->bg);
sbus_writel(~0, &fbc->pixelm);
sbus_writel(0xea80ff00, &fbc->alu);
sbus_writel(0, &fbc->s);
sbus_writel(0, &fbc->clip);
sbus_writel(~0, &fbc->pm);
if (!(sbus_readl(&fbc->s) & 0x10000000))
break;
udelay(10);
} while (--limit > 0);
if (fontheightlog(p)) {
y = sy << fontheightlog(p); h = height << fontheightlog(p);
} else {
y = sy * fontheight(p); h = height * fontheight(p);
}
if (fontwidthlog(p)) {
x = sx << fontwidthlog(p); w = width << fontwidthlog(p);
} else {
x = sx * fontwidth(p); w = width * fontwidth(p);
}
sbus_writel(y + fb->y_margin, &fbc->arecty);
sbus_writel(x + fb->x_margin, &fbc->arectx);
sbus_writel(y + fb->y_margin + h, &fbc->arecty);
sbus_writel(x + fb->x_margin + w, &fbc->arectx);
do {
i = sbus_readl(&fbc->draw);
} while (i < 0 && (i & 0x20000000));
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
}
static void cg6_fill(struct fb_info_sbusfb *fb, struct display *p, int s,
int count, unsigned short *boxes)
/**
* cg6_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 cg6_fillrect(struct fb_info *info, struct fb_fillrect *rect)
{
int i;
register struct cg6_fbc *fbc = fb->s.cg6.fbc;
struct cg6_par *par = (struct cg6_par *) info->par;
struct cg6_fbc *fbc = par->fbc;
unsigned long flags;
s32 val;
spin_lock_irqsave(&fb->lock, flags);
do {
i = sbus_readl(&fbc->s);
} while (i & 0x10000000);
sbus_writel(attr_bgcol(p,s), &fbc->fg);
sbus_writel(attr_bgcol(p,s), &fbc->bg);
sbus_writel(~0, &fbc->pixelm);
/* XXX doesn't handle ROP_XOR */
spin_lock_irqsave(&par->lock, flags);
cg6_sync(info);
sbus_writel(rect->color, &fbc->fg);
sbus_writel(~(u32)0, &fbc->pixelm);
sbus_writel(0xea80ff00, &fbc->alu);
sbus_writel(0, &fbc->s);
sbus_writel(0, &fbc->clip);
sbus_writel(~0, &fbc->pm);
while (count-- > 0) {
sbus_writel(boxes[1], &fbc->arecty);
sbus_writel(boxes[0], &fbc->arectx);
sbus_writel(boxes[3], &fbc->arecty);
sbus_writel(boxes[2], &fbc->arectx);
boxes += 4;
sbus_writel(~(u32)0, &fbc->pm);
sbus_writel(rect->dy, &fbc->arecty);
sbus_writel(rect->dx, &fbc->arectx);
sbus_writel(rect->dy + rect->height, &fbc->arecty);
sbus_writel(rect->dx + rect->width, &fbc->arectx);
do {
i = sbus_readl(&fbc->draw);
} while (i < 0 && (i & 0x20000000));
}
spin_unlock_irqrestore(&fb->lock, flags);
val = sbus_readl(&fbc->draw);
} while (val < 0 && (val & 0x20000000));
spin_unlock_irqrestore(&par->lock, flags);
}
static void cg6_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx)
/**
* cg6_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 cg6_imageblit(struct fb_info *info, struct fb_image *image)
{
struct fb_info_sbusfb *fb = sbusfbinfo(p->fb_info);
register struct cg6_fbc *fbc = fb->s.cg6.fbc;
struct cg6_par *par = (struct cg6_par *) info->par;
struct cg6_fbc *fbc = par->fbc;
u8 *data = image->data;
unsigned long flags;
int i, x, y;
u8 *fd;
u32 x, y;
int i, width;
spin_lock_irqsave(&fb->lock, flags);
if (fontheightlog(p)) {
y = fb->y_margin + (yy << fontheightlog(p));
i = ((c & p->charmask) << fontheightlog(p));
} else {
y = fb->y_margin + (yy * fontheight(p));
i = (c & p->charmask) * fontheight(p);
if (image->depth > 1) {
cfb_imageblit(info, image);
return;
}
if (fontwidth(p) <= 8)
fd = p->fontdata + i;
else
fd = p->fontdata + (i << 1);
if (fontwidthlog(p))
x = fb->x_margin + (xx << fontwidthlog(p));
else
x = fb->x_margin + (xx * fontwidth(p));
do {
i = sbus_readl(&fbc->s);
} while (i & 0x10000000);
sbus_writel(attr_fgcol(p,c), &fbc->fg);
sbus_writel(attr_bgcol(p,c), &fbc->bg);
sbus_writel(0x140000, &fbc->mode);
sbus_writel(0xe880fc30, &fbc->alu);
sbus_writel(~0, &fbc->pixelm);
sbus_writel(0, &fbc->s);
sbus_writel(0, &fbc->clip);
sbus_writel(0xff, &fbc->pm);
sbus_writel(0, &fbc->incx);
sbus_writel(1, &fbc->incy);
sbus_writel(x, &fbc->x0);
sbus_writel(x + fontwidth(p) - 1, &fbc->x1);
sbus_writel(y, &fbc->y0);
if (fontwidth(p) <= 8) {
for (i = 0; i < fontheight(p); i++) {
u32 val = *fd++ << 24;
sbus_writel(val, &fbc->font);
}
} else {
for (i = 0; i < fontheight(p); i++) {
u32 val = *(u16 *)fd << 16;
sbus_writel(val, &fbc->font);
fd += 2;
}
}
spin_unlock_irqrestore(&fb->lock, flags);
}
spin_lock_irqsave(&par->lock, flags);
static void cg6_putcs(struct vc_data *conp, struct display *p, const unsigned short *s,
int count, int yy, int xx)
{
struct fb_info_sbusfb *fb = sbusfbinfo(p->fb_info);
register struct cg6_fbc *fbc = fb->s.cg6.fbc;
unsigned long flags;
int i, x, y;
u8 *fd1, *fd2, *fd3, *fd4;
u16 c;
cg6_sync(info);
spin_lock_irqsave(&fb->lock, flags);
do {
i = sbus_readl(&fbc->s);
} while (i & 0x10000000);
c = scr_readw(s);
sbus_writel(attr_fgcol(p, c), &fbc->fg);
sbus_writel(attr_bgcol(p, c), &fbc->bg);
sbus_writel(image->fg_color, &fbc->fg);
sbus_writel(image->bg_color, &fbc->bg);
sbus_writel(0x140000, &fbc->mode);
sbus_writel(0xe880fc30, &fbc->alu);
sbus_writel(~0, &fbc->pixelm);
sbus_writel(~(u32)0, &fbc->pixelm);
sbus_writel(0, &fbc->s);
sbus_writel(0, &fbc->clip);
sbus_writel(0xff, &fbc->pm);
x = fb->x_margin;
y = fb->y_margin;
if (fontwidthlog(p))
x += (xx << fontwidthlog(p));
else
x += xx * fontwidth(p);
if (fontheightlog(p))
y += (yy << fontheightlog(p));
else
y += (yy * fontheight(p));
if (fontwidth(p) <= 8) {
while (count >= 4) {
count -= 4;
sbus_writel(0, &fbc->incx);
sbus_writel(1, &fbc->incy);
sbus_writel(x, &fbc->x0);
sbus_writel((x += 4 * fontwidth(p)) - 1, &fbc->x1);
sbus_writel(32, &fbc->incx);
sbus_writel(0, &fbc->incy);
x = image->dx;
y = image->dy;
for (i = 0; i < image->height; i++) {
width = image->width;
while (width >= 32) {
u32 val;
sbus_writel(y, &fbc->y0);
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 = ((u32)*fd4++) |
((((u32)*fd3++) |
((((u32)*fd2++) |
(((u32)*fd1++)
<< 8)) << 8)) << 8);
sbus_writel(val, &fbc->font);
}
} 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));
sbus_writel(val, &fbc->font);
}
}
}
} else {
while (count >= 2) {
count -= 2;
sbus_writel(0, &fbc->incx);
sbus_writel(1, &fbc->incy);
sbus_writel(x, &fbc->x0);
sbus_writel((x += 2 * fontwidth(p)) - 1, &fbc->x1);
sbus_writel(y, &fbc->y0);
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));
sbus_writel(x + 32 - 1, &fbc->x1);
val = ((u32)data[0] << 24) |
((u32)data[1] << 16) |
((u32)data[2] << 8) |
((u32)data[3] << 0);
sbus_writel(val, &fbc->font);
fd1 += 2; fd2 += 2;
}
}
data += 4;
x += 32;
width -= 32;
}
while (count) {
count--;
sbus_writel(0, &fbc->incx);
sbus_writel(1, &fbc->incy);
sbus_writel(x, &fbc->x0);
sbus_writel((x += fontwidth(p)) - 1, &fbc->x1);
if (width) {
u32 val;
sbus_writel(y, &fbc->y0);
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;
sbus_writel(val, &fbc->font);
}
sbus_writel(x, &fbc->x0);
sbus_writel(x + width - 1, &fbc->x1);
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 {
fd1 = p->fontdata + (i << 1);
for (i = 0; i < fontheight(p); i++) {
u32 val = *(u16 *)fd1 << 16;
sbus_writel(val, &fbc->font);
fd1 += 2;
val = ((u32) data[0] << 24) |
((u32) data[1] << 16) |
((u32) data[2] << 8);
data += 3;
}
sbus_writel(val, &fbc->font);
}
y += 1;
x = image->dx;
}
spin_unlock_irqrestore(&fb->lock, flags);
spin_unlock_irqrestore(&par->lock, flags);
}
/**
* cg6_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 cg6_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
if (var->bits_per_pixel != 8)
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;
return 0;
}
static void cg6_revc(struct display *p, int xx, int yy)
/**
* cg6_set_par - Optional function. Alters the hardware state.
* @info: frame buffer structure that represents a single frame buffer
*/
static int
cg6_set_par(struct fb_info *info)
{
/* Not used if hw cursor */
return 0;
}
static void cg6_loadcmap (struct fb_info_sbusfb *fb, struct display *p, int index, int count)
/**
* cg6_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 cg6_setcolreg(unsigned regno,
unsigned red, unsigned green, unsigned blue,
unsigned transp, struct fb_info *info)
{
struct bt_regs *bt = fb->s.cg6.bt;
struct cg6_par *par = (struct cg6_par *) info->par;
struct bt_regs *bt = par->bt;
unsigned long flags;
int i;
spin_lock_irqsave(&fb->lock, flags);
sbus_writel(index << 24, &bt->addr);
for (i = index; count--; i++){
sbus_writel(fb->color_map CM(i,0) << 24,
&bt->color_map);
sbus_writel(fb->color_map CM(i,1) << 24,
&bt->color_map);
sbus_writel(fb->color_map CM(i,2) << 24,
&bt->color_map);
}
spin_unlock_irqrestore(&fb->lock, flags);
if (regno >= 256)
return 1;
red >>= 8;
green >>= 8;
blue >>= 8;
spin_lock_irqsave(&par->lock, flags);
sbus_writel((u32)regno << 24, &bt->addr);
sbus_writel((u32)red << 24, &bt->color_map);
sbus_writel((u32)green << 24, &bt->color_map);
sbus_writel((u32)blue << 24, &bt->color_map);
spin_unlock_irqrestore(&par->lock, flags);
return 0;
}
static void cg6_restore_palette (struct fb_info_sbusfb *fb)
/**
* cg6_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
cg6_blank(int blank, struct fb_info *info)
{
struct bt_regs *bt = fb->s.cg6.bt;
struct cg6_par *par = (struct cg6_par *) info->par;
struct cg6_thc *thc = par->thc;
unsigned long flags;
u32 val;
spin_lock_irqsave(&par->lock, flags);
switch (blank) {
case 0: /* Unblanking */
val = sbus_readl(&thc->thc_misc);
val |= CG6_THC_MISC_VIDEO;
sbus_writel(val, &thc->thc_misc);
par->flags &= ~CG6_FLAG_BLANKED;
break;
case 1: /* Normal blanking */
case 2: /* VESA blank (vsync off) */
case 3: /* VESA blank (hsync off) */
case 4: /* Poweroff */
val = sbus_readl(&thc->thc_misc);
val &= ~CG6_THC_MISC_VIDEO;
sbus_writel(val, &thc->thc_misc);
par->flags |= CG6_FLAG_BLANKED;
break;
}
spin_unlock_irqrestore(&par->lock, flags);
spin_lock_irqsave(&fb->lock, flags);
sbus_writel(0, &bt->addr);
sbus_writel(0xffffffff, &bt->color_map);
sbus_writel(0xffffffff, &bt->color_map);
sbus_writel(0xffffffff, &bt->color_map);
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
}
static struct display_switch cg6_dispsw __initdata = {
.setup = cg6_setup,
.bmove = fbcon_redraw_bmove,
.clear = cg6_clear,
.putc = cg6_putc,
.putcs = cg6_putcs,
.revc = cg6_revc,
.fontwidthmask =FONTWIDTHRANGE(1,16) /* Allow fontwidths up to 16 */
static struct sbus_mmap_map cg6_mmap_map[] = {
{ CG6_FBC, CG6_FBC_OFFSET, PAGE_SIZE },
{ CG6_TEC, CG6_TEC_OFFSET, PAGE_SIZE },
{ CG6_BTREGS, CG6_BROOKTREE_OFFSET, PAGE_SIZE },
{ CG6_FHC, CG6_FHC_OFFSET, PAGE_SIZE },
{ CG6_THC, CG6_THC_OFFSET, PAGE_SIZE },
{ CG6_ROM, CG6_ROM_OFFSET, 0x10000 },
{ CG6_RAM, CG6_RAM_OFFSET, SBUS_MMAP_FBSIZE(1) },
{ CG6_DHC, CG6_DHC_OFFSET, 0x40000 },
{ 0, 0, 0 }
};
static void cg6_setcursormap (struct fb_info_sbusfb *fb, u8 *red, u8 *green, u8 *blue)
static int cg6_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
{
struct bt_regs *bt = fb->s.cg6.bt;
unsigned long flags;
struct cg6_par *par = (struct cg6_par *)info->par;
spin_lock_irqsave(&fb->lock, flags);
sbus_writel(1 << 24, &bt->addr);
sbus_writel(red[0] << 24, &bt->cursor);
sbus_writel(green[0] << 24, &bt->cursor);
sbus_writel(blue[0] << 24, &bt->cursor);
sbus_writel(3 << 24, &bt->addr);
sbus_writel(red[1] << 24, &bt->cursor);
sbus_writel(green[1] << 24, &bt->cursor);
sbus_writel(blue[1] << 24, &bt->cursor);
spin_unlock_irqrestore(&fb->lock, flags);
return sbusfb_mmap_helper(cg6_mmap_map,
par->physbase, par->fbsize,
par->sdev->reg_addrs[0].which_io,
vma);
}
/* Set cursor shape */
static void cg6_setcurshape (struct fb_info_sbusfb *fb)
/*
* Initialisation
*/
static void
cg6_init_fix(struct fb_info *info, int linebytes)
{
struct cg6_thc *thc = fb->s.cg6.thc;
unsigned long flags;
int i;
struct cg6_par *par = (struct cg6_par *)info->par;
const char *cg6_cpu_name, *cg6_card_name;
u32 conf;
spin_lock_irqsave(&fb->lock, flags);
for (i = 0; i < 32; i++) {
sbus_writel(fb->cursor.bits[0][i],
&thc->thc_cursmask [i]);
sbus_writel(fb->cursor.bits[1][i],
&thc->thc_cursbits [i]);
conf = sbus_readl(par->fhc);
switch(conf & CG6_FHC_CPU_MASK) {
case CG6_FHC_CPU_SPARC:
cg6_cpu_name = "sparc";
break;
case CG6_FHC_CPU_68020:
cg6_cpu_name = "68020";
break;
default:
cg6_cpu_name = "i386";
break;
};
if (((conf >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK) >= 11) {
if (par->fbsize <= 0x100000) {
cg6_card_name = "TGX";
} else {
cg6_card_name = "TGX+";
}
} else {
if (par->fbsize <= 0x100000) {
cg6_card_name = "GX";
} else {
cg6_card_name = "GX+";
}
}
spin_unlock_irqrestore(&fb->lock, flags);
}
/* Load cursor information */
static void cg6_setcursor (struct fb_info_sbusfb *fb)
{
unsigned int v;
unsigned long flags;
struct cg_cursor *c = &fb->cursor;
spin_lock_irqsave(&fb->lock, flags);
if (c->enable)
v = ((c->cpos.fbx - c->chot.fbx) << 16)
|((c->cpos.fby - c->chot.fby) & 0xffff);
else
/* Magic constant to turn off the cursor */
v = ((65536-32) << 16) | (65536-32);
sbus_writel(v, &fb->s.cg6.thc->thc_cursxy);
spin_unlock_irqrestore(&fb->lock, flags);
}
sprintf(info->fix.id, "%s %s", cg6_card_name, cg6_cpu_name);
info->fix.id[sizeof(info->fix.id)-1] = 0;
static int cg6_blank (struct fb_info_sbusfb *fb)
{
unsigned long flags;
u32 tmp;
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
spin_lock_irqsave(&fb->lock, flags);
tmp = sbus_readl(&fb->s.cg6.thc->thc_misc);
tmp &= ~CG6_THC_MISC_VIDEO;
sbus_writel(tmp, &fb->s.cg6.thc->thc_misc);
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
info->fix.line_length = linebytes;
info->fix.accel = FB_ACCEL_SUN_CGSIX;
}
static int cg6_unblank (struct fb_info_sbusfb *fb)
/* Initialize Brooktree DAC */
static void cg6_bt_init(struct cg6_par *par)
{
unsigned long flags;
u32 tmp;
struct bt_regs *bt = par->bt;
spin_lock_irqsave(&fb->lock, flags);
tmp = sbus_readl(&fb->s.cg6.thc->thc_misc);
tmp |= CG6_THC_MISC_VIDEO;
sbus_writel(tmp, &fb->s.cg6.thc->thc_misc);
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
sbus_writel(0x04 << 24, &bt->addr); /* color planes */
sbus_writel(0xff << 24, &bt->control);
sbus_writel(0x05 << 24, &bt->addr);
sbus_writel(0x00 << 24, &bt->control);
sbus_writel(0x06 << 24, &bt->addr); /* overlay plane */
sbus_writel(0x73 << 24, &bt->control);
sbus_writel(0x07 << 24, &bt->addr);
sbus_writel(0x00 << 24, &bt->control);
}
static void cg6_reset (struct fb_info_sbusfb *fb)
static void cg6_chip_init(struct fb_info *info)
{
unsigned int rev, conf;
struct cg6_tec *tec = fb->s.cg6.tec;
struct cg6_fbc *fbc = fb->s.cg6.fbc;
unsigned long flags;
u32 mode, tmp;
struct cg6_par *par = (struct cg6_par *) info->par;
struct cg6_tec *tec = par->tec;
struct cg6_fbc *fbc = par->fbc;
u32 rev, conf, mode, tmp;
int i;
spin_lock_irqsave(&fb->lock, flags);
/* Turn off stuff in the Transform Engine. */
sbus_writel(0, &tec->tec_matrix);
sbus_writel(0, &tec->tec_clip);
sbus_writel(0, &tec->tec_vdc);
/* Take care of bugs in old revisions. */
rev = (sbus_readl(fb->s.cg6.fhc) >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK;
rev = (sbus_readl(par->fhc) >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK;
if (rev < 5) {
conf = (sbus_readl(fb->s.cg6.fhc) & CG6_FHC_RES_MASK) |
conf = (sbus_readl(par->fhc) & CG6_FHC_RES_MASK) |
CG6_FHC_CPU_68020 | CG6_FHC_TEST |
(11 << CG6_FHC_TEST_X_SHIFT) |
(11 << CG6_FHC_TEST_Y_SHIFT);
if (rev < 2)
conf |= CG6_FHC_DST_DISABLE;
sbus_writel(conf, fb->s.cg6.fhc);
sbus_writel(conf, par->fhc);
}
/* Set things in the FBC. Bad things appear to happen if we do
......@@ -658,167 +645,149 @@ static void cg6_reset (struct fb_info_sbusfb *fb)
sbus_writel(0, &fbc->offy);
sbus_writel(0, &fbc->clipminx);
sbus_writel(0, &fbc->clipminy);
sbus_writel(fb->type.fb_width - 1, &fbc->clipmaxx);
sbus_writel(fb->type.fb_height - 1, &fbc->clipmaxy);
/* Enable cursor in Brooktree DAC. */
sbus_writel(0x06 << 24, &fb->s.cg6.bt->addr);
tmp = sbus_readl(&fb->s.cg6.bt->control);
tmp |= 0x03 << 24;
sbus_writel(tmp, &fb->s.cg6.bt->control);
spin_unlock_irqrestore(&fb->lock, flags);
sbus_writel(info->var.xres - 1, &fbc->clipmaxx);
sbus_writel(info->var.yres - 1, &fbc->clipmaxy);
/* Disable cursor in Brooktree DAC. */
sbus_writel(0x06 << 24, &par->bt->addr);
tmp = sbus_readl(&par->bt->control);
tmp &= ~(0x03 << 24);
sbus_writel(tmp, &par->bt->control);
}
static void cg6_margins (struct fb_info_sbusfb *fb, struct display *p, int x_margin, int y_margin)
{
fb->info.screen_base += (y_margin - fb->y_margin) *
fb->info.fix.line_length + (x_margin - fb->x_margin);
}
struct all_info {
struct fb_info info;
struct cg6_par par;
struct list_head list;
};
static LIST_HEAD(cg6_list);
static int __init cg6_rasterimg (struct fb_info *info, int start)
static void cg6_init_one(struct sbus_dev *sdev)
{
struct fb_info_sbusfb *fb = sbusfbinfo(info);
register struct cg6_fbc *fbc = fb->s.cg6.fbc;
int i;
struct all_info *all;
int linebytes;
do {
i = sbus_readl(&fbc->s);
} while (i & 0x10000000);
return 0;
}
static char idstring[70] __initdata = { 0 };
char __init *cgsixfb_init(struct fb_info_sbusfb *fb)
{
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 sbus_dev *sdev = fb->sbdp;
unsigned long phys = sdev->reg_addrs[0].phys_addr;
u32 conf;
char *p;
char *cardtype;
struct bt_regs *bt;
struct fb_ops *fbops;
all = kmalloc(sizeof(*all), GFP_KERNEL);
if (!all) {
printk(KERN_ERR "cg6: Cannot allocate memory.\n");
return;
}
memset(all, 0, sizeof(*all));
fbops = kmalloc(sizeof(*fbops), GFP_KERNEL);
if (fbops == NULL)
return NULL;
INIT_LIST_HEAD(&all->list);
*fbops = *fb->info.fbops;
fbops->fb_rasterimg = cg6_rasterimg;
fb->info.fbops = fbops;
spin_lock_init(&all->par.lock);
all->par.sdev = sdev;
if (prom_getbool (fb->prom_node, "dblbuf")) {
type->fb_size *= 4;
fix->smem_len *= 4;
}
all->par.physbase = sdev->reg_addrs[0].phys_addr;
fix->line_length = fb->info.var.xres_virtual;
fix->accel = FB_ACCEL_SUN_CGSIX;
sbusfb_fill_var(&all->info.var, sdev->prom_node, 8);
var->accel_flags = FB_ACCELF_TEXT;
linebytes = prom_getintdefault(sdev->prom_node, "linebytes",
all->info.var.xres);
all->par.fbsize = PAGE_ALIGN(linebytes * all->info.var.yres);
if (prom_getbool(sdev->prom_node, "dblbuf"))
all->par.fbsize *= 4;
disp->scrollmode = SCROLL_YREDRAW;
if (!fb->info.screen_base) {
fb->info.screen_base = (char *)
sbus_ioremap(&sdev->resource[0], CG6_RAM_OFFSET,
type->fb_size, "cgsix ram");
}
fb->info.screen_base += fix->line_length * fb->y_margin + fb->x_margin;
fb->s.cg6.fbc = (struct cg6_fbc *)
all->par.fbc = (struct cg6_fbc *)
sbus_ioremap(&sdev->resource[0], CG6_FBC_OFFSET,
4096, "cgsix fbc");
fb->s.cg6.tec = (struct cg6_tec *)
all->par.tec = (struct cg6_tec *)
sbus_ioremap(&sdev->resource[0], CG6_TEC_OFFSET,
sizeof(struct cg6_tec), "cgsix tec");
fb->s.cg6.thc = (struct cg6_thc *)
all->par.thc = (struct cg6_thc *)
sbus_ioremap(&sdev->resource[0], CG6_THC_OFFSET,
sizeof(struct cg6_thc), "cgsix thc");
fb->s.cg6.bt = bt = (struct bt_regs *)
all->par.bt = (struct bt_regs *)
sbus_ioremap(&sdev->resource[0], CG6_BROOKTREE_OFFSET,
sizeof(struct bt_regs), "cgsix dac");
fb->s.cg6.fhc = (u32 *)
all->par.fhc = (u32 *)
sbus_ioremap(&sdev->resource[0], CG6_FHC_OFFSET,
sizeof(u32), "cgsix fhc");
#if 0
prom_printf("CG6: RES[%016lx:%016lx:%016lx]\n",
sdev->resource[0].start,
sdev->resource[0].end,
sdev->resource[0].flags);
prom_printf("CG6: fbc(%p) tec(%p) thc(%p) bt(%p) fhc(%p)\n",
fb->s.cg6.fbc,
fb->s.cg6.tec,
fb->s.cg6.thc,
fb->s.cg6.bt,
fb->s.cg6.fhc);
prom_halt();
all->info.node = NODEV;
all->info.flags = FBINFO_FLAG_DEFAULT;
all->info.fbops = &cg6_ops;
#ifdef CONFIG_SPARC32
all->info.screen_base = (char *)
prom_getintdefault(sdev->prom_node, "address", 0);
#endif
fb->dispsw = cg6_dispsw;
fb->margins = cg6_margins;
fb->loadcmap = cg6_loadcmap;
fb->setcursor = cg6_setcursor;
fb->setcursormap = cg6_setcursormap;
fb->setcurshape = cg6_setcurshape;
fb->restore_palette = cg6_restore_palette;
fb->fill = cg6_fill;
fb->blank = cg6_blank;
fb->unblank = cg6_unblank;
fb->reset = cg6_reset;
fb->physbase = phys;
fb->mmap_map = cg6_mmap_map;
/* Initialize Brooktree DAC */
sbus_writel(0x04 << 24, &bt->addr); /* color planes */
sbus_writel(0xff << 24, &bt->control);
sbus_writel(0x05 << 24, &bt->addr);
sbus_writel(0x00 << 24, &bt->control);
sbus_writel(0x06 << 24, &bt->addr); /* overlay plane */
sbus_writel(0x73 << 24, &bt->control);
sbus_writel(0x07 << 24, &bt->addr);
sbus_writel(0x00 << 24, &bt->control);
if (!all->info.screen_base)
all->info.screen_base = (char *)
sbus_ioremap(&sdev->resource[0], CG6_RAM_OFFSET,
all->par.fbsize, "cgsix ram");
all->info.currcon = -1;
all->info.par = &all->par;
conf = sbus_readl(fb->s.cg6.fhc);
switch(conf & CG6_FHC_CPU_MASK) {
case CG6_FHC_CPU_SPARC: p = "sparc"; break;
case CG6_FHC_CPU_68020: p = "68020"; break;
default: p = "i386"; break;
}
all->info.var.accel_flags = FB_ACCELF_TEXT;
if (((conf >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK) >= 11) {
if (fix->smem_len <= 0x100000) {
cardtype = "TGX";
} else {
cardtype = "TGX+";
cg6_bt_init(&all->par);
cg6_chip_init(&all->info);
cg6_blank(0, &all->info);
if (fb_alloc_cmap(&all->info.cmap, 256, 0)) {
printk(KERN_ERR "cg6: Could not allocate color map.\n");
kfree(all);
return;
}
} else {
if (fix->smem_len <= 0x100000) {
cardtype = "GX";
} else {
cardtype = "GX+";
cg6_set_par(&all->info);
cg6_init_fix(&all->info, linebytes);
if (register_framebuffer(&all->info) < 0) {
printk(KERN_ERR "cg6: Could not register framebuffer.\n");
fb_dealloc_cmap(&all->info.cmap);
kfree(all);
return;
}
list_add(&all->list, &cg6_list);
printk("cg6: CGsix [%s] at %lx:%lx\n",
all->info.fix.id,
(long) sdev->reg_addrs[0].which_io,
(long) sdev->reg_addrs[0].phys_addr);
}
int __init cg6_init(void)
{
struct sbus_bus *sbus;
struct sbus_dev *sdev;
for_all_sbusdev(sdev, sbus) {
if (!strcmp(sdev->prom_name, "cgsix") ||
!strcmp(sdev->prom_name, "cgthree+"))
cg6_init_one(sdev);
}
sprintf(idstring,
#ifdef __sparc_v9__
"cgsix at %016lx TEC Rev %x CPU %s Rev %x [%s]", phys,
#else
"cgsix at %x.%08lx TEC Rev %x CPU %s Rev %x [%s]",
fb->iospace, phys,
#endif
((sbus_readl(&fb->s.cg6.thc->thc_misc) >> CG6_THC_MISC_REV_SHIFT) &
CG6_THC_MISC_REV_MASK),
p, (conf >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK, cardtype);
return 0;
}
sprintf(fb->info.modename, "CGsix [%s]", cardtype);
sprintf(fix->id, "CGsix [%s]", cardtype);
void __exit cg6_exit(void)
{
struct list_head *pos, *tmp;
list_for_each_safe(pos, tmp, &cg6_list) {
struct all_info *all = list_entry(pos, typeof(*all), list);
cg6_reset(fb);
unregister_framebuffer(&all->info);
fb_dealloc_cmap(&all->info.cmap);
kfree(all);
}
}
return idstring;
int __init
cg6_setup(char *arg)
{
/* No cmdline options yet... */
return 0;
}
#ifdef MODULE
module_init(cg6_init);
module_exit(cg6_exit);
#endif
MODULE_DESCRIPTION("framebuffer driver for CGsix chipsets");
MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
MODULE_LICENSE("GPL");
/* $Id: cgthreefb.c,v 1.11 2001/09/19 00:04:33 davem Exp $
* cgthreefb.c: CGthree frame buffer driver
*
* Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/
#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 <asm/io.h>
#include <video/fbcon-cfb8.h>
/* Control Register Constants */
#define CG3_CR_ENABLE_INTS 0x80
#define CG3_CR_ENABLE_VIDEO 0x40
#define CG3_CR_ENABLE_TIMING 0x20
#define CG3_CR_ENABLE_CURCMP 0x10
#define CG3_CR_XTAL_MASK 0x0c
#define CG3_CR_DIVISOR_MASK 0x03
/* Status Register Constants */
#define CG3_SR_PENDING_INT 0x80
#define CG3_SR_RES_MASK 0x70
#define CG3_SR_1152_900_76_A 0x40
#define CG3_SR_1152_900_76_B 0x60
#define CG3_SR_ID_MASK 0x0f
#define CG3_SR_ID_COLOR 0x01
#define CG3_SR_ID_MONO 0x02
#define CG3_SR_ID_MONO_ECL 0x03
MODULE_LICENSE("GPL");
enum cg3_type {
CG3_AT_66HZ = 0,
CG3_AT_76HZ,
CG3_RDI
};
struct cg3_regs {
struct bt_regs cmap;
volatile u8 control;
volatile u8 status;
volatile u8 cursor_start;
volatile u8 cursor_end;
volatile u8 h_blank_start;
volatile u8 h_blank_end;
volatile u8 h_sync_start;
volatile u8 h_sync_end;
volatile u8 comp_sync_end;
volatile u8 v_blank_start_high;
volatile u8 v_blank_start_low;
volatile u8 v_blank_end;
volatile u8 v_sync_start;
volatile u8 v_sync_end;
volatile u8 xfer_holdoff_start;
volatile u8 xfer_holdoff_end;
};
/* Offset of interesting structures in the OBIO space */
#define CG3_REGS_OFFSET 0x400000UL
#define CG3_RAM_OFFSET 0x800000UL
static struct sbus_mmap_map cg3_mmap_map[] = {
{ CG3_MMAP_OFFSET, CG3_RAM_OFFSET, SBUS_MMAP_FBSIZE(1) },
{ 0, 0, 0 }
};
/* The cg3 palette is loaded with 4 color values at each time */
/* so you end up with: (rgb)(r), (gb)(rg), (b)(rgb), and so on */
#define D4M3(x) ((((x)>>2)<<1) + ((x)>>2)) /* (x/4)*3 */
#define D4M4(x) ((x)&~0x3) /* (x/4)*4 */
static void cg3_loadcmap (struct fb_info_sbusfb *fb, struct display *p, int index, int count)
{
struct bt_regs *bt = &fb->s.cg3.regs->cmap;
unsigned long flags;
u32 *i;
volatile u8 *regp;
int steps;
spin_lock_irqsave(&fb->lock, flags);
i = (((u32 *)fb->color_map) + D4M3(index));
steps = D4M3(index+count-1) - D4M3(index)+3;
regp = (volatile u8 *)&bt->addr;
sbus_writeb(D4M4(index), regp);
while (steps--) {
u32 val = *i++;
sbus_writel(val, &bt->color_map);
}
spin_unlock_irqrestore(&fb->lock, flags);
}
static int cg3_blank (struct fb_info_sbusfb *fb)
{
unsigned long flags;
u8 tmp;
spin_lock_irqsave(&fb->lock, flags);
tmp = sbus_readb(&fb->s.cg3.regs->control);
tmp &= ~CG3_CR_ENABLE_VIDEO;
sbus_writeb(tmp, &fb->s.cg3.regs->control);
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
}
static int cg3_unblank (struct fb_info_sbusfb *fb)
{
unsigned long flags;
u8 tmp;
spin_lock_irqsave(&fb->lock, flags);
tmp = sbus_readb(&fb->s.cg3.regs->control);
tmp |= CG3_CR_ENABLE_VIDEO;
sbus_writeb(tmp, &fb->s.cg3.regs->control);
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
}
static void cg3_margins (struct fb_info_sbusfb *fb, struct display *p,
int x_margin, int y_margin)
{
fb->info.screen_base += (y_margin - fb->y_margin) *
fb->info.fix.line_length + (x_margin - fb->x_margin);
}
static u8 cg3regvals_66hz[] __initdata = { /* 1152 x 900, 66 Hz */
0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14,
0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24,
0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x20, 0
};
static u8 cg3regvals_76hz[] __initdata = { /* 1152 x 900, 76 Hz */
0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f,
0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a,
0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x24, 0
};
static u8 cg3regvals_rdi[] __initdata = { /* 640 x 480, cgRDI */
0x14, 0x70, 0x15, 0x20, 0x16, 0x08, 0x17, 0x10,
0x18, 0x06, 0x19, 0x02, 0x1a, 0x31, 0x1b, 0x51,
0x1c, 0x06, 0x1d, 0x0c, 0x1e, 0xff, 0x1f, 0x01,
0x10, 0x22, 0
};
static u8 *cg3_regvals[] __initdata = {
cg3regvals_66hz, cg3regvals_76hz, cg3regvals_rdi
};
static u_char cg3_dacvals[] __initdata = {
4, 0xff, 5, 0x00, 6, 0x70, 7, 0x00, 0
};
static char idstring[60] __initdata = { 0 };
char __init *cgthreefb_init(struct fb_info_sbusfb *fb)
{
struct fb_fix_screeninfo *fix = &fb->info.fix;
struct display *disp = &fb->disp;
struct fbtype *type = &fb->type;
struct sbus_dev *sdev = fb->sbdp;
unsigned long phys = sdev->reg_addrs[0].phys_addr;
int cgRDI = strstr(fb->sbdp->prom_name, "cgRDI") != NULL;
#ifndef FBCON_HAS_CFB8
return NULL;
#endif
if (!fb->s.cg3.regs) {
fb->s.cg3.regs = (struct cg3_regs *)
sbus_ioremap(&sdev->resource[0], CG3_REGS_OFFSET,
sizeof(struct cg3_regs), "cg3 regs");
if (cgRDI) {
char buffer[40];
char *p;
int ww, hh;
*buffer = 0;
prom_getstring (fb->prom_node, "params", buffer, sizeof(buffer));
if (*buffer) {
ww = simple_strtoul (buffer, &p, 10);
if (ww && *p == 'x') {
hh = simple_strtoul (p + 1, &p, 10);
if (hh && *p == '-') {
if (type->fb_width != ww || type->fb_height != hh) {
type->fb_width = ww;
type->fb_height = hh;
return SBUSFBINIT_SIZECHANGE;
}
}
}
}
}
}
strcpy(fb->info.modename, "CGthree");
strcpy(fix->id, "CGthree");
fix->line_length = fb->info.var.xres_virtual;
fix->accel = FB_ACCEL_SUN_CGTHREE;
disp->scrollmode = SCROLL_YREDRAW;
if (!fb->info.screen_base) {
fb->info.screen_base = (char *)
sbus_ioremap(&sdev->resource[0], CG3_RAM_OFFSET,
type->fb_size, "cg3 ram");
}
fb->info.screen_base += fix->line_length * fb->y_margin + fb->x_margin;
fb->dispsw = fbcon_cfb8;
fb->margins = cg3_margins;
fb->loadcmap = cg3_loadcmap;
fb->blank = cg3_blank;
fb->unblank = cg3_unblank;
fb->physbase = phys;
fb->mmap_map = cg3_mmap_map;
#ifdef __sparc_v9__
sprintf(idstring, "%s at %016lx", cgRDI ? "cgRDI" : "cgthree", phys);
#else
sprintf(idstring, "%s at %x.%08lx", cgRDI ? "cgRDI" : "cgthree", fb->iospace, phys);
#endif
if (!prom_getbool(fb->prom_node, "width")) {
/* Ugh, broken PROM didn't initialize us.
* Let's deal with this ourselves.
*/
enum cg3_type type;
u8 *p;
if (cgRDI)
type = CG3_RDI;
else {
u8 status = sbus_readb(&fb->s.cg3.regs->status), mon;
if ((status & CG3_SR_ID_MASK) == CG3_SR_ID_COLOR) {
mon = status & CG3_SR_RES_MASK;
if (mon == CG3_SR_1152_900_76_A ||
mon == CG3_SR_1152_900_76_B)
type = CG3_AT_76HZ;
else
type = CG3_AT_66HZ;
} else {
prom_printf("cgthree: can't handle SR %02x\n",
status);
prom_halt();
return NULL; /* fool gcc. */
}
}
for (p = cg3_regvals[type]; *p; p += 2) {
u8 *regp = &((u8 *)fb->s.cg3.regs)[p[0]];
sbus_writeb(p[1], regp);
}
for (p = cg3_dacvals; *p; p += 2) {
volatile u8 *regp;
regp = (volatile u8 *)&fb->s.cg3.regs->cmap.addr;
sbus_writeb(p[0], regp);
regp = (volatile u8 *)&fb->s.cg3.regs->cmap.control;
sbus_writeb(p[1], regp);
}
}
return idstring;
}
......@@ -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,20 @@ 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*);
extern int cg6_init(void);
extern int cg6_setup(char*);
extern int cg3_init(void);
extern int cg3_setup(char*);
extern int bw2_init(void);
extern int bw2_setup(char*);
extern int cg14_init(void);
extern int cg14_setup(char*);
extern int p9100_init(void);
extern int p9100_setup(char*);
extern int tcx_init(void);
extern int tcx_setup(char*);
static struct {
const char *name;
......@@ -153,13 +165,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 +249,27 @@ static struct {
#ifdef CONFIG_FB_STI
{ "stifb", stifb_init, stifb_setup },
#endif
#ifdef CONFIG_FB_FFB
{ "ffb", ffb_init, ffb_setup },
#endif
#ifdef CONFIG_FB_CG6
{ "cg6", cg6_init, cg6_setup },
#endif
#ifdef CONFIG_FB_CG3
{ "cg3", cg3_init, cg3_setup },
#endif
#ifdef CONFIG_FB_BW2
{ "bw2", bw2_init, bw2_setup },
#endif
#ifdef CONFIG_FB_CG14
{ "cg14", cg14_init, cg14_setup },
#endif
#ifdef CONFIG_FB_P9100
{ "p9100", p9100_init, p9100_setup },
#endif
#ifdef CONFIG_FB_TCX
{ "tcx", tcx_init, tcx_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);
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);
fg = ((u32 *)info->pseudo_palette)[rect->color];
spin_lock_irqsave(&par->lock, flags);
if (fg != par->fg_cache) {
FFBFifo(par, 1);
upa_writel(fg, &fbc->fg);
fb->s.ffb.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;
par->fg_cache = fg;
}
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 (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;
if (area->dx != area->sx ||
area->dy == area->dy) {
cfb_copyarea(info, area);
return;
}
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;
ffb_log(OP_IMAGEBLIT, image->depth,
image->dx, image->dy, image->width, image->height);
if (image->depth > 1) {
cfb_imageblit(info, image);
return;
}
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);
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);
if (fgbg != *(u64 *)&par->fg_cache) {
FFBFifo(par, 2);
upa_writeq(fgbg, &fbc->fg);
*(u64 *)&fb->s.ffb.fg_cache = fgbg;
*(u64 *)&par->fg_cache = fgbg;
}
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);
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)) {
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++) {
xy += (1 << 16);
while (width >= 32) {
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));
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);
fd1 += 2; fd2 += 2;
}
xy += 2 * fontwidth(p);
}
}
while (count) {
count--;
FFBFifo(fb, 2 + fontheight(p));
upa_writel(fontwidth(p), &fbc->fontw);
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;
upa_writel(val, &fbc->font);
data += 4;
width -= 32;
}
} else {
fd1 = p->fontdata + (i << 1);
for (i = 0; i < fontheight(p); i++) {
u32 val = *(u16 *)fd1 << 16;
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;
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_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)
{
struct ffb_dac *dac = fb->s.ffb.dac;
unsigned long flags;
int i, j = count;
u32 value;
spin_lock_irqsave(&fb->lock, flags);
upa_writel(0x2000 | index, &dac->type);
for (i = index; j--; i++) {
u32 val;
if (regno >= 256)
return 1;
/* 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);
}
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);
}
red >>= 8;
green >>= 8;
blue >>= 8;
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 */
};
value = (blue << 16) | (green << 8) | red;
((u32 *)info->pseudo_palette)[regno] = value;
return 0;
}
static void ffb_margins (struct fb_info_sbusfb *fb, struct display *p, int x_margin, int y_margin)
/**
* 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)
{
register struct ffb_fbc *fbc = fb->s.ffb.fbc;
struct ffb_par *par = (struct ffb_par *) info->par;
struct ffb_dac *dac = par->dac;
unsigned long flags;
u32 tmp;
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);
}
spin_lock_irqsave(&par->lock, flags);
static __inline__ void __ffb_curs_enable (struct fb_info_sbusfb *fb, int enable)
{
struct ffb_dac *dac = fb->s.ffb.dac;
u32 val;
FFBWait(par);
upa_writel(0x100, &dac->type2);
if (fb->s.ffb.dac_rev <= 2) {
val = enable ? 3 : 0;
} else {
val = enable ? 0 : 3;
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;
}
upa_writel(val, &dac->value2);
}
static void ffb_setcursormap (struct fb_info_sbusfb *fb, u8 *red, u8 *green, u8 *blue)
{
struct ffb_dac *dac = fb->s.ffb.dac;
unsigned long flags;
spin_unlock_irqrestore(&par->lock, 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);
return 0;
}
/* 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);
}
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 }
};
/* Load cursor information */
static void ffb_setcursor (struct fb_info_sbusfb *fb)
static int ffb_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
{
struct ffb_dac *dac = fb->s.ffb.dac;
struct cg_cursor *c = &fb->cursor;
unsigned long flags;
u32 val;
struct ffb_par *par = (struct ffb_par *)info->par;
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);
return sbusfb_mmap_helper(ffb_mmap_map,
par->physbase, par->fbsize,
0, vma);
}
static void ffb_switch_from_graph (struct fb_info_sbusfb *fb)
/*
* Initialisation
*/
static void
ffb_init_fix(struct fb_info *info)
{
register struct ffb_fbc *fbc = fb->s.ffb.fbc;
unsigned long flags;
struct ffb_par *par = (struct ffb_par *)info->par;
const char *ffb_type_name;
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);
}
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";
static int __init ffb_rasterimg (struct fb_info *info, int start)
{
ffb_switch_from_graph (sbusfbinfo(info));
return 0;
}
strncpy(info->fix.id, ffb_type_name, sizeof(info->fix.id) - 1);
info->fix.id[sizeof(info->fix.id)-1] = 0;
static char idstring[60] __initdata = { 0 };
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_TRUECOLOR;
static int __init creator_apply_upa_parent_ranges(int parent, struct linux_prom64_registers *regs)
/* Framebuffer length is the same regardless of resolution. */
info->fix.line_length = 8192;
info->fix.accel = FB_ACCEL_SUN_CREATOR;
}
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));
struct ffb_fbc *fbc;
struct ffb_dac *dac;
struct all_info *all;
fbops = kmalloc(sizeof(*fbops), GFP_KERNEL);
if (fbops == NULL) {
kfree(disp->dispsw_data);
return NULL;
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);
if (ffb_apply_upa_parent_ranges(parent, &regs[0])) {
printk("ffb: Cannot apply parent ranges to regs.\n");
return;
}
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");
all = kmalloc(sizeof(*all), GFP_KERNEL);
if (!all) {
printk(KERN_ERR "ffb: Cannot allocate memory.\n");
return;
}
memset(all, 0, sizeof(*all));
fix->visual = FB_VISUAL_TRUECOLOR;
fix->line_length = 8192;
fix->accel = FB_ACCEL_SUN_CREATOR;
INIT_LIST_HEAD(&all->list);
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
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;
/* 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);
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;
ffb_switch_from_graph(fb);
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);
fb->physbase = regs[0].phys_addr;
fb->mmap_map = ffb_mmap_map;
all->info.var.accel_flags = FB_ACCELF_TEXT;
fb->cursor.hwsize.fbx = 64;
fb->cursor.hwsize.fby = 64;
prom_getstring(node, "name", all->par.name, sizeof(all->par.name));
if (!strcmp(all->par.name, "SUNW,afb"))
all->par.flags |= FFB_FLAG_AFB;
type->fb_depth = 24;
all->par.board_type = prom_getintdefault(node, "board_type", 0);
upa_writel(0x8000, &fb->s.ffb.dac->type);
fb->s.ffb.dac_rev = (upa_readl(&fb->s.ffb.dac->value) >> 0x1c);
fbc = all->par.fbc;
if((upa_readl(&fbc->ucsr) & FFB_UCSR_ALL_ERRORS) != 0)
upa_writel(FFB_UCSR_ALL_ERRORS, &fbc->ucsr);
i = prom_getintdefault (fb->prom_node, "board_type", 8);
ffb_switch_from_graph(&all->par);
sprintf(idstring, "%s at %016lx type %d DAC %d",
fix->id, regs[0].phys_addr, i, fb->s.ffb.dac_rev);
dac = all->par.dac;
upa_writel(0x8000, &dac->type);
all->par.dac_rev = upa_readl(&dac->value) >> 0x1c;
/* 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);
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);
}
return idstring;
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");
/* p9100.c: P9100 frame buffer driver
*
* Copyright (C) 2003 David S. Miller (davem@redhat.com)
* Copyright 1999 Derrick J Brashear (shadow@dementia.org)
*
* Driver layout based loosely on tgafb.c, see that file for credits.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fb.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <asm/sbus.h>
#include <asm/oplib.h>
#include <asm/fbio.h>
#include "sbuslib.h"
/*
* Local functions.
*/
static int p9100_check_var(struct fb_var_screeninfo *, struct fb_info *);
static int p9100_set_par(struct fb_info *);
static int p9100_setcolreg(unsigned, unsigned, unsigned, unsigned,
unsigned, struct fb_info *);
static int p9100_blank(int, struct fb_info *);
static int p9100_mmap(struct fb_info *, struct file *, struct vm_area_struct *);
/*
* Frame buffer operations
*/
static struct fb_ops p9100_ops = {
.owner = THIS_MODULE,
.fb_check_var = p9100_check_var,
.fb_set_par = p9100_set_par,
.fb_setcolreg = p9100_setcolreg,
.fb_blank = p9100_blank,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_mmap = p9100_mmap,
.fb_cursor = soft_cursor,
};
/* P9100 control registers */
#define P9100_SYSCTL_OFF 0x0UL
#define P9100_VIDEOCTL_OFF 0x100UL
#define P9100_VRAMCTL_OFF 0x180UL
#define P9100_RAMDAC_OFF 0x200UL
#define P9100_VIDEOCOPROC_OFF 0x400UL
/* P9100 command registers */
#define P9100_CMD_OFF 0x0UL
/* P9100 framebuffer memory */
#define P9100_FB_OFF 0x0UL
/* 3 bits: 2=8bpp 3=16bpp 5=32bpp 7=24bpp */
#define SYS_CONFIG_PIXELSIZE_SHIFT 26
#define SCREENPAINT_TIMECTL1_ENABLE_VIDEO 0x20 /* 0 = off, 1 = on */
struct p9100_regs {
/* Registers for the system control */
volatile u32 sys_base;
volatile u32 sys_config;
volatile u32 sys_intr;
volatile u32 sys_int_ena;
volatile u32 sys_alt_rd;
volatile u32 sys_alt_wr;
volatile u32 sys_xxx[58];
/* Registers for the video control */
volatile u32 vid_base;
volatile u32 vid_hcnt;
volatile u32 vid_htotal;
volatile u32 vid_hsync_rise;
volatile u32 vid_hblank_rise;
volatile u32 vid_hblank_fall;
volatile u32 vid_hcnt_preload;
volatile u32 vid_vcnt;
volatile u32 vid_vlen;
volatile u32 vid_vsync_rise;
volatile u32 vid_vblank_rise;
volatile u32 vid_vblank_fall;
volatile u32 vid_vcnt_preload;
volatile u32 vid_screenpaint_addr;
volatile u32 vid_screenpaint_timectl1;
volatile u32 vid_screenpaint_qsfcnt;
volatile u32 vid_screenpaint_timectl2;
volatile u32 vid_xxx[15];
/* Registers for the video control */
volatile u32 vram_base;
volatile u32 vram_memcfg;
volatile u32 vram_refresh_pd;
volatile u32 vram_refresh_cnt;
volatile u32 vram_raslo_max;
volatile u32 vram_raslo_cur;
volatile u32 pwrup_cfg;
volatile u32 vram_xxx[25];
/* Registers for IBM RGB528 Palette */
volatile u32 ramdac_cmap_wridx;
volatile u32 ramdac_palette_data;
volatile u32 ramdac_pixel_mask;
volatile u32 ramdac_palette_rdaddr;
volatile u32 ramdac_idx_lo;
volatile u32 ramdac_idx_hi;
volatile u32 ramdac_idx_data;
volatile u32 ramdac_idx_ctl;
volatile u32 ramdac_xxx[1784];
};
struct p9100_cmd_parameng {
volatile u32 parameng_status;
volatile u32 parameng_bltcmd;
volatile u32 parameng_quadcmd;
};
struct p9100_par {
spinlock_t lock;
struct p9100_regs *regs;
u32 flags;
#define P9100_FLAG_BLANKED 0x00000001
unsigned long physbase;
unsigned long fbsize;
struct sbus_dev *sdev;
struct list_head list;
};
/**
* p9100_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 p9100_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
if (var->bits_per_pixel != 8)
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;
return 0;
}
/**
* p9100_set_par - Optional function. Alters the hardware state.
* @info: frame buffer structure that represents a single frame buffer
*/
static int
p9100_set_par(struct fb_info *info)
{
return 0;
}
/**
* p9100_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 p9100_setcolreg(unsigned regno,
unsigned red, unsigned green, unsigned blue,
unsigned transp, struct fb_info *info)
{
struct p9100_par *par = (struct p9100_par *) info->par;
struct p9100_regs *regs = par->regs;
unsigned long flags;
if (regno >= 256)
return 1;
red >>= 8;
green >>= 8;
blue >>= 8;
spin_lock_irqsave(&par->lock, flags);
sbus_writel((regno << 16), &regs->ramdac_cmap_wridx);
sbus_writel((red << 16), &regs->ramdac_palette_data);
sbus_writel((green << 16), &regs->ramdac_palette_data);
sbus_writel((blue << 16), &regs->ramdac_palette_data);
spin_unlock_irqrestore(&par->lock, flags);
return 0;
}
/**
* p9100_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
p9100_blank(int blank, struct fb_info *info)
{
struct p9100_par *par = (struct p9100_par *) info->par;
struct p9100_regs *regs = par->regs;
unsigned long flags;
u32 val;
spin_lock_irqsave(&par->lock, flags);
switch (blank) {
case 0: /* Unblanking */
val = sbus_readl(&regs->vid_screenpaint_timectl1);
val |= SCREENPAINT_TIMECTL1_ENABLE_VIDEO;
sbus_writel(val, &regs->vid_screenpaint_timectl1);
par->flags &= ~P9100_FLAG_BLANKED;
break;
case 1: /* Normal blanking */
case 2: /* VESA blank (vsync off) */
case 3: /* VESA blank (hsync off) */
case 4: /* Poweroff */
val = sbus_readl(&regs->vid_screenpaint_timectl1);
val &= ~SCREENPAINT_TIMECTL1_ENABLE_VIDEO;
sbus_writel(val, &regs->vid_screenpaint_timectl1);
par->flags |= P9100_FLAG_BLANKED;
break;
}
spin_unlock_irqrestore(&par->lock, flags);
return 0;
}
static struct sbus_mmap_map p9100_mmap_map[] = {
{ CG3_MMAP_OFFSET, 0, SBUS_MMAP_FBSIZE(1) },
{ 0, 0, 0 }
};
static int p9100_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
{
struct p9100_par *par = (struct p9100_par *)info->par;
return sbusfb_mmap_helper(p9100_mmap_map,
par->physbase, par->fbsize,
par->sdev->reg_addrs[0].which_io,
vma);
}
/*
* Initialisation
*/
static void
p9100_init_fix(struct fb_info *info, int linebytes)
{
struct p9100_par *par = (struct p9100_par *)info->par;
strncpy(info->fix.id, par->sdev->prom_name, sizeof(info->fix.id) - 1);
info->fix.id[sizeof(info->fix.id)-1] = 0;
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
info->fix.line_length = linebytes;
info->fix.accel = FB_ACCEL_SUN_CGTHREE;
}
struct all_info {
struct fb_info info;
struct p9100_par par;
struct list_head list;
};
static LIST_HEAD(p9100_list);
static void p9100_init_one(struct sbus_dev *sdev)
{
struct all_info *all;
int linebytes;
all = kmalloc(sizeof(*all), GFP_KERNEL);
if (!all) {
printk(KERN_ERR "p9100: Cannot allocate memory.\n");
return;
}
memset(all, 0, sizeof(*all));
INIT_LIST_HEAD(&all->list);
spin_lock_init(&all->par.lock);
all->par.sdev = sdev;
/* This is the framebuffer and the only resource apps can mmap. */
all->par.physbase = sdev->reg_addrs[2].phys_addr;
sbusfb_fill_var(&all->info.var, sdev->prom_node, 8);
linebytes = prom_getintdefault(sdev->prom_node, "linebytes",
all->info.var.xres);
all->par.fbsize = PAGE_ALIGN(linebytes * all->info.var.yres);
all->par.regs = (struct p9100_regs *)
sbus_ioremap(&sdev->resource[0], 0,
sizeof(struct p9100_regs), "p9100 regs");
all->info.node = NODEV;
all->info.flags = FBINFO_FLAG_DEFAULT;
all->info.fbops = &p9100_ops;
#ifdef CONFIG_SPARC32
all->info.screen_base = (char *)
prom_getintdefault(sdev->prom_node, "address", 0);
#endif
if (!all->info.screen_base)
all->info.screen_base = (char *)
sbus_ioremap(&sdev->resource[2], 0,
all->par.fbsize, "p9100 ram");
all->info.currcon = -1;
all->info.par = &all->par;
p9100_blank(0, &all->info);
if (fb_alloc_cmap(&all->info.cmap, 256, 0)) {
printk(KERN_ERR "p9100: Could not allocate color map.\n");
kfree(all);
return;
}
p9100_set_par(&all->info);
p9100_init_fix(&all->info, linebytes);
if (register_framebuffer(&all->info) < 0) {
printk(KERN_ERR "p9100: Could not register framebuffer.\n");
fb_dealloc_cmap(&all->info.cmap);
kfree(all);
return;
}
list_add(&all->list, &p9100_list);
printk("p9100: %s at %lx:%lx\n",
sdev->prom_name,
(long) sdev->reg_addrs[0].which_io,
(long) sdev->reg_addrs[0].phys_addr);
}
int __init p9100_init(void)
{
struct sbus_bus *sbus;
struct sbus_dev *sdev;
for_all_sbusdev(sdev, sbus) {
if (!strcmp(sdev->prom_name, "p9100"))
p9100_init_one(sdev);
}
return 0;
}
void __exit p9100_exit(void)
{
struct list_head *pos, *tmp;
list_for_each_safe(pos, tmp, &p9100_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
p9100_setup(char *arg)
{
/* No cmdline options yet... */
return 0;
}
#ifdef MODULE
module_init(p9100_init);
module_exit(p9100_exit);
#endif
MODULE_DESCRIPTION("framebuffer driver for P9100 chipsets");
MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
MODULE_LICENSE("GPL");
/*
* Register information for the Weitek P9100 as found
* on the Tadpole Sparcbook 3 laptops.
*
* From the technical specification document provided by Tadpole.
*
* Derrick J Brashear (shadow@dementia.org)
*/
#ifndef _P9100_H_
#define _P9100_H_
/* P9100 control registers */
#define P9100_SYSCTL_OFF 0x0UL
#define P9100_VIDEOCTL_OFF 0x100UL
#define P9100_VRAMCTL_OFF 0x180UL
#define P9100_RAMDAC_OFF 0x200UL
#define P9100_VIDEOCOPROC_OFF 0x400UL
/* P9100 command registers */
#define P9100_CMD_OFF 0x0UL
/* P9100 framebuffer memory */
#define P9100_FB_OFF 0x0UL
/* 3 bits: 2=8bpp 3=16bpp 5=32bpp 7=24bpp */
#define SYS_CONFIG_PIXELSIZE_SHIFT 26
#define SCREENPAINT_TIMECTL1_ENABLE_VIDEO 0x20 /* 0 = off, 1 = on */
struct p9100_ctrl {
/* Registers for the system control */
__volatile__ __u32 sys_base;
__volatile__ __u32 sys_config;
__volatile__ __u32 sys_intr;
__volatile__ __u32 sys_int_ena;
__volatile__ __u32 sys_alt_rd;
__volatile__ __u32 sys_alt_wr;
__volatile__ __u32 sys_xxx[58];
/* Registers for the video control */
__volatile__ __u32 vid_base;
__volatile__ __u32 vid_hcnt;
__volatile__ __u32 vid_htotal;
__volatile__ __u32 vid_hsync_rise;
__volatile__ __u32 vid_hblank_rise;
__volatile__ __u32 vid_hblank_fall;
__volatile__ __u32 vid_hcnt_preload;
__volatile__ __u32 vid_vcnt;
__volatile__ __u32 vid_vlen;
__volatile__ __u32 vid_vsync_rise;
__volatile__ __u32 vid_vblank_rise;
__volatile__ __u32 vid_vblank_fall;
__volatile__ __u32 vid_vcnt_preload;
__volatile__ __u32 vid_screenpaint_addr;
__volatile__ __u32 vid_screenpaint_timectl1;
__volatile__ __u32 vid_screenpaint_qsfcnt;
__volatile__ __u32 vid_screenpaint_timectl2;
__volatile__ __u32 vid_xxx[15];
/* Registers for the video control */
__volatile__ __u32 vram_base;
__volatile__ __u32 vram_memcfg;
__volatile__ __u32 vram_refresh_pd;
__volatile__ __u32 vram_refresh_cnt;
__volatile__ __u32 vram_raslo_max;
__volatile__ __u32 vram_raslo_cur;
__volatile__ __u32 pwrup_cfg;
__volatile__ __u32 vram_xxx[25];
/* Registers for IBM RGB528 Palette */
__volatile__ __u32 ramdac_cmap_wridx;
__volatile__ __u32 ramdac_palette_data;
__volatile__ __u32 ramdac_pixel_mask;
__volatile__ __u32 ramdac_palette_rdaddr;
__volatile__ __u32 ramdac_idx_lo;
__volatile__ __u32 ramdac_idx_hi;
__volatile__ __u32 ramdac_idx_data;
__volatile__ __u32 ramdac_idx_ctl;
__volatile__ __u32 ramdac_xxx[1784];
};
struct p9100_cmd_parameng {
__volatile__ __u32 parameng_status;
__volatile__ __u32 parameng_bltcmd;
__volatile__ __u32 parameng_quadcmd;
};
#endif /* _P9100_H_ */
/* $id: p9100fb.c,v 1.4 1999/08/18 10:55:01 shadow Exp $
* p9100fb.c: P9100 frame buffer driver
*
* Copyright 1999 Derrick J Brashear (shadow@dementia.org)
*/
#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 <asm/io.h>
#include <video/fbcon-cfb8.h>
#include "p9100.h"
static struct sbus_mmap_map p9100_mmap_map[] = {
#if 0 /* For now, play we're a dumb color fb */
{ P9100_CTL_OFF, 0x38000000, 0x2000 },
{ P9100_CMD_OFF, 0x38002000, 0x2000 },
{ P9100_FB_OFF, 0x38800000, 0x200000 },
{ CG3_MMAP_OFFSET, 0x38800000, SBUS_MMAP_FBSIZE(1) },
#else
{ CG3_MMAP_OFFSET, 0x0, SBUS_MMAP_FBSIZE(1) },
#endif
{ 0, 0, 0 }
};
#define _READCTL(member, out) \
{ \
struct p9100_ctrl *actual; \
actual = (struct p9100_ctrl *)fb->s.p9100.ctrl; \
out = sbus_readl(&actual-> ## member ); \
}
#define READCTL(member, out) \
{ \
struct p9100_ctrl *enab, *actual; \
actual = (struct p9100_ctrl *)fb->s.p9100.ctrl; \
enab = (struct p9100_ctrl *)fb->s.p9100.fbmem; \
out = sbus_readl(&enab-> ## member ); \
out = sbus_readl(&actual-> ## member ); \
}
#define WRITECTL(member, val) \
{ \
u32 __writetmp; \
struct p9100_ctrl *enab, *actual; \
actual = (struct p9100_ctrl *)fb->s.p9100.ctrl; \
enab = (struct p9100_ctrl *)fb->s.p9100.fbmem; \
__writetmp = sbus_readl(&enab-> ## member ); \
sbus_writel(val, &actual-> ## member ); \
}
static void p9100_loadcmap (struct fb_info_sbusfb *fb, struct display *p, int index, int count)
{
unsigned long flags;
u32 tmp;
int i;
spin_lock_irqsave(&fb->lock, flags);
_READCTL(pwrup_cfg, tmp);
WRITECTL(ramdac_cmap_wridx, (index << 16));
for (i = index; count--; i++){
_READCTL(pwrup_cfg, tmp);
WRITECTL(ramdac_palette_data, (fb->color_map CM(i,0) << 16));
_READCTL(pwrup_cfg, tmp);
WRITECTL(ramdac_palette_data, (fb->color_map CM(i,1) << 16));
_READCTL(pwrup_cfg, tmp);
WRITECTL(ramdac_palette_data, (fb->color_map CM(i,2) << 16));
}
spin_unlock_irqrestore(&fb->lock, flags);
}
static int p9100_blank (struct fb_info_sbusfb *fb)
{
unsigned long flags;
u32 val;
spin_lock_irqsave(&fb->lock, flags);
READCTL(vid_screenpaint_timectl1, val);
val &= ~ SCREENPAINT_TIMECTL1_ENABLE_VIDEO;
WRITECTL(vid_screenpaint_timectl1, val);
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
}
static int p9100_unblank (struct fb_info_sbusfb *fb)
{
unsigned long flags;
u32 val;
spin_lock_irqsave(&fb->lock, flags);
READCTL(vid_screenpaint_timectl1, val);
val |= SCREENPAINT_TIMECTL1_ENABLE_VIDEO;
WRITECTL(vid_screenpaint_timectl1, val);
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
}
static void p9100_margins (struct fb_info_sbusfb *fb, struct display *p, int x_margin, int y_margin)
{
fb->info.screen_base += (y_margin - fb->y_margin) * p->line_length +
(x_margin - fb->x_margin);
}
static char idstring[60] __initdata = { 0 };
char * __init p9100fb_init(struct fb_info_sbusfb *fb)
{
struct fb_fix_screeninfo *fix = &fb->fix;
struct display *disp = &fb->disp;
struct fbtype *type = &fb->type;
struct sbus_dev *sdev = fb->sbdp;
unsigned long phys = sdev->reg_addrs[2].phys_addr;
int tmp;
#ifndef FBCON_HAS_CFB8
return NULL;
#endif
/* Control regs: fb->sbdp->reg_addrs[0].phys_addr
* Command regs: fb->sbdp->reg_addrs[1].phys_addr
* Frame buffer: fb->sbdp->reg_addrs[2].phys_addr
*/
if (!fb->s.p9100.ctrl) {
fb->s.p9100.ctrl = (struct p9100_ctrl *)
sbus_ioremap(&sdev->resource[0], 0,
sdev->reg_addrs[0].reg_size, "p9100 ctrl");
}
strcpy(fb->info.modename, "p9100");
strcpy(fix->id, "p9100");
fix->accel = FB_ACCEL_SUN_CGTHREE;
fix->line_length = fb->var.xres_virtual;
disp->scrollmode = SCROLL_YREDRAW;
if (!fb->info.screen_base)
fb->info.screen_base = (char *)
sbus_ioremap(&sdev->resource[2], 0,
type->fb_size, "p9100 ram");
fb->s.p9100.fbmem = (volatile u32 *)fb->info.screen_base;
fb->info.screen_base += fix->line_length * fb->y_margin + fb->x_margin;
READCTL(sys_config, tmp);
switch ((tmp >> SYS_CONFIG_PIXELSIZE_SHIFT) & 7) {
case 7:
type->fb_depth = 24;
break;
case 5:
type->fb_depth = 32;
break;
case 3:
type->fb_depth = 16;
break;
case 2:
type->fb_depth = 8;
break;
default:
printk("p9100: screen depth unknown: 0x%x", tmp);
return NULL;
}
fb->dispsw = fbcon_cfb8;
fb->margins = p9100_margins;
fb->loadcmap = p9100_loadcmap;
fb->blank = p9100_blank;
fb->unblank = p9100_unblank;
fb->physbase = phys;
fb->mmap_map = p9100_mmap_map;
sprintf(idstring, "%s at 0x%x", "p9100",
(unsigned int)fb->info.screen_base);
return idstring;
}
/*
* 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 */
/* tcx.c: TCX frame buffer driver
*
* Copyright (C) 2003 David S. Miller (davem@redhat.com)
* Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
* Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
*
* Driver layout based loosely on tgafb.c, see that file for credits.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fb.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <asm/sbus.h>
#include <asm/oplib.h>
#include <asm/fbio.h>
#include "sbuslib.h"
/*
* Local functions.
*/
static int tcx_check_var(struct fb_var_screeninfo *, struct fb_info *);
static int tcx_set_par(struct fb_info *);
static int tcx_setcolreg(unsigned, unsigned, unsigned, unsigned,
unsigned, struct fb_info *);
static int tcx_blank(int, struct fb_info *);
static int tcx_mmap(struct fb_info *, struct file *, struct vm_area_struct *);
/*
* Frame buffer operations
*/
static struct fb_ops tcx_ops = {
.owner = THIS_MODULE,
.fb_check_var = tcx_check_var,
.fb_set_par = tcx_set_par,
.fb_setcolreg = tcx_setcolreg,
.fb_blank = tcx_blank,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_mmap = tcx_mmap,
.fb_cursor = soft_cursor,
};
/* THC definitions */
#define TCX_THC_MISC_REV_SHIFT 16
#define TCX_THC_MISC_REV_MASK 15
#define TCX_THC_MISC_VSYNC_DIS (1 << 25)
#define TCX_THC_MISC_HSYNC_DIS (1 << 24)
#define TCX_THC_MISC_RESET (1 << 12)
#define TCX_THC_MISC_VIDEO (1 << 10)
#define TCX_THC_MISC_SYNC (1 << 9)
#define TCX_THC_MISC_VSYNC (1 << 8)
#define TCX_THC_MISC_SYNC_ENAB (1 << 7)
#define TCX_THC_MISC_CURS_RES (1 << 6)
#define TCX_THC_MISC_INT_ENAB (1 << 5)
#define TCX_THC_MISC_INT (1 << 4)
#define TCX_THC_MISC_INIT 0x9f
#define TCX_THC_REV_REV_SHIFT 20
#define TCX_THC_REV_REV_MASK 15
#define TCX_THC_REV_MINREV_SHIFT 28
#define TCX_THC_REV_MINREV_MASK 15
/* The contents are unknown */
struct tcx_tec {
volatile u32 tec_matrix;
volatile u32 tec_clip;
volatile u32 tec_vdc;
};
struct tcx_thc {
volatile u32 thc_rev;
u32 thc_pad0[511];
volatile u32 thc_hs; /* hsync timing */
volatile u32 thc_hsdvs;
volatile u32 thc_hd;
volatile u32 thc_vs; /* vsync timing */
volatile u32 thc_vd;
volatile u32 thc_refresh;
volatile u32 thc_misc;
u32 thc_pad1[56];
volatile u32 thc_cursxy; /* cursor x,y position (16 bits each) */
volatile u32 thc_cursmask[32]; /* cursor mask bits */
volatile u32 thc_cursbits[32]; /* what to show where mask enabled */
};
struct bt_regs {
volatile u32 addr;
volatile u32 color_map;
volatile u32 control;
volatile u32 cursor;
};
#define TCX_MMAP_ENTRIES 14
struct tcx_par {
spinlock_t lock;
struct bt_regs *bt;
struct tcx_thc *thc;
struct tcx_tec *tec;
volatile u32 *cplane;
u32 flags;
#define TCX_FLAG_BLANKED 0x00000001
unsigned long physbase;
unsigned long fbsize;
struct sbus_mmap_map mmap_map[TCX_MMAP_ENTRIES];
int lowdepth;
struct sbus_dev *sdev;
struct list_head list;
};
/* Reset control plane so that WID is 8-bit plane. */
static void __tcx_set_control_plane (struct tcx_par *par)
{
volatile u32 *p, *pend;
if (par->lowdepth)
return;
p = par->cplane;
if (p == NULL)
return;
for (pend = p + par->fbsize; p < pend; p++) {
u32 tmp = sbus_readl(p);
tmp &= 0xffffff;
sbus_writel(tmp, p);
}
}
static void tcx_reset (struct fb_info *info)
{
struct tcx_par *par = (struct tcx_par *) info->par;
unsigned long flags;
spin_lock_irqsave(&par->lock, flags);
__tcx_set_control_plane(par);
spin_unlock_irqrestore(&par->lock, flags);
}
/**
* tcx_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 tcx_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
if (var->bits_per_pixel != 8)
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;
return 0;
}
/**
* tcx_set_par - Optional function. Alters the hardware state.
* @info: frame buffer structure that represents a single frame buffer
*/
static int
tcx_set_par(struct fb_info *info)
{
return 0;
}
/**
* tcx_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 tcx_setcolreg(unsigned regno,
unsigned red, unsigned green, unsigned blue,
unsigned transp, struct fb_info *info)
{
struct tcx_par *par = (struct tcx_par *) info->par;
struct bt_regs *bt = par->bt;
unsigned long flags;
if (regno >= 256)
return 1;
red >>= 8;
green >>= 8;
blue >>= 8;
spin_lock_irqsave(&par->lock, flags);
sbus_writel(regno << 24, &bt->addr);
sbus_writel(red << 24, &bt->color_map);
sbus_writel(green << 24, &bt->color_map);
sbus_writel(blue << 24, &bt->color_map);
spin_unlock_irqrestore(&par->lock, flags);
return 0;
}
/**
* tcx_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
tcx_blank(int blank, struct fb_info *info)
{
struct tcx_par *par = (struct tcx_par *) info->par;
struct tcx_thc *thc = par->thc;
unsigned long flags;
u32 val;
spin_lock_irqsave(&par->lock, flags);
val = sbus_readl(&thc->thc_misc);
switch (blank) {
case 0: /* Unblanking */
val &= ~(TCX_THC_MISC_VSYNC_DIS |
TCX_THC_MISC_HSYNC_DIS);
val |= TCX_THC_MISC_VIDEO;
par->flags &= ~TCX_FLAG_BLANKED;
break;
case 1: /* Normal blanking */
val &= ~TCX_THC_MISC_VIDEO;
par->flags |= TCX_FLAG_BLANKED;
break;
case 2: /* VESA blank (vsync off) */
val |= TCX_THC_MISC_VSYNC_DIS;
break;
case 3: /* VESA blank (hsync off) */
val |= TCX_THC_MISC_HSYNC_DIS;
break;
case 4: /* Poweroff */
break;
};
sbus_writel(val, &thc->thc_misc);
spin_unlock_irqrestore(&par->lock, flags);
return 0;
}
static struct sbus_mmap_map __tcx_mmap_map[TCX_MMAP_ENTRIES] = {
{ TCX_RAM8BIT, 0, SBUS_MMAP_FBSIZE(1) },
{ TCX_RAM24BIT, 0, SBUS_MMAP_FBSIZE(4) },
{ TCX_UNK3, 0, SBUS_MMAP_FBSIZE(8) },
{ TCX_UNK4, 0, SBUS_MMAP_FBSIZE(8) },
{ TCX_CONTROLPLANE, 0, SBUS_MMAP_FBSIZE(4) },
{ TCX_UNK6, 0, SBUS_MMAP_FBSIZE(8) },
{ TCX_UNK7, 0, SBUS_MMAP_FBSIZE(8) },
{ TCX_TEC, 0, PAGE_SIZE },
{ TCX_BTREGS, 0, PAGE_SIZE },
{ TCX_THC, 0, PAGE_SIZE },
{ TCX_DHC, 0, PAGE_SIZE },
{ TCX_ALT, 0, PAGE_SIZE },
{ TCX_UNK2, 0, 0x20000 },
{ 0, 0, 0 }
};
static int tcx_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
{
struct tcx_par *par = (struct tcx_par *)info->par;
return sbusfb_mmap_helper(par->mmap_map,
par->physbase, par->fbsize,
par->sdev->reg_addrs[0].which_io,
vma);
}
/*
* Initialisation
*/
static void
tcx_init_fix(struct fb_info *info, int linebytes)
{
struct tcx_par *par = (struct tcx_par *)info->par;
const char *tcx_name;
if (par->lowdepth)
tcx_name = "TCX8";
else
tcx_name = "TCX24";
strncpy(info->fix.id, tcx_name, sizeof(info->fix.id) - 1);
info->fix.id[sizeof(info->fix.id)-1] = 0;
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
info->fix.line_length = linebytes;
info->fix.accel = FB_ACCEL_SUN_TCX;
}
struct all_info {
struct fb_info info;
struct tcx_par par;
struct list_head list;
};
static LIST_HEAD(tcx_list);
static void tcx_init_one(struct sbus_dev *sdev)
{
struct all_info *all;
int linebytes, i;
all = kmalloc(sizeof(*all), GFP_KERNEL);
if (!all) {
printk(KERN_ERR "tcx: Cannot allocate memory.\n");
return;
}
memset(all, 0, sizeof(*all));
INIT_LIST_HEAD(&all->list);
spin_lock_init(&all->par.lock);
all->par.sdev = sdev;
all->par.lowdepth = prom_getbool(sdev->prom_node, "tcx-8-bit");
sbusfb_fill_var(&all->info.var, sdev->prom_node, 8);
linebytes = prom_getintdefault(sdev->prom_node, "linebytes",
all->info.var.xres);
all->par.fbsize = PAGE_ALIGN(linebytes * all->info.var.yres);
all->par.tec = (struct tcx_tec *)
sbus_ioremap(&sdev->resource[7], 0,
sizeof(struct tcx_tec), "tcx tec");
all->par.thc = (struct tcx_thc *)
sbus_ioremap(&sdev->resource[9], 0,
sizeof(struct tcx_thc), "tcx thc");
all->par.bt = (struct bt_regs *)
sbus_ioremap(&sdev->resource[8], 0,
sizeof(struct bt_regs), "tcx dac");
memcpy(&all->par.mmap_map, &__tcx_mmap_map, sizeof(all->par.mmap_map));
if (!all->par.lowdepth) {
all->par.cplane = (volatile u32 *)
sbus_ioremap(&sdev->resource[4], 0,
all->par.fbsize * sizeof(u32), "tcx cplane");
} else {
all->par.mmap_map[1].size = SBUS_MMAP_EMPTY;
all->par.mmap_map[4].size = SBUS_MMAP_EMPTY;
all->par.mmap_map[5].size = SBUS_MMAP_EMPTY;
all->par.mmap_map[6].size = SBUS_MMAP_EMPTY;
}
all->par.physbase = 0;
for (i = 0; i < TCX_MMAP_ENTRIES; i++) {
int j;
switch (i) {
case 10:
j = 12;
break;
case 11: case 12:
j = i - 1;
break;
default:
j = i;
break;
};
all->par.mmap_map[i].poff = sdev->reg_addrs[j].phys_addr;
}
all->info.node = NODEV;
all->info.flags = FBINFO_FLAG_DEFAULT;
all->info.fbops = &tcx_ops;
#ifdef CONFIG_SPARC32
all->info.screen_base = (char *)
prom_getintdefault(sdev->prom_node, "address", 0);
#endif
if (!all->info.screen_base)
all->info.screen_base = (char *)
sbus_ioremap(&sdev->resource[0], 0,
all->par.fbsize, "tcx ram");
all->info.currcon = -1;
all->info.par = &all->par;
/* Initialize brooktree DAC. */
sbus_writel(0x04 << 24, &all->par.bt->addr); /* color planes */
sbus_writel(0xff << 24, &all->par.bt->control);
sbus_writel(0x05 << 24, &all->par.bt->addr);
sbus_writel(0x00 << 24, &all->par.bt->control);
sbus_writel(0x06 << 24, &all->par.bt->addr); /* overlay plane */
sbus_writel(0x73 << 24, &all->par.bt->control);
sbus_writel(0x07 << 24, &all->par.bt->addr);
sbus_writel(0x00 << 24, &all->par.bt->control);
tcx_reset(&all->info);
tcx_blank(0, &all->info);
if (fb_alloc_cmap(&all->info.cmap, 256, 0)) {
printk(KERN_ERR "tcx: Could not allocate color map.\n");
kfree(all);
return;
}
tcx_set_par(&all->info);
tcx_init_fix(&all->info, linebytes);
if (register_framebuffer(&all->info) < 0) {
printk(KERN_ERR "tcx: Could not register framebuffer.\n");
fb_dealloc_cmap(&all->info.cmap);
kfree(all);
return;
}
list_add(&all->list, &tcx_list);
printk("tcx: %s at %lx:%lx, %s\n",
sdev->prom_name,
(long) sdev->reg_addrs[0].which_io,
(long) sdev->reg_addrs[0].phys_addr,
all->par.lowdepth ? "8-bit only" : "24-bit depth");
}
int __init tcx_init(void)
{
struct sbus_bus *sbus;
struct sbus_dev *sdev;
for_all_sbusdev(sdev, sbus) {
if (!strcmp(sdev->prom_name, "tcx"))
tcx_init_one(sdev);
}
return 0;
}
void __exit tcx_exit(void)
{
struct list_head *pos, *tmp;
list_for_each_safe(pos, tmp, &tcx_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
tcx_setup(char *arg)
{
/* No cmdline options yet... */
return 0;
}
#ifdef MODULE
module_init(tcx_init);
module_exit(tcx_exit);
#endif
MODULE_DESCRIPTION("framebuffer driver for TCX chipsets");
MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
MODULE_LICENSE("GPL");
/* $Id: tcxfb.c,v 1.13 2001/09/19 00:04:33 davem Exp $
* tcxfb.c: TCX 24/8bit frame buffer driver
*
* Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
* Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
*/
#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 <asm/io.h>
#include <asm/sbus.h>
#include <video/fbcon-cfb8.h>
/* THC definitions */
#define TCX_THC_MISC_REV_SHIFT 16
#define TCX_THC_MISC_REV_MASK 15
#define TCX_THC_MISC_VSYNC_DIS (1 << 25)
#define TCX_THC_MISC_HSYNC_DIS (1 << 24)
#define TCX_THC_MISC_RESET (1 << 12)
#define TCX_THC_MISC_VIDEO (1 << 10)
#define TCX_THC_MISC_SYNC (1 << 9)
#define TCX_THC_MISC_VSYNC (1 << 8)
#define TCX_THC_MISC_SYNC_ENAB (1 << 7)
#define TCX_THC_MISC_CURS_RES (1 << 6)
#define TCX_THC_MISC_INT_ENAB (1 << 5)
#define TCX_THC_MISC_INT (1 << 4)
#define TCX_THC_MISC_INIT 0x9f
#define TCX_THC_REV_REV_SHIFT 20
#define TCX_THC_REV_REV_MASK 15
#define TCX_THC_REV_MINREV_SHIFT 28
#define TCX_THC_REV_MINREV_MASK 15
/* The contents are unknown */
struct tcx_tec {
volatile u32 tec_matrix;
volatile u32 tec_clip;
volatile u32 tec_vdc;
};
struct tcx_thc {
volatile u32 thc_rev;
u32 thc_pad0[511];
volatile u32 thc_hs; /* hsync timing */
volatile u32 thc_hsdvs;
volatile u32 thc_hd;
volatile u32 thc_vs; /* vsync timing */
volatile u32 thc_vd;
volatile u32 thc_refresh;
volatile u32 thc_misc;
u32 thc_pad1[56];
volatile u32 thc_cursxy; /* cursor x,y position (16 bits each) */
volatile u32 thc_cursmask[32]; /* cursor mask bits */
volatile u32 thc_cursbits[32]; /* what to show where mask enabled */
};
static struct sbus_mmap_map tcx_mmap_map[] = {
{ TCX_RAM8BIT, 0, SBUS_MMAP_FBSIZE(1) },
{ TCX_RAM24BIT, 0, SBUS_MMAP_FBSIZE(4) },
{ TCX_UNK3, 0, SBUS_MMAP_FBSIZE(8) },
{ TCX_UNK4, 0, SBUS_MMAP_FBSIZE(8) },
{ TCX_CONTROLPLANE, 0, SBUS_MMAP_FBSIZE(4) },
{ TCX_UNK6, 0, SBUS_MMAP_FBSIZE(8) },
{ TCX_UNK7, 0, SBUS_MMAP_FBSIZE(8) },
{ TCX_TEC, 0, PAGE_SIZE },
{ TCX_BTREGS, 0, PAGE_SIZE },
{ TCX_THC, 0, PAGE_SIZE },
{ TCX_DHC, 0, PAGE_SIZE },
{ TCX_ALT, 0, PAGE_SIZE },
{ TCX_UNK2, 0, 0x20000 },
{ 0, 0, 0 }
};
static void __tcx_set_control_plane (struct fb_info_sbusfb *fb)
{
u32 *p, *pend;
p = fb->s.tcx.cplane;
if (p == NULL)
return;
for (pend = p + fb->type.fb_size; p < pend; p++) {
u32 tmp = sbus_readl(p);
tmp &= 0xffffff;
sbus_writel(tmp, p);
}
}
static void tcx_switch_from_graph (struct fb_info_sbusfb *fb)
{
unsigned long flags;
spin_lock_irqsave(&fb->lock, flags);
/* Reset control plane to 8bit mode if necessary */
if (fb->open && fb->mmaped)
__tcx_set_control_plane (fb);
spin_unlock_irqrestore(&fb->lock, flags);
}
static void tcx_loadcmap (struct fb_info_sbusfb *fb, struct display *p, int index, int count)
{
struct bt_regs *bt = fb->s.tcx.bt;
unsigned long flags;
int i;
spin_lock_irqsave(&fb->lock, flags);
sbus_writel(index << 24, &bt->addr);
for (i = index; count--; i++){
sbus_writel(fb->color_map CM(i,0) << 24, &bt->color_map);
sbus_writel(fb->color_map CM(i,1) << 24, &bt->color_map);
sbus_writel(fb->color_map CM(i,2) << 24, &bt->color_map);
}
sbus_writel(0, &bt->addr);
spin_unlock_irqrestore(&fb->lock, flags);
}
static void tcx_restore_palette (struct fb_info_sbusfb *fb)
{
struct bt_regs *bt = fb->s.tcx.bt;
unsigned long flags;
spin_lock_irqsave(&fb->lock, flags);
sbus_writel(0, &bt->addr);
sbus_writel(0xffffffff, &bt->color_map);
sbus_writel(0xffffffff, &bt->color_map);
sbus_writel(0xffffffff, &bt->color_map);
spin_unlock_irqrestore(&fb->lock, flags);
}
static void tcx_setcursormap (struct fb_info_sbusfb *fb, u8 *red, u8 *green, u8 *blue)
{
struct bt_regs *bt = fb->s.tcx.bt;
unsigned long flags;
spin_lock_irqsave(&fb->lock, flags);
/* Note the 2 << 24 is different from cg6's 1 << 24 */
sbus_writel(2 << 24, &bt->addr);
sbus_writel(red[0] << 24, &bt->cursor);
sbus_writel(green[0] << 24, &bt->cursor);
sbus_writel(blue[0] << 24, &bt->cursor);
sbus_writel(3 << 24, &bt->addr);
sbus_writel(red[1] << 24, &bt->cursor);
sbus_writel(green[1] << 24, &bt->cursor);
sbus_writel(blue[1] << 24, &bt->cursor);
sbus_writel(0, &bt->addr);
spin_unlock_irqrestore(&fb->lock, flags);
}
/* Set cursor shape */
static void tcx_setcurshape (struct fb_info_sbusfb *fb)
{
struct tcx_thc *thc = fb->s.tcx.thc;
unsigned long flags;
int i;
spin_lock_irqsave(&fb->lock, flags);
for (i = 0; i < 32; i++){
sbus_writel(fb->cursor.bits[0][i], &thc->thc_cursmask[i]);
sbus_writel(fb->cursor.bits[1][i], &thc->thc_cursbits[i]);
}
spin_unlock_irqrestore(&fb->lock, flags);
}
/* Load cursor information */
static void tcx_setcursor (struct fb_info_sbusfb *fb)
{
struct cg_cursor *c = &fb->cursor;
unsigned long flags;
unsigned int v;
spin_lock_irqsave(&fb->lock, flags);
if (c->enable)
v = ((c->cpos.fbx - c->chot.fbx) << 16)
|((c->cpos.fby - c->chot.fby) & 0xffff);
else
/* Magic constant to turn off the cursor */
v = ((65536-32) << 16) | (65536-32);
sbus_writel(v, &fb->s.tcx.thc->thc_cursxy);
spin_unlock_irqrestore(&fb->lock, flags);
}
static int tcx_blank (struct fb_info_sbusfb *fb)
{
unsigned long flags;
u32 tmp;
spin_lock_irqsave(&fb->lock, flags);
tmp = sbus_readl(&fb->s.tcx.thc->thc_misc);
tmp &= ~TCX_THC_MISC_VIDEO;
/* This should put us in power-save */
tmp |= TCX_THC_MISC_VSYNC_DIS;
tmp |= TCX_THC_MISC_HSYNC_DIS;
sbus_writel(tmp, &fb->s.tcx.thc->thc_misc);
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
}
static int tcx_unblank (struct fb_info_sbusfb *fb)
{
unsigned long flags;
u32 tmp;
spin_lock_irqsave(&fb->lock, flags);
tmp = sbus_readl(&fb->s.tcx.thc->thc_misc);
tmp &= ~TCX_THC_MISC_VSYNC_DIS;
tmp &= ~TCX_THC_MISC_HSYNC_DIS;
tmp |= TCX_THC_MISC_VIDEO;
sbus_writel(tmp, &fb->s.tcx.thc->thc_misc);
spin_unlock_irqrestore(&fb->lock, flags);
return 0;
}
static void tcx_reset (struct fb_info_sbusfb *fb)
{
unsigned long flags;
u32 tmp;
spin_lock_irqsave(&fb->lock, flags);
if (fb->open && fb->mmaped)
__tcx_set_control_plane(fb);
/* Turn off stuff in the Transform Engine. */
sbus_writel(0, &fb->s.tcx.tec->tec_matrix);
sbus_writel(0, &fb->s.tcx.tec->tec_clip);
sbus_writel(0, &fb->s.tcx.tec->tec_vdc);
/* Enable cursor in Brooktree DAC. */
sbus_writel(0x06 << 24, &fb->s.tcx.bt->addr);
tmp = sbus_readl(&fb->s.tcx.bt->control);
tmp |= 0x03 << 24;
sbus_writel(tmp, &fb->s.tcx.bt->control);
spin_unlock_irqrestore(&fb->lock, flags);
}
static void tcx_margins (struct fb_info_sbusfb *fb, struct display *p, int x_margin, int y_margin)
{
fb->info.screen_base += (y_margin - fb->y_margin) * p->fb_info->fix.line_length + (x_margin - fb->x_margin);
}
static char idstring[60] __initdata = { 0 };
char __init *tcxfb_init(struct fb_info_sbusfb *fb)
{
struct fb_fix_screeninfo *fix = &fb->info.fix;
struct display *disp = &fb->disp;
struct fbtype *type = &fb->type;
struct sbus_dev *sdev = fb->sbdp;
unsigned long phys = sdev->reg_addrs[0].phys_addr;
int lowdepth, i, j;
#ifndef FBCON_HAS_CFB8
return NULL;
#endif
lowdepth = prom_getbool (fb->prom_node, "tcx-8-bit");
if (lowdepth) {
strcpy(fb->info.modename, "TCX8");
strcpy(fix->id, "TCX8");
} else {
strcpy(fb->info.modename, "TCX24");
strcpy(fix->id, "TCX24");
}
fix->line_length = fb->info.var.xres_virtual;
fix->accel = FB_ACCEL_SUN_TCX;
disp->scrollmode = SCROLL_YREDRAW;
if (!fb->info.screen_base) {
fb->info.screen_base = (char *)
sbus_ioremap(&sdev->resource[0], 0,
type->fb_size, "tcx ram");
}
fb->info.screen_base += fix->line_length * fb->y_margin + fb->x_margin;
fb->s.tcx.tec = (struct tcx_tec *)
sbus_ioremap(&sdev->resource[7], 0,
sizeof(struct tcx_tec), "tcx tec");
fb->s.tcx.thc = (struct tcx_thc *)
sbus_ioremap(&sdev->resource[9], 0,
sizeof(struct tcx_thc), "tcx thc");
fb->s.tcx.bt = (struct bt_regs *)
sbus_ioremap(&sdev->resource[8], 0,
sizeof(struct bt_regs), "tcx dac");
if (!lowdepth) {
fb->s.tcx.cplane = (u32 *)
sbus_ioremap(&sdev->resource[4], 0,
type->fb_size * sizeof(u32), "tcx cplane");
type->fb_depth = 24;
fb->switch_from_graph = tcx_switch_from_graph;
} else {
/* As there can be one tcx in a machine only, we can write directly into
tcx_mmap_map */
tcx_mmap_map[1].size = SBUS_MMAP_EMPTY;
tcx_mmap_map[4].size = SBUS_MMAP_EMPTY;
tcx_mmap_map[5].size = SBUS_MMAP_EMPTY;
tcx_mmap_map[6].size = SBUS_MMAP_EMPTY;
}
fb->dispsw = fbcon_cfb8;
fb->margins = tcx_margins;
fb->loadcmap = tcx_loadcmap;
if (prom_getbool (fb->prom_node, "hw-cursor")) {
fb->setcursor = tcx_setcursor;
fb->setcursormap = tcx_setcursormap;
fb->setcurshape = tcx_setcurshape;
}
fb->restore_palette = tcx_restore_palette;
fb->blank = tcx_blank;
fb->unblank = tcx_unblank;
fb->reset = tcx_reset;
fb->physbase = 0;
for (i = 0; i < 13; i++) {
/* tcx_mmap_map has to be sorted by voff, while
order of phys registers from PROM differs a little
bit. Here is the correction */
switch (i) {
case 10: j = 12; break;
case 11:
case 12: j = i - 1; break;
default: j = i; break;
}
tcx_mmap_map[i].poff = fb->sbdp->reg_addrs[j].phys_addr;
}
fb->mmap_map = tcx_mmap_map;
/* Initialize Brooktree DAC */
sbus_writel(0x04 << 24, &fb->s.tcx.bt->addr); /* color planes */
sbus_writel(0xff << 24, &fb->s.tcx.bt->control);
sbus_writel(0x05 << 24, &fb->s.tcx.bt->addr);
sbus_writel(0x00 << 24, &fb->s.tcx.bt->control);
sbus_writel(0x06 << 24, &fb->s.tcx.bt->addr); /* overlay plane */
sbus_writel(0x73 << 24, &fb->s.tcx.bt->control);
sbus_writel(0x07 << 24, &fb->s.tcx.bt->addr);
sbus_writel(0x00 << 24, &fb->s.tcx.bt->control);
sprintf(idstring, "tcx at %x.%08lx Rev %d.%d %s",
fb->iospace, phys,
((sbus_readl(&fb->s.tcx.thc->thc_rev) >> TCX_THC_REV_REV_SHIFT) &
TCX_THC_REV_REV_MASK),
((sbus_readl(&fb->s.tcx.thc->thc_rev) >> TCX_THC_REV_MINREV_SHIFT) &
TCX_THC_REV_MINREV_MASK),
lowdepth ? "8-bit only" : "24-bit depth");
tcx_reset(fb);
return idstring;
}
MODULE_LICENSE("GPL");
......@@ -10,6 +10,7 @@
#include <linux/threads.h>
#include <linux/brlock.h>
#include <linux/spinlock.h>
#include <linux/cache.h>
/* entry.S is sensitive to the offsets of these fields */
/* rtrap.S is sensitive to the size of this structure */
......
#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