2711 lines
81 KiB
Diff
2711 lines
81 KiB
Diff
diff -urN linux-2.4.0-official/arch/i386/Linuxbios.in linux-2.4.0-linuxbios/arch/i386/Linuxbios.in
|
|
--- linux-2.4.0-official/arch/i386/Linuxbios.in Thu Jan 1 08:00:00 1970
|
|
+++ linux-2.4.0-linuxbios/arch/i386/Linuxbios.in Mon Jan 15 14:43:36 2001
|
|
@@ -0,0 +1,7 @@
|
|
+mainmenu_option next_comment
|
|
+comment 'Linuxbios Options'
|
|
+bool 'Force IDE Controllers on?' CONFIG_LINUXBIOS_FORCE_IDE_CONTROLLER_ON
|
|
+bool 'Wait for HDA to spin up?' CONFIG_LINUXBIOS_WAIT_HDA_SPINUP
|
|
+bool 'Do not touch debug registers?' CONFIG_LINUXBIOS_DONT_TOUCH_DR
|
|
+bool 'LOBOS (Linux Os Boots OS)?' CONFIG_LINUXBIOS_LOBOS
|
|
+endmenu
|
|
diff -urN linux-2.4.0-official/arch/i386/config.in linux-2.4.0-linuxbios/arch/i386/config.in
|
|
--- linux-2.4.0-official/arch/i386/config.in Sat Dec 30 06:35:47 2000
|
|
+++ linux-2.4.0-linuxbios/arch/i386/config.in Mon Jan 15 14:43:36 2001
|
|
@@ -375,3 +375,13 @@
|
|
#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
|
|
bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
|
|
endmenu
|
|
+
|
|
+mainmenu_option next_comment
|
|
+comment 'LinuxBIOS'
|
|
+
|
|
+tristate 'LinuxBIOS Support' CONFIG_LINUXBIOS
|
|
+if [ "$CONFIG_LINUXBIOS" != "n" ]; then
|
|
+ source arch/i386/Linuxbios.in
|
|
+fi
|
|
+
|
|
+endmenu
|
|
diff -urN linux-2.4.0-official/arch/i386/kernel/pci-irq.c linux-2.4.0-linuxbios/arch/i386/kernel/pci-irq.c
|
|
--- linux-2.4.0-official/arch/i386/kernel/pci-irq.c Thu Jan 4 12:45:26 2001
|
|
+++ linux-2.4.0-linuxbios/arch/i386/kernel/pci-irq.c Mon Jan 15 14:43:36 2001
|
|
@@ -237,20 +237,27 @@
|
|
static int pirq_sis_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
|
|
{
|
|
u8 x;
|
|
- int reg = 0x41 + (pirq - 'A') ;
|
|
|
|
- pci_read_config_byte(router, reg, &x);
|
|
+ /* pirq == 0x61 means internal PCI IDE controller */
|
|
+ if (pirq == 0x61)
|
|
+ return 0;
|
|
+
|
|
+ /* pirq == IRQ Pin == Reg in Northbirdge */
|
|
+ pci_read_config_byte(router, pirq, &x);
|
|
return (x & 0x80) ? 0 : (x & 0x0f);
|
|
}
|
|
|
|
static int pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
|
|
{
|
|
u8 x;
|
|
- int reg = 0x41 + (pirq - 'A') ;
|
|
|
|
- pci_read_config_byte(router, reg, &x);
|
|
- x = (pirq & 0x20) ? 0 : (irq & 0x0f);
|
|
- pci_write_config_byte(router, reg, x);
|
|
+ /* pirq == 0x61 means internal PCI IDE controller */
|
|
+ if (pirq == 0x61)
|
|
+ return 0;
|
|
+
|
|
+ /* pirq == IRQ Pin == Reg in Northbirdge */
|
|
+ x = irq & 0x0f;
|
|
+ pci_write_config_byte(router, pirq, x);
|
|
|
|
return 1;
|
|
}
|
|
@@ -597,7 +604,11 @@
|
|
* Still no IRQ? Try to lookup one...
|
|
*/
|
|
if (pin && !dev->irq)
|
|
- pcibios_lookup_irq(dev, 0);
|
|
+#ifdef CONFIG_LINUXBIOS
|
|
+ pcibios_lookup_irq(dev, 1);
|
|
+#else
|
|
+ pcibios_lookup_irq(dev, 0);
|
|
+#endif
|
|
}
|
|
}
|
|
|
|
diff -urN linux-2.4.0-official/arch/i386/kernel/process.c linux-2.4.0-linuxbios/arch/i386/kernel/process.c
|
|
--- linux-2.4.0-official/arch/i386/kernel/process.c Fri Jan 5 04:50:17 2001
|
|
+++ linux-2.4.0-linuxbios/arch/i386/kernel/process.c Mon Jan 15 14:43:36 2001
|
|
@@ -49,6 +49,131 @@
|
|
|
|
#include <linux/irq.h>
|
|
|
|
+#define CONFIG_LINUXBIOS_PM
|
|
+#ifdef CONFIG_LINUXBIOS_PM
|
|
+#include <linux/pci.h>
|
|
+void
|
|
+sis503_reset(struct pci_dev *dev)
|
|
+{
|
|
+ unsigned char b;
|
|
+ unsigned short acpi_base;
|
|
+
|
|
+ printk(KERN_ERR __FUNCTION__ ": starting reset operation. \n");
|
|
+
|
|
+ /* Enable ACPI by set B7 on Reg 0x40, LPC */
|
|
+ pci_read_config_byte(dev, 0x40, &b);
|
|
+ pci_write_config_byte(dev, 0x40, b | 0x80);
|
|
+ printk(KERN_ERR __FUNCTION__ ": enabled ACPI. \n");
|
|
+
|
|
+ /* get the ACPI base address for register 0x74,0x75 of LPC */
|
|
+ pci_read_config_word(dev, 0x74, &acpi_base);
|
|
+ printk(KERN_ERR __FUNCTION__ ":acpi base: %x\n", acpi_base);
|
|
+
|
|
+ /* Set software watchdog timer init value */
|
|
+ outb(0x03, 0x4a + acpi_base);
|
|
+ printk(KERN_ERR __FUNCTION__ ": set the dog. \n");
|
|
+
|
|
+ printk(KERN_ERR __FUNCTION__ ": enabling dog. \n");
|
|
+ /* Software watchdog enable, issue PCIRST# when time expire */
|
|
+ outb(0x8f, 0x4b + acpi_base);
|
|
+
|
|
+ printk(KERN_ERR __FUNCTION__ ": We should reset soon. \n");
|
|
+}
|
|
+
|
|
+void
|
|
+sis503_off(struct pci_dev *dev)
|
|
+{
|
|
+ unsigned char b;
|
|
+ unsigned short acpi_base;
|
|
+
|
|
+ printk(KERN_ERR __FUNCTION__ ": starting reset operation. \n");
|
|
+ /* Enable ACPI by set B7 on Reg 0x40, LPC */
|
|
+ pci_read_config_byte(dev, 0x40, &b);
|
|
+ pci_write_config_byte(dev, 0x40, b | 0x80);
|
|
+ printk(KERN_ERR __FUNCTION__ ": enabled ACPI. \n");
|
|
+
|
|
+ /* get the ACPI base address for register 0x74,0x75 of LPC */
|
|
+ pci_read_config_word(dev, 0x74, &acpi_base);
|
|
+ printk (KERN_ERR __FUNCTION__ ":acpi base: %x\n", acpi_base);
|
|
+
|
|
+ /* ACPI Register 5, Bit 10-12, Sleeping Type,
|
|
+ set to 101 -> S5, soft_off */
|
|
+ outb(0x14, 0x05 + acpi_base);
|
|
+ printk(KERN_ERR __FUNCTION__ ": DONE setting sleep type. \n");
|
|
+
|
|
+ /* ACPI Register 5, Bit 13, Sleep Enable */
|
|
+ outb(0x20 | 0x14, 0x05 + acpi_base);
|
|
+ printk(KERN_ERR __FUNCTION__ ": DONE sleep enable. \n");
|
|
+}
|
|
+
|
|
+struct pci_dev * pci_find_device(unsigned int vendor, unsigned int device,
|
|
+ const struct pci_dev *from);
|
|
+
|
|
+struct linuxbios_control {
|
|
+ u_short vendor, device;
|
|
+ void (*poweroff)(struct pci_dev *);
|
|
+ void (*reset)(struct pci_dev *);
|
|
+};
|
|
+
|
|
+struct linuxbios_control controls[] = {
|
|
+ {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, sis503_off, sis503_reset}
|
|
+};
|
|
+
|
|
+struct linuxbios_control *findcontrol(struct pci_dev **d)
|
|
+{
|
|
+ struct linuxbios_control *lb = controls, *retval = 0;
|
|
+ int i;
|
|
+
|
|
+ printk(KERN_ERR __FUNCTION__ ": Find vendor 0x%x device 0x%x\n",
|
|
+ lb->vendor, lb->device);
|
|
+ for(lb = controls, i = 0;
|
|
+ (i < sizeof(controls)/sizeof(controls[0])) && (! retval);
|
|
+ i++, lb++)
|
|
+ {
|
|
+ *d = pci_find_device(lb->vendor, lb->device, 0);
|
|
+ if (*d)
|
|
+ retval = lb;
|
|
+ }
|
|
+
|
|
+ printk(KERN_ERR __FUNCTION__ ": result of find is %p\n", retval);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+void
|
|
+linuxbios_poweroff(void)
|
|
+{
|
|
+ struct linuxbios_control *lb = 0;
|
|
+ struct pci_dev *dev;
|
|
+
|
|
+ printk(KERN_ERR __FUNCTION__ ": find an lb\n");
|
|
+ lb = findcontrol(&dev);
|
|
+
|
|
+ printk(KERN_ERR __FUNCTION__ ": found lb %p, call %p\n",
|
|
+ lb, lb ? lb->poweroff : 0);
|
|
+ if (lb && (lb->poweroff))
|
|
+ lb->poweroff(dev);
|
|
+ printk(KERN_ERR __FUNCTION__ ": Returning? Can't happen, I thought?\n");
|
|
+}
|
|
+
|
|
+void
|
|
+linuxbios_reset(void)
|
|
+{
|
|
+ struct linuxbios_control *lb = 0;
|
|
+ struct pci_dev *dev;
|
|
+
|
|
+ printk(KERN_ERR __FUNCTION__ ": find an lb\n");
|
|
+ lb = findcontrol(&dev);
|
|
+
|
|
+ printk(KERN_ERR __FUNCTION__ ": found lb %p, call %p\n",
|
|
+ lb, lb ? lb->reset : 0);
|
|
+ if (lb && (lb->reset))
|
|
+ lb->reset(dev);
|
|
+ printk(KERN_ERR __FUNCTION__ ": Returning? Can't happen, I thought?\n");
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
|
|
|
|
int hlt_counter;
|
|
@@ -353,6 +478,9 @@
|
|
smp_send_stop();
|
|
disable_IO_APIC();
|
|
#endif
|
|
+#ifdef CONFIG_LINUXBIOS_PM
|
|
+ linuxbios_reset();
|
|
+#endif
|
|
|
|
if(!reboot_thru_bios) {
|
|
/* rebooting needs to touch the page at absolute addr 0 */
|
|
@@ -380,6 +508,11 @@
|
|
|
|
void machine_power_off(void)
|
|
{
|
|
+ printk("MACHINE_POWER_OFF\n");
|
|
+#ifdef CONFIG_LINUXBIOS_PM
|
|
+ linuxbios_poweroff();
|
|
+#endif
|
|
+
|
|
if (pm_power_off)
|
|
pm_power_off();
|
|
}
|
|
diff -urN linux-2.4.0-official/drivers/ide/ide-pci.c linux-2.4.0-linuxbios/drivers/ide/ide-pci.c
|
|
--- linux-2.4.0-official/drivers/ide/ide-pci.c Wed Jan 3 08:58:45 2001
|
|
+++ linux-2.4.0-linuxbios/drivers/ide/ide-pci.c Mon Jan 15 14:43:36 2001
|
|
@@ -603,8 +603,19 @@
|
|
for (port = 0; port <= 1; ++port) {
|
|
unsigned long base = 0, ctl = 0;
|
|
ide_pci_enablebit_t *e = &(d->enablebits[port]);
|
|
+#ifndef CONFIG_LINUXBIOS_FORCE_IDE_CONTROLLER_ON
|
|
if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) || (tmp & e->mask) != e->val))
|
|
continue; /* port not enabled */
|
|
+#else
|
|
+ /* force 'em on! */
|
|
+ if (e->reg) {
|
|
+ pci_read_config_byte(dev, e->reg, &tmp);
|
|
+ tmp |= e->val;
|
|
+ pci_write_config_byte(dev, e->reg, tmp);
|
|
+ printk("%s: LINUXBIOS, so Jammed the enable on!\n",
|
|
+ d->name);
|
|
+ }
|
|
+#endif
|
|
if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366) && (port) && (class_rev < 0x03))
|
|
return;
|
|
if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE || (dev->class & (port ? 4 : 1)) != 0) {
|
|
diff -urN linux-2.4.0-official/drivers/ide/ide-probe.c linux-2.4.0-linuxbios/drivers/ide/ide-probe.c
|
|
--- linux-2.4.0-official/drivers/ide/ide-probe.c Fri Oct 27 14:35:48 2000
|
|
+++ linux-2.4.0-linuxbios/drivers/ide/ide-probe.c Mon Jan 15 14:43:36 2001
|
|
@@ -308,6 +308,13 @@
|
|
if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
|
|
return 4;
|
|
}
|
|
+#ifdef CONFIG_LINUXBIOS_WAIT_HDA_SPINUP
|
|
+ if (! strncmp(drive->name, "hda", 3)) {
|
|
+ printk("jamming drive present for %s\n", drive->name);
|
|
+ drive->present = 1;
|
|
+ }
|
|
+#endif
|
|
+
|
|
#ifdef DEBUG
|
|
printk("probing for %s: present=%d, media=%d, probetype=%s\n",
|
|
drive->name, drive->present, drive->media,
|
|
diff -urN linux-2.4.0-official/drivers/mtd/docprobe.c linux-2.4.0-linuxbios/drivers/mtd/docprobe.c
|
|
--- linux-2.4.0-official/drivers/mtd/docprobe.c Sat Dec 30 06:07:22 2000
|
|
+++ linux-2.4.0-linuxbios/drivers/mtd/docprobe.c Mon Jan 15 14:43:57 2001
|
|
@@ -39,7 +39,7 @@
|
|
a Millennium enough that they go through and work out what the
|
|
difference is :)
|
|
*/
|
|
-#define DOC_SINGLE_DRIVER
|
|
+#undef DOC_SINGLE_DRIVER
|
|
|
|
#include <linux/config.h>
|
|
#include <linux/kernel.h>
|
|
diff -urN linux-2.4.0-official/drivers/video/sis/Makefile linux-2.4.0-linuxbios/drivers/video/sis/Makefile
|
|
--- linux-2.4.0-official/drivers/video/sis/Makefile Sat Dec 30 06:07:23 2000
|
|
+++ linux-2.4.0-linuxbios/drivers/video/sis/Makefile Mon Jan 15 15:01:29 2001
|
|
@@ -4,7 +4,7 @@
|
|
|
|
O_TARGET := sisfb.o
|
|
|
|
-obj-y := sis_main.o sis_300.o sis_301.o
|
|
+obj-y := sisfb_lite.o
|
|
obj-m := $(O_TARGET)
|
|
|
|
include $(TOPDIR)/Rules.make
|
|
diff -urN linux-2.4.0-official/drivers/video/sis/sisfb_lite.c linux-2.4.0-linuxbios/drivers/video/sis/sisfb_lite.c
|
|
--- linux-2.4.0-official/drivers/video/sis/sisfb_lite.c Thu Jan 1 08:00:00 1970
|
|
+++ linux-2.4.0-linuxbios/drivers/video/sis/sisfb_lite.c Thu Feb 1 14:57:11 2001
|
|
@@ -0,0 +1,2266 @@
|
|
+/* sisfb_lite.c: SiSFB Lite, Frame Buffer Device Driver for SiS 540/630/730 requires no VGA BIOS
|
|
+ *
|
|
+ * Copyright 2000 Ollie Lho <ollie@sis.com.tw>
|
|
+ *
|
|
+ * This driver is derived form an eraly version of sisfb.c which is:
|
|
+ * This driver is partly based on the VBE 2.0 compliant graphic
|
|
+ * boards framebuffer driver, which is
|
|
+ *
|
|
+ * (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
|
|
+ *
|
|
+ * This driver is implemented by the Author for his own personal interests and is NOT a
|
|
+ * commitment NOR supported officially by Silicon Integrated System Corp. Please direct
|
|
+ * any bug report/question to the Author and don't bother SiS Technical Support Personell.
|
|
+ *
|
|
+ *
|
|
+ * ToDo:
|
|
+ * 2. make sure we have to set_par when init or NOT !!!
|
|
+ * 4. MMAP
|
|
+ * 5. 2D Acceleration
|
|
+ */
|
|
+#include <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/tty.h>
|
|
+#include <linux/malloc.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/fb.h>
|
|
+#include <linux/console.h>
|
|
+#include <linux/selection.h>
|
|
+#include <linux/ioport.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/pci.h>
|
|
+#include <linux/vt_kern.h>
|
|
+#include <linux/capability.h>
|
|
+
|
|
+#include <asm/io.h>
|
|
+#include <asm/mtrr.h>
|
|
+
|
|
+#include <video/fbcon.h>
|
|
+#include <video/fbcon-cfb8.h>
|
|
+#include <video/fbcon-cfb16.h>
|
|
+#include <video/fbcon-cfb24.h>
|
|
+#include <video/fbcon-cfb32.h>
|
|
+
|
|
+#include "sisfb_lite.h"
|
|
+
|
|
+static struct pci_device_id __devinitdata sisfb_pci_tbl [] = {
|
|
+ {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_630_VGA,
|
|
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SIS_GLAMOUR},
|
|
+ {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_540_VGA,
|
|
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SIS_GLAMOUR},
|
|
+ {0,}
|
|
+};
|
|
+MODULE_DEVICE_TABLE (pci, sisfb_pci_tbl);
|
|
+
|
|
+static char __initdata fontname[40] = { 0 };
|
|
+static const char * mode_option __initdata = NULL;
|
|
+
|
|
+static struct fb_var_screeninfo __initdata default_var = {
|
|
+ /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
|
|
+ xres: 640,
|
|
+ yres: 480,
|
|
+ xres_virtual: 640,
|
|
+ yres_virtual: 480,
|
|
+ xoffset: 0,
|
|
+ yoffset: 0,
|
|
+ bits_per_pixel: 8,
|
|
+ grayscale: 0,
|
|
+ red: {0, 8, 0},
|
|
+ green: {0, 8, 0},
|
|
+ blue: {0, 8, 0},
|
|
+ transp: {0, 0, 0},
|
|
+ nonstd: 0,
|
|
+ activate: FB_ACTIVATE_NOW,
|
|
+ height: -1,
|
|
+ width: -1,
|
|
+ accel_flags: 0,
|
|
+
|
|
+ /* timming, virtually the same as sisfb_default_mode */
|
|
+ pixclock: 39722,
|
|
+ left_margin: 48,
|
|
+ right_margin: 16,
|
|
+ upper_margin: 33,
|
|
+ lower_margin: 10,
|
|
+ hsync_len: 96,
|
|
+ vsync_len: 2,
|
|
+ sync: 0,
|
|
+ vmode: FB_VMODE_NONINTERLACED
|
|
+};
|
|
+
|
|
+static struct fb_videomode __initdata sisfb_default_mode = {
|
|
+ name: "640x480-8@60",
|
|
+ refresh: 60,
|
|
+ xres: 640,
|
|
+ yres: 480,
|
|
+ pixclock: 39722,
|
|
+ left_margin: 48,
|
|
+ right_margin: 16,
|
|
+ upper_margin: 33,
|
|
+ lower_margin: 10,
|
|
+ hsync_len: 96,
|
|
+ vsync_len: 2,
|
|
+ sync: 0,
|
|
+ vmode: FB_VMODE_NONINTERLACED
|
|
+};
|
|
+
|
|
+static u8 __initdata default_vga_palette[] = {
|
|
+ /* Palette 0-15, original EGA 16 colors stored as 00bbggrr where
|
|
+ * bbggrr are 2 bits index to the color values {0, 42, 21, 63} */
|
|
+ 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
|
|
+ 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
|
|
+
|
|
+ /* Palette 16-31, 16 evenly spaced shades of gray */
|
|
+ 0x00, 0x05, 0x08, 0x0B, 0x0E, 0x11, 0x14, 0x18,
|
|
+ 0x1C, 0x20, 0x24, 0x28, 0x2D, 0x32, 0x38, 0x3F,
|
|
+
|
|
+ /* Palette 32-247, 24 colors group generated form 5 elements,
|
|
+ * each group has 3 intensity and 3 saturation levels */
|
|
+ 0x00, 0x10, 0x1F, 0x2F, 0x3F,
|
|
+ 0x1F, 0x27, 0x2F, 0x37, 0x3F,
|
|
+ 0x2D, 0x31, 0x36, 0x3A, 0x3F,
|
|
+ 0x00, 0x07, 0x0E, 0x15, 0x1C,
|
|
+ 0x0E, 0x11, 0x15, 0x18, 0x1C,
|
|
+ 0x14, 0x16, 0x18, 0x1A, 0x1C,
|
|
+ 0x00, 0x04, 0x08, 0x0C, 0x10,
|
|
+ 0x08, 0x0A, 0x0C, 0x0E, 0x10,
|
|
+ 0x0B, 0x0C, 0x0D, 0x0F, 0x10
|
|
+};
|
|
+
|
|
+/* Default values for PCI/AGP Timming control registers: SR21-SR25, SR32 */
|
|
+static u8 __initdata pci_timming[] = {
|
|
+ 0xB6, 0xBA, 0xF6, 0x0D, 0x00, 0x11
|
|
+};
|
|
+
|
|
+/* The Programmable Clock Generator:
|
|
+ *
|
|
+ * The programmable clock generator is based on Phase Lock Loop (PLL) technology
|
|
+ * which requires only one external clock source to generate clock signals with
|
|
+ * various frequences. Conceptially, the PLL is just like an OP Amp and is consisted
|
|
+ * of a Phase Detector, a Voltage Controlled Oscillator and some frequence dividers.
|
|
+ * The clock source Fref is feed to the frequence divider /DeNumerator, then passed
|
|
+ * to the Phase Detector (PD). The output of the Phase Detector is used to drive the
|
|
+ * Voltage Controlled Oscillator (VCO). The clock signal output of the VCO, called Fvco,
|
|
+ * is then feedback to the Pahse Detector after divided by /Divider and /Numerator.
|
|
+ * When the PLL is in a stationary state, the 2 inputs of the Phase Detector is the
|
|
+ * same. Thus we have equation (1).
|
|
+ *
|
|
+ * The VCO can only generate a certain range of Fvco, for most case 135 MHz < Fvco < 250 MHz.
|
|
+ * If we would like to generate clock frequence Fout that is lower then the lower
|
|
+ * bound of Fvco, another frequnce divider called Post Scalar is needed. Then we
|
|
+ * have equation (2). By solving equatiuon (1) and (2) we can find the relationship
|
|
+ * between clock source Fref and generated clock Fout as in equation (3).
|
|
+ *
|
|
+ * Note:
|
|
+ * 1. From dumping the VGA BIOS data, it seems that the VCO in SiS 630 can generate
|
|
+ * frequence in the range of 90 MHZ - 400 MHz. But for the sake of safety, we just
|
|
+ * assume that 150 MHz < Fvco < 300 MHz in this implementation.
|
|
+ *
|
|
+ * 2. The /Divider is in Bit 7 of clock generator I, /Numerator is in Bit 0..6 of
|
|
+ * clock generator I and /DeNumerator is in Bit 0..4 of clock generator II. Their
|
|
+ * actual values are the values stored in the corresponding register plus 1.
|
|
+ *
|
|
+ * 3. The /Post Scalar is in Bit 5..7 of clock generator II. The actual meaning of
|
|
+ * the values in the register is not clear from the document. We assume that:
|
|
+ * 000 => 1, 001 => 2, 010 => 3, 011 => 4
|
|
+ * 110 => 6, 111 => 8, others are reserved.
|
|
+ *
|
|
+ * 4. The document (6326 Spec and Glamour Design Guideline) only states the VCO Gain,
|
|
+ * Bit 7 of clock generator register III should be set for "high frequence operation"
|
|
+ * but without specifying the definition of "high frequence". From XFree86 3.3.6,
|
|
+ * sisfb.c and BIOS dumping, they are always set.
|
|
+ *
|
|
+ * --------------
|
|
+ * -------| /Numerator |<------
|
|
+ * | -------------- |
|
|
+ * | ------------
|
|
+ * | | /Divider |
|
|
+ * | ------------
|
|
+ * | |
|
|
+ * ---------------- V ------ ------- | ----------------
|
|
+ * Fref ----> | /DeNumerator |---+--->| CP |----->| VCO |------->| /Post Scalar | ----> Fout
|
|
+ * ---------------- PD ------ ------- Fvco ----------------
|
|
+ * ^
|
|
+ * |
|
|
+ * Gain
|
|
+ *
|
|
+ * For
|
|
+ * Fref Fvco Fvco
|
|
+ * ----------- = ------------------- (1) and Fout = ----------- (2)
|
|
+ * DeNumerator Divider * Numerator Post Scalar
|
|
+ *
|
|
+ * We have
|
|
+ * Divider * Numerator
|
|
+ * Fout = ------------------------- Fref (14.318 MHz) (3)
|
|
+ * Denumerator * Post Scalar
|
|
+ *
|
|
+ * Reference:
|
|
+ * 1. SiS 6326 AGP/PCI Graphics & Video Acclerator. Rev 1.0, May 12, 1997
|
|
+ * 2. Graphics Hardware by Geert Uytterhoeven <geert@linux-m86k.org>
|
|
+ * 3. sis86c201.c in XFree86 3.3.6
|
|
+ * 4. Glamour Design Guideline V0.8
|
|
+ */
|
|
+
|
|
+/* Clock generator source, 14.318 MHz */
|
|
+#define CLOCK_SOURCE 14318
|
|
+/* Minimium Fvco 150 MHz */
|
|
+#define MIN_F_VCO 150000
|
|
+/* Maximium Fvco 300 MHz */
|
|
+#define MAX_F_VCO 300000
|
|
+
|
|
+struct sisfb_clock_param {
|
|
+ /* parameters for setting clock generator for SiS VGA core */
|
|
+ u8 divider;
|
|
+ u8 numerator;
|
|
+ u8 denumerator;
|
|
+ u8 post_scalar;
|
|
+ u8 vco_gain;
|
|
+};
|
|
+
|
|
+enum clock_gen_base {
|
|
+ SIS300_MCLK = 0x28,
|
|
+ SIS300_DCLK = 0x2B,
|
|
+ SIS300_ECLK = 0x2E
|
|
+};
|
|
+
|
|
+static int inverse = 0;
|
|
+
|
|
+/* Interface used by the world */
|
|
+int sisfb_setup(char *options);
|
|
+int sisfb_init(void);
|
|
+
|
|
+/* Internal routines */
|
|
+static void do_install_cmap(int con, struct fb_info *info);
|
|
+static void sisfb_set_dispsw(struct display * disp, struct sisfb_info * sisfb, int bpp, int accel);
|
|
+/* ---------------------------------------------------------------------------------- */
|
|
+static void sisfb_lock_regs(struct sisfb_info * sisfb);
|
|
+static void sisfb_unlock_regs(struct sisfb_info * sisfb);
|
|
+static void sisfb_display_on(struct sisfb_info * sisfb);
|
|
+static void sisfb_display_off(struct sisfb_info * sisfb);
|
|
+/* ---------------------------------------------------------------------------------- */
|
|
+static void sisfb_set_default_misc_reg(struct sisfb_info * sisfb);
|
|
+static void sisfb_set_default_seq_regs(struct sisfb_info * sisfb);
|
|
+static void sisfb_set_default_crtc_regs(struct sisfb_info * sisfb);
|
|
+static void sisfb_set_default_grc_regs(struct sisfb_info * sisfb);
|
|
+static void sisfb_set_default_attr_regs(struct sisfb_info * sisfb);
|
|
+static void sisfb_permute_dac_rgb(struct sisfb_info * sisfb, u8 * colors);
|
|
+static void sisfb_load_default_palette(struct sisfb_info * sisfb);
|
|
+static void sisfb_init_legacy_vga(struct sisfb_info * sisfb);
|
|
+/* ---------------------------------------------------------------------------------- */
|
|
+static unsigned long sisfb_calc_clock_freq(struct sisfb_clock_param * clock);
|
|
+static int sisfb_calc_clock_param(struct sisfb_clock_param * clock, unsigned long freq);
|
|
+static void sisfb_get_clock(struct sisfb_info * sisfb, struct sisfb_clock_param * clock, int which);
|
|
+static void sisfb_set_clock(struct sisfb_info * sisfb, struct sisfb_clock_param * clock, int which);
|
|
+/* ---------------------------------------------------------------------------------- */
|
|
+static int sisfb_get_memory_size(struct sisfb_info * sisfb);
|
|
+static void sisfb_set_memory_clocks(struct sisfb_info * sisfb);
|
|
+static int sisfb_config_memory(struct sisfb_info * sisfb);
|
|
+static void sisfb_fake_vgabios(struct sisfb_info * sisfb);
|
|
+static void sisfb_set_pci_agp_timming(struct sisfb_info * sisfb);
|
|
+static void sisfb_init_300(struct sisfb_info * sisfb);
|
|
+/* ---------------------------------------------------------------------------------- */
|
|
+static int sisfb_encode_fix(struct fb_fix_screeninfo * fix, struct sisfb_par * par,
|
|
+ struct sisfb_info * sisfb);
|
|
+static int sisfb_decode_var(struct fb_var_screeninfo * var, struct sisfb_par * par,
|
|
+ const struct sisfb_info * sisfb);
|
|
+static int sisfb_encode_var(struct fb_var_screeninfo * var, const struct sisfb_par * par,
|
|
+ const struct sisfb_info * sisfb);
|
|
+static void sisfb_set_par(struct sisfb_par *par, struct sisfb_info * sisfb);
|
|
+static int sisfb_getcolreg(unsigned regno, unsigned * red, unsigned * green, unsigned * blue,
|
|
+ unsigned * transp, struct fb_info * info);
|
|
+static int sisfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
|
|
+ unsigned transp, struct fb_info * info);
|
|
+/* ---------------------------------------------------------------------------------- */
|
|
+static int sisfb_switch_con(int con, struct fb_info * info);
|
|
+static void sisfb_blank(int blank, struct fb_info * info);
|
|
+/* ----------------------------------------- fb_ops --------------------------------- */
|
|
+static int sisfb_get_fix(struct fb_fix_screeninfo * fix, int con, struct fb_info * info);
|
|
+static int sisfb_get_var(struct fb_var_screeninfo * var, int con, struct fb_info * info);
|
|
+static int sisfb_set_var(struct fb_var_screeninfo * var, int con, struct fb_info * info);
|
|
+static int sisfb_get_cmap(struct fb_cmap * cmap, int kspc, int con, struct fb_info * info);
|
|
+static int sisfb_set_cmap(struct fb_cmap * cmap, int kspc, int con, struct fb_info * info);
|
|
+static int sisfb_ioctl(struct inode * inode, struct file * file, unsigned int cmd,
|
|
+ unsigned long arg, int con, struct fb_info * info);
|
|
+static int sisfb_mmap(struct fb_info * info, struct file * file, struct vm_area_struct * vma);
|
|
+
|
|
+static __inline__ void
|
|
+sisfb_set_gen_reg(struct sisfb_info * sisfb, u16 port, u8 data)
|
|
+{
|
|
+ u16 base = sisfb->vga_io_base;
|
|
+
|
|
+ outb(data, port + base - 0x3B0);
|
|
+}
|
|
+
|
|
+static __inline__ u8
|
|
+sisfb_get_gen_reg(struct sisfb_info * sisfb, u16 port)
|
|
+{
|
|
+ u16 base = sisfb->vga_io_base;
|
|
+
|
|
+ return inb(port + base - 0x3B0);
|
|
+}
|
|
+
|
|
+static __inline__ void
|
|
+sisfb_set_seq_reg(struct sisfb_info * sisfb, u8 index, u8 data)
|
|
+{
|
|
+ u16 port = sisfb->vga_io_base;
|
|
+
|
|
+ outb(index, port + SEQ_ADDR);
|
|
+ outb(data, port + SEQ_DATA);
|
|
+}
|
|
+
|
|
+static __inline__ u8
|
|
+sisfb_get_seq_reg(struct sisfb_info * sisfb, u8 index)
|
|
+{
|
|
+ u16 port = sisfb->vga_io_base;
|
|
+
|
|
+ outb(index, port + SEQ_ADDR);
|
|
+ return inb(port + SEQ_DATA);
|
|
+}
|
|
+
|
|
+static __inline__ void
|
|
+sisfb_set_crtc_reg(struct sisfb_info * sisfb, u8 index, u8 data)
|
|
+{
|
|
+ u16 port = sisfb->vga_io_base;
|
|
+
|
|
+ outb(index, port + CRTC_ADDR);
|
|
+ outb(data, port + CRTC_DATA);
|
|
+}
|
|
+
|
|
+static __inline__ u8
|
|
+sisfb_get_crtc_reg(struct sisfb_info * sisfb, u8 index)
|
|
+{
|
|
+ u16 port = sisfb->vga_io_base;
|
|
+
|
|
+ outb(index, port + CRTC_ADDR);
|
|
+ return inb(port + CRTC_DATA);
|
|
+}
|
|
+
|
|
+static __inline__ void
|
|
+sisfb_set_grc_reg(struct sisfb_info * sisfb, u8 index, u8 data)
|
|
+{
|
|
+ u16 port = sisfb->vga_io_base;
|
|
+
|
|
+ outb(index, port + GRC_ADDR);
|
|
+ outb(data, port + GRC_DATA);
|
|
+}
|
|
+
|
|
+static __inline__ u8
|
|
+sisfb_get_grc_reg(struct sisfb_info * sisfb, u8 index)
|
|
+{
|
|
+ u16 port = sisfb->vga_io_base;
|
|
+
|
|
+ outb(index, port + GRC_ADDR);
|
|
+ return inb(port + GRC_DATA);
|
|
+}
|
|
+
|
|
+static __inline__ void
|
|
+sisfb_set_attr_reg(struct sisfb_info * sisfb, u8 index, u8 data)
|
|
+{
|
|
+ u16 port = sisfb->vga_io_base;
|
|
+ volatile u8 dummy;
|
|
+
|
|
+ /* Read port 0x3DA, to set the Adderss flip-flop */
|
|
+ dummy = sisfb_get_gen_reg(sisfb, 0x3DA);
|
|
+ outb(index, port + ATTR_WRITE);
|
|
+ outb(data, port + ATTR_WRITE);
|
|
+}
|
|
+
|
|
+static __inline__ u8
|
|
+sisfb_get_attr_reg(struct sisfb_info * sisfb, u8 index)
|
|
+{
|
|
+ u16 port = sisfb->vga_io_base;
|
|
+ volatile u8 dummy;
|
|
+
|
|
+ /* Read port 0x3DA, to set the Adderss flip-flop */
|
|
+ dummy = sisfb_get_gen_reg(sisfb, 0x3DA);
|
|
+ outb(index, port + ATTR_READ);
|
|
+ return inb(port + ATTR_READ);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_set_dac_index: - Select DAC color register to be loaded
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ * @index: Index of the DAC color Register to be loaded
|
|
+ *
|
|
+ * Select the DAC color register to be loaded by programming the
|
|
+ * DAC Write Address Register.
|
|
+ */
|
|
+static __inline__ void
|
|
+sisfb_set_dac_index(struct sisfb_info * sisfb, u8 index)
|
|
+{
|
|
+ u16 port = sisfb->vga_io_base;
|
|
+
|
|
+ outb(index, port + DACW_ADDR);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_set_dac_rgb: - Load the DAC color register
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ * @red: Red component
|
|
+ * @green: Green component
|
|
+ * @blue: Blue compoenet
|
|
+ *
|
|
+ * Load the DAC color register with (@red, @green, @blue) by writing to the DAC Data
|
|
+ * Register. The color register to be loaded is implicitely selected by the DAC Write
|
|
+ * Address Register.
|
|
+ */
|
|
+static __inline__ void
|
|
+sisfb_set_dac_rgb(struct sisfb_info * sisfb, u8 red, u8 green, u8 blue)
|
|
+{
|
|
+ u16 port = sisfb->vga_io_base;
|
|
+
|
|
+ outb(red, port + DAC_DATA);
|
|
+ outb(green, port + DAC_DATA);
|
|
+ outb(blue, port + DAC_DATA);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_lock_regs: - Lock the CRTC Registers 0-7 and Extended Sequencer Registers
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Lock CRTC 0-7 by setting Bit 7 of CRTC 0x11.
|
|
+ * Lock Extended Registers by writing 0x21 (%SIS_PASSWD_LOCK) to
|
|
+ * SR 0x05 (%SIS_PASSWD)
|
|
+ */
|
|
+static void
|
|
+sisfb_lock_regs(struct sisfb_info * sisfb)
|
|
+{
|
|
+ u8 data;
|
|
+
|
|
+ /* Set Bit 7 to enable Write Protect for CR0 to CR7 */
|
|
+ data = sisfb_get_crtc_reg(sisfb, 0x11);
|
|
+ data = data | 0x80;
|
|
+ sisfb_set_crtc_reg(sisfb, 0x11, data);
|
|
+
|
|
+ /* lock the access to Extended Registers SR5-SR3C and/or CR30-CR37 ?? */
|
|
+ sisfb_set_seq_reg(sisfb, SIS_PASSWD, SIS_PASSWD_LOCK);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_unlock_regs: - Unlock the CRTC Registers 0-7 and Extended Sequencer Registers
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Unlock CRTC 0-7 by clearing Bit 7 of CRTC 0x11.
|
|
+ * Unlock Extended Registers by writing 0x86 (%SIS_PASSWD_UNLOCK) to
|
|
+ * SR 0x05 (%SIS_PASSWD)
|
|
+ */
|
|
+static void
|
|
+sisfb_unlock_regs(struct sisfb_info * sisfb)
|
|
+{
|
|
+ u8 data;
|
|
+
|
|
+ /* clear Bit 7 to disable Write Protect for CR0 to CR7 */
|
|
+ data = sisfb_get_crtc_reg(sisfb, 0x11);
|
|
+ data = data & 0x7f;
|
|
+ sisfb_set_crtc_reg(sisfb, 0x11, data);
|
|
+
|
|
+ /* unlock the access to Extended Registers SR5-SR3C and/or CR30-CR37 ?? */
|
|
+ sisfb_set_seq_reg(sisfb, SIS_PASSWD, SIS_PASSWD_UNLOCK);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_display_on: - Turn the monitor on.
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Turn on the monitor by clearing Bit 5 of Seqencer Register 0x01
|
|
+ */
|
|
+static void
|
|
+sisfb_display_on(struct sisfb_info * sisfb)
|
|
+{
|
|
+ u8 data;
|
|
+
|
|
+ data = sisfb_get_seq_reg(sisfb, 0x01);
|
|
+
|
|
+ /* clear SR1 Bit 5: Screen Off ==> Screen On */
|
|
+ data &= ~0x20;
|
|
+ sisfb_set_seq_reg(sisfb, 0x01, data);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_display_off: - Turn the monitor off.
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Turn off the monitor by settng Bit 5 of Seqencer Register 0x01
|
|
+ */
|
|
+static void
|
|
+sisfb_display_off(struct sisfb_info * sisfb)
|
|
+{
|
|
+ u8 data;
|
|
+
|
|
+ data = sisfb_get_seq_reg(sisfb, 0x01);
|
|
+
|
|
+ /* set SR1 Bit5: Screen Off */
|
|
+ data |= 0x20;
|
|
+ sisfb_set_seq_reg(sisfb, 0x01, data);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_set_default_misc_reg: - Set default value for standard VGA Miscellaneouse
|
|
+ * Output Register
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Miscellaneouse Output Register is used for selecting Sync Polarity, Clock Generator
|
|
+ * and Enable/Disable IO/MEM address space.
|
|
+ *
|
|
+ * The Miscellaneouse Output Register is programmed with 0x6F => VGA 400 lines,
|
|
+ * internal clock generator, MEM/IO address enabled.
|
|
+ *
|
|
+ * This function is needed to be called once at driver initialization.
|
|
+ */
|
|
+static void __devinit
|
|
+sisfb_set_default_misc_reg(struct sisfb_info * sisfb)
|
|
+{
|
|
+ sisfb_set_gen_reg(sisfb, 0x3c2, 0x6F);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_set_default_seq_regs: - Set default values for standard VGA Sequencer Registers
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Most of the Seqencer Registers are used for "backward compability" with EGA cards.
|
|
+ * The really essential registers are SR0 for reset and SR1 for turnning on/off the
|
|
+ * display.
|
|
+ *
|
|
+ * This function is needed to be called once at driver initialization.
|
|
+ */
|
|
+static void __devinit
|
|
+sisfb_set_default_seq_regs(struct sisfb_info * sisfb)
|
|
+{
|
|
+ /* recover from Reset state */
|
|
+ sisfb_set_seq_reg(sisfb, 0x00, 0x03);
|
|
+
|
|
+ /* set SR1 Bit5: Screen Off , Bit 0, 9 dots/char (why not 8 ??) */
|
|
+ sisfb_set_seq_reg(sisfb, 0x01, 0x21);
|
|
+
|
|
+ /* Enable bit plane 0-3 */
|
|
+ sisfb_set_seq_reg(sisfb, 0x02, 0x0F);
|
|
+
|
|
+ /* Use Character Table 0 */
|
|
+ sisfb_set_seq_reg(sisfb, 0x03, 0x00);
|
|
+
|
|
+ /* Enable 4-Chain Mode, Disable Odd/Even Mode, Enable 256KB Memory */
|
|
+ sisfb_set_seq_reg(sisfb, 0x04, 0x0E);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_set_default_crtc_regs: - Set default values for standard VGA CRT
|
|
+ * Controller Registers
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Most of the CRT Controllers Registers are display mode specific, they are set
|
|
+ * in various node setting routines.
|
|
+ *
|
|
+ * This function is needed to be called once at driver initialization.
|
|
+ */
|
|
+static void __devinit
|
|
+sisfb_set_default_crtc_regs(struct sisfb_info * sisfb)
|
|
+{
|
|
+ /* Disable Vertical Panning */
|
|
+ sisfb_set_crtc_reg(sisfb, 0x08, 0x00);
|
|
+
|
|
+ /* No text cursor */
|
|
+ sisfb_set_crtc_reg(sisfb, 0x0A, 0x00);
|
|
+ sisfb_set_crtc_reg(sisfb, 0x0B, 0x00);
|
|
+
|
|
+ /* Screen start from 0 */
|
|
+ sisfb_set_crtc_reg(sisfb, 0x0C, 0x00);
|
|
+ sisfb_set_crtc_reg(sisfb, 0x0D, 0x00);
|
|
+
|
|
+ /* Text cursor at 0 */
|
|
+ sisfb_set_crtc_reg(sisfb, 0x0E, 0x00);
|
|
+ sisfb_set_crtc_reg(sisfb, 0x0F, 0x00);
|
|
+
|
|
+ /* Enable Double Word Mode */
|
|
+ sisfb_set_crtc_reg(sisfb, 0x14, 0x40);
|
|
+
|
|
+ /* Enable vertical and horizonal retrace,
|
|
+ sequential memory addressing */
|
|
+ sisfb_set_crtc_reg(sisfb, 0x17, 0xA3);
|
|
+ /* set line compare Bit 0..7 to 0xFF */
|
|
+ sisfb_set_crtc_reg(sisfb, 0x18, 0xFF);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_set_default_grc_regs: - Set default values for standard VGA Graphics
|
|
+ * Controller Registers
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Graphics Controller Registers are used for "backward compability" with EGA cards.
|
|
+ * These registers are not actually used by this driver. Set them to some sane values
|
|
+ * or clear to 0.
|
|
+ *
|
|
+ * This function is needed to be called once at driver initialization.
|
|
+ *
|
|
+ * FixME: GR05 Bit 6 seem to be used for 256-color stuff, is it really needed ??
|
|
+ */
|
|
+static void __devinit
|
|
+sisfb_set_default_grc_regs(struct sisfb_info * sisfb)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ /* GR0-GR4 are not used by VGA, clear them */
|
|
+ for (i = 0; i <= 0x04; i++) {
|
|
+ sisfb_set_grc_reg(sisfb, i, 0x00);
|
|
+ }
|
|
+
|
|
+ /* enable 256-color mode */
|
|
+ sisfb_set_grc_reg(sisfb, 0x05, 0x40);
|
|
+
|
|
+ /* Graphics Mode, Memory Address 0xA0000 to 0xAFFFF */
|
|
+ sisfb_set_grc_reg(sisfb, 0x06, 0x05);
|
|
+ sisfb_set_grc_reg(sisfb, 0x07, 0x0F);
|
|
+ sisfb_set_grc_reg(sisfb, 0x08, 0xFF);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_set_default_attr_regs: - Set default values for standard VGA Attribute Registers
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Attribute Registers are used for "backward compability" with EGA cards. These
|
|
+ * registers are not actually used by this driver. Set them to some sane values
|
|
+ * or clear to 0.
|
|
+ *
|
|
+ * This function is needed to be called once at driver initialization.
|
|
+ */
|
|
+static void __devinit
|
|
+sisfb_set_default_attr_regs(struct sisfb_info * sisfb)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ /* Color Palette Registers AR0-ARF are used for EGA compability,
|
|
+ * set them as "identical mapping" */
|
|
+ for (i = 0; i <= 0x0F; i++) {
|
|
+ sisfb_set_attr_reg(sisfb, i, i);
|
|
+ }
|
|
+
|
|
+ /* AR10-AR14 are not used by VGA, clear them */
|
|
+ for (i = 0x10; i <= 0x14; i++) {
|
|
+ sisfb_set_attr_reg(sisfb, i, 0x00);
|
|
+ }
|
|
+
|
|
+ /* Set the Palette Address Source to Video Memory */
|
|
+ sisfb_get_gen_reg(sisfb, 0x3DA);
|
|
+ sisfb_set_gen_reg(sisfb, 0x3C0, 0x20);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_permute_dac_rgb: - Program the DAC color registers for a color group
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ * @colors: Color elements in a color group
|
|
+ *
|
|
+ * Generate the 24 permutation of the colors in each color group and program the
|
|
+ * DAC color registers in the pre-defined order.
|
|
+ */
|
|
+static void __devinit
|
|
+sisfb_permute_dac_rgb(struct sisfb_info * sisfb, u8 * colors)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < 5; i++)
|
|
+ sisfb_set_dac_rgb(sisfb, colors[i], colors[0], colors[4]);
|
|
+ for (i = 3; i > 0; i--)
|
|
+ sisfb_set_dac_rgb(sisfb, colors[4], colors[0], colors[i]);
|
|
+
|
|
+ for (i = 0; i < 5; i++)
|
|
+ sisfb_set_dac_rgb(sisfb, colors[4], colors[i], colors[0]);
|
|
+ for (i = 3; i > 0; i--)
|
|
+ sisfb_set_dac_rgb(sisfb, colors[i], colors[4], colors[0]);
|
|
+
|
|
+ for (i = 0; i < 5; i++)
|
|
+ sisfb_set_dac_rgb(sisfb, colors[0], colors[4], colors[i]);
|
|
+ for (i = 3; i > 0; i--)
|
|
+ sisfb_set_dac_rgb(sisfb, colors[0], colors[i], colors[4]);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_load_default_palette: - Load the default VGA Palette.
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Load the pre-defined standard VGA Palette default_vga_palette by writing
|
|
+ * to DAC color registers.
|
|
+ */
|
|
+static void __devinit
|
|
+sisfb_load_default_palette(struct sisfb_info * sisfb)
|
|
+{
|
|
+ int i, level;
|
|
+ u8 data;
|
|
+ u8 * table = default_vga_palette;
|
|
+
|
|
+ /* set PEL Mask */
|
|
+ sisfb_set_gen_reg(sisfb, 0x3C6, 0xFF);
|
|
+ sisfb_set_dac_index(sisfb, 0x00);
|
|
+
|
|
+ /* 0-15, 16 colors initially loaded to EGA color palette */
|
|
+ for (i = 0; i < 16; i++) {
|
|
+ u8 ega_colors[] = {0, 42, 21, 63};
|
|
+ u8 red, green, blue;
|
|
+
|
|
+ /* data == 00bbggrr */
|
|
+ data = table[i];
|
|
+ red = ega_colors[((data >> 0) & 0x03)];
|
|
+ green = ega_colors[((data >> 2) & 0x03)];
|
|
+ blue = ega_colors[((data >> 4) & 0x03)];
|
|
+ sisfb_set_dac_rgb(sisfb, red, green, blue);
|
|
+ }
|
|
+
|
|
+ /* 16-31, 16 evenly spaced shades of gray */
|
|
+ for (i = 16; i < 32; i++) {
|
|
+ data = table[i];
|
|
+ sisfb_set_dac_rgb(sisfb, data, data, data);
|
|
+ }
|
|
+
|
|
+ /* for each of 3 intensity x 3 saturation == 9 levels */
|
|
+ for (level = 0; level < 9; level++) {
|
|
+ u8 * colors = table + 32;
|
|
+ sisfb_permute_dac_rgb(sisfb, colors);
|
|
+ colors += 5;
|
|
+ }
|
|
+
|
|
+ /* last 8 entries are cleared to 0 for black */
|
|
+ for (i = 0; i < 8; i++) {
|
|
+ sisfb_set_dac_rgb(sisfb, 0x00, 0x00, 0x00);
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_init_legacy_vga: - Initialize the Standard VGA Registers
|
|
+ * @clock: paramters of the clock generator.
|
|
+ *
|
|
+ * Call other help functions to initialize the standard/legacy VGA registers.
|
|
+ */
|
|
+static void __devinit
|
|
+sisfb_init_legacy_vga(struct sisfb_info * sisfb)
|
|
+{
|
|
+ sisfb_set_default_seq_regs(sisfb);
|
|
+ sisfb_set_default_misc_reg(sisfb);
|
|
+ sisfb_set_default_attr_regs(sisfb);
|
|
+ sisfb_set_default_grc_regs(sisfb);
|
|
+ sisfb_load_default_palette(sisfb);
|
|
+
|
|
+ /* you have to load CRTC registers last or SiS 630E and SiS 630S
|
|
+ will not work. I have no idea why */
|
|
+ sisfb_set_default_crtc_regs(sisfb);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_calc_clock_freq: - Calculate the clock frequence generated by @clock
|
|
+ * @clock: paramters of the clock generator.
|
|
+ *
|
|
+ * Calculate the clock frequence that will be generated by the internal clock
|
|
+ * generator given the paremters stated in @clock. The frequence is returned
|
|
+ * in the unit of KHz.
|
|
+ */
|
|
+static unsigned long
|
|
+sisfb_calc_clock_freq(struct sisfb_clock_param * clock)
|
|
+{
|
|
+ unsigned long freq = CLOCK_SOURCE;
|
|
+
|
|
+ freq *= (clock->divider * clock->numerator);
|
|
+ freq /= (clock->denumerator * clock->post_scalar);
|
|
+
|
|
+ return freq;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_calc_clock_param: - Calculate the correct clock generator parameters for @freq
|
|
+ * @clock: Paramters of the clock generator (return)
|
|
+ * @freq: The desired clock frequence in KHz
|
|
+ *
|
|
+ * Calculate the clock generator parameters that will generate the clock signal
|
|
+ * with frequence @freq. This is the inverse function of sisfb_calc_clock_freq()
|
|
+ *
|
|
+ * Return 0 on success, -EINVAL otherwise.
|
|
+ *
|
|
+ * In this function, we are trying to find a set of parmeters (divider, numerator,
|
|
+ * denumerator, post scalar) such that:
|
|
+ *
|
|
+ * | Divider * Numerator |
|
|
+ * |Fout - Freq| = |------------------------- Fref - Freq| => 0
|
|
+ * |Denumerator * Post Scalar |
|
|
+ *
|
|
+ * In order to simplify the alogrithm we have introduced the following contrains:
|
|
+ * 1. The Post Scalar can only be power of 2 i.e. 1,2,4,8
|
|
+ * 2. The divider is always 1.
|
|
+ * 3. The frequence range for the VCO output (Fout * Post Scalar)
|
|
+ * is 150 MHz - 300 MHZ
|
|
+ *
|
|
+ * First we have to find out the proper value for Post Scalar. Due to the
|
|
+ * constrains stated above, for @freq in the following ranges, we set the
|
|
+ * Post Scalar as:
|
|
+ * freq post scalar
|
|
+ * 150 - 300 MHz 1
|
|
+ * 75 - 150 MHz 2
|
|
+ * 37.5 - 75 MHz 4
|
|
+ * 18.75 - 37.5 MHz 8
|
|
+ * Once the Post Scalar is determined, we try every possible (numerator, denumerator)
|
|
+ * pair such that the error between the generated frequence f_out and desirec
|
|
+ * frequence @freq is minimal.
|
|
+ */
|
|
+static int
|
|
+sisfb_calc_clock_param(struct sisfb_clock_param * clock, unsigned long freq)
|
|
+{
|
|
+ int numerator, denumerator;
|
|
+ unsigned long f_vco = freq;
|
|
+ long error, min_error = freq;
|
|
+
|
|
+ /* test if the required frequency is out of range */
|
|
+ if ((freq > MAX_F_VCO) || (freq < MIN_F_VCO / 8))
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* find the proper Post Scalar */
|
|
+ clock->post_scalar = 8;
|
|
+ while (f_vco > MAX_F_VCO / 8) {
|
|
+ f_vco /= 2;
|
|
+ clock->post_scalar /= 2;
|
|
+ }
|
|
+
|
|
+ /* find the (numerator, denumerator) pair that minimize the error = |f_out - freq| */
|
|
+ for (denumerator = 2; denumerator <= 32; denumerator++) {
|
|
+ for (numerator = 1; numerator <= 128; numerator++) {
|
|
+ unsigned long f_out = CLOCK_SOURCE;
|
|
+
|
|
+ f_out *= numerator;
|
|
+ f_out /= (denumerator * clock->post_scalar);
|
|
+ error = f_out - freq;
|
|
+ error = (error > 0) ? error : -error;
|
|
+ if (error < min_error) {
|
|
+ min_error = error;
|
|
+ clock->numerator = numerator;
|
|
+ clock->denumerator = denumerator;
|
|
+ }
|
|
+ /* FixMe: return early if we are satisfied with current
|
|
+ * parameters */
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* this is our unfortunate constrain */
|
|
+ clock->divider = 1;
|
|
+ clock->vco_gain = 1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_get_clock: - Read the clock generator parameters
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ * @clock: Paramters of the clock generator (return)
|
|
+ * @which: Which clock generator to be read.
|
|
+ *
|
|
+ * Read the clock generator parameters to @clock form the hardware. The desired
|
|
+ * clock generator is specified by @which. Valid values are MCLK for memory clock,
|
|
+ * VCLK for video dot clock and ECLK for 3D engine.
|
|
+ */
|
|
+static void
|
|
+sisfb_get_clock(struct sisfb_info * sisfb, struct sisfb_clock_param * clock, int which)
|
|
+{
|
|
+ u8 data;
|
|
+
|
|
+ /* get divider and numerator */
|
|
+ data = sisfb_get_seq_reg(sisfb, which);
|
|
+ clock->divider = ((data & 0x80) >> 7) + 1;
|
|
+ clock->numerator = (data & ~0x80) + 1;
|
|
+
|
|
+ /* get denumerator and post scalar */
|
|
+ data = sisfb_get_seq_reg(sisfb, which + 1);
|
|
+ clock->denumerator = (data & 0x1F) + 1;
|
|
+ clock->post_scalar = ((data & 0xE0) >> 5) + 1;
|
|
+
|
|
+ /* get VCO gain */
|
|
+ data = sisfb_get_seq_reg(sisfb, which + 2);
|
|
+ clock->vco_gain = (data >> 7);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_set_clock: - Set the clock generator parameters
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ * @clock: Paramters of the clock generator
|
|
+ * @which: Which clock generator to be read.
|
|
+ *
|
|
+ * Set the clock generator parameters @clock to the hardware. The desired clock
|
|
+ * generator is specified by @which. Valid values are MCLK for memory clock,
|
|
+ * VCLK for video dot clock and ECLK for 3D engine.
|
|
+ */
|
|
+static void
|
|
+sisfb_set_clock(struct sisfb_info * sisfb, struct sisfb_clock_param * clock, int which)
|
|
+{
|
|
+ u8 data;
|
|
+
|
|
+ /* set divider and numerator */
|
|
+ data = (clock->numerator - 1) & 0x7F;
|
|
+ data |= (clock->divider - 1) ? 0x80 : 0x00;
|
|
+ sisfb_set_seq_reg(sisfb, which, data);
|
|
+
|
|
+ /* set denumerator and post scalar */
|
|
+ data = (clock->denumerator - 1) & 0x1F;
|
|
+ data |= (clock->post_scalar - 1) << 5;
|
|
+ sisfb_set_seq_reg(sisfb, which + 1, data);
|
|
+
|
|
+ /* set VCO gain */
|
|
+ data = clock->vco_gain ? 0x80 : 0x00;
|
|
+ sisfb_set_seq_reg(sisfb, which + 2, data);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_get_memory_size: - Get framebuffer memory size
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Return 0 on Success, -ENODEV if SMA is incorrectly configured
|
|
+ *
|
|
+ * Get the framebuffer memory size from host controller. For SiS 530/620 and
|
|
+ * SiS 540/630/730 Families, the Shared Memory Area (SMA) size is stored in
|
|
+ * Register 0x63, Bit 4..6 of Host Bridge (i.e. PCI 0:0.0). The register is
|
|
+ * set by DRAM initialzation code during boot process and can not be re-configured
|
|
+ * once the DRAM has been turned on.
|
|
+ */
|
|
+#define SIS630_BANKENABLE 0x63
|
|
+static int __devinit
|
|
+sisfb_get_memory_size(struct sisfb_info * sisfb)
|
|
+{
|
|
+ struct pci_dev * host = NULL;
|
|
+ u8 dram_status, sma_size, sma_size_bits;
|
|
+
|
|
+ if ((host = pci_find_slot(0, 0)) == NULL) {
|
|
+ printk(KERN_EMERG "sisfb_lite: Can not find Host Controller !!!\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ /* compute Shared Menory Area (SMA) size in Mega Bytes */
|
|
+ pci_read_config_byte(host, SIS630_BANKENABLE, &dram_status);
|
|
+ sma_size_bits = (dram_status >> 4) & 0x7;
|
|
+ if (sma_size_bits > 5) {
|
|
+ // this is invalid!
|
|
+ printk(KERN_EMERG "sisfb_lite: Invalid Shared Memory Area Configuration !!!\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ sma_size = (2 << sma_size_bits);
|
|
+ sisfb->video_size_virt = sma_size * 1024 * 1024;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_set_memory_clocks: - Set memory bus clock rate
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Set the memory clocks for VGA core. Form SiS 300 and up, the VGA core has
|
|
+ * 2 independent clocks for 2D and 3D engines. We can set them to different
|
|
+ * rates seperatly.
|
|
+ */
|
|
+static unsigned long mclk = 66000, eclk = 100000;
|
|
+static void __devinit
|
|
+sisfb_set_memory_clocks(struct sisfb_info * sisfb)
|
|
+{
|
|
+ struct sisfb_clock_param clock;
|
|
+
|
|
+ /* set the clock rate according to the option */
|
|
+ sisfb_calc_clock_param(&clock, mclk);
|
|
+ sisfb_set_clock(sisfb, &clock, SIS300_MCLK);
|
|
+
|
|
+ sisfb_calc_clock_param(&clock, eclk);
|
|
+ sisfb_set_clock(sisfb, &clock, SIS300_ECLK);
|
|
+
|
|
+ /* read the clock rate back and show to user */
|
|
+ sisfb_get_clock(sisfb, &clock, SIS300_MCLK);
|
|
+ printk(KERN_INFO "sisfb_lite: 2D Memory Clock = %6ld KHz\n",
|
|
+ sisfb_calc_clock_freq(&clock));
|
|
+
|
|
+ sisfb_get_clock(sisfb, &clock, SIS300_ECLK);
|
|
+ printk(KERN_INFO "sisfb_lite: 3D Memory Clock = %6ld KHz\n",
|
|
+ sisfb_calc_clock_freq(&clock));
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_config_memory: - Configure Framebuffer Memory
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Return 0 on Success, -ENODEV on Failure
|
|
+ *
|
|
+ * Configure the frame buffer memory.
|
|
+ * If Memory Type Range Registers (MTRR) is configured in the kernel. We set the
|
|
+ * Frame Buffer area as "Write Combine" to boost the perfromance.
|
|
+ */
|
|
+static int __devinit
|
|
+sisfb_config_memory(struct sisfb_info * sisfb)
|
|
+{
|
|
+ int ret;
|
|
+ u8 data;
|
|
+
|
|
+ /* Check if we have SMA configured correctly and HOW MUCH */
|
|
+ if ((ret = sisfb_get_memory_size(sisfb)) < 0)
|
|
+ return ret;
|
|
+
|
|
+ /* Enable PCI Relocated IO, Memory Mapped IO and Linear Framebuffer Addressing */
|
|
+ data = SIS300_PCI_MMIO_ENABLE;
|
|
+ data |= SIS300_PCI_IO_ENABLE;
|
|
+ data |= SIS300_PCI_LINEAR_ENABLE;
|
|
+ sisfb_set_seq_reg(sisfb, SIS300_PCI_ADDR_SET, data);
|
|
+
|
|
+ /* ioremap MMIO and Framebuffer Area */
|
|
+ sisfb->video_base_virt =
|
|
+ (unsigned long) ioremap(sisfb->video_base_phy, sisfb->video_size_virt);
|
|
+ sisfb->mmio_base_virt =
|
|
+ (unsigned long) ioremap(sisfb->mmio_base_phy, sisfb->mmio_size_phy);
|
|
+
|
|
+ printk(KERN_INFO "sisfb_lite: framebuffer at 0x%lx, mapped to 0x%p, size %lu KB\n",
|
|
+ sisfb->video_base_phy, (char *) sisfb->video_base_virt,
|
|
+ sisfb->video_size_virt / 1024);
|
|
+
|
|
+ sisfb_set_memory_clocks(sisfb);
|
|
+
|
|
+#ifdef CONFIG_MTRR
|
|
+ /* Use MTRR to boost performance
|
|
+ * FixMe: There is some noise on the screen if we ehable the MTRR on SiS 630. */
|
|
+ sisfb->mtrr = mtrr_add(sisfb->video_base_phy, sisfb->video_size_virt,
|
|
+ MTRR_TYPE_WRCOMB, 1);
|
|
+ if (sisfb->mtrr >= 0) {
|
|
+ printk(KERN_INFO "sisfb_lite: Turned on MTRR Write Combine for framebuffer\n");
|
|
+ }
|
|
+#endif /* CONFIG_MTRR */
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_fake_vgabios: - Pretend we still have VGA BIOS
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Many of the Extended Sequencer Registers and Externded CRT Controller Registers
|
|
+ * are used by traditional System/VGA BIOS and Drivers to pass information back and
|
|
+ * forth. We have to fake some resonable values for these registers in order to let
|
|
+ * some stupid applications like XFree 3.3.6 work properly.
|
|
+ */
|
|
+static void __devinit
|
|
+sisfb_fake_vgabios(struct sisfb_info * sisfb)
|
|
+{
|
|
+ int i;
|
|
+ u8 data, sma_size;
|
|
+ u8 fake_bios[] = {0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F};
|
|
+
|
|
+ /* SR 14 is used as "protocol" by VGA BIOS and XFree86 for passing SMA size
|
|
+ and bus width, we have to fake them :~-( */
|
|
+ sma_size = sisfb->video_size_virt >> 21;
|
|
+ data = fake_bios[ffs(sma_size) - 1];
|
|
+ data |= 0x40; // 64-bit bus
|
|
+ sisfb_set_seq_reg(sisfb, 0x14, data);
|
|
+
|
|
+ /* SR16-SR19 are used as "protocol" by VGA BIOS and System BIOS for passing
|
|
+ information about TV-out, we have to clear them :~-( */
|
|
+ for (i = 0x16; i <= 0x19; i++) {
|
|
+ sisfb_set_seq_reg(sisfb, i, 0x00);
|
|
+ }
|
|
+
|
|
+ /* SR1A are used as "protocol" by VGA BIOS and System BIOS for passing
|
|
+ information about M/B frequence, we have to fake them :~-( */
|
|
+ sisfb_set_seq_reg(sisfb, 0x1A, 0x12);
|
|
+
|
|
+ /* set MD out enable to 1T (what the hell ??) */
|
|
+ sisfb_set_seq_reg(sisfb, 0x15, 0x01);
|
|
+
|
|
+ /* SR1B, SR1C are not used by SiS 630 (used by SiS 300 ??),
|
|
+ clear them. */
|
|
+ sisfb_set_seq_reg(sisfb, 0x1B, 0x00);
|
|
+ sisfb_set_seq_reg(sisfb, 0x1C, 0x00);
|
|
+
|
|
+ /* CR30-CR37 are used by VGA BIOS to pass information
|
|
+ about SiS 301, clean them */
|
|
+ for (i = 0x30; i <= 0x37; i++) {
|
|
+ sisfb_set_crtc_reg(sisfb, i, 0x00);
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_set_pci_agp_timming: - Set AGP/PCI timming control registers
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * FixME: This function is not finished and is almost BOGUS.
|
|
+ */
|
|
+static void __devinit
|
|
+sisfb_set_pci_agp_timming(struct sisfb_info * sisfb)
|
|
+{
|
|
+ u8 AGP, tmp;
|
|
+
|
|
+ /* SR3A, Hardware Trap III */
|
|
+ tmp = sisfb_get_seq_reg(sisfb, 0x3A);
|
|
+ if ((tmp & 0x30) == 0x30)
|
|
+ // PCI Mode
|
|
+ AGP = 0;
|
|
+ else
|
|
+ // AGP Mode;
|
|
+ AGP = 1;
|
|
+
|
|
+ tmp = pci_timming[0];
|
|
+ if (AGP == 0)
|
|
+ // Disable AGP Request High Priority
|
|
+ tmp &= ~0x10;
|
|
+ sisfb_set_seq_reg(sisfb, 0x21, tmp);
|
|
+
|
|
+ tmp = pci_timming[1];
|
|
+ if (AGP == 1)
|
|
+ // Enable PCI Burst memory write
|
|
+ tmp |= 0x20;
|
|
+ sisfb_set_seq_reg(sisfb, 0x22, tmp);
|
|
+
|
|
+ sisfb_set_seq_reg(sisfb, 0x23, pci_timming[2]);
|
|
+ sisfb_set_seq_reg(sisfb, 0x24, pci_timming[3]);
|
|
+ sisfb_set_seq_reg(sisfb, 0x25, pci_timming[4]);
|
|
+ sisfb_set_seq_reg(sisfb, 0x32, pci_timming[5]);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_init_300: - Initialize the SiS300 VGA Core in SiS300, SiS630,540,730
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * SiS 300 VGA core is used in SiS540/630/730 chipsets. This routine inits
|
|
+ * the very SiS300 specific stuff.
|
|
+ */
|
|
+static void __devinit
|
|
+sisfb_init_300(struct sisfb_info * sisfb)
|
|
+{
|
|
+ /* set to High Speed DAC by default */
|
|
+ sisfb_set_seq_reg(sisfb, 0x07, 0x13);
|
|
+
|
|
+ /* Disable DAC pedestal */
|
|
+ sisfb_set_seq_reg(sisfb, 0x1F, 0x00);
|
|
+
|
|
+ sisfb_set_pci_agp_timming(sisfb);
|
|
+
|
|
+ /* disable power management mode */
|
|
+ sisfb_set_seq_reg(sisfb, 0x11, 0x0F);
|
|
+
|
|
+ sisfb_fake_vgabios(sisfb);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_install_cmap: - Install colormap
|
|
+ * @con: Console number
|
|
+ * @info: Frame Buffer structure
|
|
+ *
|
|
+ * Install colormap for the current console. This function is called every
|
|
+ * time the display mode is changed (by set_var or switch_con).
|
|
+ */
|
|
+static void do_install_cmap(int con, struct fb_info *info)
|
|
+{
|
|
+ struct sisfb_info * sisfb = (struct sisfb_info *) info;
|
|
+
|
|
+ if (con != sisfb->currcon)
|
|
+ return;
|
|
+
|
|
+ if (fb_display[con].cmap.len)
|
|
+ fb_set_cmap(&fb_display[con].cmap, 1, sisfb_setcolreg, info);
|
|
+ else {
|
|
+ int size = (fb_display[con].var.bits_per_pixel <= 8) ? 256 : 16;
|
|
+ fb_set_cmap(fb_default_cmap(size), 1, sisfb_setcolreg, info);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+sisfb_set_dispsw(struct display * disp, struct sisfb_info * sisfb, int bpp, int accel)
|
|
+{
|
|
+ switch (bpp) {
|
|
+#ifdef FBCON_HAS_CFB8
|
|
+ case 8:
|
|
+ disp->dispsw = &fbcon_cfb8;
|
|
+ break;
|
|
+#endif
|
|
+
|
|
+#ifdef FBCON_HAS_CFB16
|
|
+ case 15:
|
|
+ case 16:
|
|
+ disp->dispsw = &fbcon_cfb16;
|
|
+ disp->dispsw_data = sisfb->fbcon_cmap.cfb16;
|
|
+ break;
|
|
+#endif
|
|
+
|
|
+#ifdef FBCON_HAS_CFB24
|
|
+ case 24:
|
|
+ disp->dispsw = &fbcon_cfb24;
|
|
+ disp->dispsw_data = sisfb->fbcon_cmap.cfb24;
|
|
+ break;
|
|
+#endif
|
|
+
|
|
+#ifdef FBCON_HAS_CFB32
|
|
+ case 32:
|
|
+ disp->dispsw = &fbcon_cfb32;
|
|
+ disp->dispsw_data = sisfb->fbcon_cmap.cfb32;
|
|
+ break;
|
|
+#endif
|
|
+
|
|
+ default:
|
|
+ disp->dispsw = &fbcon_dummy;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ disp->scrollmode = SCROLL_YREDRAW;
|
|
+ //sisfb_sw.bmove = fbcon_redraw_bmove;
|
|
+}
|
|
+
|
|
+static void
|
|
+sisfb_set_crt1_crtc_regs(struct sisfb_info * sisfb, struct sisfb_par * par)
|
|
+{
|
|
+ u16 htotal, hdispend, hsyncstart, hsyncend, hblankstart, hblankend;
|
|
+ u16 vtotal, vdispend, vsyncstart, vsyncend, vblankstart, vblankend;
|
|
+ u16 overflow, overflow_h1, overflow_h2, overflow_v;
|
|
+
|
|
+ /* first convert 'par' to "screen" unit */
|
|
+ htotal = (par->htotal >> 3) - 5;
|
|
+ hdispend = (par->hdispend >> 3) - 1;
|
|
+ hblankstart = (par->hsyncstart >> 3) - 1;
|
|
+ hblankend = (par->hsyncend >> 3);
|
|
+ hsyncstart = (par->hsyncstart >> 3);
|
|
+ hsyncend = (par->hsyncend >> 3);
|
|
+
|
|
+ vtotal = par->vtotal - 2;
|
|
+ vdispend = par->vdispend - 1;
|
|
+ vsyncstart = par->vsyncstart;
|
|
+ vsyncend = par->vsyncend;
|
|
+ vblankstart = par->vsyncstart;
|
|
+ vblankend = par->vsyncend + 1;
|
|
+
|
|
+ overflow =
|
|
+ ((vsyncstart & 0x200) >> 2) | /* Bit 7 */
|
|
+ ((vdispend & 0x200) >> 3) |
|
|
+ ((vtotal & 0x200) >> 4) |
|
|
+ 0x10 | /* line compare Bit 8 */
|
|
+ ((vblankstart & 0x100) >> 5) |
|
|
+ ((vsyncstart & 0x100) >> 6) |
|
|
+ ((vdispend & 0x100) >> 7) |
|
|
+ ((vtotal & 0x100) >> 8); /* Bit 0 */
|
|
+
|
|
+ overflow_v =
|
|
+ ((vsyncend & 0x010) << 1) | /* Bit 5 */
|
|
+ ((vblankend & 0x100) >> 4) |
|
|
+ ((vsyncstart & 0x400) >> 7) |
|
|
+ ((vblankstart & 0x400) >> 8) |
|
|
+ ((vdispend & 0x400) >> 9) |
|
|
+ ((vtotal & 0x400) >> 10); /* Bit 0 */
|
|
+
|
|
+ overflow_h1 =
|
|
+ ((hsyncstart & 0x300) >> 2) | /* Bit 7 */
|
|
+ ((hblankstart & 0x300) >> 4) |
|
|
+ ((hdispend & 0x300) >> 6) |
|
|
+ ((htotal & 0x300) >> 8); /* Bit 0 */
|
|
+
|
|
+ overflow_h2 =
|
|
+ ((hsyncend & 0x020) >> 3) | /* Bit 2 */
|
|
+ ((hblankend & 0x0C0) >> 6); /* Bit 1,0 */
|
|
+
|
|
+ /* write 'par' to hardware registers, Standard VGA part, low order bits */
|
|
+ sisfb_set_crtc_reg(sisfb, 0x00, htotal);
|
|
+ sisfb_set_crtc_reg(sisfb, 0x01, hdispend);
|
|
+ sisfb_set_crtc_reg(sisfb, 0x02, hblankstart);
|
|
+ sisfb_set_crtc_reg(sisfb, 0x03, 0x80 | (hblankend & 0x1f));
|
|
+ sisfb_set_crtc_reg(sisfb, 0x04, hsyncstart);
|
|
+ sisfb_set_crtc_reg(sisfb, 0x05, (hblankend & 0x20) << 2 | (hsyncend & 0x1f));
|
|
+
|
|
+ sisfb_set_crtc_reg(sisfb, 0x06, vtotal);
|
|
+ sisfb_set_crtc_reg(sisfb, 0x09, (vblankstart & 0x200) >> 4 | 0x40 /* line compare Bit 9 */);
|
|
+ sisfb_set_crtc_reg(sisfb, 0x10, vsyncstart);
|
|
+ sisfb_set_crtc_reg(sisfb, 0x11, (vsyncend & 0x0F) | 0x20);
|
|
+ sisfb_set_crtc_reg(sisfb, 0x12, vdispend);
|
|
+ sisfb_set_crtc_reg(sisfb, 0x15, vblankstart);
|
|
+ sisfb_set_crtc_reg(sisfb, 0x16, vblankend);
|
|
+
|
|
+ /* Standard VGA part, overflow bits */
|
|
+ sisfb_set_crtc_reg(sisfb, 0x07, overflow);
|
|
+
|
|
+ /* Extended Registers, overflow bits */
|
|
+ sisfb_set_seq_reg(sisfb, 0x0A, overflow_v);
|
|
+ sisfb_set_seq_reg(sisfb, 0x0B, overflow_h1);
|
|
+ sisfb_set_seq_reg(sisfb, 0x0C, overflow_h2);
|
|
+}
|
|
+
|
|
+static void
|
|
+sisfb_set_crt1_mode_regs(struct sisfb_info * sisfb, struct sisfb_par * par)
|
|
+{
|
|
+ u8 tmp;
|
|
+
|
|
+ /* enable Enhanced Graphics Mode and Atuo Line Width Counter */
|
|
+ tmp = 0x03;
|
|
+ switch (par->bits_per_pixel) {
|
|
+ case 32:
|
|
+ tmp |= 0x10;
|
|
+ break;
|
|
+ case 24:
|
|
+ tmp |= 0x0C;
|
|
+ break;
|
|
+ case 16:
|
|
+ tmp |= 0x08;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ sisfb_set_seq_reg(sisfb, 0x06, tmp);
|
|
+
|
|
+ /* enable 2D engine */
|
|
+ sisfb_set_seq_reg(sisfb, 0x1E, 0x40);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_set_crt1_pitch: - Set screen pitch registers for CRT1
|
|
+ * @sisfb: SiS Frame Buffer structure
|
|
+ *
|
|
+ * Set the screen pitch registers for CRT1. Screen pitch refers to the memory address
|
|
+ * difference between two dots of the same col on two vertically neighboring scan lines.
|
|
+ * The CRTC 0x13 is called "offset register" in VGA literatures. SiS VGA also defines
|
|
+ * its own screen line width register SR 0x10.
|
|
+ */
|
|
+static void
|
|
+sisfb_set_crt1_pitch(struct sisfb_info * sisfb, struct sisfb_par * par)
|
|
+{
|
|
+ /* FixME: take interlance into account */
|
|
+ u16 pitch = par->line_length >> 3;
|
|
+ u8 line_length = ((par->line_length + 63) >> 6) + 1;
|
|
+
|
|
+ /* disable line compare */
|
|
+ sisfb_set_seq_reg(sisfb, 0x0F, 0x08);
|
|
+
|
|
+ /* screen pitch */
|
|
+ sisfb_set_crtc_reg(sisfb, 0x13, pitch & 0xFF);
|
|
+ sisfb_set_seq_reg(sisfb, 0x0E, (pitch >> 8) & 0x0F);
|
|
+
|
|
+ /* line length */
|
|
+ sisfb_set_seq_reg(sisfb, 0x10, line_length);
|
|
+}
|
|
+
|
|
+static void
|
|
+sisfb_set_crt1_vclk(struct sisfb_info * sisfb, struct sisfb_par * par)
|
|
+{
|
|
+ /* FixME: Use clock_param for par->dot_clock */
|
|
+ u32 vclk = par->dot_clock / 1000;
|
|
+ u8 tmp = 0x10, tmp1;
|
|
+ struct sisfb_clock_param clock;
|
|
+
|
|
+ /* Select normal DCLK */
|
|
+ sisfb_set_seq_reg(sisfb, 0x31, 0x00);
|
|
+
|
|
+ /* set vclk frequence, FixME: do error check */
|
|
+ sisfb_calc_clock_param(&clock, vclk);
|
|
+ sisfb_set_clock(sisfb, &clock, SIS300_DCLK);
|
|
+
|
|
+ sisfb_get_clock(sisfb, &clock, SIS300_DCLK);
|
|
+ printk(KERN_INFO "sisfb_lite: Video Dot Clock = %6ld KHz\n",
|
|
+ sisfb_calc_clock_freq(&clock));
|
|
+
|
|
+ /* set vclk state, this is totally contradict to Glamour Design Guideline */
|
|
+ if (vclk < 100000)
|
|
+ tmp |= 0x03;
|
|
+ else if (vclk < 200000)
|
|
+ tmp |= 0x02;
|
|
+ else if (vclk < 250000)
|
|
+ tmp |= 0x01;
|
|
+
|
|
+ /* set half clock, why ?? */
|
|
+ if (vclk > 150000) {
|
|
+ tmp |= 0x80;
|
|
+ tmp1 = 0x08;
|
|
+ } else {
|
|
+ tmp &= 0x7F;
|
|
+ tmp1 = 0x00;
|
|
+ }
|
|
+
|
|
+ sisfb_set_seq_reg(sisfb, 0x07, tmp);
|
|
+ sisfb_set_seq_reg(sisfb, 0x32, tmp1);
|
|
+}
|
|
+
|
|
+static u8 latency_factor[] = {
|
|
+ 97, 88, 86, 79, 77, 0,
|
|
+ 0, 87, 85, 78, 76, 54,
|
|
+ 80, 72, 69, 63, 61, 0,
|
|
+ 0, 70, 68, 62, 59, 37,
|
|
+};
|
|
+
|
|
+static int
|
|
+sisfb_set_fifo_thresholds(struct sisfb_info * sisfb, struct sisfb_par * par)
|
|
+{
|
|
+ u32 vclk = par->dot_clock / 1000;
|
|
+ unsigned int i = 0, threshold;
|
|
+ int grant_timer, fg_queue, bg_queue;
|
|
+ struct pci_dev * host = NULL;
|
|
+ u8 queue, srf;
|
|
+
|
|
+ if ((host = pci_find_slot(0, 0)) == NULL) {
|
|
+ printk(KERN_EMERG "sisfb_lite: Can not find Host Controller !!!\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ pci_read_config_byte(host, 0x53, &queue);
|
|
+ queue &= 0xF0;
|
|
+
|
|
+ for (grant_timer = 1; grant_timer >= 0; grant_timer--) {
|
|
+ for (fg_queue = 0; fg_queue <= 5; fg_queue++) {
|
|
+ for (bg_queue = 0; bg_queue <= 1; bg_queue++) {
|
|
+ threshold = latency_factor[i++] * vclk;
|
|
+ threshold *= (par->bits_per_pixel >> 3);
|
|
+ threshold /= (mclk * 16);
|
|
+ if (threshold > 0x13 || threshold == 0x00)
|
|
+ ;
|
|
+ else
|
|
+ goto set_threshold;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return -EINVAL;
|
|
+
|
|
+ set_threshold:
|
|
+ /* write froeground and backgroudn queue, GUI grant timer */
|
|
+ queue |= ((fg_queue << 1) | bg_queue);
|
|
+ pci_write_config_byte(host, 0x53, queue);
|
|
+ pci_write_config_byte(host, 0xA3, grant_timer);
|
|
+
|
|
+ /* Write CRT/CPU threshold low, CRT/Engine threshold high */
|
|
+ threshold += 1;
|
|
+ srf = sisfb_get_seq_reg(sisfb, 0x0F);
|
|
+ sisfb_set_seq_reg(sisfb, 0x08, ((threshold & 0x0F) << 4 | 0x0F));
|
|
+ sisfb_set_seq_reg(sisfb, 0x0F, ((threshold & 0x10) << 1 | srf ));
|
|
+
|
|
+ /* Write CRT/CPU threshold high */
|
|
+ threshold += 3;
|
|
+ if (threshold > 0x0F)
|
|
+ threshold = 0x0F;
|
|
+ sisfb_set_seq_reg(sisfb, 0x09, (threshold & 0x0F));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_encode_fix: - Encode the Fixed Part of the Display
|
|
+ * @fix: Fixed screen info (return)
|
|
+ * @con: Console number
|
|
+ * @info: Frame Buffer structure
|
|
+ *
|
|
+ * Return the fixed part of the display information in @fix. The struct of @fix is
|
|
+ * defined in linux/fb.h.
|
|
+ *
|
|
+ * In order to be compable with sisfb.c and SiS MPEG decoder library, the fix->smem_len
|
|
+ * is not actually the size of the frame buffer memory. Instead, fix->reserved[0] and
|
|
+ * fix->reserved[1] are served for this purpose. fix->reserved[2] is used as "capability"
|
|
+ * field of the driver, currently, Bit 7 --> Turbo Queue is On, Bit 6 --> Hardware Cursor
|
|
+ * is On.
|
|
+ */
|
|
+static int
|
|
+sisfb_encode_fix(struct fb_fix_screeninfo * fix, struct sisfb_par * par, struct sisfb_info * sisfb)
|
|
+{
|
|
+ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
|
|
+ strcpy(fix->id, "SiSFB Lite");
|
|
+
|
|
+ /* fix->smem_start is used as a hack to reserve offsecren memory for
|
|
+ * Xserver and MPEG decoder library */
|
|
+ fix->smem_start = sisfb->video_base_phy;
|
|
+ if (sisfb->video_size_virt > 0x800000)
|
|
+ fix->smem_len = RESERVED_MEM_SIZE_8M; /* reserved for Xserver */
|
|
+ else
|
|
+ fix->smem_len = RESERVED_MEM_SIZE_4M; /* reserved for Xserver */
|
|
+
|
|
+ fix->type = FB_TYPE_PACKED_PIXELS;
|
|
+ fix->type_aux = 0;
|
|
+
|
|
+ if (par->bits_per_pixel == 8)
|
|
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
|
|
+ else
|
|
+ fix->visual = FB_VISUAL_TRUECOLOR;
|
|
+
|
|
+ fix->xpanstep = 0;
|
|
+ fix->ypanstep = 0;
|
|
+ fix->ywrapstep = 0;
|
|
+
|
|
+ fix->line_length = par->line_length;
|
|
+
|
|
+ fix->mmio_start = sisfb->mmio_base_phy;
|
|
+ fix->mmio_len = sisfb->mmio_size_phy;
|
|
+
|
|
+ fix->accel = FB_ACCEL_SIS_GLAMOUR;
|
|
+
|
|
+ /* FixMe: Remove these lines, currently used for old sisfb.c and
|
|
+ * other graphics library */
|
|
+ fix->reserved[0] = sisfb->video_size_virt & 0xFFFF;
|
|
+ fix->reserved[1] = (sisfb->video_size_virt >> 16) & 0xFFFF;
|
|
+ fix->reserved[2] = 0; /* capabilities, Trubo Queue and HW Cursor */
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_decode_var: - Convert @var to @par
|
|
+ * @var:
|
|
+ * @par:
|
|
+ * @info: Frame Buffer structure
|
|
+ *
|
|
+ * Get the video params out of 'var'. If a value doesn't fit, round it up,
|
|
+ * if it's too big, return -EINVAL.
|
|
+ *
|
|
+ * The driver's default par i.e. sisfb->default_par is convert from default_var
|
|
+ * during initialization. Default_var is either predefined or provided by find_mode().
|
|
+ */
|
|
+static int
|
|
+sisfb_decode_var(struct fb_var_screeninfo * var,
|
|
+ struct sisfb_par * par, const struct sisfb_info * sisfb)
|
|
+{
|
|
+ if (var->bits_per_pixel != 8 && var->bits_per_pixel != 16 &&
|
|
+ var->bits_per_pixel != 24 && var->bits_per_pixel != 32) {
|
|
+ DPRINTK("depth not supported: %u\n", var->bits_per_pixel);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (var->xoffset) {
|
|
+ DPRINTK("xoffset not supported\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (var->yoffset) {
|
|
+ DPRINTK("yoffset not supported\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ memset(par, 0, sizeof(struct sisfb_par));
|
|
+
|
|
+ par->hdispend = var->xres;
|
|
+ par->hsyncstart = par->hdispend + var->right_margin;
|
|
+ par->hsyncend = par->hsyncstart + var->hsync_len;
|
|
+ par->htotal = par->hsyncend + var->left_margin;
|
|
+
|
|
+ par->vdispend = var->yres;
|
|
+ par->vsyncstart = par->vdispend + var->lower_margin;
|
|
+ par->vsyncend = par->vsyncstart + var->vsync_len;
|
|
+ par->vtotal = par->vsyncend + var->upper_margin;
|
|
+
|
|
+ par->dot_clock = 1E12 / var->pixclock;
|
|
+
|
|
+ par->bits_per_pixel = var->bits_per_pixel;
|
|
+ par->cmap_len = (par->bits_per_pixel == 8) ? 256 : 16;
|
|
+
|
|
+ par->line_length = var->xres * (par->bits_per_pixel >> 3);
|
|
+
|
|
+#if 0
|
|
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
|
|
+ par->sync_mode |= SISFB_HSYNC_ACT_HIGH;
|
|
+ else
|
|
+ par->sunc_mode |= SISFB_HSYNC_ACT_LOW;
|
|
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
|
|
+ par->sync_mode |= SISFB_VSYNC_ACT_HIGH;
|
|
+ else
|
|
+ par->sync_mode |= SISFB_VSYNC_ACT_LOW;
|
|
+#endif
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_encode_var: - Convert @par to @var
|
|
+ * @var:
|
|
+ * @par:
|
|
+ * @info: Frame Buffer structure
|
|
+ *
|
|
+ * Fill the @var structure based on the values in the hardware specific @par.
|
|
+ */
|
|
+static int
|
|
+sisfb_encode_var(struct fb_var_screeninfo * var, const struct sisfb_par * par,
|
|
+ const struct sisfb_info * sisfb)
|
|
+{
|
|
+ memset(var, 0, sizeof(struct fb_var_screeninfo));
|
|
+
|
|
+ var->xres = par->hdispend;
|
|
+ var->yres = par->vdispend;
|
|
+ var->xres_virtual = var->xres;
|
|
+ var->yres_virtual = var->yres;
|
|
+ var->right_margin = par->hsyncstart - par->hdispend;
|
|
+ var->hsync_len = par->hsyncend - par->hsyncstart;
|
|
+ var->left_margin = par->htotal - par->hsyncend;
|
|
+ var->lower_margin = par->vsyncstart - par->vdispend;
|
|
+ var->vsync_len = par->vsyncend - par->vsyncstart;
|
|
+ var->upper_margin = par->vtotal - par->vsyncend;
|
|
+ var->bits_per_pixel = par->bits_per_pixel;
|
|
+ var->height = -1;
|
|
+ var->width = -1;
|
|
+
|
|
+ switch (par->bits_per_pixel) {
|
|
+ case 8:
|
|
+ var->red.length = 6;
|
|
+ var->green.length = 6;
|
|
+ var->blue.length = 6;
|
|
+ break;
|
|
+ case 16: /* RGB 565 */
|
|
+ var->red.offset = 11;
|
|
+ var->red.length = 5;
|
|
+ var->green.offset = 5;
|
|
+ var->green.length = 6;
|
|
+ var->blue.offset = 0;
|
|
+ var->blue.length = 5;
|
|
+ var->transp.offset = 0;
|
|
+ var->transp.length = 0;
|
|
+ break;
|
|
+ case 24: /* RGB 888 */
|
|
+ var->red.offset = 16;
|
|
+ var->red.length = 8;
|
|
+ var->green.offset = 8;
|
|
+ var->green.length = 8;
|
|
+ var->blue.offset = 0;
|
|
+ var->blue.length = 8;
|
|
+ var->transp.offset = 0;
|
|
+ var->transp.length = 0;
|
|
+ break;
|
|
+ case 32:
|
|
+ var->red.offset = 16;
|
|
+ var->red.length = 8;
|
|
+ var->green.offset = 8;
|
|
+ var->green.length = 8;
|
|
+ var->blue.offset = 0;
|
|
+ var->blue.length = 8;
|
|
+ var->transp.offset = 24;
|
|
+ var->transp.length = 8;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ var->pixclock = (u32) (1E12 / par->dot_clock);
|
|
+
|
|
+ if (par->sync_mode & 0x80)
|
|
+ var->sync &= ~FB_SYNC_VERT_HIGH_ACT;
|
|
+ else
|
|
+ var->sync |= FB_SYNC_VERT_HIGH_ACT;
|
|
+
|
|
+ if (par->sync_mode & 0x40)
|
|
+ var->sync &= ~FB_SYNC_HOR_HIGH_ACT;
|
|
+ else
|
|
+ var->sync |= FB_SYNC_HOR_HIGH_ACT;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set the hardware according to 'par'.
|
|
+ */
|
|
+static void
|
|
+sisfb_set_par(struct sisfb_par *par, struct sisfb_info * sisfb)
|
|
+{
|
|
+ sisfb_display_off(sisfb);
|
|
+
|
|
+ /* Standard VAG CRTC Registers */
|
|
+ sisfb_set_crt1_crtc_regs(sisfb, par);
|
|
+
|
|
+ /* Extended Registers, other stuff */
|
|
+ sisfb_set_crt1_pitch(sisfb, par);
|
|
+ sisfb_set_crt1_vclk(sisfb, par);
|
|
+ sisfb_set_crt1_mode_regs(sisfb, par);
|
|
+ sisfb_set_fifo_thresholds(sisfb, par);
|
|
+
|
|
+ sisfb_display_on(sisfb);
|
|
+
|
|
+ sisfb->current_par = *par;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_getcolreg: - Read color register
|
|
+ * @regno: Color register number
|
|
+ * @red: Red component (return)
|
|
+ * @green: Green component (return)
|
|
+ * @blue: Blue component (return)
|
|
+ * @trans: Alpha component (return)
|
|
+ * @fb_info: Frame Buffer structure
|
|
+ *
|
|
+ * Return != 0 for invalid register number.
|
|
+ *
|
|
+ * Read a single color register form the driver's color palette and split
|
|
+ * it into colors/transparent components. sisfb_get_cmap() uses this function
|
|
+ * to query the colormap.
|
|
+ */
|
|
+static int
|
|
+sisfb_getcolreg(unsigned regno, unsigned *red, unsigned *green, unsigned *blue,
|
|
+ unsigned *transp, struct fb_info * info)
|
|
+{
|
|
+ struct sisfb_info * sisfb = (struct sisfb_info *) info;
|
|
+ struct sisfb_par * par = (struct sisfb_par *) &sisfb->current_par;
|
|
+
|
|
+ if (regno >= par->cmap_len)
|
|
+ return 1;
|
|
+
|
|
+ *red = (sisfb->palette[regno].red << 8) | sisfb->palette[regno].red;
|
|
+ *green = (sisfb->palette[regno].green << 8) | sisfb->palette[regno].green;
|
|
+ *blue = (sisfb->palette[regno].blue << 8) | sisfb->palette[regno].blue;
|
|
+ *transp = 0;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_getcolreg: - Set color register
|
|
+ * @regno: Color register number
|
|
+ * @red: Red component (return)
|
|
+ * @green: Green component (return)
|
|
+ * @blue: Blue component (return)
|
|
+ * @trans: Alpha component (return)
|
|
+ * @fb_info: Frame Buffer structure
|
|
+ *
|
|
+ * Return != 0 for invalid register number.
|
|
+ *
|
|
+ * Set a single color register to the driver's color palette (HW for 256 color
|
|
+ * mode, SW otherwise) according to colors/transparent components supplied.
|
|
+ * The values supplied are already rounded down to the hardware's capabilities
|
|
+ * (according to the entries in the var structure)
|
|
+ *
|
|
+ * sisfb_set_cmap() uses this function to set the colormap.
|
|
+ */
|
|
+static int
|
|
+sisfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
|
|
+ unsigned transp, struct fb_info * info)
|
|
+{
|
|
+ struct sisfb_info * sisfb = (struct sisfb_info *) info;
|
|
+ struct sisfb_par * par = (struct sisfb_par *) &sisfb->current_par;
|
|
+
|
|
+ if (regno >= par->cmap_len)
|
|
+ return 1;
|
|
+
|
|
+ red >>= 8;
|
|
+ green >>= 8;
|
|
+ blue >>= 8;
|
|
+
|
|
+ sisfb->palette[regno].red = red;
|
|
+ sisfb->palette[regno].green = green;
|
|
+ sisfb->palette[regno].blue = blue;
|
|
+
|
|
+ switch (par->bits_per_pixel) {
|
|
+#ifdef FBCON_HAS_CFB8
|
|
+ case 8:
|
|
+ sisfb_set_dac_index(sisfb, regno);
|
|
+ sisfb_set_dac_rgb(sisfb, red >> 2, green >> 2, blue >> 2);
|
|
+ break;
|
|
+#endif
|
|
+#ifdef FBCON_HAS_CFB16
|
|
+ case 15:
|
|
+ case 16:
|
|
+ sisfb->fbcon_cmap.cfb16[regno] =
|
|
+ ((red & 0xF8) << 8) |
|
|
+ ((green & 0xFC) << 3) |
|
|
+ ((blue & 0xF8) >> 3);
|
|
+ break;
|
|
+#endif
|
|
+#ifdef FBCON_HAS_CFB24
|
|
+ case 24:
|
|
+ sisfb->fbcon_cmap.cfb24[regno] = (red << 16) | (green << 8) | (blue);
|
|
+ break;
|
|
+#endif
|
|
+#ifdef FBCON_HAS_CFB32
|
|
+ case 32:
|
|
+ sisfb->fbcon_cmap.cfb32[regno] = (red << 16) | (green << 8) | (blue);
|
|
+ break;
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Switch Console (called by fbcon.c)
|
|
+ */
|
|
+static int sisfb_switch_con(int con, struct fb_info * info)
|
|
+{
|
|
+ struct sisfb_info * sisfb = (struct sisfb_info *) info;
|
|
+ struct sisfb_par par;
|
|
+
|
|
+ /* unlock VGA registers, XFree86 3.3.6 leave the registers locked
|
|
+ * during console switch */
|
|
+ sisfb_unlock_regs(sisfb);
|
|
+
|
|
+ /* backup colormap of current console */
|
|
+ if (fb_display[sisfb->currcon].cmap.len)
|
|
+ fb_get_cmap(&fb_display[sisfb->currcon].cmap,
|
|
+ 1, sisfb_getcolreg, info);
|
|
+
|
|
+ /* same mode, needn't change mode actually */
|
|
+ if (!memcmp(&fb_display[con].var, &fb_display[sisfb->currcon].var,
|
|
+ sizeof(struct fb_var_screeninfo))) {
|
|
+ sisfb->currcon = con;
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ sisfb->currcon = con;
|
|
+
|
|
+ /* convert var to par and det the hardware by set_par */
|
|
+ sisfb_decode_var(&fb_display[con].var, &par, sisfb);
|
|
+ sisfb_set_par(&par, sisfb);
|
|
+
|
|
+ /* set display swtich for current bits_per_pixel */
|
|
+ sisfb_set_dispsw(&fb_display[con], sisfb, fb_display[con].var.bits_per_pixel, 0);
|
|
+
|
|
+ /* Install new colormap */
|
|
+ do_install_cmap(con, info);
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_blank: - Blanking or Unblanking the CRT
|
|
+ * @blank: 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off
|
|
+ * @info: Frame Buffer structure
|
|
+ *
|
|
+ * The documents on bit 6..7 of SR11 and SR1F are ambigiuos. They are either marked as
|
|
+ * "reserved" or conflicts with each other from different sources. It is possible that
|
|
+ * all these bits are "wired" altogether in the actual circuit implementation (HW bug ??).
|
|
+ * And the VGA guys just choose one to document/program on their own will.
|
|
+ *
|
|
+ * XFree86 3.3.6 programs SR11 and CRTC17 but not SR1F
|
|
+ */
|
|
+static void
|
|
+sisfb_blank(int blank, struct fb_info * info)
|
|
+{
|
|
+ u8 display, power, retrace;
|
|
+ struct sisfb_info * sisfb = (struct sisfb_info *) info;
|
|
+
|
|
+ display = sisfb_get_seq_reg(sisfb, 0x01);
|
|
+ power = sisfb_get_seq_reg(sisfb, 0x11);
|
|
+ retrace = sisfb_get_crtc_reg(sisfb, 0x17);
|
|
+
|
|
+ /* Turn the display off */
|
|
+ display |= 0x20;
|
|
+
|
|
+ switch (blank) {
|
|
+ case 0:
|
|
+ /* Turn the display on */
|
|
+ display &= ~0x20;
|
|
+ /* Disable DPMS mode */
|
|
+ power &= ~0xC0;
|
|
+ /* Enable horizontal and vertical retrace signals */
|
|
+ retrace |= 0x80;
|
|
+ break;
|
|
+ case 1:
|
|
+ break;
|
|
+ case 2:
|
|
+ /* Enter DPMS Suspend Mode -> no vsync */
|
|
+ power |= 0x80;
|
|
+ break;
|
|
+ case 3:
|
|
+ /* Enter DPMS standby Mode -> no hsync */
|
|
+ power |= 0x40;
|
|
+ break;
|
|
+ case 4:
|
|
+ default:
|
|
+ /* Enter DPMS Power Off Mode -> no hsync,vsync */
|
|
+ power |= 0xC0;
|
|
+ /* Disable horizontal and vertical retrace signals */
|
|
+ retrace &= ~0x80;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ sisfb_set_seq_reg(sisfb, 0x01, display);
|
|
+ sisfb_set_seq_reg(sisfb, 0x11, power);
|
|
+ sisfb_set_crtc_reg(sisfb, 0x17, retrace);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_get_fix: - Get the Fixed Part of the Display
|
|
+ * @fix: Fixed screen info (return)
|
|
+ * @con: Console number
|
|
+ * @info: Frame Buffer structure
|
|
+ *
|
|
+ * Return the fixed part of the display information in @fix. The struct of @fix is
|
|
+ * defined in linux/fb.h.
|
|
+ *
|
|
+ * This function calls sisfb_encode_fix() to encode @fix from current 'var' or 'par'
|
|
+ */
|
|
+static int
|
|
+sisfb_get_fix(struct fb_fix_screeninfo * fix, int con, struct fb_info * info)
|
|
+{
|
|
+ struct sisfb_info * sisfb = (struct sisfb_info *) info;
|
|
+ struct sisfb_par par;
|
|
+
|
|
+ if (con == -1)
|
|
+ par = sisfb->default_par;
|
|
+ else
|
|
+ sisfb_decode_var(&fb_display[con].var, &par, sisfb);
|
|
+
|
|
+ sisfb_encode_fix(fix, &par, sisfb);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_get_var: - Convert @var to @par
|
|
+ * @var:
|
|
+ * @par:
|
|
+ * @info: Frame Buffer structure
|
|
+ *
|
|
+ * Get the video params out of 'var'. If a value doesn't fit, round it up,
|
|
+ * if it's too big, return -EINVAL.
|
|
+ *
|
|
+ * The driver's default par i.e. sisfb->default_par is convert from default_var
|
|
+ * during initialization. Default_var is either predefined or provided by find_mode().
|
|
+ */
|
|
+static int
|
|
+sisfb_get_var(struct fb_var_screeninfo * var, int con, struct fb_info * info)
|
|
+{
|
|
+ const struct sisfb_info * sisfb = (struct sisfb_info *) info;
|
|
+
|
|
+ if (con == -1)
|
|
+ /* initialization, get the var from default_par */
|
|
+ sisfb_encode_var(var, &sisfb->default_par, sisfb);
|
|
+ else
|
|
+ *var = fb_display[con].var;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set the User Defined Part of the Display
|
|
+ */
|
|
+static int sisfb_set_var(struct fb_var_screeninfo * var, int con, struct fb_info * info)
|
|
+{
|
|
+ struct sisfb_info * sisfb = (struct sisfb_info *) info;
|
|
+ struct sisfb_par par;
|
|
+ struct display * display;
|
|
+ int err, oldbpp;
|
|
+
|
|
+ if (con == -1)
|
|
+ display = &sisfb->disp;
|
|
+ else
|
|
+ display = &fb_display[con];
|
|
+
|
|
+ /* try to convert var to HW register values (par) */
|
|
+ if ((err = sisfb_decode_var(var, &par, sisfb)))
|
|
+ /* HW does not support this display mode */
|
|
+ return err;
|
|
+
|
|
+ /* return current mode to user */
|
|
+ sisfb_encode_var(var, &par, sisfb);
|
|
+
|
|
+ if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
|
|
+ return 0;
|
|
+
|
|
+ oldbpp = display->var.bits_per_pixel;
|
|
+
|
|
+ if (display->var.xres != var->xres ||
|
|
+ display->var.yres != var->yres ||
|
|
+ display->var.xres_virtual != var->xres_virtual ||
|
|
+ display->var.yres_virtual != var->yres_virtual ||
|
|
+ display->var.bits_per_pixel != var->bits_per_pixel ||
|
|
+ display->var.accel_flags != var->accel_flags) {
|
|
+ struct fb_fix_screeninfo fix;
|
|
+
|
|
+ sisfb_encode_fix(&fix, &par, sisfb);
|
|
+
|
|
+ display->screen_base = (char *) sisfb->video_base_virt;
|
|
+ display->visual = fix.visual;
|
|
+ display->type = fix.type;
|
|
+ display->type_aux = fix.type_aux;
|
|
+ display->ypanstep = fix.ypanstep;
|
|
+ display->ywrapstep = fix.ywrapstep;
|
|
+ display->line_length = fix.line_length;
|
|
+ display->next_line = fix.line_length;
|
|
+ display->can_soft_blank = 1;
|
|
+ display->inverse = inverse;
|
|
+ display->var = *var;
|
|
+
|
|
+ sisfb_set_dispsw(display, sisfb, var->bits_per_pixel, 0);
|
|
+
|
|
+ if (info->changevar)
|
|
+ (*info->changevar) (con);
|
|
+ }
|
|
+
|
|
+ /* call sisfb_set_par if the console is visible (foreground) console
|
|
+ or during initialization */
|
|
+ if (!sisfb->info.display_fg || sisfb->info.display_fg->vc_num == con || con < 0)
|
|
+ sisfb_set_par(&par, sisfb);
|
|
+
|
|
+ /* clear OnScreen */
|
|
+ memset((unsigned char *) sisfb->video_base_virt, 0,
|
|
+ par.line_length * var->yres);
|
|
+
|
|
+ /* if we have changed the color depth, install new colormap */
|
|
+ if (oldbpp != var->bits_per_pixel || con < 0) {
|
|
+ if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
|
|
+ return err;
|
|
+ do_install_cmap(con, info);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_get_cmap: - Get the Colormap
|
|
+ * @cmap: Colormap to be get (return)
|
|
+ * @kspc:
|
|
+ * @con: Console number
|
|
+ * @info: Frame Buffer structure
|
|
+ *
|
|
+ * Get the colormap of console @con to @cmap. If the @con is the current console,
|
|
+ * get the colormap from the hardware registers otherwise we return the private
|
|
+ * colormap of the console. If the console does not have a private colormap we
|
|
+ * return the "default colormap".
|
|
+ */
|
|
+static int
|
|
+sisfb_get_cmap(struct fb_cmap * cmap, int kspc, int con, struct fb_info * info)
|
|
+{
|
|
+ struct sisfb_info * sisfb = (struct sisfb_info *) info;
|
|
+
|
|
+ if (con == sisfb->currcon)
|
|
+ /* current console, use getcolreg to get the HW colormap */
|
|
+ return fb_get_cmap(cmap, kspc, sisfb_getcolreg, info);
|
|
+ else if (fb_display[con].cmap.len)
|
|
+ /* non default colormap? */
|
|
+ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
|
|
+ else
|
|
+ fb_copy_cmap(fb_default_cmap(sisfb->current_par.cmap_len), cmap,
|
|
+ kspc ? 0 : 2);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * sisfb_set_cmap: - Set the Colormap
|
|
+ * @cmap: Colormap to be set
|
|
+ * @kspc:
|
|
+ * @con: Console number
|
|
+ * @info: Frame Buffer structure
|
|
+ *
|
|
+ * Set the colormap of console @con to @cmap. If the colormap of console @con is
|
|
+ * not allocated yet, allocate it. If the @con is the current console, really set
|
|
+ * the hardware registers otherwise only set to the private colormap "in memory".
|
|
+ */
|
|
+static int
|
|
+sisfb_set_cmap(struct fb_cmap * cmap, int kspc, int con, struct fb_info * info)
|
|
+{
|
|
+ int err = 0;
|
|
+ struct sisfb_info * sisfb = (struct sisfb_info *) info;
|
|
+
|
|
+ if (fb_display[con].cmap.len == 0) {
|
|
+ /* no colormap allocated, allocate it */
|
|
+ err = fb_alloc_cmap(&fb_display[con].cmap,
|
|
+ sisfb->current_par.cmap_len, 0);
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (con == sisfb->currcon)
|
|
+ /* current console, use setcolreg to set the HW colormap */
|
|
+ err = fb_set_cmap(cmap, kspc, sisfb_setcolreg, info);
|
|
+ else
|
|
+ /* not current console, don't touch the HW, make a copy instead */
|
|
+ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static int sisfb_ioctl(struct inode * inode, struct file * file, unsigned int cmd,
|
|
+ unsigned long arg, int con, struct fb_info * info)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int sisfb_mmap(struct fb_info * info, struct file * file,
|
|
+ struct vm_area_struct * vma)
|
|
+{
|
|
+ struct sisfb_info * sisfb = (struct sisfb_info *) info;
|
|
+ struct fb_var_screeninfo var;
|
|
+ unsigned long start;
|
|
+ unsigned long off;
|
|
+ u32 len;
|
|
+
|
|
+ if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
|
|
+ return -EINVAL;
|
|
+ off = vma->vm_pgoff << PAGE_SHIFT;
|
|
+
|
|
+ /* frame buffer memory */
|
|
+ start = (unsigned long) sisfb->video_base_phy;
|
|
+ len = PAGE_ALIGN((start & ~PAGE_MASK) + sisfb->video_size_virt);
|
|
+
|
|
+ if (off >= len) {
|
|
+ /* memory mapped io */
|
|
+ off -= len;
|
|
+ sisfb_get_var(&var, sisfb->currcon, info);
|
|
+ if (var.accel_flags)
|
|
+ return -EINVAL;
|
|
+ start = (unsigned long) sisfb->mmio_base_phy;
|
|
+ len = PAGE_ALIGN((start & ~PAGE_MASK) + sisfb->mmio_size_phy);
|
|
+ }
|
|
+
|
|
+ start &= PAGE_MASK;
|
|
+ if ((vma->vm_end - vma->vm_start + off) > len)
|
|
+ return -EINVAL;
|
|
+ off += start;
|
|
+ vma->vm_pgoff = off >> PAGE_SHIFT;
|
|
+
|
|
+#if defined(__i386__)
|
|
+ if (boot_cpu_data.x86 > 3)
|
|
+ pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
|
|
+#endif
|
|
+ if (io_remap_page_range(vma->vm_start, off, vma->vm_end - vma->vm_start,
|
|
+ vma->vm_page_prot))
|
|
+ return -EAGAIN;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct fb_ops sisfb_ops = {
|
|
+ owner: THIS_MODULE,
|
|
+ fb_get_fix: sisfb_get_fix,
|
|
+ fb_get_var: sisfb_get_var,
|
|
+ fb_set_var: sisfb_set_var,
|
|
+ fb_get_cmap: sisfb_get_cmap,
|
|
+ fb_set_cmap: sisfb_set_cmap,
|
|
+ fb_ioctl: sisfb_ioctl,
|
|
+ //fb_mmap: sisfb_mmap,
|
|
+};
|
|
+
|
|
+int sisfb_setup(char * options)
|
|
+{
|
|
+ char *this_opt;
|
|
+
|
|
+ if (!options || !*options)
|
|
+ return 0;
|
|
+
|
|
+ for (this_opt = strtok(options, ","); this_opt;
|
|
+ this_opt = strtok(NULL, ",")) {
|
|
+ if (!*this_opt)
|
|
+ continue;
|
|
+
|
|
+ if (!strcmp(this_opt, "inverse")) {
|
|
+ inverse = 1;
|
|
+ fb_invert_cmaps();
|
|
+ } else if (!strncmp(this_opt, "font:", 5)) {
|
|
+ strncpy(fontname, this_opt + 5, 40);
|
|
+ } else {
|
|
+ mode_option = this_opt;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int __devinit
|
|
+sisfb_probe(struct pci_dev * pci_dev, const struct pci_device_id * pci_id)
|
|
+{
|
|
+ unsigned long tsc;
|
|
+ struct sisfb_info * sisfb;
|
|
+ struct fb_var_screeninfo var;
|
|
+
|
|
+ rdtscl(tsc);
|
|
+ printk("sisfb init start TSC = %lu\n", tsc);
|
|
+
|
|
+ if (pci_enable_device(pci_dev))
|
|
+ return -ENODEV;
|
|
+
|
|
+ if ((sisfb = kmalloc(sizeof(struct sisfb_info), GFP_KERNEL)) == NULL)
|
|
+ return -ENOMEM;
|
|
+ memset(sisfb, 0, sizeof(struct sisfb_info));
|
|
+
|
|
+ sisfb->pci_dev = pci_dev;
|
|
+
|
|
+ sisfb->video_base_phy = pci_resource_start(pci_dev, 0);
|
|
+ sisfb->video_size_phy = pci_resource_len(pci_dev, 0);
|
|
+ if (!request_mem_region(sisfb->video_base_phy, sisfb->video_size_phy, "sisfb FB")) {
|
|
+ printk(KERN_ERR "sisfb_lite: cannot reserve frame buffer memory\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ sisfb->mmio_base_phy = pci_resource_start(pci_dev, 1);
|
|
+ sisfb->mmio_size_phy = pci_resource_len(pci_dev, 1);
|
|
+ if (!request_mem_region(sisfb->mmio_base_phy, sisfb->mmio_size_phy, "sisfb MMIO")) {
|
|
+ printk(KERN_ERR "sisfb_lite: cannot reserve MMIO region\n");
|
|
+ release_mem_region(sisfb->video_base_phy, sisfb->video_size_phy);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ sisfb->vga_io_base = pci_resource_start(pci_dev, 2);
|
|
+ sisfb->vga_io_size = pci_resource_len(pci_dev, 2);
|
|
+ if (!request_region(sisfb->vga_io_base, sisfb->vga_io_size, "sisfb IO")) {
|
|
+ printk(KERN_ERR "sisfb_lite: cannot reserve I/O ports\n");
|
|
+ release_mem_region(sisfb->video_base_phy, sisfb->video_size_phy);
|
|
+ release_mem_region(sisfb->mmio_base_phy, sisfb->mmio_size_phy);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ sisfb->vga_io_base += 0x30;
|
|
+
|
|
+ sisfb_unlock_regs(sisfb);
|
|
+ sisfb_config_memory(sisfb);
|
|
+ sisfb_init_legacy_vga(sisfb);
|
|
+ sisfb_init_300(sisfb);
|
|
+
|
|
+ strcpy(sisfb->info.modename, "SiSFB Lite");
|
|
+
|
|
+ sisfb->info.changevar = NULL;
|
|
+ sisfb->info.node = -1;
|
|
+ sisfb->info.fbops = &sisfb_ops;
|
|
+ sisfb->info.disp = &sisfb->disp;
|
|
+ strcpy(sisfb->info.fontname, fontname);
|
|
+ sisfb->info.switch_con = &sisfb_switch_con;
|
|
+ sisfb->info.updatevar = NULL;
|
|
+ sisfb->info.blank = &sisfb_blank;
|
|
+ sisfb->info.flags = FBINFO_FLAG_DEFAULT;
|
|
+
|
|
+ sisfb->currcon = -1;
|
|
+
|
|
+ memset(&var, 0, sizeof(var));
|
|
+ if (fb_find_mode(&var, &sisfb->info, mode_option, NULL, 0, &sisfb_default_mode, 8) == 0)
|
|
+ var = default_var;
|
|
+
|
|
+ /* get the default_par and make it as our current_par */
|
|
+ sisfb_decode_var(&var, &sisfb->default_par, sisfb);
|
|
+ sisfb->current_par = sisfb->default_par;
|
|
+ sisfb->disp.var = var;
|
|
+
|
|
+#if 0
|
|
+ /* FixME: Not neceressary ?? */
|
|
+ if (sisfb_set_var(&var, -1, &sisfb->info)) {
|
|
+ printk(KERN_EMERG "sisfb_lite: can't set default video mode\n");
|
|
+ return -ENXIO;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ sisfb->currcon = 0;
|
|
+
|
|
+ if (register_framebuffer((struct fb_info *) sisfb) < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* make sisfb a driver_data of the PCI device */
|
|
+ pci_dev->driver_data = sisfb;
|
|
+
|
|
+ rdtscl(tsc);
|
|
+ printk("sisfb init end TSC = %lu\n", tsc);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static void __devexit
|
|
+sisfb_remove(struct pci_dev * pci_dev)
|
|
+{
|
|
+ struct sisfb_info * sisfb = (struct sisfb_info *) pci_dev->driver_data;
|
|
+
|
|
+ unregister_framebuffer((struct fb_info *) sisfb);
|
|
+
|
|
+ release_mem_region(sisfb->video_base_phy, sisfb->video_size_phy);
|
|
+ release_mem_region(sisfb->mmio_base_phy, sisfb->mmio_size_phy);
|
|
+ release_region(sisfb->vga_io_base, sisfb->vga_io_size);
|
|
+
|
|
+#ifdef CONFIG_MTRR
|
|
+ mtrr_del(sisfb->mtrr, sisfb->video_base_phy, sisfb->video_size_virt);
|
|
+#endif /* CONFIG_MTRR */
|
|
+
|
|
+ kfree(sisfb);
|
|
+}
|
|
+
|
|
+#define SISFB_MODULE_NAME "sisfb_lite"
|
|
+
|
|
+static struct pci_driver sisfb_pci_driver = {
|
|
+ name: SISFB_MODULE_NAME,
|
|
+ id_table: sisfb_pci_tbl,
|
|
+ probe: sisfb_probe,
|
|
+ remove: sisfb_remove,
|
|
+};
|
|
+
|
|
+static const char * version = "$Id$";
|
|
+
|
|
+int __init sisfb_init(void)
|
|
+{
|
|
+ printk(KERN_INFO "sisfb_lite: %s\n", version);
|
|
+
|
|
+ return pci_module_init(&sisfb_pci_driver);
|
|
+}
|
|
+
|
|
+static void __exit sisfb_cleanup(void)
|
|
+{
|
|
+ pci_unregister_driver(&sisfb_pci_driver);
|
|
+}
|
|
+
|
|
+#ifdef MODULE
|
|
+module_init(sisfb_init);
|
|
+#endif /* MODULE */
|
|
+
|
|
+module_exit(sisfb_cleanup);
|
|
diff -urN linux-2.4.0-official/drivers/video/sis/sisfb_lite.h linux-2.4.0-linuxbios/drivers/video/sis/sisfb_lite.h
|
|
--- linux-2.4.0-official/drivers/video/sis/sisfb_lite.h Thu Jan 1 08:00:00 1970
|
|
+++ linux-2.4.0-linuxbios/drivers/video/sis/sisfb_lite.h Mon Jan 15 10:49:34 2001
|
|
@@ -0,0 +1,126 @@
|
|
+#ifndef __SISFB_LITE__
|
|
+#define __SISFB_LITE__
|
|
+
|
|
+#define NR_PALETTE 256
|
|
+
|
|
+struct sisfb_par {
|
|
+ unsigned htotal;
|
|
+ unsigned hdispend;
|
|
+ unsigned hsyncstart;
|
|
+ unsigned hsyncend;
|
|
+ unsigned hblankstart;
|
|
+ unsigned hblankend;
|
|
+
|
|
+ unsigned vtotal;
|
|
+ unsigned vdispend;
|
|
+ unsigned vsyncstart;
|
|
+ unsigned vsyncend;
|
|
+ unsigned vblankstart;
|
|
+ unsigned vblankend;
|
|
+
|
|
+ unsigned long dot_clock;
|
|
+ unsigned fifo_threshold;
|
|
+
|
|
+ unsigned bits_per_pixel;
|
|
+ unsigned cmap_len;
|
|
+
|
|
+ unsigned line_length;
|
|
+ unsigned sync_mode;
|
|
+};
|
|
+
|
|
+struct sisfb_info {
|
|
+ struct fb_info info;
|
|
+ struct pci_dev *pci_dev;
|
|
+
|
|
+ unsigned long video_size_phy;
|
|
+ unsigned long video_base_phy;
|
|
+ unsigned long video_size_virt;
|
|
+ unsigned long video_base_virt;
|
|
+
|
|
+ unsigned long mmio_size_phy;
|
|
+ unsigned long mmio_base_phy;
|
|
+ unsigned long mmio_base_virt;
|
|
+
|
|
+ unsigned vga_io_base;
|
|
+ unsigned vga_io_size;
|
|
+
|
|
+ unsigned mtrr;
|
|
+
|
|
+ struct sisfb_par default_par;
|
|
+ struct sisfb_par current_par;
|
|
+
|
|
+ struct display disp;
|
|
+ unsigned currcon;
|
|
+
|
|
+ struct {
|
|
+ u8 blue, green, red, pad;
|
|
+ } palette[NR_PALETTE];
|
|
+
|
|
+ /* These ifdefs must be last! They differ for module & non-module compiles */
|
|
+#if defined(FBCON_HAS_CFB16) || defined(FBCON_HAS_CFB24) || defined(FBCON_HAS_CFB32)
|
|
+ union {
|
|
+#ifdef FBCON_HAS_CFB16
|
|
+ u16 cfb16[16];
|
|
+#endif /* FBCON_HAS_CFB16 */
|
|
+#ifdef FBCON_HAS_CFB24
|
|
+ u32 cfb24[16];
|
|
+#endif /* FBCON_HAS_CFB24 */
|
|
+#ifdef FBCON_HAS_CFB32
|
|
+ u32 cfb32[16];
|
|
+#endif /* FBCON_HAS_CFB32 */
|
|
+ } fbcon_cmap;
|
|
+#endif /* defined(FBCON_HAS_CFB16) || defined(FBCON_HAS_CFB24) || defined(FBCON_HAS_CFB32) */
|
|
+};
|
|
+
|
|
+/* VGA register Offsets */
|
|
+#define ATTR_WRITE 0x10
|
|
+#define ATTR_READ 0x11
|
|
+#define MISC_OUT_W 0x12
|
|
+#define SEQ_ADDR 0x14
|
|
+#define SEQ_DATA 0x15
|
|
+#define PEL_MASK 0x16
|
|
+#define DACR_ADDR 0x17
|
|
+#define DACW_ADDR 0x18
|
|
+#define DAC_DATA 0x19
|
|
+#define MISC_OUT_R 0x1C
|
|
+#define GRC_ADDR 0x1E
|
|
+#define GRC_DATA 0x1F
|
|
+#define CRTC_ADDR 0x24
|
|
+#define CRTC_DATA 0x25
|
|
+
|
|
+#define FEATURE_READ 0x1A
|
|
+#define FEATURE_WRITE 0x2A
|
|
+
|
|
+#define SIS_PASSWD 0x05
|
|
+#define SIS_PASSWD_LOCK 0x21
|
|
+#define SIS_PASSWD_UNLOCK 0x86
|
|
+
|
|
+#define SIS300_PCI_ADDR_SET 0x20
|
|
+#define SIS300_PCI_MMIO_ENABLE 0x01
|
|
+#define SIS300_PCI_IO_ENABLE 0x20
|
|
+#define SIS300_PCI_LINEAR_ENABLE 0x80
|
|
+
|
|
+/* ------------------- Constant Definitions ------------------------- */
|
|
+
|
|
+#define FALSE 0
|
|
+#define TRUE 1
|
|
+
|
|
+/* capabilities */
|
|
+#define TURBO_QUEUE_CAP 0x80
|
|
+#define HW_CURSOR_CAP 0x40
|
|
+
|
|
+#define RESERVED_MEM_SIZE_4M 0x400000 /* 4M */
|
|
+#define RESERVED_MEM_SIZE_8M 0x800000 /* 8M */
|
|
+
|
|
+#define TURBO_QUEUE_AREA_SIZE 0x80000 /* 512K */
|
|
+#define HW_CURSOR_AREA_SIZE 0x1000 /* 4K */
|
|
+
|
|
+/* -------------------- Macro definitions --------------------------- */
|
|
+
|
|
+#ifdef DEBUG
|
|
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
|
|
+#else
|
|
+#define DPRINTK(fmt, args...)
|
|
+#endif
|
|
+
|
|
+#endif /* __SISFB_LITE__ */
|
|
diff -urN linux-2.4.0-official/include/linux/pc_keyb.h linux-2.4.0-linuxbios/include/linux/pc_keyb.h
|
|
--- linux-2.4.0-official/include/linux/pc_keyb.h Tue Oct 12 01:15:40 1999
|
|
+++ linux-2.4.0-linuxbios/include/linux/pc_keyb.h Mon Jan 15 14:43:47 2001
|
|
@@ -14,7 +14,7 @@
|
|
#define KBD_REPORT_UNKN /* Report unknown scan codes */
|
|
#define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */
|
|
#undef KBD_IS_FOCUS_9000 /* We have the brain-damaged FOCUS-9000 keyboard */
|
|
-#undef INITIALIZE_MOUSE /* Define if your PS/2 mouse needs initialization. */
|
|
+#define INITIALIZE_MOUSE /* Define if your PS/2 mouse needs initialization. */
|
|
|
|
|
|
|