diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
commit | 19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch) | |
tree | 40b1cb534496a7f1ca0f5c314a523c69f1fee464 /drivers/sbus/char | |
parent | 7206675c40394c78a90e74812bbdbf8cf3cca1be (diff) |
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'drivers/sbus/char')
28 files changed, 9355 insertions, 1960 deletions
diff --git a/drivers/sbus/char/Config.in b/drivers/sbus/char/Config.in new file mode 100644 index 000000000..3f25aa828 --- /dev/null +++ b/drivers/sbus/char/Config.in @@ -0,0 +1,48 @@ +comment 'SBUS Frame Buffer support' +bool 'Sun FB drivers appear in PROCFS' SUN_FBS_IN_PROCFS +bool 'Load All Supported Drivers' CONFIG_SUN_FB_DISPLAY + +if [ "$CONFIG_SUN_FB_DISPLAY" = "n" ]; then + bool 'cgsix support' SUN_FB_CGSIX + bool 'tcx support' SUN_FB_TCX + bool 'cgthree support' SUN_FB_CGTHREE + bool 'cgfourteen support' SUN_FB_CGFOURTEEN + bool 'bwtwo support' SUN_FB_BWTWO + bool 'leo/zx support' SUN_FB_LEO + bool 'weitek P9X00 support' TADPOLE_FB_WEITEK + bool 'creator support' SUN_FB_CREATOR + if [ "$TADPOLE_FB_WEITEK" = "n" ]; then + fbs=$SUN_FB_CGSIX + fbs=$fbs$SUN_FB_TCX + fbs=$fbs$SUN_FB_CGTHREE + fbs=$fbs$SUN_FB_BWTWO + fbs=$fbs$SUN_FB_CGFOURTEEN + fbs=$fbs$SUN_FB_LEO + fbs=$fbs$TADPOLE_FB_WEITEK + if [ "$fbs" = "nnnnnnnn" ]; then + echo "Warning: You have excluded ALL FB Support" + echo "Notice: Enabling Generic AutoResolution" + define_bool SUN_FB_GENERIC y + fi + else + define_bool SUN_FB_GENERIC y + fi +else + define_bool SUN_FB_CGSIX y + define_bool SUN_FB_TCX y + define_bool SUN_FB_CGTHREE y + define_bool SUN_FB_CGFOURTEEN y + define_bool SUN_FB_BWTWO y + define_bool SUN_FB_LEO y + define_bool TADPOLE_FB_WEITEK y + define_bool SUN_FB_CREATOR y +fi + +comment 'Misc Linux/SPARC drivers' +tristate '/dev/openprom device support' CONFIG_SUN_OPENPROMIO +tristate 'Mostek real time clock support' CONFIG_SUN_MOSTEK_RTC + +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Bidirectional parallel port support (EXPERIMENTAL)' CONFIG_SUN_BPP + tristate 'Videopix Frame Grabber (EXPERIMENTAL)' CONFIG_SUN_VIDEOPIX +fi diff --git a/drivers/sbus/char/Makefile b/drivers/sbus/char/Makefile index 5f3bd978b..4ce803b34 100644 --- a/drivers/sbus/char/Makefile +++ b/drivers/sbus/char/Makefile @@ -7,7 +7,84 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... +# Dave Redman Frame Buffer tuning support. +# OK this is kind of ugly but it does allow drivers to be added fairly +# easily. and you can even choose what sort of support you want. +ifdef SUN_FB_CGSIX + FB_OBJS += cgsix.o +endif +ifdef SUN_FB_CGTHREE + FB_OBJS += cgthree.o +endif +ifdef SUN_FB_TCX + FB_OBJS += tcx.o +endif +ifdef SUN_FB_BWTWO + FB_OBJS += bwtwo.o +endif +ifdef SUN_FB_LEO + FB_OBJS += leo.o +endif +ifdef SUN_FB_CGFOURTEEN + FB_OBJS += cgfourteen.o +endif +ifdef TADPOLE_FB_WEITEK + FB_OBJS += weitek.o +endif +ifdef SUN_FB_CREATOR + FB_OBJS += creator.o +endif +#ifdef SUN_FB_FAST_ONE +# FB_OBJS += sun_8bit_fast1.o +#endif +#ifdef SUN_FB_FAST_TWO +# FB_OBJS += sun_8bit_fast2.o +#endif +#ifdef SUN_FB_FAST_MONO +# FB_OBJS += sun_mono_fast1.o +#endif +#ifdef SUN_FB_GENERIC +# FB_OBJS += sun_8bit_generic.o +#endif + O_TARGET := sunchar.o -O_OBJS := suncons.o sunkbd.o sunkeymap.o sunmouse.o sunserial.o +O_OBJ := ${FB_OBJS} suncons.o sunfb.o +O_OBJS := ${O_OBJ} sunkbd.o sunkeymap.o sunmouse.o sunserial.o +M_OBJS := + +ifeq ($(CONFIG_SUN_OPENPROMIO),y) +O_OBJS += openprom.o +else + ifeq ($(CONFIG_SUN_OPENPROMIO),m) + M_OBJS += openprom.o + endif +endif + +ifeq ($(CONFIG_SUN_MOSTEK_RTC),y) +O_OBJS += rtc.o +else + ifeq ($(CONFIG_SUN_MOSTEK_RTC),m) + M_OBJS += rtc.o + endif +endif + +ifeq ($(CONFIG_SUN_BPP),y) +O_OBJS += bpp.o +else + ifeq ($(CONFIG_SUN_BPP),m) + M_OBJS += bpp.o + endif +endif + +ifeq ($(CONFIG_SUN_VIDEOPIX),y) +O_OBJS += vfc.o +else + ifeq ($(CONFIG_SUN_VIDEOPIX),m) + M_OBJS += vfc.o + endif +endif include $(TOPDIR)/Rules.make + +vfc.o: vfc_dev.o vfc_i2c.o + $(LD) -r -o vfc.o vfc_dev.o vfc_i2c.o diff --git a/drivers/sbus/char/bpp.c b/drivers/sbus/char/bpp.c new file mode 100644 index 000000000..2fd138748 --- /dev/null +++ b/drivers/sbus/char/bpp.c @@ -0,0 +1,1091 @@ +/* + * drivers/sbus/char/bpp.c + * + * Copyright (c) 1995 Picture Elements + * Stephen Williams (steve@icarus.com) + * Gus Baldauf (gbaldauf@ix.netcom.com) + * + * Linux/SPARC port by Peter Zaitcev. + * Integration into SPARC tree by Tom Dyas. + */ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/ioport.h> +#include <linux/major.h> + +#include <asm/uaccess.h> + +#if defined(__i386__) +# include <asm/io.h> +# include <asm/system.h> +# include <asm/segment.h> +#endif + +#if defined(__sparc__) +# include <linux/init.h> +# include <linux/delay.h> /* udelay() */ + +# include <asm/oplib.h> /* OpenProm Library */ +# include <asm/sbus.h> /* struct linux_sbus *SBus_chain */ +# include <asm/io.h> /* sparc_alloc_io() */ +#endif + +#include <asm/bpp.h> + +#define BPP_PROBE_CODE 0x55 +#define BPP_DELAY 100 + +static const unsigned BPP_MAJOR = LP_MAJOR; +static const char* dev_name = "bpp"; + +/* When switching from compatability to a mode where I can read, try + the following mode first. */ + +/* const unsigned char DEFAULT_ECP = 0x10; */ +static const unsigned char DEFAULT_ECP = 0x30; +static const unsigned char DEFAULT_NIBBLE = 0x00; + +/* + * These are 1284 time constraints, in units of jiffies. + */ + +static const unsigned long TIME_PSetup = 1; +static const unsigned long TIME_PResponse = 6; +static const unsigned long TIME_IDLE_LIMIT = 2000; + +/* + * One instance per supported subdevice... + */ +# define BPP_NO 3 + +enum IEEE_Mode { COMPATIBILITY, NIBBLE, ECP, ECP_RLE, EPP }; + +struct inst { + unsigned present : 1; /* True if the hardware exists */ + unsigned enhanced : 1; /* True if the hardware in "enhanced" */ + unsigned opened : 1; /* True if the device is opened already */ + unsigned run_flag : 1; /* True if waiting for a repeate byte */ + + unsigned char direction; /* 0 --> out, 0x20 --> IN */ + unsigned char pp_state; /* State of host controlled pins. */ + enum IEEE_Mode mode; + + unsigned char run_length; + unsigned char repeat_byte; + + /* These members manage timeouts for programmed delays */ + struct wait_queue *wait_queue; + struct timer_list timer_list; +}; + +static struct inst instances[BPP_NO]; + +#if defined(__i386__) + +const unsigned short base_addrs[BPP_NO] = { 0x278, 0x378, 0x3bc }; + +/* + * These are for data access. + * Control lines accesses are hidden in set_bits() and get_bits(). + * The exeption is the probe procedure, which is system-dependent. + */ +#define bpp_outb_p(data, base) outb_p((data), (base)) +#define bpp_inb(base) inb(base) +#define bpp_inb_p(base) inb_p(base) + +/* + * This method takes the pin values mask and sets the hardware pins to + * the requested value: 1 == high voltage, 0 == low voltage. This + * burries the annoying PC bit inversion and preserves the direction + * flag. + */ +static void set_pins(unsigned short pins, unsigned minor) +{ + unsigned char bits = instances[minor].direction; /* == 0x20 */ + + if (! (pins & BPP_PP_nStrobe)) bits |= 1; + if (! (pins & BPP_PP_nAutoFd)) bits |= 2; + if ( pins & BPP_PP_nInit) bits |= 4; + if (! (pins & BPP_PP_nSelectIn)) bits |= 8; + + instances[minor].pp_state = bits; + + outb_p(bits, base_addrs[minor]+2); +} + +static unsigned short get_pins(unsigned minor) +{ + unsigned short bits = 0; + + unsigned value = instances[minor].pp_state; + if (! (value & 0x01)) bits |= BPP_PP_nStrobe; + if (! (value & 0x02)) bits |= BPP_PP_nAutoFd; + if (value & 0x04) bits |= BPP_PP_nInit; + if (! (value & 0x08)) bits |= BPP_PP_nSelectIn; + + value = inb_p(base_addrs[minor]+1); + if (value & 0x08) bits |= BPP_GP_nFault; + if (value & 0x10) bits |= BPP_GP_Select; + if (value & 0x20) bits |= BPP_GP_PError; + if (value & 0x40) bits |= BPP_GP_nAck; + if (! (value & 0x80)) bits |= BPP_GP_Busy; + + return bits; +} + +#endif /* __i386__ */ + +#if defined(__sparc__) + +/* + * Register block + */ +struct bpp_regs { + /* DMA registers */ + __u32 p_csr; /* DMA Control/Status Register */ + __u32 p_addr; /* Address Register */ + __u32 p_bcnt; /* Byte Count Register */ + __u32 p_tst_csr; /* Test Control/Status (DMA2 only) */ + /* Parallel Port registers */ + __u16 p_hcr; /* Hardware Configuration Register */ + __u16 p_ocr; /* Operation Configuration Register */ + __u8 p_dr; /* Parallel Data Register */ + __u8 p_tcr; /* Transfer Control Register */ + __u8 p_or; /* Output Register */ + __u8 p_ir; /* Input Register */ + __u16 p_icr; /* Interrupt Control Register */ +}; + +/* P_CSR. Bits of type RW1 are cleared with writting '1'. */ +#define P_DEV_ID_MASK 0xf0000000 /* R */ +#define P_DEV_ID_ZEBRA 0x40000000 +#define P_DEV_ID_L64854 0xa0000000 /* == NCR 89C100+89C105. Pity. */ +#define P_NA_LOADED 0x08000000 /* R NA wirtten but was not used */ +#define P_A_LOADED 0x04000000 /* R */ +#define P_DMA_ON 0x02000000 /* R DMA is not disabled */ +#define P_EN_NEXT 0x01000000 /* RW */ +#define P_TCI_DIS 0x00800000 /* RW TCI forbidden from interrupts */ +#define P_DIAG 0x00100000 /* RW Disables draining and resetting + of P-FIFO on loading of P_ADDR*/ +#define P_BURST_SIZE 0x000c0000 /* RW SBus burst size */ +#define P_BURST_8 0x00000000 +#define P_BURST_4 0x00040000 +#define P_BURST_1 0x00080000 /* "No burst" write */ +#define P_TC 0x00004000 /* RW1 Term Count, can be cleared when + P_EN_NEXT=1 */ +#define P_EN_CNT 0x00002000 /* RW */ +#define P_EN_DMA 0x00000200 /* RW */ +#define P_WRITE 0x00000100 /* R DMA dir, 1=to ram, 0=to port */ +#define P_RESET 0x00000080 /* RW */ +#define P_SLAVE_ERR 0x00000040 /* RW1 Access size error */ +#define P_INVALIDATE 0x00000020 /* W Drop P-FIFO */ +#define P_INT_EN 0x00000010 /* RW OK to P_INT_PEND||P_ERR_PEND */ +#define P_DRAINING 0x0000000c /* R P-FIFO is draining to memory */ +#define P_ERR_PEND 0x00000002 /* R */ +#define P_INT_PEND 0x00000001 /* R */ + +/* P_HCR. Time is in increments of SBus clock. */ +#define P_HCR_TEST 0x8000 /* Allows buried counters to be read */ +#define P_HCR_DSW 0x7f00 /* Data strobe width (in ticks) */ +#define P_HCR_DDS 0x007f /* Data setup before strobe (in ticks) */ + +/* P_OCR. */ +#define P_OCR_MEM_CLR 0x8000 +#define P_OCR_DATA_SRC 0x4000 /* ) */ +#define P_OCR_DS_DSEL 0x2000 /* ) Bidirectional */ +#define P_OCR_BUSY_DSEL 0x1000 /* ) selects */ +#define P_OCR_ACK_DSEL 0x0800 /* ) */ +#define P_OCR_EN_DIAG 0x0400 +#define P_OCR_BUSY_OP 0x0200 /* Busy operation */ +#define P_OCR_ACK_OP 0x0100 /* Ack operation */ +#define P_OCR_SRST 0x0080 /* Reset state machines. Not selfcleaning. */ +#define P_OCR_IDLE 0x0008 /* PP data transfer state machine is idle */ +#define P_OCR_V_ILCK 0x0002 /* Versatec faded. Zebra only. */ +#define P_OCR_EN_VER 0x0001 /* Enable Versatec (0 - enable). Zebra only. */ + +/* P_TCR */ +#define P_TCR_DIR 0x08 +#define P_TCR_BUSY 0x04 +#define P_TCR_ACK 0x02 +#define P_TCR_DS 0x01 /* Strobe */ + +/* P_OR */ +#define P_OR_V3 0x20 /* ) */ +#define P_OR_V2 0x10 /* ) on Zebra only */ +#define P_OR_V1 0x08 /* ) */ +#define P_OR_INIT 0x04 +#define P_OR_AFXN 0x02 /* Auto Feed */ +#define P_OR_SLCT_IN 0x01 + +/* P_IR */ +#define P_IR_PE 0x04 +#define P_IR_SLCT 0x02 +#define P_IR_ERR 0x01 + +/* P_ICR */ +#define P_DS_IRQ 0x8000 /* RW1 */ +#define P_ACK_IRQ 0x4000 /* RW1 */ +#define P_BUSY_IRQ 0x2000 /* RW1 */ +#define P_PE_IRQ 0x1000 /* RW1 */ +#define P_SLCT_IRQ 0x0800 /* RW1 */ +#define P_ERR_IRQ 0x0400 /* RW1 */ +#define P_DS_IRQ_EN 0x0200 /* RW Always on rising edge */ +#define P_ACK_IRQ_EN 0x0100 /* RW Always on rising edge */ +#define P_BUSY_IRP 0x0080 /* RW 1= rising edge */ +#define P_BUSY_IRQ_EN 0x0040 /* RW */ +#define P_PE_IRP 0x0020 /* RW 1= rising edge */ +#define P_PE_IRQ_EN 0x0010 /* RW */ +#define P_SLCT_IRP 0x0008 /* RW 1= rising edge */ +#define P_SLCT_IRQ_EN 0x0004 /* RW */ +#define P_ERR_IRP 0x0002 /* RW1 1= rising edge */ +#define P_ERR_IRQ_EN 0x0001 /* RW */ + +volatile struct bpp_regs *base_addrs[BPP_NO]; + +static inline void bpp_outb_p(__u8 data, volatile struct bpp_regs *base){ + base->p_dr = data; +} + +#define bpp_inb_p(base) bpp_inb(base) + +static inline __u8 bpp_inb(volatile struct bpp_regs *base){ + return base->p_dr; +} + + +static void set_pins(unsigned short pins, unsigned minor) +{ + volatile struct bpp_regs *base = base_addrs[minor]; + unsigned char bits_tcr = 0, bits_or = 0; + + if (instances[minor].direction & 0x20) bits_tcr |= P_TCR_DIR; + if ( pins & BPP_PP_nStrobe) bits_tcr |= P_TCR_DS; + + if ( pins & BPP_PP_nAutoFd) bits_or |= P_OR_AFXN; + if (! (pins & BPP_PP_nInit)) bits_or |= P_OR_INIT; + if (! (pins & BPP_PP_nSelectIn)) bits_or |= P_OR_SLCT_IN; + + base->p_or = bits_or; + base->p_tcr = bits_tcr; +} + +/* + * i386 people read output pins from a software image. + * We may get them back from hardware. + * Again, inversion of pins must he buried here. + */ +static unsigned short get_pins(unsigned minor) +{ + volatile struct bpp_regs *base = base_addrs[minor]; + unsigned short bits = 0; + unsigned value_tcr = base->p_tcr; + unsigned value_ir = base->p_ir; + unsigned value_or = base->p_or; + + if (value_tcr & P_TCR_DS) bits |= BPP_PP_nStrobe; + if (value_or & P_OR_AFXN) bits |= BPP_PP_nAutoFd; + if (! (value_or & P_OR_INIT)) bits |= BPP_PP_nInit; + if (! (value_or & P_OR_SLCT_IN)) bits |= BPP_PP_nSelectIn; + + if (value_ir & P_IR_ERR) bits |= BPP_GP_nFault; + if (! (value_ir & P_IR_SLCT)) bits |= BPP_GP_Select; + if (! (value_ir & P_IR_PE)) bits |= BPP_GP_PError; + if (! (value_tcr & P_TCR_ACK)) bits |= BPP_GP_nAck; + if (value_tcr & P_TCR_BUSY) bits |= BPP_GP_Busy; + + return bits; +} + +#if 0 +/* P3 */ +static inline void bpp_snap(const char *msg, unsigned minor) +{ + volatile struct bpp_regs *r = base_addrs[minor]; + printk("bpp.%s: c=%02x o=%02x i=%02x\n", msg, r->p_tcr, r->p_or, r->p_ir); +} +#endif + +#endif /* __sparc__ */ + +/* + * This is TRUE if the module_init successfully loaded the module. + */ +#if 0 +static int loaded_flag = 0; +#endif + +static void bpp_wake_up(unsigned long val) +{ wake_up(&instances[val].wait_queue); } + +static void snooze(unsigned long snooze_time, unsigned minor) +{ + instances[minor].timer_list.expires = jiffies + snooze_time + 1; + instances[minor].timer_list.data = minor; + add_timer(&instances[minor].timer_list); + sleep_on (&instances[minor].wait_queue); +} + +static int wait_for(unsigned short set, unsigned short clr, + unsigned long delay, unsigned minor) +{ + unsigned short pins = get_pins(minor); + + unsigned long extime = 0; + + /* + * Try a real fast scan for the first jiffy, in case the device + * responds real good. The first while loop guesses an expire + * time accounting for possible wraparound of jiffies. + */ + while (extime <= jiffies) extime = jiffies + 1; + while ( (jiffies < extime) + && (((pins & set) != set) || ((pins & clr) != 0)) ) { + pins = get_pins(minor); + } + + delay -= 1; + + /* + * If my delay expired or the pins are still not where I want + * them, then resort to using the timer and greatly reduce my + * sample rate. If the peripheral is going to be slow, this will + * give the CPU up to some more worthy process. + */ + while ( delay && (((pins & set) != set) || ((pins & clr) != 0)) ) { + + snooze(1, minor); + pins = get_pins(minor); + delay -= 1; + } + + if (delay == 0) return -1; + else return pins; +} + +/* + * Return ZERO(0) If the negotiation succeeds, an errno otherwise. An + * errno means something broke, and I do not yet know how to fix it. + */ +static int negotiate(unsigned char mode, unsigned minor) +{ + int rc; + unsigned short pins = get_pins(minor); + if (pins & BPP_PP_nSelectIn) return -EIO; + + + /* Event 0: Write the mode to the data lines */ + bpp_outb_p(mode, base_addrs[minor]); + + snooze(TIME_PSetup, minor); + + /* Event 1: Strobe the mode code into the peripheral */ + set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nInit, minor); + + /* Wait for Event 2: Peripheral responds as a 1284 device. */ + rc = wait_for(BPP_GP_PError|BPP_GP_Select|BPP_GP_nFault, + BPP_GP_nAck, + TIME_PResponse, + minor); + + if (rc == -1) return -ETIMEDOUT; + + /* Event 3: latch extensibility request */ + set_pins(BPP_PP_nSelectIn|BPP_PP_nInit, minor); + + /* ... quick nap while peripheral ponders the byte i'm sending...*/ + snooze(1, minor); + + /* Event 4: restore strobe, to ACK peripheral's response. */ + set_pins(BPP_PP_nSelectIn|BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor); + + /* Wait for Event 6: Peripheral latches response bits */ + rc = wait_for(BPP_GP_nAck, 0, TIME_PSetup+TIME_PResponse, minor); + if (rc == -1) return -EIO; + + /* A 1284 device cannot refuse nibble mode */ + if (mode == DEFAULT_NIBBLE) return 0; + + if (pins & BPP_GP_Select) return 0; + + return -EPROTONOSUPPORT; +} + +static int terminate(unsigned minor) +{ + int rc; + + /* Event 22: Request termination of 1284 mode */ + set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor); + + /* Wait for Events 23 and 24: ACK termination request. */ + rc = wait_for(BPP_GP_Busy|BPP_GP_nFault, + BPP_GP_nAck, + TIME_PSetup+TIME_PResponse, + minor); + + instances[minor].direction = 0; + instances[minor].mode = COMPATIBILITY; + + if (rc == -1) { + return -EIO; + } + + /* Event 25: Handshake by lowering nAutoFd */ + set_pins(BPP_PP_nStrobe|BPP_PP_nInit, minor); + + /* Event 26: Peripheral wiggles lines... */ + + /* Event 27: Peripheral sets nAck HIGH to ack handshake */ + rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor); + if (rc == -1) { + set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor); + return -EIO; + } + + /* Event 28: Finish phase by raising nAutoFd */ + set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor); + + return 0; +} + + +/* + * Allow only one process to open the device at a time. + */ +static int bpp_open(struct inode *inode, struct file *f) +{ + unsigned minor = MINOR(inode->i_rdev); + if (minor >= BPP_NO) return -ENODEV; + if (! instances[minor].present) return -ENODEV; + if (instances[minor].opened) return -EBUSY; + + instances[minor].opened = 1; + + return 0; +} + +/* + * When the process closes the device, this method is called to clean + * up and reset the hardware. Always leave the device in compatibility + * mode as this is a reasonable place to clean up from messes made by + * ioctls, or other mayhem. + */ +static void bpp_release(struct inode *inode, struct file *f) +{ + unsigned minor = MINOR(inode->i_rdev); + instances[minor].opened = 0; + + if (instances[minor].mode != COMPATIBILITY) + terminate(minor); +} + +static long read_nibble(unsigned minor, char *c, unsigned long cnt) +{ + unsigned long remaining = cnt; + long rc; + + while (remaining > 0) { + unsigned char byte = 0; + int pins; + + /* Event 7: request nibble */ + set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe, minor); + + /* Wait for event 9: Peripher strobes first nibble */ + pins = wait_for(0, BPP_GP_nAck, TIME_IDLE_LIMIT, minor); + if (pins == -1) return -ETIMEDOUT; + + /* Event 10: I handshake nibble */ + set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nAutoFd, minor); + if (pins & BPP_GP_nFault) byte |= 0x01; + if (pins & BPP_GP_Select) byte |= 0x02; + if (pins & BPP_GP_PError) byte |= 0x04; + if (pins & BPP_GP_Busy) byte |= 0x08; + + /* Wait for event 11: Peripheral handshakes nibble */ + rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor); + + /* Event 7: request nibble */ + set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe, minor); + + /* Wait for event 9: Peripher strobes first nibble */ + pins = wait_for(0, BPP_GP_nAck, TIME_PResponse, minor); + if (rc == -1) return -ETIMEDOUT; + + /* Event 10: I handshake nibble */ + set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nAutoFd, minor); + if (pins & BPP_GP_nFault) byte |= 0x10; + if (pins & BPP_GP_Select) byte |= 0x20; + if (pins & BPP_GP_PError) byte |= 0x40; + if (pins & BPP_GP_Busy) byte |= 0x80; + + put_user_ret(byte, c, -EFAULT); + c += 1; + remaining -= 1; + + /* Wait for event 11: Peripheral handshakes nibble */ + rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor); + if (rc == -1) return -EIO; + } + + return cnt - remaining; +} + +static long read_ecp(unsigned minor, char *c, unsigned long cnt) +{ + unsigned long remaining; + long rc; + + /* Turn ECP mode from forward to reverse if needed. */ + if (! instances[minor].direction) { + unsigned short pins = get_pins(minor); + + /* Event 38: Turn the bus around */ + instances[minor].direction = 0x20; + pins &= ~BPP_PP_nAutoFd; + set_pins(pins, minor); + + /* Event 39: Set pins for reverse mode. */ + snooze(TIME_PSetup, minor); + set_pins(BPP_PP_nStrobe|BPP_PP_nSelectIn, minor); + + /* Wait for event 40: Peripheral ready to be strobed */ + rc = wait_for(0, BPP_GP_PError, TIME_PResponse, minor); + if (rc == -1) return -ETIMEDOUT; + } + + remaining = cnt; + + while (remaining > 0) { + + /* If there is a run length for a repeated byte, repeat */ + /* that byte a few times. */ + if (instances[minor].run_length && !instances[minor].run_flag) { + + char buffer[128]; + unsigned idx; + unsigned repeat = remaining < instances[minor].run_length + ? remaining + : instances[minor].run_length; + + for (idx = 0 ; idx < repeat ; idx += 1) + buffer[idx] = instances[minor].repeat_byte; + + copy_to_user_ret(c, buffer, repeat, -EFAULT); + remaining -= repeat; + c += repeat; + instances[minor].run_length -= repeat; + } + + if (remaining == 0) break; + + + /* Wait for Event 43: Data active on the bus. */ + rc = wait_for(0, BPP_GP_nAck, TIME_IDLE_LIMIT, minor); + if (rc == -1) break; + + if (rc & BPP_GP_Busy) { + /* OK, this is data. read it in. */ + unsigned char byte = bpp_inb(base_addrs[minor]); + put_user_ret(byte, c, -EFAULT); + c += 1; + remaining -= 1; + + if (instances[minor].run_flag) { + instances[minor].repeat_byte = byte; + instances[minor].run_flag = 0; + } + + } else { + unsigned char byte = bpp_inb(base_addrs[minor]); + if (byte & 0x80) { + printk("bpp%d: " + "Ignoring ECP channel %u from device.\n", + minor, byte & 0x7f); + } else { + instances[minor].run_length = byte; + instances[minor].run_flag = 1; + } + } + + /* Event 44: I got it. */ + set_pins(BPP_PP_nStrobe|BPP_PP_nAutoFd|BPP_PP_nSelectIn, minor); + + /* Wait for event 45: peripheral handshake */ + rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor); + if (rc == -1) return -ETIMEDOUT; + + /* Event 46: Finish handshake */ + set_pins(BPP_PP_nStrobe|BPP_PP_nSelectIn, minor); + + } + + + return cnt - remaining; +} + +static long bpp_read(struct inode *inode, struct file *f, + char *c, unsigned long cnt) +{ + long rc; + const unsigned minor = MINOR(inode->i_rdev); + if (minor >= BPP_NO) return -ENODEV; + if (!instances[minor].present) return -ENODEV; + + switch (instances[minor].mode) { + + default: + if (instances[minor].mode != COMPATIBILITY) + terminate(minor); + + if (instances[minor].enhanced) { + /* For now, do all reads with ECP-RLE mode */ + unsigned short pins; + + rc = negotiate(DEFAULT_ECP, minor); + if (rc < 0) break; + + instances[minor].mode = ECP_RLE; + + /* Event 30: set nAutoFd low to setup for ECP mode */ + pins = get_pins(minor); + pins &= ~BPP_PP_nAutoFd; + set_pins(pins, minor); + + /* Wait for Event 31: peripheral ready */ + rc = wait_for(BPP_GP_PError, 0, TIME_PResponse, minor); + if (rc == -1) return -ETIMEDOUT; + + rc = read_ecp(minor, c, cnt); + + } else { + rc = negotiate(DEFAULT_NIBBLE, minor); + if (rc < 0) break; + + instances[minor].mode = NIBBLE; + + rc = read_nibble(minor, c, cnt); + } + break; + + case NIBBLE: + rc = read_nibble(minor, c, cnt); + break; + + case ECP: + case ECP_RLE: + rc = read_ecp(minor, c, cnt); + break; + + } + + + return rc; +} + +/* + * Compatibility mode handshaking is a matter of writing data, + * strobing it, and waiting for the printer to stop being busy. + */ +static long write_compat(unsigned minor, const char *c, unsigned long cnt) +{ + long rc; + unsigned short pins = get_pins(minor); + + unsigned long remaining = cnt; + + while (remaining > 0) { + unsigned char byte; + c += 1; + get_user_ret(byte, c, -EFAULT); + + rc = wait_for(BPP_GP_nAck, BPP_GP_Busy, TIME_IDLE_LIMIT, minor); + if (rc == -1) return -ETIMEDOUT; + + bpp_outb_p(byte, base_addrs[minor]); + remaining -= 1; + /* snooze(1, minor); */ + + pins &= ~BPP_PP_nStrobe; + set_pins(pins, minor); + + rc = wait_for(BPP_GP_Busy, 0, TIME_PResponse, minor); + + pins |= BPP_PP_nStrobe; + set_pins(pins, minor); + } + + return cnt - remaining; +} + +/* + * Write data using ECP mode. Watch out that the port may be set up + * for reading. If so, turn the port around. + */ +static long write_ecp(unsigned minor, const char *c, unsigned long cnt) +{ + unsigned short pins = get_pins(minor); + unsigned long remaining = cnt; + + if (instances[minor].direction) { + int rc; + + /* Event 47 Request bus be turned around */ + pins |= BPP_PP_nInit; + set_pins(pins, minor); + + /* Wait for Event 49: Peripheral relinquished bus */ + rc = wait_for(BPP_GP_PError, 0, TIME_PResponse, minor); + + pins |= BPP_PP_nAutoFd; + instances[minor].direction = 0; + set_pins(pins, minor); + } + + while (remaining > 0) { + unsigned char byte; + int rc; + + get_user_ret(byte, c, -EFAULT); + + rc = wait_for(0, BPP_GP_Busy, TIME_PResponse, minor); + if (rc == -1) return -ETIMEDOUT; + + c += 1; + + bpp_outb_p(byte, base_addrs[minor]); + + pins &= ~BPP_PP_nStrobe; + set_pins(pins, minor); + + pins |= BPP_PP_nStrobe; + rc = wait_for(BPP_GP_Busy, 0, TIME_PResponse, minor); + if (rc == -1) return -EIO; + + set_pins(pins, minor); + } + + return cnt - remaining; +} + +/* + * Write to the peripheral. Be sensitive of the current mode. If I'm + * in a mode that can be turned around (ECP) then just do + * that. Otherwise, terminate and do my writing in compat mode. This + * is the safest course as any device can handle it. + */ +static long bpp_write(struct inode *inode, struct file *f, + const char *c, unsigned long cnt) +{ + long errno = 0; + unsigned minor = MINOR(inode->i_rdev); + if (minor >= BPP_NO) return -ENODEV; + if (!instances[minor].present) return -ENODEV; + + switch (instances[minor].mode) { + + case ECP: + case ECP_RLE: + errno = write_ecp(minor, c, cnt); + break; + case COMPATIBILITY: + errno = write_compat(minor, c, cnt); + break; + default: + terminate(minor); + errno = write_compat(minor, c, cnt); + } + + return errno; +} + +static int bpp_ioctl(struct inode *inode, struct file *f, unsigned int cmd, + unsigned long arg) +{ + int errno = 0; + + unsigned minor = MINOR(inode->i_rdev); + if (minor >= BPP_NO) return -ENODEV; + if (!instances[minor].present) return -ENODEV; + + + switch (cmd) { + + case BPP_PUT_PINS: + set_pins(arg, minor); + break; + + case BPP_GET_PINS: + errno = get_pins(minor); + break; + + case BPP_PUT_DATA: + bpp_outb_p(arg, base_addrs[minor]); + break; + + case BPP_GET_DATA: + errno = bpp_inb_p(base_addrs[minor]); + break; + + case BPP_SET_INPUT: + if (arg) + if (instances[minor].enhanced) { + unsigned short bits = get_pins(minor); + instances[minor].direction = 0x20; + set_pins(bits, minor); + } else { + errno = -ENOTTY; + } + else { + unsigned short bits = get_pins(minor); + instances[minor].direction = 0x00; + set_pins(bits, minor); + } + break; + + default: + errno = -EINVAL; + } + + return errno; +} + +static struct file_operations bpp_fops = { + NULL, /* bpp_lseek */ + bpp_read, + bpp_write, + NULL, /* bpp_readdir */ + NULL, /* bpp_select */ + bpp_ioctl, + NULL, /* bpp_mmap */ + bpp_open, + bpp_release, +}; + +#if defined(__i386__) + +#define collectLptPorts() {} + +static void probeLptPort(unsigned idx) +{ + unsigned int testvalue; + const unsigned short lpAddr = base_addrs[idx]; + + instances[idx].present = 0; + instances[idx].enhanced = 0; + instances[idx].direction = 0; + instances[idx].mode = COMPATIBILITY; + instances[idx].wait_queue = 0; + instances[idx].run_length = 0; + instances[idx].run_flag = 0; + init_timer(&instances[idx].timer_list); + instances[idx].timer_list.function = bpp_wake_up; + if (check_region(lpAddr,3)) return; + + /* + * First, make sure the instance exists. Do this by writing to + * the data latch and reading the value back. If the port *is* + * present, test to see if it supports extended-mode + * operation. This will be required for IEEE1284 reverse + * transfers. + */ + + outb_p(BPP_PROBE_CODE, lpAddr); + for (testvalue=0; testvalue<BPP_DELAY; testvalue++) + ; + testvalue = inb_p(lpAddr); + if (testvalue == BPP_PROBE_CODE) { + unsigned save; + instances[idx].present = 1; + + request_region(lpAddr,3, dev_name); + save = inb_p(lpAddr+2); + for (testvalue=0; testvalue<BPP_DELAY; testvalue++) + ; + outb_p(save|0x20, lpAddr+2); + for (testvalue=0; testvalue<BPP_DELAY; testvalue++) + ; + outb_p(~BPP_PROBE_CODE, lpAddr); + for (testvalue=0; testvalue<BPP_DELAY; testvalue++) + ; + testvalue = inb_p(lpAddr); + if ((testvalue&0xff) == (0xff&~BPP_PROBE_CODE)) + instances[idx].enhanced = 0; + else + instances[idx].enhanced = 1; + outb_p(save, lpAddr+2); + } + + /* + * Leave the port in compat idle mode. + */ + set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, idx); + + printk("bpp%d: Port at 0x%03x: Enhanced mode %s\n", idx, base_addrs[idx], + instances[idx].enhanced? "SUPPORTED" : "UNAVAILABLE"); +} + +static inline void freeLptPort(int idx) +{ + release_region(base_addrs[idx], 3); +} + +#endif + +#if defined(__sparc__) + +static volatile struct bpp_regs *map_bpp(struct linux_sbus_device *dev, int idx) +{ + volatile struct bpp_regs *regs; + + /* Apply ranges to here, do not pollute Sbus devices list. */ + struct linux_prom_registers areg; + + /* + * PROM reports different numbers on Zebra and on DMA2. + * We need to figure out when to apply parent ranges. + * printk will show this on different machines. + */ + + areg = dev->reg_addrs[0]; + printk("bpp%d.map_bpp: 0x%x.%p[0x%x] i=%d\n", idx, + areg.which_io, areg.phys_addr, areg.reg_size, + dev->irqs[0].pri); + /* IPC Zebra 1.fa200000[1c] i=2 */ + /** prom_apply_sbus_ranges (&areg, 1); **/ + + regs = sparc_alloc_io (areg.phys_addr, 0, + sizeof(struct bpp_regs), "bpp", + areg.which_io, 0x0); + + return regs; +} + +static int collectLptPorts(void) +{ + struct linux_sbus *bus; + struct linux_sbus_device *dev; + int count; + + count = 0; + for_all_sbusdev(dev, bus) { + if (strcmp(dev->prom_name, "SUNW,bpp") == 0) { + if (count >= BPP_NO) { + printk(KERN_NOTICE + "bpp: More than %d bpp ports," + " rest is ignored\n", BPP_NO); + return count; + } + base_addrs[count] = map_bpp(dev, count); + count++; + } + } + return count; +} + +static void probeLptPort(unsigned idx) +{ + volatile struct bpp_regs *rp = base_addrs[idx]; + __u32 csr; + char *brand; + + instances[idx].present = 0; + instances[idx].enhanced = 0; + instances[idx].direction = 0; + instances[idx].mode = COMPATIBILITY; + instances[idx].wait_queue = 0; + instances[idx].run_length = 0; + instances[idx].run_flag = 0; + init_timer(&instances[idx].timer_list); + instances[idx].timer_list.function = bpp_wake_up; + + if (rp == 0) return; + + instances[idx].present = 1; + instances[idx].enhanced = 1; /* Sure */ + + if (((csr = rp->p_csr) & P_DRAINING) != 0 && (csr & P_ERR_PEND) == 0) { + udelay(20); + csr = rp->p_csr; + if ((csr & P_DRAINING) != 0 && (csr & P_ERR_PEND) == 0) { + printk("bpp%d: DRAINING still active (0x%08x)\n", idx, csr); + } + } + printk("bpp%d: reset with 0x%08x ..", idx, csr); + rp->p_csr = (csr | P_RESET) & ~P_INT_EN; + udelay(500); + rp->p_csr &= ~P_RESET; + printk(" done with csr=0x%08x ocr=0x%04x\n", rp->p_csr, rp->p_ocr); + + switch (rp->p_csr & P_DEV_ID_MASK) { + case P_DEV_ID_ZEBRA: + brand = "Zebra"; + break; + case P_DEV_ID_L64854: + brand = "DMA2"; + break; + default: + brand = "Unknown"; + } + printk("bpp%d: %s at 0x%p\n", idx, brand, rp); + + /* + * Leave the port in compat idle mode. + */ + set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, idx); + + return; +} + +static inline void freeLptPort(int idx) +{ + sparc_free_io ((void *)base_addrs[idx], sizeof(struct bpp_regs)); +} + +#endif + +#ifdef MODULE +int init_module(void) +#else +__initfunc(int bpp_init(void)) +#endif +{ + int rc; + unsigned idx; + + rc = collectLptPorts(); + if (rc == 0) + return -ENODEV; + + rc = register_chrdev(BPP_MAJOR, dev_name, &bpp_fops); + if (rc < 0) + return rc; + + for (idx = 0; idx < BPP_NO; idx += 1) { + instances[idx].opened = 0; + probeLptPort(idx); + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + unsigned idx; + + unregister_chrdev(BPP_MAJOR, dev_name); + + for (idx = 0 ; idx < BPP_NO ; idx += 1) { + if (instances[idx].present) + freeLptPort(idx); + } +} +#endif diff --git a/drivers/sbus/char/bwtwo.c b/drivers/sbus/char/bwtwo.c new file mode 100644 index 000000000..be68d09c9 --- /dev/null +++ b/drivers/sbus/char/bwtwo.c @@ -0,0 +1,211 @@ +/* $Id: bwtwo.c,v 1.13 1997/04/14 17:04:55 jj Exp $ + * bwtwo.c: bwtwo console driver + * + * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) + * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) + */ + +#include <linux/kd.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/proc_fs.h> + +#include <asm/sbus.h> +#include <asm/io.h> +#include <asm/fbio.h> +#include <asm/pgtable.h> + +#include "../../char/vt_kern.h" +#include "../../char/selection.h" +#include "../../char/console_struct.h" +#include "fb.h" +#include "cg_common.h" + +/* OBio addresses for the bwtwo registers */ +#define BWTWO_REGISTER_OFFSET 0x400000 + +struct bwtwo_regs { + __volatile__ 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 + +/* 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 int +bwtwo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, + long base, fbinfo_t *fb) +{ + uint size, map_offset, r; + int map_size; + + map_size = size = vma->vm_end - vma->vm_start; + + if (vma->vm_offset & ~PAGE_MASK) + return -ENXIO; + + /* To stop the swapper from even considering these pages */ + vma->vm_flags |= FB_MMAP_VM_FLAGS; + + /* This routine should also map the register if asked for, + * but we don't do that yet. + */ + map_offset = get_phys ((unsigned long) fb->base); + r = io_remap_page_range (vma->vm_start, map_offset, map_size, + vma->vm_page_prot, fb->space); + if (r) return -EAGAIN; + vma->vm_inode = inode; + inode->i_count++; + return 0; +} + +static void +bwtwo_blank (fbinfo_t *fb) +{ + fb->info.bwtwo.regs->control &= ~BWTWO_CTL_ENABLE_VIDEO; +} + +static void +bwtwo_unblank (fbinfo_t *fb) +{ + fb->info.bwtwo.regs->control |= BWTWO_CTL_ENABLE_VIDEO; +} + + +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 +}; + +__initfunc(void bwtwo_setup (fbinfo_t *fb, int slot, u32 bwtwo, int bw2_io, + struct linux_sbus_device *sbdp)) +{ + printk ("bwtwo%d at 0x%8.8x\n", slot, bwtwo); + fb->type.fb_cmsize = 0; + fb->mmap = bwtwo_mmap; + fb->loadcmap = 0; + fb->ioctl = 0; + fb->reset = 0; + fb->blank = bwtwo_blank; + fb->unblank = bwtwo_unblank; + + fb->info.bwtwo.regs = + sparc_alloc_io (bwtwo + BWTWO_REGISTER_OFFSET, + 0, sizeof (struct bwtwo_regs), + "bwtwo_regs", bw2_io, 0); + + if (!prom_getbool(sbdp->prom_node, "width")) { + /* Ugh, broken PROM didn't initialize us. + * Let's deal with this ourselves. + */ + u8 status, mon; + u8 *p; + + status = fb->info.bwtwo.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; + } 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; + default: + prom_printf("bwtwo: can't handle SR %02x\n", + status); + prom_halt(); + return; /* fool gcc. */ + } + for ( ; *p; p += 2) + ((u8 *)fb->info.bwtwo.regs)[p[0]] = p[1]; + } + + if(!fb->base) + fb->base = (unsigned long) sparc_alloc_io(bwtwo, 0, + ((fb->type.fb_depth*fb->type.fb_height*fb->type.fb_width)/8), + "bwtwo_fbase", bw2_io, 0); + + if (slot && sun_prom_console_id != slot) + bwtwo_blank (fb); +} + diff --git a/drivers/sbus/char/cg_common.h b/drivers/sbus/char/cg_common.h new file mode 100644 index 000000000..c4376f66c --- /dev/null +++ b/drivers/sbus/char/cg_common.h @@ -0,0 +1,28 @@ +/* sun_cg_common.h + * contains the definitions of the structures that various sun + * frame buffer can use to do console driver stuff. + * + * This is not in sun_framebuffer.h because you may not want cgXX + * support so you wont require this. + * + */ + +#define BT_D4M3(x) ((((x) >> 2) << 1) + ((x) >> 2)) /* (x / 4) * 3 */ +#define BT_D4M4(x) ((x) & ~3) /* (x / 4) * 4 */ + +#define D4M3(x) ((((x)>>2)<<1) + ((x)>>2)) /* (x/4)*3 */ +#define D4M4(x) ((x)&~0x3) /* (x/4)*4 */ + +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 */ +}; + +/* The cg3 driver, obio space addresses for mapping the cg3 stuff */ +/* We put these constants here, because the cg14 driver initially will emulate a cg3 */ +#define CG3_REGS 0x400000 +#define CG3_RAM 0x800000 + + diff --git a/drivers/sbus/char/cgfourteen.c b/drivers/sbus/char/cgfourteen.c new file mode 100644 index 000000000..d42a4343c --- /dev/null +++ b/drivers/sbus/char/cgfourteen.c @@ -0,0 +1,472 @@ +/* $Id: cgfourteen.c,v 1.19 1997/04/14 17:04:57 jj Exp $ + * cgfourteen.c: Sun SparcStation console support. + * + * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) + * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * + * TODO: + * + * Add the ioctls for CLUT manipulation. + * Map only the amount requested, not a constant amount. + * XBGR mapping. + * Add the interrupt handler. +*/ + +#include <linux/kd.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/proc_fs.h> + +#include <asm/sbus.h> +#include <asm/io.h> +#include <asm/fbio.h> +#include <asm/pgtable.h> +#include <asm/uaccess.h> + +#include "../../char/vt_kern.h" +#include "../../char/selection.h" +#include "../../char/console_struct.h" +#include "fb.h" + +#define CG14_MCR_INTENABLE_SHIFT 7 +#define CG14_MCR_INTENABLE_MASK 0x80 +#define CG14_MCR_VIDENABLE_SHIFT 6 +#define CG14_MCR_VIDENABLE_MASK 0x40 +#define CG14_MCR_PIXMODE_SHIFT 4 +#define CG14_MCR_PIXMODE_MASK 0x30 +#define CG14_MCR_TMR_SHIFT 2 +#define CG14_MCR_TMR_MASK 0x0c +#define CG14_MCR_TMENABLE_SHIFT 1 +#define CG14_MCR_TMENABLE_MASK 0x02 +#define CG14_MCR_RESET_SHIFT 0 +#define CG14_MCR_RESET_MASK 0x01 +#define CG14_REV_REVISION_SHIFT 4 +#define CG14_REV_REVISION_MASK 0xf0 +#define CG14_REV_IMPL_SHIFT 0 +#define CG14_REV_IMPL_MASK 0x0f +#define CG14_VBR_FRAMEBASE_SHIFT 12 +#define CG14_VBR_FRAMEBASE_MASK 0x00fff000 +#define CG14_VMCR1_SETUP_SHIFT 0 +#define CG14_VMCR1_SETUP_MASK 0x000001ff +#define CG14_VMCR1_VCONFIG_SHIFT 9 +#define CG14_VMCR1_VCONFIG_MASK 0x00000e00 +#define CG14_VMCR2_REFRESH_SHIFT 0 +#define CG14_VMCR2_REFRESH_MASK 0x00000001 +#define CG14_VMCR2_TESTROWCNT_SHIFT 1 +#define CG14_VMCR2_TESTROWCNT_MASK 0x00000002 +#define CG14_VMCR2_FBCONFIG_SHIFT 2 +#define CG14_VMCR2_FBCONFIG_MASK 0x0000000c +#define CG14_VCR_REFRESHREQ_SHIFT 0 +#define CG14_VCR_REFRESHREQ_MASK 0x000003ff +#define CG14_VCR1_REFRESHENA_SHIFT 10 +#define CG14_VCR1_REFRESHENA_MASK 0x00000400 +#define CG14_VCA_CAD_SHIFT 0 +#define CG14_VCA_CAD_MASK 0x000003ff +#define CG14_VCA_VERS_SHIFT 10 +#define CG14_VCA_VERS_MASK 0x00000c00 +#define CG14_VCA_RAMSPEED_SHIFT 12 +#define CG14_VCA_RAMSPEED_MASK 0x00001000 +#define CG14_VCA_8MB_SHIFT 13 +#define CG14_VCA_8MB_MASK 0x00002000 + +#define CG14_MCR_PIXMODE_8 0 +#define CG14_MCR_PIXMODE_16 2 +#define CG14_MCR_PIXMODE_32 3 + +struct cg14_regs{ + volatile u8 mcr; /* Master Control Reg */ + volatile u8 ppr; /* Packed Pixel Reg */ + volatile u8 tms[2]; /* Test Mode Status Regs */ + volatile u8 msr; /* Master Status Reg */ + volatile u8 fsr; /* Fault Status Reg */ + volatile u8 rev; /* Revision & Impl */ + volatile u8 ccr; /* Clock Control Reg */ + volatile u32 tmr; /* Test Mode Read Back */ + volatile u8 mod; /* Monitor Operation Data Reg */ + volatile u8 acr; /* Aux Control */ + u8 xxx0[6]; + volatile u16 hct; /* Hor Counter */ + volatile u16 vct; /* Vert Counter */ + volatile u16 hbs; /* Hor Blank Start */ + volatile u16 hbc; /* Hor Blank Clear */ + volatile u16 hss; /* Hor Sync Start */ + volatile u16 hsc; /* Hor Sync Clear */ + volatile u16 csc; /* Composite Sync Clear */ + volatile u16 vbs; /* Vert Blank Start */ + volatile u16 vbc; /* Vert Blank Clear */ + volatile u16 vss; /* Vert Sync Start */ + volatile u16 vsc; /* Vert Sync Clear */ + volatile u16 xcs; + volatile u16 xcc; + volatile u16 fsa; /* Fault Status Address */ + volatile u16 adr; /* Address Registers */ + u8 xxx1[0xce]; + volatile u8 pcg[0x100]; /* Pixel Clock Generator */ + volatile u32 vbr; /* Frame Base Row */ + volatile u32 vmcr; /* VBC Master Control */ + volatile u32 vcr; /* VBC refresh */ + volatile u32 vca; /* VBC Config */ +}; + +#define CG14_CCR_ENABLE 0x04 +#define CG14_CCR_SELECT 0x02 /* HW/Full screen */ + +struct cg14_cursor { + volatile u32 cpl0[32]; /* Enable plane 0 */ + volatile u32 cpl1[32]; /* Color selection plane */ + volatile u8 ccr; /* Cursor Control Reg */ + u8 xxx0[3]; + volatile u16 cursx; /* Cursor x,y position */ + volatile u16 cursy; /* Cursor x,y position */ + volatile u32 color0; + volatile u32 color1; + u32 xxx1[0x1bc]; + volatile u32 cpl0i[32]; /* Enable plane 0 autoinc */ + volatile u32 cpl1i[32]; /* Color selection autoinc */ +}; + +struct cg14_dac { + volatile u8 addr; /* Address Register */ + u8 xxx0[255]; + volatile u8 glut; /* Gamma table */ + u8 xxx1[255]; + volatile u8 select; /* Register Select */ + u8 xxx2[255]; + volatile u8 mode; /* Mode Register */ +}; + +struct cg14_xlut{ + volatile u8 x_xlut [256]; + volatile u8 x_xlutd [256]; + u8 xxx0[0x600]; + volatile u8 x_xlut_inc [256]; + volatile u8 x_xlutd_inc [256]; +}; + +/* Color look up table (clut) */ +/* Each one of these arrays hold the color lookup table (for 256 + * colors) for each MDI page (I assume then there should be 4 MDI + * pages, I still wonder what they are. I have seen NeXTStep split + * the screen in four parts, while operating in 24 bits mode. Each + * integer holds 4 values: alpha value (transparency channel, thanks + * go to John Stone (johns@umr.edu) from OpenBSD), red, green and blue + * + * I currently use the clut instead of the Xlut + */ +struct cg14_clut { + unsigned int c_clut [256]; + unsigned int c_clutd [256]; /* i wonder what the 'd' is for */ + unsigned int c_clut_inc [256]; + unsigned int c_clutd_inc [256]; +}; + +static int +cg14_mmap (struct inode *inode, struct file *file, + struct vm_area_struct *vma, long base, fbinfo_t *fb) +{ + uint size, page, r, map_size; + uint map_offset = 0; + uint ram_size = fb->info.cg14.ramsize; + + printk ("RAMSIZE=%d\n", ram_size); + size = vma->vm_end - vma->vm_start; + if (vma->vm_offset & ~PAGE_MASK) + return -ENXIO; + + /* To stop the swapper from even considering these pages */ + vma->vm_flags |= FB_MMAP_VM_FLAGS; + + /* Each page, see which map applies */ + for (page = 0; page < size; ){ + switch (vma->vm_offset+page){ + case CG3_MMAP_OFFSET-0x7000: + printk ("Wee! They are mapping the register, report this to miguel@gnu.ai.mit.edu\n"); + printk ("Mapping fb->info.regs!\n"); + map_size = 0x7000; + map_offset = get_phys ((unsigned long) fb->info.cg14.regs); + break; + + case CG3_MMAP_OFFSET: + map_size = size-page; + map_offset = get_phys ((unsigned long) fb->base); + break; + + case MDI_PLANAR_X16_MAP: + map_size = ram_size/2; + map_offset = get_phys ((unsigned long) fb->base) | 0x2000000; + break; + + case MDI_PLANAR_C16_MAP: + map_size = ram_size/2; + map_offset = get_phys ((unsigned long) fb->base) | 0x2800000; + break; + + case MDI_CHUNKY_XBGR_MAP: + map_size = 0; + printk ("Woo Woo: XBGR not there yet\n"); + break; + + case MDI_CHUNKY_BGR_MAP: + map_size = ram_size; + map_offset = get_phys ((unsigned long) fb->base) | 0x1000000; + break; + + case MDI_PLANAR_X32_MAP: + map_size = ram_size/4; + map_offset = get_phys ((unsigned long) fb->base) | 0x3000000; + break; + case MDI_PLANAR_B32_MAP: + map_size = ram_size/4; + map_offset = get_phys ((unsigned long) fb->base) | 0x3400000; + break; + case MDI_PLANAR_G32_MAP: + map_size = ram_size/4; + map_offset = get_phys ((unsigned long) fb->base) | 0x3800000; + break; + case MDI_PLANAR_R32_MAP: + map_size = ram_size/4; + map_offset = get_phys ((unsigned long) fb->base) | 0x3c00000; + break; + + case MDI_CURSOR_MAP: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long) fb->info.cg14.cursor_regs); + break; + + case CG14_REGS: + printk ("Wee! They are mapping the register, report this to miguel@gnu.ai.mit.edu\n"); + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long) fb->info.cg14.regs); + break; + + case CG14_XLUT: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long) fb->info.cg14.regs+0x3000); + break; + + case CG14_CLUT1: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long) fb->info.cg14.regs+0x4000); + break; + + case CG14_CLUT2: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long) fb->info.cg14.regs+0x5000); + break; + + default: + map_size = 0; + break; + } + if (!map_size){ + page += PAGE_SIZE; + continue; + } + if (page + map_size > size) + map_size = size - page; + r = io_remap_page_range (vma->vm_start+page, + map_offset, + map_size, vma->vm_page_prot, + fb->space); + if (r) return -EAGAIN; + page += map_size; + } + vma->vm_inode = inode; + inode->i_count++; + return 0; +} + +static void +cg14_cmap (fbinfo_t *fb, int index, int count) +{ + struct cg14_clut *clut = fb->info.cg14.clut; + int i; + + for (i = index; count--; i++){ + clut->c_clut [i] = + (fb->color_map CM(i,2) << 16) | + (fb->color_map CM(i,1) << 8) | + (fb->color_map CM(i,0)); + } +} + +static void +cg14_setcursormap (fbinfo_t *fb, unsigned char *red, + unsigned char *green, + unsigned char *blue) +{ + struct cg14_cursor *cur = fb->info.cg14.cursor_regs; + + cur->color0 = ((red[0]) | (green[0] << 8) | (blue[0] << 16)); + cur->color1 = ((red[1]) | (green[1] << 8) | (blue[1] << 16)); +} + +/* Load cursor information */ +static void +cg14_setcursor (fbinfo_t *fb) +{ + struct cg_cursor *c = &fb->cursor; + struct cg14_cursor *cur = fb->info.cg14.cursor_regs; + + if (c->enable) + cur->ccr |= CG14_CCR_ENABLE; + cur->cursx = ((c->cpos.fbx - c->chot.fbx) & 0xfff); + cur->cursy = ((c->cpos.fby - c->chot.fby) & 0xfff); +} + +/* Set cursor shape */ +static void +cg14_setcurshape (fbinfo_t *fb) +{ + struct cg14_cursor *cur = fb->info.cg14.cursor_regs; + int i; + + for (i = 0; i < 32; i++){ + cur->cpl0 [i] = fb->cursor.bits[0][i]; + cur->cpl1 [i] = fb->cursor.bits[1][i]; + } +} + +/* These ones are for putting the video card on 16/32 bpp */ +static int +cg14_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long arg, fbinfo_t *fb) +{ + switch (cmd){ + case MDI_RESET: { + volatile unsigned char *control = &(fb->info.cg14.regs->mcr); + *control = (*control & ~CG14_MCR_PIXMODE_MASK); + } + break; + + case MDI_GET_CFGINFO: { + int error; + struct mdi_cfginfo *mdii; + + error = verify_area (VERIFY_WRITE, (void *) arg, + sizeof (struct mdi_cfginfo)); + if (error) + return error; + + mdii = (struct mdi_cfginfo *) arg; +#if 0 + __put_user_ret(2, &mdii->mdi_ncluts, -EFAULT); + switch (fb->info.cg14.regs->rev & CG14_REV_IMPL_MASK){ + case 0: + case 2: + break; + + case 1: + case 3: + __put_user_ret(3, &mdii->mdi_ncluts, -EFAULT); + break; + default: + printk ("Unknown implementation number\n"); + } +#endif + __put_user_ret(FBTYPE_MDICOLOR, &mdii->mdi_type, -EFAULT); + __put_user_ret(fb->type.fb_height, &mdii->mdi_height, -EFAULT); + __put_user_ret(fb->type.fb_width, &mdii->mdi_width, -EFAULT); + __put_user_ret(fb->info.cg14.video_mode, &mdii->mdi_mode, -EFAULT); + __put_user_ret(72, &mdii->mdi_pixfreq, -EFAULT); /* FIXME */ + __put_user_ret(fb->info.cg14.ramsize, &mdii->mdi_size, -EFAULT); + } + break; + + case MDI_SET_PIXELMODE: { + int newmode; + volatile u8 *control; + + get_user_ret(newmode, (int *)arg, -EFAULT); + control = &(fb->info.cg14.regs->mcr); + switch (newmode){ + case MDI_32_PIX: + *control = (*control & ~CG14_MCR_PIXMODE_MASK) | + (CG14_MCR_PIXMODE_32 << CG14_MCR_PIXMODE_SHIFT); + break; + case MDI_16_PIX: + *control = (*control & ~CG14_MCR_PIXMODE_MASK) | 0x20; + break; + case MDI_8_PIX: + *control = (*control & ~CG14_MCR_PIXMODE_MASK); + break; + + default: + return -ENOSYS; + } + fb->info.cg14.video_mode = newmode; + } + break; + + } /* switch */ + return 0; +} + +static void +cg14_switch_from_graph (void) +{ + fbinfo_t *fb = &(fbinfo [0]); + struct cg14_info *cg14info = (struct cg14_info *) &fb->info.cg14; + + /* Set the 8-bpp mode */ + if (fb->open && fb->mmaped){ + volatile char *mcr = (char *)(&cg14info->regs->mcr); + + fb->info.cg14.video_mode = 8; + *mcr = (*mcr & ~(CG14_MCR_PIXMODE_MASK)); + } +} + +void +cg14_reset (fbinfo_t *fb) +{ + volatile char *mcr = &(fb->info.cg14.regs->mcr); + + *mcr = (*mcr & ~(CG14_MCR_PIXMODE_MASK)); +} + +__initfunc(void cg14_setup (fbinfo_t *fb, int slot, int con_node, u32 cg14, int cg14_io)) +{ + struct cg14_info *cg14info; + uint bases [2]; + unsigned long cg14regs = 0; + struct cg14_regs *regs = 0; + + if (!cg14) { + prom_getproperty (con_node, "address", (char *) &bases[0], 8); + cg14 = bases[1]; + cg14regs = bases[0]; + fb->base = cg14; + fb->info.cg14.regs = (struct cg14_regs *) cg14regs; + regs = (struct cg14_regs *) cg14regs; + } + + if (!cg14regs){ + printk ("The PROM does not have mapped the frame buffer or the registers\n" + "Mr. Penguin can't use that"); + prom_halt (); + } + + fb->type.fb_cmsize = 256; + fb->mmap = cg14_mmap; + fb->loadcmap = cg14_cmap; + fb->setcursor = cg14_setcursor; + fb->setcursormap = cg14_setcursormap; + fb->setcurshape = cg14_setcurshape; + fb->ioctl = cg14_ioctl; + fb->switch_from_graph = cg14_switch_from_graph; + fb->postsetup = sun_cg_postsetup; + fb->reset = cg14_reset; + fb->blank = 0; + fb->unblank = 0; + fb->info.cg14.video_mode = 8; + fb->emulations [1] = FBTYPE_SUN3COLOR; + fb->type.fb_depth = 24; + cg14info = (struct cg14_info *) &fb->info.cg14; + cg14info->clut = (void *) (cg14regs + CG14_CLUT1); + cg14info->cursor_regs = (void *) (cg14regs + CG14_CURSORREGS); + + /* If the bit is turned on, the card has 8 mb of ram, otherwise just 4 */ + cg14info->ramsize = (regs->vca & CG14_VCA_8MB_MASK ? 8 : 4) * 1024 * 1024; + printk ("cgfourteen%d at 0x%8.8x with %d megs of RAM rev=%d, impl=%d\n", + slot, cg14, cg14info->ramsize/(1024*1024), regs->rev >> 4, regs->rev & 0xf); +} diff --git a/drivers/sbus/char/cgsix.c b/drivers/sbus/char/cgsix.c new file mode 100644 index 000000000..5f91c1308 --- /dev/null +++ b/drivers/sbus/char/cgsix.c @@ -0,0 +1,642 @@ +/* $Id: cgsix.c,v 1.27 1997/04/14 17:04:55 jj Exp $ + * cgsix.c: cgsix frame buffer driver + * + * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) + * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) + */ +#include <linux/kd.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/proc_fs.h> + +#include <asm/sbus.h> +#include <asm/io.h> +#include <asm/fbio.h> +#include <asm/pgtable.h> + +#include "../../char/vt_kern.h" +#include "../../char/selection.h" +#include "../../char/console_struct.h" +#include "fb.h" +#include "cg_common.h" + +/* Offset of interesting structures in the OBIO space */ +/* + * Brooktree is the video dac and is funny to program on the cg6. + * (it's even funnier on the cg3) + * The FBC could be the the frame buffer control + * The FHC could is the frame buffer hardware control. + */ +#define CG6_ROM_OFFSET 0x0 +#define CG6_BROOKTREE_OFFSET 0x200000 +#define CG6_DHC_OFFSET 0x240000 +#define CG6_ALT_OFFSET 0x280000 +#define CG6_FHC_OFFSET 0x300000 +#define CG6_THC_OFFSET 0x301000 +#define CG6_FBC_OFFSET 0x700000 +#define CG6_TEC_OFFSET 0x701000 +#define CG6_RAM_OFFSET 0x800000 + +/* FHC definitions */ +#define CG6_FHC_FBID_SHIFT 24 +#define CG6_FHC_FBID_MASK 255 +#define CG6_FHC_REV_SHIFT 20 +#define CG6_FHC_REV_MASK 15 +#define CG6_FHC_FROP_DISABLE (1 << 19) +#define CG6_FHC_ROW_DISABLE (1 << 18) +#define CG6_FHC_SRC_DISABLE (1 << 17) +#define CG6_FHC_DST_DISABLE (1 << 16) +#define CG6_FHC_RESET (1 << 15) +#define CG6_FHC_LITTLE_ENDIAN (1 << 13) +#define CG6_FHC_RES_MASK (3 << 11) +#define CG6_FHC_1024 (0 << 11) +#define CG6_FHC_1152 (1 << 11) +#define CG6_FHC_1280 (2 << 11) +#define CG6_FHC_1600 (3 << 11) +#define CG6_FHC_CPU_MASK (3 << 9) +#define CG6_FHC_CPU_SPARC (0 << 9) +#define CG6_FHC_CPU_68020 (1 << 9) +#define CG6_FHC_CPU_386 (2 << 9) +#define CG6_FHC_TEST (1 << 8) +#define CG6_FHC_TEST_X_SHIFT 4 +#define CG6_FHC_TEST_X_MASK 15 +#define CG6_FHC_TEST_Y_SHIFT 0 +#define CG6_FHC_TEST_Y_MASK 15 + +/* FBC mode definitions */ +#define CG6_FBC_BLIT_IGNORE 0x00000000 +#define CG6_FBC_BLIT_NOSRC 0x00100000 +#define CG6_FBC_BLIT_SRC 0x00200000 +#define CG6_FBC_BLIT_ILLEGAL 0x00300000 +#define CG6_FBC_BLIT_MASK 0x00300000 + +#define CG6_FBC_VBLANK 0x00080000 + +#define CG6_FBC_MODE_IGNORE 0x00000000 +#define CG6_FBC_MODE_COLOR8 0x00020000 +#define CG6_FBC_MODE_COLOR1 0x00040000 +#define CG6_FBC_MODE_HRMONO 0x00060000 +#define CG6_FBC_MODE_MASK 0x00060000 + +#define CG6_FBC_DRAW_IGNORE 0x00000000 +#define CG6_FBC_DRAW_RENDER 0x00008000 +#define CG6_FBC_DRAW_PICK 0x00010000 +#define CG6_FBC_DRAW_ILLEGAL 0x00018000 +#define CG6_FBC_DRAW_MASK 0x00018000 + +#define CG6_FBC_BWRITE0_IGNORE 0x00000000 +#define CG6_FBC_BWRITE0_ENABLE 0x00002000 +#define CG6_FBC_BWRITE0_DISABLE 0x00004000 +#define CG6_FBC_BWRITE0_ILLEGAL 0x00006000 +#define CG6_FBC_BWRITE0_MASK 0x00006000 + +#define CG6_FBC_BWRITE1_IGNORE 0x00000000 +#define CG6_FBC_BWRITE1_ENABLE 0x00000800 +#define CG6_FBC_BWRITE1_DISABLE 0x00001000 +#define CG6_FBC_BWRITE1_ILLEGAL 0x00001800 +#define CG6_FBC_BWRITE1_MASK 0x00001800 + +#define CG6_FBC_BREAD_IGNORE 0x00000000 +#define CG6_FBC_BREAD_0 0x00000200 +#define CG6_FBC_BREAD_1 0x00000400 +#define CG6_FBC_BREAD_ILLEGAL 0x00000600 +#define CG6_FBC_BREAD_MASK 0x00000600 + +#define CG6_FBC_BDISP_IGNORE 0x00000000 +#define CG6_FBC_BDISP_0 0x00000080 +#define CG6_FBC_BDISP_1 0x00000100 +#define CG6_FBC_BDISP_ILLEGAL 0x00000180 +#define CG6_FBC_BDISP_MASK 0x00000180 + +#define CG6_FBC_INDEX_MOD 0x00000040 +#define CG6_FBC_INDEX_MASK 0x00000030 + +/* THC definitions */ +#define CG6_THC_MISC_REV_SHIFT 16 +#define CG6_THC_MISC_REV_MASK 15 +#define CG6_THC_MISC_RESET (1 << 12) +#define CG6_THC_MISC_VIDEO (1 << 10) +#define CG6_THC_MISC_SYNC (1 << 9) +#define CG6_THC_MISC_VSYNC (1 << 8) +#define CG6_THC_MISC_SYNC_ENAB (1 << 7) +#define CG6_THC_MISC_CURS_RES (1 << 6) +#define CG6_THC_MISC_INT_ENAB (1 << 5) +#define CG6_THC_MISC_INT (1 << 4) +#define CG6_THC_MISC_INIT 0x9f + +/* The contents are unknown */ +struct cg6_tec { + volatile int tec_matrix; + volatile int tec_clip; + volatile int tec_vdc; +}; + +struct cg6_thc { + uint thc_pad0[512]; + volatile uint thc_hs; /* hsync timing */ + volatile uint thc_hsdvs; + volatile uint thc_hd; + volatile uint thc_vs; /* vsync timing */ + volatile uint thc_vd; + volatile uint thc_refresh; + volatile uint thc_misc; + uint thc_pad1[56]; + volatile uint thc_cursxy; /* cursor x,y position (16 bits each) */ + volatile uint thc_cursmask[32]; /* cursor mask bits */ + volatile uint thc_cursbits[32]; /* what to show where mask enabled */ +}; + +struct cg6_fbc { + u32 xxx0[1]; + volatile u32 mode; + volatile u32 clip; + u32 xxx1[1]; + volatile u32 s; + volatile u32 draw; + volatile u32 blit; + volatile u32 font; + u32 xxx2[24]; + volatile u32 x0, y0, z0, color0; + volatile u32 x1, y1, z1, color1; + volatile u32 x2, y2, z2, color2; + volatile u32 x3, y3, z3, color3; + volatile u32 offx, offy; + u32 xxx3[2]; + volatile u32 incx, incy; + u32 xxx4[2]; + volatile u32 clipminx, clipminy; + u32 xxx5[2]; + volatile u32 clipmaxx, clipmaxy; + u32 xxx6[2]; + volatile u32 fg; + volatile u32 bg; + volatile u32 alu; + volatile u32 pm; + volatile u32 pixelm; + u32 xxx7[2]; + volatile u32 patalign; + volatile u32 pattern[8]; + u32 xxx8[432]; + volatile u32 apointx, apointy, apointz; + u32 xxx9[1]; + volatile u32 rpointx, rpointy, rpointz; + u32 xxx10[5]; + volatile u32 pointr, pointg, pointb, pointa; + volatile u32 alinex, aliney, alinez; + u32 xxx11[1]; + volatile u32 rlinex, rliney, rlinez; + u32 xxx12[5]; + volatile u32 liner, lineg, lineb, linea; + volatile u32 atrix, atriy, atriz; + u32 xxx13[1]; + volatile u32 rtrix, rtriy, rtriz; + u32 xxx14[5]; + volatile u32 trir, trig, trib, tria; + volatile u32 aquadx, aquady, aquadz; + u32 xxx15[1]; + volatile u32 rquadx, rquady, rquadz; + u32 xxx16[5]; + volatile u32 quadr, quadg, quadb, quada; + volatile u32 arectx, arecty, arectz; + u32 xxx17[1]; + volatile u32 rrectx, rrecty, rrectz; + u32 xxx18[5]; + volatile u32 rectr, rectg, rectb, recta; +}; + +static void +cg6_restore_palette (fbinfo_t *fbinfo) +{ + volatile struct bt_regs *bt; + + bt = fbinfo->info.cg6.bt; + bt->addr = 0; + bt->color_map = 0xffffffff; + bt->color_map = 0xffffffff; + bt->color_map = 0xffffffff; +} + +static void cg6_blitc(unsigned short, int, int); +static void cg6_setw(int, int, unsigned short, int); +static void cg6_cpyw(int, int, unsigned short *, int); + +#if 0 +static void cg6_fill(int, int, int *); +#endif + +/* Ugh: X wants to mmap a bunch of cute stuff at the same time :-( */ +/* So, we just mmap the things that are being asked for */ +static int +cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, + long base, fbinfo_t *fb) +{ + uint size, page, r, map_size; + uint map_offset = 0; + + size = vma->vm_end - vma->vm_start; + if (vma->vm_offset & ~PAGE_MASK) + return -ENXIO; + + /* To stop the swapper from even considering these pages */ + vma->vm_flags |= FB_MMAP_VM_FLAGS; + + /* Each page, see which map applies */ + for (page = 0; page < size; ){ + switch (vma->vm_offset+page){ + case CG6_TEC: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.cg6.tec); + break; + case CG6_FBC: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.cg6.fbc); + break; + case CG6_FHC: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.cg6.fhc); + break; + case CG6_THC: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.cg6.thc); + break; + case CG6_BTREGS: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.cg6.bt); + break; + case CG6_DHC: + map_size = PAGE_SIZE * 40; + map_offset = get_phys ((unsigned long)fb->info.cg6.dhc); + break; + case CG6_ROM: + map_size = PAGE_SIZE * 16; + map_offset = get_phys ((unsigned long)fb->info.cg6.rom); + break; + case CG6_RAM: + map_size = size-page; + map_offset = get_phys ((unsigned long) fb->base); + if (map_size < fb->type.fb_size) + map_size = fb->type.fb_size; + break; + default: + map_size = 0; + break; + } + if (!map_size){ + page += PAGE_SIZE; + continue; + } + if (page + map_size > size) + map_size = size - page; + r = io_remap_page_range (vma->vm_start+page, + map_offset, + map_size, vma->vm_page_prot, + fb->space); + if (r) return -EAGAIN; + page += map_size; + } + vma->vm_inode = inode; + inode->i_count++; + return 0; +} + +static void +cg6_loadcmap (fbinfo_t *fb, int index, int count) +{ + struct bt_regs *bt = fb->info.cg6.bt; + int i; + + bt->addr = index << 24; + for (i = index; count--; i++){ + bt->color_map = fb->color_map CM(i,0) << 24; + bt->color_map = fb->color_map CM(i,1) << 24; + bt->color_map = fb->color_map CM(i,2) << 24; + } +} + +static void +cg6_setcursormap (fbinfo_t *fb, unsigned char *red, + unsigned char *green, + unsigned char *blue) +{ + struct bt_regs *bt = fb->info.cg6.bt; + + bt->addr = 1 << 24; + bt->cursor = red[0] << 24; + bt->cursor = green[0] << 24; + bt->cursor = blue[0] << 24; + bt->addr = 3 << 24; + bt->cursor = red[1] << 24; + bt->cursor = green[1] << 24; + bt->cursor = blue[1] << 24; +} + +/* Load cursor information */ +static void +cg6_setcursor (fbinfo_t *fb) +{ + uint v; + struct cg_cursor *c = &fb->cursor; + + 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); + } + fb->info.cg6.thc->thc_cursxy = v; +} + +/* Set cursor shape */ +static void +cg6_setcurshape (fbinfo_t *fb) +{ + struct cg6_thc *thc = fb->info.cg6.thc; + int i; + + for (i = 0; i < 32; i++){ + thc->thc_cursmask [i] = fb->cursor.bits[0][i]; + thc->thc_cursbits [i] = fb->cursor.bits[1][i]; + } +} + +static void +cg6_blank (fbinfo_t *fb) +{ + fb->info.cg6.thc->thc_misc &= ~CG6_THC_MISC_VIDEO; +} + +static void +cg6_unblank (fbinfo_t *fb) +{ + fb->info.cg6.thc->thc_misc |= CG6_THC_MISC_VIDEO; +} + +void +cg6_reset (fbinfo_t *fb) +{ + struct cg6_info *cg6 = &(fb->info.cg6); + unsigned int rev, conf; + + if (fb->setcursor) + sun_hw_hide_cursor (); + /* Turn off stuff in the Transform Engine. */ + cg6->tec->tec_matrix = 0; + cg6->tec->tec_clip = 0; + cg6->tec->tec_vdc = 0; + + /* Take care of bugs in old revisions. */ + rev = (*(cg6->fhc) >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK; + if (rev < 5) { + conf = (*(cg6->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; + *(cg6->fhc) = conf; + } + + /* Set things in the FBC. */ + cg6->fbc->mode &= ~(CG6_FBC_BLIT_MASK | CG6_FBC_MODE_MASK | + CG6_FBC_DRAW_MASK | CG6_FBC_BWRITE0_MASK | + CG6_FBC_BWRITE1_MASK | CG6_FBC_BREAD_MASK | + CG6_FBC_BDISP_MASK); + cg6->fbc->mode |= (CG6_FBC_BLIT_SRC | CG6_FBC_MODE_COLOR8 | + CG6_FBC_DRAW_RENDER | CG6_FBC_BWRITE0_ENABLE | + CG6_FBC_BWRITE1_DISABLE | CG6_FBC_BREAD_0 | + CG6_FBC_BDISP_0); + cg6->fbc->clip = 0; + cg6->fbc->offx = 0; + cg6->fbc->offy = 0; + cg6->fbc->clipminx = 0; + cg6->fbc->clipminy = 0; + cg6->fbc->clipmaxx = fb->type.fb_width - 1; + cg6->fbc->clipmaxy = fb->type.fb_height - 1; + /* Enable cursor in Brooktree DAC. */ + cg6->bt->addr = 0x06 << 24; + cg6->bt->control |= 0x03 << 24; +} + +__initfunc(void cg6_setup (fbinfo_t *fb, int slot, u32 cg6, int cg6_io)) +{ + struct cg6_info *cg6info; + unsigned int rev, cpu, conf; + + printk ("cgsix%d at 0x%8.8x ", slot, cg6); + + /* Fill in parameters we left out */ + fb->type.fb_cmsize = 256; + fb->mmap = cg6_mmap; + fb->loadcmap = cg6_loadcmap; + fb->reset = cg6_reset; + fb->blank = cg6_blank; + fb->unblank = cg6_unblank; + fb->setcursor = cg6_setcursor; + fb->setcursormap = cg6_setcursormap; + fb->setcurshape = cg6_setcurshape; + fb->postsetup = sun_cg_postsetup; + fb->blitc = cg6_blitc; + fb->setw = cg6_setw; + fb->cpyw = cg6_cpyw; + + cg6info = (struct cg6_info *) &fb->info.cg6; + + /* Map the hardware registers */ + cg6info->bt = sparc_alloc_io (cg6+CG6_BROOKTREE_OFFSET, 0, + sizeof (struct bt_regs), "cgsix_dac", cg6_io, 0); + cg6info->fhc = sparc_alloc_io (cg6+CG6_FHC_OFFSET, 0, + sizeof (int), "cgsix_fhc", cg6_io, 0); + cg6info->thc = sparc_alloc_io (cg6+CG6_THC_OFFSET, 0, + sizeof (struct cg6_thc), "cgsix_thc", cg6_io, 0); + cg6info->tec = sparc_alloc_io (cg6+CG6_TEC_OFFSET, 0, + sizeof (struct cg6_tec), "cgsix_tec", cg6_io, 0); + cg6info->dhc = sparc_alloc_io (cg6+CG6_DHC_OFFSET, 0, + 0x40000, "cgsix_dhc", cg6_io, 0); + cg6info->fbc = sparc_alloc_io (cg6+CG6_FBC_OFFSET, 0, + 0x1000, "cgsix_fbc", cg6_io, 0); + cg6info->rom = sparc_alloc_io (cg6+CG6_ROM_OFFSET, 0, + 0x10000, "cgsix_rom", cg6_io, 0); + if (!fb->base) { + fb->base = (unsigned long) + sparc_alloc_io (cg6+CG6_RAM_OFFSET, 0, + fb->type.fb_size, "cgsix_ram", cg6_io, 0); + } + if (slot == sun_prom_console_id) + fb_restore_palette = cg6_restore_palette; + + /* Initialize Brooktree DAC */ + cg6info->bt->addr = 0x04 << 24; /* color planes */ + cg6info->bt->control = 0xff << 24; + cg6info->bt->addr = 0x05 << 24; + cg6info->bt->control = 0x00 << 24; + cg6info->bt->addr = 0x06 << 24; /* overlay plane */ + cg6info->bt->control = 0x73 << 24; + cg6info->bt->addr = 0x07 << 24; + cg6info->bt->control = 0x00 << 24; + + printk("TEC Rev %x ", + (cg6info->thc->thc_misc >> CG6_THC_MISC_REV_SHIFT) & + CG6_THC_MISC_REV_MASK); + + /* Get FHC Revision */ + conf = *(cg6info->fhc); + + cpu = conf & CG6_FHC_CPU_MASK; + printk("CPU "); + if (cpu == CG6_FHC_CPU_SPARC) + printk("sparc "); + else if (cpu == CG6_FHC_CPU_68020) + printk("68020 "); + else + printk("386 "); + + rev = conf >> CG6_FHC_REV_SHIFT & CG6_FHC_REV_MASK; + printk("Rev %x\n", rev); + + if (slot && sun_prom_console_id == slot) + return; + + /* Reset the cg6 */ + cg6_reset(fb); + + if (!slot) { + /* Enable Video */ + cg6_unblank(fb); + } else { + cg6_blank(fb); + } +} + +extern unsigned char vga_font[]; + +#define GX_BLITC_START(attr) \ + { \ + register struct cg6_fbc *gx = fbinfo[0].info.cg6.fbc; \ + register uint i; \ + do { \ + i = gx->s; \ + } while (i & 0x10000000); \ + gx->fg = attr & 0xf; \ + gx->bg = (attr>>4); \ + gx->mode = 0x140000; \ + gx->alu = 0xe880fc30; \ + gx->pixelm = ~(0); \ + gx->s = 0; \ + gx->clip = 0; \ + gx->pm = 0xff; +#define GX_BLITC_BODY4(count,x,y,start,action) \ + while (count >= 4) { \ + count -= 4; \ + gx->incx = 0; \ + gx->incy = 1; \ + gx->x0 = x; \ + gx->x1 = (x += 32) - 1; \ + gx->y0 = y; \ + start; \ + for (i = 0; i < CHAR_HEIGHT; i++) { \ + action; \ + } \ + } +#define GX_BLITC_BODY1(x,y,action) \ + gx->incx = 0; \ + gx->incy = 1; \ + gx->x0 = x; \ + gx->x1 = (x += 8) - 1; \ + gx->y0 = y; \ + for (i = 0; i < CHAR_HEIGHT; i++) { \ + action; \ + } +#define GX_BLITC_END \ + } + +static void cg6_blitc(unsigned short charattr, int xoff, int yoff) +{ + unsigned char attrib = CHARATTR_TO_SUNCOLOR(charattr); + unsigned char *p = &vga_font[((unsigned char)charattr) << 4]; + GX_BLITC_START(attrib) + GX_BLITC_BODY1(xoff, yoff, gx->font=((*p++) << 24)) + GX_BLITC_END +} + +static void cg6_setw(int xoff, int yoff, unsigned short c, int count) +{ + unsigned char attrib = CHARATTR_TO_SUNCOLOR(c); + unsigned char *p = &vga_font[((unsigned char)c) << 4]; + register unsigned char *q; + register uint l; + GX_BLITC_START(attrib) + if (count >= 4) { + GX_BLITC_BODY4(count, xoff, yoff, q = p, + l = *q++; + l |= l << 8; + l |= l << 16; + gx->font=l) + } + while (count) { + count--; + q = p; + GX_BLITC_BODY1(xoff, yoff, gx->font=((*q++) << 24)); + } + GX_BLITC_END +} + +static void cg6_cpyw(int xoff, int yoff, unsigned short *p, int count) +{ + unsigned char attrib = CHARATTR_TO_SUNCOLOR(*p); + unsigned char *p1, *p2, *p3, *p4; + GX_BLITC_START(attrib) + if (count >= 4) { + GX_BLITC_BODY4(count, xoff, yoff, + p1 = &vga_font[((unsigned char)*p++) << 4]; + p2 = &vga_font[((unsigned char)*p++) << 4]; + p3 = &vga_font[((unsigned char)*p++) << 4]; + p4 = &vga_font[((unsigned char)*p++) << 4], + gx->font=((uint)*p4++) | ((((uint)*p3++) | ((((uint)*p2++) | (((uint)*p1++) << 8)) << 8)) << 8)) + } + while (count) { + count--; + p1 = &vga_font[((unsigned char)*p++) << 4]; + GX_BLITC_BODY1(xoff, yoff, gx->font=((*p1++) << 24)); + } + GX_BLITC_END +} + +#define GX_FILL_START(attr) \ + { \ + register struct cg6_fbc *gx = fbinfo[0].info.cg6.fbc; \ + register uint i; \ + do { \ + i = gx->s; \ + } while (i & 0x10000000); \ + gx->fg = attr & 0xf; \ + gx->bg = 0; \ + gx->pixelm = ~(0); \ + gx->s = 0; \ + gx->alu = 0xea80ff00; \ + gx->pm = ~(0); \ + gx->clip = 0; +#define GX_FILL_END \ + } + +#if 0 +static void cg6_fill(int attrib, int count, int *boxes) +{ + register int r; + + attrib = 5; + GX_FILL_START(attrib) + while (count-- > 0) { + gx->arecty = boxes[1]; + gx->arectx = boxes[0]; + gx->arecty = boxes[3]; + gx->arecty = boxes[2]; + boxes += 4; + do { + r = gx->draw; + } while (r < 0 && (r & 0x20000000) ); + } + GX_FILL_END +} +#endif diff --git a/drivers/sbus/char/cgthree.c b/drivers/sbus/char/cgthree.c new file mode 100644 index 000000000..ac1265c61 --- /dev/null +++ b/drivers/sbus/char/cgthree.c @@ -0,0 +1,265 @@ +/* $Id: cgthree.c,v 1.18 1997/04/16 17:51:09 jj Exp $ + * cgtree.c: cg3 frame buffer driver + * + * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) + * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) + * + * Support for cgRDI added, Nov/96, jj. + */ + +#include <linux/kd.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/proc_fs.h> + +#include <asm/sbus.h> +#include <asm/io.h> +#include <asm/fbio.h> +#include <asm/pgtable.h> + +#include "../../char/vt_kern.h" +#include "../../char/selection.h" +#include "../../char/console_struct.h" +#include "fb.h" +#include "cg_common.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 + + +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; +}; + +/* 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 */ +static void +cg3_loadcmap (fbinfo_t *fb, int index, int count) +{ + struct bt_regs *bt = &fb->info.cg3.regs->cmap; + int *i, steps; + + i = (((int *)fb->color_map) + D4M3(index)); + steps = D4M3(index+count-1) - D4M3(index)+3; + + *(volatile u8 *)&bt->addr = (u8)D4M4(index); + while (steps--) + bt->color_map = *i++; +} + +/* The cg3 is presumed to emulate a cg4, I guess older programs will want that + * addresses above 0x4000000 are for cg3, below that it's cg4 emulation. + */ +static int +cg3_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, + long base, fbinfo_t *fb) +{ + uint size, page, r, map_size; + uint map_offset = 0; + + size = vma->vm_end - vma->vm_start; + if (vma->vm_offset & ~PAGE_MASK) + return -ENXIO; + + /* To stop the swapper from even considering these pages */ + vma->vm_flags |= FB_MMAP_VM_FLAGS; + + /* Each page, see which map applies */ + for (page = 0; page < size; ){ + switch (vma->vm_offset+page){ + case CG3_MMAP_OFFSET: + map_size = size-page; + map_offset = get_phys ((unsigned long) fb->base); + if (map_size > fb->type.fb_size) + map_size = fb->type.fb_size; + break; + default: + map_size = 0; + break; + } + if (!map_size){ + page += PAGE_SIZE; + continue; + } + if (page + map_size > size) + map_size = size - page; + r = io_remap_page_range (vma->vm_start+page, + map_offset, + map_size, vma->vm_page_prot, + fb->space); + if (r) return -EAGAIN; + page += map_size; + } + vma->vm_inode = inode; + inode->i_count++; + return 0; +} + +static void +cg3_blank (fbinfo_t *fb) +{ + fb->info.cg3.regs->control &= ~CG3_CR_ENABLE_VIDEO; +} + +static void +cg3_unblank (fbinfo_t *fb) +{ + fb->info.cg3.regs->control |= CG3_CR_ENABLE_VIDEO; +} + + +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 +}; + + +__initfunc(void cg3_setup (fbinfo_t *fb, int slot, u32 cg3, int cg3_io, + struct linux_sbus_device *sbdp)) +{ + struct cg3_info *cg3info = (struct cg3_info *) &fb->info.cg3; + + if (strstr (sbdp->prom_name, "cgRDI")) { + char buffer[40]; + char *p; + int w, h; + + prom_getstring (sbdp->prom_node, "params", + buffer, sizeof(buffer)); + if (*buffer) { + w = simple_strtoul (buffer, &p, 10); + if (w && *p == 'x') { + h = simple_strtoul (p + 1, &p, 10); + if (h && *p == '-') { + fb->type.fb_width = w; + fb->type.fb_height = h; + } + } + } + printk ("cgRDI%d at 0x%8.8x\n", slot, cg3); + cg3info->cgrdi = 1; + } else { + printk ("cgthree%d at 0x%8.8x\n", slot, cg3); + cg3info->cgrdi = 0; + } + + /* Fill in parameters we left out */ + fb->type.fb_cmsize = 256; + fb->mmap = cg3_mmap; + fb->loadcmap = cg3_loadcmap; + fb->postsetup = sun_cg_postsetup; + fb->ioctl = 0; /* no special ioctls */ + fb->reset = 0; + fb->blank = cg3_blank; + fb->unblank = cg3_unblank; + + /* Map the card registers */ + cg3info->regs = sparc_alloc_io (cg3+CG3_REGS, 0, + sizeof (struct cg3_regs),"cg3_regs", cg3_io, 0); + + if (!prom_getbool(sbdp->prom_node, "width")) { + /* Ugh, broken PROM didn't initialize us. + * Let's deal with this ourselves. + */ + u8 status, mon; + enum cg3_type type; + u8 *p; + + if (cg3info->cgrdi) { + type = CG3_RDI; + } else { + status = cg3info->regs->status; + 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; /* fool gcc. */ + } + } + + for (p = cg3_regvals[type]; *p; p += 2) + ((u8 *)cg3info->regs)[p[0]] = p[1]; + + for (p = cg3_dacvals; *p; p += 2) { + *(volatile u8 *)&cg3info->regs->cmap.addr = p[0]; + *(volatile u8 *)&cg3info->regs->cmap.control = p[1]; + } + } + + if (!fb->base){ + fb->base=(unsigned long) sparc_alloc_io (cg3+CG3_RAM, 0, + fb->type.fb_size, "cg3_ram", cg3_io, 0); + } +} + diff --git a/drivers/sbus/char/creator.c b/drivers/sbus/char/creator.c new file mode 100644 index 000000000..193a26190 --- /dev/null +++ b/drivers/sbus/char/creator.c @@ -0,0 +1,98 @@ +/* + * creator.c: Linux/Sun Ultra Creator console support. + * + * Copyright (C) 1997 MIguel de Icaza (miguel@nuclecu.unam.mx) + * + */ +#include <linux/kd.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/proc_fs.h> + +#include <asm/sbus.h> +#include <asm/io.h> +#include <asm/fbio.h> +#include <asm/pgtable.h> +#include <asm/uaccess.h> + +#include "../../char/vt_kern.h" +#include "../../char/selection.h" +#include "../../char/console_struct.h" +#include "fb.h" + +__initfunc(void creator_setup (fbinfo_t *fb, int slot, int con_node, unsigned long creator, int creator_io)) +{ + uint bases [2]; + unsigned long *p; + + if (!creator) { + prom_getproperty (con_node, "address", (char *) &bases[0], 4); + prom_printf ("Bases: %x %x\n", bases [0], bases [1]); + p = (unsigned long *) creator = bases[0]; + fb->base = creator; + fb->base = 0xff168000; + } + + fb->type.fb_cmsize = 256; + fb->mmap = 0; + fb->loadcmap = 0; + fb->setcursor = 0; + fb->setcursormap = 0; + fb->setcurshape = 0; + fb->ioctl = 0; + fb->switch_from_graph = 0; + fb->postsetup = sun_cg_postsetup; + fb->reset = 0; + fb->blank = 0; + fb->unblank = 0; + fb->type.fb_depth = 8; +} +/* + * creator.c: Linux/Sun Ultra Creator console support. + * + * Copyright (C) 1997 MIguel de Icaza (miguel@nuclecu.unam.mx) + * + */ +#include <linux/kd.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/proc_fs.h> + +#include <asm/sbus.h> +#include <asm/io.h> +#include <asm/fbio.h> +#include <asm/pgtable.h> +#include <asm/uaccess.h> + +#include "../../char/vt_kern.h" +#include "../../char/selection.h" +#include "../../char/console_struct.h" +#include "fb.h" + +__initfunc(void creator_setup (fbinfo_t *fb, int slot, int con_node, unsigned long creator, int creator_io)) +{ + uint bases [2]; + unsigned long *p; + + if (!creator) { + prom_getproperty (con_node, "address", (char *) &bases[0], 4); + prom_printf ("Bases: %x %x\n", bases [0], bases [1]); + p = (unsigned long *) creator = bases[0]; + fb->base = creator; + fb->base = 0xff168000; + } + + fb->type.fb_cmsize = 256; + fb->mmap = 0; + fb->loadcmap = 0; + fb->setcursor = 0; + fb->setcursormap = 0; + fb->setcurshape = 0; + fb->ioctl = 0; + fb->switch_from_graph = 0; + fb->postsetup = sun_cg_postsetup; + fb->reset = 0; + fb->blank = 0; + fb->unblank = 0; + fb->type.fb_depth = 8; +} diff --git a/drivers/sbus/char/fb.h b/drivers/sbus/char/fb.h new file mode 100644 index 000000000..029eac81b --- /dev/null +++ b/drivers/sbus/char/fb.h @@ -0,0 +1,210 @@ +/* $Id: fb.h,v 1.26 1997/04/17 02:29:33 miguel Exp $ + * fb.h: contains the definitions of the structures that various sun + * frame buffer can use to do console driver stuff. + * + * (C) 1996 Dave Redman (djhr@tadpole.co.uk) + * (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) + * (C) 1996 David Miller (davem@rutgers.edu) + * (C) 1996 Peter Zaitcev (zaitcev@lab.ipmce.su) + * (C) 1996 Eddie C. Dost (ecd@skynet.be) + * (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#ifndef __SPARC_FB_H_ +#define __SPARC_FB_H_ + +#include <linux/init.h> + +#define FRAME_BUFFERS 8 +#define CHAR_WIDTH 8 +#define CHAR_HEIGHT 16 + +/* Change this if we run into problems if the kernel want's to free or + * use our frame buffer pages, never seen it though. + */ +#define FB_MMAP_VM_FLAGS (VM_SHM| VM_LOCKED) + +#undef color + +/* cursor status, kernel tracked copy */ +struct cg_cursor { + short enable; /* cursor is enabled */ + 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][32]; /* space for mask & image bits */ + char color [6]; /* cursor colors */ +}; + +struct cg6_info { + struct bt_regs *bt; /* color control */ + struct cg6_fbc *fbc; + unsigned int *fhc; + struct cg6_tec *tec; + struct cg6_thc *thc; + void *dhc; + unsigned char *rom; +}; + +struct tcx_info { + struct bt_regs *bt; /* color control */ + struct tcx_tec *tec; + struct tcx_thc *thc; + void *tcx_cplane; + int tcx_sizes[13]; + long tcx_offsets[13]; + int lowdepth; +}; + +struct leo_info { + struct leo_cursor *cursor; + struct leo_lc_ss0_krn *lc_ss0_krn; + struct leo_lc_ss0_usr *lc_ss0_usr; + struct leo_lc_ss1_krn *lc_ss1_krn; + struct leo_lc_ss1_usr *lc_ss1_usr; + struct leo_ld_ss0 *ld_ss0; + struct leo_ld_ss1 *ld_ss1; + struct leo_ld_gbl *ld_gbl; + struct leo_lx_krn *lx_krn; + u32 *cluts[3]; + u8 *xlut; + unsigned long offset; +}; + +struct bwtwo_info { + struct bwtwo_regs *regs; +}; + +struct cg3_info { + struct cg3_regs *regs; /* brooktree (color) registers, and more */ + int cgrdi; /* 1 if this is a cgRDI */ +}; + +struct cg14_info { + struct cg14_regs *regs; + struct cg14_cursor *cursor_regs; + struct cg14_dac *dac; + struct cg14_xlut *xlut; + struct cg14_clut *clut; + int ramsize; + int video_mode; +}; + +typedef union +{ + unsigned int bt[8]; + unsigned char ibm[8]; +} dacptr; + +struct weitek_info +{ + int p9000; /* p9000? or p9100 */ + dacptr *dac; /* dac structures */ + unsigned int fbsize; /* size of frame buffer */ +}; + +/* Array holding the information for the frame buffers */ +typedef struct fbinfo { + union { + struct bwtwo_info bwtwo; + struct cg3_info cg3; + struct cg6_info cg6; + struct cg14_info cg14; + struct tcx_info tcx; + struct leo_info leo; + } info; /* per frame information */ + int space; /* I/O space this card resides in */ + int blanked; /* true if video blanked */ + int open; /* is this fb open? */ + int mmaped; /* has this fb been mmapped? */ + int vtconsole; /* virtual console where it is opened */ + long base; /* frame buffer base */ + struct fbtype type; /* frame buffer type */ + int real_type; /* real frame buffer FBTYPE* */ + int emulations[4]; /* possible emulations (-1 N/A) */ + int prom_node; /* node of the device in prom tree */ + int base_depth; /* depth of fb->base piece */ + struct cg_cursor cursor; /* kernel state of hw cursor */ + int (*mmap)(struct inode *, struct file *, struct vm_area_struct *, + long fb_base, struct fbinfo *); + void (*loadcmap)(struct fbinfo *fb, int index, int count); + void (*blank)(struct fbinfo *fb); + void (*unblank)(struct fbinfo *fb); + int (*ioctl)(struct inode *, struct file *, uint, unsigned long, + struct fbinfo *); + void (*reset)(struct fbinfo *fb); + void (*switch_from_graph)(void); + void (*setcursor)(struct fbinfo *); + void (*setcurshape)(struct fbinfo *); + void (*setcursormap)(struct fbinfo *, unsigned char *, + unsigned char *, unsigned char *); + unsigned long (*postsetup)(struct fbinfo *, unsigned long); + void (*blitc)(unsigned short, int, int); + void (*setw)(int, int, unsigned short, int); + void (*cpyw)(int, int, unsigned short *, int); + void (*fill)(int, int, int *); + unsigned char *color_map; + struct openpromfs_dev proc_entry; +} fbinfo_t; + +#define CM(i, j) [3*(i)+(j)] + +extern unsigned char reverse_color_table[]; + +#define CHARATTR_TO_SUNCOLOR(attr) \ + ((reverse_color_table[(attr) >> 12] << 4) | \ + reverse_color_table[((attr) >> 8) & 0x0f]) + +extern fbinfo_t *fbinfo; +extern int fbinfos; + +struct { + char *name; /* prom name */ + int width, height; /* prefered w,h match */ + void (*fbtype)(fbinfo_t *); /* generic device type */ + /* device specific init routine */ + unsigned long (*fbinit)(fbinfo_t *fbinfo, unsigned int addr); +} fb_entry; + +extern int fb_init(void); + +extern void (*fb_restore_palette)(fbinfo_t *fbinfo); +extern void (*fb_hide_cursor)(int cursor_pos); +extern void (*fb_set_cursor)(int oldpos, int idx); +extern void (*fb_clear_screen)( void ); +extern void (*fb_blitc)(unsigned char *, int, unsigned int *, unsigned int); +extern void (*fb_font_init)(unsigned char *font); +/* All framebuffers are likely to require this info */ + +/* Screen dimensions and color depth. */ +extern int con_depth, con_width; +extern int con_height, con_linebytes; +extern int ints_per_line; + +/* used in the mmap routines */ +extern unsigned int get_phys (unsigned long addr); +extern int get_iospace (unsigned long addr); +extern void render_screen(void); + +extern void sun_hw_hide_cursor(void); +extern void sun_hw_set_cursor(int, int); +extern int sun_hw_scursor(struct fbcursor *,fbinfo_t *); +extern int sun_hw_cursor_shown; +extern int sun_prom_console_id; + +extern unsigned long sun_cg_postsetup(fbinfo_t *, unsigned long); + +#define FB_DEV(x) (MINOR(x) / 32) + +extern void cg3_setup (fbinfo_t *, int, u32, int, struct linux_sbus_device *); +extern void cg6_setup (fbinfo_t *, int, u32, int); +extern void cg14_setup (fbinfo_t *, int, int, u32, int); +extern void bwtwo_setup (fbinfo_t *, int, u32, int, + struct linux_sbus_device *); +extern void leo_setup (fbinfo_t *, int, u32, int); +extern void tcx_setup (fbinfo_t *, int, int, u32, struct linux_sbus_device *); +extern void creator_setup (fbinfo_t *, int, int, unsigned long, int); +extern int io_remap_page_range(unsigned long from, unsigned long offset, unsigned long size, pgprot_t prot, int space); + +#endif __SPARC_FB_H_ diff --git a/drivers/sbus/char/leo.c b/drivers/sbus/char/leo.c new file mode 100644 index 000000000..b3ec23867 --- /dev/null +++ b/drivers/sbus/char/leo.c @@ -0,0 +1,692 @@ +/* $Id: leo.c,v 1.15 1997/04/14 17:04:54 jj Exp $ + * leo.c: SUNW,leo 24/8bit frame buffer driver + * + * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 Michal Rehacek (Michal.Rehacek@st.mff.cuni.cz) + */ + +#include <linux/kd.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/proc_fs.h> + +#include <asm/sbus.h> +#include <asm/io.h> +#include <asm/fbio.h> +#include <asm/pgtable.h> +#include <asm/delay.h> +#include <asm/uaccess.h> + +#include "../../char/vt_kern.h" +#include "../../char/selection.h" +#include "../../char/console_struct.h" +#include "fb.h" +#include "cg_common.h" + +#define LEO_OFF_LC_SS0_KRN 0x00200000 +#define LEO_OFF_LC_SS0_USR 0x00201000 +#define LEO_OFF_LC_SS1_KRN 0x01200000 +#define LEO_OFF_LC_SS1_USR 0x01201000 +#define LEO_OFF_LD_SS0 0x00400000 +#define LEO_OFF_LD_SS1 0x01400000 +#define LEO_OFF_LD_GBL 0x00401000 +#define LEO_OFF_LX_KRN 0x00600000 +#define LEO_OFF_LX_CURSOR 0x00601000 +#define LEO_OFF_SS0 0x00800000 +#define LEO_OFF_SS1 0x01800000 +#define LEO_OFF_UNK 0x00602000 +#define LEO_OFF_UNK2 0x00000000 + +#define LEO_CUR_ENABLE 0x00000080 +#define LEO_CUR_UPDATE 0x00000030 +#define LEO_CUR_PROGRESS 0x00000006 +#define LEO_CUR_UPDATECMAP 0x00000003 + +#define LEO_CUR_TYPE_MASK 0x00000000 +#define LEO_CUR_TYPE_IMAGE 0x00000020 +#define LEO_CUR_TYPE_CMAP 0x00000050 + +struct leo_cursor { + u8 xxx0[16]; + volatile u32 cur_type; + volatile u32 cur_misc; + volatile u32 cur_cursxy; + volatile u32 cur_data; +}; + +#define LEO_KRN_TYPE_CLUT0 0x00001000 +#define LEO_KRN_TYPE_CLUT1 0x00001001 +#define LEO_KRN_TYPE_CLUT2 0x00001002 +#define LEO_KRN_TYPE_WID 0x00001003 +#define LEO_KRN_TYPE_UNK 0x00001006 +#define LEO_KRN_TYPE_VIDEO 0x00002003 +#define LEO_KRN_TYPE_CLUTDATA 0x00004000 +#define LEO_KRN_CSR_ENABLE 0x00000008 +#define LEO_KRN_CSR_PROGRESS 0x00000004 +#define LEO_KRN_CSR_UNK 0x00000002 +#define LEO_KRN_CSR_UNK2 0x00000001 + +struct leo_lx_krn { + volatile u32 krn_type; + volatile u32 krn_csr; + volatile u32 krn_value; +}; + +struct leo_lc_ss0_krn { + volatile u32 misc; + u8 xxx0[0x800-4]; + volatile u32 rev; +}; + +struct leo_lc_ss0_usr { + volatile u32 csr; + volatile u32 attrs; + volatile u32 fontc; + volatile u32 fontc2; + volatile u32 extent; + volatile u32 src; + u32 xxx1[1]; + volatile u32 copy; + volatile u32 fill; +}; + +struct leo_lc_ss1_krn { + u8 unknown; +}; + +struct leo_lc_ss1_usr { + u8 unknown; +}; + +struct leo_ld_ss0 { + u8 xxx0[0xe00]; + u32 xxx1[2]; + volatile u32 unk; + u32 xxx2[1]; + volatile u32 unk2; + volatile u32 unk3; + u32 xxx3[2]; + volatile u32 fg; + volatile u32 bg; + u8 xxx4[0x05c]; + volatile u32 planemask; + volatile u32 rop; +}; + +#define LEO_SS1_MISC_ENABLE 0x00000001 +#define LEO_SS1_MISC_STEREO 0x00000002 +struct leo_ld_ss1 { + u8 xxx0[0xef4]; + volatile u32 ss1_misc; +}; + +struct leo_ld_gbl { + u8 unknown; +}; + +static void leo_blitc(unsigned short, int, int); +static void leo_setw(int, int, unsigned short, int); +static void leo_cpyw(int, int, unsigned short *, int); +static void leo_fill(int, int, int *); + +static void +leo_restore_palette (fbinfo_t *fb) +{ + fb->info.leo.ld_ss1->ss1_misc &= ~(LEO_SS1_MISC_ENABLE); +} + +/* Ugh: X wants to mmap a bunch of cute stuff at the same time :-( */ +/* So, we just mmap the things that are being asked for */ +static int +leo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, + long base, fbinfo_t *fb) +{ + uint size, page, r, map_size = 0; + uint map_offset = 0; + + size = vma->vm_end - vma->vm_start; + if (vma->vm_offset & ~PAGE_MASK) + return -ENXIO; + + /* To stop the swapper from even considering these pages */ + vma->vm_flags |= FB_MMAP_VM_FLAGS; + + /* Each page, see which map applies */ + for (page = 0; page < size; ){ + switch (vma->vm_offset+page){ + case LEO_SS0_MAP: + map_size = 0x800000; + map_offset = get_phys ((unsigned long)fb->base); + break; + case LEO_LC_SS0_USR_MAP: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.leo.lc_ss0_usr); + break; + case LEO_LD_SS0_MAP: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.leo.ld_ss0); + break; + case LEO_LX_CURSOR_MAP: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.leo.cursor); + break; + case LEO_SS1_MAP: + map_size = 0x800000; + map_offset = fb->info.leo.offset + LEO_OFF_SS1; + break; + case LEO_LC_SS1_USR_MAP: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.leo.lc_ss1_usr); + break; + case LEO_LD_SS1_MAP: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.leo.ld_ss1); + break; + case LEO_UNK_MAP: + map_size = PAGE_SIZE; + map_offset = fb->info.leo.offset + LEO_OFF_UNK; + break; + case LEO_LX_KRN_MAP: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.leo.lx_krn); + break; + case LEO_LC_SS0_KRN_MAP: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.leo.lc_ss0_krn); + break; + case LEO_LC_SS1_KRN_MAP: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.leo.lc_ss1_krn); + break; + case LEO_LD_GBL_MAP: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.leo.ld_gbl); + break; + case LEO_UNK2_MAP: + map_size = 0x100000; + map_offset = fb->info.leo.offset + LEO_OFF_UNK2; + break; + } + if (!map_size){ + page += PAGE_SIZE; + continue; + } + if (page + map_size > size) + map_size = size - page; + r = io_remap_page_range (vma->vm_start+page, + map_offset, + map_size, vma->vm_page_prot, + fb->space); + if (r) return -EAGAIN; + page += map_size; + } + vma->vm_inode = inode; + inode->i_count++; + return 0; +} + +static void +leo_setcursormap (fbinfo_t *fb, unsigned char *red, + unsigned char *green, + unsigned char *blue) +{ + struct leo_cursor *l = fb->info.leo.cursor; + int i; + + for (i = 0; (l->cur_misc & LEO_CUR_PROGRESS) && i < 300000; i++) + udelay (1); /* Busy wait at most 0.3 sec */ + if (i == 300000) return; /* Timed out - should we print some message? */ + l->cur_type = LEO_CUR_TYPE_CMAP; + l->cur_data = (red[0] | (green[0]<<8) | (blue[0]<<16)); + l->cur_data = (red[1] | (green[1]<<8) | (blue[1]<<16)); + l->cur_misc = LEO_CUR_UPDATECMAP; +} + +/* Load cursor information */ +static void +leo_setcursor (fbinfo_t *fb) +{ + struct cg_cursor *c = &fb->cursor; + struct leo_cursor *l = fb->info.leo.cursor; + + l->cur_misc &= ~LEO_CUR_ENABLE; + l->cur_cursxy = ((c->cpos.fbx - c->chot.fbx) & 0x7ff) + |(((c->cpos.fby - c->chot.fby) & 0x7ff) << 11); + l->cur_misc |= LEO_CUR_UPDATE; + if (c->enable) + l->cur_misc |= LEO_CUR_ENABLE; +} + +/* Set cursor shape */ +static void +leo_setcurshape (fbinfo_t *fb) +{ + int i, j, k; + u32 m, n, mask; + struct leo_cursor *l = fb->info.leo.cursor; + + l->cur_misc &= ~LEO_CUR_ENABLE; + for (k = 0; k < 2; k ++) { + l->cur_type = (k * LEO_CUR_TYPE_IMAGE); /* LEO_CUR_TYPE_MASK is 0 */ + for (i = 0; i < 32; i++) { + mask = 0; + m = fb->cursor.bits[k][i]; + /* mask = m with reversed bit order */ + for (j = 0, n = 1; j < 32; j++, n <<= 1) + if (m & n) + mask |= (0x80000000 >> j); + l->cur_data = mask; + } + } + l->cur_misc |= LEO_CUR_ENABLE; +} + +static void +leo_blank (fbinfo_t *fb) +{ + fb->info.leo.lx_krn->krn_type = LEO_KRN_TYPE_VIDEO; + fb->info.leo.lx_krn->krn_csr &= ~LEO_KRN_CSR_ENABLE; +} + +static void +leo_unblank (fbinfo_t *fb) +{ + fb->info.leo.lx_krn->krn_type = LEO_KRN_TYPE_VIDEO; + if (!(fb->info.leo.lx_krn->krn_csr & LEO_KRN_CSR_ENABLE)) + fb->info.leo.lx_krn->krn_csr |= LEO_KRN_CSR_ENABLE; +} + +static int leo_wait (struct leo_lx_krn *lx_krn) +{ + int i; + for (i = 0; (lx_krn->krn_csr & LEO_KRN_CSR_PROGRESS) && i < 300000; i++) + udelay (1); /* Busy wait at most 0.3 sec */ + if (i == 300000) return -EFAULT; /* Timed out - should we print some message? */ + return 0; +} + +static int +leo_wid_get (fbinfo_t *fb, struct fb_wid_list *wl) +{ + struct leo_lx_krn *lx_krn = fb->info.leo.lx_krn; + struct fb_wid_item *wi; + int i, j; + u32 l; + + lx_krn->krn_type = LEO_KRN_TYPE_WID; + i = leo_wait (lx_krn); + if (i) return i; + lx_krn->krn_csr &= ~LEO_KRN_CSR_UNK2; + lx_krn->krn_csr |= LEO_KRN_CSR_UNK; + lx_krn->krn_type = LEO_KRN_TYPE_WID; + i = leo_wait (lx_krn); + if (i) return i; + for (i = 0, wi = wl->wl_list; i < wl->wl_count; i++, wi++) { + switch (wi->wi_type) { + case FB_WID_DBL_8: j = (wi->wi_index & 0xf) + 0x40; break; + case FB_WID_DBL_24: j = wi->wi_index & 0x3f; break; + default: return -EINVAL; + } + wi->wi_attrs = 0xffff; + lx_krn->krn_type = 0x5800 + j; + l = lx_krn->krn_value; + for (j = 0; j < 32; j++) + wi->wi_values [j] = l; + } + return 0; +} + +static int +leo_wid_put (fbinfo_t *fb, struct fb_wid_list *wl) +{ + struct leo_lx_krn *lx_krn = fb->info.leo.lx_krn; + struct fb_wid_item *wi; + int i, j; + + lx_krn->krn_type = LEO_KRN_TYPE_WID; + i = leo_wait (lx_krn); + if (i) return i; + for (i = 0, wi = wl->wl_list; i < wl->wl_count; i++, wi++) { + switch (wi->wi_type) { + case FB_WID_DBL_8: j = (wi->wi_index & 0xf) + 0x40; break; + case FB_WID_DBL_24: j = wi->wi_index & 0x3f; break; + default: return -EINVAL; + } + lx_krn->krn_type = 0x5800 + j; + lx_krn->krn_value = wi->wi_values[0]; + } + return 0; +} + +static int leo_clutstore (fbinfo_t *fb, int clutid) +{ + int i; + u32 *clut = fb->info.leo.cluts [clutid]; + struct leo_lx_krn *lx_krn = fb->info.leo.lx_krn; + + lx_krn->krn_type = LEO_KRN_TYPE_CLUT0 + clutid; + i = leo_wait (lx_krn); + if (i) return i; + lx_krn->krn_type = LEO_KRN_TYPE_CLUTDATA; + for (i = 0; i < 256; i++) + lx_krn->krn_value = *clut++; /* Throw colors there :)) */ + lx_krn->krn_type = LEO_KRN_TYPE_CLUT0 + clutid; + lx_krn->krn_csr |= (LEO_KRN_CSR_UNK|LEO_KRN_CSR_UNK2); + return 0; +} + +static int leo_clutpost (fbinfo_t *fb, struct leo_clut *lc) +{ + int xlate = 0, i; + u32 *clut; + u8 *xlut = fb->info.leo.xlut; + + switch (lc->clutid) { + case 0: + case 1: + case 2: break; + case 3: return -EINVAL; /* gamma clut - not yet implemented */ + case 4: return -EINVAL; /* degamma clut - not yet implemented */ + default: return -EINVAL; + } + clut = fb->info.leo.cluts [lc->clutid] + lc->offset; + for (i = 0; i < lc->count; i++) + *clut++ = xlate ? + ((xlut[(u8)(lc->red[i])])|(xlut[(u8)(lc->green[i])]<<8)|(xlut[(u8)(lc->blue[i])]<<16)) : + (((u8)(lc->red[i]))|(((u8)(lc->green[i]))<<8)|(((u8)(lc->blue[i]))<<16)); + return leo_clutstore (fb, lc->clutid); +} + +static int leo_clutread (fbinfo_t *fb, struct leo_clut *lc) +{ + int i; + u32 u; + struct leo_lx_krn *lx_krn = fb->info.leo.lx_krn; + + if (lc->clutid >= 3) return -EINVAL; + lx_krn->krn_type = LEO_KRN_TYPE_CLUT0 + lc->clutid; + i = leo_wait (lx_krn); + if (i) return i; + lx_krn->krn_csr &= ~LEO_KRN_CSR_UNK2; + lx_krn->krn_csr |= LEO_KRN_CSR_UNK; + i = leo_wait (lx_krn); + if (i) return i; + lx_krn->krn_type = LEO_KRN_TYPE_CLUTDATA; + for (i = 0; i < lc->offset; i++) + u = lx_krn->krn_value; + for (i = 0; i < lc->count; i++) { + u = lx_krn->krn_value; + lc->red [i] = u; + lc->green [i] = (u >> 8); + lc->blue [i] = (u >> 16); + } + return 0; +} + +static void +leo_loadcmap (fbinfo_t *fb, int index, int count) +{ + u32 *clut = ((u32 *)fb->info.leo.cluts [0]) + index; + int i; + + for (i = index; count--; i++) + *clut++ = ((fb->color_map CM(i,0))) | + ((fb->color_map CM(i,1)) << 8) | + ((fb->color_map CM(i,2)) << 16); + leo_clutstore (fb, 0); +} + +/* Handle leo-specific ioctls */ +static int +leo_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long arg, fbinfo_t *fb) +{ + int i; + + switch (cmd) { + case FBIO_WID_GET: + i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct fb_wid_list)); + if (i) return i; + if (((struct fb_wid_list *)arg)->wl_count != 1 || + !((struct fb_wid_list *)arg)->wl_list) return -EINVAL; + i = verify_area (VERIFY_WRITE, (void *)(((struct fb_wid_list *)arg)->wl_list), + ((struct fb_wid_list *)arg)->wl_count * sizeof (struct fb_wid_item)); + if (i) return i; + return leo_wid_get (fb, (struct fb_wid_list *)arg); + case FBIO_WID_PUT: + i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct fb_wid_list)); + if (i) return i; + if (((struct fb_wid_list *)arg)->wl_count != 1 || + !((struct fb_wid_list *)arg)->wl_list) return -EINVAL; + i = verify_area (VERIFY_WRITE, (void *)(((struct fb_wid_list *)arg)->wl_list), + ((struct fb_wid_list *)arg)->wl_count * sizeof (struct fb_wid_item)); + if (i) return i; + return leo_wid_put (fb, (struct fb_wid_list *)arg); + case LEO_CLUTPOST: + i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct leo_clut)); + if (i) return i; + i = ((struct leo_clut *)arg)->offset + ((struct leo_clut *)arg)->count; + if (i <= 0 || i > 256) return -EINVAL; + i = verify_area (VERIFY_READ, ((struct leo_clut *)arg)->red, ((struct leo_clut *)arg)->count); + if (i) return i; + i = verify_area (VERIFY_READ, ((struct leo_clut *)arg)->green, ((struct leo_clut *)arg)->count); + if (i) return i; + i = verify_area (VERIFY_READ, ((struct leo_clut *)arg)->blue, ((struct leo_clut *)arg)->count); + if (i) return i; + return leo_clutpost (fb, (struct leo_clut *)arg); + case LEO_CLUTREAD: + i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct leo_clut)); + if (i) return i; + i = ((struct leo_clut *)arg)->offset + ((struct leo_clut *)arg)->count; + if (i <= 0 || i > 256) return -EINVAL; + i = verify_area (VERIFY_WRITE, ((struct leo_clut *)arg)->red, ((struct leo_clut *)arg)->count); + if (i) return i; + i = verify_area (VERIFY_WRITE, ((struct leo_clut *)arg)->green, ((struct leo_clut *)arg)->count); + if (i) return i; + i = verify_area (VERIFY_WRITE, ((struct leo_clut *)arg)->blue, ((struct leo_clut *)arg)->count); + if (i) return i; + return leo_clutread (fb, (struct leo_clut *)arg); + + default: + return -ENOSYS; + } +} + +static void +leo_reset (fbinfo_t *fb) +{ + if (fb->setcursor) + sun_hw_hide_cursor (); +} + + +__initfunc(static unsigned long leo_postsetup (fbinfo_t *fb, unsigned long memory_start)) +{ + fb->info.leo.cluts[0] = (u32 *)(memory_start); + fb->info.leo.cluts[1] = (u32 *)(memory_start+256*4); + fb->info.leo.cluts[2] = (u32 *)(memory_start+256*4*2); + fb->info.leo.xlut = (u8 *)(memory_start+256*4*3); + fb->color_map = (u8 *)(memory_start+256*4*3+256); + return memory_start + (256*4*3) + 256 + 256*3; +} + +__initfunc(void leo_setup (fbinfo_t *fb, int slot, u32 leo, int leo_io)) +{ + struct leo_info *leoinfo; + int i; + struct fb_wid_item wi; + struct fb_wid_list wl; + + printk ("leo%d at 0x%8.8x ", slot, leo); + + /* Fill in parameters we left out */ + fb->type.fb_size = 0x800000; /* 8MB */ + fb->type.fb_cmsize = 256; + fb->mmap = leo_mmap; + fb->loadcmap = leo_loadcmap; + fb->postsetup = leo_postsetup; + fb->ioctl = (void *)leo_ioctl; + fb->reset = leo_reset; + fb->blank = leo_blank; + fb->unblank = leo_unblank; + fb->setcursor = leo_setcursor; + fb->setcursormap = leo_setcursormap; + fb->setcurshape = leo_setcurshape; + fb->blitc = leo_blitc; + fb->setw = leo_setw; + fb->cpyw = leo_cpyw; + fb->fill = leo_fill; + fb->base_depth = 0; + + leoinfo = (struct leo_info *) &fb->info.leo; + + memset (leoinfo, 0, sizeof(struct leo_info)); + + leoinfo->offset = leo; + /* Map the hardware registers */ + leoinfo->lc_ss0_krn = sparc_alloc_io(leo + LEO_OFF_LC_SS0_KRN, 0, + PAGE_SIZE,"leo_lc_ss0_krn", fb->space, 0); + leoinfo->lc_ss0_usr = sparc_alloc_io(leo + LEO_OFF_LC_SS0_USR, 0, + PAGE_SIZE,"leo_lc_ss0_usr", fb->space, 0); + leoinfo->lc_ss1_krn = sparc_alloc_io(leo + LEO_OFF_LC_SS1_KRN, 0, + PAGE_SIZE,"leo_lc_ss1_krn", fb->space, 0); + leoinfo->lc_ss1_usr = sparc_alloc_io(leo + LEO_OFF_LC_SS1_USR, 0, + PAGE_SIZE,"leo_lc_ss1_usr", fb->space, 0); + leoinfo->ld_ss0 = sparc_alloc_io(leo + LEO_OFF_LD_SS0, 0, + PAGE_SIZE,"leo_ld_ss0", fb->space, 0); + leoinfo->ld_ss1 = sparc_alloc_io(leo + LEO_OFF_LD_SS1, 0, + PAGE_SIZE,"leo_ld_ss1", fb->space, 0); + leoinfo->ld_gbl = sparc_alloc_io(leo + LEO_OFF_LD_GBL, 0, + PAGE_SIZE,"leo_ld_gbl", fb->space, 0); + leoinfo->lx_krn = sparc_alloc_io(leo + LEO_OFF_LX_KRN, 0, + PAGE_SIZE,"leo_lx_krn", fb->space, 0); + leoinfo->cursor = sparc_alloc_io(leo + LEO_OFF_LX_CURSOR, 0, + sizeof(struct leo_cursor),"leo_lx_crsr", fb->space, 0); + fb->base = (long)sparc_alloc_io(leo + LEO_OFF_SS0, 0, + 0x800000,"leo_ss0", fb->space, 0); + + leoinfo->ld_ss0->unk = 0xffff; + leoinfo->ld_ss0->unk2 = 0; + leoinfo->ld_ss0->unk3 = (fb->type.fb_width - 1) | ((fb->type.fb_height - 1) << 16); + wl.wl_count = 1; + wl.wl_list = &wi; + wi.wi_type = FB_WID_DBL_8; + wi.wi_index = 0; + wi.wi_values [0] = 0x2c0; + leo_wid_put (fb, &wl); + wi.wi_index = 1; + wi.wi_values [0] = 0x30; + leo_wid_put (fb, &wl); + wi.wi_index = 2; + wi.wi_values [0] = 0x20; + leo_wid_put (fb, &wl); + + leoinfo->ld_ss1->ss1_misc |= LEO_SS1_MISC_ENABLE; + + leoinfo->ld_ss0->fg = 0x30703; + leoinfo->ld_ss0->planemask = 0xff000000; + leoinfo->ld_ss0->rop = 0xd0840; + leoinfo->lc_ss0_usr->extent = (fb->type.fb_width-1) | ((fb->type.fb_height-1) << 11); + i = leoinfo->lc_ss0_usr->attrs; + leoinfo->lc_ss0_usr->fill = (0) | ((0) << 11) | ((i & 3) << 29) | ((i & 8) ? 0x80000000 : 0); + do { + i = leoinfo->lc_ss0_usr->csr; + } while (i & 0x20000000); + + if (slot == sun_prom_console_id) + fb_restore_palette = leo_restore_palette; + + printk("Cmd Rev %d\n", + (leoinfo->lc_ss0_krn->rev >> 28)); + + /* Reset the leo */ + leo_reset(fb); + + if (!slot) + /* Enable Video */ + leo_unblank (fb); + else if (slot != sun_prom_console_id) + leo_blank (fb); +} + +extern unsigned char vga_font []; + +#define GX_BLITC_START(attr,x,y,count) \ + { \ + register struct leo_lc_ss0_usr *us = fbinfo[0].info.leo.lc_ss0_usr; \ + register struct leo_ld_ss0 *ss = fbinfo[0].info.leo.ld_ss0; \ + register u32 i; \ + do { \ + i = us->csr; \ + } while (i & 0x20000000); \ + ss->fg = (attr & 0xf) << 24; \ + ss->bg = (attr >> 4) << 24; \ + ss->rop = 0x310040; \ + ss->planemask = 0xff000000; \ + us->fontc2 = 0xFFFFFFFE; \ + us->attrs = 4; \ + us->fontc = 0xFF000000; +#define GX_BLITC_END \ + } + +static void leo_blitc(unsigned short charattr, int xoff, int yoff) +{ + unsigned char attrib = CHARATTR_TO_SUNCOLOR(charattr); + unsigned char *p = &vga_font[((unsigned char)charattr) << 4]; + u32 *u = ((u32 *)fbinfo[0].base) + (yoff << 11) + xoff; + GX_BLITC_START(attrib, xoff, yoff, 1) + for (i = 0; i < CHAR_HEIGHT; i++, u += 2048) + *u = (*p++) << 24; + GX_BLITC_END +} + +static void leo_setw(int xoff, int yoff, unsigned short c, int count) +{ + unsigned char attrib = CHARATTR_TO_SUNCOLOR(c); + unsigned char *p = &vga_font[((unsigned char)c) << 4]; + register unsigned char *q; + u32 *u = ((u32 *)fbinfo[0].base) + (yoff << 11) + xoff; + GX_BLITC_START(attrib, xoff, yoff, count) + while (count-- > 0) { + q = p; + for (i = 0; i < CHAR_HEIGHT; i++, u += 2048) + *u = (*q++) << 24; + u += 8 - (CHAR_HEIGHT * 2048); + } + GX_BLITC_END +} + +static void leo_cpyw(int xoff, int yoff, unsigned short *p, int count) +{ + unsigned char attrib = CHARATTR_TO_SUNCOLOR(*p); + register unsigned char *q; + u32 *u = ((u32 *)fbinfo[0].base) + (yoff << 11) + xoff; + GX_BLITC_START(attrib, xoff, yoff, count) + while (count-- > 0) { + q = &vga_font[((unsigned char)*p++) << 4]; + for (i = 0; i < CHAR_HEIGHT; i++, u += 2048) + *u = (*q++) << 24; + u += 8 - (CHAR_HEIGHT * 2048); + } + GX_BLITC_END +} + +static void leo_fill(int attrib, int count, int *boxes) +{ + register struct leo_lc_ss0_usr *us = fbinfo[0].info.leo.lc_ss0_usr; + register struct leo_ld_ss0 *ss = fbinfo[0].info.leo.ld_ss0; + register u32 i; + do { + i = us->csr; + } while (i & 0x20000000); + ss->unk = 0xffff; + ss->unk2 = 0; + ss->unk3 = (fbinfo[0].type.fb_width - 1) | ((fbinfo[0].type.fb_height - 1) << 16); + ss->fg = ((attrib & 0xf)<<24) | 0x030703; + ss->planemask = 0xff000000; + ss->rop = 0xd0840; + while (count-- > 0) { + us->extent = ((boxes[2] - boxes[0] - 1) & 0x7ff) | (((boxes[3] - boxes[1] - 1) & 0x7ff) << 11); + i = us->attrs; + us->fill = (boxes[0] & 0x7ff) | ((boxes[1] & 0x7ff) << 11) | ((i & 3) << 29) | ((i & 8) ? 0x80000000 : 0); + } +} diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c new file mode 100644 index 000000000..548abb601 --- /dev/null +++ b/drivers/sbus/char/openprom.c @@ -0,0 +1,611 @@ +/* + * Linux/SPARC PROM Configuration Driver + * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu) + * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) + * + * This character device driver allows user programs to access the + * PROM device tree. It is compatible with the SunOS /dev/openprom + * driver and the NetBSD /dev/openprom driver. The SunOS eeprom + * utility works without any modifications. + * + * The driver uses a minor number under the misc device major. The + * file read/write mode determines the type of access to the PROM. + * Interrupts are disabled whenever the driver calls into the PROM for + * sanity's sake. + */ + +/* This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define PROMLIB_INTERNAL + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/miscdevice.h> +#include <linux/init.h> +#include <asm/oplib.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/openpromio.h> + + +/* Private data kept by the driver for each descriptor. */ +typedef struct openprom_private_data +{ + int current_node; /* Current node for SunOS ioctls. */ + int lastnode; /* Last valid node used by BSD ioctls. */ +} DATA; + +/* ID of the PROM node containing all of the EEPROM options. */ +static int options_node = 0; + +/* + * Copy an openpromio structure into kernel space from user space. + * This routine does error checking to make sure that all memory + * accesses are within bounds. A pointer to the allocated openpromio + * structure will be placed in "*opp_p". Return value is the length + * of the user supplied buffer. + */ +static int copyin(struct openpromio *info, struct openpromio **opp_p) +{ + int bufsize; + + if (!info || !opp_p) + return -EFAULT; + + get_user_ret(bufsize, &info->oprom_size, -EFAULT); + + if (bufsize == 0 || bufsize > OPROMMAXPARAM) + return -EINVAL; + + if (!(*opp_p = kmalloc(sizeof(int) + bufsize + 1, GFP_KERNEL))) + return -ENOMEM; + memset(*opp_p, 0, sizeof(int) + bufsize + 1); + + if (copy_from_user(&(*opp_p)->oprom_array, + &info->oprom_array, bufsize)) { + kfree(*opp_p); + return -EFAULT; + } + return bufsize; +} + +static int getstrings(struct openpromio *info, struct openpromio **opp_p) +{ + int n, bufsize; + char c; + + if (!info || !opp_p) + return -EFAULT; + + if (!(*opp_p = kmalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL))) + return -ENOMEM; + + memset(*opp_p, 0, sizeof(int) + OPROMMAXPARAM + 1); + (*opp_p)->oprom_size = 0; + + n = bufsize = 0; + while ((n < 2) && (bufsize < OPROMMAXPARAM)) { + if (get_user(c, &info->oprom_array[bufsize])) { + kfree(*opp_p); + return -EFAULT; + } + if (c == '\0') + n++; + (*opp_p)->oprom_array[bufsize++] = c; + } + if (!n) { + kfree(*opp_p); + return -EINVAL; + } + return bufsize; +} + +/* + * Copy an openpromio structure in kernel space back to user space. + */ +static int copyout(void *info, struct openpromio *opp, int len) +{ + copy_to_user_ret(info, opp, len, -EFAULT); + return 0; +} + +/* + * SunOS and Solaris /dev/openprom ioctl calls. + */ +static int openprom_sunos_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg, int node) +{ + DATA *data = (DATA *) file->private_data; + char buffer[OPROMMAXPARAM+1], *buf; + struct openpromio *opp; + unsigned long flags; + int bufsize, len, error = 0; + + if (cmd == OPROMSETOPT) + bufsize = getstrings((void *)arg, &opp); + else + bufsize = copyin((void *)arg, &opp); + + if (bufsize < 0) + return bufsize; + + switch (cmd) { + case OPROMGETOPT: + case OPROMGETPROP: + save_and_cli(flags); + len = prom_getproplen(node, opp->oprom_array); + restore_flags(flags); + + if (len <= 0 || len > bufsize) { + error = copyout((void *)arg, opp, sizeof(int)); + break; + } + + save_and_cli(flags); + len = prom_getproperty(node, opp->oprom_array, buffer, bufsize); + restore_flags(flags); + + memcpy(opp->oprom_array, buffer, len); + opp->oprom_array[len] = '\0'; + opp->oprom_size = len; + + error = copyout((void *)arg, opp, sizeof(int) + bufsize); + break; + + case OPROMNXTOPT: + case OPROMNXTPROP: + save_and_cli(flags); + buf = prom_nextprop(node, opp->oprom_array); + restore_flags(flags); + + len = strlen(buf); + if (len == 0 || len + 1 > bufsize) { + error = copyout((void *)arg, opp, sizeof(int)); + break; + } + + memcpy(opp->oprom_array, buf, len); + opp->oprom_array[len] = '\0'; + opp->oprom_size = ++len; + + error = copyout((void *)arg, opp, sizeof(int) + bufsize); + break; + + case OPROMSETOPT: + case OPROMSETOPT2: + buf = opp->oprom_array + strlen(opp->oprom_array) + 1; + len = opp->oprom_array + bufsize - buf; + + printk(KERN_DEBUG "OPROMSETOPT%s %s='%s'\n", + (cmd == OPROMSETOPT) ? "" : "2", opp->oprom_array, buf); + + save_and_cli(flags); + error = prom_setprop(options_node, opp->oprom_array, + buf, len); + restore_flags(flags); + + if (error <= 0) + error = -EINVAL; + break; + + case OPROMNEXT: + case OPROMCHILD: + if (bufsize < sizeof(int)) { + error = -EINVAL; + break; + } + + node = *((int *) opp->oprom_array); + + save_and_cli(flags); + if (cmd == OPROMNEXT) + node = __prom_getsibling(node); + else + node = __prom_getchild(node); + restore_flags(flags); + + data->current_node = node; + *((int *)opp->oprom_array) = node; + opp->oprom_size = sizeof(int); + + error = copyout((void *)arg, opp, bufsize + sizeof(int)); + break; + + case OPROMGETBOOTARGS: + save_and_cli(flags); + buf = prom_getbootargs(); + restore_flags(flags); + + len = strlen(buf); + + if (len > bufsize) { + error = -EINVAL; + break; + } + + strcpy(opp->oprom_array, buf); + opp->oprom_size = len; + + error = copyout((void *)arg, opp, bufsize + sizeof(int)); + break; + + case OPROMU2P: + case OPROMGETCONS: + case OPROMGETFBNAME: + printk(KERN_INFO "openprom_sunos_ioctl: unimplemented ioctl\n"); + error = -EINVAL; + break; + default: + printk(KERN_INFO "openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg); + error = -EINVAL; + break; + } + + kfree(opp); + return error; +} + + +/* Return nonzero if a specific node is in the PROM device tree. */ +static int intree(int root, int node) +{ + for (; root != 0; root = prom_getsibling(root)) + if (root == node || intree(prom_getchild(root),node)) + return 1; + return 0; +} + +/* Return nonzero if a specific node is "valid". */ +static int goodnode(int n, DATA *data) +{ + if (n == data->lastnode || n == prom_root_node || n == options_node) + return 1; + if (n == 0 || n == -1 || !intree(prom_root_node,n)) + return 0; + data->lastnode = n; + return 1; +} + +/* Copy in a whole string from userspace into kernelspace. */ +static int copyin_string(char *user, size_t len, char **ptr) +{ + char *tmp; + + tmp = kmalloc(len + 1, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + if(copy_from_user(tmp, user, len)) { + kfree(tmp); + return -EFAULT; + } + + tmp[len] = '\0'; + + *ptr = tmp; + + return 0; +} + +/* + * NetBSD /dev/openprom ioctl calls. + */ +static int openprom_bsd_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + DATA *data = (DATA *) file->private_data; + struct opiocdesc op; + unsigned long flags; + int error, node, len; + char *str, *tmp; + + switch (cmd) { + case OPIOCGET: + copy_from_user_ret(&op, (void *)arg, sizeof(op), -EFAULT); + + if (!goodnode(op.op_nodeid,data)) + return -EINVAL; + + error = copyin_string(op.op_name, op.op_namelen, &str); + if (error) + return error; + + save_and_cli(flags); + len = prom_getproplen(op.op_nodeid,str); + restore_flags(flags); + + if (len > op.op_buflen) { + kfree(str); + return -ENOMEM; + } + + op.op_buflen = len; + + if (len <= 0) { + kfree(str); + /* Verified by the above copy_from_user_ret */ + __copy_to_user_ret((void *)arg, &op, + sizeof(op), -EFAULT); + return 0; + } + + tmp = kmalloc(len + 1, GFP_KERNEL); + if (!tmp) { + kfree(str); + return -ENOMEM; + } + + save_and_cli(flags); + prom_getproperty(op.op_nodeid, str, tmp, len); + restore_flags(flags); + + tmp[len] = '\0'; + + error = __copy_to_user((void *)arg, &op, sizeof(op)); + if (!error) + error = copy_to_user(op.op_buf, tmp, len); + + kfree(tmp); + kfree(str); + + return error; + + case OPIOCNEXTPROP: + copy_from_user_ret(&op, (void *)arg, sizeof(op), -EFAULT); + + if (!goodnode(op.op_nodeid,data)) + return -EINVAL; + + error = copyin_string(op.op_name, op.op_namelen, &str); + if (error) + return error; + + save_and_cli(flags); + tmp = prom_nextprop(op.op_nodeid,str); + restore_flags(flags); + + if (tmp) { + len = strlen(tmp); + if (len > op.op_buflen) + len = op.op_buflen; + else + op.op_buflen = len; + } else { + len = op.op_buflen = 0; + } + + error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(op)); + if (error) { + kfree(str); + return error; + } + + error = verify_area(VERIFY_WRITE, op.op_buf, len); + if (error) { + kfree(str); + return error; + } + + error = __copy_to_user((void *)arg, &op, sizeof(op)); + if (!error) error = __copy_to_user(op.op_buf, tmp, len); + + kfree(str); + + return error; + + case OPIOCSET: + copy_from_user_ret(&op, (void *)arg, sizeof(op), -EFAULT); + + if (!goodnode(op.op_nodeid,data)) + return -EINVAL; + + error = copyin_string(op.op_name, op.op_namelen, &str); + if (error) + return error; + + error = copyin_string(op.op_buf, op.op_buflen, &tmp); + if (error) { + kfree(str); + return error; + } + + save_and_cli(flags); + len = prom_setprop(op.op_nodeid,str,tmp,op.op_buflen+1); + restore_flags(flags); + + if (len != op.op_buflen) + return -EINVAL; + + kfree(str); + kfree(tmp); + + return 0; + + case OPIOCGETOPTNODE: + copy_to_user_ret((void *)arg, &options_node, + sizeof(int), -EFAULT); + return 0; + + case OPIOCGETNEXT: + case OPIOCGETCHILD: + copy_from_user_ret(&node, (void *)arg, sizeof(int), -EFAULT); + + save_and_cli(flags); + if (cmd == OPIOCGETNEXT) + node = __prom_getsibling(node); + else + node = __prom_getchild(node); + restore_flags(flags); + + __copy_to_user_ret((void *)arg, &node, sizeof(int), -EFAULT); + + return 0; + + default: + printk(KERN_INFO "openprom_bsd_ioctl: cmd 0x%X\n", cmd); + return -EINVAL; + + } +} + + +/* + * Handoff control to the correct ioctl handler. + */ +static int openprom_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + DATA *data = (DATA *) file->private_data; + + switch (cmd) { + case OPROMGETOPT: + case OPROMNXTOPT: + if ((file->f_mode & FMODE_READ) == 0) + return -EPERM; + return openprom_sunos_ioctl(inode, file, cmd, arg, + options_node); + + case OPROMSETOPT: + case OPROMSETOPT2: + if ((file->f_mode & FMODE_WRITE) == 0) + return -EPERM; + return openprom_sunos_ioctl(inode, file, cmd, arg, + options_node); + + case OPROMNEXT: + case OPROMCHILD: + case OPROMGETPROP: + case OPROMNXTPROP: + if ((file->f_mode & FMODE_READ) == 0) + return -EPERM; + return openprom_sunos_ioctl(inode, file, cmd, arg, + data->current_node); + + case OPROMU2P: + case OPROMGETCONS: + case OPROMGETFBNAME: + case OPROMGETBOOTARGS: + if ((file->f_mode & FMODE_READ) == 0) + return -EPERM; + return openprom_sunos_ioctl(inode, file, cmd, arg, 0); + + case OPIOCGET: + case OPIOCNEXTPROP: + case OPIOCGETOPTNODE: + case OPIOCGETNEXT: + case OPIOCGETCHILD: + if ((file->f_mode & FMODE_READ) == 0) + return -EBADF; + return openprom_bsd_ioctl(inode,file,cmd,arg); + + case OPIOCSET: + if ((file->f_mode & FMODE_WRITE) == 0) + return -EBADF; + return openprom_bsd_ioctl(inode,file,cmd,arg); + + default: + printk("openprom_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg); + return -EINVAL; + } +} + +static long long openprom_lseek(struct inode * inode, struct file * file, + long long offset, int origin) +{ + return -ESPIPE; +} + +static int openprom_open(struct inode * inode, struct file * file) +{ + DATA *data; + + data = (DATA *) kmalloc(sizeof(DATA), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->current_node = prom_root_node; + data->lastnode = prom_root_node; + file->private_data = (void *)data; + + MOD_INC_USE_COUNT; + + return 0; +} + +static int openprom_release(struct inode * inode, struct file * file) +{ + kfree_s(file->private_data, sizeof(DATA)); + MOD_DEC_USE_COUNT; + return 0; +} + +static struct file_operations openprom_fops = { + openprom_lseek, + NULL, /* openprom_read */ + NULL, /* openprom_write */ + NULL, /* openprom_readdir */ + NULL, /* openprom_poll */ + openprom_ioctl, + NULL, /* openprom_mmap */ + openprom_open, + openprom_release +}; + +static struct miscdevice openprom_dev = { + SUN_OPENPROM_MINOR, "openprom", &openprom_fops +}; + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +int init_module(void) +#else +__initfunc(int openprom_init(void)) +#endif +{ + unsigned long flags; + int error; + + error = misc_register(&openprom_dev); + if (error) { + printk(KERN_ERR "openprom: unable to get misc minor\n"); + return error; + } + + save_and_cli(flags); + options_node = prom_getchild(prom_root_node); + options_node = prom_searchsiblings(options_node,"options"); + restore_flags(flags); + + if (options_node == 0 || options_node == -1) { + printk(KERN_ERR "openprom: unable to find options node\n"); + misc_deregister(&openprom_dev); + return -EIO; + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + misc_deregister(&openprom_dev); +} +#endif diff --git a/drivers/sbus/char/rtc.c b/drivers/sbus/char/rtc.c new file mode 100644 index 000000000..e0fbf2569 --- /dev/null +++ b/drivers/sbus/char/rtc.c @@ -0,0 +1,166 @@ +/* $Id: rtc.c,v 1.10 1997/04/03 08:47:55 davem Exp $ + * + * Linux/SPARC Real Time Clock Driver + * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) + * + * This is a little driver that lets a user-level program access + * the SPARC Mostek real time clock chip. It is no use unless you + * use the modified clock utility. + * + * Get the modified clock utility from: + * ftp://vger.rutgers.edu/pub/linux/Sparc/userland/clock.c + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/miscdevice.h> +#include <linux/malloc.h> +#include <linux/fcntl.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <asm/mostek.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/rtc.h> + +static int rtc_busy = 0; + +/* Retrieve the current date and time from the real time clock. */ +void get_rtc_time(struct rtc_time *t) +{ + register struct mostek48t02 *regs = mstk48t02_regs; + unsigned long flags; + + save_flags(flags); + cli(); + regs->creg |= MSTK_CREG_READ; + + t->sec = MSTK_REG_SEC(regs); + t->min = MSTK_REG_MIN(regs); + t->hour = MSTK_REG_HOUR(regs); + t->dow = MSTK_REG_DOW(regs); + t->dom = MSTK_REG_DOM(regs); + t->month = MSTK_REG_MONTH(regs); + t->year = MSTK_CVT_YEAR( MSTK_REG_YEAR(regs) ); + + regs->creg &= ~MSTK_CREG_READ; + restore_flags(flags); +} + +/* Set the current date and time inthe real time clock. */ +void set_rtc_time(struct rtc_time *t) +{ + register struct mostek48t02 *regs = mstk48t02_regs; + unsigned long flags; + + save_flags(flags); + cli(); + regs->creg |= MSTK_CREG_WRITE; + + MSTK_SET_REG_SEC(regs,t->sec); + MSTK_SET_REG_MIN(regs,t->min); + MSTK_SET_REG_HOUR(regs,t->hour); + MSTK_SET_REG_DOW(regs,t->dow); + MSTK_SET_REG_DOM(regs,t->dom); + MSTK_SET_REG_MONTH(regs,t->month); + MSTK_SET_REG_YEAR(regs,t->year - MSTK_YEAR_ZERO); + + regs->creg &= ~MSTK_CREG_WRITE; + restore_flags(flags); +} + +static long long rtc_lseek(struct inode *inode, struct file *file, + long long offset, int origin) +{ + return -ESPIPE; +} + +static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct rtc_time rtc_tm; + + switch (cmd) + { + case RTCGET: + get_rtc_time(&rtc_tm); + + copy_to_user_ret((struct rtc_time*)arg, &rtc_tm, sizeof(struct rtc_time), -EFAULT); + + return 0; + + + case RTCSET: + if (!suser()) + return -EPERM; + + copy_from_user_ret(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time), -EFAULT); + + set_rtc_time(&rtc_tm); + + return 0; + + default: + return -EINVAL; + } +} + +static int rtc_open(struct inode *inode, struct file *file) +{ + if (rtc_busy) + return -EBUSY; + + rtc_busy = 1; + + MOD_INC_USE_COUNT; + + return 0; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + rtc_busy = 0; + return 0; +} + +static struct file_operations rtc_fops = { + rtc_lseek, + NULL, /* rtc_read */ + NULL, /* rtc_write */ + NULL, /* rtc_readdir */ + NULL, /* rtc_poll */ + rtc_ioctl, + NULL, /* rtc_mmap */ + rtc_open, + rtc_release +}; + +static struct miscdevice rtc_dev = { RTC_MINOR, "rtc", &rtc_fops }; + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +int init_module(void) +#else +__initfunc(int rtc_init(void)) +#endif +{ + int error; + + error = misc_register(&rtc_dev); + if (error) { + printk(KERN_ERR "rtc: unable to get misc minor\n"); + return error; + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + misc_deregister(&rtc_dev); +} +#endif diff --git a/drivers/sbus/char/suncons.c b/drivers/sbus/char/suncons.c index 48adfd152..20b65c658 100644 --- a/drivers/sbus/char/suncons.c +++ b/drivers/sbus/char/suncons.c @@ -1,8 +1,13 @@ -/* suncons.c: Sun SparcStation console support. +/* $Id: suncons.c,v 1.61 1997/04/17 02:29:36 miguel Exp $ + * + * suncons.c: Sun SparcStation console support. * * Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su) * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) + * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) + * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk) + * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) * * Added font loading Nov/21, Miguel de Icaza (miguel@nuclecu.unam.mx) * Added render_screen and faster scrolling Nov/27, miguel @@ -11,14 +16,23 @@ * Added cgsix and bwtwo drivers Jan/96, miguel * Added 4m, and cg3 driver Feb/96, miguel * Fixed the cursor on color displays Feb/96, miguel. - * * Cleaned up the detection code, generic 8bit depth display - * code, Mar/96 miguel + * code, Mar/96 miguel + * Hacked support for cg14 video cards -- Apr/96, miguel. + * Color support for cg14 video cards -- May/96, miguel. + * Code split, Dave Redman, May/96 + * Be more VT change friendly, May/96, miguel. + * Support for hw cursor and graphics acceleration, Jun/96, jj. + * Added TurboGX+ detection (cgthree+), Aug/96, Iain Lea (iain@sbs.de) + * Added TCX support (8/24bit), Aug/96, jj. + * Support for multiple framebuffers, Sep/96, jj. + * Fix bwtwo inversion and handle inverse monochrome cells in + * sun_blitc, Nov/96, ecd. + * Fix sun_blitc and screen size on displays other than 1152x900, + * 128x54 chars, Nov/96, jj. + * Fix cursor spots left on some non-accelerated fbs, changed + * software cursor to be like the hw one, Nov/96, jj. * - * This file contains the frame buffer device drivers. - * Each driver is kept together in case we would like to - * split this file. - * * Much of this driver is derived from the DEC TGA driver by * Jay Estabrook who has done a nice job with the console * driver abstraction btw. @@ -29,23 +43,12 @@ * since not all Sparcs have the hardware to do it. * * TODO: - * do not use minor to index into instances of the frame buffer, - * since the numbers assigned to us are not consecutive. - * * do not blank the screen when frame buffer is mapped. * - * Change the detection loop to use more than one video card. */ -/* Define this one if you are debugging something in X, it will not disable the console output */ -/* #define DEBUGGING_X */ -/* See also: sparc/keyboard.c: CODING_NEW_DRIVER */ - -#define GRAPHDEV_MAJOR 29 - -#define FRAME_BUFFERS 1 - +#include <linux/config.h> #include <linux/sched.h> #include <linux/timer.h> #include <linux/interrupt.h> @@ -59,9 +62,11 @@ #include <linux/major.h> #include <linux/mm.h> #include <linux/types.h> +#include <linux/version.h> +#include <linux/proc_fs.h> #include <asm/system.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/bitops.h> @@ -69,7 +74,7 @@ #include <asm/sbus.h> #include <asm/fbio.h> #include <asm/io.h> -#include <asm/pgtsun4c.h> /* for the sun4c_nocache */ +#include <asm/smp.h> #include "../../char/kbd_kern.h" #include "../../char/vt_kern.h" @@ -77,51 +82,53 @@ #include "../../char/selection.h" #include "../../char/console_struct.h" +#include "fb.h" + #define cmapsz 8192 +#include "suncons_font.h" +#include <asm/linux_logo.h> + +fbinfo_t *fbinfo; +int fbinfos; + +#define ASM_BLITC + +int sun_hw_cursor_shown = 0; + +void sun_hw_hide_cursor(void); +void sun_hw_set_cursor(int,int); + extern void register_console(void (*proc)(const char *)); extern void console_print(const char *); +extern void putconsxy(int, char *); extern unsigned char vga_font[]; -extern int graphics_on; extern int serial_console; - -/* Based upon what the PROM tells us, we can figure out where - * the console is currently located. The situation can be either - * of the following two scenarios: - * - * 1) Console i/o is done over the serial line, ttya or ttyb - * 2) Console output on frame buffer (video card) and input - * coming from the keyboard/mouse which each use a zilog8530 - * serial channel a piece. - */ +char *console_fb_path = NULL; /* Set in setup.c */ /* The following variables describe a Sparc console. */ -/* From the PROM */ -static char con_name[40]; - /* Screen dimensions and color depth. */ static int con_depth, con_width, con_height, con_type; -static int con_linebytes; - /* Base address of first line. */ static unsigned char *con_fb_base; /* Screen parameters: we compute those at startup to make the code faster */ static int chars_per_line; /* number of bytes per line */ static int ints_per_line; /* number of ints per line */ -static int skip_bytes; /* number of bytes we skip for the y margin */ +static int ints_per_cursor; /* 14 * ints_per_line */ +static int skip_bytes; /* number of bytes we skip for the y margin + x_margin */ static int x_margin, y_margin; /* the x and y margins */ static int bytes_per_row; /* bytes used by one screen line (of 16 scan lines) */ +int sun_prom_console_id = 0; /* Functions used by the SPARC dependent console code - * to perform the restore_palette function. + * to perform the fb_restore_palette function. */ -static void (*restore_palette)(void); +void (*fb_restore_palette)(fbinfo_t *fbinfo); void set_palette (void); - /* Our screen looks like at 1152 X 900: * * 0,0 @@ -157,9 +164,66 @@ void set_palette (void); ((NICE_X_MARGIN) + (((cindex)&127)))) -#define COLOR_FBUF_OFFSET(cindex) \ - (((skip_bytes) + (((cindex)>>7) * bytes_per_row)) + \ - ((x_margin) + (((cindex)&127) << 3))) +#define COLOR_FBUF_OFFSET(cindex) (*color_fbuf_offset)(cindex) + +/* These four routines are optimizations for the _generic routine for + * the most common cases. + * I guess doing twice sll is much faster than doing .mul, sra faster + * than doing .div, and the disadvantage that someone has to call it + * (it cannot be inline) runs away, 'cause otherwise it would have to + * call .mul anyway. + * The shifting + addition only routines won't eat any stack frame :)) + * Names come from width, screen_num_columns. + */ +static int +color_fbuf_offset_1280_144 (int cindex) +{ + register int i = (cindex/144); + /* (1280 * CHAR_HEIGHT) == 101.0000.0000.0000 */ + return skip_bytes + (i << 14) + (i << 12) + ((cindex % 144) << 3); +} + +static int +color_fbuf_offset_1152_128 (int cindex) +{ + register int i = (cindex>>7); + /* (1152 * CHAR_HEIGHT) == 100.1000.0000.0000 */ + return skip_bytes + (i << 14) + (i << 11) + ((cindex & 127) << 3); +} + +static int +color_fbuf_offset_1024_128 (int cindex) +{ + register int i = (cindex>>7); + /* (1024 * CHAR_HEIGHT) == 100.0000.0000.0000 */ + return skip_bytes + (i << 14) + ((cindex & 127) << 3); +} + +static int +color_fbuf_offset_800_96 (int cindex) +{ + register int i = (cindex / 96); + /* (800 * CHAR_HEIGHT) == 11.0010.0000.0000 */ + return skip_bytes + (i<<13) + (i<<12) + (i<<9) + ((cindex % 96)<<3); +} + +static int +color_fbuf_offset_640_80 (int cindex) +{ + register int i = (cindex/80); + /* (640 * CHAR_HEIGHT) == 10.1000.0000.0000 */ + return skip_bytes + (i << 13) + (i << 11) + ((cindex % 80) << 3); +} + +static int +color_fbuf_offset_generic (int cindex) +{ + return skip_bytes + (cindex / video_num_columns) * bytes_per_row + ((cindex % video_num_columns) << 3); +} + +static int (*color_fbuf_offset)(int) = color_fbuf_offset_generic; + +static int do_accel = 0; void __set_origin(unsigned short offset) @@ -175,64 +239,55 @@ __set_origin(unsigned short offset) * Hide the cursor from view, during blanking, usually... */ static int cursor_pos = -1; + +static unsigned int under_cursor[4]; + void hide_cursor(void) { unsigned long flags; int j; - save_flags(flags); cli(); + if (fbinfo[0].setcursor) { + sun_hw_hide_cursor(); + return; + } + + if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) + return; /* Don't paint anything on fb which is not ours, + but turn off the hw cursor in such case */ + + __save_and_cli(flags); if(cursor_pos == -1) { - restore_flags (flags); + __restore_flags (flags); return; } - /* We just zero out the area for now. Certain graphics - * cards like the cg6 have a hardware cursor that we could - * use, but this is an optimization for some time later. - */ switch (con_depth){ case 1: { unsigned char *dst; dst = (unsigned char *)((unsigned long)con_fb_base + FBUF_OFFSET(cursor_pos)); for(j = 0; j < CHAR_HEIGHT; j++, dst += CHARS_PER_LINE) - *dst = ~(0); + *dst = ~(*dst); break; } case 8: { - unsigned long *dst; - const int ipl = ints_per_line; + unsigned int *dst; - dst = (unsigned long *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(cursor_pos)); - for(j = 0; j < CHAR_HEIGHT; j++, dst += ipl) { - *dst = ~(0UL); - *(dst + 1) = ~(0UL); - } + dst = (unsigned int *)((unsigned long)con_fb_base + + COLOR_FBUF_OFFSET(cursor_pos)) + ints_per_cursor; + dst[0] = under_cursor[0]; + dst[1] = under_cursor[1]; + dst[ints_per_line] = under_cursor[2]; + dst[ints_per_line+1] = under_cursor[3]; break; } default: break; } - restore_flags(flags); -} - -/* The idea is the following: - * we only use the colors in the range 0..15, and we only - * setup the palette on that range, so we better keep the - * pixel inversion using those colors, that's why we have - * those constants below. - */ -inline static void -cursor_reverse (long *dst, int height, const int ints_on_line) -{ - int j; - - for (j = 0; j < height; j++){ - *dst = ~(*dst) & 0x0f0f0f0f; - *(dst+1) = ~(*(dst+1)) & 0x0f0f0f0f; - dst += ints_on_line; - } + cursor_pos = -1; + __restore_flags(flags); } void @@ -244,19 +299,33 @@ set_cursor(int currcons) if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS) return; +#if 0 +/* This is a nop anyway */ if (__real_origin != __origin) __set_origin(__real_origin); +#endif + + if (fbinfo[0].setcursor) { + if (!deccm) + hide_cursor(); + else { + idx = (pos - video_mem_base) >> 1; + + sun_hw_set_cursor(x_margin + ((idx % video_num_columns) << 3), y_margin + ((idx / video_num_columns) * CHAR_HEIGHT)); + } + return; + } - save_flags(flags); cli(); + __save_and_cli(flags); idx = (pos - video_mem_base) >> 1; oldpos = cursor_pos; - cursor_pos = idx; if (!deccm) { hide_cursor (); - restore_flags (flags); + __restore_flags (flags); return; } + cursor_pos = idx; switch (con_depth){ case 1: { unsigned char *dst, *opos; @@ -275,25 +344,37 @@ set_cursor(int currcons) break; } case 8: { - unsigned long *dst, *opos; - dst = (unsigned long *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(idx)); - opos = (unsigned long *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(oldpos)); + unsigned int *dst, *opos; + dst = (unsigned int *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(idx)) + ints_per_cursor; - if(oldpos != -1) - cursor_reverse(opos, CHAR_HEIGHT, ints_per_line); - cursor_reverse (dst, CHAR_HEIGHT, ints_per_line); + if(oldpos != -1) { + opos = (unsigned int *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(oldpos)) + ints_per_cursor; + opos[0] = under_cursor[0]; + opos[1] = under_cursor[1]; + opos[ints_per_line] = under_cursor[2]; + opos[ints_per_line+1] = under_cursor[3]; + } + under_cursor[0] = dst[0]; + under_cursor[1] = dst[1]; + under_cursor[2] = dst[ints_per_line]; + under_cursor[3] = dst[ints_per_line+1]; + dst[0] = 0x00000000; + dst[1] = 0x00000000; + dst[ints_per_line] = 0x00000000; + dst[ints_per_line+1] = 0x00000000; break; } default: } - restore_flags(flags); + __restore_flags(flags); } /* * Render the current screen - * Only used at startup to avoid the caching that is being done in selection.h + * Only used at startup and when switching from KD_GRAPHICS to KD_TEXT + * to avoid the caching that is being done in selection.h */ -static void +void render_screen(void) { int count; @@ -301,13 +382,71 @@ render_screen(void) count = video_num_columns * video_num_lines; contents = (unsigned short *) video_mem_base; - + for (;count--; contents++) sun_blitc (*contents, (unsigned long) contents); } -unsigned long -con_type_init(unsigned long kmem_start, const char **display_desc) +__initfunc(void serial_finish_init(void (*printfunc)(const char *))) +{ + char buffer[2048]; + + sprintf (buffer, linux_serial_image, UTS_RELEASE); + (*printfunc)(buffer); +} + +__initfunc(void con_type_init_finish(void)) +{ + int i, cpu; + char *p = con_fb_base + skip_bytes; + char q[2] = {0,5}; + int currcons = 0; + unsigned short *ush; + + if (serial_console) + return; + if (con_type == FBTYPE_SUNLEO) { + int rects [4]; + + rects [0] = 0; + rects [1] = 0; + rects [2] = con_width; + rects [3] = con_height; + (*fbinfo[0].fill)(reverse_color_table[0], 1, rects); + return; /* Dunno how to display logo on leo/zx yet */ + } + if (con_depth == 8 && fbinfo[0].loadcmap) { + for (i = 0; i < LINUX_LOGO_COLORS; i++) { + fbinfo[0].color_map CM(i+32,0) = linux_logo_red [i]; + fbinfo[0].color_map CM(i+32,1) = linux_logo_green [i]; + fbinfo[0].color_map CM(i+32,2) = linux_logo_blue [i]; + } + (*fbinfo [0].loadcmap)(&fbinfo [0], 0, LINUX_LOGO_COLORS + 32); + for (i = 0; i < 80; i++, p += chars_per_line){ + for (cpu = 0; cpu < linux_num_cpus; cpu++){ + memcpy (p + (cpu * 88), linux_logo + 80 * i, 80); + } + } + } else if (con_depth == 1) { + for (i = 0; i < 80; i++, p += chars_per_line) + memcpy (p, linux_logo_bw + 10 * i, 10); + } + putconsxy(0, q); + ush = (unsigned short *) video_mem_base + video_num_columns * 2 + 20 + 11 * (linux_num_cpus - 1); + + p = linux_logo_banner; + for (; *p; p++, ush++) { + *ush = (attr << 8) + *p; + sun_blitc (*ush, (unsigned long) ush); + } + for (i = 0; i < 5; i++) { + ush = (unsigned short *) video_mem_base + i * video_num_columns; + memset (ush, 0, 20); + } +} + +__initfunc(unsigned long +con_type_init(unsigned long kmem_start, const char **display_desc)) { can_do_color = (con_type != FBTYPE_SUN2BW); @@ -315,7 +454,7 @@ con_type_init(unsigned long kmem_start, const char **display_desc) *display_desc = "SUN"; if (!serial_console) { - /* If we fall back to PROM than our output have to remain readable. */ + /* If we fall back to PROM then our output have to remain readable. */ prom_putchar('\033'); prom_putchar('['); prom_putchar('H'); /* @@ -324,8 +463,6 @@ con_type_init(unsigned long kmem_start, const char **display_desc) video_mem_base = kmem_start; kmem_start += video_screen_size; video_mem_term = kmem_start; - - render_screen(); } return kmem_start; } @@ -364,31 +501,37 @@ set_scrmem(int currcons, long offset) int set_get_font(char * arg, int set, int ch512) { - int error, i, line; + int i, line; if (!arg) return -EINVAL; - error = verify_area (set ? VERIFY_READ : VERIFY_WRITE, (void *) arg, - ch512 ? 2* cmapsz : cmapsz); - if (error) - return error; /* download the current font */ if (!set){ - memset (arg, 0, cmapsz); - for (i = 0; i < 256; i++) - for (line = 0; line < CHAR_HEIGHT; line++) - put_user (vga_font [i], arg+(i*32+line)); + if(clear_user(arg, cmapsz)) + return -EFAULT; + for (i = 0; i < 256; i++) { + for (line = 0; line < CHAR_HEIGHT; line++) { + unsigned char value = vga_font[i]; + + /* Access checked by the above clear_user */ + __put_user_ret (value, (arg + (i * 32 + line)), + -EFAULT); + } + } return 0; } /* set the font */ - for (i = 0; i < 256; i++) + + if (verify_area (VERIFY_READ, arg, 256 * CHAR_HEIGHT)) return -EFAULT; + for (i = 0; i < 256; i++) { for (line = 0; line < CHAR_HEIGHT; line++){ - vga_font [i*CHAR_HEIGHT + line] = (get_user (arg + (i * 32 + line))); - if (con_depth == 1) - vga_font [i*CHAR_HEIGHT + line] = vga_font [i*CHAR_HEIGHT + line]; + unsigned char value; + __get_user_ret(value, (arg + (i * 32 + line)),-EFAULT); + vga_font [i*CHAR_HEIGHT + line] = value; } + } return 0; } @@ -411,20 +554,23 @@ set_get_cmap(unsigned char * arg, int set) { int i; - i = verify_area(set ? VERIFY_READ : VERIFY_WRITE, (void *)arg, 16*3); - if (i) - return i; - + if(set) + i = VERIFY_READ; + else + i = VERIFY_WRITE; + if(verify_area(i, arg, (16 * 3 * sizeof(unsigned char)))) + return -EFAULT; for (i=0; i<16; i++) { if (set) { - default_red[i] = get_user(arg++) ; - default_grn[i] = get_user(arg++) ; - default_blu[i] = get_user(arg++) ; + __get_user_ret(default_red[i], (arg+0),-EFAULT); + __get_user_ret(default_grn[i], (arg+1),-EFAULT); + __get_user_ret(default_blu[i], (arg+2),-EFAULT); } else { - put_user (default_red[i], arg++) ; - put_user (default_grn[i], arg++) ; - put_user (default_blu[i], arg++) ; + __put_user_ret(default_red[i], (arg+0),-EFAULT); + __put_user_ret(default_grn[i], (arg+1),-EFAULT); + __put_user_ret(default_blu[i], (arg+2),-EFAULT); } + arg += 3; } if (set) { for (i=0; i<MAX_NR_CONSOLES; i++) @@ -442,14 +588,99 @@ set_get_cmap(unsigned char * arg, int set) return 0; } - void sun_clear_screen(void) { - memset (con_fb_base, (con_depth == 1 ? ~(0) : (0)), - (con_depth * con_height * con_width) / 8); + if (fbinfo[0].fill) { + int rects [4]; + + rects [0] = 0; + rects [1] = 0; + rects [2] = con_width; + rects [3] = con_height; + (*fbinfo[0].fill)(reverse_color_table[0], 1, rects); + } else if (fbinfo[0].base && fbinfo[0].base_depth) + memset (con_fb_base, + (con_depth == 1) ? ~(0) : reverse_color_table[0], + (con_depth * con_height * con_width) / 8); /* also clear out the "shadow" screen memory */ memset((char *)video_mem_base, 0, (video_mem_term - video_mem_base)); + cursor_pos = -1; +} + +void +sun_clear_fb(int n) +{ + if (!n) sun_clear_screen (); +#if 0 +/* This makes in some configurations serious problems. + * Who cares if other screens are cleared? + */ + else if (fbinfo[n].fill) { + int rects [4]; + + rects [0] = 0; + rects [1] = 0; + rects [2] = fbinfo[n].type.fb_width; + rects [3] = fbinfo[n].type.fb_height; + (*fbinfo[n].fill)(reverse_color_table[0], 1, rects); + } +#endif + else if (fbinfo[n].base && fbinfo[n].base_depth) { + memset((void *)fbinfo[n].base, + (fbinfo[n].base_depth == 1) ? + ~(0) : reverse_color_table[0], + (fbinfo[n].base_depth * fbinfo[n].type.fb_height + * fbinfo[n].type.fb_width) / 8); + } +} + +void +sun_clear_margin(void) +{ + int h, he, i; + unsigned char *p; + + if (fbinfo[0].fill) { + int rects [16]; + + memset (rects, 0, sizeof (rects)); + rects [2] = con_width; + rects [3] = y_margin; + rects [5] = y_margin; + rects [6] = x_margin; + rects [7] = con_height; + rects [8] = con_width - x_margin; + rects [9] = y_margin; + rects [10] = con_width; + rects [11] = con_height; + rects [12] = x_margin; + rects [13] = con_height - y_margin; + rects [14] = con_width - x_margin; + rects [15] = con_height; + (*fbinfo[0].fill)(reverse_color_table[0], 4, rects); + } else { + memset (con_fb_base, + (con_depth == 1) ? ~(0) : reverse_color_table[0], + skip_bytes - (x_margin<<1)); + memset (con_fb_base + chars_per_line * con_height + - skip_bytes + (x_margin<<1), + (con_depth == 1) ? ~(0) : reverse_color_table[0], + skip_bytes - (x_margin<<1)); + he = con_height - 2 * y_margin; + i = 2 * x_margin; + if (con_depth == 1) { + for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0; + h <= he; p += chars_per_line, h++) + memset (p, ~(0), i); + } else { + for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0; + h <= he; p += chars_per_line, h++) + memset (p, reverse_color_table[0], i); + } + } + if (fbinfo [0].switch_from_graph) + (*fbinfo [0].switch_from_graph)(); } /* @@ -459,9 +690,11 @@ sun_clear_screen(void) void vesa_blank(void) { } + void vesa_unblank(void) { } + void set_vesa_blanking(const unsigned long arg) { } @@ -470,242 +703,16 @@ void vesa_powerdown(void) { } -#undef color -/* cg6 cursor status, kernel tracked copy */ -struct cg6_cursor { - short enable; /* cursor is enabled */ - struct fbcurpos cpos; /* position */ - struct fbcurpos chot; /* hot-spot */ - struct fbcurpos size; /* size of mask & image fields */ - int bits[2][32]; /* space for mask & image bits */ - char color [6]; /* cursor colors */ -}; - -struct cg6_info { - struct bt_regs *bt; /* color control */ - void *fbc; - struct cg6_fhc *fhc; - struct cg6_tec *tec; - struct cg6_thc *thc; - struct cg6_cursor cursor; /* cursor control */ - void *dhc; -}; - -struct bwtwo_info { - struct bwtwo_regs *regs; +/* + * We permutate the colors, so we match the PROM's idea of + * black and white. + */ +unsigned char reverse_color_table[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0 }; -struct cg3_info { - struct bt_regs *bt; /* brooktree (color) registers */ -}; - -/* Array holding the information for the frame buffers */ -typedef struct { - union { - struct bwtwo_info bwtwo; - struct cg3_info cg3; - struct cg6_info cg6; - } info; /* per frame information */ - int space; /* I/O space this card resides in */ - int blanked; /* true if video blanked */ - int open; /* is this fb open? */ - int mmaped; /* has this fb been mmapped? */ - int vtconsole; /* virtual console where it is opened */ - long base; /* frame buffer base */ - struct fbtype type; /* frame buffer type */ - int (*mmap)(struct inode *, struct file *, struct vm_area_struct *, long fb_base, void *); - void (*loadcmap)(void *this, int index, int count); - void (*blank)(void *this); - void (*unblank)(void *this); - int (*ioctl)(struct inode *, struct file *, unsigned int, unsigned long, void *); -} fbinfo_t; - -static fbinfo_t fbinfo [FRAME_BUFFERS]; - -/* We need to keep a copy of the color map to answer ioctl requests */ -static union { - unsigned char map[256][3]; /* reasonable way to access */ - unsigned int raw[256*3/4]; /* hardware wants it like this */ -} color_map; - -#define FB_MMAP_VM_FLAGS (VM_SHM| VM_LOCKED) - -static int -fb_open (struct inode * inode, struct file * file) -{ - int minor = MINOR (inode->i_rdev); - - if (minor >= FRAME_BUFFERS) - return -EBADF; - if (fbinfo [minor].open) - return -EBUSY; - fbinfo [minor].open = 1; - fbinfo [minor].mmaped = 0; - return 0; -} - -static int -fb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - int minor = MINOR (inode->i_rdev); - fbinfo_t *fb; - struct fbcmap *cmap; - int i; - - if (minor >= FRAME_BUFFERS) - return -EBADF; - fb = &fbinfo [minor]; - - switch (cmd){ - case FBIOGTYPE: /* return frame buffer type */ - i = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct fbtype)); - if (i) return i; - *(struct fbtype *)arg = (fb->type); - break; - case FBIOGATTR:{ - struct fbgattr *fba = (struct fbgattr *) arg; - - i = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct fbgattr)); - if (i) return i; - fba->real_type = fb->type.fb_type; - fba->owner = 0; - fba->fbtype = fb->type; - fba->sattr.flags = 0; - fba->sattr.emu_type = fb->type.fb_type; - fba->sattr.dev_specific [0] = -1; - fba->emu_types [0] = fb->type.fb_type; - fba->emu_types [1] = -1; - break; - } - case FBIOSVIDEO: - i = verify_area(VERIFY_READ, (void *)arg, sizeof(int)); - if (i) return i; - - if (*(int *)arg){ - if (!fb->blanked || !fb->unblank) - break; - (*fb->unblank)(fb); - fb->blanked = 0; - } else { - if (fb->blanked || !fb->blank) - break; - (*fb->blank)(fb); - fb->blanked = 1; - } - break; - case FBIOGVIDEO: - i = verify_area (VERIFY_WRITE, (void *) arg, sizeof (int)); - if (i) return i; - *(int *) arg = fb->blanked; - break; - case FBIOPUTCMAP: { /* load color map entries */ - char *rp, *gp, *bp; - int end, count;; - - if (!fb->loadcmap) - return -EINVAL; - i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbcmap)); - if (i) return i; - cmap = (struct fbcmap *) arg; - count = cmap->count; - if ((cmap->index < 0) || (cmap->index > 255)) - return -EINVAL; - if (cmap->index + count > 256) - count = 256 - cmap->index; - i = verify_area (VERIFY_READ, rp = cmap->red, cmap->count); - if (i) return i; - i = verify_area (VERIFY_READ, gp = cmap->green, cmap->count); - if (i) return i; - i = verify_area (VERIFY_READ, bp = cmap->blue, cmap->count); - if (i) return i; - - end = cmap->index + count; - for (i = cmap->index; i < end; i++){ - color_map.map [i][0] = *rp++; - color_map.map [i][1] = *gp++; - color_map.map [i][2] = *bp++; - } - (*fb->loadcmap)(fb, cmap->index, count); - break; - } - - default: - if (fb->ioctl){ - i = fb->ioctl (inode, file, cmd, arg, fb); - if (i == -EINVAL) - printk ("[[FBIO: %8.8x]]\n", cmd); - return i; - } - printk ("[[FBIO: %8.8x]]\n", cmd); - return -EINVAL; - } - return 0; -} - -static void -fb_close (struct inode * inode, struct file *filp) -{ - int minor = MINOR(inode->i_rdev); - struct fbcursor cursor; - - if (minor >= FRAME_BUFFERS) - return; - if (fbinfo [minor].open) - fbinfo [minor].open = 0; - vt_cons [fbinfo [minor].vtconsole]->vc_mode = KD_TEXT; - - /* Leaving graphics mode, turn off the cursor */ - graphics_on = 0; - if (fbinfo [minor].mmaped) - sun_clear_screen (); - cursor.set = FB_CUR_SETCUR; - cursor.enable = 0; - fb_ioctl (inode, filp, FBIOSCURPOS, (unsigned long) &cursor); - set_palette (); - render_screen (); - return; -} - -static int -fb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma) -{ - int minor = MINOR (inode->i_rdev); - fbinfo_t *fb; - - if (minor >= FRAME_BUFFERS) - return -ENXIO; - /* FIXME: the fg_console below should actually be the - * console on which the invoking process is running - */ - if (vt_cons [fg_console]->vc_mode == KD_GRAPHICS) - return -ENXIO; - fbinfo [minor].vtconsole = fg_console; - fb = &fbinfo [minor]; - - if (fb->mmap){ - int v; - - v = (*fb->mmap)(inode, file, vma, fb->base, fb); - if (v) return v; - fbinfo [minor].mmaped = 1; - vt_cons [fg_console]->vc_mode = KD_GRAPHICS; - graphics_on = 1; - return 0; - } else - return -ENXIO; -} - -static struct file_operations graphdev_fops = -{ - NULL, /* lseek */ - NULL, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* select */ - fb_ioctl, - fb_mmap, - fb_open, /* open */ - fb_close, /* close */ +static unsigned char sparc_color_table[] = { + 15, 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11 }; /* Call the frame buffer routine for setting the palette */ @@ -720,518 +727,65 @@ set_palette (void) /* First keep color_map with the palette colors */ for (i = 0; i < 16; i++){ - j = color_table [i]; - color_map.map [i][0] = default_red [j]; - color_map.map [i][1] = default_grn [j]; - color_map.map [i][2] = default_blu [j]; + j = sparc_color_table [i]; + fbinfo[0].color_map CM(i,0) = default_red [j]; + fbinfo[0].color_map CM(i,1) = default_grn [j]; + fbinfo[0].color_map CM(i,2) = default_blu [j]; } (*fbinfo [0].loadcmap)(&fbinfo [0], 0, 16); } } -/* Called when returning to prom */ void -console_restore_palette (void) -{ - if (restore_palette) - (*restore_palette) (); -} - -/* This routine should be moved to srmmu.c */ -static __inline__ unsigned int -srmmu_get_pte (unsigned long addr) -{ - register unsigned long entry; - - __asm__ __volatile__("\n\tlda [%1] %2,%0\n\t" : - "=r" (entry): - "r" ((addr & 0xfffff000) | 0x400), "i" (ASI_M_FLUSH_PROBE)); - return entry; -} - -unsigned int -get_phys (unsigned int addr) -{ - switch (sparc_cpu_model){ - case sun4c: - return sun4c_get_pte (addr) << PAGE_SHIFT; - case sun4m: - return ((srmmu_get_pte (addr) & 0xffffff00) << 4); - default: - panic ("get_phys called for unsupported cpu model\n"); - return 0; - } -} - -/* CG6 support code */ - -/* Offset of interesting structures in the OBIO space */ -/* - * Brooktree is the video dac and is funny to program on the cg6. - * (it's even funnier on the cg3) - * The FBC could be the the frame buffer control - * The FHC could be the frame buffer hardware control. - */ -#define CG6_ROM_OFFSET 0x0 -#define CG6_BROOKTREE_OFFSET 0x200000 -#define CG6_DHC_OFFSET 0x240000 -#define CG6_ALT_OFFSET 0x280000 -#define CG6_FHC_OFFSET 0x300000 -#define CG6_THC_OFFSET 0x301000 -#define CG6_FBC_OFFSET 0x700000 -#define CG6_TEC_OFFSET 0x701000 -#define CG6_RAM_OFFSET 0x800000 - -struct bt_regs { - unsigned int addr; /* address register */ - unsigned int color_map; /* color map */ - unsigned int control; /* control register */ - unsigned int cursor; /* cursor map register */ -}; - -/* The contents are unknown */ -struct cg6_tec { - int tec_matrix; - int tec_clip; - int tec_vdc; -}; - -struct cg6_thc { - unsigned int thc_xxx0[512]; /* ??? */ - unsigned int thc_hsync1; /* hsync timing */ - unsigned int thc_hsync2; - unsigned int thc_hsync3; - unsigned int thc_vsync1; /* vsync timing */ - unsigned int thc_vsync2; - unsigned int thc_refresh; - unsigned int thc_misc; - unsigned int thc_xxx1[56]; - unsigned int thc_cursxy; /* cursor x,y position (16 bits each) */ - unsigned int thc_cursmask[32]; /* cursor mask bits */ - unsigned int thc_cursbits[32]; /* what to show where mask enabled */ -}; - -static void -cg6_restore_palette (void) -{ - volatile struct bt_regs *bt; - - bt = fbinfo [0].info.cg6.bt; - bt->addr = 0; - bt->color_map = 0xffffffff; - bt->color_map = 0xffffffff; - bt->color_map = 0xffffffff; -} - -/* Ugh: X wants to mmap a bunch of cute stuff at the same time :-( */ -/* So, we just mmap the things that are being asked for */ -static int -cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, void *xx) -{ - unsigned int size, page, r, map_size; - unsigned int map_offset = 0; - fbinfo_t *fb = (fbinfo_t *) xx; - - size = vma->vm_end - vma->vm_start; - if (vma->vm_offset & ~PAGE_MASK) - return -ENXIO; - - /* To stop the swapper from even considering these pages */ - vma->vm_flags |= FB_MMAP_VM_FLAGS; - - /* Each page, see which map applies */ - for (page = 0; page < size; ){ - switch (vma->vm_offset+page){ - case CG6_TEC: - map_size = PAGE_SIZE; - map_offset = get_phys ((uint)fb->info.cg6.tec); - break; - case CG6_FBC: - map_size = PAGE_SIZE; - map_offset = get_phys ((uint)fb->info.cg6.fbc); - break; - case CG6_FHC: - map_size = PAGE_SIZE; - map_offset = get_phys ((uint)fb->info.cg6.fhc); - break; - case CG6_THC: - map_size = PAGE_SIZE; - map_offset = get_phys ((uint)fb->info.cg6.thc); - break; - case CG6_BTREGS: - map_size = PAGE_SIZE; - map_offset = get_phys ((uint)fb->info.cg6.bt); - break; - - case CG6_DHC: - map_size = PAGE_SIZE * 40; - map_offset = get_phys ((uint)fb->info.cg6.dhc); - break; - - case CG6_ROM: - map_size = 0; - break; - - case CG6_RAM: - map_size = size-page; - map_offset = get_phys ((uint) con_fb_base); - if (map_size < fb->type.fb_size) - map_size = fb->type.fb_size; - break; - default: - map_size = 0; - break; - } - if (!map_size){ - page += PAGE_SIZE; - continue; - } - r = io_remap_page_range (vma->vm_start+page, - map_offset, - map_size, vma->vm_page_prot, - fb->space); - if (r) return -EAGAIN; - page += map_size; - } - vma->vm_inode = inode; - inode->i_count++; - return 0; -} - -#define BT_D4M3(x) ((((x) >> 2) << 1) + ((x) >> 2)) /* (x / 4) * 3 */ -#define BT_D4M4(x) ((x) & ~3) /* (x / 4) * 4 */ - -static void -cg6_loadcmap (void *fbinfo, int index, int count) -{ - fbinfo_t *fb = (fbinfo_t *) fbinfo; - struct bt_regs *bt = fb->info.cg6.bt; - int i; - - bt->addr = index << 24; - for (i = index; count--; i++){ - bt->color_map = color_map.map [i][0] << 24; - bt->color_map = color_map.map [i][1] << 24; - bt->color_map = color_map.map [i][2] << 24; - } -} - -/* Load cursor information */ -static void -cg6_setcursor (struct cg6_info *info) -{ - unsigned int v; - struct cg6_cursor *c = &info->cursor; - - 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); - } - info->thc->thc_cursxy = v; -} - -#undef pos -static int -cg6_scursor (struct fbcursor *cursor, fbinfo_t *fb) +set_other_palette (int n) { - int op = cursor->set; - volatile struct cg6_thc *thc = fb->info.cg6.thc; - struct cg6_cursor *cursor_info = &fb->info.cg6.cursor; - int i, bytes = 0; - - if (op & FB_CUR_SETSHAPE){ - if ((unsigned int) cursor->size.fbx > 32) - return -EINVAL; - if ((unsigned int) cursor->size.fby > 32) - return -EINVAL; - bytes = (cursor->size.fby * 32)/8; - i = verify_area (VERIFY_READ, cursor->image, bytes); - if (i) return i; - i = verify_area (VERIFY_READ, cursor->mask, bytes); - if (i) return i; - } - if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){ - if (op & FB_CUR_SETCUR) - cursor_info->enable = cursor->enable; - if (op & FB_CUR_SETPOS) - cursor_info->cpos = cursor->pos; - if (op & FB_CUR_SETHOT) - cursor_info->chot = cursor->hot; - cg6_setcursor (&fb->info.cg6); + if (!n) { + set_palette (); + return; } - if (op & FB_CUR_SETSHAPE){ - unsigned int u; - - cursor_info->size = cursor->size; - memset ((void *)&cursor_info->bits, 0, sizeof (cursor_info->size)); - memcpy (cursor_info->bits [0], cursor->mask, bytes); - memcpy (cursor_info->bits [1], cursor->image, bytes); - u = ~0; - if (cursor_info->size.fbx < 32) - u = ~(u >> cursor_info->size.fbx); - for (i = 0; i < 32; i++){ - int m = cursor_info->bits [0][i] & u; - thc->thc_cursmask [i] = m; - thc->thc_cursbits [i] = m & cursor_info->bits [1][i]; - } + if (fbinfo [n].loadcmap){ + fbinfo[n].color_map CM(0,0) = 0; + fbinfo[n].color_map CM(0,1) = 0; + fbinfo[n].color_map CM(0,2) = 0; + (*fbinfo [n].loadcmap)(&fbinfo [n], 0, 1); } - return 0; -} - -/* Handle cg6-specific ioctls */ -static int -cg6_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long arg, fbinfo_t *fb) -{ - int i; - - switch (cmd){ - case FBIOGCURMAX: - i = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct fbcurpos)); - if (i) return i; - ((struct fbcurpos *) arg)->fbx = 32; - ((struct fbcurpos *) arg)->fby = 32; - break; - - case FBIOSVIDEO: - /* vesa_blank and vesa_unblank could do the job on fb [0] */ - break; - - case FBIOSCURSOR: - return cg6_scursor ((struct fbcursor *) arg, fb); - - case FBIOSCURPOS: - /* - i= verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbcurpos)); - if (i) return i; - */ - fb->info.cg6.cursor.cpos = *(struct fbcurpos *)arg; - cg6_setcursor (&fb->info.cg6); - break; - default: - return -EINVAL; - } - return 0; } -static void -cg6_setup (int slot, unsigned int cg6, int cg6_io) +/* Called when returning to prom */ +void +console_restore_palette (void) { - struct cg6_info *cg6info; - - printk ("cgsix%d at 0x%8.8x\n", slot, (unsigned int) cg6); - - /* Fill in parameters we left out */ - fbinfo [slot].type.fb_cmsize = 256; - fbinfo [slot].mmap = cg6_mmap; - fbinfo [slot].loadcmap = cg6_loadcmap; - fbinfo [slot].ioctl = (void *) cg6_ioctl; - fbinfo [slot].blank = 0; - fbinfo [slot].unblank = 0; - - cg6info = (struct cg6_info *) &fbinfo [slot].info.cg6; - - /* Map the hardware registers */ - cg6info->bt = sparc_alloc_io ((void *) cg6+CG6_BROOKTREE_OFFSET, 0, - sizeof (struct bt_regs),"cgsix_dac", cg6_io, 0); - cg6info->fhc = sparc_alloc_io ((void *) cg6+CG6_FHC_OFFSET, 0, - sizeof (int), "cgsix_fhc", cg6_io, 0); - cg6info->thc = sparc_alloc_io ((void *) cg6+CG6_THC_OFFSET, 0, - sizeof (struct cg6_thc), "cgsix_thc", cg6_io, 0); - cg6info->tec = sparc_alloc_io ((void *) cg6+CG6_TEC_OFFSET, 0, - sizeof (struct cg6_tec), "cgsix_tec", cg6_io, 0); - cg6info->dhc = sparc_alloc_io ((void *) cg6+CG6_DHC_OFFSET, 0, - 0x40000, "cgsix_dhc", cg6_io, 0); - cg6info->fbc = sparc_alloc_io ((void *) cg6+CG6_FBC_OFFSET, 0, - 0x1000, "cgsix_fbc", cg6_io, 0); - if (!con_fb_base){ - con_fb_base = sparc_alloc_io ((void *) cg6+CG6_RAM_OFFSET, 0, - fbinfo [slot].type.fb_size, "cgsix_ram", cg6_io, 0); - } - if (!slot) - restore_palette = cg6_restore_palette; + if (fb_restore_palette) + (*fb_restore_palette) (&fbinfo[0]); } -/* The cg3 driver, obio space addresses for mapping the cg3 stuff */ -#define CG3_REGS 0x400000 -#define CG3_RAM 0x800000 -#define D4M3(x) ((((x)>>2)<<1) + ((x)>>2)) /* (x/4)*3 */ -#define D4M4(x) ((x)&~0x3) /* (x/4)*4 */ - -/* 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 */ -static void -cg3_loadcmap (void *fbinfo, int index, int count) +unsigned int +get_phys (unsigned long addr) { - fbinfo_t *fb = (fbinfo_t *) fbinfo; - struct bt_regs *bt = fb->info.cg3.bt; - int *i, steps; - - i = &color_map.raw [D4M3(index)]; - steps = D4M3(index+count-1) - D4M3(index)+3; - bt->addr = D4M4(index); - while (steps--) - bt->color_map = *i++; + return __get_phys(addr); } -/* The cg3 is presumed to emulate a cg4, I guess older programs will want that */ -/* addresses above 0x4000000 are for cg3, below that it's cg4 emulation */ -static int -cg3_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, void *xx) +int +get_iospace (unsigned long addr) { - unsigned int size, page, r, map_size; - unsigned int map_offset = 0; - fbinfo_t *fb = (fbinfo_t *) xx; - - size = vma->vm_end - vma->vm_start; - if (vma->vm_offset & ~PAGE_MASK) - return -ENXIO; - - /* To stop the swapper from even considering these pages */ - vma->vm_flags |= FB_MMAP_VM_FLAGS; - - /* Each page, see which map applies */ - for (page = 0; page < size; ){ - switch (vma->vm_offset+page){ - case CG3_MMAP_OFFSET: - map_size = size-page; - map_offset = get_phys ((uint) con_fb_base); - if (map_size > fb->type.fb_size) - map_size = fb->type.fb_size; - break; - default: - map_size = 0; - break; - } - if (!map_size){ - page += PAGE_SIZE; - continue; - } - r = io_remap_page_range (vma->vm_start+page, - map_offset, - map_size, vma->vm_page_prot, - fb->space); - if (r) return -EAGAIN; - page += map_size; - } - vma->vm_inode = inode; - inode->i_count++; - return 0; + return __get_iospace(addr); } -static void -cg3_setup (int slot, unsigned int cg3, int cg3_io) +__initfunc(unsigned long sun_cg_postsetup(fbinfo_t *fb, unsigned long start_mem)) { - struct cg3_info *cg3info; - - printk ("cgthree%d at 0x%8.8x\n", slot, cg3); - - /* Fill in parameters we left out */ - fbinfo [slot].type.fb_cmsize = 256; - fbinfo [slot].mmap = cg3_mmap; - fbinfo [slot].loadcmap = cg3_loadcmap; - fbinfo [slot].ioctl = 0; /* no special ioctls */ - - cg3info = (struct cg3_info *) &fbinfo [slot].info.cg3; - - /* Map the card registers */ - cg3info->bt = sparc_alloc_io ((void *) cg3+CG3_REGS, 0, - sizeof (struct bt_regs),"cg3_bt", cg3_io, 0); - - if (!con_fb_base){ - con_fb_base=sparc_alloc_io ((void*) cg3+CG3_RAM, 0, - fbinfo [slot].type.fb_size, "cg3_ram", cg3_io, 0); - } + fb->color_map = (char *)start_mem; + return start_mem + 256*3; } -/* OBio addresses for the bwtwo registers */ -#define BWTWO_REGISTER_OFFSET 0x400000 - -struct bwtwo_regs { - char unknown [16]; -#define BWTWO_ENABLE_VIDEO 0x40 - unsigned char control; - char unknown2 [15]; +static char *known_cards [] __initdata = { + "cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", "SUNW,tcx", + "cgfourteen", "SUNW,leo", "SUNW,ffb", 0 }; - -static int -bwtwo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, void *xx) -{ - unsigned int size, map_offset, r; - fbinfo_t *fb = (fbinfo_t *) xx; - int map_size; - - map_size = size = vma->vm_end - vma->vm_start; - - if (vma->vm_offset & ~PAGE_MASK) - return -ENXIO; - - /* To stop the swapper from even considering these pages */ - vma->vm_flags |= FB_MMAP_VM_FLAGS; - printk ("base=%8.8xl start=%8.8xl size=%x offset=%8.8x\n", - (unsigned int) base, - (unsigned int) vma->vm_start, size, - (unsigned int) vma->vm_offset); - - /* This routine should also map the register if asked for, but we don't do that yet */ - map_offset = get_phys ((uint) con_fb_base); - r = io_remap_page_range (vma->vm_start, map_offset, map_size, vma->vm_page_prot, - fb->space); - if (r) return -EAGAIN; - vma->vm_inode = inode; - inode->i_count++; - return 0; -} - -static void -bwtwo_blank (void *xx) -{ - fbinfo_t *fb = (fbinfo_t *) xx; - - fb->info.bwtwo.regs->control &= ~BWTWO_ENABLE_VIDEO; -} - -static void -bwtwo_unblank (void *xx) -{ - fbinfo_t *fb = (fbinfo_t *) xx; - fb->info.bwtwo.regs->control |= BWTWO_ENABLE_VIDEO; -} - -static void -bwtwo_setup (int slot, unsigned int bwtwo, int bw2_io) -{ - printk ("bwtwo%d at 0x%8.8x\n", slot, bwtwo); - fbinfo [slot].type.fb_cmsize = 2; - fbinfo [slot].mmap = bwtwo_mmap; - fbinfo [slot].loadcmap = 0; - fbinfo [slot].ioctl = 0; - fbinfo [slot].blank = bwtwo_blank; - fbinfo [slot].unblank = bwtwo_unblank; - fbinfo [slot].info.bwtwo.regs = sparc_alloc_io ((void *) bwtwo+BWTWO_REGISTER_OFFSET, - 0, sizeof (struct bwtwo_regs), "bwtwo_regs", bw2_io, 0); -} - -static void -cg14_setup (int slot, unsigned int cg14, int cg14_io) -{ - printk ("cgfourteen%d at 0x%8.8x\n", slot, cg14); - fbinfo [slot].type.fb_cmsize = 256; - fbinfo [slot].mmap = 0; - fbinfo [slot].loadcmap = 0; - fbinfo [slot].ioctl = 0; - fbinfo [slot].blank = 0; - fbinfo [slot].unblank = 0; -} - -static char *known_cards [] = { - "cgsix", "cgthree", "bwtwo", "SUNW,tcx", "cgfourteen", 0 +static char *v0_known_cards [] __initdata = { + "cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", 0 }; -static int -known_card (char *name) +__initfunc(static int known_card (char *name, char **known_cards)) { int i; @@ -1246,19 +800,20 @@ static struct { int resx, resy; int x_margin, y_margin; } scr_def [] = { - { 1, 1152, 900, 8, 18 }, + { 8, 1280, 1024, 64, 80 }, + { 8, 1152, 1024, 64, 80 }, { 8, 1152, 900, 64, 18 }, - { 8, 1280, 1024, 96, 80 }, { 8, 1024, 768, 0, 0 }, + { 8, 800, 600, 16, 12 }, + { 8, 640, 480, 0, 0 }, + { 1, 1152, 900, 8, 18 }, { 0 }, }; -static int -cg14_present(void) +__initfunc(static int cg14_present(void)) { int root, n; - prom_printf ("Looking for cg14\n"); root = prom_getchild (prom_root_node); if ((n = prom_searchsiblings (root, "obio")) == 0) return 0; @@ -1266,21 +821,211 @@ cg14_present(void) n = prom_getchild (n); if ((n = prom_searchsiblings (n, "cgfourteen")) == 0) return 0; - prom_printf ("Cg14 found!\n"); return n; } -static int -sparc_console_probe(void) +__initfunc(static int creator_present (void)) +{ + int root, n; + + root = prom_getchild (prom_root_node); + if ((n = prom_searchsiblings (root, "SUNW,ffb")) == 0) + return 0; + return n; +} + +__initfunc(static void + sparc_framebuffer_setup(int primary, int con_node, + int type, struct linux_sbus_device *sbdp, + uint base, unsigned long con_base, int prom_fb, + int parent_node)) { - int propl, con_node, i; - struct linux_sbus_device *sbdp; - unsigned int fbbase = 0xb001b001; - int fbiospace = 0; - int cg14 = 0; - - /* XXX The detection code needs to support multiple video cards in one system */ - con_node = 0; + static int frame_buffers = 1; + int n, i; + int linebytes; + uint io = 0; + char *p; + + if (primary) + n = 0; + else { + if (frame_buffers == FRAME_BUFFERS) + return; /* Silently ignore */ + n = frame_buffers++; + } + + if (prom_fb) sun_prom_console_id = n; + + if (sbdp) + io = sbdp->reg_addrs [0].which_io; + + /* Fill in common fb information */ + fbinfo [n].type.fb_type = type; + fbinfo [n].real_type = type; + fbinfo [n].prom_node = con_node; + memset (&(fbinfo [n].emulations), 0xff, sizeof (fbinfo [n].emulations)); + fbinfo [n].type.fb_height = prom_getintdefault(con_node, "height", 900); + fbinfo [n].type.fb_width = prom_getintdefault(con_node, "width", 1152); + fbinfo [n].type.fb_depth = (type == FBTYPE_SUN2BW) ? 1 : 8; + linebytes = prom_getint(con_node, "linebytes"); + if (linebytes == -1) linebytes = fbinfo [n].type.fb_width; + fbinfo [n].type.fb_size = PAGE_ALIGN((linebytes) * (fbinfo [n].type.fb_height)); + fbinfo [n].space = io; + fbinfo [n].blanked = 0; + if (con_base >= PAGE_OFFSET) + fbinfo [n].base = con_base; + else + fbinfo [n].base = 0; + fbinfo [n].cursor.hwsize.fbx = 32; + fbinfo [n].cursor.hwsize.fby = 32; + fbinfo [n].proc_entry.node = parent_node; + fbinfo [n].proc_entry.rdev = MKDEV(GRAPHDEV_MAJOR, n); + fbinfo [n].proc_entry.mode = S_IFCHR | S_IRUSR | S_IWUSR; + prom_getname (con_node, fbinfo [n].proc_entry.name, 32 - 3); + p = strchr (fbinfo [n].proc_entry.name, 0); + sprintf (p, ":%d", n); + + /* Should be filled in for supported video cards */ + fbinfo [n].mmap = 0; + fbinfo [n].loadcmap = 0; + fbinfo [n].ioctl = 0; + fbinfo [n].reset = 0; + fbinfo [n].blank = 0; + fbinfo [n].unblank = 0; + fbinfo [n].setcursor = 0; + fbinfo [n].base_depth = fbinfo [n].type.fb_depth; + + /* Per card setup */ + switch (fbinfo [n].type.fb_type){ +#ifdef SUN_FB_CGTHREE + case FBTYPE_SUN3COLOR: + cg3_setup (&fbinfo [n], n, base, io, sbdp); + break; +#endif +#ifdef SUN_FB_TCX + case FBTYPE_TCXCOLOR: + tcx_setup (&fbinfo [n], n, con_node, base, sbdp); + break; +#endif +#ifdef SUN_FB_CGSIX + case FBTYPE_SUNFAST_COLOR: + cg6_setup (&fbinfo [n], n, base, io); + break; +#endif +#ifdef SUN_FB_BWTWO + case FBTYPE_SUN2BW: + bwtwo_setup (&fbinfo [n], n, base, io, sbdp); + break; +#endif +#ifdef SUN_FB_CGFOURTEEN + case FBTYPE_MDICOLOR: + cg14_setup (&fbinfo [n], n, con_node, base, io); + break; +#endif +#ifdef SUN_FB_LEO + case FBTYPE_SUNLEO: + leo_setup (&fbinfo [n], n, base, io); + break; +#endif +#ifdef SUN_FB_CREATOR + case FBTYPE_CREATOR: + creator_setup (&fbinfo [n], n, con_node, base, io); + break; +#endif + default: + fbinfo [n].type.fb_type = FBTYPE_NOTYPE; + return; + } + + if (n) + return; + + /* Code below here is just executed for the first frame buffer */ + con_type = type; + con_height = fbinfo [n].type.fb_height; + con_width = fbinfo [n].type.fb_width; + con_depth = (type == FBTYPE_SUN2BW) ? 1 : 8; + for (i = 0; scr_def [i].depth; i++){ + if ((scr_def [i].resx != con_width) || + (scr_def [i].resy != con_height)) + continue; + if (scr_def [i].depth != con_depth) + continue; + x_margin = scr_def [i].x_margin; + y_margin = scr_def [i].y_margin; + chars_per_line = (con_width * con_depth) / 8; + skip_bytes = chars_per_line * y_margin + x_margin; + ints_per_line = chars_per_line / 4; + ints_per_cursor = 14 * ints_per_line; + bytes_per_row = CHAR_HEIGHT * chars_per_line; + ORIG_VIDEO_COLS = con_width / 8 - + 2 * x_margin / con_depth; + ORIG_VIDEO_LINES = (con_height - 2 * y_margin) / 16; + switch (chars_per_line) { + case 1280: + if (ORIG_VIDEO_COLS == 144) + color_fbuf_offset = + color_fbuf_offset_1280_144; + break; + case 1152: + if (ORIG_VIDEO_COLS == 128) + color_fbuf_offset = + color_fbuf_offset_1152_128; + break; + case 1024: + if (ORIG_VIDEO_COLS == 128) + color_fbuf_offset = + color_fbuf_offset_1024_128; + break; + case 800: + if (ORIG_VIDEO_COLS == 96) + color_fbuf_offset = + color_fbuf_offset_800_96; + break; + case 640: + if (ORIG_VIDEO_COLS == 80) + color_fbuf_offset = + color_fbuf_offset_640_80; + break; + } + break; + } + + if (!scr_def [i].depth){ + x_margin = y_margin = 0; + prom_printf ("console: unknown video resolution %dx%d," + " depth %d\n", + con_width, con_height, con_depth); + prom_halt (); + } + + /* P3: I fear this strips 15inch 1024/768 PC-like + * monitors out. */ + if ((linebytes*8) / con_depth != con_width) { + prom_printf("console: unusual video, linebytes=%d, " + "width=%d, height=%d depth=%d\n", + linebytes, con_width, con_height, + con_depth); + prom_halt (); + } +} + +__initfunc(static int sparc_console_probe(void)) +{ + int propl, con_node, default_node = 0, i; + char prop[16]; + struct linux_sbus_device *sbdp, *sbdprom; + struct linux_sbus *sbus; + int creator = 0, cg14 = 0; + char prom_name[40]; + int type, card_found = 0; + unsigned long con_base; + u32 tmp; + u32 prom_console_node = 0; + + for (i = 0; i < FRAME_BUFFERS; i++) + fbinfo [i].type.fb_type = FBTYPE_NOTYPE; + sbdprom = 0; switch(prom_vers) { case PROM_V0: /* V0 proms are at sun4c only. Can skip many checks. */ @@ -1290,206 +1035,203 @@ sparc_console_probe(void) prom_halt(); } for_each_sbusdev(sbdp, SBus_chain) { - con_node = sbdp->prom_node; - /* If no "address" than it is not the PROM console. */ if(sbdp->num_vaddrs) { - if(!strncmp(sbdp->prom_name, "cgsix", 5)) { - con_type = FBTYPE_SUNFAST_COLOR; - fbbase = (uint) sbdp->reg_addrs [0].phys_addr; - fbiospace = sbdp->reg_addrs[0].which_io; - break; - } else if(!strncmp(sbdp->prom_name, "cgthree", 7)) { - con_type = FBTYPE_SUN3COLOR; - fbbase = (uint) sbdp->reg_addrs [0].phys_addr; - fbiospace = sbdp->reg_addrs[0].which_io; - break; - } else if (!strncmp(sbdp->prom_name, "bwtwo", 5)) { - con_type = FBTYPE_SUN2BW; - fbbase = (uint) sbdp->reg_addrs [0].phys_addr; - fbiospace = sbdp->reg_addrs[0].which_io; + if(known_card(sbdp->prom_name, v0_known_cards)) { + sbdprom = sbdp; + strncpy(prom_name, sbdp->prom_name, sizeof (prom_name)); break; } } } - if(con_type == FBTYPE_NOTYPE) return -1; - con_fb_base = (unsigned char *) sbdp->sbus_vaddrs[0]; - strncpy(con_name, sbdp->prom_name, sizeof (con_name)); + if(!sbdprom) return -1; + for_each_sbusdev(sbdp, SBus_chain) { + con_node = sbdp->prom_node; + + if(!strncmp(sbdp->prom_name, "cgsix", 5) || + !strncmp(sbdp->prom_name, "cgthree+", 8)) { + type = FBTYPE_SUNFAST_COLOR; + } else if(!strncmp(sbdp->prom_name, "cgthree", 7) || + !strncmp(sbdp->prom_name, "cgRDI", 5)) { + type = FBTYPE_SUN3COLOR; + } else if (!strncmp(sbdp->prom_name, "bwtwo", 5)) { + type = FBTYPE_SUN2BW; + } else + continue; + sparc_framebuffer_setup (sbdprom == sbdp, con_node, type, sbdp, + (uint)sbdp->reg_addrs [0].phys_addr, sbdp->sbus_vaddrs[0], 0, + sbdp->my_bus->prom_node); + /* XXX HACK */ + if (sbdprom == sbdp && !strncmp(sbdp->prom_name, "cgRDI", 5)) + break; + } break; case PROM_V2: case PROM_V3: case PROM_P1275: - for_each_sbusdev(sbdp, SBus_chain) { - prom_printf ("Trying: %s\n", sbdp->prom_name); - if (known_card (sbdp->prom_name)) + if (console_fb_path) { + char *q, c; + + for (q = console_fb_path; *q && *q != ' '; q++); + c = *q; + *q = 0; + default_node = prom_pathtoinode(console_fb_path); + if (default_node) { + prom_printf ("Using %s for console\n", console_fb_path); + prom_console_node = prom_inst2pkg(prom_stdout); + if (prom_console_node == default_node) + prom_console_node = 0; + } + } + if (!default_node) + default_node = prom_inst2pkg(prom_stdout); + propl = prom_getproperty(default_node, "device_type", + prop, sizeof (prop)); + if (propl < 0) { + prom_printf ("output-device doesn't have device_type property\n"); + prom_halt (); + } else if (propl != sizeof("display") || strncmp("display", prop, sizeof("display"))) { + prop [propl] = 0; + prom_printf ("console_probe: output-device is %s" + " (not \"display\")\n", prop); + prom_halt (); + } + for_all_sbusdev(sbdp, sbus) { + if ((sbdp->prom_node == default_node) + && known_card (sbdp->prom_name, known_cards)) { + sbdprom = sbdp; break; + } } - if (!sbdp){ - if (!(cg14 = cg14_present ())){ - prom_printf ("Could not find a known video card on this machine\n"); - prom_halt (); - } + if (sbdprom) + card_found = 1; + if (!card_found) + card_found = cg14 = cg14_present (); + if (!card_found){ + prom_printf ("Searching for a creator\n"); + card_found = creator = creator_present (); } - if (!cg14){ - prom_apply_sbus_ranges (&sbdp->reg_addrs [0], sbdp->num_registers); - fbbase = (long) sbdp->reg_addrs [0].phys_addr; - fbiospace = sbdp->reg_addrs[0].which_io; - con_node = (*romvec->pv_v2devops.v2_inst2pkg) - (*romvec->pv_v2bootargs.fd_stdout); - /* - * Determine the type of hardware accelerator. - */ - propl = prom_getproperty(con_node, "emulation", con_name, sizeof (con_name)); - if (propl < 0 || propl >= sizeof (con_name)) { + if (!card_found){ + prom_printf ("Could not find a known video card on this machine\n"); + prom_halt (); + } + + for_all_sbusdev(sbdp, sbus) { + if (!known_card (sbdp->prom_name, known_cards)) + continue; + con_node = sbdp->prom_node; + prom_apply_sbus_ranges (sbdp->my_bus, &sbdp->reg_addrs [0], + sbdp->num_registers, sbdp); + + propl = prom_getproperty(con_node, "address", (char *) &tmp, 4); + con_base = tmp; + if (propl != 4) con_base = 0; + propl = prom_getproperty(con_node, "emulation", prom_name, sizeof (prom_name)); + if (propl < 0 || propl >= sizeof (prom_name)) { /* Early cg3s had no "emulation". */ - propl = prom_getproperty(con_node, "name", con_name, sizeof (con_name)); + propl = prom_getproperty(con_node, "name", prom_name, sizeof (prom_name)); if (propl < 0) { prom_printf("console: no device name!!\n"); return -1; } } - if(!strncmp(con_name, "cgsix", sizeof (con_name))) { - con_type = FBTYPE_SUNFAST_COLOR; - } else if(!strncmp(con_name, "cgthree", sizeof (con_name))) { - con_type = FBTYPE_SUN3COLOR; - } else if(!strncmp(con_name, "cgfourteen", sizeof (con_name))) { - con_type = FBTYPE_MDICOLOR; - } else if(!strncmp(con_name, "bwtwo", sizeof (con_name))) { - con_type = FBTYPE_SUN2BW; - } else if(!strncmp(con_name,"SUNW,tcx", sizeof (con_name))){ - con_type = FBTYPE_SUN3COLOR; + prom_name [sizeof (prom_name) - 1] = 0; + if(!strcmp(prom_name, "cgsix") || + !strcmp(prom_name, "cgthree+")) { + type = FBTYPE_SUNFAST_COLOR; + } else if(!strcmp(prom_name, "cgthree") || + !strcmp(prom_name, "cgRDI")) { + type = FBTYPE_SUN3COLOR; + } else if(!strcmp(prom_name, "cgfourteen")) { + type = FBTYPE_MDICOLOR; + } else if(!strcmp(prom_name, "SUNW,leo")) { + type = FBTYPE_SUNLEO; + } else if(!strcmp(prom_name, "bwtwo")) { + type = FBTYPE_SUN2BW; + } else if(!strcmp(prom_name,"SUNW,tcx")){ + sparc_framebuffer_setup (sbdprom == sbdp, con_node, FBTYPE_TCXCOLOR, sbdp, + (uint)sbdp->reg_addrs [10].phys_addr, con_base, + prom_console_node == con_node, sbdp->my_bus->prom_node); + continue; } else { - prom_printf("console: \"%s\" is unsupported\n", con_name); - return -1; - } - propl = prom_getproperty(con_node, "address", (char *) &con_fb_base, 4); - if (propl != 4) { - con_fb_base = 0; + prom_printf("console: \"%s\" is unsupported\n", prom_name); + continue; } - } else { - int bases [2]; - - con_node = cg14; - prom_printf ("Found a cg14\n"); - propl = prom_getproperty (cg14, "address", - (char *) &bases[0], 8); - prom_printf ("Size=%d, %x\n", propl, bases [1]); - con_fb_base = (unsigned char *) bases [1]; - con_type = FBTYPE_MDICOLOR; + sparc_framebuffer_setup (sbdprom == sbdp, con_node, type, sbdp, + (uint)sbdp->reg_addrs [0].phys_addr, con_base, + prom_console_node == con_node, sbdp->my_bus->prom_node); + /* XXX HACK */ + if (sbdprom == sbdp && !strncmp(sbdp->prom_name, "cgRDI", 5)) + break; + } + if (cg14) { + sparc_framebuffer_setup (!sbdprom, cg14, FBTYPE_MDICOLOR, + 0, 0, 0, prom_console_node == cg14, + prom_searchsiblings (prom_getchild (prom_root_node), "obio")); + } + if (creator){ + sparc_framebuffer_setup (!sbdprom, creator, FBTYPE_CREATOR, + 0, 0, 0, prom_console_node == creator, + prom_getchild (prom_root_node)); } break; default: return -1; - }; - - /* Get the device geometry */ - con_linebytes = prom_getintdefault(con_node, "linebytes", 1152); - con_width = prom_getintdefault(con_node, "width", 1152); - con_height = prom_getintdefault(con_node, "height", 900); - - /* Currently we just support 1-bit and 8-bit depth displays */ - if (con_type == FBTYPE_SUN2BW) { - con_depth = 1; - } else { - con_depth = 8; - } - for (i = 0; scr_def [i].depth; i++){ - if (scr_def [i].resx != con_width || scr_def [i].resy != con_height) - continue; - if (scr_def [i].depth != con_depth) - continue; - x_margin = scr_def [i].x_margin; - y_margin = scr_def [i].y_margin; - chars_per_line = (con_width * con_depth) / 8; - skip_bytes = chars_per_line * y_margin; - ints_per_line = chars_per_line / 4; - bytes_per_row = CHAR_HEIGHT * chars_per_line; - break; } - if (!scr_def [i].depth){ - x_margin = y_margin = 0; - prom_printf ("PenguinCon: unknown video resolution %dx%d may be slow\n", con_width, con_height); + + if (fbinfo [0].type.fb_type == FBTYPE_NOTYPE) { + prom_printf ("Couldn't setup your primary frame buffer.\n"); prom_halt (); } - /* P3: I fear this strips 15inch 1024/768 PC-like monitors out. */ - if ((con_linebytes*8) / con_depth != con_width) { - prom_printf("console: UNUSUAL VIDEO, linebytes=%d, width=%d, depth=%d\n", - con_linebytes, con_width, con_depth); - return -1; - } - - /* Negate the font table on 1 bit depth cards so we have white on black */ - if (con_depth == 1) - for(i=0; i<(16 * 256); i++) - vga_font[i] = ~vga_font[i]; - - /* Fill in common fb information */ - fbinfo [0].type.fb_type = con_type; - fbinfo [0].type.fb_height = con_height; - fbinfo [0].type.fb_width = con_width; - fbinfo [0].type.fb_depth = con_depth; - fbinfo [0].type.fb_size = PAGE_ALIGN((con_linebytes) * (con_height)); - fbinfo [0].space = fbiospace; - fbinfo [0].blanked = 0; - - /* Should be filled in for supported video cards */ - fbinfo [0].mmap = 0; - fbinfo [0].loadcmap = 0; - fbinfo [0].ioctl = 0; - fbinfo [0].blank = 0; - fbinfo [0].unblank = 0; - - if (fbbase == 0xb001b001){ - printk ("Mail miguel@nuclecu.unam.mx video_card=%d (%s)\n", con_type, con_name); - } - /* Per card setup */ - switch (con_type){ - case FBTYPE_SUN3COLOR: - cg3_setup (0, fbbase, fbiospace); - break; - case FBTYPE_SUNFAST_COLOR: - cg6_setup (0, fbbase, fbiospace); - break; - case FBTYPE_SUN2BW: - bwtwo_setup (0, fbbase, fbiospace); - break; - case FBTYPE_MDICOLOR: - cg14_setup (0, fbbase, fbiospace); - break; - default: - break; - } + if (fbinfo [0].blitc) + do_accel = 1; + + con_fb_base = (unsigned char *)fbinfo[0].base; if (!con_fb_base){ prom_printf ("PROM does not have an 'address' property for this\n" "frame buffer and the Linux drivers do not know how\n" "to map the video of this device\n"); prom_halt (); } - fbinfo [0].base = (long) con_fb_base; - - /* Register the frame buffer device */ - if (register_chrdev (GRAPHDEV_MAJOR, "graphics", &graphdev_fops)){ - printk ("Could not register graphics device\n"); - return -EIO; - } - return 0; /* success */ + return fb_init (); } /* video init code, called from within the SBUS bus scanner at * boot time. */ -void -sun_console_init(void) +__initfunc(unsigned long sun_console_init(unsigned long memory_start)) { + int i, j; if(serial_console) - return; + return memory_start; + fbinfo = (fbinfo_t *)memory_start; + memset (fbinfo, 0, FRAME_BUFFERS * sizeof (fbinfo_t)); if(sparc_console_probe()) { prom_printf("Could not probe console, bailing out...\n"); prom_halt(); } + sun_clear_screen(); + for (i = FRAME_BUFFERS; i > 1; i--) + if (fbinfo[i - 1].type.fb_type != FBTYPE_NOTYPE) break; + fbinfos = i; + memory_start = memory_start + i * sizeof (fbinfo_t); + for (j = 0; j < i; j++) + if (fbinfo[j].postsetup) + memory_start = (*fbinfo[j].postsetup)(fbinfo+j, memory_start); + for (j = 1; j < i; j++) + if (fbinfo[j].type.fb_type != FBTYPE_NOTYPE) { + sun_clear_fb(j); + set_other_palette(j); + } +#if defined(CONFIG_PROC_FS) && ( defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) ) + for (j = 0; j < i; j++) + if (fbinfo[j].type.fb_type != FBTYPE_NOTYPE) + proc_openprom_regdev (&fbinfo[j].proc_entry); +#endif + return memory_start; } /* @@ -1501,7 +1243,7 @@ sun_console_init(void) * Called from scr_writew() when the destination is * the "shadow" screen */ -static unsigned int +static uint fontmask_bits[16] = { 0x00000000, 0x000000ff, @@ -1522,53 +1264,231 @@ fontmask_bits[16] = { }; int -sun_blitc(unsigned int charattr, unsigned long addr) +sun_blitc(uint charattr, unsigned long addr) { + unsigned int fgmask, bgmask; + unsigned char attrib; int j, idx; unsigned char *font_row; -#ifndef DEBUGGING_X - if (graphics_on) + if (do_accel) { + (*fbinfo[0].blitc)(charattr, + x_margin + (((addr - video_mem_base) % video_size_row)<<2), + y_margin + CHAR_HEIGHT * ((addr - video_mem_base) / video_size_row)); return 0; -#endif + } + + /* Invalidate the cursor position if necessary. */ idx = (addr - video_mem_base) >> 1; - /* Invalidate the cursor position if necessary. */ - if(idx == cursor_pos) - cursor_pos = -1; - font_row = &vga_font[(charattr & 0xff) << 4]; + attrib = CHARATTR_TO_SUNCOLOR(charattr); + font_row = &vga_font[(j = (charattr & 0xff)) << 4]; switch (con_depth){ case 1: { register unsigned char *dst; + unsigned long flags; dst = (unsigned char *)(((unsigned long)con_fb_base) + FBUF_OFFSET(idx)); - for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE) - *dst = *font_row; + + __save_and_cli(flags); + if ((!(charattr & 0xf000)) ^ (idx == cursor_pos)) { + for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE) + *dst = ~(*font_row); + } else { + for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE) + *dst = *font_row; + } + __restore_flags(flags); break; } case 8: { - register unsigned long *dst; - unsigned long fgmask, bgmask, data, rowbits, attrib; +#ifdef ASM_BLITC + const int cpl = chars_per_line; + /* The register assignment is important here, do not modify without touching the assembly code as well */ + register unsigned int x1 __asm__("g4"), x2 __asm__("g5"), x3 __asm__("g2"), x4 __asm__("g3"), flags __asm__("g7"); + register unsigned int *dst __asm__("g1"); +#else const int ipl = ints_per_line; + unsigned int data2, data3, data4; + unsigned int data, rowbits; + register unsigned int *dst; + unsigned long flags; +#endif + const uint *fontm_bits = fontmask_bits; - dst = (unsigned long *)(((unsigned long)con_fb_base) + COLOR_FBUF_OFFSET(idx)); - attrib = (charattr >> 8) & 0x0ff; - fgmask = attrib & 0x0f; - bgmask = (attrib >> 4) & 0x0f; - fgmask = fgmask << 8 | fgmask; - fgmask |= fgmask << 16; - bgmask = bgmask << 8 | bgmask; - bgmask |= bgmask << 16; - - for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst += ipl) { - rowbits = *font_row; - data = fontmask_bits[(rowbits>>4)&0xf]; - data = (data & fgmask) | (~data & bgmask); - *dst = data; - data = fontmask_bits[rowbits&0xf]; - data = (data & fgmask) | (~data & bgmask); - *(dst+1) = data; + dst = (unsigned int *)(((unsigned long)con_fb_base) + COLOR_FBUF_OFFSET(idx)); + if (j == ' ') /* space is quite common, so we optimize a bit */ { +#ifdef ASM_BLITC +#define BLITC_SPACE \ + "\n\t std %%g4, [%%g1]" \ + "\n\t std %%g4, [%%g1 + %0]" \ + "\n\t add %%g1, %1, %%g1" +#define BLITC_SPC \ + "\n\t std %0, [%1]" \ + "\n\t std %0, [%1 + %2]" + + x1 = attrib >> 4; + x1 |= x1 << 8; + x1 |= x1 << 16; + x3 = cpl << 1; + + __asm__ __volatile__ ( + "\n\t mov %2, %3" + BLITC_SPACE + BLITC_SPACE + BLITC_SPACE + BLITC_SPACE + BLITC_SPACE + BLITC_SPACE + BLITC_SPACE + : : "r" (cpl), "r" (x3), "r" (x1), "r" (x2)); + __save_and_cli (flags); + if (idx != cursor_pos) + __asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (dst), "r" (cpl)); + else + __asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (under_cursor), "i" (8)); + __restore_flags (flags); +#else + bgmask = attrib >> 4; + bgmask |= bgmask << 8; + bgmask |= bgmask << 16; + + for(j = 0; j < CHAR_HEIGHT - 2; j++, font_row++, dst += ipl) { + *dst = bgmask; + *(dst+1) = bgmask; + } + /* Prevent cursor spots left on the screen */ + __save_and_cli(flags); + if (idx != cursor_pos) { + *dst = bgmask; + *(dst+1) = bgmask; + dst += ipl; + *dst = bgmask; + *(dst+1) = bgmask; + } else { + under_cursor [0] = bgmask; + under_cursor [1] = bgmask; + under_cursor [2] = bgmask; + under_cursor [3] = bgmask; + } + __restore_flags(flags); +#endif + } else /* non-space */ { + fgmask = attrib & 0x0f; + bgmask = attrib >> 4; + fgmask |= fgmask << 8; + fgmask |= fgmask << 16; + bgmask |= bgmask << 8; + bgmask |= bgmask << 16; + +#ifdef ASM_BLITC +#define BLITC_INIT \ + "\n\t ld [%0], %%g2" +#define BLITC_BODY(ST1,SC1,ST2,SC2) \ + "\n\t " #ST1 " %%g2, " #SC1 ", %%g7" \ + "\n\t " #ST2 " %%g2, " #SC2 ", %7" \ + "\n\t and %%g7, 0x3c, %%g7" \ + "\n\t and %7, 0x3c, %7" \ + "\n\t ld [%1 + %%g7], %6" \ + "\n\t and %6, %2, %%g7" \ + "\n\t andn %3, %6, %6" \ + "\n\t or %%g7, %6, %6" \ + "\n\t ld [%1 + %7], %7" \ + "\n\t and %7, %2, %%g7" \ + "\n\t andn %3, %7, %7" \ + "\n\t or %%g7, %7, %7" +#define BLITC_BODYEND \ + "\n\t sll %3, 2, %%g7" \ + "\n\t srl %3, 2, %3" \ + "\n\t and %%g7, 0x3c, %%g7" \ + "\n\t and %3, 0x3c, %3" \ + "\n\t ld [%0 + %%g7], %4" \ + "\n\t and %4, %1, %%g7" \ + "\n\t andn %2, %4, %4" \ + "\n\t or %%g7, %4, %4" \ + "\n\t ld [%0 + %3], %3" \ + "\n\t and %3, %1, %%g7" \ + "\n\t andn %2, %3, %3" \ + "\n\t or %%g7, %3, %3" +#define BLITC_STOREIT \ + "\n\t std %6, [%5]" \ + "\n\t add %5, %4, %5" \ + "\n\t" +#define BLITC_STORE \ + "\n\t std %%g4, [%0]" \ + "\n\t std %%g2, [%0 + %1]" + + for (j = 0; j < 3; j++, font_row+=4) { + __asm__ __volatile__ (BLITC_INIT + BLITC_BODY(srl, 26, srl, 22) + BLITC_STOREIT + BLITC_BODY(srl, 18, srl, 14) + BLITC_STOREIT + BLITC_BODY(srl, 10, srl, 6) + BLITC_STOREIT + BLITC_BODY(srl, 2, sll, 2) + BLITC_STOREIT + : : "r" (font_row), "r" (fontm_bits), "r" (fgmask), "r" (bgmask), "r" (cpl), "r" (dst), + "r" (x1), "r" (x2)); + } + __asm__ __volatile__ (BLITC_INIT + BLITC_BODY(srl, 26, srl, 22) + BLITC_STOREIT + BLITC_BODY(srl, 18, srl, 14) + BLITC_STOREIT + /* Now prepare date for the 15th line, but don't put it anywhere yet (leave it in g4,g5) */ + BLITC_BODY(srl, 10, srl, 6) + : : "r" (font_row), "r" (fontm_bits), "r" (fgmask), "r" (bgmask), "r" (cpl), "r" (dst), + "r" (x1), "r" (x2)); + /* Prepare the data the bottom line (and put it into g2,g3) */ + __asm__ __volatile__ (BLITC_BODYEND : : "r" (fontm_bits), "r" (fgmask), "r" (bgmask), + "r" (x3), "r" (x4)); + __save_and_cli(flags); + if (idx != cursor_pos) + __asm__ __volatile__ (BLITC_STORE : : "r" (dst), "r" (cpl)); + else + __asm__ __volatile__ (BLITC_STORE : : "r" (under_cursor), "i" (8)); + __restore_flags (flags); +#else + for(j = 0; j < CHAR_HEIGHT - 2; j++, font_row++, dst += ipl) { + rowbits = *font_row; + data = fontm_bits[(rowbits>>4)&0xf]; + data = (data & fgmask) | (~data & bgmask); + *dst = data; + data = fontm_bits[rowbits&0xf]; + data = (data & fgmask) | (~data & bgmask); + *(dst+1) = data; + } + rowbits = *font_row; + data = fontm_bits[(rowbits>>4)&0xf]; + data = (data & fgmask) | (~data & bgmask); + data2 = fontm_bits[rowbits&0xf]; + data2 = (data2 & fgmask) | (~data2 & bgmask); + rowbits = font_row[1]; + data3 = fontm_bits[(rowbits>>4)&0xf]; + data3 = (data3 & fgmask) | (~data3 & bgmask); + data4 = fontm_bits[rowbits&0xf]; + data4 = (data4 & fgmask) | (~data4 & bgmask); + + /* Prevent cursor spots left on the screen */ + __save_and_cli(flags); + + if (idx != cursor_pos) { + *dst = data; + *(dst+1) = data2; + dst += ipl; + *dst = data3; + *(dst+1) = data4; + } else { + under_cursor [0] = data; + under_cursor [1] = data2; + under_cursor [2] = data3; + under_cursor [3] = data4; + } + + __restore_flags(flags); +#endif } break; } /* case */ @@ -1576,347 +1496,234 @@ sun_blitc(unsigned int charattr, unsigned long addr) return (0); } -unsigned char vga_font[cmapsz] = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, -0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, -0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, -0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, -0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, -0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, -0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, -0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, -0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1e, 0x0e, -0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, -0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, -0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, -0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0e, -0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, -0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb, -0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, -0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, -0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, -0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, -0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, -0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, -0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, -0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, -0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, -0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, -0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, -0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, -0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, -0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, -0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, -0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, -0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, -0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, -0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, -0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, -0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, -0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde, -0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, -0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, -0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x6c, -0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, -0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, -0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c, -0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, -0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe7, -0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, -0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, -0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, -0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, -0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, -0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, -0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, -0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, -0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, -0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, -0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, -0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, -0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, -0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x60, -0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, -0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, -0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, 0x00, 0x00, 0xe0, 0x60, -0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, -0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0xe0, 0x60, -0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, -0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, -0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, -0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, -0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, -0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, -0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, -0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x18, -0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, -0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, -0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, -0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, -0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, -0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, -0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, -0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, -0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, -0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, -0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, -0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, -0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, -0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, -0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, -0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, -0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00, -0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, -0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, -0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c, -0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, -0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, -0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, -0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, -0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, -0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, -0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, -0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, -0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, -0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, -0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, -0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, -0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, -0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, -0xd8, 0x70, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, -0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, -0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, -0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, -0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, -0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, -0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, -0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, -0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, -0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, -0x0c, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, -0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, -0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, -0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x44, 0x11, 0x44, -0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, -0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, -0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, -0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, -0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, -0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, -0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, -0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, -0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, -0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, -0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, -0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, -0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, -0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, -0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, -0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, -0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, -0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, -0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, -0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, -0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, -0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x1b, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, -0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, -0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c, -0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, -0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; +void memsetw(void * s, unsigned short c, unsigned int count) +{ + unsigned short * addr = (unsigned short *) s; + + count /= 2; + if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) { + while (count) { + count--; + *addr++ = c; + } + return; + } + if ((unsigned long) addr + count > video_mem_term || + (unsigned long) addr < video_mem_base) { + if ((unsigned long) addr + count <= video_mem_term || + (unsigned long) addr > video_mem_base) { + while (count) { + count--; + *addr++ = c; + } + return; + } else { + while (count) { + count--; + scr_writew(c, addr++); + } + } +#define GX_SETW (*fbinfo[0].setw)(x_margin + ((xoff - (addr - last)) << 3), y_margin + CHAR_HEIGHT * yoff, c, addr - last); + } else if (do_accel) { + int yoff = (addr - (unsigned short *)video_mem_base) / video_num_columns; + int xoff = (addr - (unsigned short *)video_mem_base) % video_num_columns; + unsigned short * last = addr; + + while (count) { + count--; + if (*addr != c) { + if (xoff == video_num_columns) { + if (last != addr) + GX_SETW + xoff = 0; + yoff++; + last = addr; + } + *addr++ = c; + xoff++; + } else { + if (last != addr) + GX_SETW + if (xoff == video_num_columns) { + xoff = 0; + yoff++; + } + addr++; + xoff++; + last = addr; + } + } + if (last != addr) + GX_SETW + } else { + while (count) { + count--; + if (*addr != c) { + sun_blitc(c, (unsigned long)addr); + *addr++ = c; + } else + addr++; + } + } +} + +void memcpyw(unsigned short *to, unsigned short *from, unsigned int count) +{ + if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) { + memcpy(to, from, count); + return; + } + if ((unsigned long) to + count > video_mem_term || + (unsigned long) to < video_mem_base) { + if ((unsigned long) to + count <= video_mem_term || + (unsigned long) to > video_mem_base) + memcpy(to, from, count); + else { + count /= 2; + while (count) { + count--; + scr_writew(scr_readw(from++), to++); + } + } +#define GX_CPYW (*fbinfo[0].cpyw)(x_margin + ((xoff - (to - last)) << 3), y_margin + CHAR_HEIGHT * yoff, last, to - last); + } else if (do_accel) { + int yoff = (to - (unsigned short *)video_mem_base) / video_num_columns; + int xoff = (to - (unsigned short *)video_mem_base) % video_num_columns; + unsigned short * last = to; + + count /= 2; + while (count) { + count--; + if (*to != *from) { + if (xoff == video_num_columns) { + if (last != to) + GX_CPYW + xoff = 0; + yoff++; + last = to; + } else if (last != to && (*last & 0xff00) != (*from & 0xff00)) { + GX_CPYW + last = to; + } + *to++ = *from++; + xoff++; + } else { + if (last != to) + GX_CPYW + if (xoff == video_num_columns) { + xoff = 0; + yoff++; + } + to++; + xoff++; + last = to; + from++; + } + } + if (last != to) + GX_CPYW + } else { + count /= 2; + while (count) { + count--; + if (*to != *from) { + sun_blitc(*from, (unsigned long)to); + *to++ = *from++; + } else { + from++; + to++; + } + } + } +} + +#undef pos +int +sun_hw_scursor (struct fbcursor *cursor, fbinfo_t *fb) +{ + int op = cursor->set; + int i, bytes = 0; + + if (op & FB_CUR_SETSHAPE){ + if ((uint) cursor->size.fbx > fb->cursor.hwsize.fbx) + return -EINVAL; + if ((uint) cursor->size.fby > fb->cursor.hwsize.fby) + return -EINVAL; + bytes = (cursor->size.fby * 32)/8; + i = verify_area (VERIFY_READ, cursor->image, bytes); + if (i) return i; + i = verify_area (VERIFY_READ, cursor->mask, bytes); + if (i) return i; + } + if (op & FB_CUR_SETCMAP){ + if (cursor->cmap.index && cursor->cmap.count != 2) + return -EINVAL; + i = verify_area (VERIFY_READ, cursor->cmap.red, 2); + if (i) return i; + i = verify_area (VERIFY_READ, cursor->cmap.green, 2); + if (i) return i; + i = verify_area (VERIFY_READ, cursor->cmap.blue, 2); + if (i) return i; + } + if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){ + if (op & FB_CUR_SETCUR) + fb->cursor.enable = cursor->enable; + if (op & FB_CUR_SETPOS) + fb->cursor.cpos = cursor->pos; + if (op & FB_CUR_SETHOT) + fb->cursor.chot = cursor->hot; + (*fb->setcursor) (fb); + } + if (op & FB_CUR_SETCMAP) + (*fb->setcursormap) (fb, cursor->cmap.red, cursor->cmap.green, cursor->cmap.blue); + if (op & FB_CUR_SETSHAPE){ + uint u; + + fb->cursor.size = cursor->size; + memset ((void *)&fb->cursor.bits, 0, sizeof (fb->cursor.bits)); + memcpy (fb->cursor.bits [0], cursor->mask, bytes); + memcpy (fb->cursor.bits [1], cursor->image, bytes); + u = ~0; + if (cursor->size.fbx < fb->cursor.hwsize.fbx) + u = ~(u >> cursor->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]; + } + (*fb->setcurshape) (fb); + } + return 0; +} + +static unsigned char hw_cursor_cmap[2] = { 0, 0xff }; + +void +sun_hw_hide_cursor (void) +{ + fbinfo[0].cursor.enable = 0; + (*fbinfo[0].setcursor)(&fbinfo[0]); + sun_hw_cursor_shown = 0; +} + +void +sun_hw_set_cursor (int xoff, int yoff) +{ + if (!sun_hw_cursor_shown) { + fbinfo[0].cursor.size.fbx = CHAR_WIDTH; + fbinfo[0].cursor.size.fby = CHAR_HEIGHT; + fbinfo[0].cursor.chot.fbx = 0; + fbinfo[0].cursor.chot.fby = 0; + fbinfo[0].cursor.enable = 1; + memset (fbinfo[0].cursor.bits, 0, sizeof (fbinfo[0].cursor.bits)); + fbinfo[0].cursor.bits[0][CHAR_HEIGHT - 2] = 0xff000000; + fbinfo[0].cursor.bits[1][CHAR_HEIGHT - 2] = 0xff000000; + fbinfo[0].cursor.bits[0][CHAR_HEIGHT - 1] = 0xff000000; + fbinfo[0].cursor.bits[1][CHAR_HEIGHT - 1] = 0xff000000; + (*fbinfo[0].setcursormap) (&fbinfo[0], hw_cursor_cmap, hw_cursor_cmap, hw_cursor_cmap); + (*fbinfo[0].setcurshape) (&fbinfo[0]); + sun_hw_cursor_shown = 1; + } + fbinfo[0].cursor.cpos.fbx = xoff; + fbinfo[0].cursor.cpos.fby = yoff; + (*fbinfo[0].setcursor)(&fbinfo[0]); +} diff --git a/drivers/sbus/char/suncons_font.h b/drivers/sbus/char/suncons_font.h new file mode 100644 index 000000000..87a07b10d --- /dev/null +++ b/drivers/sbus/char/suncons_font.h @@ -0,0 +1,258 @@ +unsigned char vga_font[cmapsz] = { +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x7e,0x81,0xa5,0x81,0x81,0xbd,0x99,0x81,0x81,0x7e,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x7e,0xff,0xdb,0xff,0xff,0xc3,0xe7,0xff,0xff,0x7e,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x6c,0xfe,0xfe,0xfe,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x18,0x3c,0x3c,0xe7,0xe7,0xe7,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x18,0x3c,0x7e,0xff,0xff,0x7e,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0xff,0xff,0xff,0xff,0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff,0xff,0xff,0xff,0xff, +/* */ 0x00,0x00,0x00,0x00,0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00,0x00,0x00,0x00,0x00, +/* */ 0xff,0xff,0xff,0xff,0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff,0xff,0xff,0xff,0xff, +/* */ 0x00,0x00,0x1e,0x0e,0x1a,0x32,0x78,0xcc,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x3c,0x66,0x66,0x66,0x66,0x3c,0x18,0x7e,0x18,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x3f,0x33,0x3f,0x30,0x30,0x30,0x30,0x70,0xf0,0xe0,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x7f,0x63,0x7f,0x63,0x63,0x63,0x63,0x67,0xe7,0xe6,0xc0,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x18,0x18,0xdb,0x3c,0xe7,0x3c,0xdb,0x18,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfe,0xf8,0xf0,0xe0,0xc0,0x80,0x00,0x00,0x00,0x00, +/* */ 0x00,0x02,0x06,0x0e,0x1e,0x3e,0xfe,0x3e,0x1e,0x0e,0x06,0x02,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x66,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x7f,0xdb,0xdb,0xdb,0x7b,0x1b,0x1b,0x1b,0x1b,0x1b,0x00,0x00,0x00,0x00, +/* */ 0x00,0x7c,0xc6,0x60,0x38,0x6c,0xc6,0xc6,0x6c,0x38,0x0c,0xc6,0x7c,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xfe,0xfe,0xfe,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x7e,0x3c,0x18,0x7e,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x18,0x0c,0xfe,0x0c,0x18,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x30,0x60,0xfe,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xfe,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x24,0x66,0xff,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x10,0x38,0x38,0x7c,0x7c,0xfe,0xfe,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0xfe,0xfe,0x7c,0x7c,0x38,0x38,0x10,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*!*/ 0x00,0x00,0x18,0x3c,0x3c,0x3c,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00, +/*"*/ 0x00,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*#*/ 0x00,0x00,0x00,0x6c,0x6c,0xfe,0x6c,0x6c,0x6c,0xfe,0x6c,0x6c,0x00,0x00,0x00,0x00, +/*$*/ 0x18,0x18,0x7c,0xc6,0xc2,0xc0,0x7c,0x06,0x06,0x86,0xc6,0x7c,0x18,0x18,0x00,0x00, +/*%*/ 0x00,0x00,0x00,0x00,0xc2,0xc6,0x0c,0x18,0x30,0x60,0xc6,0x86,0x00,0x00,0x00,0x00, +/*&*/ 0x00,0x00,0x38,0x6c,0x6c,0x38,0x76,0xdc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/*'*/ 0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*(*/ 0x00,0x00,0x0c,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x18,0x0c,0x00,0x00,0x00,0x00, +/*)*/ 0x00,0x00,0x30,0x18,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x18,0x30,0x00,0x00,0x00,0x00, +/***/ 0x00,0x00,0x00,0x00,0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00,0x00,0x00,0x00,0x00, +/*+*/ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7e,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00, +/*,*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00, +/*-*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*.*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x02,0x06,0x0c,0x18,0x30,0x60,0xc0,0x80,0x00,0x00,0x00,0x00, +/*0*/ 0x00,0x00,0x7c,0xc6,0xc6,0xce,0xde,0xf6,0xe6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*1*/ 0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7e,0x00,0x00,0x00,0x00, +/*2*/ 0x00,0x00,0x7c,0xc6,0x06,0x0c,0x18,0x30,0x60,0xc0,0xc6,0xfe,0x00,0x00,0x00,0x00, +/*3*/ 0x00,0x00,0x7c,0xc6,0x06,0x06,0x3c,0x06,0x06,0x06,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*4*/ 0x00,0x00,0x0c,0x1c,0x3c,0x6c,0xcc,0xfe,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00, +/*5*/ 0x00,0x00,0xfe,0xc0,0xc0,0xc0,0xfc,0x06,0x06,0x06,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*6*/ 0x00,0x00,0x38,0x60,0xc0,0xc0,0xfc,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*7*/ 0x00,0x00,0xfe,0xc6,0x06,0x06,0x0c,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00, +/*8*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0x7c,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*9*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0x7e,0x06,0x06,0x06,0x0c,0x78,0x00,0x00,0x00,0x00, +/*:*/ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00, +/*;*/ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00, +/*<*/ 0x00,0x00,0x00,0x06,0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x06,0x00,0x00,0x00,0x00, +/*=*/ 0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*>*/ 0x00,0x00,0x00,0x60,0x30,0x18,0x0c,0x06,0x0c,0x18,0x30,0x60,0x00,0x00,0x00,0x00, +/*?*/ 0x00,0x00,0x7c,0xc6,0xc6,0x0c,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00, +/*@*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xde,0xde,0xde,0xdc,0xc0,0x7c,0x00,0x00,0x00,0x00, +/*A*/ 0x00,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, +/*B*/ 0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x66,0x66,0x66,0x66,0xfc,0x00,0x00,0x00,0x00, +/*C*/ 0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xc0,0xc0,0xc2,0x66,0x3c,0x00,0x00,0x00,0x00, +/*D*/ 0x00,0x00,0xf8,0x6c,0x66,0x66,0x66,0x66,0x66,0x66,0x6c,0xf8,0x00,0x00,0x00,0x00, +/*E*/ 0x00,0x00,0xfe,0x66,0x62,0x68,0x78,0x68,0x60,0x62,0x66,0xfe,0x00,0x00,0x00,0x00, +/*F*/ 0x00,0x00,0xfe,0x66,0x62,0x68,0x78,0x68,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00, +/*G*/ 0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xde,0xc6,0xc6,0x66,0x3a,0x00,0x00,0x00,0x00, +/*H*/ 0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, +/*I*/ 0x00,0x00,0x3c,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/*J*/ 0x00,0x00,0x1e,0x0c,0x0c,0x0c,0x0c,0x0c,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00,0x00, +/*K*/ 0x00,0x00,0xe6,0x66,0x66,0x6c,0x78,0x78,0x6c,0x66,0x66,0xe6,0x00,0x00,0x00,0x00, +/*L*/ 0x00,0x00,0xf0,0x60,0x60,0x60,0x60,0x60,0x60,0x62,0x66,0xfe,0x00,0x00,0x00,0x00, +/*M*/ 0x00,0x00,0xc3,0xe7,0xff,0xff,0xdb,0xc3,0xc3,0xc3,0xc3,0xc3,0x00,0x00,0x00,0x00, +/*N*/ 0x00,0x00,0xc6,0xe6,0xf6,0xfe,0xde,0xce,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, +/*O*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*P*/ 0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x60,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00, +/*Q*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xd6,0xde,0x7c,0x0c,0x0e,0x00,0x00, +/*R*/ 0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x6c,0x66,0x66,0x66,0xe6,0x00,0x00,0x00,0x00, +/*S*/ 0x00,0x00,0x7c,0xc6,0xc6,0x60,0x38,0x0c,0x06,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*T*/ 0x00,0x00,0xff,0xdb,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/*U*/ 0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*V*/ 0x00,0x00,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x00,0x00,0x00,0x00, +/*W*/ 0x00,0x00,0xc3,0xc3,0xc3,0xc3,0xc3,0xdb,0xdb,0xff,0x66,0x66,0x00,0x00,0x00,0x00, +/*X*/ 0x00,0x00,0xc3,0xc3,0x66,0x3c,0x18,0x18,0x3c,0x66,0xc3,0xc3,0x00,0x00,0x00,0x00, +/*Y*/ 0x00,0x00,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/*Z*/ 0x00,0x00,0xff,0xc3,0x86,0x0c,0x18,0x30,0x60,0xc1,0xc3,0xff,0x00,0x00,0x00,0x00, +/*[*/ 0x00,0x00,0x3c,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3c,0x00,0x00,0x00,0x00, +/*\*/ 0x00,0x00,0x00,0x80,0xc0,0xe0,0x70,0x38,0x1c,0x0e,0x06,0x02,0x00,0x00,0x00,0x00, +/*]*/ 0x00,0x00,0x3c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x3c,0x00,0x00,0x00,0x00, +/*^*/ 0x10,0x38,0x6c,0xc6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*_*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00, +/*`*/ 0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*a*/ 0x00,0x00,0x00,0x00,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/*b*/ 0x00,0x00,0xe0,0x60,0x60,0x78,0x6c,0x66,0x66,0x66,0x66,0x7c,0x00,0x00,0x00,0x00, +/*c*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xc0,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*d*/ 0x00,0x00,0x1c,0x0c,0x0c,0x3c,0x6c,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/*e*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*f*/ 0x00,0x00,0x38,0x6c,0x64,0x60,0xf0,0x60,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00, +/*g*/ 0x00,0x00,0x00,0x00,0x00,0x76,0xcc,0xcc,0xcc,0xcc,0xcc,0x7c,0x0c,0xcc,0x78,0x00, +/*h*/ 0x00,0x00,0xe0,0x60,0x60,0x6c,0x76,0x66,0x66,0x66,0x66,0xe6,0x00,0x00,0x00,0x00, +/*i*/ 0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/*j*/ 0x00,0x00,0x06,0x06,0x00,0x0e,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3c,0x00, +/*k*/ 0x00,0x00,0xe0,0x60,0x60,0x66,0x6c,0x78,0x78,0x6c,0x66,0xe6,0x00,0x00,0x00,0x00, +/*l*/ 0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/*m*/ 0x00,0x00,0x00,0x00,0x00,0xe6,0xff,0xdb,0xdb,0xdb,0xdb,0xdb,0x00,0x00,0x00,0x00, +/*n*/ 0x00,0x00,0x00,0x00,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00, +/*o*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*p*/ 0x00,0x00,0x00,0x00,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x7c,0x60,0x60,0xf0,0x00, +/*q*/ 0x00,0x00,0x00,0x00,0x00,0x76,0xcc,0xcc,0xcc,0xcc,0xcc,0x7c,0x0c,0x0c,0x1e,0x00, +/*r*/ 0x00,0x00,0x00,0x00,0x00,0xdc,0x76,0x66,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00, +/*s*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0x60,0x38,0x0c,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*t*/ 0x00,0x00,0x10,0x30,0x30,0xfc,0x30,0x30,0x30,0x30,0x36,0x1c,0x00,0x00,0x00,0x00, +/*u*/ 0x00,0x00,0x00,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/*v*/ 0x00,0x00,0x00,0x00,0x00,0xc3,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x00,0x00,0x00,0x00, +/*w*/ 0x00,0x00,0x00,0x00,0x00,0xc3,0xc3,0xc3,0xdb,0xdb,0xff,0x66,0x00,0x00,0x00,0x00, +/*x*/ 0x00,0x00,0x00,0x00,0x00,0xc3,0x66,0x3c,0x18,0x3c,0x66,0xc3,0x00,0x00,0x00,0x00, +/*y*/ 0x00,0x00,0x00,0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7e,0x06,0x0c,0xf8,0x00, +/*z*/ 0x00,0x00,0x00,0x00,0x00,0xfe,0xcc,0x18,0x30,0x60,0xc6,0xfe,0x00,0x00,0x00,0x00, +/*{*/ 0x00,0x00,0x0e,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0e,0x00,0x00,0x00,0x00, +/*|*/ 0x00,0x00,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, +/*}*/ 0x00,0x00,0x70,0x18,0x18,0x18,0x0e,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00, +/*~*/ 0x00,0x00,0x76,0xdc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xc6,0xfe,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xc0,0xc2,0x66,0x3c,0x0c,0x06,0x7c,0x00,0x00, +/* */ 0x00,0x00,0xcc,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x0c,0x18,0x30,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x10,0x38,0x6c,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0xcc,0x00,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x60,0x30,0x18,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x38,0x6c,0x38,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x3c,0x66,0x60,0x60,0x66,0x3c,0x0c,0x06,0x3c,0x00,0x00,0x00, +/* */ 0x00,0x10,0x38,0x6c,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0xc6,0x00,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x60,0x30,0x18,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x66,0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x18,0x3c,0x66,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x60,0x30,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/* */ 0x00,0xc6,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, +/* */ 0x38,0x6c,0x38,0x00,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, +/* */ 0x18,0x30,0x60,0x00,0xfe,0x66,0x60,0x7c,0x60,0x60,0x66,0xfe,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x6e,0x3b,0x1b,0x7e,0xd8,0xdc,0x77,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x3e,0x6c,0xcc,0xcc,0xfe,0xcc,0xcc,0xcc,0xcc,0xce,0x00,0x00,0x00,0x00, +/* */ 0x00,0x10,0x38,0x6c,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0xc6,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x60,0x30,0x18,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x30,0x78,0xcc,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x60,0x30,0x18,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0xc6,0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7e,0x06,0x0c,0x78,0x00, +/* */ 0x00,0xc6,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0xc6,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x18,0x18,0x7e,0xc3,0xc0,0xc0,0xc0,0xc3,0x7e,0x18,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x38,0x6c,0x64,0x60,0xf0,0x60,0x60,0x60,0x60,0xe6,0xfc,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0xc3,0x66,0x3c,0x18,0xff,0x18,0xff,0x18,0x18,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0xfc,0x66,0x66,0x7c,0x62,0x66,0x6f,0x66,0x66,0x66,0xf3,0x00,0x00,0x00,0x00, +/* */ 0x00,0x0e,0x1b,0x18,0x18,0x18,0x7e,0x18,0x18,0x18,0x18,0x18,0xd8,0x70,0x00,0x00, +/* */ 0x00,0x18,0x30,0x60,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x0c,0x18,0x30,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x18,0x30,0x60,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x18,0x30,0x60,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x76,0xdc,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00, +/* */ 0x76,0xdc,0x00,0xc6,0xe6,0xf6,0xfe,0xde,0xce,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, +/* */ 0x00,0x3c,0x6c,0x6c,0x3e,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x38,0x6c,0x6c,0x38,0x00,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x60,0xc0,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0xc0,0xc0,0xc2,0xc6,0xcc,0x18,0x30,0x60,0xce,0x9b,0x06,0x0c,0x1f,0x00,0x00, +/* */ 0x00,0xc0,0xc0,0xc2,0xc6,0xcc,0x18,0x30,0x66,0xce,0x96,0x3e,0x06,0x06,0x00,0x00, +/* */ 0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x18,0x3c,0x3c,0x3c,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x36,0x6c,0xd8,0x6c,0x36,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0xd8,0x6c,0x36,0x6c,0xd8,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44, +/* */ 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa, +/* */ 0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x36,0x36,0x36,0x36,0x36,0xf6,0x06,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x00,0x00,0x00,0x00,0x00,0xfe,0x06,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x36,0x36,0x36,0x36,0x36,0xf6,0x06,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x3f,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x36,0x36,0x36,0x36,0x36,0xf7,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xf7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x36,0x36,0x36,0x36,0x36,0xf7,0x00,0xf7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x18,0x18,0x18,0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xff,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x18,0x18,0x18,0x18,0x18,0xff,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +/* */ 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0, +/* */ 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, +/* */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x76,0xdc,0xd8,0xd8,0xd8,0xdc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x78,0xcc,0xcc,0xcc,0xd8,0xcc,0xc6,0xc6,0xc6,0xcc,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0xfe,0xc6,0xc6,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0xfe,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0xfe,0xc6,0x60,0x30,0x18,0x30,0x60,0xc6,0xfe,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x7e,0xd8,0xd8,0xd8,0xd8,0xd8,0x70,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x7c,0x60,0x60,0xc0,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x76,0xdc,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x7e,0x18,0x3c,0x66,0x66,0x66,0x3c,0x18,0x7e,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0x6c,0x38,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x38,0x6c,0xc6,0xc6,0xc6,0x6c,0x6c,0x6c,0x6c,0xee,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x1e,0x30,0x18,0x0c,0x3e,0x66,0x66,0x66,0x66,0x3c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x7e,0xdb,0xdb,0xdb,0x7e,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x03,0x06,0x7e,0xdb,0xdb,0xf3,0x7e,0x60,0xc0,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x1c,0x30,0x60,0x60,0x7c,0x60,0x60,0x60,0x30,0x1c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0xfe,0x00,0x00,0xfe,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x18,0x18,0x7e,0x18,0x18,0x00,0x00,0xff,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x30,0x18,0x0c,0x06,0x0c,0x18,0x30,0x00,0x7e,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x00,0x7e,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x0e,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xd8,0xd8,0xd8,0x70,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x7e,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x76,0xdc,0x00,0x76,0xdc,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x38,0x6c,0x6c,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0xec,0x6c,0x6c,0x3c,0x1c,0x00,0x00,0x00,0x00, +/* */ 0x00,0xd8,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x70,0xd8,0x30,0x60,0xc8,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; diff --git a/drivers/sbus/char/sunfb.c b/drivers/sbus/char/sunfb.c new file mode 100644 index 000000000..e803344bd --- /dev/null +++ b/drivers/sbus/char/sunfb.c @@ -0,0 +1,315 @@ +/* $Id: sunfb.c,v 1.22 1997/04/03 08:47:56 davem Exp $ + * sunfb.c: Sun generic frame buffer support. + * + * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * + * Added getcmap ioctl, may, 96 + * Support for multiple fbs, sep, 96 + */ + +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/kd.h> +#include <linux/malloc.h> +#include <linux/major.h> +#include <linux/mm.h> +#include <linux/types.h> +#include <linux/proc_fs.h> + +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/bitops.h> +#include <asm/oplib.h> +#include <asm/sbus.h> +#include <asm/fbio.h> +#include <asm/io.h> + +#include "../../char/kbd_kern.h" +#include "../../char/vt_kern.h" +#include "../../char/consolemap.h" +#include "../../char/selection.h" +#include "../../char/console_struct.h" + +#include "fb.h" + +extern void set_other_palette (int); +extern void sun_clear_fb(int); +extern void set_cursor (int); + +#define FB_SETUP(err) \ + int minor = FB_DEV (inode->i_rdev); \ +\ + if (minor >= fbinfos || \ + fbinfo [minor].type.fb_type == FBTYPE_NOTYPE) \ + return -(err); + +static int +fb_open (struct inode * inode, struct file * file) +{ + FB_SETUP(EBADF) + if (fbinfo [minor].open) + return -EBUSY; + fbinfo [minor].mmaped = 0; + fbinfo [minor].open = 1; + return 0; +} + +static int +fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg) +{ + fbinfo_t *fb; + struct fbcmap *cmap; + int i; + FB_SETUP(EBADF) + + fb = &fbinfo [minor]; + + switch (cmd){ + case FBIOGTYPE: /* return frame buffer type */ + copy_to_user_ret((struct fbtype *)arg, &fb->type, sizeof(struct fbtype), -EFAULT); + break; + case FBIOGATTR:{ + struct fbgattr *fba = (struct fbgattr *) arg; + + i = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct fbgattr)); + if (i) return i; + __put_user_ret(fb->real_type, &fba->real_type, -EFAULT); + __put_user_ret(0, &fba->owner, -EFAULT); + __copy_to_user_ret(&fba->fbtype, &fb->type, + sizeof(struct fbtype), -EFAULT); + __put_user_ret(0, &fba->sattr.flags, -EFAULT); + __put_user_ret(fb->type.fb_type, &fba->sattr.emu_type, -EFAULT); + __put_user_ret(-1, &fba->sattr.dev_specific[0], -EFAULT); + __put_user_ret(fb->type.fb_type, &fba->emu_types[0], -EFAULT); + for (i = 1; i < 4; i++) + put_user_ret(fb->emulations[i], &fba->emu_types[i], -EFAULT); + break; + } + case FBIOSATTR: + i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbsattr)); + if (i) return i; + return -EINVAL; + case FBIOSVIDEO: + if (fb == fbinfo && vt_cons[fg_console]->vc_mode == KD_TEXT) + break; + get_user_ret(i, (int *)arg, -EFAULT); + if (i){ + if (!fb->blanked || !fb->unblank) + break; + if (!minor || (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: + put_user_ret(fb->blanked, (int *) arg, -EFAULT); + break; + case FBIOGETCMAP: { + char *rp, *gp, *bp; + int end, count, index; + + if (!fb->loadcmap) + return -EINVAL; + i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbcmap)); + if (i) return i; + cmap = (struct fbcmap *) arg; + __get_user_ret(count, &cmap->count, -EFAULT); + __get_user_ret(index, &cmap->index, -EFAULT); + if ((index < 0) || (index > 255)) + return -EINVAL; + if (index + count > 256) + count = 256 - cmap->index; + __get_user_ret(rp, &cmap->red, -EFAULT); + __get_user_ret(gp, &cmap->green, -EFAULT); + __get_user_ret(bp, &cmap->blue, -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++){ + __put_user_ret(fb->color_map CM(i,0), rp, -EFAULT); + __put_user_ret(fb->color_map CM(i,1), gp, -EFAULT); + __put_user_ret(fb->color_map CM(i,2), bp, -EFAULT); + rp++; gp++; bp++; + } + (*fb->loadcmap)(fb, cmap->index, count); + break; + + } + case FBIOPUTCMAP: { /* load color map entries */ + char *rp, *gp, *bp; + int end, count, index; + + if (!fb->loadcmap) + return -EINVAL; + i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbcmap)); + if (i) return i; + cmap = (struct fbcmap *) arg; + __get_user_ret(count, &cmap->count, -EFAULT); + __get_user_ret(index, &cmap->index, -EFAULT); + if ((index < 0) || (index > 255)) + return -EINVAL; + if (index + count > 256) + count = 256 - cmap->index; + __get_user_ret(rp, &cmap->red, -EFAULT); + __get_user_ret(gp, &cmap->green, -EFAULT); + __get_user_ret(bp, &cmap->blue, -EFAULT); + if(verify_area (VERIFY_READ, rp, cmap->count)) return -EFAULT; + if(verify_area (VERIFY_READ, gp, cmap->count)) return -EFAULT; + if(verify_area (VERIFY_READ, bp, cmap->count)) return -EFAULT; + + end = index + count; + for (i = index; i < end; i++){ + __get_user_ret(fb->color_map CM(i,0), rp, -EFAULT); + __get_user_ret(fb->color_map CM(i,1), gp, -EFAULT); + __get_user_ret(fb->color_map CM(i,2), bp, -EFAULT); + rp++; gp++; bp++; + } + (*fb->loadcmap)(fb, cmap->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; + __put_user_ret(fb->cursor.hwsize.fbx, &p->fbx, -EFAULT); + __put_user_ret(fb->cursor.hwsize.fby, &p->fby, -EFAULT); + break; + } + case FBIOSCURSOR: + if (!fb->setcursor) return -EINVAL; + if (fb == fbinfo) { + if (vt_cons[fg_console]->vc_mode == KD_TEXT) + return -EINVAL; /* Don't let graphics programs hide our nice text cursor */ + sun_hw_cursor_shown = 0; /* Forget state of our text cursor */ + } + return sun_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 == fbinfo) { + if (vt_cons[fg_console]->vc_mode == KD_TEXT) + return -EINVAL; /* Don't let graphics programs move our nice text cursor */ + } + i= verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbcurpos)); + if (i) return i; + fb->cursor.cpos = *(struct fbcurpos *)arg; + (*fb->setcursor) (fb); + break; + + default: + if (fb->ioctl){ + i = fb->ioctl (inode, file, cmd, arg, fb); + if (i == -ENOSYS) { + printk ("[[FBIO: %8.8x]]\n", cmd); + return -EINVAL; + } + return i; + } + printk ("[[FBIO: %8.8x]]\n", cmd); + return -EINVAL; + } + return 0; +} + +static int +fb_close (struct inode * inode, struct file *filp) +{ + fbinfo_t *fb; + struct fbcursor cursor; + FB_SETUP(EBADF) + + fb = &fbinfo[minor]; + + if (!minor) + vt_cons [fb->vtconsole]->vc_mode = KD_TEXT; + + /* Leaving graphics mode, turn off the cursor */ + if (fb->mmaped) + sun_clear_fb (minor); + cursor.set = FB_CUR_SETCUR; + cursor.enable = 0; + + /* Reset the driver */ + if (fb->reset) + fb->reset(fb); + + if (fb->open) + fb->open = 0; + fb_ioctl (inode, filp, FBIOSCURPOS, (unsigned long) &cursor); + set_other_palette (minor); + if (!minor) { + render_screen (); + set_cursor (fg_console); + } else if (fb->blank) + (*fb->blank)(fb); + return 0; +} + +static int +fb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma) +{ + fbinfo_t *fb; + FB_SETUP(ENXIO) + + fb = &fbinfo [minor]; + + if (fb->mmap){ + int v; + + v = (*fb->mmap)(inode, file, vma, fb->base, fb); + if (v) return v; + if (!fb->mmaped) { + fb->mmaped = 1; + if (!minor) { + fb->vtconsole = fg_console; + vt_cons [fg_console]->vc_mode = KD_GRAPHICS; + } else { + if (fb->unblank && !fb->blanked) + (*fb->unblank)(fb); + } + } + return 0; + } else + return -ENXIO; +} + +static struct file_operations graphdev_fops = +{ + NULL, /* lseek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + fb_ioctl, + fb_mmap, + fb_open, /* open */ + fb_close, /* close */ +}; + +__initfunc(int fb_init (void)) +{ + /* Register the frame buffer device */ + if (register_chrdev (GRAPHDEV_MAJOR, "graphics", &graphdev_fops)){ + printk ("Could not register graphics device\n"); + return -EIO; + } + return 0; /* success */ +} diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c index b5d3c42ea..87fb0fea4 100644 --- a/drivers/sbus/char/sunkbd.c +++ b/drivers/sbus/char/sunkbd.c @@ -14,11 +14,16 @@ #include <linux/signal.h> #include <linux/string.h> #include <linux/fcntl.h> +#include <linux/poll.h> +#include <linux/random.h> +#include <linux/init.h> + #include <asm/kbio.h> #include <asm/vuid_event.h> #include <asm/delay.h> #include <asm/bitops.h> #include <asm/oplib.h> +#include <asm/uaccess.h> #include "../../char/kbd_kern.h" #include "../../char/diacr.h" @@ -58,7 +63,15 @@ extern void reset_vc(unsigned int new_console); extern void scrollback(int); extern void scrollfront(int); -unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */ +unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */ +unsigned char aux_device_present = 0x00; /* To make kernel/ksyms.c happy */ + +struct wait_queue * keypress_wait = NULL; + +void keyboard_wait_for_keypress(void) +{ + sleep_on(&keypress_wait); +} /* * global state includes the following, and various static variables @@ -89,6 +102,9 @@ struct kbd_struct kbd_table[MAX_NR_CONSOLES]; static struct tty_struct **ttytab; static struct kbd_struct * kbd = kbd_table; static struct tty_struct * tty = NULL; +static int compose_led_on = 0; +static int kbd_delay_ticks = HZ / 5; +static int kbd_rate_ticks = HZ / 20; extern void compute_shiftstate(void); @@ -141,8 +157,8 @@ volatile unsigned char sunkbd_type; #define SUNKBD_TYPE3 0x03 #define SUNKBD_TYPE4 0x04 -#define SUNKBD_LOUT_TYP4 0x00 -#define SUNKBD_LOUT_TYP5 0x22 +#define SUNKBD_LOUT_TYP4 0x00 +#define SUNKBD_LOUT_TYP5_MASK 0x20 volatile int kbd_reset_pending; volatile int kbd_layout_pending; @@ -213,6 +229,8 @@ static inline unsigned char vcleds_to_sunkbd(unsigned char vcleds) retval |= LED_NLOCK; if(vcleds & (1<<VC_CAPSLOCK)) retval |= LED_CLOCK; + if(compose_led_on) + retval |= LED_CMPOSE; return retval; } @@ -331,6 +349,21 @@ static unsigned char e0_keys[128] = { 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ }; +/* we use this map to determine if a particular key should not be + autorepeated. We don't autorepeat CONTROL, LSHIFT, CAPS, + ALT, LMETA, RSHIFT, RMETA, ALTG and COMPOSE */ +static unsigned char norepeat_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, /* 0x00-0x0f */ + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x2f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30-0x3f */ + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, /* 0x40-0x4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50-0x5f */ + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, /* 0x60-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, /* 0x70-0x7f */ +}; + + int setkeycode(unsigned int scancode, unsigned int keycode) { if (scancode < SC_LIM || scancode > 255 || keycode > 127) @@ -350,7 +383,7 @@ int getkeycode(unsigned int scancode) e0_keys[scancode - 128]; } -void sunkbd_inchar(unsigned char ch, unsigned char status, struct pt_regs *regs); +void sunkbd_inchar(unsigned char ch, struct pt_regs *regs); static void keyboard_timer (unsigned long ignored); static struct timer_list @@ -367,16 +400,18 @@ keyboard_timer (unsigned long ignored) save_flags(flags); cli(); /* Auto repeat: send regs = 0 to indicate autorepeat */ - sunkbd_inchar (last_keycode, 0, 0); + sunkbd_inchar (last_keycode, 0); del_timer (&auto_repeat_timer); - auto_repeat_timer.expires = jiffies + HZ/20; - add_timer (&auto_repeat_timer); + if (kbd_rate_ticks) { + auto_repeat_timer.expires = jiffies + kbd_rate_ticks; + add_timer (&auto_repeat_timer); + } restore_flags(flags); } /* #define SKBD_DEBUG */ /* This is our keyboard 'interrupt' routine. */ -void sunkbd_inchar(unsigned char ch, unsigned char status, struct pt_regs *regs) +void sunkbd_inchar(unsigned char ch, struct pt_regs *regs) { unsigned char keycode; char up_flag; /* 0 or SUNKBD_UBIT */ @@ -401,7 +436,9 @@ void sunkbd_inchar(unsigned char ch, unsigned char status, struct pt_regs *regs) kbd_layout_pending = 0; return; } else if(ch == SKBD_ALLUP) { - /* eat it */ + del_timer (&auto_repeat_timer); + memset(key_down, 0, sizeof(key_down)); + compute_shiftstate(); return; } #ifdef SKBD_DEBUG @@ -413,11 +450,13 @@ void sunkbd_inchar(unsigned char ch, unsigned char status, struct pt_regs *regs) #endif /* Whee, a real character. */ - if (regs){ + if(regs) { pt_regs = regs; last_keycode = keycode = ch; - } else + } else { keycode = ch; + } + add_keyboard_randomness(keycode); mark_bh(KEYBOARD_BH); do_poke_blanked_console = 1; @@ -441,8 +480,13 @@ void sunkbd_inchar(unsigned char ch, unsigned char status, struct pt_regs *regs) rep = 0; clear_bit(keycode, key_down); } else { - auto_repeat_timer.expires = jiffies+HZ/5; - add_timer (&auto_repeat_timer); + if (!norepeat_keys[keycode]) { + if (kbd_rate_ticks) { + auto_repeat_timer.expires = + jiffies + kbd_delay_ticks; + add_timer (&auto_repeat_timer); + } + } rep = set_bit(keycode, key_down); } @@ -661,6 +705,8 @@ static void boot_it(void) static void compose(void) { dead_key_next = 1; + compose_led_on = 1; + set_leds(); } int spawnpid, spawnsig; @@ -718,8 +764,11 @@ static void do_self(unsigned char value, char up_flag) if (up_flag) return; /* no action, if this is a key release */ - if (diacr) + if (diacr) { value = handle_diacr(value); + compose_led_on = 0; + set_leds(); + } if (dead_key_next) { dead_key_next = 0; @@ -782,7 +831,7 @@ static void do_cons(unsigned char value, char up_flag) { if (up_flag) return; - want_console = value; + set_console(value); } static void do_fn(unsigned char value, char up_flag) @@ -971,6 +1020,7 @@ static void do_lock(unsigned char value, char up_flag) */ static unsigned char ledstate = 0xff; /* undefined */ +static unsigned char sunkbd_ledstate = 0xff; /* undefined */ static unsigned char ledioctl; unsigned char getledstate(void) { @@ -1050,15 +1100,56 @@ static inline unsigned char getleds(void){ static void kbd_bh(void) { unsigned char leds = getleds(); + unsigned char kbd_leds = vcleds_to_sunkbd(leds); - if (leds != ledstate) { + if (kbd_leds != sunkbd_ledstate) { ledstate = leds; + sunkbd_ledstate = kbd_leds; send_cmd(SKBDCMD_SETLED); - send_cmd(vcleds_to_sunkbd(leds)); + send_cmd(kbd_leds); } } -int kbd_init(void) +/* Support for keyboard "beeps". */ + +/* Timer routine to turn off the beep after the interval expires. */ +static void sunkbd_kd_nosound(unsigned long __unused) +{ + send_cmd(SKBDCMD_BELLOFF); +} + +/* + * Initiate a keyboard beep. If the frequency is zero, then we stop + * the beep. Any other frequency will start a monotone beep. The beep + * will be stopped by a timer after "ticks" jiffies. If ticks is 0, + * then we do not start a timer. + */ +static void sunkbd_kd_mksound(unsigned int hz, unsigned int ticks) +{ + unsigned long flags; + static struct timer_list sound_timer = { NULL, NULL, 0, 0, + sunkbd_kd_nosound }; + + save_flags(flags); + cli(); + + del_timer(&sound_timer); + + if (hz) { + send_cmd(SKBDCMD_BELLON); + if (ticks) { + sound_timer.expires = jiffies + ticks; + add_timer(&sound_timer); + } + } else + send_cmd(SKBDCMD_BELLOFF); + + restore_flags(flags); +} + +extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); + +__initfunc(int kbd_init(void)) { int i, opt_node; struct kbd_struct kbd0; @@ -1075,6 +1166,8 @@ int kbd_init(void) ttytab = console_driver.table; + kd_mksound = sunkbd_kd_mksound; + /* XXX Check keyboard-click? property in 'options' PROM node XXX */ if(sparc_cpu_model != sun4) { opt_node = prom_getchild(prom_root_node); @@ -1098,6 +1191,7 @@ int kbd_init(void) static Firm_event kbd_queue [KBD_QSIZE]; static int kbd_head, kbd_tail; char kbd_opened; +static int kbd_active = 0; static struct wait_queue *kbd_wait; static struct fasync_struct *kb_fasync; @@ -1119,8 +1213,8 @@ push_kbd (int scan) wake_up_interruptible (&kbd_wait); } -static int -kbd_read (struct inode *inode, struct file *f, char *buffer, int count) +static long +kbd_read (struct inode *inode, struct file *f, char *buffer, unsigned long count) { struct wait_queue wait = { current, NULL }; char *end, *p; @@ -1141,7 +1235,7 @@ kbd_read (struct inode *inode, struct file *f, char *buffer, int count) end = buffer+count; p = buffer; for (; p < end && kbd_head != kbd_tail; p += sizeof (Firm_event)){ - *(Firm_event *)p = kbd_queue [kbd_tail]; + copy_to_user_ret((Firm_event *)p, &kbd_queue [kbd_tail], sizeof(Firm_event), -EFAULT); #ifdef KBD_DEBUG printk ("[%s]", kbd_queue [kbd_tail].value == VKEY_UP ? "UP" : "DOWN"); #endif @@ -1163,47 +1257,40 @@ kbd_fasync (struct inode *inode, struct file *filp, int on) return 0; } -static int -kbd_select (struct inode *i, struct file *f, int sel_type, select_table *wait) +static unsigned int kbd_poll (struct file *f, poll_table *wait) { - if (sel_type != SEL_IN) - return 0; + poll_wait(&kbd_wait, wait); if (kbd_head != kbd_tail) - return 1; - select_wait (&kbd_wait, wait); + return POLLIN | POLLRDNORM; return 0; } static int kbd_ioctl (struct inode *i, struct file *f, unsigned int cmd, unsigned long arg) { + unsigned char c; + unsigned char leds = 0; + int value; + switch (cmd){ - case KIOCTYPE: /* return keyboard type */ - if (verify_area (VERIFY_WRITE, (void *)arg, sizeof (int))) - return -EFAULT; - *(int *) arg = sunkbd_type; + case KIOCTYPE: /* return keyboard type */ + put_user_ret(sunkbd_type, (int *) arg, -EFAULT); break; case KIOCGTRANS: - if (verify_area (VERIFY_WRITE, (void *) arg, sizeof (int))) - return -EFAULT; - *(int *) arg = TR_UNTRANS_EVENT; + put_user_ret(TR_UNTRANS_EVENT, (int *) arg, -EFAULT); break; case KIOCTRANS: - if (verify_area (VERIFY_READ, (void *) arg, sizeof (int))) - return -EFAULT; - if (*(int *) arg != TR_UNTRANS_EVENT) + get_user_ret(value, (int *) arg, -EFAULT); + if (value != TR_UNTRANS_EVENT) return -EINVAL; break; case KIOCLAYOUT: - if (verify_area (VERIFY_WRITE, (void *) arg, sizeof (int))) - return -EFAULT; - *(int *) arg = sunkbd_layout; + put_user_ret(sunkbd_layout, (int *) arg, -EFAULT); break; case KIOCSDIRECT: - if (verify_area (VERIFY_WRITE, (void *) arg, sizeof (int))) - return -EFAULT; #ifndef CODING_NEW_DRIVER - if (*(int *) arg) + get_user_ret(value, (int *) arg, -EFAULT); + if(value) kbd_redirected = fg_console + 1; else kbd_redirected = 0; @@ -1211,16 +1298,75 @@ kbd_ioctl (struct inode *i, struct file *f, unsigned int cmd, unsigned long arg) #endif break; case KIOCCMD: - /* Need to support beep on/off, keyclick on/off */ + get_user_ret(value, (int *) arg, -EFAULT); + c = (unsigned char) value; + switch (c) { + case SKBDCMD_CLICK: + case SKBDCMD_NOCLICK: + send_cmd(c); + return 0; + case SKBDCMD_BELLON: + kd_mksound(1,0); + return 0; + case SKBDCMD_BELLOFF: + kd_mksound(0,0); + return 0; + default: + return -EINVAL; + } + case KIOCSLED: + get_user_ret(c, (unsigned char *) arg, -EFAULT); + + if (c & LED_SCRLCK) leds |= (1 << VC_SCROLLOCK); + if (c & LED_NLOCK) leds |= (1 << VC_NUMLOCK); + if (c & LED_CLOCK) leds |= (1 << VC_CAPSLOCK); + compose_led_on = !!(c & LED_CMPOSE); + setledstate(kbd_table + fg_console, leds); + break; + case KIOCGLED: + put_user_ret(vcleds_to_sunkbd(getleds()), (unsigned char *) arg, -EFAULT); + break; + case KIOCGRATE: + { + struct kbd_rate rate; + + rate.delay = kbd_delay_ticks; + if (kbd_rate_ticks) + rate.rate = HZ / kbd_rate_ticks; + else + rate.rate = 0; + + copy_to_user_ret((struct kbd_rate *)arg, &rate, + sizeof(struct kbd_rate), -EFAULT); + + return 0; + } + case KIOCSRATE: + { + struct kbd_rate rate; + + if (verify_area(VERIFY_READ, (void *)arg, + sizeof(struct kbd_rate))) + return -EFAULT; + copy_from_user(&rate, (struct kbd_rate *)arg, + sizeof(struct kbd_rate)); + + if (rate.rate > 50) + return -EINVAL; + if (rate.rate == 0) + kbd_rate_ticks = 0; + else + kbd_rate_ticks = HZ / rate.rate; + kbd_delay_ticks = rate.delay; + return 0; + } case FIONREAD: /* return number of bytes in kbd queue */ { int count; - if (verify_area (VERIFY_WRITE, (void *) arg, sizeof (int))) - return -EFAULT; count = kbd_head - kbd_tail; - * (int *)arg = (count < 0) ? KBD_QSIZE - count : count; + put_user_ret((count < 0) ? KBD_QSIZE - count : count, (int *) arg, -EFAULT); return 0; } default: @@ -1233,22 +1379,30 @@ kbd_ioctl (struct inode *i, struct file *f, unsigned int cmd, unsigned long arg) static int kbd_open (struct inode *i, struct file *f) { + kbd_active++; + if (kbd_opened) return 0; + kbd_opened = fg_console + 1; kbd_head = kbd_tail = 0; return 0; } -static void +static int kbd_close (struct inode *i, struct file *f) { + if (--kbd_active) + return 0; + if (kbd_redirected) kbd_table [kbd_opened-1].kbdmode = VC_XLATE; + kbd_redirected = 0; kbd_opened = 0; kbd_fasync (i, f, 0); + return 0; } static struct @@ -1258,7 +1412,7 @@ file_operations kbd_fops = kbd_read, /* read */ NULL, /* write */ NULL, /* readdir */ - kbd_select, /* select */ + kbd_poll, /* poll */ kbd_ioctl, /* ioctl */ NULL, /* mmap */ kbd_open, /* open */ @@ -1269,8 +1423,7 @@ file_operations kbd_fops = NULL, /* revalidate */ }; -void -keyboard_zsinit(void) +__initfunc(void keyboard_zsinit(void)) { int timeout = 0; @@ -1295,15 +1448,18 @@ keyboard_zsinit(void) while(timeout++ < 500000) barrier(); printk("Sun TYPE %d keyboard detected ", - ((sunkbd_layout==SUNKBD_LOUT_TYP5) ? 5 : 4)); + ((sunkbd_layout & SUNKBD_LOUT_TYP5_MASK) ? 5 : 4)); } if(sunkbd_type == SUNKBD_TYPE2) sunkbd_clickp = 0; - if(sunkbd_clickp) + if(sunkbd_clickp) { + send_cmd(SKBDCMD_CLICK); printk("with keyclick\n"); - else + } else { + send_cmd(SKBDCMD_NOCLICK); printk("without keyclick\n"); + } /* Dork with led lights, then turn them all off */ send_cmd(SKBDCMD_SETLED); send_cmd(0xf); /* All on */ diff --git a/drivers/sbus/char/sunkeymap.c b/drivers/sbus/char/sunkeymap.c index 17b2f5789..076367f59 100644 --- a/drivers/sbus/char/sunkeymap.c +++ b/drivers/sbus/char/sunkeymap.c @@ -26,8 +26,8 @@ u_short plain_map[NR_KEYS] = { u_short shift_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf10a, 0xf10b, 0xf113, - 0xf10c, 0xf10a, 0xf10d, 0xf10b, 0xf10e, 0xf701, 0xf105, 0xf200, - 0xf110, 0xf107, 0xf112, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf203, + 0xf10c, 0xf10a, 0xf10d, 0xf10b, 0xf10e, 0xf701, 0xf10f, 0xf200, + 0xf110, 0xf111, 0xf112, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf203, 0xf601, 0xf200, 0xf200, 0xf600, 0xf602, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07e, 0xf07f, 0xf115, 0xf200, 0xf30d, 0xf30c, @@ -45,7 +45,7 @@ u_short shift_map[NR_KEYS] = { u_short altgr_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf50c, 0xf50d, 0xf515, - 0xf50e, 0xf516, 0xf50f, 0xf517, 0xf510, 0xf701, 0xf200, 0xf200, + 0xf50e, 0xf516, 0xf50f, 0xf517, 0xf510, 0xf701, 0xf511, 0xf200, 0xf512, 0xf513, 0xf514, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf202, 0xf601, 0xf200, 0xf200, 0xf600, 0xf602, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, 0xf07b, 0xf05b, 0xf05d, 0xf07d, @@ -64,8 +64,8 @@ u_short altgr_map[NR_KEYS] = { u_short ctrl_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf100, 0xf101, 0xf109, - 0xf102, 0xf10a, 0xf103, 0xf10b, 0xf104, 0xf701, 0xf200, 0xf200, - 0xf107, 0xf200, 0xf108, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf204, + 0xf102, 0xf10a, 0xf103, 0xf10b, 0xf104, 0xf701, 0xf105, 0xf200, + 0xf106, 0xf107, 0xf108, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf204, 0xf601, 0xf200, 0xf200, 0xf600, 0xf602, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf000, 0xf008, 0xf115, 0xf200, 0xf30d, 0xf30c, @@ -103,7 +103,7 @@ u_short shift_ctrl_map[NR_KEYS] = { u_short alt_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf500, 0xf501, 0xf509, 0xf502, 0xf50a, 0xf503, 0xf50b, 0xf504, 0xf701, 0xf505, 0xf200, - 0xf507, 0xf200, 0xf508, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf209, + 0xf506, 0xf507, 0xf508, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf209, 0xf210, 0xf200, 0xf200, 0xf600, 0xf211, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf860, 0xf87f, 0xf115, 0xf200, 0xf30d, 0xf30c, @@ -121,14 +121,14 @@ u_short alt_map[NR_KEYS] = { u_short ctrl_alt_map[NR_KEYS] = { 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf500, 0xf501, 0xf509, - 0xf502, 0xf50a, 0xf503, 0xf50b, 0xf504, 0xf701, 0xf200, 0xf200, - 0xf507, 0xf200, 0xf508, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf200, + 0xf502, 0xf50a, 0xf503, 0xf50b, 0xf504, 0xf701, 0xf505, 0xf200, + 0xf506, 0xf507, 0xf508, 0xf703, 0xf603, 0xf11d, 0xf200, 0xf200, 0xf601, 0xf200, 0xf200, 0xf600, 0xf602, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf115, 0xf200, 0xf30d, 0xf30c, 0xf200, 0xf200, 0xf20c, 0xf200, 0xf114, 0xf200, 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 0xf80f, 0xf810, - 0xf200, 0xf200, 0xf200, 0xf20e, 0xf307, 0xf308, 0xf309, 0xf30b, + 0xf200, 0xf200, 0xf20c, 0xf20e, 0xf307, 0xf308, 0xf309, 0xf30b, 0xf200, 0xf200, 0xf117, 0xf200, 0xf702, 0xf801, 0xf813, 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 0xf200, 0xf200, 0xf201, 0xf30e, 0xf304, 0xf305, 0xf306, 0xf300, 0xf200, diff --git a/drivers/sbus/char/sunkeymap.map b/drivers/sbus/char/sunkeymap.map index 734419108..7fa644d1e 100644 --- a/drivers/sbus/char/sunkeymap.map +++ b/drivers/sbus/char/sunkeymap.map @@ -44,18 +44,20 @@ keycode 0x0c = F5 F15 Console_17 alt keycode 0x0c = Console_5 control alt keycode 0x0c = Console_5 keycode 0x0d = AltGr -keycode 0x0e = F6 F6 +keycode 0x0e = F6 F16 Console_18 + control keycode 0x0e = F6 alt keycode 0x0e = Console_6 + control alt keycode 0x0e = Console_6 # BLANK KEY on type 5 keyboards keycode 0x0f = keycode 0x10 = F7 F17 Console_19 - control Keycode 0x10 = F7 + control keycode 0x10 = F7 alt keycode 0x10 = Console_7 control alt keycode 0x10 = Console_7 -keycode 0x11 = F8 F8 Console_20 - control keycode 0x10 = F8 - alt keycode 0x10 = Console_8 - control alt keycode 0x10 = Console_8 +keycode 0x11 = F8 F18 Console_20 + control keycode 0x11 = F8 + alt keycode 0x11 = Console_8 + control alt keycode 0x11 = Console_8 keycode 0x12 = F9 F19 Console_21 control keycode 0x12 = F9 alt keycode 0x12 = Console_9 @@ -159,6 +161,7 @@ keycode 0x41 = bracketright braceright asciitilde keycode 0x42 = Delete Delete control keycode 0x42 = BackSpace alt keycode 0x43 = Meta_Delete + control alt keycode 0x42 = Boot keycode 0x43 = Compose keycode 0x44 = KP_7 alt keycode 0x44 = Ascii_7 diff --git a/drivers/sbus/char/sunmouse.c b/drivers/sbus/char/sunmouse.c index 9b32cf59a..371fb3004 100644 --- a/drivers/sbus/char/sunmouse.c +++ b/drivers/sbus/char/sunmouse.c @@ -9,6 +9,7 @@ * Dec/19/95 Added SunOS mouse ioctls - miguel. * Jan/5/96 Added VUID support, sigio support - miguel. * Mar/5/96 Added proper mouse stream support - miguel. + * Sep/96 Allow more than one reader -miguel. */ /* The mouse is run off of one of the Zilog serial ports. On @@ -31,7 +32,9 @@ * set when the device is opened and allows the application to see the * mouse character stream as we get it from the serial (for gpm for * example). The second method, VUID_FIRM_EVENT will provide cooked - * events in Firm_event records. + * events in Firm_event records as expected by SunOS/Solaris applications. + * + * FIXME: We need to support more than one mouse. * */ #include <linux/kernel.h> @@ -42,7 +45,9 @@ #include <linux/errno.h> #include <linux/miscdevice.h> #include <linux/mm.h> -#include <asm/segment.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <asm/uaccess.h> #include <asm/system.h> #include <asm/vuid_event.h> #include <linux/random.h> @@ -129,11 +134,61 @@ push_char (char c) wake_up_interruptible (&sunmouse.proc_list); } +/* Auto baud rate "detection". ;-) */ +static int mouse_bogon_bytes = 0; +static int mouse_baud_changing = 0; /* For reporting things to the user. */ +static int mouse_baud = 4800; /* Initial rate set by zilog driver. */ + +/* Change the baud rate after receiving too many "bogon bytes". */ +void sun_mouse_change_baud(void) +{ + extern void zs_change_mouse_baud(int newbaud); + + if(mouse_baud == 1200) + mouse_baud = 4800; + else + mouse_baud = 1200; + + zs_change_mouse_baud(mouse_baud); + mouse_baud_changing = 1; +} + +void mouse_baud_detection(unsigned char c) +{ + static int wait_for_synchron = 1; + static int ctr = 0; + + if(wait_for_synchron) { + if((c < 0x80) || (c > 0x87)) + mouse_bogon_bytes++; + else { + ctr = 0; + wait_for_synchron = 0; + } + } else { + ctr++; + if(ctr >= 4) { + ctr = 0; + wait_for_synchron = 1; + if(mouse_baud_changing == 1) { + printk("sunmouse: Successfully adjusted to %d baud.\n", + mouse_baud); + mouse_baud_changing = 0; + } + } + } + if(mouse_bogon_bytes > 12) { + sun_mouse_change_baud(); + mouse_bogon_bytes = 0; + wait_for_synchron = 1; + } +} + /* The following is called from the zs driver when bytes are received on * the Mouse zs8530 channel. */ void -sun_mouse_inbyte(unsigned char byte, unsigned char status) +sun_mouse_inbyte(unsigned char byte) { signed char mvalue; int d; @@ -143,12 +198,12 @@ sun_mouse_inbyte(unsigned char byte, unsigned char status) if(!sunmouse.active) return; + mouse_baud_detection(byte); + if (!gen_events){ push_char (byte); return; } - /* Check for framing errors and parity errors */ - /* XXX TODO XXX */ /* If the mouse sends us a byte from 0x80 to 0x87 * we are starting at byte zero in the transaction @@ -259,11 +314,10 @@ sun_mouse_inbyte(unsigned char byte, unsigned char status) static int sun_mouse_open(struct inode * inode, struct file * file) { + if(sunmouse.active++) + return 0; if(!sunmouse.present) return -EINVAL; - if(sunmouse.active) - return -EBUSY; - sunmouse.active = 1; sunmouse.ready = sunmouse.delta_x = sunmouse.delta_y = 0; sunmouse.button_state = 0x80; sunmouse.vuid_mode = VUID_NATIVE; @@ -281,23 +335,26 @@ sun_mouse_fasync (struct inode *inode, struct file *filp, int on) return 0; } -static void +static int sun_mouse_close(struct inode *inode, struct file *file) { - sunmouse.active = sunmouse.ready = 0; sun_mouse_fasync (inode, file, 0); + if (--sunmouse.active) + return 0; + sunmouse.ready = 0; + return 0; } -static int +static long sun_mouse_write(struct inode *inode, struct file *file, const char *buffer, - int count) + unsigned long count) { return -EINVAL; /* foo on you */ } -static int +static long sun_mouse_read(struct inode *inode, struct file *file, char *buffer, - int count) + unsigned long count) { struct wait_queue wait = { current, NULL }; @@ -316,7 +373,8 @@ sun_mouse_read(struct inode *inode, struct file *file, char *buffer, char *p = buffer, *end = buffer+count; while (p < end && !queue_empty ()){ - *(Firm_event *)p = *get_from_queue (); + copy_to_user_ret((Firm_event *)p, get_from_queue(), + sizeof(Firm_event), -EFAULT); p += sizeof (Firm_event); } sunmouse.ready = !queue_empty (); @@ -325,11 +383,12 @@ sun_mouse_read(struct inode *inode, struct file *file, char *buffer, } else { int c; - for (c = count; !queue_empty () && c; c--){ - *buffer++ = sunmouse.queue.stream [sunmouse.tail]; + for (c = count; !queue_empty() && c; c--){ + put_user_ret(sunmouse.queue.stream[sunmouse.tail], buffer, -EFAULT); + buffer++; sunmouse.tail = (sunmouse.tail + 1) % STREAM_SIZE; } - sunmouse.ready = !queue_empty (); + sunmouse.ready = !queue_empty(); inode->i_atime = CURRENT_TIME; return count-c; } @@ -339,15 +398,11 @@ sun_mouse_read(struct inode *inode, struct file *file, char *buffer, return 0; } -static int -sun_mouse_select(struct inode *inode, struct file *file, int sel_type, - select_table *wait) +static unsigned int sun_mouse_poll(struct file *file, poll_table *wait) { - if(sel_type != SEL_IN) - return 0; + poll_wait(&sunmouse.proc_list, wait); if(sunmouse.ready) - return 1; - select_wait(&sunmouse.proc_list, wait); + return POLLIN | POLLRDNORM; return 0; } int @@ -358,22 +413,28 @@ sun_mouse_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsig switch (cmd){ /* VUIDGFORMAT - Get input device byte stream format */ case _IOR('v', 2, int): - i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (int)); - if (i) return i; - *(int *)arg = sunmouse.vuid_mode; + put_user_ret(sunmouse.vuid_mode, (int *) arg, -EFAULT); break; /* VUIDSFORMAT - Set input device byte stream format*/ case _IOW('v', 1, int): - i = verify_area (VERIFY_READ, (void *)arg, sizeof (int)); - if (i) return i; - i = *(int *) arg; + get_user_ret(i, (int *) arg, -EFAULT); if (i == VUID_NATIVE || i == VUID_FIRM_EVENT){ - sunmouse.vuid_mode = *(int *)arg; + int value; + + get_user_ret(value, (int *)arg, -EFAULT); + sunmouse.vuid_mode = value; sunmouse.head = sunmouse.tail = 0; } else return -EINVAL; break; + + case 0x8024540b: + case 0x40245408: + /* This is a buggy application doing termios on the mouse driver */ + /* we ignore it. I keep this check here so that we will notice */ + /* future mouse vuid ioctls */ + break; default: printk ("[MOUSE-ioctl: %8.8x]\n", cmd); @@ -387,7 +448,7 @@ struct file_operations sun_mouse_fops = { sun_mouse_read, sun_mouse_write, NULL, - sun_mouse_select, + sun_mouse_poll, sun_mouse_ioctl, NULL, sun_mouse_open, @@ -400,8 +461,7 @@ static struct miscdevice sun_mouse_mouse = { SUN_MOUSE_MINOR, "sunmouse", &sun_mouse_fops }; -int -sun_mouse_init(void) +__initfunc(int sun_mouse_init(void)) { printk("Sun Mouse-Systems mouse driver version 1.00\n"); sunmouse.present = 1; @@ -410,6 +470,7 @@ sun_mouse_init(void) sunmouse.delta_x = sunmouse.delta_y = 0; sunmouse.button_state = 0x80; sunmouse.proc_list = NULL; + sunmouse.byte = 69; return 0; } diff --git a/drivers/sbus/char/sunserial.c b/drivers/sbus/char/sunserial.c index ed1d75073..cd4fe4ee3 100644 --- a/drivers/sbus/char/sunserial.c +++ b/drivers/sbus/char/sunserial.c @@ -1,6 +1,9 @@ -/* serial.c: Serial port driver for the Sparc. +/* $Id: sunserial.c,v 1.38 1997/04/14 17:05:00 jj Exp $ + * serial.c: Serial port driver for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) + * Fixes by Pete A. Zaitcev <zaitcev@ipmce.su>. */ #include <linux/errno.h> @@ -16,37 +19,41 @@ #include <linux/fcntl.h> #include <linux/mm.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/oplib.h> #include <asm/system.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include <asm/bitops.h> #include <asm/delay.h> #include <asm/kdebug.h> #include "sunserial.h" -#define NUM_SERIAL 2 /* Two chips on board. */ +static int num_serial = 2; /* sun4/sun4c/sun4m - Two chips on board. */ +#define NUM_SERIAL num_serial #define NUM_CHANNELS (NUM_SERIAL * 2) #define KEYBOARD_LINE 0x2 #define MOUSE_LINE 0x3 -struct sun_zslayout *zs_chips[NUM_SERIAL] = { 0, 0, }; -struct sun_zschannel *zs_channels[NUM_CHANNELS] = { 0, 0, 0, 0, }; +extern struct wait_queue * keypress_wait; + +struct sun_zslayout **zs_chips; +struct sun_zschannel **zs_channels; struct sun_zschannel *zs_conschan; struct sun_zschannel *zs_mousechan; struct sun_zschannel *zs_kbdchan; struct sun_zschannel *zs_kgdbchan; -int zs_nodes[NUM_SERIAL] = { 0, 0, }; +int *zs_nodes; -struct sun_serial zs_soft[NUM_CHANNELS]; +struct sun_serial *zs_soft; struct sun_serial *zs_chain; /* IRQ servicing chain */ int zilog_irq; -struct tty_struct zs_ttys[NUM_CHANNELS]; +struct tty_struct *zs_ttys; /** struct tty_struct *zs_constty; **/ /* Console hooks... */ @@ -61,23 +68,39 @@ struct sun_serial *zs_consinfo = 0; #define SUNKBD_UP 0x80 #define SUNKBD_A 0x4d -extern void sunkbd_inchar(unsigned char ch, unsigned char status, struct pt_regs *regs); -extern void sun_mouse_inbyte(unsigned char byte, unsigned char status); +extern void sunkbd_inchar(unsigned char ch, struct pt_regs *regs); +extern void sun_mouse_inbyte(unsigned char byte); static unsigned char kgdb_regs[16] = { 0, 0, 0, /* write 0, 1, 2 */ - (Rx8 | RxENABLE), /* write 3 */ + (Rx8 | RxENAB), /* write 3 */ (X16CLK | SB1 | PAR_EVEN), /* write 4 */ - (Tx8 | TxENAB), /* write 5 */ + (DTR | Tx8 | TxENAB), /* write 5 */ 0, 0, 0, /* write 6, 7, 8 */ (NV), /* write 9 */ (NRZ), /* write 10 */ (TCBR | RCBR), /* write 11 */ 0, 0, /* BRG time constant, write 12 + 13 */ - (BRSRC | BRENABL), /* write 14 */ + (BRSRC | BRENAB), /* write 14 */ (DCDIE) /* write 15 */ }; +static unsigned char zscons_regs[16] = { + 0, /* write 0 */ + (EXT_INT_ENAB | INT_ALL_Rx), /* write 1 */ + 0, /* write 2 */ + (Rx8 | RxENAB), /* write 3 */ + (X16CLK), /* write 4 */ + (DTR | Tx8 | TxENAB), /* write 5 */ + 0, 0, 0, /* write 6, 7, 8 */ + (NV | MIE), /* write 9 */ + (NRZ), /* write 10 */ + (TCBR | RCBR), /* write 11 */ + 0, 0, /* BRG time constant, write 12 + 13 */ + (BRSRC | BRENAB), /* write 14 */ + (DCDIE | CTSIE | TxUIE | BRKIE) /* write 15 */ +}; + #define ZS_CLOCK 4915200 /* Zilog input clock rate */ DECLARE_TASK_QUEUE(tq_serial); @@ -106,9 +129,9 @@ static int serial_refcount; static void change_speed(struct sun_serial *info); -static struct tty_struct *serial_table[NUM_CHANNELS]; -static struct termios *serial_termios[NUM_CHANNELS]; -static struct termios *serial_termios_locked[NUM_CHANNELS]; +static struct tty_struct **serial_table; +static struct termios **serial_termios; +static struct termios **serial_termios_locked; #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -152,7 +175,7 @@ static inline int serial_paranoia_check(struct sun_serial *info, */ static int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 0 }; + 9600, 19200, 38400, 76800, 0 }; /* * Reading and writing Zilog8530 registers. The delays are to make this @@ -160,7 +183,8 @@ static int baud_table[] = { * register access, other machines handle this in hardware via auxiliary * flip-flops which implement the settle time we do in software. */ -static inline unsigned char read_zsreg(struct sun_zschannel *channel, unsigned char reg) +static inline unsigned char read_zsreg(struct sun_zschannel *channel, + unsigned char reg) { unsigned char retval; @@ -171,48 +195,87 @@ static inline unsigned char read_zsreg(struct sun_zschannel *channel, unsigned c return retval; } -static inline void write_zsreg(struct sun_zschannel *channel, unsigned char reg, unsigned char value) +static inline void write_zsreg(struct sun_zschannel *channel, + unsigned char reg, unsigned char value) { channel->control = reg; udelay(5); channel->control = value; udelay(5); - return; } -static inline void load_zsregs(struct sun_zschannel *channel, unsigned char *regs) +static inline void load_zsregs(struct sun_serial *info, unsigned char *regs) { + unsigned long flags; + struct sun_zschannel *channel = info->zs_channel; + unsigned char stat; + int i; + + for (i = 0; i < 1000; i++) { + stat = read_zsreg(channel, R1); + if (stat & ALL_SNT) + break; + udelay(100); + } + write_zsreg(channel, R3, 0); + ZS_CLEARSTAT(channel); ZS_CLEARERR(channel); ZS_CLEARFIFO(channel); + /* Load 'em up */ + save_flags(flags); cli(); + if (info->channelA) + write_zsreg(channel, R9, CHRA); + else + write_zsreg(channel, R9, CHRB); + udelay(20); /* wait for some old sun4's */ write_zsreg(channel, R4, regs[R4]); - write_zsreg(channel, R10, regs[R10]); - write_zsreg(channel, R3, regs[R3] & ~RxENABLE); + write_zsreg(channel, R3, regs[R3] & ~RxENAB); write_zsreg(channel, R5, regs[R5] & ~TxENAB); - write_zsreg(channel, R1, regs[R1]); - write_zsreg(channel, R9, regs[R9]); + write_zsreg(channel, R9, regs[R9] & ~MIE); + write_zsreg(channel, R10, regs[R10]); write_zsreg(channel, R11, regs[R11]); write_zsreg(channel, R12, regs[R12]); write_zsreg(channel, R13, regs[R13]); + write_zsreg(channel, R14, regs[R14] & ~BRENAB); write_zsreg(channel, R14, regs[R14]); - write_zsreg(channel, R15, regs[R15]); + write_zsreg(channel, R14, (regs[R14] & ~SNRZI) | BRENAB); write_zsreg(channel, R3, regs[R3]); write_zsreg(channel, R5, regs[R5]); - return; + write_zsreg(channel, R15, regs[R15]); + write_zsreg(channel, R0, RES_EXT_INT); + write_zsreg(channel, R0, ERR_RES); + write_zsreg(channel, R1, regs[R1]); + write_zsreg(channel, R9, regs[R9]); + restore_flags(flags); +} + +static inline void zs_put_char(struct sun_zschannel *channel, char ch) +{ + int loops = 0; + + while((channel->control & Tx_BUF_EMP) == 0 && loops < 10000) { + loops++; + udelay(5); + } + channel->data = ch; + udelay(5); } /* Sets or clears DTR/RTS on the requested line */ static inline void zs_rtsdtr(struct sun_serial *ss, int set) { + unsigned long flags; + + save_flags(flags); cli(); if(set) { ss->curregs[5] |= (RTS | DTR); - ss->pendregs[5] = ss->curregs[5]; write_zsreg(ss->zs_channel, 5, ss->curregs[5]); } else { ss->curregs[5] &= ~(RTS | DTR); - ss->pendregs[5] = ss->curregs[5]; write_zsreg(ss->zs_channel, 5, ss->curregs[5]); } + restore_flags(flags); return; } @@ -230,23 +293,7 @@ static inline void kgdb_chaninit(struct sun_serial *ss, int intson, int bps) brg = BPS_TO_BRG(bps, ZS_CLOCK/16); kgdb_regs[R12] = (brg & 255); kgdb_regs[R13] = ((brg >> 8) & 255); - load_zsregs(ss->zs_channel, kgdb_regs); -} - -/* Utility routines for the Zilog */ -static inline int get_zsbaud(struct sun_serial *ss) -{ - struct sun_zschannel *channel = ss->zs_channel; - int brg; - - /* The baud rate is split up between two 8-bit registers in - * what is termed 'BRG time constant' format in my docs for - * the chip, it is a function of the clk rate the chip is - * receiving which happens to be constant. - */ - brg = ((read_zsreg(channel, 13)&0xff) << 8); - brg |= (read_zsreg(channel, 12)&0xff); - return BRG_TO_BPS(brg, (ZS_CLOCK/(ss->clk_divisor))); + load_zsregs(ss, kgdb_regs); } /* @@ -268,7 +315,6 @@ static void rs_stop(struct tty_struct *tty) save_flags(flags); cli(); if (info->curregs[5] & TxENAB) { info->curregs[5] &= ~TxENAB; - info->pendregs[5] &= ~TxENAB; write_zsreg(info->zs_channel, 5, info->curregs[5]); } restore_flags(flags); @@ -285,7 +331,6 @@ static void rs_start(struct tty_struct *tty) save_flags(flags); cli(); if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) { info->curregs[5] |= TxENAB; - info->pendregs[5] = info->curregs[5]; write_zsreg(info->zs_channel, 5, info->curregs[5]); } restore_flags(flags); @@ -307,7 +352,7 @@ static void batten_down_hatches(void) (((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR)) sp_enter_debugger(); else - prom_halt(); + prom_cmdline(); /* XXX We want to notify the keyboard driver that all * XXX keys are in the up state or else weird things @@ -317,14 +362,6 @@ static void batten_down_hatches(void) return; } -/* On receive, this clears errors and the receiver interrupts */ -static inline void rs_recv_clear(struct sun_zschannel *zsc) -{ - zsc->control = ERR_RES; - udelay(5); - zsc->control = RES_H_IUS; - udelay(5); -} /* * ---------------------------------------------------------------------- @@ -355,7 +392,7 @@ static _INLINE_ void rs_sched_event(struct sun_serial *info, int event) { info->event |= 1 << event; - queue_task_irq_off(&info->tqueue, &tq_serial); + queue_task(&info->tqueue, &tq_serial); mark_bh(SERIAL_BH); } @@ -366,116 +403,108 @@ static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs struct tty_struct *tty = info->tty; unsigned char ch, stat; - ch = info->zs_channel->data; - udelay(5); - stat = read_zsreg(info->zs_channel, R1); - udelay(5); + do { + ch = (info->zs_channel->data) & info->parity_mask; + udelay(5); - /* If this is the console keyboard, we need to handle - * L1-A's here. - */ - if(info->cons_keyb) { - if(ch == SUNKBD_RESET) { - l1a_state.kbd_id = 1; - l1a_state.l1_down = 0; - } else if(l1a_state.kbd_id) { - l1a_state.kbd_id = 0; - } else if(ch == SUNKBD_L1) { - l1a_state.l1_down = 1; - } else if(ch == (SUNKBD_L1|SUNKBD_UP)) { - l1a_state.l1_down = 0; - } else if(ch == SUNKBD_A && l1a_state.l1_down) { - /* whee... */ - batten_down_hatches(); - /* Clear the line and continue execution... */ - rs_recv_clear(info->zs_channel); - l1a_state.l1_down = 0; - l1a_state.kbd_id = 0; + /* If this is the console keyboard, we need to handle + * L1-A's here. + */ + if(info->cons_keyb) { + if(ch == SUNKBD_RESET) { + l1a_state.kbd_id = 1; + l1a_state.l1_down = 0; + } else if(l1a_state.kbd_id) { + l1a_state.kbd_id = 0; + } else if(ch == SUNKBD_L1) { + l1a_state.l1_down = 1; + } else if(ch == (SUNKBD_L1|SUNKBD_UP)) { + l1a_state.l1_down = 0; + } else if(ch == SUNKBD_A && l1a_state.l1_down) { + /* whee... */ + batten_down_hatches(); + /* Continue execution... */ + l1a_state.l1_down = 0; + l1a_state.kbd_id = 0; + return; + } + sunkbd_inchar(ch, regs); return; } - rs_recv_clear(info->zs_channel); - sunkbd_inchar(ch, stat, regs); - - return; - } - if(info->cons_mouse) { - rs_recv_clear(info->zs_channel); - sun_mouse_inbyte(ch, stat); - return; - } - if(info->is_cons) { - if(ch==0) { /* whee, break received */ - batten_down_hatches(); - rs_recv_clear(info->zs_channel); - return; - } else if (ch == 1) { - show_state(); + if(info->cons_mouse) { + sun_mouse_inbyte(ch); return; - } else if (ch == 2) { - show_buffers(); + } + if(info->is_cons) { + if(ch==0) { + /* whee, break received */ + batten_down_hatches(); + /* Continue execution... */ + return; +#if 0 + } else if (ch == 1) { + show_state(); + return; + } else if (ch == 2) { + show_buffers(); + return; +#endif + } + /* It is a 'keyboard interrupt' ;-) */ + wake_up(&keypress_wait); + } + /* Look for kgdb 'stop' character, consult the gdb + * documentation for remote target debugging and + * arch/sparc/kernel/sparc-stub.c to see how all this works. + */ + if((info->kgdb_channel) && (ch =='\003')) { + breakpoint(); return; } - /* It is a 'keyboard interrupt' ;-) */ - wake_up(&keypress_wait); - } - /* Look for kgdb 'stop' character, consult the gdb documentation - * for remote target debugging and arch/sparc/kernel/sparc-stub.c - * to see how all this works. - */ - if((info->kgdb_channel) && (ch =='\003')) { - breakpoint(); - goto clear_and_exit; - } - if(!tty) - goto clear_and_exit; + if(!tty) + return; - if (tty->flip.count >= TTY_FLIPBUF_SIZE) - queue_task_irq_off(&tty->flip.tqueue, &tq_timer); - tty->flip.count++; - if(stat & PAR_ERR) - *tty->flip.flag_buf_ptr++ = TTY_PARITY; - else if(stat & Rx_OVR) - *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; - else if(stat & CRC_ERR) - *tty->flip.flag_buf_ptr++ = TTY_FRAME; - else - *tty->flip.flag_buf_ptr++ = 0; /* XXX */ - *tty->flip.char_buf_ptr++ = ch; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + break; - queue_task_irq_off(&tty->flip.tqueue, &tq_timer); + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = ch; -clear_and_exit: - rs_recv_clear(info->zs_channel); - return; + /* Check if we have another character... */ + stat = info->zs_channel->control; + udelay(5); + if (!(stat & Rx_CH_AV)) + break; + + /* ... and see if it is clean. */ + stat = read_zsreg(info->zs_channel, R1); + } while (!(stat & (PAR_ERR | Rx_OVR | CRC_ERR))); + + queue_task(&tty->flip.tqueue, &tq_timer); } static _INLINE_ void transmit_chars(struct sun_serial *info) { - /* P3: In theory we have to test readiness here because a - * serial console can clog the chip through rs_put_char(). - * David did not do this. I think he relies on 3-chars FIFO in 8530. - * Let's watch for lost _output_ characters. XXX - */ + struct tty_struct *tty = info->tty; if (info->x_char) { /* Send next char */ - info->zs_channel->data = info->x_char; - udelay(5); + zs_put_char(info->zs_channel, info->x_char); info->x_char = 0; - goto clear_and_return; + return; } - if((info->xmit_cnt <= 0) || info->tty->stopped) { + if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) { /* That's peculiar... */ info->zs_channel->control = RES_Tx_P; udelay(5); - goto clear_and_return; + return; } /* Send char */ - info->zs_channel->data = info->xmit_buf[info->xmit_tail++]; - udelay(5); + zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]); info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); info->xmit_cnt--; @@ -485,14 +514,7 @@ static _INLINE_ void transmit_chars(struct sun_serial *info) if(info->xmit_cnt <= 0) { info->zs_channel->control = RES_Tx_P; udelay(5); - goto clear_and_return; } - -clear_and_return: - /* Clear interrupt */ - info->zs_channel->control = RES_H_IUS; - udelay(5); - return; } static _INLINE_ void status_handle(struct sun_serial *info) @@ -505,22 +527,17 @@ static _INLINE_ void status_handle(struct sun_serial *info) /* Clear status condition... */ info->zs_channel->control = RES_EXT_INT; udelay(5); - /* Clear the interrupt */ - info->zs_channel->control = RES_H_IUS; - udelay(5); #if 0 if(status & DCD) { if((info->tty->termios->c_cflag & CRTSCTS) && ((info->curregs[3] & AUTO_ENAB)==0)) { info->curregs[3] |= AUTO_ENAB; - info->pendregs[3] |= AUTO_ENAB; write_zsreg(info->zs_channel, 3, info->curregs[3]); } } else { if((info->curregs[3] & AUTO_ENAB)) { info->curregs[3] &= ~AUTO_ENAB; - info->pendregs[3] &= ~AUTO_ENAB; write_zsreg(info->zs_channel, 3, info->curregs[3]); } } @@ -538,79 +555,107 @@ static _INLINE_ void status_handle(struct sun_serial *info) return; } -/* - * This is the serial driver's generic interrupt routine - */ -void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static _INLINE_ void special_receive(struct sun_serial *info) { - struct sun_serial * info; - unsigned char zs_intreg; + struct tty_struct *tty = info->tty; + unsigned char ch, stat; - info = zs_chain; - if (!info) - return; + stat = read_zsreg(info->zs_channel, R1); + if (stat & (PAR_ERR | Rx_OVR | CRC_ERR)) { + ch = info->zs_channel->data; + udelay(5); + } - zs_intreg = read_zsreg(info->zs_channel, 3); + if (!tty) + goto clear; - /* NOTE: The read register 3, which holds the irq status, - * does so for both channels on each chip. Although - * the status value itself must be read from the A - * channel and is only valid when read from channel A. - * Yes... broken hardware... - */ -#define CHAN_A_IRQMASK (CHARxIP | CHATxIP | CHAEXT) -#define CHAN_B_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT) + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto done; - /* *** Chip 1 *** */ - /* Channel A -- /dev/ttya, could be the console */ - if(zs_intreg & CHAN_A_IRQMASK) { - if (zs_intreg & CHARxIP) - receive_chars(info, regs); - if (zs_intreg & CHATxIP) - transmit_chars(info); - if (zs_intreg & CHAEXT) - status_handle(info); - } + tty->flip.count++; + if(stat & PAR_ERR) + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + else if(stat & Rx_OVR) + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + else if(stat & CRC_ERR) + *tty->flip.flag_buf_ptr++ = TTY_FRAME; - info=info->zs_next; +done: + queue_task(&tty->flip.tqueue, &tq_timer); +clear: + info->zs_channel->control = ERR_RES; + udelay(5); +} - /* Channel B -- /dev/ttyb, could be the console */ - if(zs_intreg & CHAN_B_IRQMASK) { - if (zs_intreg & CHBRxIP) - receive_chars(info, regs); - if (zs_intreg & CHBTxIP) - transmit_chars(info); - if (zs_intreg & CHBEXT) - status_handle(info); - } - info = info->zs_next; +/* + * This is the serial driver's generic interrupt routine + */ +void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct sun_serial *info; + unsigned char zs_intreg; + int i; + + info = (struct sun_serial *)dev_id; + for (i = 0; i < NUM_SERIAL; i++) { + zs_intreg = read_zsreg(info->zs_next->zs_channel, 2); + zs_intreg &= STATUS_MASK; + + /* NOTE: The read register 2, which holds the irq status, + * does so for both channels on each chip. Although + * the status value itself must be read from the B + * channel and is only valid when read from channel B. + * When read from channel A, read register 2 contains + * the value written to write register 2. + */ - zs_intreg = read_zsreg(info->zs_channel, 3); - /* *** Chip 2 *** */ - /* Channel A -- /dev/kbd, pass communication to keyboard driver */ - if(zs_intreg & CHAN_A_IRQMASK) { - if (zs_intreg & CHARxIP) + /* Channel A -- /dev/ttya or /dev/kbd, could be the console */ + if (zs_intreg == CHA_Rx_AVAIL) { receive_chars(info, regs); - if (zs_intreg & CHATxIP) + return; + } + if(zs_intreg == CHA_Tx_EMPTY) { transmit_chars(info); - if (zs_intreg & CHAEXT) + return; + } + if (zs_intreg == CHA_EXT_STAT) { status_handle(info); - } + return; + } + if (zs_intreg == CHA_SPECIAL) { + special_receive(info); + return; + } + + /* Channel B -- /dev/ttyb or /dev/mouse, could be the console */ + if(zs_intreg == CHB_Rx_AVAIL) { + receive_chars(info->zs_next, regs); + return; + } + if(zs_intreg == CHB_Tx_EMPTY) { + transmit_chars(info->zs_next); + return; + } + if (zs_intreg == CHB_EXT_STAT) { + status_handle(info->zs_next); + return; + } - info=info->zs_next; + /* NOTE: The default value for the IRQ status in read register + * 2 in channel B is CHB_SPECIAL, so we need to look at + * read register 3 in channel A to check if this is a + * real interrupt, or just the default value. + * Yes... broken hardware... + */ - /* Channel B -- /dev/mouse, pass communication to mouse driver */ - if(zs_intreg & CHAN_B_IRQMASK) { - if (zs_intreg & CHBRxIP) - receive_chars(info, regs); - if (zs_intreg & CHBTxIP) - transmit_chars(info); - if (zs_intreg & CHBEXT) - status_handle(info); + zs_intreg = read_zsreg(info->zs_channel, 3); + if (zs_intreg & CHBRxIP) { + special_receive(info->zs_next); + return; + } + info = info->zs_next->zs_next; } - - return; } /* @@ -667,6 +712,10 @@ static void do_serial_hangup(void *private_) tty = info->tty; if (!tty) return; +#ifdef SERIAL_DEBUG_OPEN + printk("do_serial_hangup<%p: tty-%d\n", + __builtin_return_address(0), info->line); +#endif tty_hangup(tty); } @@ -701,7 +750,7 @@ static int startup(struct sun_serial * info) save_flags(flags); cli(); #ifdef SERIAL_DEBUG_OPEN - printk("starting up ttys%d (irq %d)...", info->line, info->irq); + printk("Starting up tty-%d (irq %d)...\n", info->line, info->irq); #endif /* @@ -727,15 +776,12 @@ static int startup(struct sun_serial * info) /* * Finally, enable sequencing and interrupts */ - info->curregs[1] |= (info->curregs[1] & ~0x18) | (EXT_INT_ENAB|INT_ALL_Rx); - info->pendregs[1] = info->curregs[1]; - info->curregs[3] |= (RxENABLE | Rx8); - info->pendregs[3] = info->curregs[3]; + info->curregs[1] |= (info->curregs[1] & ~(RxINT_MASK)) | + (EXT_INT_ENAB | INT_ALL_Rx); + info->curregs[3] |= (RxENAB | Rx8); /* We enable Tx interrupts as needed. */ info->curregs[5] |= (TxENAB | Tx8); - info->pendregs[5] = info->curregs[5]; info->curregs[9] |= (NV | MIE); - info->pendregs[9] = info->curregs[9]; write_zsreg(info->zs_channel, 3, info->curregs[3]); write_zsreg(info->zs_channel, 5, info->curregs[5]); write_zsreg(info->zs_channel, 9, info->curregs[9]); @@ -808,6 +854,7 @@ static void change_speed(struct sun_serial *info) { unsigned short port; unsigned cflag; + int quot = 0; int i; int brg; @@ -817,57 +864,76 @@ static void change_speed(struct sun_serial *info) if (!(port = info->port)) return; i = cflag & CBAUD; - if (i & CBAUDEX) { - /* XXX CBAUDEX is not obeyed. - * It is impossible at a 32bits SPARC. - * But we have to report this to user ... someday. - */ - i = B9600; + if (cflag & CBAUDEX) { + i &= ~CBAUDEX; + if (i != 1) + info->tty->termios->c_cflag &= ~CBAUDEX; + else + i += 15; + } + if (i == 15) { + if ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_HI) + i += 1; + if ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_CUST) + quot = info->custom_divisor; + } + if (quot) { + info->zs_baud = info->baud_base / quot; + info->clk_divisor = 16; + + info->curregs[4] = X16CLK; + info->curregs[11] = TCBR | RCBR; + brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor); + info->curregs[12] = (brg & 255); + info->curregs[13] = ((brg >> 8) & 255); + info->curregs[14] = BRSRC | BRENAB; + zs_rtsdtr(info, 1); + } else if (baud_table[i]) { + info->zs_baud = baud_table[i]; + info->clk_divisor = 16; + + info->curregs[4] = X16CLK; + info->curregs[11] = TCBR | RCBR; + brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor); + info->curregs[12] = (brg & 255); + info->curregs[13] = ((brg >> 8) & 255); + info->curregs[14] = BRSRC | BRENAB; + zs_rtsdtr(info, 1); + } else { + zs_rtsdtr(info, 0); + return; } - info->zs_baud = baud_table[i]; - info->clk_divisor = 16; - - info->curregs[4] = X16CLK; - info->curregs[11] = TCBR | RCBR; - brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor); - info->curregs[12] = (brg & 255); - info->curregs[13] = ((brg >> 8) & 255); - info->curregs[14] = BRSRC | BRENABL; /* byte size and parity */ switch (cflag & CSIZE) { case CS5: - info->curregs[3] &= ~(0xc0); + info->curregs[3] &= ~(RxN_MASK); info->curregs[3] |= Rx5; - info->pendregs[3] = info->curregs[3]; - info->curregs[5] &= ~(0xe0); + info->curregs[5] &= ~(TxN_MASK); info->curregs[5] |= Tx5; - info->pendregs[5] = info->curregs[5]; + info->parity_mask = 0x1f; break; case CS6: - info->curregs[3] &= ~(0xc0); + info->curregs[3] &= ~(RxN_MASK); info->curregs[3] |= Rx6; - info->pendregs[3] = info->curregs[3]; - info->curregs[5] &= ~(0xe0); + info->curregs[5] &= ~(TxN_MASK); info->curregs[5] |= Tx6; - info->pendregs[5] = info->curregs[5]; + info->parity_mask = 0x3f; break; case CS7: - info->curregs[3] &= ~(0xc0); + info->curregs[3] &= ~(RxN_MASK); info->curregs[3] |= Rx7; - info->pendregs[3] = info->curregs[3]; - info->curregs[5] &= ~(0xe0); + info->curregs[5] &= ~(TxN_MASK); info->curregs[5] |= Tx7; - info->pendregs[5] = info->curregs[5]; + info->parity_mask = 0x7f; break; case CS8: default: /* defaults to 8 bits */ - info->curregs[3] &= ~(0xc0); + info->curregs[3] &= ~(RxN_MASK); info->curregs[3] |= Rx8; - info->pendregs[3] = info->curregs[3]; - info->curregs[5] &= ~(0xe0); + info->curregs[5] &= ~(TxN_MASK); info->curregs[5] |= Tx8; - info->pendregs[5] = info->curregs[5]; + info->parity_mask = 0xff; break; } info->curregs[4] &= ~(0x0c); @@ -876,24 +942,19 @@ static void change_speed(struct sun_serial *info) } else { info->curregs[4] |= SB1; } - info->pendregs[4] = info->curregs[4]; if (cflag & PARENB) { - info->curregs[4] |= PAR_ENA; - info->pendregs[4] |= PAR_ENA; + info->curregs[4] |= PAR_ENAB; } else { - info->curregs[4] &= ~PAR_ENA; - info->pendregs[4] &= ~PAR_ENA; + info->curregs[4] &= ~PAR_ENAB; } if (!(cflag & PARODD)) { info->curregs[4] |= PAR_EVEN; - info->pendregs[4] |= PAR_EVEN; } else { info->curregs[4] &= ~PAR_EVEN; - info->pendregs[4] &= ~PAR_EVEN; } /* Load up the new values */ - load_zsregs(info->zs_channel, info->curregs); + load_zsregs(info, info->curregs); return; } @@ -904,38 +965,26 @@ static void change_speed(struct sun_serial *info) void kbd_put_char(unsigned char ch) { struct sun_zschannel *chan = zs_kbdchan; - int flags, loops = 0; + unsigned long flags; if(!chan) return; save_flags(flags); cli(); - while((chan->control & Tx_BUF_EMP)==0 && loops < 10000) { - loops++; - udelay(5); - } - - chan->data = ch; - udelay(5); + zs_put_char(chan, ch); restore_flags(flags); } void mouse_put_char(char ch) { struct sun_zschannel *chan = zs_mousechan; - int flags, loops = 0; + unsigned long flags; if(!chan) return; save_flags(flags); cli(); - while((chan->control & Tx_BUF_EMP)==0 && loops < 10000) { - loops++; - udelay(5); - } - - chan->data = ch; - udelay(5); + zs_put_char(chan, ch); restore_flags(flags); } @@ -944,19 +993,13 @@ void mouse_put_char(char ch) static void rs_put_char(char ch) { struct sun_zschannel *chan = zs_conschan; - int flags, loops = 0; + unsigned long flags; if(!chan) return; save_flags(flags); cli(); - while((chan->control & Tx_BUF_EMP)==0 && loops < 10000) { - loops++; - udelay(5); - } - - chan->data = ch; - udelay(5); + zs_put_char(chan, ch); restore_flags(flags); } @@ -969,7 +1012,6 @@ void putDebugChar(char kgdb_char) while((chan->control & Tx_BUF_EMP)==0) udelay(5); - chan->data = kgdb_char; } @@ -1005,7 +1047,7 @@ static void rs_fair_output(void) rs_put_char(c); - save_flags(flags); cli(); + cli(); left = MIN(info->xmit_cnt, left-1); } @@ -1051,10 +1093,8 @@ static void rs_flush_chars(struct tty_struct *tty) /* Enable transmitter */ save_flags(flags); cli(); info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB; - info->pendregs[1] |= TxINT_ENAB|EXT_INT_ENAB; write_zsreg(info->zs_channel, 1, info->curregs[1]); info->curregs[5] |= TxENAB; - info->pendregs[5] |= TxENAB; write_zsreg(info->zs_channel, 5, info->curregs[5]); /* @@ -1064,13 +1104,10 @@ static void rs_flush_chars(struct tty_struct *tty) * but resets interrupts also what we do not desire here. * XXX Discuss with David. */ - if (info->zs_channel->control & Tx_BUF_EMP) { - /* Send char */ - info->zs_channel->data = info->xmit_buf[info->xmit_tail++]; - udelay(5); - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt--; - } + zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]); + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + restore_flags(flags); } @@ -1084,7 +1121,7 @@ static int rs_write(struct tty_struct * tty, int from_user, if (serial_paranoia_check(info, tty->device, "rs_write")) return 0; - if (!tty || !info->xmit_buf) + if (!info || !info->xmit_buf) return 0; save_flags(flags); @@ -1097,7 +1134,7 @@ static int rs_write(struct tty_struct * tty, int from_user, if (from_user) { down(&tmp_buf_sem); - memcpy_fromfs(tmp_buf, buf, c); + copy_from_user(tmp_buf, buf, c); c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); @@ -1111,16 +1148,22 @@ static int rs_write(struct tty_struct * tty, int from_user, count -= c; total += c; } - if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped && - !(info->curregs[5] & TxENAB)) { + + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { /* Enable transmitter */ info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB; - info->pendregs[1] |= TxINT_ENAB|EXT_INT_ENAB; write_zsreg(info->zs_channel, 1, info->curregs[1]); info->curregs[5] |= TxENAB; - info->pendregs[5] |= TxENAB; write_zsreg(info->zs_channel, 5, info->curregs[5]); } +#if 1 + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { + zs_put_char(info->zs_channel, + info->xmit_buf[info->xmit_tail++]); + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + } +#endif restore_flags(flags); return total; } @@ -1189,7 +1232,6 @@ static void rs_throttle(struct tty_struct * tty) /* Turn off RTS line */ cli(); info->curregs[5] &= ~RTS; - info->pendregs[5] &= ~RTS; write_zsreg(info->zs_channel, 5, info->curregs[5]); sti(); } @@ -1217,7 +1259,6 @@ static void rs_unthrottle(struct tty_struct * tty) /* Assert RTS line */ cli(); info->curregs[5] |= RTS; - info->pendregs[5] |= RTS; write_zsreg(info->zs_channel, 5, info->curregs[5]); sti(); } @@ -1245,7 +1286,7 @@ static int get_serial_info(struct sun_serial * info, tmp.close_delay = info->close_delay; tmp.closing_wait = info->closing_wait; tmp.custom_divisor = info->custom_divisor; - memcpy_tofs(retinfo,&tmp,sizeof(*retinfo)); + copy_to_user_ret(retinfo,&tmp,sizeof(*retinfo), -EFAULT); return 0; } @@ -1256,9 +1297,8 @@ static int set_serial_info(struct sun_serial * info, struct sun_serial old_info; int retval = 0; - if (!new_info) + if (!new_info || copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; - memcpy_fromfs(&new_serial,new_info,sizeof(new_serial)); old_info = *info; if (!suser()) { @@ -1285,6 +1325,7 @@ static int set_serial_info(struct sun_serial * info, info->baud_base = new_serial.baud_base; info->flags = ((info->flags & ~ZILOG_FLAGS) | (new_serial.flags & ZILOG_FLAGS)); + info->custom_divisor = new_serial.custom_divisor; info->type = new_serial.type; info->close_delay = new_serial.close_delay; info->closing_wait = new_serial.closing_wait; @@ -1311,7 +1352,57 @@ static int get_lsr_info(struct sun_serial * info, unsigned int *value) cli(); status = info->zs_channel->control; sti(); - put_user(status,value); + put_user_ret(status,value, -EFAULT); + return 0; +} + +static int get_modem_info(struct sun_serial * info, unsigned int *value) +{ + unsigned char status; + unsigned int result; + + cli(); + status = info->zs_channel->control; + sti(); + result = ((info->curregs[5] & RTS) ? TIOCM_RTS : 0) + | ((info->curregs[5] & DTR) ? TIOCM_DTR : 0) + | ((status & DCD) ? TIOCM_CAR : 0) + | ((status & SYNC) ? TIOCM_DSR : 0) + | ((status & CTS) ? TIOCM_CTS : 0); + put_user_ret(result, value, -EFAULT); + return 0; +} + +static int set_modem_info(struct sun_serial * info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg; + + get_user_ret(arg, value, -EFAULT); + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->curregs[5] |= RTS; + if (arg & TIOCM_DTR) + info->curregs[5] |= DTR; + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->curregs[5] &= ~RTS; + if (arg & TIOCM_DTR) + info->curregs[5] &= ~DTR; + break; + case TIOCMSET: + info->curregs[5] = ((info->curregs[5] & ~(RTS | DTR)) + | ((arg & TIOCM_RTS) ? RTS : 0) + | ((arg & TIOCM_DTR) ? DTR : 0)); + break; + default: + return -EINVAL; + } + cli(); + write_zsreg(info->zs_channel, 5, info->curregs[5]); + sti(); return 0; } @@ -1334,7 +1425,6 @@ static void send_break( struct sun_serial * info, int duration) static int rs_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { - int error; struct sun_serial * info = (struct sun_serial *)tty->driver_data; int retval; @@ -1365,44 +1455,33 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, send_break(info, arg ? arg*(HZ/10) : HZ/4); return 0; case TIOCGSOFTCAR: - return put_user(C_CLOCAL(tty) ? 1 : 0, - (unsigned int *) arg); + put_user_ret(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg, -EFAULT); + return 0; case TIOCSSOFTCAR: - { - unsigned int value; - retval = get_user(value, (unsigned int *) arg); - if (retval) - return retval; - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (value ? CLOCAL : 0)); - } + get_user_ret(arg, (unsigned long *) arg, -EFAULT); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); return 0; + case TIOCMGET: + return get_modem_info(info, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *) arg); case TIOCGSERIAL: - error = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct serial_struct)); - if (error) - return error; return get_serial_info(info, (struct serial_struct *) arg); case TIOCSSERIAL: return set_serial_info(info, (struct serial_struct *) arg); case TIOCSERGETLSR: /* Get line status register */ - error = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned int)); - if (error) - return error; - else - return get_lsr_info(info, (unsigned int *) arg); + return get_lsr_info(info, (unsigned int *) arg); case TIOCSERGSTRUCT: - error = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct sun_serial)); - if (error) - return error; - memcpy_tofs((struct sun_serial *) arg, - info, sizeof(struct sun_serial)); + copy_to_user_ret((struct sun_serial *) arg, + info, sizeof(struct sun_serial), -EFAULT); return 0; default: @@ -1453,7 +1532,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) } #ifdef SERIAL_DEBUG_OPEN - printk("rs_close ttys%d, count = %d\n", info->line, info->count); + printk("rs_close tty-%d, count = %d\n", info->line, info->count); #endif if ((tty->count == 1) && (info->count != 1)) { /* @@ -1499,11 +1578,9 @@ static void rs_close(struct tty_struct *tty, struct file * filp) * line status register. */ /** if (!info->iscons) ... **/ - info->curregs[3] &= ~RxENABLE; - info->pendregs[3] = info->curregs[3]; + info->curregs[3] &= ~RxENAB; write_zsreg(info->zs_channel, 3, info->curregs[3]); - info->curregs[1] &= ~(0x18); - info->pendregs[1] = info->curregs[1]; + info->curregs[1] &= ~(RxINT_MASK); write_zsreg(info->zs_channel, 1, info->curregs[1]); ZS_CLEARFIFO(info->zs_channel); @@ -1534,6 +1611,9 @@ static void rs_close(struct tty_struct *tty, struct file * filp) info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE| ZILOG_CLOSING); wake_up_interruptible(&info->close_wait); +#ifdef SERIAL_DEBUG_OPEN + printk("rs_close tty-%d exiting, count = %d\n", info->line, info->count); +#endif restore_flags(flags); } @@ -1547,6 +1627,11 @@ void rs_hangup(struct tty_struct *tty) if (serial_paranoia_check(info, tty->device, "rs_hangup")) return; +#ifdef SERIAL_DEBUG_OPEN + printk("rs_hangup<%p: tty-%d, count = %d bye\n", + __builtin_return_address(0), info->line, info->count); +#endif + rs_flush_buffer(tty); shutdown(info); info->event = 0; @@ -1567,6 +1652,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, struct wait_queue wait = { current, NULL }; int retval; int do_clocal = 0; + unsigned char r0; /* * If the device is in the middle of being closed, then block @@ -1636,7 +1722,10 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, printk("block_til_ready before block: ttys%d, count = %d\n", info->line, info->count); #endif - info->count--; + cli(); + if(!tty_hung_up_p(filp)) + info->count--; + sti(); info->blocked_open++; while (1) { cli(); @@ -1646,6 +1735,10 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, current->state = TASK_INTERRUPTIBLE; if (tty_hung_up_p(filp) || !(info->flags & ZILOG_INITIALIZED)) { +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready hup-ed: ttys%d, count = %d\n", + info->line, info->count); +#endif #ifdef SERIAL_DO_RESTART if (info->flags & ZILOG_HUP_NOTIFY) retval = -EAGAIN; @@ -1656,8 +1749,13 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, #endif break; } + + cli(); + r0 = read_zsreg(info->zs_channel, R0); + sti(); if (!(info->flags & ZILOG_CALLOUT_ACTIVE) && - !(info->flags & ZILOG_CLOSING) && do_clocal) + !(info->flags & ZILOG_CLOSING) && + (do_clocal || (DCD & r0))) break; if (current->signal & ~current->blocked) { retval = -ERESTARTSYS; @@ -1713,6 +1811,11 @@ int rs_open(struct tty_struct *tty, struct file * filp) printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, info->count); #endif + if (info->tty != 0 && info->tty != tty) { + /* Never happen? */ + printk("rs_open %s%d, tty overwrite.\n", tty->driver.name, info->line); + return -EBUSY; + } info->count++; tty->driver_data = info; info->tty = tty; @@ -1754,16 +1857,28 @@ int rs_open(struct tty_struct *tty, struct file * filp) static void show_serial_version(void) { - printk("Sparc Zilog8530 serial driver version 1.00\n"); + char *revision = "$Revision: 1.38 $"; + char *version, *p; + + version = strchr(revision, ' '); + p = strchr(++version, ' '); + *p = '\0'; + printk("Sparc Zilog8530 serial driver version %s\n", version); + *p = ' '; } -/* Probe the PROM for the request zs chip number. */ -static inline struct sun_zslayout *get_zs(int chip) +/* Probe the PROM for the request zs chip number. + * + * Note: The Sun Voyager shows two addresses and two intr for it's + * Zilogs, what the second does, I don't know. It does work + * with using only the first number of each property. + */ +static struct sun_zslayout *get_zs(int chip) { - struct linux_prom_irqs tmp_irq; - unsigned long paddr = 0; - unsigned long vaddr = 0; - int zsnode, tmpnode, iospace, slave; + struct linux_prom_irqs tmp_irq[2]; + unsigned int paddr = 0; + unsigned int vaddr[2] = { 0, 0 }; + int zsnode, tmpnode, iospace, slave, len, seen, sun4u_irq; static int irq = 0; #if CONFIG_AP1000 @@ -1789,54 +1904,265 @@ static inline struct sun_zslayout *get_zs(int chip) zs_nodes[chip] = 0; if(!irq) zilog_irq = irq = 12; - vaddr = (unsigned long) - sparc_alloc_io((char *) paddr, 0, 8, - "Zilog Serial", iospace, 0); + vaddr[0] = (unsigned long) + sparc_alloc_io(paddr, 0, 8, + "Zilog Serial", iospace, 0); } else { /* Can use the prom for other machine types */ zsnode = prom_getchild(prom_root_node); - tmpnode = prom_searchsiblings(zsnode, "obio"); - if(tmpnode) - zsnode = prom_getchild(tmpnode); + if (sparc_cpu_model == sun4d) { + int board, node; + + tmpnode = zsnode; + while (tmpnode && (tmpnode = prom_searchsiblings(tmpnode, "cpu-unit"))) { + board = prom_getintdefault (tmpnode, "board#", -1); + if (board == (chip >> 1)) { + node = prom_getchild(tmpnode); + if (node && (node = prom_searchsiblings(node, "bootbus"))) { + zsnode = node; + break; + } + } + tmpnode = prom_getsibling(tmpnode); + } + if (!tmpnode) + panic ("get_zs: couldn't find board%d's bootbus\n", chip >> 1); + } else if (sparc_cpu_model == sun4u) { + tmpnode = prom_searchsiblings(zsnode, "sbus"); + if(tmpnode) + zsnode = prom_getchild(tmpnode); + } else { + tmpnode = prom_searchsiblings(zsnode, "obio"); + if(tmpnode) + zsnode = prom_getchild(tmpnode); + } if(!zsnode) panic("get_zs no zs serial prom node"); + seen = 0; while(zsnode) { zsnode = prom_searchsiblings(zsnode, "zs"); slave = prom_getintdefault(zsnode, "slave", -1); - if(slave==chip) { + if((slave == chip) || + (sparc_cpu_model == sun4u && seen == chip)) { /* The one we want */ - vaddr = (unsigned long) - prom_getintdefault(zsnode, "address", - 0xdeadbeef); - if(vaddr == 0xdeadbeef) - prom_halt(); + len = prom_getproperty(zsnode, "address", + (void *) vaddr, + sizeof(vaddr)); + if (len % sizeof(unsigned int)) { + prom_printf("WHOOPS: proplen for %s " + "was %d, need multiple of " + "%d\n", "address", len, + sizeof(unsigned int)); + panic("zilog: address property"); + } zs_nodes[chip] = zsnode; - prom_getproperty(zsnode, "intr", - (char *) &tmp_irq, - sizeof(tmp_irq)); -#ifdef OLD_STYLE_IRQ - tmp_irq.pri &= 0xf; -#endif + if(sparc_cpu_model == sun4u) { + len = prom_getproperty(zsnode, "interrupts", + (char *) &sun4u_irq, + sizeof(tmp_irq)); + tmp_irq[0].pri = sun4u_irq; + } else { + len = prom_getproperty(zsnode, "intr", + (char *) tmp_irq, + sizeof(tmp_irq)); + if (len % sizeof(struct linux_prom_irqs)) { + prom_printf( + "WHOOPS: proplen for %s " + "was %d, need multiple of " + "%d\n", "address", len, + sizeof(struct linux_prom_irqs)); + panic("zilog: address property"); + } + } if(!irq) { - irq = zilog_irq = tmp_irq.pri; + irq = zilog_irq = tmp_irq[0].pri; } else { - if(tmp_irq.pri != irq) + if(tmp_irq[0].pri != irq) panic("zilog: bogon irqs"); } break; } zsnode = prom_getsibling(zsnode); + seen++; } if(!zsnode) panic("get_zs whee chip not found"); } - if(!vaddr) + if(!vaddr[0]) panic("get_zs whee no serial chip mappable"); - return (struct sun_zslayout *) vaddr; - + return (struct sun_zslayout *)(unsigned long) vaddr[0]; } +static inline void +init_zscons_termios(struct termios *termios) +{ + char mode[16], buf[16]; + char *mode_prop = "ttyX-mode"; + char *cd_prop = "ttyX-ignore-cd"; + char *dtr_prop = "ttyX-rts-dtr-off"; + char *s; + int baud, bits, cflag; + char parity; + int topnd, nd; + int channel, stop; + int carrier = 0; + int rtsdtr = 1; + extern int serial_console; + + if (!serial_console) + return; + + if (serial_console == 1) { + mode_prop[3] = 'a'; + cd_prop[3] = 'a'; + dtr_prop[3] = 'a'; + } else { + mode_prop[3] = 'b'; + cd_prop[3] = 'b'; + dtr_prop[3] = 'b'; + } + + topnd = prom_getchild(prom_root_node); + nd = prom_searchsiblings(topnd, "options"); + if (!nd) { + strcpy(mode, "9600,8,n,1,-"); + goto no_options; + } + + if (!prom_node_has_property(nd, mode_prop)) { + strcpy(mode, "9600,8,n,1,-"); + goto no_options; + } + + memset(mode, 0, sizeof(mode)); + prom_getstring(nd, mode_prop, mode, sizeof(mode)); + + if (prom_node_has_property(nd, cd_prop)) { + memset(buf, 0, sizeof(buf)); + prom_getstring(nd, cd_prop, buf, sizeof(buf)); + if (!strcmp(buf, "false")) + carrier = 1; + + /* XXX this is unused below. */ + } + + if (prom_node_has_property(nd, cd_prop)) { + memset(buf, 0, sizeof(buf)); + prom_getstring(nd, cd_prop, buf, sizeof(buf)); + if (!strcmp(buf, "false")) + rtsdtr = 0; + + /* XXX this is unused below. */ + } + +no_options: + cflag = CREAD | HUPCL | CLOCAL; + + s = mode; + baud = simple_strtoul(s, 0, 0); + s = strchr(s, ','); + bits = simple_strtoul(++s, 0, 0); + s = strchr(s, ','); + parity = *(++s); + s = strchr(s, ','); + stop = simple_strtoul(++s, 0, 0); + s = strchr(s, ','); + /* XXX handshake is not handled here. */ + + for (channel = 0; channel < NUM_CHANNELS; channel++) + if (zs_soft[channel].is_cons) + break; + + switch (baud) { + case 150: + cflag |= B150; + break; + case 300: + cflag |= B300; + break; + case 600: + cflag |= B600; + break; + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + default: + baud = 9600; + case 9600: + cflag |= B9600; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + } + zs_soft[channel].zs_baud = baud; + + switch (bits) { + case 5: + zscons_regs[3] = Rx5 | RxENAB; + zscons_regs[5] = Tx5 | TxENAB; + zs_soft[channel].parity_mask = 0x1f; + cflag |= CS5; + break; + case 6: + zscons_regs[3] = Rx6 | RxENAB; + zscons_regs[5] = Tx6 | TxENAB; + zs_soft[channel].parity_mask = 0x3f; + cflag |= CS6; + break; + case 7: + zscons_regs[3] = Rx7 | RxENAB; + zscons_regs[5] = Tx7 | TxENAB; + zs_soft[channel].parity_mask = 0x7f; + cflag |= CS7; + break; + default: + case 8: + zscons_regs[3] = Rx8 | RxENAB; + zscons_regs[5] = Tx8 | TxENAB; + zs_soft[channel].parity_mask = 0xff; + cflag |= CS8; + break; + } + zscons_regs[5] |= DTR; + + switch (parity) { + case 'o': + zscons_regs[4] |= PAR_ENAB; + cflag |= (PARENB | PARODD); + break; + case 'e': + zscons_regs[4] |= (PAR_ENAB | PAR_EVEN); + cflag |= PARENB; + break; + default: + case 'n': + break; + } + + switch (stop) { + default: + case 1: + zscons_regs[4] |= SB1; + break; + case 2: + cflag |= CSTOPB; + zscons_regs[4] |= SB2; + break; + } + + termios->c_cflag = cflag; +} extern void register_console(void (*proc)(const char *)); @@ -1844,8 +2170,8 @@ static inline void rs_cons_check(struct sun_serial *ss, int channel) { int i, o, io; - static consout_registered = 0; - static msg_printed = 0; + static int consout_registered = 0; + static int msg_printed = 0; i = o = io = 0; @@ -1861,6 +2187,9 @@ rs_cons_check(struct sun_serial *ss, int channel) o = 1; /* double whee.. */ if(!consout_registered) { + extern void serial_finish_init (void (*)(const char *)); + + serial_finish_init (zs_console_print); register_console(zs_console_print); consout_registered = 1; } @@ -1875,8 +2204,6 @@ rs_cons_check(struct sun_serial *ss, int channel) } if(o && i) io = 1; - if(ss->zs_baud != 9600) - panic("Console baud rate weirdness"); /* Set flag variable for this port so that it cannot be * opened for other uses by accident. @@ -1894,15 +2221,66 @@ rs_cons_check(struct sun_serial *ss, int channel) } } -volatile int test_done; extern void keyboard_zsinit(void); extern void sun_mouse_zsinit(void); -/* rs_init inits the driver */ -int rs_init(void) +/* This is for the auto baud rate detection in the mouse driver. */ +void zs_change_mouse_baud(int newbaud) { - int chip, channel, i, flags; + int channel = MOUSE_LINE; + int brg; + + zs_soft[channel].zs_baud = newbaud; + brg = BPS_TO_BRG(zs_soft[channel].zs_baud, + (ZS_CLOCK / zs_soft[channel].clk_divisor)); + write_zsreg(zs_soft[channel].zs_channel, R12, (brg & 0xff)); + write_zsreg(zs_soft[channel].zs_channel, R13, ((brg >> 8) & 0xff)); +} + +__initfunc(unsigned long sun_serial_setup (unsigned long memory_start)) +{ + char *p; + int i; + + if (sparc_cpu_model == sun4d) { + int node = prom_searchsiblings(prom_getchild(prom_root_node), "boards"); + NUM_SERIAL = 0; + if (!node) + panic ("Cannot find out count of boards"); + else + node = prom_getchild(node); + while (node && (node = prom_searchsiblings(node, "bif"))) { + NUM_SERIAL += 2; + node = prom_getsibling(node); + } + } + p = (char *)((memory_start + 7) & ~7); + zs_chips = (struct sun_zslayout **)(p); + i = NUM_SERIAL * sizeof (struct sun_zslayout *); + zs_channels = (struct sun_zschannel **)(p + i); + i += NUM_CHANNELS * sizeof (struct sun_zschannel *); + zs_nodes = (int *)(p + i); + i += NUM_SERIAL * sizeof (int); + zs_soft = (struct sun_serial *)(p + i); + i += NUM_CHANNELS * sizeof (struct sun_serial); + zs_ttys = (struct tty_struct *)(p + i); + i += NUM_CHANNELS * sizeof (struct tty_struct); + serial_table = (struct tty_struct **)(p + i); + i += NUM_CHANNELS * sizeof (struct tty_struct *); + serial_termios = (struct termios **)(p + i); + i += NUM_CHANNELS * sizeof (struct termios *); + serial_termios_locked = (struct termios **)(p + i); + i += NUM_CHANNELS * sizeof (struct termios *); + memset (p, 0, i); + return (((unsigned long)p) + i + 7) & ~7; +} + +__initfunc(int rs_init(void)) +{ + int chip, channel, brg, i; + unsigned long flags; struct sun_serial *info; + char dummy; #if CONFIG_AP1000 printk("not doing rs_init()\n"); @@ -1921,6 +2299,7 @@ int rs_init(void) memset(&serial_driver, 0, sizeof(struct tty_driver)); serial_driver.magic = TTY_DRIVER_MAGIC; + serial_driver.driver_name = "serial"; serial_driver.name = "ttyS"; serial_driver.major = TTY_MAJOR; serial_driver.minor_start = 64; @@ -1952,6 +2331,12 @@ int rs_init(void) serial_driver.start = rs_start; serial_driver.hangup = rs_hangup; + /* I'm too lazy, someone write versions of this for us. -DaveM */ + serial_driver.read_proc = 0; + serial_driver.proc_entry = 0; + + init_zscons_termios(&serial_driver.init_termios); + /* * The callout device is just like normal device except for * major number and the subtype code. @@ -1970,11 +2355,11 @@ int rs_init(void) /* Set up our interrupt linked list */ zs_chain = &zs_soft[0]; - zs_soft[0].zs_next = &zs_soft[1]; - zs_soft[1].zs_next = &zs_soft[2]; - zs_soft[2].zs_next = &zs_soft[3]; - zs_soft[3].zs_next = 0; + for(channel = 0; channel < NUM_CHANNELS - 1; channel++) + zs_soft[channel].zs_next = &zs_soft[channel + 1]; + zs_soft[channel + 1].zs_next = 0; + /* Initialize Softinfo */ for(chip = 0; chip < NUM_SERIAL; chip++) { /* If we are doing kgdb over one of the channels on * chip zero, kgdb_channel will be set to 1 by the @@ -1988,105 +2373,175 @@ int rs_init(void) zs_soft[(chip*2)].kgdb_channel = 0; zs_soft[(chip*2)+1].kgdb_channel = 0; } + /* First, set up channel A on this chip. */ channel = chip * 2; zs_soft[channel].zs_channel = zs_channels[channel]; zs_soft[channel].change_needed = 0; zs_soft[channel].clk_divisor = 16; - zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]); + zs_soft[channel].cons_keyb = 0; zs_soft[channel].cons_mouse = 0; - /* If not keyboard/mouse and is console serial - * line, then enable receiver interrupts. - */ - if((channel<KEYBOARD_LINE) && (zs_soft[channel].is_cons)) { - write_zsreg(zs_soft[channel].zs_channel, R1, - (EXT_INT_ENAB | INT_ALL_Rx)); - write_zsreg(zs_soft[channel].zs_channel, R9, (NV | MIE)); - write_zsreg(zs_soft[channel].zs_channel, R10, (NRZ)); - write_zsreg(zs_soft[channel].zs_channel, R3, (Rx8|RxENABLE)); - write_zsreg(zs_soft[channel].zs_channel, R5, (Tx8 | TxENAB)); + zs_soft[channel].channelA = 1; + + /* Now, channel B */ + channel++; + zs_soft[channel].zs_channel = zs_channels[channel]; + zs_soft[channel].change_needed = 0; + zs_soft[channel].clk_divisor = 16; + zs_soft[channel].cons_keyb = 0; + zs_soft[channel].cons_mouse = 0; + zs_soft[channel].channelA = 0; + } + + /* Initialize Hardware */ + for(channel = 0; channel < NUM_CHANNELS; channel++) { + + /* Hardware reset each chip */ + if (!(channel & 1)) { + write_zsreg(zs_soft[channel].zs_channel, R9, FHWRES); + udelay(20); /* wait for some old sun4's */ + dummy = read_zsreg(zs_soft[channel].zs_channel, R0); } - /* If this is the kgdb line, enable interrupts because we - * now want to receive the 'control-c' character from the - * client attached to us asynchronously. - */ - if(zs_soft[channel].kgdb_channel) - kgdb_chaninit(&zs_soft[channel], 1, zs_soft[channel].zs_baud); if(channel == KEYBOARD_LINE) { - /* Tell keyboard driver about our presence. */ - if(zs_soft[channel].zs_baud != 1200) - panic("Weird keyboard serial baud rate"); zs_soft[channel].cons_keyb = 1; + zs_soft[channel].parity_mask = 0xff; zs_kbdchan = zs_soft[channel].zs_channel; - /* Enable Rx/Tx, IRQs, and inform kbd driver */ - write_zsreg(zs_soft[channel].zs_channel, R1, - (EXT_INT_ENAB | INT_ALL_Rx)); + write_zsreg(zs_soft[channel].zs_channel, R4, (PAR_EVEN | X16CLK | SB1)); - write_zsreg(zs_soft[channel].zs_channel, R9, (NV|MIE)); - write_zsreg(zs_soft[channel].zs_channel, R10, (NRZ)); + write_zsreg(zs_soft[channel].zs_channel, R3, Rx8); + write_zsreg(zs_soft[channel].zs_channel, R5, Tx8); + write_zsreg(zs_soft[channel].zs_channel, R9, NV); + write_zsreg(zs_soft[channel].zs_channel, R10, NRZ); write_zsreg(zs_soft[channel].zs_channel, R11, (TCBR | RCBR)); + zs_soft[channel].zs_baud = 1200; + brg = BPS_TO_BRG(zs_soft[channel].zs_baud, + ZS_CLOCK/zs_soft[channel].clk_divisor); + write_zsreg(zs_soft[channel].zs_channel, R12, + (brg & 0xff)); + write_zsreg(zs_soft[channel].zs_channel, R13, + ((brg >> 8) & 0xff)); + write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC); + + /* Enable Rx/Tx, IRQs, and inform kbd driver */ write_zsreg(zs_soft[channel].zs_channel, R14, - (BRSRC | BRENABL)); - write_zsreg(zs_soft[channel].zs_channel, R3, (Rx8|RxENABLE)); + (BRSRC | BRENAB)); + write_zsreg(zs_soft[channel].zs_channel, R3, + (Rx8 | RxENAB)); write_zsreg(zs_soft[channel].zs_channel, R5, (Tx8 | TxENAB | DTR | RTS)); -#if 0 + write_zsreg(zs_soft[channel].zs_channel, R15, (DCDIE | CTSIE | TxUIE | BRKIE)); -#endif - ZS_CLEARERR(zs_soft[channel].zs_channel); - ZS_CLEARFIFO(zs_soft[channel].zs_channel); - } + write_zsreg(zs_soft[channel].zs_channel, R0, + RES_EXT_INT); + write_zsreg(zs_soft[channel].zs_channel, R0, + RES_EXT_INT); - /* Now, channel B */ - channel++; - zs_soft[channel].zs_channel = zs_channels[channel]; - zs_soft[channel].change_needed = 0; - zs_soft[channel].clk_divisor = 16; - zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]); - zs_soft[channel].cons_keyb = 0; - /* If not keyboard/mouse and is console serial - * line, then enable receiver interrupts. - */ - if(channel<KEYBOARD_LINE && zs_soft[channel].is_cons) { write_zsreg(zs_soft[channel].zs_channel, R1, (EXT_INT_ENAB | INT_ALL_Rx)); write_zsreg(zs_soft[channel].zs_channel, R9, (NV | MIE)); - write_zsreg(zs_soft[channel].zs_channel, R10, - (NRZ)); - write_zsreg(zs_soft[channel].zs_channel, R3, - (Rx8|RxENABLE)); - write_zsreg(zs_soft[channel].zs_channel, R5, - (Tx8 | TxENAB | RTS | DTR)); - } - if(channel == MOUSE_LINE) { - /* Tell mouse driver about our presence. */ - if(zs_soft[channel].zs_baud != 1200) - panic("Weird mouse serial baud rate"); + ZS_CLEARERR(zs_soft[channel].zs_channel); + ZS_CLEARFIFO(zs_soft[channel].zs_channel); + } else if(channel == MOUSE_LINE) { zs_soft[channel].cons_mouse = 1; + zs_soft[channel].parity_mask = 0xff; zs_mousechan = zs_soft[channel].zs_channel; + + write_zsreg(zs_soft[channel].zs_channel, R4, + (PAR_EVEN | X16CLK | SB1)); + write_zsreg(zs_soft[channel].zs_channel, R3, Rx8); + write_zsreg(zs_soft[channel].zs_channel, R5, Tx8); + write_zsreg(zs_soft[channel].zs_channel, R9, NV); + write_zsreg(zs_soft[channel].zs_channel, R10, NRZ); + write_zsreg(zs_soft[channel].zs_channel, R11, + (TCBR | RCBR)); + + zs_soft[channel].zs_baud = 4800; + brg = BPS_TO_BRG(zs_soft[channel].zs_baud, + ZS_CLOCK/zs_soft[channel].clk_divisor); + write_zsreg(zs_soft[channel].zs_channel, R12, + (brg & 0xff)); + write_zsreg(zs_soft[channel].zs_channel, R13, + ((brg >> 8) & 0xff)); + write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC); + /* Enable Rx, IRQs, and inform mouse driver */ - write_zsreg(zs_soft[channel].zs_channel, R1, (INT_ALL_Rx)); - write_zsreg(zs_soft[channel].zs_channel, R9, (NV|MIE)); - write_zsreg(zs_soft[channel].zs_channel, R3, (Rx8|RxENABLE)); -#if 0 /* XXX hangs sun4c's sometimes */ + write_zsreg(zs_soft[channel].zs_channel, R14, + (BRSRC | BRENAB)); + write_zsreg(zs_soft[channel].zs_channel, R3, + (Rx8 | RxENAB)); + write_zsreg(zs_soft[channel].zs_channel, R5, Tx8); + write_zsreg(zs_soft[channel].zs_channel, R15, (DCDIE | CTSIE | TxUIE | BRKIE)); -#endif + write_zsreg(zs_soft[channel].zs_channel, R0, + RES_EXT_INT); + write_zsreg(zs_soft[channel].zs_channel, R0, + RES_EXT_INT); + + write_zsreg(zs_soft[channel].zs_channel, R1, + (EXT_INT_ENAB | INT_ALL_Rx)); + write_zsreg(zs_soft[channel].zs_channel, R9, + (NV | MIE)); + sun_mouse_zsinit(); + } else if (zs_soft[channel].is_cons) { + brg = BPS_TO_BRG(zs_soft[channel].zs_baud, + ZS_CLOCK/zs_soft[channel].clk_divisor); + zscons_regs[12] = brg & 0xff; + zscons_regs[13] = (brg >> 8) & 0xff; + + memcpy(zs_soft[channel].curregs, zscons_regs, sizeof(zscons_regs)); + load_zsregs(&zs_soft[channel], zscons_regs); + + ZS_CLEARERR(zs_soft[channel].zs_channel); + ZS_CLEARFIFO(zs_soft[channel].zs_channel); + } else if (zs_soft[channel].kgdb_channel) { + /* If this is the kgdb line, enable interrupts because + * we now want to receive the 'control-c' character + * from the client attached to us asynchronously. + */ + zs_soft[channel].parity_mask = 0xff; + kgdb_chaninit(&zs_soft[channel], 1, + zs_soft[channel].zs_baud); } else { - zs_soft[channel].cons_mouse = 0; + zs_soft[channel].parity_mask = 0xff; + write_zsreg(zs_soft[channel].zs_channel, R4, + (PAR_EVEN | X16CLK | SB1)); + write_zsreg(zs_soft[channel].zs_channel, R3, Rx8); + write_zsreg(zs_soft[channel].zs_channel, R5, Tx8); + write_zsreg(zs_soft[channel].zs_channel, R9, NV); + write_zsreg(zs_soft[channel].zs_channel, R10, NRZ); + write_zsreg(zs_soft[channel].zs_channel, R11, + (RCBR | TCBR)); + zs_soft[channel].zs_baud = 9600; + brg = BPS_TO_BRG(zs_soft[channel].zs_baud, + ZS_CLOCK/zs_soft[channel].clk_divisor); + write_zsreg(zs_soft[channel].zs_channel, R12, + (brg & 0xff)); + write_zsreg(zs_soft[channel].zs_channel, R13, + ((brg >> 8) & 0xff)); + write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC); + write_zsreg(zs_soft[channel].zs_channel, R14, + (BRSRC | BRENAB)); + write_zsreg(zs_soft[channel].zs_channel, R3, Rx8); + write_zsreg(zs_soft[channel].zs_channel, R5, Tx8); + write_zsreg(zs_soft[channel].zs_channel, R15, DCDIE); + write_zsreg(zs_soft[channel].zs_channel, R9, NV | MIE); + write_zsreg(zs_soft[channel].zs_channel, R0, + RES_EXT_INT); + write_zsreg(zs_soft[channel].zs_channel, R0, + RES_EXT_INT); } } - for(info=zs_chain, i=0; info; info = info->zs_next, i++) - { + for (info = zs_chain, i=0; info; info = info->zs_next, i++) { info->magic = SERIAL_MAGIC; - info->port = (int) info->zs_channel; + info->port = (long) info->zs_channel; info->line = i; info->tty = 0; info->irq = zilog_irq; @@ -2101,7 +2556,7 @@ int rs_init(void) info->tqueue.data = info; info->tqueue_hangup.routine = do_serial_hangup; info->tqueue_hangup.data = info; - info->callout_termios =callout_driver.init_termios; + info->callout_termios = callout_driver.init_termios; info->normal_termios = serial_driver.init_termios; info->open_wait = 0; info->close_wait = 0; @@ -2110,15 +2565,13 @@ int rs_init(void) printk(" is a Zilog8530\n"); } - if (request_irq(zilog_irq, - rs_interrupt, + if (request_irq(zilog_irq, rs_interrupt, (SA_INTERRUPT | SA_STATIC_ALLOC), - "Zilog8530", NULL)) + "Zilog8530", zs_chain)) panic("Unable to attach zs intr\n"); restore_flags(flags); keyboard_zsinit(); - return 0; } @@ -2144,10 +2597,15 @@ void unregister_serial(int line) * are addressed backwards, channel B is first, then channel A. */ void -rs_cons_hook(int chip, int out, int channel) +rs_cons_hook(int chip, int out, int line) { + int channel; + if(chip) panic("rs_cons_hook called with chip not zero"); + if(line != 1 && line != 2) + panic("rs_cons_hook called with line not ttya or ttyb"); + channel = line - 1; if(!zs_chips[chip]) { zs_chips[chip] = get_zs(chip); /* Two channels per chip */ @@ -2157,13 +2615,11 @@ rs_cons_hook(int chip, int out, int channel) zs_soft[channel].zs_channel = zs_channels[channel]; zs_soft[channel].change_needed = 0; zs_soft[channel].clk_divisor = 16; - zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]); - rs_cons_check(&zs_soft[channel], channel); if(out) zs_cons_chanout = ((chip * 2) + channel); else zs_cons_chanin = ((chip * 2) + channel); - + rs_cons_check(&zs_soft[channel], channel); } /* This is called at boot time to prime the kgdb serial debugging @@ -2186,12 +2642,11 @@ rs_kgdb_hook(int tty_num) zs_kgdbchan = zs_soft[tty_num].zs_channel; zs_soft[tty_num].change_needed = 0; zs_soft[tty_num].clk_divisor = 16; - zs_soft[tty_num].zs_baud = get_zsbaud(&zs_soft[tty_num]); + zs_soft[tty_num].zs_baud = 9600; zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */ zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */ /* Turn on transmitter/receiver at 8-bits/char */ - kgdb_chaninit(&zs_soft[tty_num], 0, 9600); - ZS_CLEARERR(zs_kgdbchan); - udelay(5); - ZS_CLEARFIFO(zs_kgdbchan); + kgdb_chaninit(&zs_soft[tty_num], 0, 9600); + ZS_CLEARERR(zs_kgdbchan); + ZS_CLEARFIFO(zs_kgdbchan); } diff --git a/drivers/sbus/char/sunserial.h b/drivers/sbus/char/sunserial.h index 8fb029669..b8ae23305 100644 --- a/drivers/sbus/char/sunserial.h +++ b/drivers/sbus/char/sunserial.h @@ -1,6 +1,8 @@ -/* serial.h: Definitions for the Sparc Zilog serial driver. +/* $Id: sunserial.h,v 1.9 1997/04/12 23:33:12 ecd Exp $ + * serial.h: Definitions for the Sparc Zilog serial driver. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) */ #ifndef _SPARC_SERIAL_H #define _SPARC_SERIAL_H @@ -66,9 +68,7 @@ struct serial_struct { #define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */ #define ZILOG_SPD_MASK 0x0030 -#define ZILOG_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */ - -#define ZILOG_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */ +#define ZILOG_SPD_HI 0x0010 /* Use 76800 instead of 38400 bps */ #define ZILOG_SPD_CUST 0x0030 /* Use user-specified divisor */ #define ZILOG_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */ @@ -114,6 +114,9 @@ struct sun_serial { char kgdb_channel; /* Kgdb is running on this channel */ char is_cons; /* Is this our console. */ + char channelA; /* This is channel A. */ + char parity_mask; /* Mask out parity bits in data register. */ + /* We need to know the current clock divisor * to read the bps rate the chip has currently * loaded. @@ -124,9 +127,6 @@ struct sun_serial { /* Current write register values */ unsigned char curregs[NUM_ZSREGS]; - /* Values we need to set next opportunity */ - unsigned char pendregs[NUM_ZSREGS]; - char change_needed; int magic; @@ -231,6 +231,7 @@ struct sun_serial { #define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ #define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ #define INT_ERR_Rx 0x18 /* Int on error only */ +#define RxINT_MASK 0x18 #define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ #define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ @@ -240,7 +241,7 @@ struct sun_serial { /* Write Register 3 */ -#define RxENABLE 0x1 /* Rx Enable */ +#define RxENAB 0x1 /* Rx Enable */ #define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ #define ADD_SM 0x4 /* Address Search Mode (SDLC) */ #define RxCRC_ENAB 0x8 /* Rx CRC Enable */ @@ -250,10 +251,11 @@ struct sun_serial { #define Rx7 0x40 /* Rx 7 Bits/Character */ #define Rx6 0x80 /* Rx 6 Bits/Character */ #define Rx8 0xc0 /* Rx 8 Bits/Character */ +#define RxN_MASK 0xc0 /* Write Register 4 */ -#define PAR_ENA 0x1 /* Parity Enable */ +#define PAR_ENAB 0x1 /* Parity Enable */ #define PAR_EVEN 0x2 /* Parity Even/Odd* */ #define SYNC_ENAB 0 /* Sync Modes Enable */ @@ -282,6 +284,7 @@ struct sun_serial { #define Tx7 0x20 /* Tx 7 bits/character */ #define Tx6 0x40 /* Tx 6 bits/character */ #define Tx8 0x60 /* Tx 8 bits/character */ +#define TxN_MASK 0x60 #define DTR 0x80 /* DTR */ /* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ @@ -334,7 +337,7 @@ struct sun_serial { /* Write Register 13 (upper byte of baud rate generator time constant) */ /* Write Register 14 (Misc control bits) */ -#define BRENABL 1 /* Baud rate generator enable */ +#define BRENAB 1 /* Baud rate generator enable */ #define BRSRC 2 /* Baud rate generator source */ #define DTRREQ 4 /* DTR/Request function */ #define AUTOECHO 8 /* Auto Echo */ @@ -361,7 +364,7 @@ struct sun_serial { #define ZCOUNT 0x2 /* Zero count */ #define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ #define DCD 0x8 /* DCD */ -#define SYNC_HUNT 0x10 /* Sync/hunt */ +#define SYNC 0x10 /* Sync/hunt */ #define CTS 0x20 /* CTS */ #define TxEOM 0x40 /* Tx underrun */ #define BRK_ABRT 0x80 /* Break/Abort */ @@ -384,6 +387,15 @@ struct sun_serial { #define END_FR 0x80 /* End of Frame (SDLC) */ /* Read Register 2 (channel b only) - Interrupt vector */ +#define CHB_Tx_EMPTY 0x00 +#define CHB_EXT_STAT 0x02 +#define CHB_Rx_AVAIL 0x04 +#define CHB_SPECIAL 0x06 +#define CHA_Tx_EMPTY 0x08 +#define CHA_EXT_STAT 0x0a +#define CHA_Rx_AVAIL 0x0c +#define CHA_SPECIAL 0x0e +#define STATUS_MASK 0x0e /* Read Register 3 (interrupt pending register) ch a only */ #define CHBEXT 0x1 /* Channel B Ext/Stat IP */ @@ -408,7 +420,12 @@ struct sun_serial { /* Read Register 15 (value of WR 15) */ /* Misc macros */ -#define ZS_CLEARERR(channel) (channel->control = ERR_RES) +#define ZS_CLEARERR(channel) do { channel->control = ERR_RES; \ + udelay(5); } while(0) + +#define ZS_CLEARSTAT(channel) do { channel->control = RES_EXT_INT; \ + udelay(5); } while(0) + #define ZS_CLEARFIFO(channel) do { volatile unsigned char garbage; \ garbage = channel->data; \ udelay(2); \ diff --git a/drivers/sbus/char/tcx.c b/drivers/sbus/char/tcx.c new file mode 100644 index 000000000..2ecabda62 --- /dev/null +++ b/drivers/sbus/char/tcx.c @@ -0,0 +1,369 @@ +/* $Id: tcx.c,v 1.12 1997/04/14 17:04:51 jj Exp $ + * tcx.c: SUNW,tcx 24/8bit frame buffer driver + * + * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) + * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) + */ + +#include <linux/kd.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/proc_fs.h> + +#include <asm/sbus.h> +#include <asm/io.h> +#include <asm/fbio.h> +#include <asm/pgtable.h> + +#include "../../char/vt_kern.h" +#include "../../char/selection.h" +#include "../../char/console_struct.h" +#include "fb.h" +#include "cg_common.h" + +/* Offset of interesting structures in the tcx registers */ +#define TCX_RAM8BIT_OFFSET 0 +#define TCX_CONTROLPLANE_OFFSET 4 +#define TCX_BROOKTREE_OFFSET 8 +#define TCX_THC_OFFSET 9 +#define TCX_TEC_OFFSET 7 + +/* THC definitions */ +#define TCX_THC_MISC_REV_SHIFT 16 +#define TCX_THC_MISC_REV_MASK 15 +#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 int tec_matrix; + volatile int tec_clip; + volatile int tec_vdc; +}; + +struct tcx_thc { + volatile uint thc_rev; + uint thc_pad0[511]; + volatile uint thc_hs; /* hsync timing */ + volatile uint thc_hsdvs; + volatile uint thc_hd; + volatile uint thc_vs; /* vsync timing */ + volatile uint thc_vd; + volatile uint thc_refresh; + volatile uint thc_misc; + uint thc_pad1[56]; + volatile uint thc_cursxy; /* cursor x,y position (16 bits each) */ + volatile uint thc_cursmask[32]; /* cursor mask bits */ + volatile uint thc_cursbits[32]; /* what to show where mask enabled */ +}; + +static void +tcx_restore_palette (fbinfo_t *fbinfo) +{ + volatile struct bt_regs *bt; + + bt = fbinfo->info.tcx.bt; + bt->addr = 0; + bt->color_map = 0xffffffff; + bt->color_map = 0xffffffff; + bt->color_map = 0xffffffff; +} + +static void +tcx_set_control_plane (fbinfo_t *fb) +{ + register uint *p, *pend; + + p = fb->info.tcx.tcx_cplane; + if (!p) return; + for (pend = p + (fb->info.tcx.tcx_sizes [TCX_CONTROLPLANE_OFFSET] >> 2); p < pend; p++) + *p &= 0xffffff; +} + +static void +tcx_switch_from_graph (void) +{ + fbinfo_t *fb = &(fbinfo [0]); + + /* Reset control plane to 8bit mode if necessary */ + if (fb->open && fb->mmaped) + tcx_set_control_plane (fb); +} + +/* Ugh: X wants to mmap a bunch of cute stuff at the same time :-( */ +/* So, we just mmap the things that are being asked for */ +static int +tcx_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, + long base, fbinfo_t *fb) +{ + uint size, page, r, map_size; + uint map_offset = 0, i; + long offsets[13] = { -1, TCX_RAM24BIT, TCX_UNK3, TCX_UNK4, + -1, TCX_UNK6, TCX_UNK7, + -1, -1, -1, TCX_UNK2, TCX_DHC, TCX_ALT }; + + size = vma->vm_end - vma->vm_start; + if (vma->vm_offset & ~PAGE_MASK) + return -ENXIO; + + /* To stop the swapper from even considering these pages */ + vma->vm_flags |= FB_MMAP_VM_FLAGS; + + /* Each page, see which map applies */ + for (page = 0; page < size; ){ + switch (vma->vm_offset+page){ + case TCX_RAM8BIT: + map_size = fb->type.fb_size; + map_offset = get_phys ((unsigned long) fb->base); + break; + case TCX_TEC: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.tcx.tec); + break; + case TCX_BTREGS: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.tcx.bt); + break; + case TCX_THC: + map_size = PAGE_SIZE; + map_offset = get_phys ((unsigned long)fb->info.tcx.thc); + break; + case TCX_CONTROLPLANE: + if (fb->info.tcx.tcx_cplane) { + map_size = fb->info.tcx.tcx_sizes [TCX_CONTROLPLANE_OFFSET]; + map_offset = get_phys ((unsigned long)fb->info.tcx.tcx_cplane); + } else + map_size = 0; + break; + default: + map_size = 0; + for (i = 0; i < 13; i++) + if (offsets [i] == vma->vm_offset+page) { + if ((map_size = fb->info.tcx.tcx_sizes [i])) + map_offset = fb->info.tcx.tcx_offsets [i]; + break; + } + break; + } + if (!map_size){ + page += PAGE_SIZE; + continue; + } + if (page + map_size > size) + map_size = size - page; + r = io_remap_page_range (vma->vm_start+page, + map_offset, + map_size, vma->vm_page_prot, + fb->space); + if (r) return -EAGAIN; + page += map_size; + } + vma->vm_inode = inode; + inode->i_count++; + return 0; +} + +static void +tcx_loadcmap (fbinfo_t *fb, int index, int count) +{ + struct bt_regs *bt = fb->info.tcx.bt; + int i; + + bt->addr = index << 24; + for (i = index; count--; i++){ + bt->color_map = fb->color_map CM(i,0) << 24; + bt->color_map = fb->color_map CM(i,1) << 24; + bt->color_map = fb->color_map CM(i,2) << 24; + } + bt->addr = 0; +} + +static void +tcx_setcursormap (fbinfo_t *fb, unsigned char *red, + unsigned char *green, + unsigned char *blue) +{ + struct bt_regs *bt = fb->info.tcx.bt; + + /* Note the 2 << 24 is different from cg6's 1 << 24 */ + bt->addr = 2 << 24; + bt->cursor = red[0] << 24; + bt->cursor = green[0] << 24; + bt->cursor = blue[0] << 24; + bt->addr = 3 << 24; + bt->cursor = red[1] << 24; + bt->cursor = green[1] << 24; + bt->cursor = blue[1] << 24; + bt->addr = 0; +} + +/* Load cursor information */ +static void +tcx_setcursor (fbinfo_t *fb) +{ + uint v; + struct cg_cursor *c = &fb->cursor; + + 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); + } + fb->info.tcx.thc->thc_cursxy = v; +} + +/* Set cursor shape */ +static void +tcx_setcurshape (fbinfo_t *fb) +{ + struct tcx_thc *thc = fb->info.tcx.thc; + int i; + + for (i = 0; i < 32; i++){ + thc->thc_cursmask [i] = fb->cursor.bits[0][i]; + thc->thc_cursbits [i] = fb->cursor.bits[1][i]; + } +} + +static void +tcx_blank (fbinfo_t *fb) +{ + fb->info.tcx.thc->thc_misc &= ~TCX_THC_MISC_VIDEO; +} + +static void +tcx_unblank (fbinfo_t *fb) +{ + fb->info.tcx.thc->thc_misc |= TCX_THC_MISC_VIDEO; +} + +void +tcx_reset (fbinfo_t *fb) +{ + struct tcx_info *tcx = &(fb->info.tcx); + + if (fb->setcursor) + sun_hw_hide_cursor (); + /* Reset control plane to 8bit mode if necessary */ + if (fb->open && fb->mmaped) + tcx_set_control_plane (fb); + + /* Turn off stuff in the Transform Engine. */ + tcx->tec->tec_matrix = 0; + tcx->tec->tec_clip = 0; + tcx->tec->tec_vdc = 0; + + /* Enable cursor in Brooktree DAC. */ + tcx->bt->addr = 0x06 << 24; + tcx->bt->control |= 0x03 << 24; +} + +__initfunc(void tcx_setup (fbinfo_t *fb, int slot, int node, u32 tcx, struct linux_sbus_device *sbdp)) +{ + struct tcx_info *tcxinfo; + int i; + + printk ("tcx%d at 0x%8.8x ", slot, tcx); + + /* Fill in parameters we left out */ + fb->type.fb_cmsize = 256; + fb->mmap = tcx_mmap; + fb->loadcmap = tcx_loadcmap; + fb->reset = tcx_reset; + fb->blank = tcx_blank; + fb->unblank = tcx_unblank; + fb->emulations [1] = FBTYPE_SUN3COLOR; + fb->emulations [2] = FBTYPE_MEMCOLOR; + fb->switch_from_graph = tcx_switch_from_graph; + fb->postsetup = sun_cg_postsetup; + + tcxinfo = (struct tcx_info *) &fb->info.tcx; + + memset (tcxinfo, 0, sizeof(struct tcx_info)); + + for (i = 0; i < 13; i++) + tcxinfo->tcx_offsets [i] = (long)(sbdp->reg_addrs [i].phys_addr); + + /* Map the hardware registers */ + tcxinfo->bt = sparc_alloc_io((u32)tcxinfo->tcx_offsets [TCX_BROOKTREE_OFFSET], 0, + sizeof (struct bt_regs),"tcx_dac", fb->space, 0); + tcxinfo->thc = sparc_alloc_io((u32)tcxinfo->tcx_offsets [TCX_THC_OFFSET], 0, + sizeof (struct tcx_thc), "tcx_thc", fb->space, 0); + tcxinfo->tec = sparc_alloc_io((u32)tcxinfo->tcx_offsets [TCX_TEC_OFFSET], 0, + sizeof (struct tcx_tec), "tcx_tec", fb->space, 0); + if (!fb->base){ + fb->base = (uint) + sparc_alloc_io((u32)tcxinfo->tcx_offsets [TCX_RAM8BIT_OFFSET], 0, + fb->type.fb_size, "tcx_ram", fb->space, 0); + } + + if (prom_getbool (node, "hw-cursor")) { + fb->setcursor = tcx_setcursor; + fb->setcursormap = tcx_setcursormap; + fb->setcurshape = tcx_setcurshape; + } + + if (!slot) { + fb_restore_palette = tcx_restore_palette; + } + + i = fb->type.fb_size; + tcxinfo->tcx_sizes[2] = i << 3; + tcxinfo->tcx_sizes[3] = i << 3; + tcxinfo->tcx_sizes[10] = 0x20000; + tcxinfo->tcx_sizes[11] = PAGE_SIZE; + tcxinfo->tcx_sizes[12] = PAGE_SIZE; + + if (prom_getbool (node, "tcx-8-bit")) + tcxinfo->lowdepth = 1; + + if (!tcxinfo->lowdepth) { + tcxinfo->tcx_sizes[1] = i << 2; + tcxinfo->tcx_sizes[4] = i << 2; + tcxinfo->tcx_sizes[5] = i << 3; + tcxinfo->tcx_sizes[6] = i << 3; + fb->type.fb_depth = 24; + tcxinfo->tcx_cplane = + sparc_alloc_io((u32)tcxinfo->tcx_offsets[TCX_CONTROLPLANE_OFFSET], 0, + tcxinfo->tcx_sizes [TCX_CONTROLPLANE_OFFSET], + "tcx_cplane", fb->space, 0); + } + + /* Initialize Brooktree DAC */ + tcxinfo->bt->addr = 0x04 << 24; /* color planes */ + tcxinfo->bt->control = 0xff << 24; + tcxinfo->bt->addr = 0x05 << 24; + tcxinfo->bt->control = 0x00 << 24; + tcxinfo->bt->addr = 0x06 << 24; /* overlay plane */ + tcxinfo->bt->control = 0x73 << 24; + tcxinfo->bt->addr = 0x07 << 24; + tcxinfo->bt->control = 0x00 << 24; + + printk("Rev %d.%d %s\n", + (tcxinfo->thc->thc_rev >> TCX_THC_REV_REV_SHIFT) & TCX_THC_REV_REV_MASK, + (tcxinfo->thc->thc_rev >> TCX_THC_REV_MINREV_SHIFT) & TCX_THC_REV_MINREV_MASK, + tcxinfo->lowdepth ? "8-bit only" : "24-bit depth"); + + /* Reset the tcx */ + tcx_reset(fb); + + if (!slot) + /* Enable Video */ + tcx_unblank (fb); + else + tcx_blank (fb); +} diff --git a/drivers/sbus/char/vfc.h b/drivers/sbus/char/vfc.h new file mode 100644 index 000000000..752bf748c --- /dev/null +++ b/drivers/sbus/char/vfc.h @@ -0,0 +1,171 @@ +#ifndef _LINUX_VFC_H_ +#define _LINUX_VFC_H_ + +/* + * The control register for the vfc is at offset 0x4000 + * The first field ram bank is located at offset 0x5000 + * The second field ram bank is at offset 0x7000 + * i2c_reg address the Phillips PCF8584(see notes in vfc_i2c.c) + * data and transmit register. + * i2c_s1 controls register s1 of the PCF8584 + * i2c_write seems to be similar to i2c_write but I am not + * quite sure why sun uses it + * + * I am also not sure whether or not you can read the fram bank as a + * whole or whether you must read each word individually from offset + * 0x5000 as soon as I figure it out I will update this file */ + +struct vfc_regs { + char pad1[0x4000]; + unsigned int control; /* Offset 0x4000 */ + char pad2[0xffb]; /* from offset 0x4004 to 0x5000 */ + unsigned int fram_bank1; /* Offset 0x5000 */ + char pad3[0xffb]; /* from offset 0x5004 to 0x6000 */ + unsigned int i2c_reg; /* Offset 0x6000 */ + unsigned int i2c_magic2; /* Offset 0x6004 */ + unsigned int i2c_s1; /* Offset 0x6008 */ + unsigned int i2c_write; /* Offset 0x600c */ + char pad4[0xff0]; /* from offset 0x6010 to 0x7000 */ + unsigned int fram_bank2; /* Offset 0x7000 */ + char pad5[0x1000]; +}; + +#define VFC_SAA9051_NR (13) +#define VFC_SAA9051_ADDR (0x8a) + /* The saa9051 returns the following for its status + * bit 0 - 0 + * bit 1 - SECAM color detected (1=found,0=not found) + * bit 2 - COLOR detected (1=found,0=not found) + * bit 3 - 0 + * bit 4 - Field frequency bit (1=60Hz (NTSC), 0=50Hz (PAL)) + * bit 5 - 1 + * bit 6 - horizontal frequency lock (1=transmitter found, + * 0=no transmitter) + * bit 7 - Power on reset bit (1=reset,0=at least one successful + * read of the status byte) + */ + +#define VFC_SAA9051_PONRES (0x80) +#define VFC_SAA9051_HLOCK (0x40) +#define VFC_SAA9051_FD (0x10) +#define VFC_SAA9051_CD (0x04) +#define VFC_SAA9051_CS (0x02) + + +/* The various saa9051 sub addresses */ + +#define VFC_SAA9051_IDEL (0) +#define VFC_SAA9051_HSY_START (1) +#define VFC_SAA9051_HSY_STOP (2) +#define VFC_SAA9051_HC_START (3) +#define VFC_SAA9051_HC_STOP (4) +#define VFC_SAA9051_HS_START (5) +#define VFC_SAA9051_HORIZ_PEAK (6) +#define VFC_SAA9051_HUE (7) +#define VFC_SAA9051_C1 (8) +#define VFC_SAA9051_C2 (9) +#define VFC_SAA9051_C3 (0xa) +#define VFC_SAA9051_SECAM_DELAY (0xb) + + +/* Bit settings for saa9051 sub address 0x06 */ + +#define VFC_SAA9051_AP1 (0x01) +#define VFC_SAA9051_AP2 (0x02) +#define VFC_SAA9051_COR1 (0x04) +#define VFC_SAA9051_COR2 (0x08) +#define VFC_SAA9051_BP1 (0x10) +#define VFC_SAA9051_BP2 (0x20) +#define VFC_SAA9051_PF (0x40) +#define VFC_SAA9051_BY (0x80) + + +/* Bit settings for saa9051 sub address 0x08 */ + +#define VFC_SAA9051_CCFR0 (0x01) +#define VFC_SAA9051_CCFR1 (0x02) +#define VFC_SAA9051_YPN (0x04) +#define VFC_SAA9051_ALT (0x08) +#define VFC_SAA9051_CO (0x10) +#define VFC_SAA9051_VTR (0x20) +#define VFC_SAA9051_FS (0x40) +#define VFC_SAA9051_HPLL (0x80) + + +/* Bit settings for saa9051 sub address 9 */ + +#define VFC_SAA9051_SS0 (0x01) +#define VFC_SAA9051_SS1 (0x02) +#define VFC_SAA9051_AFCC (0x04) +#define VFC_SAA9051_CI (0x08) +#define VFC_SAA9051_SA9D4 (0x10) /* Don't care bit */ +#define VFC_SAA9051_OEC (0x20) +#define VFC_SAA9051_OEY (0x40) +#define VFC_SAA9051_VNL (0x80) + + +/* Bit settings for saa9051 sub address 0x0A */ + +#define VFC_SAA9051_YDL0 (0x01) +#define VFC_SAA9051_YDL1 (0x02) +#define VFC_SAA9051_YDL2 (0x04) +#define VFC_SAA9051_SS2 (0x08) +#define VFC_SAA9051_SS3 (0x10) +#define VFC_SAA9051_YC (0x20) +#define VFC_SAA9051_CT (0x40) +#define VFC_SAA9051_SYC (0x80) + + +#define VFC_SAA9051_SA(a,b) ((a)->saa9051_state_array[(b)+1]) +#define vfc_update_saa9051(a) (vfc_i2c_sendbuf((a),VFC_SAA9051_ADDR,\ + (a)->saa9051_state_array,\ + VFC_SAA9051_NR)) + + +struct vfc_dev { + volatile struct vfc_regs *regs; + struct vfc_regs *phys_regs; + unsigned int control_reg; + struct semaphore device_lock_sem; + struct timer_list poll_timer; + struct wait_queue *poll_wait; + int instance; + int busy; + unsigned long which_io; + unsigned char saa9051_state_array[VFC_SAA9051_NR]; +}; + +extern struct vfc_dev **vfc_dev_lst; + +void captstat_reset(struct vfc_dev *); +void memptr_reset(struct vfc_dev *); + +int vfc_pcf8584_init(struct vfc_dev *); +void vfc_i2c_delay_no_busy(struct vfc_dev *, unsigned long); +void vfc_i2c_delay(struct vfc_dev *); +int vfc_i2c_sendbuf(struct vfc_dev *, unsigned char, char *, int) ; +int vfc_i2c_recvbuf(struct vfc_dev *, unsigned char, char *, int) ; +int vfc_i2c_reset_bus(struct vfc_dev *); +int vfc_init_i2c_bus(struct vfc_dev *); +void vfc_lock_device(struct vfc_dev *); +void vfc_unlock_device(struct vfc_dev *); + +#define VFC_CONTROL_DIAGMODE 0x10000000 +#define VFC_CONTROL_MEMPTR 0x20000000 +#define VFC_CONTROL_CAPTURE 0x02000000 +#define VFC_CONTROL_CAPTRESET 0x04000000 + +#define VFC_STATUS_CAPTURE 0x08000000 + +#ifdef VFC_DEBUG +#define VFC_DEBUG_PRINTK(a) printk a +#else +#define VFC_DEBUG_PRINTK(a) +#endif + +#endif /* _LINUX_VFC_H_ */ + + + + + diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c new file mode 100644 index 000000000..556d06e9f --- /dev/null +++ b/drivers/sbus/char/vfc_dev.c @@ -0,0 +1,693 @@ +/* + * drivers/sbus/char/vfc_dev.c + * + * Driver for the Videopix Frame Grabber. + * + * In order to use the VFC you need to progeam the video controller + * chip. This chip is the Phillips SAA9051. You need to call their + * documentation ordering line to get the docs. + * + * Their is very little documentation on the VFC itself. There is + * some useful info that can be found in the manuals that come with + * the card. I will hopefully write some better docs at a later date. + * + * Copyright (C) 1996 Manish Vachharajani (mvachhar@noc.rutgers.edu) + * */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/malloc.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/fs.h> + +#include <asm/openprom.h> +#include <asm/oplib.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/sbus.h> +#include <asm/delay.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/uaccess.h> + +#define VFC_MAJOR (60) + +#if 0 +#define VFC_DEBUG +#endif + +#include "vfc.h" +#include <asm/vfc_ioctls.h> + +struct vfc_dev **vfc_dev_lst; +static char vfcstr[]="vfc"; +static unsigned char saa9051_init_array[VFC_SAA9051_NR]= +{ 0x00, 0x64, 0x72, 0x52, + 0x36, 0x18, 0xff, 0x20, + 0xfc, 0x77, 0xe3, 0x50, + 0x3e}; + +void vfc_lock_device(struct vfc_dev *dev) { + down(&dev->device_lock_sem); +} + +void vfc_unlock_device(struct vfc_dev *dev) { + up(&dev->device_lock_sem); +} + + +void vfc_captstat_reset(struct vfc_dev *dev) +{ + dev->control_reg |= VFC_CONTROL_CAPTRESET; + dev->regs->control=dev->control_reg; + dev->control_reg &= ~VFC_CONTROL_CAPTRESET; + dev->regs->control=dev->control_reg; + dev->control_reg |= VFC_CONTROL_CAPTRESET; + dev->regs->control=dev->control_reg; + return; +} + +void vfc_memptr_reset(struct vfc_dev *dev) +{ + dev->control_reg |= VFC_CONTROL_MEMPTR; + dev->regs->control = dev->control_reg; + dev->control_reg &= ~VFC_CONTROL_MEMPTR; + dev->regs->control = dev->control_reg; + dev->control_reg |= VFC_CONTROL_MEMPTR; + dev->regs->control = dev->control_reg; + return; +} + +int vfc_csr_init(struct vfc_dev *dev) +{ + dev->control_reg = 0x80000000; + dev->regs->control = dev->control_reg; + udelay(200); + dev->control_reg &= ~0x80000000; + dev->regs->control = dev->control_reg; + udelay(100); + dev->regs->i2c_magic2 = 0x0f000000; + + vfc_memptr_reset(dev); + + dev->control_reg &= ~VFC_CONTROL_DIAGMODE; + dev->control_reg &= ~VFC_CONTROL_CAPTURE; + dev->control_reg |= 0x40000000; + dev->regs->control=dev->control_reg; + + vfc_captstat_reset(dev); + + return 0; +} + +int vfc_saa9051_init(struct vfc_dev *dev) +{ + int i; + for(i=0;i<VFC_SAA9051_NR;i++) { + dev->saa9051_state_array[i]=saa9051_init_array[i]; + } + vfc_i2c_sendbuf(dev,VFC_SAA9051_ADDR, + dev->saa9051_state_array, VFC_SAA9051_NR); + return 0; +} + +int init_vfc_hw(struct vfc_dev *dev) +{ + vfc_lock_device(dev); + vfc_csr_init(dev); + + vfc_pcf8584_init(dev); + vfc_init_i2c_bus(dev); /* hopefully this doesn't undo the magic + sun code above*/ + vfc_saa9051_init(dev); + vfc_unlock_device(dev); + return 0; +} + +int init_vfc_devstruct(struct vfc_dev *dev, int instance) +{ + dev->instance=instance; + dev->device_lock_sem=MUTEX; + dev->control_reg=0; + dev->poll_wait=NULL; + dev->busy=0; + /* initialize the timer struct */ + return 0; +} + +int init_vfc_device(struct linux_sbus_device *sdev,struct vfc_dev *dev, + int instance) { + struct linux_prom_registers reg; + if(!dev) { + printk(KERN_ERR "VFC: Bogus pointer passed\n"); + return -ENOMEM; + } + printk("Initializing vfc%d\n",instance); + dev->regs=NULL; + memcpy(®,&sdev->reg_addrs[0],sizeof(struct linux_prom_registers)); + prom_apply_sbus_ranges(sdev->my_bus, ®, sdev->num_registers, sdev); + dev->regs=sparc_alloc_io(reg.phys_addr, 0, + sizeof(struct vfc_regs), vfcstr, + reg.which_io, 0x0); + dev->which_io=reg.which_io; + dev->phys_regs=(struct vfc_regs *)reg.phys_addr; + if(!dev->regs) return -EIO; + + printk("vfc%d: registers mapped at phys_addr: 0x%lx\n virt_addr: 0x%lx\n", + instance,(unsigned long)reg.phys_addr,(unsigned long)dev->regs); + + if(init_vfc_devstruct(dev,instance)) return -EINVAL; + if(init_vfc_hw(dev)) return -EIO; + + return 0; +} + + +struct vfc_dev *vfc_get_dev_ptr(int instance) +{ + return vfc_dev_lst[instance]; +} + +static int vfc_open(struct inode *inode, struct file *file) +{ + struct vfc_dev *dev; + dev=vfc_get_dev_ptr(MINOR(inode->i_rdev)); + if(!dev) return -ENODEV; + if(dev->busy) return -EBUSY; + dev->busy=1; + MOD_INC_USE_COUNT; + vfc_lock_device(dev); + + vfc_csr_init(dev); + vfc_pcf8584_init(dev); + vfc_init_i2c_bus(dev); + vfc_saa9051_init(dev); + vfc_memptr_reset(dev); + vfc_captstat_reset(dev); + + vfc_unlock_device(dev); + return 0; +} + +static int vfc_release(struct inode *inode,struct file *file) +{ + struct vfc_dev *dev; + dev=vfc_get_dev_ptr(MINOR(inode->i_rdev)); + if(!dev) return -EINVAL; + if(!dev->busy) return 0; + dev->busy=0; + MOD_DEC_USE_COUNT; + return 0; +} + +static int vfc_debug(struct vfc_dev *dev, int cmd, unsigned long arg) +{ + struct vfc_debug_inout inout; + unsigned char *buffer; + + switch(cmd) { + case VFC_I2C_SEND: + if(copy_from_user(&inout, (void *)arg, sizeof(inout))) + return -EFAULT; + + buffer = kmalloc(inout.len*sizeof(char), GFP_KERNEL); + if (!buffer) + return -ENOMEM; + if(copy_from_user(buffer, inout.buffer, inout.len*sizeof(char))) { + kfree_s(buffer,inout.len); + return -EFAULT; + } + + vfc_lock_device(dev); + inout.ret= + vfc_i2c_sendbuf(dev,inout.addr & 0xff, + inout.buffer,inout.len); + if(copy_to_user((void *)arg,&inout,sizeof(inout))) { + kfree_s(buffer,inout.len); + return -EFAULT; + } + vfc_unlock_device(dev); + + kfree_s(buffer, inout.len); + + break; + case VFC_I2C_RECV: + + if(copy_from_user(&inout, (void *)arg, sizeof(inout))) + return -EFAULT; + + buffer = kmalloc(inout.len, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + memset(buffer,0,inout.len*sizeof(char)); + vfc_lock_device(dev); + inout.ret= + vfc_i2c_recvbuf(dev,inout.addr & 0xff + ,buffer,inout.len); + vfc_unlock_device(dev); + + if(copy_to_user(inout.buffer, buffer, inout.len)) { + kfree_s(buffer,inout.len); + return -EFAULT; + } + if(copy_to_user((void *)arg,&inout,sizeof(inout))) { + kfree_s(buffer,inout.len); + return -EFAULT; + } + kfree_s(buffer,inout.len); + break; + default: + return -EINVAL; + } + return 0; +} + +int vfc_capture_start(struct vfc_dev *dev) +{ + vfc_captstat_reset(dev); + dev->control_reg=dev->regs->control; + if((dev->control_reg & VFC_STATUS_CAPTURE)) { + printk(KERN_ERR "vfc%d: vfc capture status not reset\n", + dev->instance); + return -EIO; + } + + vfc_lock_device(dev); + dev->control_reg &= ~VFC_CONTROL_CAPTURE; + dev->regs->control=dev->control_reg; + dev->control_reg |= VFC_CONTROL_CAPTURE; + dev->regs->control=dev->control_reg; + dev->control_reg &= ~VFC_CONTROL_CAPTURE; + dev->regs->control=dev->control_reg; + vfc_unlock_device(dev); + + return 0; +} + +int vfc_capture_poll(struct vfc_dev *dev) +{ + int timeout=1000; + while(!timeout--) { + if((dev->regs->control & VFC_STATUS_CAPTURE)) break; + vfc_i2c_delay_no_busy(dev,100); + } + if(!timeout) { + printk(KERN_WARNING "vfc%d: capture timed out\n", dev->instance); + return -ETIMEDOUT; + } + return 0; +} + + + +static int vfc_set_control_ioctl(struct inode *inode, struct file *file, + struct vfc_dev *dev, unsigned long arg) +{ + int setcmd,ret=0; + if(copy_from_user(&setcmd,(void *)arg,sizeof(unsigned int))) + return -EFAULT; +#if 0 + VFC_DEBUG_PRINTK(("vfc%d: IOCTL(VFCSCTRL) arg=0x%x\n", + dev->instance,setcmd)); +#endif + switch(setcmd) { + case MEMPRST: + vfc_lock_device(dev); + vfc_memptr_reset(dev); + vfc_unlock_device(dev); + ret=0; + break; + case CAPTRCMD: + vfc_capture_start(dev); + vfc_capture_poll(dev); + break; + case DIAGMODE: + if(suser()) { + vfc_lock_device(dev); + dev->control_reg |= VFC_CONTROL_DIAGMODE; + dev->regs->control = dev->control_reg; + vfc_unlock_device(dev); + ret=0; + } else ret=-EPERM; + break; + case NORMMODE: + vfc_lock_device(dev); + dev->control_reg &= ~VFC_CONTROL_DIAGMODE; + dev->regs->control = dev->control_reg; + vfc_unlock_device(dev); + ret=0; + break; + case CAPTRSTR: + vfc_capture_start(dev); + ret=0; + break; + case CAPTRWAIT: + vfc_capture_poll(dev); + ret=0; + break; + default: + ret=-EINVAL; + } + return ret; +} + + +int vfc_port_change_ioctl(struct inode *inode, struct file *file, + struct vfc_dev *dev, unsigned long arg) +{ + int ret=0; + int cmd; + if(copy_from_user(&cmd, (void *)arg, sizeof(unsigned int))) { + VFC_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to " + "vfc_port_change_ioctl\n",dev->instance)); + return -EFAULT; + } + + VFC_DEBUG_PRINTK(("vfc%d: IOCTL(VFCPORTCHG) arg=0x%x\n", + dev->instance,cmd)); + + switch(cmd) { + case 1: + case 2: + VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_START) = 0x72; + VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_STOP) = 0x52; + VFC_SAA9051_SA(dev,VFC_SAA9051_HC_START) = 0x36; + VFC_SAA9051_SA(dev,VFC_SAA9051_HC_STOP) = 0x18; + VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) = VFC_SAA9051_BP2; + VFC_SAA9051_SA(dev,VFC_SAA9051_C3) = VFC_SAA9051_CT | VFC_SAA9051_SS3; + VFC_SAA9051_SA(dev,VFC_SAA9051_SECAM_DELAY) = 0x3e; + break; + case 3: + VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_START) = 0x3a; + VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_STOP) = 0x17; + VFC_SAA9051_SA(dev,VFC_SAA9051_HC_START) = 0xfa; + VFC_SAA9051_SA(dev,VFC_SAA9051_HC_STOP) = 0xde; + VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) = VFC_SAA9051_BY | VFC_SAA9051_PF | VFC_SAA9051_BP2; + VFC_SAA9051_SA(dev,VFC_SAA9051_C3) = VFC_SAA9051_YC; + VFC_SAA9051_SA(dev,VFC_SAA9051_SECAM_DELAY) = 0; + VFC_SAA9051_SA(dev,VFC_SAA9051_C2) &= ~(VFC_SAA9051_SS0 | VFC_SAA9051_SS1); + break; + default: + ret=-EINVAL; + return ret; + break; + } + + switch(cmd) { + case 1: + VFC_SAA9051_SA(dev,VFC_SAA9051_C2) |= VFC_SAA9051_SS0 | VFC_SAA9051_SS1; + break; + case 2: + VFC_SAA9051_SA(dev,VFC_SAA9051_C2) &= ~(VFC_SAA9051_SS0 | VFC_SAA9051_SS1); + VFC_SAA9051_SA(dev,VFC_SAA9051_C2) |= VFC_SAA9051_SS0; + break; + case 3: + break; + default: + ret=-EINVAL; + return ret; + break; + } + VFC_SAA9051_SA(dev,VFC_SAA9051_C3) &= ~(VFC_SAA9051_SS2); + ret=vfc_update_saa9051(dev); + udelay(500); + VFC_SAA9051_SA(dev,VFC_SAA9051_C3) |= (VFC_SAA9051_SS2); + ret=vfc_update_saa9051(dev); + return ret; +} + +int vfc_set_video_ioctl(struct inode *inode, struct file *file, + struct vfc_dev *dev, unsigned long arg) +{ + int ret=0; + int cmd; + if(copy_from_user(&cmd, (void *)arg, sizeof(unsigned int))) { + VFC_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to " + "vfc_set_video_ioctl\n",dev->instance)); + return -EFAULT; + } + + VFC_DEBUG_PRINTK(("vfc%d: IOCTL(VFCSVID) arg=0x%x\n", + dev->instance,cmd)); + switch(cmd) { + + case STD_NTSC: + VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~VFC_SAA9051_ALT; + VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_YPN | + VFC_SAA9051_CCFR0 | VFC_SAA9051_CCFR1 | VFC_SAA9051_FS; + ret=vfc_update_saa9051(dev); + break; + case STD_PAL: + VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~(VFC_SAA9051_YPN | + VFC_SAA9051_CCFR1 | + VFC_SAA9051_CCFR0 | + VFC_SAA9051_FS); + VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_ALT; + ret=vfc_update_saa9051(dev); + break; + + case COLOR_ON: + VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_CO; + VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) &= ~(VFC_SAA9051_BY | VFC_SAA9051_PF); + ret=vfc_update_saa9051(dev); + break; + case MONO: + VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~(VFC_SAA9051_CO); + VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) |= VFC_SAA9051_BY | VFC_SAA9051_PF; + ret=vfc_update_saa9051(dev); + break; + default: + ret=-EINVAL; + break; + } + return ret; +} + +int vfc_get_video_ioctl(struct inode *inode, struct file *file, + struct vfc_dev *dev, unsigned long arg) +{ + int ret=0; + unsigned int status=NO_LOCK; + unsigned char buf[1]; + + if(vfc_i2c_recvbuf(dev,VFC_SAA9051_ADDR,buf,1)) { + printk(KERN_ERR "vfc%d: Unable to get status\n",dev->instance); + return -EIO; + } + + if(buf[0] & VFC_SAA9051_HLOCK) { + status = NO_LOCK; + } else if(buf[0] & VFC_SAA9051_FD) { + if(buf[0] & VFC_SAA9051_CD) + status=NTSC_COLOR; + else + status=NTSC_NOCOLOR; + } else { + if(buf[0] & VFC_SAA9051_CD) + status=PAL_COLOR; + else + status=PAL_NOCOLOR; + } + VFC_DEBUG_PRINTK(("vfc%d: IOCTL(VFCGVID) returning status 0x%x; buf[0]=%x\n",dev->instance, + status,buf[0])); + if(copy_to_user((void *)arg,&status,sizeof(unsigned int))) { + VFC_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to " + "vfc_get_video_ioctl\n",dev->instance)); + return -EFAULT; + } + return ret; +} + +static int vfc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret=0; + unsigned int tmp; + struct vfc_dev *dev; + + dev=vfc_get_dev_ptr(MINOR(inode->i_rdev)); + if(!dev) return -ENODEV; + + switch(cmd & 0x0000ffff) { + case VFCGCTRL: +#if 0 + VFC_DEBUG_PRINTK(("vfc%d: IOCTL(VFCGCTRL)\n",dev->instance)); +#endif + tmp=dev->regs->control; + if(copy_to_user((void *)arg,&tmp,sizeof(unsigned int))) + return -EFAULT; + ret=0; + break; + case VFCSCTRL: + ret=vfc_set_control_ioctl(inode, file, dev, arg); + break; + case VFCGVID: + ret=vfc_get_video_ioctl(inode,file,dev,arg); + break; + case VFCSVID: + ret=vfc_set_video_ioctl(inode,file,dev,arg); + break; + case VFCHUE: +#if 0 + VFC_DEBUG_PRINTK(("vfc%d: IOCTL(VFCHUE)\n",dev->instance)); +#endif + + if(copy_from_user(&tmp,(void *)arg,sizeof(unsigned int))) { + VFC_DEBUG_PRINTK(("vfc%d: User passed bogus pointer " + "to IOCTL(VFCHUE)",dev->instance)); + ret=-EFAULT; + } else { + VFC_SAA9051_SA(dev,VFC_SAA9051_HUE)=tmp; + vfc_update_saa9051(dev); + ret=0; + } + break; + case VFCPORTCHG: + ret=vfc_port_change_ioctl(inode, file, dev, arg); + break; + case VFCRDINFO: + ret=-EINVAL; + VFC_DEBUG_PRINTK(("vfc%d: IOCTL(VFCRDINFO)\n",dev->instance)); + break; + default: + ret=vfc_debug(vfc_get_dev_ptr(MINOR(inode->i_rdev)), + cmd,arg); + break; + } + return ret; +} + +static int vfc_mmap(struct inode *inode, struct file *file, + struct vm_area_struct *vma) +{ + unsigned int map_size,ret,map_offset; + struct vfc_dev *dev; + + dev=vfc_get_dev_ptr(MINOR(inode->i_rdev)); + if(!dev) return -ENODEV; + + map_size=vma->vm_end - vma->vm_start; + if(map_size > sizeof(struct vfc_regs)) + map_size=sizeof(struct vfc_regs); + + + if(vma->vm_offset & ~PAGE_MASK) return -ENXIO; + vma->vm_flags |= VM_SHM | VM_LOCKED | VM_IO | VM_MAYREAD | VM_MAYWRITE | VM_MAYSHARE; + map_offset=(unsigned int)dev->phys_regs; + ret=io_remap_page_range(vma->vm_start,map_offset,map_size, + vma->vm_page_prot, dev->which_io); + if(ret) return -EAGAIN; + vma->vm_inode=inode; + inode->i_count++; + return 0; +} + +static long long vfc_lseek(struct inode *inode, struct file *file, + long long offset, int origin) +{ + return -ESPIPE; +} + +static struct file_operations vfc_fops = { + vfc_lseek, /* vfc_lseek */ + NULL, /* vfc_write */ + NULL, /* vfc_read */ + NULL, /* vfc_readdir */ + NULL, /* vfc_poll */ + vfc_ioctl, + vfc_mmap, + vfc_open, + vfc_release, +}; + + +static int vfc_probe(void) +{ + struct linux_sbus *bus; + struct linux_sbus_device *sdev = NULL; + int ret; + int instance=0,cards=0; + + for_all_sbusdev(sdev,bus) { + if (strcmp(sdev->prom_name,"vfc") == 0) { + cards++; + continue; + } + } + + if (!cards) + return -ENODEV; + + vfc_dev_lst=(struct vfc_dev **)kmalloc(sizeof(struct vfc_dev *)* + (cards+1), + GFP_KERNEL); + if(!vfc_dev_lst) + return -ENOMEM; + memset(vfc_dev_lst,0,sizeof(struct vfc_dev *)*(cards+1)); + vfc_dev_lst[cards]=NULL; + + ret=register_chrdev(VFC_MAJOR,vfcstr,&vfc_fops); + if(ret) { + printk(KERN_ERR "Unable to get major number %d\n",VFC_MAJOR); + kfree(vfc_dev_lst); + return -EIO; + } + + instance=0; + for_all_sbusdev(sdev,bus) { + if (strcmp(sdev->prom_name,"vfc") == 0) { + vfc_dev_lst[instance]=(struct vfc_dev *) + kmalloc(sizeof(struct vfc_dev), GFP_KERNEL); + if(vfc_dev_lst[instance] == NULL) return -ENOMEM; + ret=init_vfc_device(sdev, + vfc_dev_lst[instance], + instance); + if(ret) { + printk(KERN_ERR "Unable to initialize" + " vfc%d device\n",instance); + } else { + } + + instance++; + continue; + } + } + + return 0; +} + +#ifdef MODULE +int init_module(void) +#else +int vfc_init(void) +#endif +{ +#ifdef MODULE + register_symtab(0); +#endif + return vfc_probe(); +} + +#ifdef MODULE +static void deinit_vfc_device(struct vfc_dev *dev) +{ + if(!dev) return; + sparc_free_io((void *)dev->regs,sizeof(struct vfc_regs)); + kfree(dev); +} + +void cleanup_module(void) +{ + struct vfc_dev **devp; + unregister_chrdev(VFC_MAJOR,vfcstr); + for(devp=vfc_dev_lst;*devp;devp++) { + deinit_vfc_device(*devp); + } + kfree(vfc_dev_lst); + return; +} +#endif + + diff --git a/drivers/sbus/char/vfc_i2c.c b/drivers/sbus/char/vfc_i2c.c new file mode 100644 index 000000000..952eb9875 --- /dev/null +++ b/drivers/sbus/char/vfc_i2c.c @@ -0,0 +1,319 @@ +/* + * drivers/sbus/char/vfc_i2c.c + * + * Driver for the Videopix Frame Grabber. + * + * Functions that support the Phillips i2c(I squared C) bus on the vfc + * Documentation for the Phillips I2C bus can be found on the + * phillips home page + * + * Copyright (C) 1996 Manish Vachharajani (mvachhar@noc.rutgers.edu) + * + */ + +/* NOTE: It seems to me that the documentation regarding the +pcd8584t/pcf8584 does not show the correct way to address the i2c bus. +Based on the information on the I2C bus itself and the remainder of +the Phillips docs the following algorithims apper to be correct. I am +fairly certain that the flowcharts in the phillips docs are wrong. */ + + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/malloc.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/delay.h> +#include <asm/openprom.h> +#include <asm/oplib.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/sbus.h> + +#if 0 +#define VFC_DEBUG +#endif + +#include "vfc.h" +#include "vfc_i2c.h" + +#define VFC_I2C_READ (0x1) +#define VFC_I2C_WRITE (0x0) + +/****** + The i2c bus controller chip on the VFC is a pcd8584t, but + phillips claims it doesn't exist. As far as I can tell it is + identical to the PCF8584 so I treat it like it is the pcf8584. + + NOTE: The pcf8584 only cares + about the msb of the word you feed it +*****/ + +int vfc_pcf8584_init(struct vfc_dev *dev) +{ + dev->regs->i2c_s1=RESET; /* This will also choose + register S0_OWN so we can set it*/ + + dev->regs->i2c_reg=0x55000000; /* the pcf8584 shifts this + value left one bit and uses + it as its i2c bus address */ + dev->regs->i2c_s1=SELECT(S2); + dev->regs->i2c_reg=0x14000000; /* this will set the i2c bus at + the same speed sun uses, + and set another magic bit */ + + dev->regs->i2c_s1=CLEAR_I2C_BUS; /* enable the serial port, + idle the i2c bus and set + the data reg to s0 */ + udelay(100); + return 0; +} + +void vfc_i2c_delay_wakeup(struct vfc_dev *dev) +{ + wake_up(&dev->poll_wait); +} + +void vfc_i2c_delay_no_busy(struct vfc_dev *dev,unsigned long usecs) +{ + dev->poll_timer.next = NULL; + dev->poll_timer.prev = NULL; + dev->poll_timer.expires = jiffies + + ((unsigned long)usecs*(HZ))/1000000; + dev->poll_timer.data=(unsigned long)dev; + dev->poll_timer.function=(void *)(unsigned long)vfc_i2c_delay_wakeup; + add_timer(&dev->poll_timer); + sleep_on(&dev->poll_wait); + del_timer(&dev->poll_timer); +} + +void inline vfc_i2c_delay(struct vfc_dev *dev) +{ + vfc_i2c_delay_no_busy(dev,100); +} + +int vfc_init_i2c_bus(struct vfc_dev *dev) +{ + dev->regs->i2c_s1= ENABLE_SERIAL | ACK; + vfc_i2c_reset_bus(dev); + return 0; +} + +int vfc_i2c_reset_bus(struct vfc_dev *dev) +{ + VFC_DEBUG_PRINTK((KERN_DEBUG "vfc%d: Resetting the i2c bus\n", + dev->instance)); + if(!dev) return -EINVAL; + if(!dev->regs) return -EINVAL; + dev->regs->i2c_s1=SEND_I2C_STOP; + dev->regs->i2c_s1=SEND_I2C_STOP | ACK; + vfc_i2c_delay(dev); + dev->regs->i2c_s1=CLEAR_I2C_BUS; + VFC_DEBUG_PRINTK((KERN_DEBUG "vfc%d: I2C status %x\n", + dev->instance, dev->regs->i2c_s1)); + return 0; +} + +int vfc_i2c_wait_for_bus(struct vfc_dev *dev) +{ + int timeout=1000; + + while(!(dev->regs->i2c_s1 & BB)) { + if(!(timeout--)) return -ETIMEDOUT; + vfc_i2c_delay(dev); + } + return 0; +} + +int vfc_i2c_wait_for_pin(struct vfc_dev *dev, int ack) +{ + int timeout=1000; + int s1; + + while((s1=dev->regs->i2c_s1) & PIN) { + if(!(timeout--)) return -ETIMEDOUT; + vfc_i2c_delay(dev); + } + if(ack==VFC_I2C_ACK_CHECK) { + if(s1 & LRB) return -EIO; + } + return 0; +} + +#define SHIFT(a) ((a) << 24) +int vfc_i2c_xmit_addr(struct vfc_dev *dev, unsigned char addr, char mode) +{ + int ret,raddr; +#if 1 + dev->regs->i2c_s1=SEND_I2C_STOP; + dev->regs->i2c_s1=SELECT(S0) | ENABLE_SERIAL; + vfc_i2c_delay(dev); +#endif + + switch(mode) { + case VFC_I2C_READ: + dev->regs->i2c_reg=raddr=SHIFT((unsigned int)addr | 0x1); + VFC_DEBUG_PRINTK(("vfc%d: recieving from i2c addr 0x%x\n", + dev->instance,addr | 0x1)); + break; + case VFC_I2C_WRITE: + dev->regs->i2c_reg=raddr=SHIFT((unsigned int)addr & ~0x1); + VFC_DEBUG_PRINTK(("vfc%d: sending to i2c addr 0x%x\n", + dev->instance,addr & ~0x1)); + break; + default: + return -EINVAL; + } + dev->regs->i2c_s1 = SEND_I2C_START; + vfc_i2c_delay(dev); + ret=vfc_i2c_wait_for_pin(dev,VFC_I2C_ACK_CHECK); /* We wait + for the + i2c send + to finish + here but + Sun + doesn't, + hmm */ + if(ret) { + printk(KERN_ERR "vfc%d: VFC xmit addr timed out or no ack\n", + dev->instance); + return ret; + } else if(mode == VFC_I2C_READ) { + if((ret=dev->regs->i2c_reg & 0xff000000) != raddr) { + printk(KERN_WARNING + "vfc%d: returned slave address " + "mismatch(%x,%x)\n", + dev->instance,raddr,ret); + } + } + return 0; +} + +int vfc_i2c_xmit_byte(struct vfc_dev *dev,unsigned char *byte) +{ + int ret; + dev->regs->i2c_reg=SHIFT((unsigned int)*byte); + + ret=vfc_i2c_wait_for_pin(dev,VFC_I2C_ACK_CHECK); + switch(ret) { + case -ETIMEDOUT: + printk(KERN_ERR "vfc%d: VFC xmit byte timed out or no ack\n", + dev->instance); + break; + case -EIO: + ret=XMIT_LAST_BYTE; + break; + default: + break; + } + return ret; +} + +int vfc_i2c_recv_byte(struct vfc_dev *dev, unsigned char *byte, int last) +{ + int ret; + if(last) { + dev->regs->i2c_reg=NEGATIVE_ACK; + VFC_DEBUG_PRINTK((KERN_DEBUG "vfc%d: sending negative ack\n", + dev->instance)); + } else { + dev->regs->i2c_s1=ACK; + } + + ret=vfc_i2c_wait_for_pin(dev,VFC_I2C_NO_ACK_CHECK); + if(ret) { + printk(KERN_ERR "vfc%d: " + "VFC recv byte timed out\n",dev->instance); + } + *byte=(dev->regs->i2c_reg) >> 24; + return ret; +} + +int vfc_i2c_recvbuf(struct vfc_dev *dev, unsigned char addr, + char *buf, int count) +{ + int ret,last; + + if(!(count && buf && dev && dev->regs) ) return -EINVAL; + + if((ret=vfc_i2c_wait_for_bus(dev))) { + printk(KERN_ERR "vfc%d: VFC I2C bus busy\n",dev->instance); + return ret; + } + + if((ret=vfc_i2c_xmit_addr(dev,addr,VFC_I2C_READ))) { + dev->regs->i2c_s1=SEND_I2C_STOP; + vfc_i2c_delay(dev); + return ret; + } + + last=0; + while(count--) { + if(!count) last=1; + if((ret=vfc_i2c_recv_byte(dev,buf,last))) { + printk(KERN_ERR "vfc%d: " + "VFC error while recieving byte\n", + dev->instance); + } + buf++; + } + + dev->regs->i2c_s1=SEND_I2C_STOP | ACK; + vfc_i2c_delay(dev); + return ret; +} + +int vfc_i2c_sendbuf(struct vfc_dev *dev, unsigned char addr, + char *buf, int count) +{ + int ret; + + if(!(buf && dev && dev->regs) ) return -EINVAL; + + if((ret=vfc_i2c_wait_for_bus(dev))) { + printk(KERN_ERR "vfc%d: VFC I2C bus busy\n",dev->instance); + return ret; + } + + if((ret=vfc_i2c_xmit_addr(dev,addr,VFC_I2C_WRITE))) { + dev->regs->i2c_s1=SEND_I2C_STOP; + vfc_i2c_delay(dev); + return ret; + } + + while(count--) { + ret=vfc_i2c_xmit_byte(dev,buf); + switch(ret) { + case XMIT_LAST_BYTE: + VFC_DEBUG_PRINTK(("vfc%d: " + "Reciever ended transmission with " + " %d bytes remaining\n", + dev->instance,count)); + ret=0; + goto done; + break; + case 0: + break; + default: + printk(KERN_ERR "vfc%d: " + "VFC error while sending byte\n",dev->instance); + break; + } + buf++; + } +done: + dev->regs->i2c_s1=SEND_I2C_STOP | ACK; + + vfc_i2c_delay(dev); + return ret; +} + + + + + + + + + diff --git a/drivers/sbus/char/vfc_i2c.h b/drivers/sbus/char/vfc_i2c.h new file mode 100644 index 000000000..a2e697320 --- /dev/null +++ b/drivers/sbus/char/vfc_i2c.h @@ -0,0 +1,44 @@ +#ifndef _LINUX_VFC_I2C_H_ +#define _LINUX_VFC_I2C_H_ + +/* control bits */ +#define PIN (0x80000000) +#define ESO (0x40000000) +#define ES1 (0x20000000) +#define ES2 (0x10000000) +#define ENI (0x08000000) +#define STA (0x04000000) +#define STO (0x02000000) +#define ACK (0x01000000) + +/* status bits */ +#define STS (0x20000000) +#define BER (0x10000000) +#define LRB (0x08000000) +#define AAS (0x04000000) +#define LAB (0x02000000) +#define BB (0x01000000) + +#define SEND_I2C_START (PIN | ESO | STA) +#define SEND_I2C_STOP (PIN | ESO | STO) +#define CLEAR_I2C_BUS (PIN | ESO | ACK) +#define NEGATIVE_ACK ((ESO) & ~ACK) + +#define SELECT(a) (a) +#define S0 (PIN | ESO | ES1) +#define S0_OWN (PIN) +#define S2 (PIN | ES1) +#define S3 (PIN | ES2) + +#define ENABLE_SERIAL (PIN | ESO) +#define DISABLE_SERIAL (PIN) +#define RESET (PIN) + +#define XMIT_LAST_BYTE (1) +#define VFC_I2C_ACK_CHECK (1) +#define VFC_I2C_NO_ACK_CHECK (0) + +#endif /* _LINUX_VFC_I2C_H_ */ + + + diff --git a/drivers/sbus/char/weitek.c b/drivers/sbus/char/weitek.c new file mode 100644 index 000000000..0fa0cb5fc --- /dev/null +++ b/drivers/sbus/char/weitek.c @@ -0,0 +1,116 @@ +/* $Id: weitek.c,v 1.9 1997/04/14 17:04:57 jj Exp $ + * weitek.c: Tadpole P9100/P9000 console driver + * + * Copyright (C) 1996 David Redman (djhr@tadpole.co.uk) + */ + +#include <linux/kd.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/proc_fs.h> + +#include <asm/openprom.h> +#include <asm/sbus.h> +#include <asm/io.h> +#include <asm/fbio.h> +#include <asm/pgtable.h> + +#include "../../char/vt_kern.h" +#include "../../char/selection.h" +#include "../../char/console_struct.h" +#include "fb.h" +#include "cg_common.h" + +/* + * mmap info + */ +#define WEITEK_VRAM_OFFSET 0 +#define WEITEK_VRAM_SIZE (2*1024*1024) /* maximum */ +#define WEITEK_GX_REG_OFFSET WEITEK_VRAM_SIZE +#define WEITEK_GX_REG_SIZE 8192 +#define WEITEK_VID_REG_OFFSET (WEITEK_GX_REG_OFFSET+WEITEK_GX_REG_SIZE) +#define WEITEK_VID_REG_SIZE 0x1000 + +#define CONTROL_OFFSET 0 +#define RAMDAC_OFFSET (CONTROL_OFFSET+0x200) + +#if 0 +static int +weitek_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma, + long base, fbinfo_t *fb) +{ + unsigned int size, page, r, map_size; + unsigned int map_offset = 0; + + size = vma->vm_end - vma->vm_start; + if (vma->vm_offset & ~PAGE_MASK) + return -ENXIO; + + /* To stop the swapper from even considering these pages */ + vma->vm_flags |= FB_MMAP_VM_FLAGS; + + /* Each page, see which map applies */ + for (page = 0; page < size; ){ + switch (vma->vm_offset+page){ + case WEITEK_VRAM_OFFSET: + map_size = size-page; + map_offset = get_phys ((unsigned long) fb->base); + if (map_size > fb->type.fb_size) + map_size = fb->type.fb_size; + break; + case WEITEK_GX_REG_OFFSET: + map_size = size-page; + map_offset = get_phys ((unsigned long) fb->base); + if (map_size > fb->type.fb_size) + map_size = fb->type.fb_size; + break; + default: + map_size = 0; + break; + } + if (!map_size){ + page += PAGE_SIZE; + continue; + } + if (page + map_size > size) + map_size = size - page; + r = io_remap_page_range (vma->vm_start+page, + map_offset, + map_size, vma->vm_page_prot, + fb->space); + if (r) return -EAGAIN; + page += map_size; + } + vma->vm_inode = inode; + inode->i_count++; + return 0; +} +#endif + +#if 0 +static void +weitek_loadcmap (void *fbinfo, int index, int count) +{ + printk("weitek_cmap: unimplemented!\n"); +} +#endif + +__initfunc(void weitek_setup(fbinfo_t *fb, int slot, u32 addr, int io)) +{ + extern struct screen_info screen_info; + + printk ("weitek%d at 0x%8.8x\n", slot, addr); + + /* Fill in parameters we left out */ + fb->type.fb_type = FBTYPE_NOTSUN1; + fb->type.fb_cmsize = 256; + fb->mmap = 0; /* weitek_mmap; */ + fb->loadcmap = 0; /* unimplemented */ + fb->ioctl = 0; /* no special ioctls */ + fb->reset = 0; /* no special reset */ + + /* Map the card registers */ + if (!fb->base){ + prom_printf ("Missing mapping routine and no address found\n"); + } +} |