[PATCH/RFC]: SGI Octane (IP30) Patches, Part two, Octane core

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




The second part is the actual IP30 Patch that makes these beasts boot. Assuming you've already lit incense candles and sacrificed a PC to the MIPS Gods above.

There's one change that probably needs good scrutiny, as it changes a value in dma-default.c, and this'll affect other systems:

diff -Naurp linux-2.6.26.orig/arch/mips/mm/dma-default.c linux-2.6.26/arch/mips/mm/dma-default.c --- linux-2.6.26.orig/arch/mips/mm/dma-default.c 2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/arch/mips/mm/dma-default.c     2008-07-25 03:14:40.000000000 -0400
@@ -209,7 +209,7 @@ dma_addr_t dma_map_page(struct device *d
                dma_cache_wback_inv(addr, size);
        }

-       return plat_map_dma_mem_page(dev, page) + offset;
+       return plat_map_dma_mem_page(dev, page, size) + offset;
 }


It's used because IP30's implementation of plat_map_dma_mem_page passes the 'size' var to a few functions, ultimately leading to IP30's version of pdev_to_baddr, where it checks to make sure we're not above a certain memory limit (2GB) that could cause problems with DMA & PCI (I think - it's been awhile).

That's just one example, though. There's probably more, but I've mostly done forward ports, and haven't really messed with re-writing much. Hence why I'd like to ask others to look, poke, prod, compile, and boot, and see if they have other suggestions for improving and fixing this up.

Thanks!,


--Kumba

--
Unofficial Gentoo/MIPS Hermit & Kernel Monkey

"The past tempts us, the present confuses us, the future frightens us. And our lives slip away, moment by moment, lost in that vast, terrible in-between."

--Emperor Turhan, Centauri Republic
diff -Naurp linux-2.6.26.orig/arch/mips/Kconfig linux-2.6.26/arch/mips/Kconfig
--- linux-2.6.26.orig/arch/mips/Kconfig	2008-07-25 01:03:02.000000000 -0400
+++ linux-2.6.26/arch/mips/Kconfig	2008-07-25 03:15:09.000000000 -0400
@@ -449,6 +449,31 @@ config SGI_IP28
         This is the SGI Indigo2 with R10000 processor.  To compile a Linux
         kernel that runs on these, say Y here.
 
+config SGI_IP30
+	bool "SGI IP30 (Octane/Octane2) (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	select ARC
+	select ARC64
+	select BOOT_ELF64
+        select CEVT_R4K
+        select CSRC_R4K
+	select DMA_IP30
+	select DMA_COHERENT
+	select GENERIC_ISA_DMA
+	select HW_HAS_PCI
+        select IRQ_CPU
+	select NR_CPUS_DEFAULT_2
+	select PCI_DOMAINS
+	select SYS_HAS_CPU_R10000
+	select SYS_SUPPORTS_64BIT_KERNEL
+	select SYS_SUPPORTS_BIG_ENDIAN
+	select SYS_SUPPORTS_SMP
+        select ARC_MEMORY
+        select ARC_PROMLIB
+	help
+	  This are the SGI Octane and Octane2 graphics workstations.  To
+	  compile a Linux kernel that runs on these, say Y here.
+
 config SGI_IP32
 	bool "SGI IP32 (O2)"
 	select ARC
@@ -812,6 +837,9 @@ config DMA_COHERENT
 config DMA_IP27
 	bool
 
+config DMA_IP30
+	bool
+
 config DMA_NONCOHERENT
 	bool
 	select DMA_NEED_PCI_MAP_STATE
@@ -1006,7 +1034,7 @@ config BOOT_ELF32
 config MIPS_L1_CACHE_SHIFT
 	int
 	default "4" if MACH_DECSTATION
-	default "7" if SGI_IP22 || SGI_IP27 || SGI_IP28 || SNI_RM
+	default "7" if SGI_IP22 || SGI_IP27 || SGI_IP28 || SGI_IP30 || SNI_RM
 	default "4" if PMC_MSP4200_EVAL
 	default "5"
 
@@ -1019,12 +1047,12 @@ config ARC_CONSOLE
 
 config ARC_MEMORY
 	bool
-	depends on MACH_JAZZ || SNI_RM || SGI_IP32
+	depends on MACH_JAZZ || SNI_RM || SGI_IP30 || SGI_IP32
 	default y
 
 config ARC_PROMLIB
 	bool
-	depends on MACH_JAZZ || SNI_RM || SGI_IP22 || SGI_IP28 || SGI_IP32
+	depends on MACH_JAZZ || SNI_RM || SGI_IP22 || SGI_IP28 || SGI_IP30 || SGI_IP32
 	default y
 
 config ARC64
diff -Naurp linux-2.6.26.orig/arch/mips/Makefile linux-2.6.26/arch/mips/Makefile
--- linux-2.6.26.orig/arch/mips/Makefile	2008-07-23 22:26:29.000000000 -0400
+++ linux-2.6.26/arch/mips/Makefile	2008-07-25 03:14:40.000000000 -0400
@@ -492,6 +492,15 @@ cflags-$(CONFIG_SGI_IP28)	+= -mr10k-cach
 load-$(CONFIG_SGI_IP28)		+= 0xa800000020004000
 
 #
+# SGI-IP30 (Octane/Octane2)
+#
+ifdef CONFIG_SGI_IP30
+core-$(CONFIG_SGI_IP30)		+= arch/mips/sgi-ip30/
+cflags-$(CONFIG_SGI_IP30)	+= -Iinclude/asm-mips/mach-ip30
+load-$(CONFIG_SGI_IP30)		+= 0xa800000020004000
+endif
+
+#
 # SGI-IP32 (O2)
 #
 # Set the load address to >= 80069000 if you want to leave space for symmon,
diff -Naurp linux-2.6.26.orig/arch/mips/fw/arc/init.c linux-2.6.26/arch/mips/fw/arc/init.c
--- linux-2.6.26.orig/arch/mips/fw/arc/init.c	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/arch/mips/fw/arc/init.c	2008-07-25 03:14:40.000000000 -0400
@@ -56,4 +56,11 @@ void __init prom_init(void)
 		register_smp_ops(&ip27_smp_ops);
 	}
 #endif
+#ifdef CONFIG_SGI_IP30
+	{
+		extern struct plat_smp_ops ip30_smp_ops;
+
+		register_smp_ops(&ip30_smp_ops);
+	}
+#endif
 }
diff -Naurp linux-2.6.26.orig/arch/mips/kernel/cevt-r4k.c linux-2.6.26/arch/mips/kernel/cevt-r4k.c
--- linux-2.6.26.orig/arch/mips/kernel/cevt-r4k.c	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/arch/mips/kernel/cevt-r4k.c	2008-07-25 03:14:40.000000000 -0400
@@ -251,7 +251,6 @@ int __cpuinit mips_clockevent_init(void)
 	irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq;
 	if (get_c0_compare_int)
 		irq = get_c0_compare_int();
-
 	cd = &per_cpu(mips_clockevent_device, cpu);
 
 	cd->name		= "MIPS";
diff -Naurp linux-2.6.26.orig/arch/mips/kernel/setup.c linux-2.6.26/arch/mips/kernel/setup.c
--- linux-2.6.26.orig/arch/mips/kernel/setup.c	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/arch/mips/kernel/setup.c	2008-07-25 03:14:40.000000000 -0400
@@ -481,6 +481,10 @@ static void __init arch_mem_init(char **
 	printk("Determined physical RAM map:\n");
 	print_memory_map();
 
+#ifdef CONFIG_CMDLINE
+	if (strlen(CONFIG_CMDLINE))
+		strlcpy(arcs_cmdline, CONFIG_CMDLINE, sizeof(command_line));
+#endif
 	strlcpy(command_line, arcs_cmdline, sizeof(command_line));
 	strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
 
@@ -575,7 +579,6 @@ void __init setup_arch(char **cmdline_p)
 #endif
 
 	arch_mem_init(cmdline_p);
-
 	resource_init();
 	plat_smp_setup();
 }
diff -Naurp linux-2.6.26.orig/arch/mips/mm/dma-default.c linux-2.6.26/arch/mips/mm/dma-default.c
--- linux-2.6.26.orig/arch/mips/mm/dma-default.c	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/arch/mips/mm/dma-default.c	2008-07-25 03:14:40.000000000 -0400
@@ -209,7 +209,7 @@ dma_addr_t dma_map_page(struct device *d
 		dma_cache_wback_inv(addr, size);
 	}
 
-	return plat_map_dma_mem_page(dev, page) + offset;
+	return plat_map_dma_mem_page(dev, page, size) + offset;
 }
 
 EXPORT_SYMBOL(dma_map_page);
diff -Naurp linux-2.6.26.orig/arch/mips/pci/Makefile linux-2.6.26/arch/mips/pci/Makefile
--- linux-2.6.26.orig/arch/mips/pci/Makefile	2008-07-25 01:03:02.000000000 -0400
+++ linux-2.6.26/arch/mips/pci/Makefile	2008-07-25 03:14:40.000000000 -0400
@@ -34,6 +34,7 @@ obj-$(CONFIG_PMC_MSP7120_FPGA)	+= fixup-
 obj-$(CONFIG_PMC_YOSEMITE)	+= fixup-yosemite.o ops-titan.o ops-titan-ht.o \
 				   pci-yosemite.o
 obj-$(CONFIG_SGI_IP27)		+= ops-bridge.o pci-ip27.o
+obj-$(CONFIG_SGI_IP30)		+= ops-bridge.o pci-ip30.o
 obj-$(CONFIG_SGI_IP32)		+= fixup-ip32.o ops-mace.o pci-ip32.o
 obj-$(CONFIG_SIBYTE_SB1250)	+= fixup-sb1250.o pci-sb1250.o
 obj-$(CONFIG_SIBYTE_BCM112X)	+= fixup-sb1250.o pci-sb1250.o
diff -Naurp linux-2.6.26.orig/arch/mips/pci/ops-bridge.c linux-2.6.26/arch/mips/pci/ops-bridge.c
--- linux-2.6.26.orig/arch/mips/pci/ops-bridge.c	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/arch/mips/pci/ops-bridge.c	2008-07-25 03:14:40.000000000 -0400
@@ -9,9 +9,15 @@
 #include <linux/pci.h>
 #include <asm/paccess.h>
 #include <asm/pci/bridge.h>
+
+#ifdef CONFIG_SGI_IP30
+#include <asm/mach-ip30/addrs.h>
+#include <asm/mach-ip30/pcibr.h>
+#else
 #include <asm/sn/arch.h>
 #include <asm/sn/intr.h>
 #include <asm/sn/sn0/hub.h>
+#endif
 
 /*
  * Most of the IOC3 PCI config register aren't present
diff -Naurp linux-2.6.26.orig/arch/mips/pci/pci-ip30.c linux-2.6.26/arch/mips/pci/pci-ip30.c
--- linux-2.6.26.orig/arch/mips/pci/pci-ip30.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/pci/pci-ip30.c	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,321 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek (skylark@xxxxxxxxxxxxxx)
+ * Based on pci-ip27.c by
+ *  Copyright (C) 2003 Christoph Hellwig (hch@xxxxxx)
+ *  Copyright (C) 1999, 2000, 04 Ralf Baechle (ralf@xxxxxxxxxxxxxx)
+ *  Copyright (C) 1999, 2000 Silicon Graphics, Inc.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include <asm/mach-ip30/addrs.h>
+#include <asm/mach-ip30/pcibr.h>
+#include <asm/pci/bridge.h>
+#include <asm/paccess.h>
+
+extern unsigned int allocate_irqno(void);
+
+/*
+ * XXX: No kmalloc available when we do our crosstalk scan,
+ * 	we should try to move it later in the boot process.
+ */
+static struct bridge_controller bridges[PCIBR_MAX_PCI_BUSSES];
+
+/* Translate from irq to software PCI bus number and PCI slot. */
+struct bridge_controller *irq_to_bridge[PCIBR_MAX_PCI_BUSSES * PCIBR_MAX_DEV_PCIBUS];
+int irq_to_slot[PCIBR_MAX_PCI_BUSSES * PCIBR_MAX_DEV_PCIBUS];
+bridge_t *ip30_irq_bridge[PCIBR_MAX_PCI_BUSSES * PCIBR_MAX_DEV_PCIBUS] = {NULL};
+unsigned int ip30_irq_in_bridge[PCIBR_MAX_PCI_BUSSES * PCIBR_MAX_DEV_PCIBUS] = {0};
+
+extern struct pci_ops bridge_pci_ops;
+
+unsigned int ip30_bridge_count = 0;
+unsigned int ip30_irq_assigned = PCIBR_IRQ_BASE;
+
+
+/* OK, spikey dildo time */
+#define AT_FAIL	0
+#define AT_D32	1
+#define AT_D64	2
+#define AT_DIO	3
+#define AT_WIN	4
+
+static char *at_names[] = {"failed", "direct 32-bit", "direct 64-bit",
+			   "direct I/O", "window"};
+
+static unsigned int align(unsigned int ptr, unsigned int size)
+{
+	return (ptr + size - 1) & ~(size - 1);
+}
+
+static inline unsigned int win_size(int n)
+{
+	return (n < 2)? 0x200000 : 0x100000;
+}
+
+static inline unsigned int win_base(int n)
+{
+	return (n < 3) ? (0x200000 * (n + 1)) : (0x100000 * (n + 4));
+}
+
+static int startup_resource(struct pci_controller *hose, struct pci_dev *dev, int res)
+{
+	struct bridge_controller *bc = (struct bridge_controller *)hose;
+	struct resource *rs = &dev->resource[res];
+	bridge_t *bvma = (bridge_t *)bc->base;
+	int slot = PCI_SLOT(dev->devfn);
+	int is_be = bc->slot_be[slot];
+	int is_io = !!(rs->flags & IORESOURCE_IO);
+	unsigned int size = rs->end - rs->start + 1;
+	int at = AT_FAIL;
+	unsigned int base = 0;
+	unsigned long vma = 0;
+	unsigned int devio;
+	int i, j;
+
+	/* check for nonexistant resources */
+	if (size < 2)
+		return 0;
+
+	/* try direct mappings first */
+	if (!is_io && !is_be) {
+		base = align(bc->d32_p, size);
+		vma = base + BRIDGE_PCI_MEM32_BASE;
+		bc->d32_p = base + size;
+		at = AT_D32;
+	}
+	if (is_io && !is_be && bc->bridge_rev >= BRIDGE_REV_D) {
+		base = align(bc->dio_p, size);
+		vma = base + BRIDGE_PCI_IO_BASE;
+		bc->dio_p = base + size;
+		at = AT_DIO;
+	}
+
+	/* OK, that failed, try finding a compatible DevIO */
+	if (at == AT_FAIL)
+		for (j = 0; j < 8; j++) {
+			i = (j + slot) & 7;
+			if (bc->win_p[i] && bc->win_io[i] == is_io && bc->win_be[i] == is_be)
+				if (align(bc->win_p[i], size) + size <= win_size(i)) {
+					base = align(bc->win_p[i], size);
+					bc->win_p[i] = base + size;
+					base += win_base(i);
+					vma = base;
+					at = AT_WIN;
+					break;
+				}
+		}
+
+	/* if everything else fails, allocate a new DevIO */
+	if (at == AT_FAIL)
+		for (j = 0; j < 8; j++) {
+			i = (j + slot) & 7;
+			if (!bc->win_p[i] && size <= win_size(i)) {
+				bc->win_p[i] = size;
+				bc->win_io[i] = is_io;
+				bc->win_be[i] = is_be;
+				base = win_base(i);
+				vma = base;
+				at = AT_WIN;
+				/* set the DevIO params */
+				devio = bvma->b_device[i].reg;
+				if (is_be)
+					devio |= BRIDGE_DEV_DEV_SWAP;
+				else
+					devio &= ~BRIDGE_DEV_DEV_SWAP;
+				if (is_io)
+					devio &= ~BRIDGE_DEV_DEV_IO_MEM;
+				else
+					devio |= BRIDGE_DEV_DEV_IO_MEM;
+				devio &= ~BRIDGE_DEV_OFF_MASK;
+				devio |= win_base(i) >> BRIDGE_DEV_OFF_ADDR_SHFT;
+				bvma->b_device[i].reg = devio;
+				break;
+			}
+		}
+
+	/* get real VMA */
+	if (vma < PCIBR_OFFSET_END)
+		vma += NODE_SWIN_BASE(bc->nasid, bc->widget_id);
+	else
+		vma += NODE_BWIN_BASE(bc->nasid, bc->widget_id);
+
+	/* dump useless info to console */
+	if (at != AT_FAIL)
+		printk(KERN_INFO "BRIDGE: %s #%d, size 0x%x for %s-endian %s --> %s at bus 0x%08x vma 0x%016lx\n",
+					is_io ? "IO" : "Memory", res, size,
+					is_be ? "big" : "little", pci_name(dev),
+					at_names[at], base, vma);
+	else
+		printk(KERN_INFO "BRIDGE: %s #%d, size 0x%x for %s-endian %s --> %s\n",
+					is_io ? "IO" : "Memory", res, size,
+					is_be ? "big" : "little", pci_name(dev),
+					at_names[at]);
+
+	if (at == AT_FAIL)
+		return -ENOMEM;
+
+	/* set the device resource to the new address */
+	rs->start = vma;
+	rs->end = vma + size - 1;
+	pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (4 * res), &devio);
+	devio &= 15;
+	devio |= base & ~15;
+	pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + (4 * res), devio);
+	
+	return 0;
+}
+
+int __init bridge_probe(nasid_t nasid, int widget_id, int masterwid)
+{
+	struct bridge_controller *bc;
+	static int num_bridges = 0;
+	bridge_t *bridge;
+	int i;
+
+	printk(KERN_INFO "BRIDGE chip at xtalk:%d, initializing...\n", widget_id);
+
+	/* XXX: kludge alert.. */
+	if (!num_bridges)
+		ioport_resource.end = ~0UL;
+
+	bc = &bridges[num_bridges];
+
+	bc->pc.pre_enable	= startup_resource;
+
+	bc->pc.pci_ops		= &bridge_pci_ops;
+	bc->pc.mem_resource	= &bc->mem;
+	bc->pc.io_resource	= &bc->io;
+
+	bc->pc.index		= num_bridges;
+
+	bc->mem.name		= "Bridge PCI MEM";
+	bc->mem.start		= NODE_SWIN_BASE(0, widget_id) + PCIBR_OFFSET_MEM;
+	bc->mem.end		= NODE_SWIN_BASE(0, widget_id) + PCIBR_OFFSET_IO - 1;
+	bc->pc.mem_offset	= NODE_SWIN_BASE(0, widget_id);
+	bc->mem.flags		= IORESOURCE_MEM;
+
+	bc->io.name		= "Bridge IO MEM";
+	bc->io.start		= NODE_SWIN_BASE(0, widget_id) + PCIBR_OFFSET_IO;
+	bc->io.end		= NODE_SWIN_BASE(0, widget_id) + PCIBR_OFFSET_END - 1;
+	bc->pc.io_offset	= NODE_SWIN_BASE(0, widget_id);
+	bc->io.flags		= IORESOURCE_IO;
+
+	bc->irq_cpu = smp_processor_id();
+	bc->widget_id = widget_id;
+	bc->nasid = nasid;
+
+	/* set direct allocation base */
+	bc->dio_p = PCIBR_DIR_ALLOC_BASE;
+	bc->d32_p = PCIBR_DIR_ALLOC_BASE;
+
+	bc->baddr = (u64)masterwid << 60;
+	bc->baddr |= (1UL << 56);	/* Barrier set */
+
+	/* point to this bridge */
+	bridge = (bridge_t *) RAW_NODE_SWIN_BASE(nasid, widget_id);
+
+	bc->bridge_rev		= bridge->b_wid_id >> 28;
+
+	/* Clear all pending interrupts. */
+	bridge->b_int_rst_stat = BRIDGE_IRR_ALL_CLR;
+
+	/* Until otherwise set up, assume all interrupts are from slot 0 */
+	bridge->b_int_device = 0x0;
+
+	/* Fix the initial b_device configuration. */
+	bridge->b_wid_control &= ~(BRIDGE_CTRL_IO_SWAP | BRIDGE_CTRL_MEM_SWAP);
+
+	for (i = 0; i < 8; i++)
+		bridge->b_device[i].reg = BRIDGE_DEV_ERR_LOCK_EN | BRIDGE_DEV_VIRTUAL_EN |
+					  BRIDGE_DEV_PMU_WRGA_EN | BRIDGE_DEV_DIR_WRGA_EN |
+					  BRIDGE_DEV_SWAP_PMU | BRIDGE_DEV_SWAP_DIR | 
+					  BRIDGE_DEV_COH;
+
+	/* Configure direct-mapped DMA */
+	bridge->b_dir_map = (masterwid << BRIDGE_DIRMAP_W_ID_SHFT) | BRIDGE_DIRMAP_ADD512;
+
+	/*
+	 * Allocate the RRBs randomly.
+	 *
+	 * No, I'm joking :)
+	 * These are occult numbers of the Black Priesthood of Ancient Mu.
+	 */
+	bridge->b_even_resp = PCIBR_ANCIENT_MU_EVEN_RESP;
+	bridge->b_odd_resp = PCIBR_ANCIENT_MU_ODD_RESP;
+
+	/*
+	 * Route all PCI bridge interrupts to the HEART ASIC. The idea is
+	 * that we cause the bridge to send an Xtalk write to a specified
+	 * interrupt register (0x80 for HEART, 0x90 for HUB) in a defined
+	 * widget. The actual IRQ support and masking is done elsewhere.
+	 */
+	bridge->b_wid_int_upper = masterwid << 16;
+	bridge->b_wid_int_lower = PCIBR_XIO_SEES_HEART;
+
+	/* We map the IRQs to slots in a straightforward way. */
+	bc->irq_base = ip30_irq_assigned;
+	for (i = 0; i < 8; i++) {
+		bridge->b_int_addr[i].addr = ip30_irq_assigned;
+		ip30_irq_bridge[ip30_irq_assigned] = bridge;
+		ip30_irq_in_bridge[ip30_irq_assigned] = i;
+		ip30_irq_assigned++;
+	}
+	bridge->b_int_device &= PCIBR_ANCIENT_MU_INT_DEVICE;
+	bridge->b_int_enable = PCIBR_ANCIENT_MU_INT_ENABLE;
+	bridge->b_int_mode = PCIBR_ANCIENT_MU_INT_MODE;
+	ip30_bridge_count++;
+
+	bridge->b_wid_tflush;     /* wait until Bridge PIO complete */
+
+	bc->base = bridge;
+
+	register_pci_controller(&bc->pc);
+
+	num_bridges++;
+
+	return 0;
+}
+
+int __devinit pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+	struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus);
+
+	return (bc->irq_base + slot);
+}
+
+/* Do platform specific device initialization at pci_enable_device() time */
+int pcibios_plat_dev_init(struct pci_dev *dev)
+{
+	return 0;
+}
+
+static inline void pci_disable_swapping_pio(struct pci_dev *dev)
+{
+	struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus);
+
+	bc->slot_be[PCI_SLOT(dev->devfn)] = 1;
+}
+
+static inline void pci_disable_swapping_dma(struct pci_dev *dev)
+{
+	struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus);
+	bridge_t *bvma = (bridge_t *)bc->base;
+	unsigned int devio;
+	int slot = PCI_SLOT(dev->devfn);
+
+	bc->slot_bs[slot] = 1;
+	devio = bvma->b_device[slot].reg;
+	devio &= ~(BRIDGE_DEV_SWAP_PMU | BRIDGE_DEV_SWAP_DIR);
+	bvma->b_device[slot].reg = devio;
+}
+
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3,
+	pci_disable_swapping_dma);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_RAD1,
+	pci_disable_swapping_dma);
diff -Naurp linux-2.6.26.orig/arch/mips/pci/pci.c linux-2.6.26/arch/mips/pci/pci.c
--- linux-2.6.26.orig/arch/mips/pci/pci.c	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/arch/mips/pci/pci.c	2008-07-25 03:14:40.000000000 -0400
@@ -77,6 +77,10 @@ pcibios_align_resource(void *data, struc
 
 void __devinit register_pci_controller(struct pci_controller *hose)
 {
+	if (hose->pre_scan)
+		if(hose->pre_scan(hose) < 0)
+			goto out;
+
 	if (request_resource(&iomem_resource, hose->mem_resource) < 0)
 		goto out;
 	if (request_resource(&ioport_resource, hose->io_resource) < 0) {
@@ -145,6 +149,10 @@ static int __init pcibios_init(void)
 		hose->need_domain_info = need_domain_info;
 		if (bus) {
 			next_busno = bus->subordinate + 1;
+ 
+ 			if (hose->post_scan)
+ 				hose->post_scan(hose, bus);
+
 			/* Don't allow 8-bit bus number overflow inside the hose -
 			   reserve some space for bridges. */
 			if (next_busno > 224) {
@@ -168,6 +176,7 @@ static int pcibios_enable_resources(stru
 	u16 cmd, old_cmd;
 	int idx;
 	struct resource *r;
+	struct pci_controller *hose = (struct pci_controller *)dev->sysdata;
 
 	pci_read_config_word(dev, PCI_COMMAND, &cmd);
 	old_cmd = cmd;
@@ -176,6 +185,10 @@ static int pcibios_enable_resources(stru
 		if (!(mask & (1<<idx)))
 			continue;
 
+		if(hose->pre_enable)
+			if(hose->pre_enable(hose, dev, idx) < 0)
+				return -EINVAL;
+
 		r = &dev->resource[idx];
 		if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
 			continue;
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/Makefile linux-2.6.26/arch/mips/sgi-ip30/Makefile
--- linux-2.6.26.orig/arch/mips/sgi-ip30/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/Makefile	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,8 @@
+#
+# Makefile for the IP30 specific kernel interface routines under Linux.
+#
+
+obj-y	:= ip30-setup.o ip30-irq.o ip30-timer.o ip30-err.o ip30-xtalk.o ip30-power.o
+obj-$(CONFIG_SMP)	+= ip30-smp.o ip30-smp-glue.o
+
+EXTRA_AFLAGS := $(CFLAGS)
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-err.c linux-2.6.26/arch/mips/sgi-ip30/ip30-err.c
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-err.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-err.c	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,42 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * ip30-err.c: HEART error handling for IP30 architecture.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
+ *		 2007 Joshua Kinard <kumba@xxxxxxxxxx>
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+
+#include <asm/mach-ip30/heart.h>
+
+void ip30_do_err(void)
+{
+	unsigned long errors = *HEART_ISR;
+	int i;
+	irq_enter();
+	*HEART_CLR_ISR = HEART_INT_LEVEL4;
+	printk("IP30: HEART ATTACK! Caught errors: 0x%04x!\n",
+		(int)((errors >> HEART_ERR_MASK_START) & HEART_ERR_MASK));
+	for(i = HEART_ERR_MASK_END; i >= HEART_ERR_MASK_START; i--)
+		if ((errors >> i) & 1)
+			printk("    interrupt #%d\n", i);
+	irq_exit();
+}
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-irq.c linux-2.6.26/arch/mips/sgi-ip30/ip30-irq.c
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-irq.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-irq.c	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,337 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * ip30-irq.c: Highlevel interrupt handling for IP30 architecture.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
+ *		 2007 Joshua Kinard <kumba@xxxxxxxxxx>
+ *
+ * Inspired by ip27-irq.c and ip32-irq.c
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+
+#include <asm/mach-ip30/heart.h>
+#include <asm/mach-ip30/pcibr.h>
+#include <asm/mach-ip30/racermp.h>
+#include <asm/pci/bridge.h>
+
+#undef DEBUG_IRQ
+#undef DEBUG_IRQ_SET
+
+#define DYNAMIC_IRQ_START 64
+
+#ifndef CONFIG_SMP
+#define cpu_logical_map(x) 0
+#define cpu_next_pcpu(x) 0
+#else
+extern int cpu_next_pcpu(int pcpu);
+#endif
+
+/* CPU IRQ */
+
+void ip30_timer_bcast(void);
+
+void cpu_do_irq(void)
+{
+#ifdef CONFIG_SMP
+	ip30_timer_bcast();
+#endif
+	do_IRQ(TIMER_IRQ);
+}
+
+static void enable_cpu_irq(unsigned int irq)
+{
+	set_c0_status(STATUSF_IP7);
+}
+
+static unsigned int startup_cpu_irq(unsigned int irq)
+{
+	enable_cpu_irq(irq);
+	return 0;
+}
+
+static void disable_cpu_irq(unsigned int irq)
+{
+	clear_c0_status(STATUSF_IP7);
+}
+
+static void end_cpu_irq(unsigned int irq)
+{
+	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+		enable_cpu_irq(irq);
+}
+
+#define shutdown_cpu_irq disable_cpu_irq
+#define mask_and_ack_cpu_irq disable_cpu_irq
+
+static struct irq_chip ip30_cpu_irq = {
+	.typename = "CPU",
+	.startup = startup_cpu_irq,
+	.shutdown = shutdown_cpu_irq,
+	.enable = enable_cpu_irq,
+	.disable = disable_cpu_irq,
+	.ack = mask_and_ack_cpu_irq,
+	.end = end_cpu_irq,
+};
+
+/* real HEART IRQs */
+
+int heart_irq_thisowner;
+static int heart_irq_owner[SOFT_IRQ_COUNT];
+
+void ip30_do_irq(void)
+{
+	unsigned int pcpu=cpu_logical_map(smp_processor_id());
+	unsigned long irqs=((*HEART_ISR) & HEART_ATK_MASK) & (*HEART_IMR(pcpu));
+	unsigned long irqsel;
+	int irqnum;
+#ifdef DEBUG_IRQ
+	bridge_t *bvma = (bridge_t *)RAW_NODE_SWIN_BASE(0, 15);
+	if(irqs & ~(15UL << IRQ_TIMER_P(0)))
+		printk("IP30: received HEART IRQs: 0x%016lx (mask 0x%016lx) PCPU%d BRIDGE %08x\n",
+			*HEART_ISR, *HEART_IMR(pcpu), pcpu, bvma->b_int_status);
+#endif
+	/* check for all IRQs in decreasing priority order */
+	irqsel = NON_HEART_IRQ_ST;
+	irqnum = 50;
+
+	/* poll all interrupts according to priority */
+	while (irqsel) {
+		if(irqs & irqsel)
+			do_IRQ(irqnum);
+		irqsel >>= 1;
+		irqnum--;
+	}
+}
+
+static void enable_heart_irq(unsigned int irq)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+	*HEART_IMR(heart_irq_owner[irq]) |= (1UL << irq);
+	local_irq_restore(flags);
+}
+
+static unsigned int startup_heart_irq(unsigned int irq)
+{
+	unsigned long flags;
+	unsigned int device;
+	unsigned int pcpu;
+	if (irq == 14 || irq == 15)
+		pcpu = 0;
+	else if (irq < IRQ_TIMER_P(0) || irq > IRQ_IPI_P(3))
+		pcpu = heart_irq_thisowner = cpu_next_pcpu(heart_irq_thisowner);
+	else
+		pcpu = cpu_logical_map(smp_processor_id());
+#ifdef DEBUG_IRQ_SET
+	printk("IP30: start up IRQ%d for PCPU%d\n", irq, pcpu);
+#endif
+	local_irq_save(flags);
+
+	if (heart_irq_owner[irq] != -1) {
+		printk(KERN_ERR "IP30: ambiprocessorous IRQ startup request (is %d, was %d).\n",
+			pcpu, heart_irq_owner[irq]);
+		local_irq_restore(flags);
+		return 0;
+	}
+
+	heart_irq_owner[irq] = pcpu;
+	*HEART_CLR_ISR = (1UL << irq);				/* clear IRQ flag */
+	*HEART_IMR(heart_irq_owner[irq]) |= (1UL << irq);	/* unmask IRQ */
+
+	if (ip30_irq_bridge[irq]) {
+		ip30_irq_bridge[irq]->b_int_enable |= (1 << ip30_irq_in_bridge[irq]);
+		ip30_irq_bridge[irq]->b_int_mode |= (1 << ip30_irq_in_bridge[irq]);
+		device = ip30_irq_bridge[irq]->b_int_device;
+		device &= ~(7 << (ip30_irq_in_bridge[irq] * 3));
+		device |= (ip30_irq_in_bridge[irq] << (ip30_irq_in_bridge[irq] * 3));
+		ip30_irq_bridge[irq]->b_int_device = device;
+		ip30_irq_bridge[irq]->b_widget.w_tflush;
+	}
+
+	local_irq_restore(flags);
+
+	/* This is probably not right; we could have pending irqs */
+	return 0;
+}
+
+static void disable_heart_irq(unsigned int irq)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+	*HEART_IMR(heart_irq_owner[irq]) &= ~(1UL << irq);
+	local_irq_restore(flags);
+}
+
+static void shutdown_heart_irq(unsigned int irq)
+{
+	unsigned long flags;
+#ifdef DEBUG_IRQ_SET
+	printk("IP30: shutdown IRQ%d\n", irq);
+#endif
+	local_irq_save(flags);
+	*HEART_IMR(heart_irq_owner[irq]) &= ~(1UL << irq);		/* mask IRQ */
+	if (ip30_irq_bridge[irq])
+		ip30_irq_bridge[irq]->b_int_enable &= ~(1 << ip30_irq_in_bridge[irq]);
+	heart_irq_owner[irq] = -1;
+	local_irq_restore(flags);
+}
+
+static void mask_and_ack_heart_irq (unsigned int irq)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+	if (irq >= IRQ_TIMER_P(0) && irq <= IRQ_IPI_P(3))
+		*HEART_CLR_ISR = (1UL << irq);
+	if (!ip30_irq_bridge[irq])
+		*HEART_ISR = (1UL << irq);
+	*HEART_IMR(heart_irq_owner[irq]) &= ~(1UL << irq);
+	local_irq_restore(flags);
+}
+
+static void end_heart_irq(unsigned int irq)
+{
+	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+		enable_heart_irq(irq);
+}
+
+static struct irq_chip ip30_heart_irq = {
+	.typename = "HEART",
+	.startup = startup_heart_irq,
+	.shutdown = shutdown_heart_irq,
+	.enable = enable_heart_irq,
+	.disable = disable_heart_irq,
+	.ack = mask_and_ack_heart_irq,
+	.end = end_heart_irq,
+};
+
+/* dynamic pseudo-IRQs */
+
+static void do_nothing_irq(unsigned int irq)
+{
+	/* Empty */
+}
+
+static unsigned int do_nothing_irq_i(unsigned int irq)
+{
+	return 0;
+}
+
+static struct irq_chip dynamic_allocated_irq = {
+	.typename = "Allocated",
+	.startup = do_nothing_irq_i,
+	.shutdown = do_nothing_irq,
+	.enable = do_nothing_irq,
+	.disable = do_nothing_irq,
+	.ack = do_nothing_irq,
+	.end = do_nothing_irq,
+};
+
+static struct irq_chip dynamic_free_irq = {
+	.typename = "Free",
+	.startup = do_nothing_irq_i,
+	.shutdown = do_nothing_irq,
+	.enable = do_nothing_irq,
+	.disable = do_nothing_irq,
+	.ack = do_nothing_irq,
+	.end = do_nothing_irq,
+};
+
+int new_dynamic_irq(void)
+{
+	int i;
+	for (i = 0; i < NR_IRQS; i++)
+		if(irq_desc[i].chip == &dynamic_free_irq)
+			break;
+	if ( i== NR_IRQS)
+		return -1;
+	irq_desc[i].chip = &dynamic_allocated_irq;
+	return i;
+}
+
+void delete_dynamic_irq(int irq)
+{
+	irq_desc[irq].chip = &dynamic_free_irq;
+}
+
+void call_dynamic_irq(int irq)
+{
+	do_IRQ(irq);
+}
+
+/* setup procedure */
+
+extern void ip30_do_err(void);
+
+asmlinkage void plat_irq_dispatch(void)
+{
+	unsigned int pending = read_c0_cause();
+
+	if (likely(pending & (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3)))
+		ip30_do_irq();
+	else if (unlikely(pending & IE_IRQ4))
+		ip30_do_err();
+	else if (unlikely(pending & IE_IRQ5))
+		cpu_do_irq();
+}
+
+void __init arch_init_irq(void)
+{
+	int i;
+	*HEART_CLR_ISR = HEART_ACK_ALL_MASK;	/* acknowledge everything */
+	for (i = 0; i < MP_NCPU; i++)		/* mask all IRQs, leave errors on */
+		*HEART_IMR(i) = HEART_CLR_ALL_MASK;
+
+	*HEART_IMR(cpu_logical_map(0)) = HEART_BR_ERR_MASK;
+
+	for (i = 0; i < SOFT_IRQ_COUNT; i++) {
+		irq_desc[i].status = IRQ_DISABLED;
+		irq_desc[i].action = 0;
+		irq_desc[i].depth  = 1;
+		irq_desc[i].chip   = &ip30_heart_irq;
+		heart_irq_owner[i] = -1;
+	}
+
+	irq_desc[TIMER_IRQ].status = IRQ_DISABLED;
+	irq_desc[TIMER_IRQ].action = 0;
+	irq_desc[TIMER_IRQ].depth  = 1;
+	irq_desc[TIMER_IRQ].chip   = &ip30_cpu_irq;
+
+	for ( i = DYNAMIC_IRQ_START; i < NR_IRQS; i++) {
+		irq_desc[i].status = IRQ_DISABLED;
+		irq_desc[i].action = 0;
+		irq_desc[i].depth  = 1;
+		irq_desc[i].chip   = &dynamic_free_irq;
+	}
+
+	/* mask IP0, IP1 (sw int) */
+	change_c0_status(ST0_IM, STATUSF_IP2 | STATUSF_IP3 | STATUSF_IP4 |
+				 STATUSF_IP5 | STATUSF_IP6);
+	set_c0_status(ST0_IE);
+	printk("IP30: interrupt controller initialized.\n");
+}
+
+void ip30_secondary_init_irq(void)
+{
+	int pcpu = cpu_logical_map(smp_processor_id());
+	*HEART_IMR(pcpu) = HEART_CLR_ALL_MASK;
+	change_c0_status(ST0_IM, STATUSF_IP2 | STATUSF_IP3 | STATUSF_IP4 |
+				 STATUSF_IP5 | STATUSF_IP6);
+}
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-power.c linux-2.6.26/arch/mips/sgi-ip30/ip30-power.c
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-power.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-power.c	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,132 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * ip30-power.c: Software powerdown and reset handling for IP30 architecture.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
+ *		 2007 Joshua Kinard <kumba@xxxxxxxxxx>
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/time.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/delay.h>
+
+#include <asm/mach-ip30/addrs.h>
+#include <asm/mach-ip30/heart.h>
+#include <asm/mach-ip30/racermp.h>
+#include <asm/reboot.h>
+#include <asm/time.h>
+#include <asm/io.h>
+#include <asm/pci/bridge.h>
+
+#define IP30_POWER_IRQ	14
+#define IP30_ACFAIL_IRQ	15
+
+void ip30_machine_restart(char *command)
+{
+	printk("Rebooting...");
+#ifdef CONFIG_SMP
+	smp_send_stop();
+	udelay(1000);
+#endif
+	/* execute HEART cold reset
+	 *	Yes, it's cold-HEARTed! */
+	*HEART_MODE |= (1UL << 23);
+}
+
+void ip30_soft_powerdown(void);
+int ip30_clear_power_irq(void);
+int ip30_can_powerdown(void);
+
+void ip30_machine_power_off(void)
+{
+#ifdef CONFIG_SGI_IP30_RTC	
+	int i;
+
+	if (!ip30_can_powerdown())
+		return;
+	printk("Powering down, please wait...");
+
+#ifdef CONFIG_SMP
+	smp_send_stop();
+	udelay(1000);
+#endif
+
+	/* kill interrupts */
+        *HEART_CLR_ISR = HEART_ACK_ALL_MASK;
+        for (i = 0; i < MP_NCPU; i++)
+                *HEART_IMR(i) = HEART_CLR_ALL_MASK;
+
+	/* execute RTC powerdown */
+	ip30_soft_powerdown();
+#else
+	printk("RTC support is required to power down.\n");
+	printk("System halted.\n");
+	while (1);
+#endif
+}
+
+void ip30_machine_halt(void)
+{
+	ip30_machine_power_off();
+}
+
+/* power button */
+static struct timer_list power_timer;
+
+static int is_shutdown;
+
+static void power_timeout(unsigned long data)
+{
+	ip30_machine_power_off();
+}
+
+static irqreturn_t power_irq(int irq, void *dev_id)
+{
+	/* prepare for next IRQs */
+#ifdef CONFIG_SGI_IP30_RTC
+	if (!ip30_clear_power_irq())
+#endif
+		disable_irq_nosync(irq);
+
+	/* button pressed twice or no init */
+	if (is_shutdown || kill_proc(1, SIGINT, 1)) {
+		printk(KERN_INFO "Immediate powerdown...\n");
+		ip30_machine_power_off();
+		return IRQ_HANDLED;
+	}
+
+	/* power button, set LEDs if we can */
+	is_shutdown = 1;
+	printk(KERN_INFO "Power button pressed, shutting down...\n");
+
+	init_timer(&power_timer);
+	power_timer.function = power_timeout;
+	power_timer.expires = jiffies + (30 * HZ);
+	add_timer(&power_timer);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t acfail_irq(int irq, void *dev_id)
+{
+	/* we have a bit of time here */
+	return IRQ_HANDLED;
+}
+
+static int __init reboot_setup(void)
+{
+	request_irq(IP30_POWER_IRQ, power_irq, 0, "powerbtn", NULL);
+	request_irq(IP30_ACFAIL_IRQ, acfail_irq, 0, "acfail", NULL);
+	return 0;
+}
+
+subsys_initcall(reboot_setup);
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-setup.c linux-2.6.26/arch/mips/sgi-ip30/ip30-setup.c
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-setup.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-setup.c	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,73 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * SGI IP30 specific setup.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
+ *               2007 Joshua Kinard <kumba@xxxxxxxxxx>
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/time.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+
+#include <asm/bootinfo.h>
+#include <asm/reboot.h>
+#include <asm/time.h>
+#include <asm/io.h>
+#include <asm/mach-ip30/heart.h>
+#include <asm/mach-ip30/addrs.h>
+
+extern void ip30_machine_restart(char *command);
+extern void ip30_machine_halt(void);
+extern void ip30_machine_power_off(void);
+
+extern void ip30_xtalk_setup(void);
+
+extern void ip30_time_init(void);
+
+extern int ip30_locate_bootcpu(void);
+
+static unsigned long ip30_size_memory(void)
+{
+	unsigned long result = 0;
+	unsigned int *memcfg = (unsigned int *)HEART_MEMCFG0;
+	int i;
+
+	for (i = 0; i < 8; i++)
+		if(memcfg[i] & HEART_MEMCFG_VLD)
+			result += ((memcfg[i] & HEART_MEMCFG_RAM_MSK) 
+					>> HEART_MEMCFG_RAM_SHFT) + 1;
+	return result << HEART_MEMCFG_UNIT_SHFT;
+}
+
+
+static void ip30_fix_memory(void)
+{
+	unsigned long size = ip30_size_memory();
+	printk(KERN_INFO "Detected %ld MB of physical memory.\n", size >> 20);
+
+	if(size > IP30_MAX_PROM_MEM) {
+		printk(KERN_INFO "Updating PROM memory size.\n");
+		add_memory_region((IP30_MAX_PROM_MEM + IP30_MEM_BASE),
+					size - IP30_MAX_PROM_MEM, BOOT_MEM_RAM);
+	}
+}
+
+
+void __init plat_mem_setup(void)
+{
+	printk("Silicon Graphics Octane (IP30) support: (c) 2004-2007 Stanislaw Skowronek.\n");
+	set_io_port_base(IP30_IO_PORT_BASE);
+        _machine_restart  = ip30_machine_restart;
+        _machine_halt = ip30_machine_halt;
+        pm_power_off = ip30_machine_power_off;
+	ip30_fix_memory();
+	ip30_xtalk_setup();
+}
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-smp-glue.S linux-2.6.26/arch/mips/sgi-ip30/ip30-smp-glue.S
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-smp-glue.S	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-smp-glue.S	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,21 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2005-2007 Stanislaw Skowronek
+ */
+
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+	.text
+	.set	noat
+	.set	reorder
+	.align	5
+LEAF(ip30_smp_bootstrap)
+	move	gp, a0
+	j	smp_bootstrap
+	END(ip30_smp_bootstrap)
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-smp.c linux-2.6.26/arch/mips/sgi-ip30/ip30-smp.c
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-smp.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-smp.c	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,165 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * ip30-smp.c: SMP on IP30 architecture.
+ *
+ * Copyright (C) 2005-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
+ *               2006-2007 Joshua Kinard <kumba@xxxxxxxxxx>
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/spinlock.h>
+
+#include <asm/mmu_context.h>
+#include <asm/bootinfo.h>
+#include <asm/mach-ip30/heart.h>
+#include <asm/mach-ip30/racermp.h>
+#include <asm/mach-ip30/addrs.h>
+
+#undef DEBUG_IPI
+
+extern void asmlinkage ip30_smp_bootstrap(void);
+extern void plat_time_init(void);
+extern void ip30_secondary_init_irq(void);
+
+static spinlock_t ipi_mbx_lock = SPIN_LOCK_UNLOCKED;
+static volatile unsigned int ipi_mailbox[NR_CPUS];
+
+extern unsigned int (*mips_hpt_read)(void);
+extern void (*mips_hpt_init)(unsigned int);
+extern void ip30_secondary_timer_setup(void);
+
+static void ip30_send_ipi_single(int cpu, unsigned int action)
+{
+	unsigned long flags;
+#ifdef DEBUG_IPI
+	if(action == SMP_CALL_FUNCTION)
+		printk("KERN_INFO IPI call_function TX -> %d\n", cpu);
+#endif
+	spin_lock_irqsave(&ipi_mbx_lock, flags);
+	ipi_mailbox[cpu] |= action;
+	spin_unlock_irqrestore(&ipi_mbx_lock, flags);
+	*HEART_SET_ISR = 1UL << (IRQ_IPI_P(cpu));
+}
+
+static void ip30_send_ipi_mask(cpumask_t mask, unsigned int action)
+{
+        unsigned int i;
+
+        for_each_cpu_mask(i, mask)
+                ip30_send_ipi_single(i, action);
+}
+
+int cpu_next_pcpu(int pcpu)
+{
+	int i;
+	for (i = (pcpu + 1) % MP_NCPU; !cpu_isset(i, phys_cpu_present_map); i = (i + 1) % MP_NCPU)
+		if(i == pcpu)
+			return pcpu;
+	return i;
+}
+
+irqreturn_t ip30_mailbox_irq(int irq, void *dev)
+{
+	int cpu = smp_processor_id();
+	int mbx;
+	spin_lock(&ipi_mbx_lock);
+	mbx = ipi_mailbox[cpu];
+	ipi_mailbox[cpu] = 0;
+	spin_unlock(&ipi_mbx_lock);
+	if (mbx & SMP_RESCHEDULE_YOURSELF)
+		/* ignore - reschedule after IRQ */ ;
+	if (mbx & SMP_CALL_FUNCTION) {
+		smp_call_function_interrupt();
+#ifdef DEBUG_IPI
+		printk("KERN_INFO IPI call_function RX -> %d\n",
+			smp_processor_id());
+#endif
+	}
+	return IRQ_HANDLED;
+}
+
+void ip30_timer_bcast(void)
+{
+	int i;
+	for (i = 1; i < NR_CPUS; i++)
+		if (cpu_isset(i, cpu_present_map))
+			*HEART_SET_ISR = 1UL << (IRQ_TIMER_P(i));
+}
+
+irqreturn_t ip30_secondary_timer_irq(int irq, void *dev)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+	local_irq_restore(flags);
+	return IRQ_HANDLED;
+}
+
+static void __init ip30_prepare_cpus(unsigned int max_cpus)
+{
+	/* everything should be ready by now */
+}
+
+static void __init ip30_smp_setup(void)
+{
+	int i, j;
+	
+	cpus_clear(phys_cpu_present_map);
+	for (i = 0, j = 0; i < MP_NCPU; i++)
+		if (MP_MAGIC(i) == MPCONF_MAGIC && MP_VIRTID(i) < NR_CPUS) {
+			cpu_set(i, phys_cpu_present_map);
+			__cpu_number_map[i] = MP_VIRTID(i);
+			__cpu_logical_map[MP_VIRTID(i)] = i;
+			j++;
+		}
+	printk("Detected %d enabled CPU(s).\n", j);
+}
+
+static void __cpuinit ip30_boot_secondary(int cpu, struct task_struct *idle)
+{
+	int pcpu = cpu_logical_map(cpu);
+	MP_STACKADDR(pcpu) = __KSTK_TOS(idle);
+	MP_LPARM(pcpu) = (unsigned long)idle->stack;
+	MP_LAUNCH(pcpu) = ip30_smp_bootstrap;
+}
+
+static void __cpuinit ip30_init_secondary(void)
+{
+	ip30_secondary_init_irq();
+}
+
+static void __cpuinit ip30_smp_finish(void)
+{
+	int cpu = smp_processor_id();
+	if (request_irq(IRQ_IPI_P(cpu), ip30_mailbox_irq, 0, "SMP IPI", NULL))
+		printk("IP30: IPI allocation for CPU%d failed.\n", cpu);
+	if (request_irq(IRQ_TIMER_P(cpu), ip30_secondary_timer_irq, 0, "SMP TIMER", NULL))
+		printk("IP30: TIMER allocation for CPU%d failed.\n", cpu);
+	local_irq_enable();
+}
+
+static void __init ip30_cpus_done(void)
+{
+	int cpu = smp_processor_id();
+	if (request_irq(IRQ_IPI_P(cpu), ip30_mailbox_irq, 0, "SMP IPI", NULL))
+		printk("IP30: IPI allocation for CPU%d failed.\n", cpu);
+}
+
+struct plat_smp_ops ip30_smp_ops = {
+	.send_ipi_single	= ip30_send_ipi_single,
+	.send_ipi_mask		= ip30_send_ipi_mask,
+	.init_secondary		= ip30_init_secondary,
+	.smp_finish		= ip30_smp_finish,
+	.cpus_done		= ip30_cpus_done,
+	.boot_secondary		= ip30_boot_secondary,
+	.smp_setup		= ip30_smp_setup,
+	.prepare_cpus		= ip30_prepare_cpus,
+};
+
+
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-timer.c linux-2.6.26/arch/mips/sgi-ip30/ip30-timer.c
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-timer.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-timer.c	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,51 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * ip30-timer.c: Timer handling for IP30 architecture.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
+ * Inspired by ip32-timer.c
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+#include <asm/i8253.h>
+
+#include <asm/time.h>
+#include <asm/mipsregs.h>
+#include <asm/mmu_context.h>
+#include <asm/mach-ip30/heart.h>
+
+#define NSEC_PER_CYCLE		80
+#define CYCLES_PER_100MSEC	(100000000 / NSEC_PER_CYCLE)
+
+void __init plat_time_init(void)
+{
+	unsigned long heart_compare;
+	printk("IP30: initializing timer.\n");
+	heart_compare = (*HEART_COUNT) + CYCLES_PER_100MSEC;
+	write_c0_count(0);
+	while ((*HEART_COUNT - heart_compare) & 0x800000) ;
+	mips_hpt_frequency = read_c0_count() * 10;
+	printk("%d MHz CPU detected\n", (mips_hpt_frequency * 2) / 1000000);
+}
+
+unsigned int get_c0_compare_int(void) {
+	/* Return Octane's Timer IRQ */
+	return TIMER_IRQ;
+}
diff -Naurp linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-xtalk.c linux-2.6.26/arch/mips/sgi-ip30/ip30-xtalk.c
--- linux-2.6.26.orig/arch/mips/sgi-ip30/ip30-xtalk.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/arch/mips/sgi-ip30/ip30-xtalk.c	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,182 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * ip30-xtalk.c
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
+ *		 2007 Joshua Kinard <kumba@xxxxxxxxxx>
+ *
+ * XIO bus probing code
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+
+#include <asm/mach-ip30/heart.h>
+#include <asm/mach-ip30/addrs.h>
+#include <asm/mach-ip30/pcibr.h>
+#include <asm/mach-ip30/xtalk.h>
+#include <asm/xtalk/xwidget.h>
+
+static struct widget_ident widget_idents[] = {
+	{
+		XTALK_XBOW_MFGR_ID,
+		XTALK_XBOW_PART_ID,
+		"XBow",
+		{NULL, "1.0", "1.1", "1.2", "1.3", "2.0", NULL}
+	},
+	{
+		XTALK_XXBOW_MFGR_ID,
+		XTALK_XXBOW_PART_ID,
+		"XXBow",
+		{NULL, "1.0", "2.0", NULL}
+	},
+	{
+		XTALK_ODYS_MFGR_ID,
+		XTALK_ODYS_PART_ID,
+		"Buzz / Odyssey",
+		{NULL, "A", "B", NULL}
+	},
+	{
+		XTALK_TPU_MFGR_ID,
+		XTALK_TPU_PART_ID,
+		"TPU",
+		{"0", NULL}
+	},
+	{
+		XTALK_XBRDG_MFGR_ID,
+		XTALK_XBRDG_PART_ID,
+		"XBridge",
+		{NULL, "A", "B", NULL}
+	},
+	{
+		XTALK_HEART_MFGR_ID,
+		XTALK_HEART_PART_ID,
+		"Heart",
+		{NULL, "A", "B", "C", "D", "E", "F", NULL}
+	},
+	{
+		XTALK_BRIDG_MFGR_ID,
+		XTALK_BRIDG_PART_ID,
+		"Bridge",
+		{NULL, "A", "B", "C", "D", NULL}
+	},
+	{
+		XTALK_HUB_MFGR_ID,
+		XTALK_HUB_PART_ID,
+		"Hub",
+		{NULL, "1.0", "2.0", "2.1", "2.2", "2.3", "2.4", NULL}
+	},
+	{
+		XTALK_BDRCK_MFGR_ID,
+		XTALK_BDRCK_PART_ID,
+		"Bedrock",
+		{NULL, "1.0", "1.1", NULL}
+	},
+	{
+		XTALK_IMPCT_MFGR_ID,
+		XTALK_IMPCT_PART_ID,
+		"HQ4 / ImpactSR",
+		{NULL, "A", "B", NULL}
+	},
+	{
+		XTALK_KONA_MFGR_ID,
+		XTALK_KONA_PART_ID,
+		"XG / KONA",
+		{NULL}
+	},
+	{
+		XTALK_NULL_MFGR_ID,
+		XTALK_NULL_PART_ID,
+		NULL,
+		{NULL}
+	}
+};
+
+extern int bridge_probe(nasid_t nasid, int widget, int masterwid);
+
+unsigned long ip30_xtalk_swin(int wid)
+{
+	return NODE_SWIN_BASE(0, wid);
+}
+
+unsigned ip30_xtalk_get_id(int wid)
+{
+	unsigned int link_stat;
+	if (wid != XTALK_XBOW &&
+		(wid < XTALK_LOW_DEV || wid > XTALK_HIGH_DEV))
+			return XTALK_NODEV;
+
+	if (wid) {
+		link_stat = *(volatile unsigned int *)(RAW_NODE_SWIN_BASE(0, 0) + 
+				XBOW_REG_LINK_STAT_0 + 
+				XBOW_REG_LINK_BLOCK_SIZE * (wid - XTALK_LOW_DEV));
+		if (!(link_stat & XBOW_REG_LINK_ALIVE))	/* is the link alive? */
+			return XTALK_NODEV;
+	}
+
+	return *(volatile unsigned int *)(RAW_NODE_SWIN_BASE(0, wid) + WIDGET_ID);
+}
+
+int ip30_xtalk_find(unsigned mfgr, unsigned part, int last)
+{
+	unsigned wid_id;
+	while (last > 0) {
+		last--;
+		wid_id = ip30_xtalk_get_id(last);
+		if (XWIDGET_MFG_NUM(wid_id) == mfgr &&
+			XWIDGET_PART_NUM(wid_id) == part)
+				return last;
+	}
+	return -1;
+}
+
+void __init ip30_xtalk_setup(void)
+{
+	int i;
+	unsigned int wid_id;
+	unsigned int wid_part, wid_mfgr, wid_rev;
+	struct widget_ident *res;
+
+	for (i = 0; i < IP30_XTALK_NUM_WID; i++) {
+		wid_id = ip30_xtalk_get_id(i);
+		if (wid_id != XTALK_NODEV) {
+			printk(KERN_INFO "xtalk: Detected ");
+			wid_mfgr = XWIDGET_MFG_NUM(wid_id);
+			wid_part = XWIDGET_PART_NUM(wid_id);
+			wid_rev = XWIDGET_REV_NUM(wid_id);
+
+			for (res = widget_idents; res->name; res++)
+				if(res->mfgr == wid_mfgr && res->part == wid_part)
+					break;
+
+			if (res->name) {
+				printk(res->name);
+				if (res->revs[wid_rev])
+					printk(" (revision %s)", res->revs[wid_rev]);
+				else
+					printk(" (unknown revision %d)", wid_rev);
+			} else
+				printk("unknown widget 0x%08x", wid_id);
+			printk(" at %d.\n", i);
+		}
+	}
+
+	i = IP30_XTALK_NUM_WID;
+	while ((i = ip30_xtalk_find(PCIBR_XTALK_MFGR, PCIBR_XTALK_PART, i)) != -1)
+		bridge_probe(0, i, IP30_WIDGET_HEART);
+}
diff -Naurp linux-2.6.26.orig/drivers/char/Kconfig linux-2.6.26/drivers/char/Kconfig
--- linux-2.6.26.orig/drivers/char/Kconfig	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/drivers/char/Kconfig	2008-07-25 03:14:40.000000000 -0400
@@ -819,6 +819,17 @@ config SGI_IP27_RTC
 	  via the file /proc/rtc and its behaviour is set by various ioctls on
 	  /dev/rtc.
 
+config SGI_IP30_RTC
+	bool "SGI Octane RTC support"
+	depends on SGI_IP30 && SGI_IOC3
+	help
+	  If you say Y here and create a character special file /dev/rtc with
+	  major number 10 and minor number 135 using mknod ("man mknod"), you
+	  will get access to the real time clock built into your computer.
+	  Every SGI has such a clock built in. It reports status information
+	  via the file /proc/rtc and its behaviour is set by various ioctls on
+	  /dev/rtc.
+
 config GEN_RTC
 	tristate "Generic /dev/rtc emulation"
 	depends on RTC!=y && !IA64 && !ARM && !M32R && !MIPS && !SPARC && !FRV && !S390 && !SUPERH && !AVR32
@@ -866,6 +877,14 @@ config COBALT_LCD
 	  This option enables support for the LCD display and buttons found
 	  on Cobalt systems through a misc device.
 
+config SGI_IP30_LEDS
+	bool "SGI Octane LED support"
+	depends on SGI_IP30 && SGI_IOC3
+	help
+	  If you say Y here and create a character special file /dev/leds with
+	  major number 10 and minor number 42 using mknod ("man mknod"), you
+	  will be able to control the lightbar on your Octane.
+
 config DTLK
 	tristate "Double Talk PC internal speech card support"
 	depends on ISA
diff -Naurp linux-2.6.26.orig/drivers/char/Makefile linux-2.6.26/drivers/char/Makefile
--- linux-2.6.26.orig/drivers/char/Makefile	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/drivers/char/Makefile	2008-07-25 03:14:40.000000000 -0400
@@ -76,6 +76,8 @@ obj-$(CONFIG_GEN_RTC)		+= genrtc.o
 obj-$(CONFIG_EFI_RTC)		+= efirtc.o
 obj-$(CONFIG_SGI_DS1286)	+= ds1286.o
 obj-$(CONFIG_SGI_IP27_RTC)	+= ip27-rtc.o
+obj-$(CONFIG_SGI_IP30_LEDS) += ip30-leds.o
+obj-$(CONFIG_SGI_IP30_RTC) += ip30-rtc.o
 obj-$(CONFIG_DS1302)		+= ds1302.o
 obj-$(CONFIG_XILINX_HWICAP)	+= xilinx_hwicap/
 ifeq ($(CONFIG_GENERIC_NVRAM),y)
diff -Naurp linux-2.6.26.orig/drivers/char/ip30-leds.c linux-2.6.26/drivers/char/ip30-leds.c
--- linux-2.6.26.orig/drivers/char/ip30-leds.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/drivers/char/ip30-leds.c	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,280 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ *	Driver for the LEDs in SGI Octane.
+ *
+ *	Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
+ */
+
+#include <linux/bcd.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+
+#include <linux/miscdevice.h>
+#include <asm/mach-ip30/leds.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/ioc3.h>
+
+#define LEDS_STREAM_SIZE	4096
+
+
+/* hardware dependent LEDs driver */
+static struct ioc3_driver_data *ioc3 = NULL;
+static unsigned int leds_buff;
+
+static void ip30_leds_begin(void)
+{
+	leds_buff = ioc3->gpdr_shadow;
+}
+
+static void ip30_leds_set(int led, unsigned char state)
+{
+	state >>= 7;
+	leds_buff &= ~(1 << led);
+	leds_buff |= state << led;
+}
+
+static void ip30_leds_end(void)
+{
+	ioc3_gpio(ioc3, 3, leds_buff);
+}
+
+
+/* generic LEDs stream interpreter part */
+static spinlock_t leds_lock = SPIN_LOCK_UNLOCKED;
+static int leds_are_open = 0;
+static struct timer_list leds_timer;
+static unsigned char leds_stream[LEDS_STREAM_SIZE];
+static int leds_pc = 0;
+
+static void leds_timer_proc(unsigned long param)
+{
+	unsigned long timer_ms = 0;
+	int end_flag = 0;
+	unsigned char byte1, byte2;
+
+	ip30_leds_begin();
+
+	while (!end_flag) {
+		byte1 = leds_stream[leds_pc++];
+		byte2 = leds_stream[leds_pc++];
+
+		switch (byte1 >> 6) {
+		case LEDS_OP_SET:
+			ip30_leds_set(byte1 & 0x3f, byte2);
+			break;
+		case LEDS_OP_LOOP:
+			leds_pc = 0;
+		case LEDS_OP_WAIT:
+			timer_ms = ((unsigned long)byte2) << (byte1 & 0x3f);
+			end_flag = 1;
+			break;
+		case LEDS_OP_RSVD:
+			printk(KERN_INFO "ip30-leds: Stream to the future!\n");
+			leds_pc = 0;
+			timer_ms = 0;
+			end_flag = 1;
+			break;
+		}
+
+		if(leds_pc >= LEDS_STREAM_SIZE) {
+			printk(KERN_INFO "ip30-leds: The Neverending Stream?\n");
+			leds_pc = 0;
+			timer_ms = 0;
+			end_flag = 1;
+		}
+	}
+
+	ip30_leds_end();
+
+	if (timer_ms) {
+		timer_ms = (timer_ms * HZ) / 1000;
+		leds_timer.expires = jiffies + timer_ms;
+		add_timer(&leds_timer);
+	}
+}
+
+static int leds_open(struct inode *inode, struct file *file)
+{
+	spin_lock_irq(&leds_lock);
+	if (leds_are_open) {
+		spin_unlock_irq(&leds_lock);
+		return -EBUSY;
+	}
+	leds_are_open = 1;
+	del_timer(&leds_timer);
+	memset(leds_stream, 0xFF, LEDS_STREAM_SIZE);
+	spin_unlock_irq(&leds_lock);
+
+	return 0;
+}
+
+static int leds_release(struct inode *inode, struct file *file)
+{
+	spin_lock_irq(&leds_lock);
+	leds_are_open = 0;
+	leds_pc = 0;
+	leds_timer.expires = (jiffies + 1);
+	leds_timer.function = leds_timer_proc;
+	add_timer(&leds_timer);
+	spin_unlock_irq(&leds_lock);
+
+	return 0;
+}
+
+static ssize_t leds_write(struct file *file, const char *buf, size_t count, loff_t * ppos)
+{
+	if (count > LEDS_STREAM_SIZE)
+		return -ENOSPC;
+	copy_from_user(leds_stream, buf, count);
+	return count;
+}
+
+static struct file_operations leds_fops = {
+	.owner		= THIS_MODULE,
+	.open		= leds_open,
+	.write		= leds_write,
+	.release	= leds_release,
+};
+
+static struct miscdevice leds_dev= {
+	LEDS_MINOR,
+	"leds",
+	&leds_fops
+};
+
+
+/* special hacks */
+static int panic_event(struct notifier_block *this, unsigned long event,
+                      void *ptr)
+{
+	del_timer(&leds_timer);
+	memset(leds_stream, 0xFF, LEDS_STREAM_SIZE);
+
+	leds_stream[0] = 0x00;
+	leds_stream[1] = 0x00;
+	leds_stream[2] = 0x01;
+	leds_stream[3] = 0xFF;
+
+	leds_stream[4] = 0x49;
+	leds_stream[5] = 0x01;
+
+	leds_stream[6] = 0x01;
+	leds_stream[7] = 0x00;
+	leds_stream[8] = 0x00;
+	leds_stream[9] = 0xFF;
+
+	leds_stream[10] = 0x89;
+	leds_stream[11] = 0x01;
+
+	leds_pc = 0;
+	leds_timer.expires = (jiffies + 1);
+	leds_timer.function = leds_timer_proc;
+	add_timer(&leds_timer);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block panic_block = {
+	.notifier_call	= panic_event,
+};
+
+
+/* IOC3 SuperIO probe */
+static int ioc3led_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
+{
+	int i, p = 0;
+	if (ioc3 || idd->class != IOC3_CLASS_BASE_IP30)
+		return 1; /* no sense in setting LEDs on the MENETs */
+
+	ioc3 = idd;
+
+	if (misc_register(&leds_dev)) {
+		printk(KERN_ERR "ip30-leds: There is no place for me here <sob, sniff>.\n");
+		return 1;
+	}
+
+	for (i = 0; i < 3; i++) {
+		leds_stream[p++] = 0x00;
+		leds_stream[p++] = 0x00;
+		leds_stream[p++] = 0x01;
+		leds_stream[p++] = 0xff;
+
+		leds_stream[p++] = 0x48;
+		leds_stream[p++] = 0x01;
+
+		leds_stream[p++] = 0x01;
+		leds_stream[p++] = 0x00;
+		leds_stream[p++] = 0x00;
+		leds_stream[p++] = 0xff;
+
+		leds_stream[p++] = 0x48;
+		leds_stream[p++] = 0x01;
+	}
+	leds_stream[p++] = 0x80;
+	leds_stream[p++] = 0x00;
+
+	init_timer(&leds_timer);
+	leds_timer.expires = (jiffies + 1);
+	leds_timer.function = leds_timer_proc;
+	add_timer(&leds_timer);
+
+	atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
+
+	return 0;
+}
+
+static int ioc3led_remove(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
+{
+	if (ioc3 != idd)
+		return 1;
+
+	misc_deregister(&leds_dev);
+	ioc3 = NULL;
+	return 0;
+}
+
+
+/* entry/exit functions */
+static struct ioc3_submodule ioc3led_submodule = {
+	.name = "leds",
+	.probe = ioc3led_probe,
+	.remove = ioc3led_remove,
+	.owner = THIS_MODULE,
+};
+
+static int __init leds_init(void)
+{
+	ioc3_register_submodule(&ioc3led_submodule);
+	return 0;
+}
+
+static void __exit leds_exit (void)
+{
+	ioc3_unregister_submodule(&ioc3led_submodule);
+}
+
+MODULE_AUTHOR("Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("SGI Octane (IP30) LEDS Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("R28");
+
+module_init(leds_init);
+module_exit(leds_exit);
diff -Naurp linux-2.6.26.orig/drivers/char/ip30-rtc.c linux-2.6.26/drivers/char/ip30-rtc.c
--- linux-2.6.26.orig/drivers/char/ip30-rtc.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/drivers/char/ip30-rtc.c	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,417 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ *	Driver for the Maxim/Dallas DS1687 real time clock in SGI Octane.
+ *
+ *	Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
+ *		           2007 Joshua Kinard <kumba@xxxxxxxxxx>
+ *
+ *	Somewhat based on: ip27-rtc.c (userland interface code).
+ */
+
+#include <linux/bcd.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/time.h>
+
+#include <asm/mach-ip30/ds1687.h>
+
+
+/* physical access functions */
+extern spinlock_t rtc_lock;
+static struct ioc3_driver_data *ioc3 = NULL;
+
+static unsigned char ip30_rtc_read(int addr)
+{
+	RTC_ADDR = addr & 0x7f;		/* field is 7-bits wide */
+	return RTC_DATA;
+}
+
+static void ip30_rtc_write(int addr, unsigned char data)
+{
+	RTC_ADDR = addr & 0x7f;		/* field is 7-bits wide */
+	RTC_DATA = data;
+}
+
+
+/* RTC hardware driver */
+static void rtc_begin_access(int bank)
+{
+	unsigned char val = ip30_rtc_read(DS1687_CTRL_B);
+	unsigned long start = jiffies;
+	spin_lock_irq(&rtc_lock);
+	ip30_rtc_write(DS1687_CTRL_B, val | DS1687_BIT7);	/* SET bit */
+	val = ip30_rtc_read(DS1687_CTRL_A);
+	while (val & DS1687_BIT7) {				/* UIP bit */
+		udelay(10);
+
+		/* 137 is a magic number.  Don't touch! */
+		if (jiffies > start + 137) {
+			printk(KERN_ERR "ip30-rtc: RTC access lock timeout.\n");
+			return;
+		}
+		val = ip30_rtc_read(DS1687_CTRL_A);
+	}
+	ip30_rtc_write(DS1687_CTRL_A, (val & 0xef) | (bank << 4));
+}
+
+static void rtc_end_access(void)
+{
+	unsigned char val = ip30_rtc_read(DS1687_CTRL_B);
+	ip30_rtc_write(DS1687_CTRL_B, val & 0x7f);
+	spin_unlock_irq(&rtc_lock);
+}
+
+static void get_rtc_time(struct rtc_time *rtc_tm)
+{
+	rtc_begin_access(1);
+	rtc_tm->tm_sec = ip30_rtc_read(DS1687_SECNDS);
+	rtc_tm->tm_min = ip30_rtc_read(DS1687_MINS);
+	rtc_tm->tm_hour = ip30_rtc_read(DS1687_HOUR);
+	rtc_tm->tm_mday = ip30_rtc_read(DS1687_DATE);
+	rtc_tm->tm_mon = ip30_rtc_read(DS1687_MONTH);
+	rtc_tm->tm_year = ip30_rtc_read(DS1687_YEAR);
+	rtc_tm->tm_year += ip30_rtc_read(DS1687_CENTURY) * 100;
+	rtc_end_access();
+
+	rtc_tm->tm_year -= 1900;
+	rtc_tm->tm_mon--;
+}
+
+static const unsigned char days_in_mo[] =
+{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+static int set_rtc_time(struct rtc_time *rtc_tm)
+{
+	unsigned char mon, day, hrs, min, sec, leap_yr;
+	unsigned int yrs;
+
+	yrs = rtc_tm->tm_year + 1900;
+	mon = rtc_tm->tm_mon + 1;   /* tm_mon starts at zero */
+	day = rtc_tm->tm_mday;
+	hrs = rtc_tm->tm_hour;
+	min = rtc_tm->tm_min;
+	sec = rtc_tm->tm_sec;
+
+	leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
+	if ((mon > 12) || (day == 0))
+		return -EINVAL;
+
+	if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
+		return -EINVAL;
+
+	if ((hrs >= 24) || (min >= 60) || (sec >= 60))
+		return -EINVAL;
+
+	rtc_begin_access(1);
+	ip30_rtc_write(DS1687_SECNDS, sec);
+	ip30_rtc_write(DS1687_MINS, min);
+	ip30_rtc_write(DS1687_HOUR, hrs);
+	ip30_rtc_write(DS1687_DATE, day);
+	ip30_rtc_write(DS1687_MONTH, mon);
+	ip30_rtc_write(DS1687_YEAR, yrs % 100);
+	ip30_rtc_write(DS1687_CENTURY, yrs / 100);
+	rtc_end_access();
+
+	return 0;
+}
+
+
+/* power-down logic */
+static int ip30_soft_powerdown_called;
+static int ip30_clear_irq_called;
+
+int ip30_clear_power_irq(void)
+{
+	unsigned char val;
+
+	if (!ioc3) {
+		ip30_clear_irq_called = 1;
+		return 0;
+	}
+
+	spin_lock_irq(&rtc_lock);
+	val = ip30_rtc_read(DS1687_CTRL_A);
+	ip30_rtc_write(DS1687_CTRL_A, val | DS1687_BIT4);	/* select extended regs */
+	ip30_rtc_write(DS1687_EXT_CTRL_4A, 0x00);
+	spin_unlock_irq(&rtc_lock);
+	return 1;
+}
+
+int ip30_can_powerdown(void)
+{
+	if (!ioc3) {
+		ip30_soft_powerdown_called = 1;
+		return 0;
+	}
+	return 1;
+}
+
+void ip30_soft_powerdown(void)
+{
+	unsigned char val;
+	rtc_begin_access(1);
+
+	if (!ioc3) {
+		ip30_soft_powerdown_called = 1;
+		return;
+	}
+
+	/* prepare the RTC for waking us up so we don't wind up dead */
+	val = ip30_rtc_read(DS1687_EXT_CTRL_4B);
+	val &= 0x2a;
+	val |= 0x81;
+	ip30_rtc_write(DS1687_EXT_CTRL_4B, val);
+	rtc_end_access();
+
+	while (1) {
+		ip30_rtc_write(DS1687_EXT_CTRL_4A, DS1687_BIT3);	/* power down */
+		udelay(100000);
+	}
+
+	/* there is no way out */
+	/* (of mordor!) */
+}
+
+
+/* userland interface stuff */
+static int rtc_is_open = 0;
+
+static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		     unsigned long arg)
+{
+
+	struct rtc_time wtime;
+
+	switch (cmd) {
+	case RTC_RD_TIME:	/* Read the time/date from RTC	*/
+		get_rtc_time(&wtime);
+		return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
+
+	case RTC_SET_TIME:	/* Set the RTC */
+		if (!capable(CAP_SYS_TIME))
+			return -EACCES;
+
+		if (copy_from_user(&wtime, (struct rtc_time*)arg,
+				   sizeof(struct rtc_time)))
+			return -EFAULT;
+
+		return set_rtc_time(&wtime);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int rtc_open(struct inode *inode, struct file *file)
+{
+	spin_lock_irq(&rtc_lock);
+
+	if (rtc_is_open) {
+		spin_unlock_irq(&rtc_lock);
+		return -EBUSY;
+	}
+
+	rtc_is_open = 1;
+	spin_unlock_irq(&rtc_lock);
+
+	return 0;
+}
+
+static int rtc_release(struct inode *inode, struct file *file)
+{
+	spin_lock_irq(&rtc_lock);
+	rtc_is_open = 0;
+	spin_unlock_irq(&rtc_lock);
+
+	return 0;
+}
+
+static struct file_operations rtc_fops = {
+	.owner		= THIS_MODULE,
+	.ioctl		= rtc_ioctl,
+	.open		= rtc_open,
+	.release	= rtc_release,
+};
+
+static struct miscdevice rtc_dev=
+{
+	RTC_MINOR,
+	"rtc",
+	&rtc_fops
+};
+
+
+/* Info exported via "/proc/rtc". */
+static int rtc_get_status(char *buf)
+{
+	char *p;
+	struct rtc_time tm;
+
+	/* Just emulate the standard /proc/rtc */
+	p = buf;
+	get_rtc_time(&tm);
+
+	/*
+	 * There is no way to tell if the luser has the RTC set for local
+	 * time or for Universal Standard Time (GMT). Probably local though.
+	 */
+	p += sprintf(p,
+		     "rtc_time\t: %02d:%02d:%02d\n"
+		     "rtc_date\t: %04d-%02d-%02d\n"
+	 	     "rtc_epoch\t: %04u\n"
+		     "BCD\t\t: no\n"
+		     "24hr\t\t: yes\n",
+		     tm.tm_hour, tm.tm_min, tm.tm_sec,
+		     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 1900);
+
+	return  p - buf;
+}
+
+static int rtc_read_proc(char *page, char **start, off_t off,
+                                 int count, int *eof, void *data)
+{
+        int len = rtc_get_status(page);
+
+        if (len <= (off + count))
+		*eof = 1;
+
+        *start = (page + off);
+        len -= off;
+
+        if (len > count)
+		len = count;
+
+	/* Block negative time */
+        if (len < 0)
+		len = 0;
+
+        return len;
+}
+
+
+/* general MIPS compatibility */
+unsigned long read_persistent_clock(void)
+{
+	struct rtc_time tm;
+	if (ioc3) {
+		get_rtc_time(&tm);
+		return mktime((tm.tm_year + 1900), (tm.tm_mon + 1),
+			      tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+	}
+
+	/* default value ??? */
+	return mktime(2004, 8, 23, 12, 15, 0);
+}
+
+int rtc_mips_set_time(unsigned long tim)
+{
+	struct rtc_time tm;
+	rtc_time_to_tm(tim, &tm);
+
+	if (ioc3)
+		set_rtc_time(&tm);
+
+	return 0;
+}
+
+
+/* IOC3 SuperIO probe */
+static int ioc3rtc_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
+{
+	struct rtc_time wtime;
+
+	if (ioc3 || idd->class != IOC3_CLASS_BASE_IP30)
+		return 1; /* this is good and proper */
+
+	ioc3 = idd;
+
+	if (misc_register(&rtc_dev)) {
+		printk(KERN_ERR "ip30-rtc: Cannot register device.\n");
+		return 1;
+	}
+
+	if (!create_proc_read_entry("driver/rtc", 0, NULL, rtc_read_proc, NULL)) {
+		printk(KERN_ERR "ip30-rtc: Cannot create procfs entry.\n");
+		misc_deregister(&rtc_dev);
+		return 1;
+	}
+
+	/* can we set xtime here? */
+	get_rtc_time(&wtime);
+	write_seqlock_irq(&xtime_lock);
+	xtime.tv_sec = mktime((wtime.tm_year + 1900), (wtime.tm_mon + 1),
+			      wtime.tm_mday, wtime.tm_hour, wtime.tm_min,
+			      wtime.tm_sec);
+	xtime.tv_nsec = 0;
+	set_normalized_timespec(&wall_to_monotonic,
+	                        -xtime.tv_sec, -xtime.tv_nsec);
+
+	write_sequnlock_irq(&xtime_lock);
+
+	ip30_clear_power_irq();
+
+	if (ip30_clear_irq_called)
+		enable_irq(14);
+
+	if (ip30_soft_powerdown_called)
+		ip30_soft_powerdown();
+
+	return 0;
+}
+
+static int ioc3rtc_remove(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
+{
+	if (ioc3 != idd)
+		return 1;
+
+	misc_deregister(&rtc_dev);
+
+	/* TODO: kill proc, although this driver should not be removable anyway */
+	ioc3 = NULL;
+	return 0;
+}
+
+
+/* entry/exit functions */
+static struct ioc3_submodule ioc3rtc_submodule = {
+	.name = "rtc",
+	.probe = ioc3rtc_probe,
+	.remove = ioc3rtc_remove,
+	.owner = THIS_MODULE,
+};
+
+static int __init rtc_init(void)
+{
+	ioc3_register_submodule(&ioc3rtc_submodule);
+	return 0;
+}
+
+static void __exit rtc_exit (void)
+{
+	ioc3_unregister_submodule(&ioc3rtc_submodule);
+}
+
+MODULE_AUTHOR("Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("SGI Octane (IP30) RTC Interface Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("R28");
+
+module_init(rtc_init);
+module_exit(rtc_exit);
diff -Naurp linux-2.6.26.orig/drivers/usb/host/pci-quirks.c linux-2.6.26/drivers/usb/host/pci-quirks.c
--- linux-2.6.26.orig/drivers/usb/host/pci-quirks.c	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/drivers/usb/host/pci-quirks.c	2008-07-25 03:14:40.000000000 -0400
@@ -147,6 +147,9 @@ static void __devinit quirk_usb_handoff_
 	unsigned long base = 0;
 	int i;
 
+	if (!pci_enable_device(pdev))
+		return;
+
 	if (!pio_enabled(pdev))
 		return;
 
diff -Naurp linux-2.6.26.orig/drivers/video/Kconfig linux-2.6.26/drivers/video/Kconfig
--- linux-2.6.26.orig/drivers/video/Kconfig	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/drivers/video/Kconfig	2008-07-25 03:14:40.000000000 -0400
@@ -955,6 +955,18 @@ config FB_ATMEL_STN
 
 	  If unsure, say N.
 
+config FB_IMPACTSR
+	tristate "SGI Octane ImpactSR graphics support"
+	depends on FB && SGI_IP30
+	help
+	  SGI Octane ImpactSR (SI/SSI/MXI/SE/SSE/MXE) graphics card support.
+
+config FB_ODYSSEY
+	tristate "SGI Octane Odyssey graphics support"
+	depends on FB && SGI_IP30
+	help
+	  SGI Octane Odyssey (VPro V6/V8/V10/V12) graphics card support.
+
 config FB_NVIDIA
 	tristate "nVidia Framebuffer Support"
 	depends on FB && PCI
diff -Naurp linux-2.6.26.orig/drivers/video/Makefile linux-2.6.26/drivers/video/Makefile
--- linux-2.6.26.orig/drivers/video/Makefile	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/drivers/video/Makefile	2008-07-25 03:15:47.000000000 -0400
@@ -117,6 +117,8 @@ obj-$(CONFIG_FB_SM501)            += sm5
 obj-$(CONFIG_FB_XILINX)           += xilinxfb.o
 obj-$(CONFIG_FB_OMAP)             += omap/
 obj-$(CONFIG_XEN_FBDEV_FRONTEND)  += xen-fbfront.o
+obj-$(CONFIG_FB_IMPACTSR)         += impactsr.o
+obj-$(CONFIG_FB_ODYSSEY)          += odyssey.o
 
 # Platform or fallback drivers go here
 obj-$(CONFIG_FB_UVESA)            += uvesafb.o
diff -Naurp linux-2.6.26.orig/drivers/video/impactsr.c linux-2.6.26/drivers/video/impactsr.c
--- linux-2.6.26.orig/drivers/video/impactsr.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/drivers/video/impactsr.c	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,994 @@
+/*
+ * linux/drivers/video/impactsr.c -- SGI Octane MardiGras (IMPACTSR) graphics
+ *
+ *  Copyright (c) 2004 by Stanislaw Skowronek
+ *
+ *  Based on linux/drivers/video/skeletonfb.c
+ *
+ *  This driver, as most of the IP30 (SGI Octane) port, is a result of massive
+ *  amounts of reverse engineering and trial-and-error. If anyone is interested
+ *  in helping with it, please contact me: <skylark@xxxxxxxxxxxxxx>.
+ *
+ *  The basic functions of this driver are filling and blitting rectangles.
+ *  To achieve the latter, two DMA operations are used on Impact. It is unclear
+ *  to me, why is it so, but even Xsgi (the IRIX X11 server) does it this way.
+ *  It seems that fb->fb operations are not operational on these cards.
+ *
+ *  For this purpose, a kernel DMA pool is allocated (pool number 0). This pool
+ *  is (by default) 64kB in size. An ioctl could be used to set the value at
+ *  run-time. Applications can use this pool, however proper locking has to be
+ *  guaranteed. Kernel should be locked out from this pool by an ioctl.
+ *
+ *  The IMPACTSR is quite well worked-out currently, except for the Geometry
+ *  Engines (GE11). Any information about use of those devices would be very
+ *  useful. It would enable a Linux OpenGL driver, as most of OpenGL calls are
+ *  supported directly by the hardware. So far, I can't initialize the GE11.
+ *  Verification of microcode crashes the graphics.
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/spinlock.h>
+#include <linux/font.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-ip30/xtalk.h>
+#include <video/impactsr.h>
+
+#define IMPACTSR_KPOOL_SIZE	65536
+
+struct impactsr_par {
+	/* physical mmio base in HEART XTalk space */
+	unsigned long mmio_base;
+
+	/* virtual mmio base in kernel space */
+	unsigned long mmio_virt;
+
+	/* DMA pool management */
+	unsigned int *pool_txtbl[5];
+	unsigned int pool_txnum[5];
+	unsigned int pool_txmax[5];
+	unsigned long pool_txphys[5];
+
+	/* kernel DMA pools */
+	unsigned long **kpool_virt[5];
+	unsigned long *kpool_phys[5];
+	unsigned int kpool_size[5];
+
+	/* board config */
+	unsigned int num_ge, num_rss;
+
+	/* locks to prevent simultaneous user and kernel access */
+	int open_flag;
+	spinlock_t lock;
+};
+
+static struct fb_fix_screeninfo impactsr_fix = {
+	.id =		"ImpactSR 0RSS", 
+	.smem_start = 	0,
+	.smem_len =	0,
+	.type =		FB_TYPE_PACKED_PIXELS,
+	.visual =	FB_VISUAL_TRUECOLOR,
+	.xpanstep =	0,
+	.ypanstep =	0,
+	.ywrapstep =	0, 
+	.line_length =	0,
+	.accel =	FB_ACCEL_SGI_IMPACTSR,
+};
+
+static struct fb_var_screeninfo impactsr_var = {
+	.xres =		1280,
+	.yres =		1024,
+	.xres_virtual =	1280,
+	.yres_virtual =	1024,
+	.bits_per_pixel = 24,
+	.red =		{ .offset = 0, .length = 8 },
+	.green =	{ .offset = 8, .length = 8 },
+	.blue =		{ .offset = 16, .length = 8 },
+	.transp =	{ .offset = 24, .length = 8 },
+};
+
+static struct fb_info info;
+static unsigned int pseudo_palette[256];
+static struct impactsr_par current_par;
+int impactsr_init(void);
+
+
+/* --------------------- Gory Details --------------------- */
+#define MMIO (((struct impactsr_par *)p->par)->mmio_virt)
+#define PAR (*((struct impactsr_par *)p->par))
+
+static void impactsr_wait_cfifo(struct fb_info *p, int nslots)
+{
+	while ((IMPACTSR_FIFOSTATUS(MMIO) & 0xff) > (IMPACTSR_CFIFO_MAX - nslots));
+}
+
+static void impactsr_wait_cfifo_empty(struct fb_info *p)
+{
+	while (IMPACTSR_FIFOSTATUS(MMIO) & 0xff);
+}
+
+static void impactsr_wait_bfifo(struct fb_info *p, int nslots)
+{
+	while ((IMPACTSR_GIOSTATUS(MMIO) & 0x1f) > (IMPACTSR_BFIFO_MAX - nslots));
+}
+
+static void impactsr_wait_bfifo_empty(struct fb_info *p)
+{
+	while (IMPACTSR_GIOSTATUS(MMIO) & 0x1f);
+}
+
+static void impactsr_wait_dma(struct fb_info *p)
+{
+	while (IMPACTSR_DMABUSY(MMIO) & 0x1f);
+	while (!(IMPACTSR_STATUS(MMIO) & 1));
+	while (!(IMPACTSR_STATUS(MMIO) & 2));
+	while (!(IMPACTSR_RESTATUS(MMIO) & 0x100));
+}
+static void impactsr_wait_dmaready(struct fb_info *p)
+{
+	IMPACTSR_CFIFOW(MMIO) = 0x000e0100;
+	while (IMPACTSR_DMABUSY(MMIO) & 0x1eff);
+	while (!(IMPACTSR_STATUS(MMIO) & 2));
+}
+
+static void impactsr_inithq4(struct fb_info *p)
+{
+	/* CFIFO parameters */
+	IMPACTSR_CFIFO_HW(MMIO) = 0x47;
+	IMPACTSR_CFIFO_LW(MMIO) = 0x14;
+	IMPACTSR_CFIFO_DELAY(MMIO) = 0x64;
+
+	/* DFIFO parameters */
+	IMPACTSR_DFIFO_HW(MMIO) = 0x40;
+	IMPACTSR_DFIFO_LW(MMIO) = 0x10;
+	IMPACTSR_DFIFO_DELAY(MMIO) = 0;
+}
+
+static void impactsr_initrss(struct fb_info *p)
+{
+	/* transfer mask registers */
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_COLORMASKLSBSA(0xffffff);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_COLORMASKLSBSB(0xffffff);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_COLORMASKMSBS(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRMASKLO(0xffffff);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRMASKHI(0xffffff);
+
+	/* use the main plane */
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_DRBPOINTERS(0xc8240);
+
+	/* set the RE into vertical flip mode */
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CONFIG(0xcac);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XYWIN(0, 0x3ff);
+}
+
+static void impactsr_initxmap(struct fb_info *p)
+{
+	/* set XMAP into 24-bpp mode */
+	IMPACTSR_XMAP_PP1SELECT(MMIO) = 0x01;
+	IMPACTSR_XMAP_INDEX(MMIO) = 0x00;
+	IMPACTSR_XMAP_MAIN_MODE(MMIO) = 0x07a4;
+}
+
+static void impactsr_initvc3(struct fb_info *p)
+{
+	/* cursor-b-gone (disable DISPLAY bit) */
+	IMPACTSR_VC3_INDEXDATA(MMIO) = 0x1d000100;
+}
+
+static void impactsr_initdma(struct fb_info *p)
+{
+	unsigned long pool;
+	/* clear DMA pools */
+	for (pool = 0; pool < 5; pool++) {
+		impactsr_wait_cfifo_empty(p);
+		IMPACTSR_CFIFOPW(MMIO) = IMPACTSR_CMD_HQ_TXBASE(pool);
+		IMPACTSR_CFIFOP(MMIO) = 0x0000000000000009;
+		IMPACTSR_CFIFOP(MMIO) = IMPACTSR_CMD_HQ_TXMAX(pool, 0);
+		IMPACTSR_CFIFOP(MMIO) = IMPACTSR_CMD_HQ_PGBITS(pool, 0);
+		IMPACTSR_CFIFOP(MMIO) = 0x00484b0400080000|(pool << 40);
+		PAR.pool_txmax[pool] = 0;
+		PAR.pool_txnum[pool] = 0;
+	}
+
+	/* set DMA parameters */
+	IMPACTSR_CFIFOP(MMIO) = IMPACTSR_CMD_HQ_PGSIZE(0);
+	IMPACTSR_CFIFOP(MMIO) = IMPACTSR_CMD_HQ_STACKPTR(0);
+	IMPACTSR_CFIFOP(MMIO) = 0x00484a0400180000;
+	IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+	IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+	IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+	IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+	IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+	IMPACTSR_REG32(MMIO,0x40918) = 0x00680000;
+	IMPACTSR_REG32(MMIO,0x40920) = 0x80280000;
+	IMPACTSR_REG32(MMIO,0x40928) = 0x00000000;
+}
+
+static void impactsr_alloctxtbl(struct fb_info *p, int pool, int txmax)
+{
+	dma_addr_t dma_handle;
+	int alloc_size;
+	if (txmax > PAR.pool_txmax[pool]) {	/* grow the pool - unlikely but supported */
+		alloc_size = txmax;
+		if (alloc_size < 1024)
+			alloc_size = 1024;
+		if (PAR.pool_txmax[pool])
+			dma_free_coherent(NULL, (PAR.pool_txmax[pool] * 4),
+					  PAR.pool_txtbl[pool],
+					  PAR.pool_txphys[pool]);
+		PAR.pool_txtbl[pool] = dma_alloc_coherent(NULL, (alloc_size * 4),
+							  &dma_handle, GFP_KERNEL);
+		PAR.pool_txphys[pool] = dma_handle;
+		PAR.pool_txmax[pool] = alloc_size;
+	}
+	PAR.pool_txnum[pool] = txmax;
+}
+
+static void impactsr_writetxtbl(struct fb_info *p, int pool)
+{
+	impactsr_wait_cfifo_empty(p);
+
+	/* inform the card about a new DMA pool */
+	IMPACTSR_CFIFOPW(MMIO) = IMPACTSR_CMD_HQ_TXBASE(pool);
+	IMPACTSR_CFIFOP(MMIO) = PAR.pool_txphys[pool];
+	IMPACTSR_CFIFOP(MMIO) = IMPACTSR_CMD_HQ_TXMAX(pool, PAR.pool_txnum[pool]);
+	IMPACTSR_CFIFOP(MMIO) = IMPACTSR_CMD_HQ_PGBITS(pool, 0x0a);
+	IMPACTSR_CFIFOP(MMIO) = (0x00484b0400180000 | ((long)pool << 40));
+	IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+	IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+	IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+	IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+	IMPACTSR_CFIFOPW(MMIO) = 0x000e0100;
+}
+
+static void impactsr_settxtbl(struct fb_info *p, int pool, unsigned *txtbl,
+			      int txmax)
+{
+	impactsr_alloctxtbl(p, pool, txmax);
+	memcpy(PAR.pool_txtbl[pool], txtbl, (txmax * 4));
+	impactsr_writetxtbl(p, pool);
+}
+
+static void impactsr_resizekpool(struct fb_info *p, int pool, int size,
+				 int growonly)
+{
+	int pages;
+	int i;
+	dma_addr_t dma_handle;
+
+	if (growonly && PAR.kpool_size[pool] >= size)
+		return;
+
+	if (size < 8192)	/* single line smallcopy (1280 * 4) *must* work */
+		size = 8192;
+
+	pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+	if (PAR.kpool_size[pool] > 0) {
+		for (i = 0; i < PAR.pool_txnum[pool]; i++) {
+			ClearPageReserved(virt_to_page(PAR.kpool_virt[pool][i]));
+			dma_free_coherent(NULL, PAGE_SIZE, PAR.kpool_virt[pool][i],
+					  PAR.kpool_phys[pool][i]);
+		}
+		vfree(PAR.kpool_phys[pool]);
+		vfree(PAR.kpool_virt[pool]);
+	}
+
+	impactsr_alloctxtbl(p, pool, pages);
+	PAR.kpool_virt[pool] = vmalloc(pages * sizeof(unsigned long));
+	PAR.kpool_phys[pool] = vmalloc(pages * sizeof(unsigned long));
+	for (i = 0; i < PAR.pool_txnum[pool]; i++) {
+		PAR.kpool_virt[pool][i] = dma_alloc_coherent(NULL, PAGE_SIZE,
+							     &dma_handle, GFP_KERNEL);
+		SetPageReserved(virt_to_page(PAR.kpool_virt[pool][i]));
+		PAR.kpool_phys[pool][i] = dma_handle;
+		PAR.pool_txtbl[pool][i] = PAR.kpool_phys[pool][i] >> PAGE_SHIFT;
+		if (!PAR.kpool_virt[pool][i])
+			printk(KERN_ERR "impactsr: Page allocation failed!\n");
+	}
+
+	impactsr_writetxtbl(p, pool);
+	PAR.kpool_size[pool] = (pages * PAGE_SIZE);
+}
+
+static void impactsr_rect(struct fb_info *p, int x, int y, int w, int h, unsigned c, int lo)
+{
+	impactsr_wait_cfifo_empty(p);
+
+	if (lo == IMPACTSR_LO_COPY)
+		IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PP1FILLMODE(0x6300, lo);
+	else
+		IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PP1FILLMODE(0x6304, lo);
+
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_FILLMODE(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PACKEDCOLOR(c);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYSTARTI(x, y);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYENDI(x + w - 1, y + h - 1);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_IR_ALIAS(0x18);
+}
+
+static void impactsr_framerect(struct fb_info *p, int x, int y, int w, int h,
+			       unsigned c)
+{
+	impactsr_rect(p, x, y, w, 1, c, IMPACTSR_LO_COPY);
+	impactsr_rect(p, x, (y + h - 1), w, 1, c, IMPACTSR_LO_COPY);
+	impactsr_rect(p, x, y, 1, h, c, IMPACTSR_LO_COPY);
+	impactsr_rect(p, (x + w - 1), y, 1, h, c, IMPACTSR_LO_COPY);
+}
+
+static unsigned long dcntr;
+static void impactsr_debug(struct fb_info *p,int v)
+{
+	int i;
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PIXCMD(3);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PIXELFORMAT(0xe00);
+
+	switch (v) {
+	case 0:
+		for (i = 0; i < 64; i++)
+			impactsr_rect(p, 4 * (i & 7), 28 - 4 * (i >> 3),
+				      4, 4, (dcntr & (1L << i)) ? 0xa080ff : 0x100030,
+				      IMPACTSR_LO_COPY);
+		break;
+
+	case 1:
+		dcntr++;
+		for (i = 0; i < 64; i++)
+			impactsr_rect(p, 4 * (i & 7), 28 - 4 * (i >> 3),
+				      4, 4, (dcntr & (1L << i)) ? 0xff80a0 : 0x300010,
+				      IMPACTSR_LO_COPY);
+		break;
+
+	case 2:
+		for (i = 0; i < 64; i++)
+			impactsr_rect(p, 4 * (i & 7), 28 - 4 * (i >> 3),
+				      4, 4, (dcntr & (1L << i)) ? 0xa0ff80 : 0x103000,
+				      IMPACTSR_LO_COPY);
+	}
+}
+
+static void impactsr_smallcopy(struct fb_info *p, unsigned sx, unsigned sy,
+			       unsigned dx, unsigned dy, unsigned w, unsigned h)
+{
+	if (w < 1 || h < 1)
+		return;
+
+	w=(w + 1) & ~1;
+
+	/* setup and perform DMA from RE to HOST */
+	impactsr_wait_dma(p);
+
+	/* select RSS to read from */
+	if (PAR.num_rss == 2) {
+		if (sy & 1)
+			IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CONFIG(0xca5);
+		else
+			IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CONFIG(0xca4);
+	} else	/* 1 */
+		IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CONFIG(0xca4);
+
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PIXCMD(2);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PP1FILLMODE(0x2200, IMPACTSR_LO_COPY);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_COLORMASKLSBSA(0xffffff);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_COLORMASKLSBSB(0xffffff);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_COLORMASKMSBS(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_DRBPOINTERS(0xc8240);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYSTARTI(sx, sy + h - 1);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYENDI(sx + w - 1, sy);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRMASKLO(0xffffff);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRMASKHI(0xffffff);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRSIZE(w, h);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCOUNTERS(w, h);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRMODE(0x00080);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_FILLMODE(0x01000000);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PIXELFORMAT(0x200);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_SCANWIDTH(w << 2);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_DMATYPE(0x0a);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_LIST_0(0x80000000);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_WIDTH(w << 2);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_OFFSET(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_STARTADDR(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_LINECNT(h);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_WIDTHA(w << 2);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCONTROL(8);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_GLINE_XSTARTF(1);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_IR_ALIAS(0x18);
+	IMPACTSR_CFIFOW(MMIO) = 0x00080b04;
+	IMPACTSR_CFIFO(MMIO) = 0x000000b900190204L;
+	IMPACTSR_CFIFOW(MMIO) = 0x00000009;
+	impactsr_wait_dmaready(p);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_GLINE_XSTARTF(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_RE_TOGGLECNTX(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCOUNTERS(0, 0);
+
+	/* setup and perform DMA from HOST to RE */
+	impactsr_wait_dma(p);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CONFIG(0xca4);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PP1FILLMODE(0x6200, IMPACTSR_LO_COPY);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYSTARTI(dx, dy + h - 1);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYENDI(dx + w - 1, dy);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_FILLMODE(0x01400000);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRMODE(0x00080);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PIXELFORMAT(0x600);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_SCANWIDTH(w << 2);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_DMATYPE(0x0c);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PIXCMD(3);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRSIZE(w, h);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCOUNTERS(w, h);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_GLINE_XSTARTF(1);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_IR_ALIAS(0x18);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCONTROL(1);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_LIST_0(0x80000000);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_OFFSET(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_STARTADDR(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_LINECNT(h);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PG_WIDTHA(w << 2);
+	IMPACTSR_CFIFOW(MMIO) = 0x0080b04;
+	IMPACTSR_CFIFO(MMIO) = 0x000000b1000e0400L;
+
+	impactsr_wait_dma(p);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_GLINE_XSTARTF(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_RE_TOGGLECNTX(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCOUNTERS(0, 0);
+}
+
+static unsigned impactsr_getpalreg(struct fb_info *p, unsigned i)
+{
+	return ((unsigned *)p->pseudo_palette)[i];
+}
+
+
+/* ------------ Accelerated Functions --------------------- */
+static void impactsr_fillrect(struct fb_info *p, const struct fb_fillrect *region)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&PAR.lock, flags);
+
+	if (PAR.open_flag) {
+		spin_unlock_irqrestore(&PAR.lock, flags);
+		return;
+	}
+
+	switch (region->rop) {
+	case ROP_XOR:
+		impactsr_rect(p, region->dx, region->dy, region->width, region->height,
+			      impactsr_getpalreg(p, region->color), IMPACTSR_LO_XOR);
+		break;
+
+	case ROP_COPY:
+	default:
+		impactsr_rect(p, region->dx, region->dy, region->width, region->height,
+			      impactsr_getpalreg(p, region->color), IMPACTSR_LO_COPY);
+		break;
+	}
+
+	spin_unlock_irqrestore(&PAR.lock, flags);
+}
+
+static void impactsr_copyarea(struct fb_info *p, const struct fb_copyarea *area) 
+{
+	unsigned sx, sy, dx, dy, w, h;
+	unsigned th, ah;
+	unsigned long flags;
+
+	w = area->width;
+	h = area->height;
+
+	if (w < 1 || h < 1)
+		return;
+
+	spin_lock_irqsave(&PAR.lock, flags);
+	if (PAR.open_flag) {
+		spin_unlock_irqrestore(&PAR.lock, flags);
+		return;
+	}
+
+	sx = area->sx;
+	sy = 0x3ff - (area->sy + h - 1);
+	dx = area->dx;
+	dy = 0x3ff - (area->dy + h - 1);
+	th = PAR.kpool_size[0] / (w * 4);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XYWIN(0, 0);
+
+	if (dy > sy) {
+		dy += h;
+		sy += h;
+		while (h > 0) {
+			ah = (th > h) ? h : th;
+			impactsr_smallcopy(p, sx, sy - ah, dx, dy - ah, w, ah);
+			dy -= ah;
+			sy -= ah;
+			h -= ah;
+		}
+	} else {
+		while (h > 0) {
+			ah = (th > h) ? h : th;
+			impactsr_smallcopy(p, sx, sy, dx, dy, w, ah);
+			dy += ah;
+			sy += ah;
+			h -= ah;
+		}
+	}
+
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PIXCMD(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_HQ_PIXELFORMAT(0xe00);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CONFIG(0xcac);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XYWIN(0, 0x3ff);
+	spin_unlock_irqrestore(&PAR.lock, flags);
+}
+
+/* 8-bpp blits are done as PIO draw operation; the pixels are unpacked into 32-bpp
+   values from the current palette in software */
+static void impactsr_imageblit_8bpp(struct fb_info *p, const struct fb_image *image)
+{
+	int i,u,v;
+	const unsigned char *dp;
+	unsigned pix;
+	unsigned pal[256];
+
+	/* setup PIO to RE */
+	impactsr_wait_cfifo_empty(p);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PP1FILLMODE(0x6300, IMPACTSR_LO_COPY);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYSTARTI(image->dx, image->dy);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYENDI((image->dx + image->width - 1),
+							(image->dy + image->height - 1));
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_FILLMODE(0x00c00000);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRMODE(0x00080);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRSIZE(image->width, image->height);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCOUNTERS(image->width, image->height);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_GLINE_XSTARTF(1);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_IR_ALIAS(0x18);
+
+	/* another workaround.. 33 writes to alpha... hmm... */
+	for (i = 0; i < 33; i++)
+		IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_ALPHA(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCONTROL(2);
+
+	/* pairs of pixels are sent in two writes to the RE */
+	i = 0;
+	dp = image->data;
+	for (v = 0; v < 256; v++)
+		pal[v] = impactsr_getpalreg(p, v);
+	for (v = 0; v < image->height; v++) {
+		for (u = 0; u < image->width; u++) {
+			pix = pal[*(dp++)];
+			if (i)
+				IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CHAR_L(pix);
+			else
+				IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CHAR_H(pix);
+			i ^= 1;
+		}
+	}
+	if (i)
+		IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CHAR_L(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_GLINE_XSTARTF(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_RE_TOGGLECNTX(0);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_XFRCOUNTERS(0, 0);
+}
+
+/* 1-bpp blits are done as character drawing; the bitmaps are drawn as 8-bit wide
+   strips; technically, Impact supports 16-pixel wide characters, but Linux
+   bitmap alignment is 8 bits and most draws are 8 pixels wide (font width), anyway */
+static void impactsr_imageblit_1bpp(struct fb_info *p, const struct fb_image *image) 
+{
+	int x, y, w, h, b;
+	int u, v, a;
+	const unsigned char *d;
+	impactsr_wait_cfifo_empty(p);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PP1FILLMODE(0x6300, IMPACTSR_LO_COPY);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_FILLMODE(0x400018);
+	a = impactsr_getpalreg(p, image->fg_color);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_PACKEDCOLOR(a);
+	a = impactsr_getpalreg(p, image->bg_color);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BKGRD_RG(a & 0xffff);
+	IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BKGRD_BA((a & 0xff0000) >> 16);
+
+	x = image->dx;
+	y = image->dy;
+	w = image->width;
+	h = image->height;
+	b = ((w + 7) / 8);
+
+	for (u = 0; u < b; u++) {
+		impactsr_wait_cfifo_empty(p);
+		a = (w < 8) ? w : 8;
+		d = (image->data + u);
+		IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYSTARTI(x, y);
+		IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_BLOCKXYENDI(x + a - 1, y + h - 1);
+		IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_IR_ALIAS(0x18);
+		for (v = 0; v < h; v++) {
+			IMPACTSR_CFIFO(MMIO) = IMPACTSR_CMD_CHAR((*d) << 24);
+			d += b;
+		}
+		w -= a;
+		x += a;
+	}
+}
+
+static void impactsr_imageblit(struct fb_info *p, const struct fb_image *image) 
+{
+	unsigned long flags;
+	spin_lock_irqsave(&PAR.lock, flags);
+	if (PAR.open_flag) {
+		spin_unlock_irqrestore(&PAR.lock, flags);
+		return;
+	}
+
+	switch (image->depth) {
+	case 1:
+		impactsr_imageblit_1bpp(p, image);
+		break;
+	case 8:
+		impactsr_imageblit_8bpp(p, image);
+		break;
+	}
+	spin_unlock_irqrestore(&PAR.lock, flags);
+}
+
+static int impactsr_sync(struct fb_info *info)
+{
+	return 0;
+}
+
+static int impactsr_blank(int blank_mode, struct fb_info *info)
+{
+	/* TODO */
+	return 0;
+}
+
+static int impactsr_setcolreg(unsigned regno, unsigned red, unsigned green,
+			      unsigned blue, unsigned transp, struct fb_info *info)
+{
+	if (regno > 255)
+		return 1;
+
+	((unsigned *)info->pseudo_palette)[regno] = (red >> 8) |
+						    (green & 0xff00) |
+						    ((blue << 8) & 0xff0000);
+	return 0;
+}
+
+
+/* ------------------- Framebuffer Access -------------------- */
+ssize_t impactsr_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+ssize_t impactsr_write(struct file *file, const char *buf, size_t count,
+		       loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+
+/* --------------------- Userland Access --------------------- */
+int impactsr_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
+{
+	return -EINVAL;
+}
+
+int impactsr_mmap(struct fb_info *p, struct vm_area_struct *vma)
+{
+	unsigned pool, i, n;
+	unsigned long size = vma->vm_end - vma->vm_start;
+	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+	unsigned long start = vma->vm_start;
+
+	switch (offset) {
+	case 0x0000000:
+		if (size != 0x200000)
+			return -EINVAL;
+
+		if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+			return -EINVAL;
+
+		offset += MMIO;
+		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+		vma->vm_flags |= VM_IO;
+		if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, size, vma->vm_page_prot))
+			return -EAGAIN;
+
+		return 0;
+
+	case 0x1000000:
+	case 0x2000000:
+	case 0x3000000:
+	case 0x8000000:
+	case 0x9000000:
+	case 0xa000000:
+	case 0xb000000:
+		if (size > 0x1000000)
+			return EINVAL;
+
+		pool = (offset >> 24) & 3;
+		impactsr_resizekpool(&info, pool, size, (offset & 0x8000000));
+		n = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+		for (i = 0; i < n; i++) {
+			if (remap_pfn_range(vma, start,
+					    PAR.kpool_phys[pool][i] >> PAGE_SHIFT,
+					    PAGE_SIZE, vma->vm_page_prot))
+				return -EAGAIN;
+			start += PAGE_SIZE;
+		}
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int impactsr_open(struct fb_info *p, int user)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&PAR.lock, flags);
+        if (user)
+		PAR.open_flag++;
+	spin_unlock_irqrestore(&PAR.lock, flags);
+	return 0;
+}
+
+static int impactsr_release(struct fb_info *p, int user)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&PAR.lock, flags);
+        if (user && PAR.open_flag)
+                PAR.open_flag--;
+	spin_unlock_irqrestore(&PAR.lock, flags);
+	return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+    /*
+     *  Frame buffer operations
+     */
+
+static struct fb_ops impactsr_ops = {
+	.owner		= THIS_MODULE,
+	.fb_read	= impactsr_read,
+	.fb_write	= impactsr_write,
+	.fb_blank	= impactsr_blank,
+	.fb_fillrect	= impactsr_fillrect,
+	.fb_copyarea	= impactsr_copyarea,
+	.fb_imageblit	= impactsr_imageblit,
+	.fb_sync	= impactsr_sync,
+	.fb_ioctl	= impactsr_ioctl,
+	.fb_setcolreg	= impactsr_setcolreg,
+	.fb_mmap	= impactsr_mmap,
+	.fb_open	= impactsr_open,
+	.fb_release	= impactsr_release,
+};
+
+/* ------------------------------------------------------------------------- */
+
+    /*
+     *  Private early console
+     */
+
+#define MMIO_FIXED	0x900000001c000000
+
+static void impactsr_earlyrect(int x, int y, int w, int h, unsigned c)
+{
+	while (IMPACTSR_FIFOSTATUS(MMIO_FIXED) & 0xff);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_PP1FILLMODE(0x6300, IMPACTSR_LO_COPY);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_FILLMODE(0);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_PACKEDCOLOR(c);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_BLOCKXYSTARTI(x, y);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_BLOCKXYENDI(x + w - 1, y + h - 1);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_IR_ALIAS(0x18);
+}
+static void impactsr_paintchar(int x, int y, unsigned char *b, unsigned c, unsigned a)
+{
+	int v;
+	while (IMPACTSR_FIFOSTATUS(MMIO_FIXED) & 0xff);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_PP1FILLMODE(0x6300, IMPACTSR_LO_COPY);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_FILLMODE(0x400018);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_PACKEDCOLOR(c);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_BKGRD_RG(a & 0xffff);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_BKGRD_BA((a&0xff0000) >> 16);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_BLOCKXYSTARTI(x, y);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_BLOCKXYENDI(x + 7, y + 15);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_IR_ALIAS(0x18);
+	for (v = 0; v < 16; v++)
+		IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_CHAR((*(b++)) << 24);
+}
+static void impactsr_earlyhwinit(void)
+{
+	IMPACTSR_CFIFO_HW(MMIO_FIXED) = 0x47;
+	IMPACTSR_CFIFO_LW(MMIO_FIXED) = 0x14;
+	IMPACTSR_CFIFO_DELAY(MMIO_FIXED) = 0x64;
+	IMPACTSR_DFIFO_HW(MMIO_FIXED) = 0x40;
+	IMPACTSR_DFIFO_LW(MMIO_FIXED) = 0x10;
+	IMPACTSR_DFIFO_DELAY(MMIO_FIXED) = 0;
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_COLORMASKLSBSA(0xffffff);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_COLORMASKLSBSB(0xffffff);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_COLORMASKMSBS(0);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_XFRMASKLO(0xffffff);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_XFRMASKHI(0xffffff);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_DRBPOINTERS(0xc8240);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_CONFIG(0xcac);
+	IMPACTSR_CFIFO(MMIO_FIXED) = IMPACTSR_CMD_XYWIN(0, 0x3ff);
+	IMPACTSR_XMAP_PP1SELECT(MMIO_FIXED) = 0x01;
+	IMPACTSR_XMAP_INDEX(MMIO_FIXED) = 0x00;
+	IMPACTSR_XMAP_MAIN_MODE(MMIO_FIXED) = 0x07a4;
+	IMPACTSR_VC3_INDEXDATA(MMIO_FIXED) = 0x1d000100;
+}
+
+static int posx = -1, posy;
+static spinlock_t earlylock = SPIN_LOCK_UNLOCKED;
+
+void impactsr_earlychar(unsigned char c, unsigned f)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&earlylock, flags);
+	if (posx == -1)
+		goto out;
+
+	if (c == '\n') {
+		posy += 16;
+		if (posy >= 1024)
+			posy = 0;
+		posx = 0;
+		goto out;
+	}
+
+	if (posx == 0) {
+		impactsr_earlyrect(0, posy, 1280, 16, 0x000000);
+		if (posy + 16 < 1024)
+			impactsr_earlyrect(0, posy + 16, 1280, 2, 0x0000ff);
+	}
+
+	impactsr_paintchar(posx, posy, (unsigned char *)font_vga_8x16.data + (c << 4),
+			   f, 0);
+	posx += 8;
+
+	if (posx >= 1280) {
+		posx = 0;
+		posy++;
+		if (posy >= 1024)
+			posy = 0;
+	}
+out:
+	spin_unlock_irqrestore(&earlylock, flags);
+}
+void impactsr_earlystring(char *s, unsigned f)
+{
+	while (*s)
+		impactsr_earlychar(*(s++), f);
+}
+void impactsr_earlyinit(void)
+{
+	impactsr_earlyhwinit();
+	impactsr_earlyrect(0, 0, 1280, 1024, 0);
+	posx = 0;
+	posy = 0;
+	impactsr_earlystring("ImpactSR early console ready.\n",0xffffff);
+}
+
+/* ------------------------------------------------------------------------- */
+
+    /*
+     *  Initialization
+     */
+
+static void __init impactsr_hwinit(void)
+{
+	/* initialize hardware */
+	impactsr_inithq4(&info);
+	impactsr_initvc3(&info);
+	impactsr_initrss(&info);
+	impactsr_initxmap(&info);
+	impactsr_initdma(&info);
+}
+
+static int __init impactsr_devinit(void)
+{
+	int xwid;
+
+	xwid = ip30_xtalk_find(IMPACTSR_XTALK_MFGR, IMPACTSR_XTALK_PART,
+			       IP30_XTALK_NUM_WID);
+	if (xwid == -1)
+		return -ENODEV;
+
+	current_par.open_flag = 0;
+	current_par.lock = SPIN_LOCK_UNLOCKED;
+
+	current_par.mmio_base = ip30_xtalk_swin(xwid);
+	current_par.mmio_virt = (unsigned long)ioremap(current_par.mmio_base, 0x200000);
+
+	impactsr_fix.mmio_start = current_par.mmio_base;
+	impactsr_fix.mmio_len = 0x200000;
+
+	info.flags = FBINFO_FLAG_DEFAULT;
+	info.screen_base = NULL;
+	info.fbops = &impactsr_ops;
+	info.fix = impactsr_fix;
+	info.var = impactsr_var;
+	info.par = &current_par;
+	info.pseudo_palette = pseudo_palette;
+
+	/* get board config */
+	current_par.num_ge = IMPACTSR_BDVERS1(current_par.mmio_virt) & 3;
+	current_par.num_rss = current_par.num_ge;
+	info.fix.id[9] = '0' + current_par.num_rss;
+
+	impactsr_hwinit();
+
+	/* initialize buffers */
+	impactsr_resizekpool(&info, 0, 65536, 0);
+	impactsr_resizekpool(&info, 1, 8192, 0);
+	impactsr_resizekpool(&info, 2, 8192, 0);
+	impactsr_resizekpool(&info, 3, 8192, 0);
+	impactsr_resizekpool(&info, 4, 8192, 0);
+
+	/* This has to been done !!! */	
+	fb_alloc_cmap(&info.cmap, 256, 0);
+
+	if (register_framebuffer(&info) < 0)
+		return -EINVAL;
+
+	printk(KERN_INFO "fb%d: %s frame buffer device\n", info.node,
+			  info.fix.id);
+	return 0;
+}
+
+static int __init impactsr_probe(struct device *dev)
+{
+	return impactsr_devinit();
+}
+
+static struct device_driver impactsr_driver = {
+	.name = "impactsr",
+	.bus = &platform_bus_type,
+	.probe = impactsr_probe,
+	/* add remove someday */
+};
+
+static struct platform_device impactsr_device = {
+	.name = "impactsr",
+};
+
+int __init impactsr_init(void)
+{
+	int ret = driver_register(&impactsr_driver);
+	if (!ret) {
+		ret = platform_device_register(&impactsr_device);
+		if (ret)
+			driver_unregister(&impactsr_driver);
+	}
+	return ret;
+}
+
+void __exit impactsr_exit(void)
+{
+	 driver_unregister(&impactsr_driver);
+}
+
+MODULE_AUTHOR("Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("SGI Octane (IP30) ImpactSR / HQ4 Video Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("R28");
+
+module_init(impactsr_init);
+module_exit(impactsr_exit);
+
+MODULE_LICENSE("GPL");
diff -Naurp linux-2.6.26.orig/drivers/video/logo/Kconfig linux-2.6.26/drivers/video/logo/Kconfig
--- linux-2.6.26.orig/drivers/video/logo/Kconfig	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/drivers/video/logo/Kconfig	2008-07-25 03:14:40.000000000 -0400
@@ -54,7 +54,7 @@ config LOGO_PARISC_CLUT224
 
 config LOGO_SGI_CLUT224
 	bool "224-color SGI Linux logo"
-	depends on SGI_IP22 || SGI_IP27 || SGI_IP32 || X86_VISWS
+	depends on SGI_IP22 || SGI_IP27 || SGI_IP30 || SGI_IP32 || X86_VISWS
 	default y
 
 config LOGO_SUN_CLUT224
diff -Naurp linux-2.6.26.orig/drivers/video/odyssey.c linux-2.6.26/drivers/video/odyssey.c
--- linux-2.6.26.orig/drivers/video/odyssey.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/drivers/video/odyssey.c	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,1055 @@
+/*
+ * linux/drivers/video/odyssey.c -- SGI Octane Odyssey graphics
+ *
+ *  Copyright (c) 2005 by Stanislaw Skowronek
+ *
+ *  This driver, as most of the IP30 (SGI Octane) port, is a result of massive
+ *  amounts of reverse engineering and trial-and-error. If anyone is interested
+ *  in helping with it, please contact me: <skylark@xxxxxxxxxxxxxx>.
+ *
+ *  Note: the driver is specialcased for 8x16 font (will be a bit faster).
+ *
+ *  Odyssey is a really cool graphics device. It is a dual-chip OpenGL
+ *  implementation with ARB_imaging support, and overall a very elegant design.
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/spinlock.h>
+#include <linux/font.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-ip30/xtalk.h>
+#include <video/odyssey.h>
+
+struct odyssey_par {
+	/* physical mmio base in HEART XTalk space */
+	unsigned long mmio_base;
+
+	/* virtual mmio base in kernel space */
+	unsigned long mmio_virt;
+
+	/* locks to prevent simultaneous user and kernel access */
+	int open_flag;
+	int mmap_flag;
+	spinlock_t lock;
+};
+
+static struct fb_fix_screeninfo odyssey_fix = {
+	.id =		"Odyssey", 
+	.smem_start = 	0,
+	.smem_len =	0,
+	.type =		FB_TYPE_PACKED_PIXELS,
+	.visual =	FB_VISUAL_TRUECOLOR,
+	.xpanstep =	0,
+	.ypanstep =	0,
+	.ywrapstep =	0, 
+	.line_length =	0,
+	.accel =	FB_ACCEL_SGI_ODYSSEY,
+};
+
+static struct fb_var_screeninfo odyssey_var = {
+	.xres =		1280,
+	.yres =		1024,
+	.xres_virtual =	1280,
+	.yres_virtual =	1024,
+	.bits_per_pixel = 24,
+	.red =		{ .offset = 0, .length = 8 },
+	.green =	{ .offset = 8, .length = 8 },
+	.blue =		{ .offset = 16, .length = 8 },
+	.transp =	{ .offset = 24, .length = 8 },
+};
+
+static struct fb_info info;
+static unsigned int pseudo_palette[256];
+static struct odyssey_par current_par;
+int odyssey_init(void);
+
+
+/* Most of the hex numbers seen in the various functions, especially those in
+ * the hardware init functions, were discovered via reverse engineering of IRIX
+ * drivers.  Little is known as to what they do or what they mean.
+ *
+ * Possibly, these hex numbers are addresses to locations outside of what we
+ * perceive as normal reality, and instead reference a location within the void
+ * itself, from which various dark and black magiks flow forth and breathe life
+ * into this hardware.
+ *
+ * If you think you can come up with a better explanation, then feel free to
+ * send a patch!
+ */
+
+
+/* --------------------- Gory Details --------------------- */
+#define MMIO (((struct odyssey_par *)p->par)->mmio_virt)
+#define PAR (*((struct odyssey_par *)p->par))
+
+static unsigned int pack_ieee754(int val)
+{
+	unsigned sign,exp;
+
+	if (!val)
+		return 0;
+	sign = (val & 0x80000000);
+
+	if (sign)
+		val = -val;
+
+	if (val & 0xff000000)
+		return 0;
+
+	exp = 150;
+	while (!(val & 0x00800000)) {
+		val << =1;
+		exp--;
+	}
+
+	return sign | (exp << 23) | (val & 0x007fffff);
+}
+
+static void odyssey_wait_cfifo(unsigned long mmio)
+{
+	while (!(ODY_STATUS0(mmio) & ODY_STATUS0_CFIFO_LW));
+}
+
+static void odyssey_wait_dfifo(unsigned long mmio, int lw)
+{
+	while ((ODY_DBESTAT(mmio) & 0x7f) > lw);
+}
+
+static void odyssey_dfifo_write(unsigned long mmio, unsigned reg, unsigned val)
+{
+	ODY_DFIFO_D(mmio) = ((unsigned long)(0x30000001 | (reg << 14)) << 32) | val;
+}
+
+static void odyssey_flush(unsigned long mmio)
+{
+	odyssey_wait_cfifo(mmio);
+	ODY_CFIFO_W(mmio) = 0x00010443;
+	ODY_CFIFO_W(mmio) = 0x000000fa;
+	ODY_CFIFO_W(mmio) = 0x00010046;
+	ODY_CFIFO_W(mmio) = 0x00010046;
+	ODY_CFIFO_W(mmio) = 0x00010019;
+	ODY_CFIFO_W(mmio) = 0x00010443;
+	ODY_CFIFO_W(mmio) = 0x00000096;
+	ODY_CFIFO_W(mmio) = 0x00010046;
+	ODY_CFIFO_W(mmio) = 0x00010046;
+	ODY_CFIFO_W(mmio) = 0x00010046;
+	ODY_CFIFO_W(mmio) = 0x00010046;
+	ODY_CFIFO_W(mmio) = 0x00010443;
+	ODY_CFIFO_W(mmio) = 0x000000fa;
+	ODY_CFIFO_W(mmio) = 0x00010046;
+	ODY_CFIFO_W(mmio) = 0x00010046;
+}
+
+static void odyssey_smallflush(unsigned long mmio)
+{
+	odyssey_wait_cfifo(mmio);
+	ODY_CFIFO_W(mmio) = 0x00010443;
+	ODY_CFIFO_W(mmio) = 0x000000fa;
+	ODY_CFIFO_W(mmio) = 0x00010046;
+	ODY_CFIFO_W(mmio) = 0x00010046;
+}
+
+static void odyssey_initbuzzgfe(unsigned long mmio)
+{
+	ODY_CFIFO_W(mmio) = 0x20008003;
+	ODY_CFIFO_W(mmio) = 0x21008010;
+	ODY_CFIFO_W(mmio) = 0x22008000;
+	ODY_CFIFO_W(mmio) = 0x23008002;
+	ODY_CFIFO_W(mmio) = 0x2400800c;
+	ODY_CFIFO_W(mmio) = 0x2500800e;
+	ODY_CFIFO_W(mmio) = 0x27008000;
+	ODY_CFIFO_W(mmio) = 0x28008000;
+	ODY_CFIFO_W(mmio) = 0x290080d6;
+	ODY_CFIFO_W(mmio) = 0x2a0080e0;
+	ODY_CFIFO_W(mmio) = 0x2c0080ea;
+	ODY_CFIFO_W(mmio) = 0x2e008380;
+	ODY_CFIFO_W(mmio) = 0x2f008000;
+	ODY_CFIFO_W(mmio) = 0x30008000;
+	ODY_CFIFO_W(mmio) = 0x31008000;
+	ODY_CFIFO_W(mmio) = 0x32008000;
+	ODY_CFIFO_W(mmio) = 0x33008000;
+	ODY_CFIFO_W(mmio) = 0x34008000;
+	ODY_CFIFO_W(mmio) = 0x35008000;
+	ODY_CFIFO_W(mmio) = 0x310081e0;
+	odyssey_flush(mmio);
+}
+
+static void odyssey_initbuzzxform(unsigned long mmio)
+{
+	ODY_CFIFO_W(mmio) = 0x9080bda2;
+	ODY_CFIFO_W(mmio) = 0x3f800000;
+	ODY_CFIFO_W(mmio) = 0x3f000000;
+	ODY_CFIFO_W(mmio) = 0xbf800000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x4e000000;
+	ODY_CFIFO_W(mmio) = 0x40400000;
+	ODY_CFIFO_W(mmio) = 0x4e000000;
+	ODY_CFIFO_W(mmio) = 0x4d000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x34008000;
+	ODY_CFIFO_W(mmio) = 0x9080bdc8;
+	ODY_CFIFO_W(mmio) = 0x3f800000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x3f000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x3f800000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x3f000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x3f800000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x3f800000;
+	ODY_CFIFO_W(mmio) = 0x34008010;
+	ODY_CFIFO_W(mmio) = 0x908091df;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x3f800000;
+	ODY_CFIFO_W(mmio) = 0x34008000;
+	odyssey_flush(mmio);
+}
+
+static void odyssey_initbuzzrast(unsigned long mmio)
+{
+	ODY_CFIFO_W(mmio) = 0x0001203b;
+	ODY_CFIFO_W(mmio) = 0x00001000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00001000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00001000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00001000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x0001084a;
+	ODY_CFIFO_W(mmio) = 0x00000080;
+	ODY_CFIFO_W(mmio) = 0x00000080;
+	ODY_CFIFO_W(mmio) = 0x00010845;
+	ODY_CFIFO_W(mmio) = 0x000000ff;
+	ODY_CFIFO_W(mmio) = 0x000076ff;
+	ODY_CFIFO_W(mmio) = 0x0001141b;
+	ODY_CFIFO_W(mmio) = 0x00000001;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00011c16;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x03000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00010404;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00011023;
+	ODY_CFIFO_W(mmio) = 0x00ff0ff0;
+	ODY_CFIFO_W(mmio) = 0x00ff0ff0;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x000000ff;
+	ODY_CFIFO_W(mmio) = 0x00011017;
+	ODY_CFIFO_W(mmio) = 0x00002000;
+	ODY_CFIFO_W(mmio) = 0x00000050;
+	ODY_CFIFO_W(mmio) = 0x20004950;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x0001204b;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x004ff3ff;
+	ODY_CFIFO_W(mmio) = 0x00ffffff;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00ffffff;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	ODY_CFIFO_W(mmio) = 0x00ffffff;
+	ODY_CFIFO_W(mmio) = 0x00000000;
+	odyssey_flush(mmio);
+}
+
+static void odyssey_initpbjvc(unsigned long mmio)
+{
+	int x;
+
+	odyssey_wait_dfifo(mmio, 0);
+	for (x = 0; x < 16; x++)
+		odyssey_dfifo_write(mmio, (0x2900 | x), 0x905215a6);
+
+	odyssey_wait_dfifo(mmio, 0);
+	for (x = 16; x < 32; x++)
+		odyssey_dfifo_write(mmio, (0x2900 | x), 0x905215a6);
+
+	odyssey_wait_dfifo(mmio, 0);
+	odyssey_dfifo_write(mmio, 0x2581, 0x00000000);
+}
+
+static void odyssey_initpbjgamma(unsigned long mmio)
+{
+	unsigned i,v;
+
+	for (i = 0; i < 0x200; i++) {
+		if ((i & 15) == 0)
+			odyssey_wait_dfifo(mmio, 0);
+		v = i >> 2;
+		v = (v << 20) | (v << 10) | v;
+		odyssey_dfifo_write(mmio, (i + 0x1a00), v);
+	}
+
+	for (i = 0x200; i < 0x300; i++) {
+		if ((i & 15) == 0)
+			odyssey_wait_dfifo(mmio, 0);
+		v = ((i - 0x200) >> 1) + 0x80;
+		v =(v << 20) | (v << 10) | v;
+		odyssey_dfifo_write(mmio, (i + 0x1a00), v);
+	}
+
+	for (i = 0x300; i < 0x600; i++) {
+		if ((i & 15) == 0)
+			odyssey_wait_dfifo(mmio, 0);
+		v = (i - 0x300) + 0x100;
+		v = (v << 20) | (v << 10) | v;
+		odyssey_dfifo_write(mmio, (i + 0x1a00), v);
+	}
+}
+
+static void odyssey_rect(unsigned long mmio, int x, int y, int w, int h,
+			 unsigned c, int lo)
+{
+	if ( lo != ODY_LO_COPY) {
+		ODY_CFIFO_W(mmio) = 0x00010404;
+		ODY_CFIFO_W(mmio) = 0x00100000;
+		ODY_CFIFO_W(mmio) = 0x00010422;	/* glLogicOp */
+		ODY_CFIFO_W(mmio) = lo;
+		odyssey_smallflush(mmio);
+	}
+
+	ODY_CFIFO_W(mmio) = 0x00014400;		/* glBegin */
+	ODY_CFIFO_W(mmio) = 0x00000007;		/* GL_QUADS */
+	ODY_CFIFO_W(mmio) = 0xc580cc08;		/* glColor3ub */
+	ODY_CFIFO_W(mmio) = c & 255;
+	ODY_CFIFO_W(mmio) = (c >> 8) & 255;
+	ODY_CFIFO_W(mmio) = (c >> 16) & 255;
+	ODY_CFIFO_W(mmio) = 0x8080c800;		/* glVertex2i */
+	ODY_CFIFO_W(mmio) = x;
+	ODY_CFIFO_W(mmio) = y;
+	ODY_CFIFO_W(mmio) = 0x8080c800;		/* glVertex2i */
+	ODY_CFIFO_W(mmio) = (x + w);
+	ODY_CFIFO_W(mmio) = y;
+	ODY_CFIFO_W(mmio) = 0x8080c800;		/* glVertex2i */
+	ODY_CFIFO_W(mmio) = (x + w);
+	ODY_CFIFO_W(mmio) = (y + h);
+	ODY_CFIFO_W(mmio) = 0x8080c800;		/* glVertex2i */
+	ODY_CFIFO_W(mmio) = x;
+	ODY_CFIFO_W(mmio) = (y + h);
+	ODY_CFIFO_W(mmio) = 0x00014001;		/* glEnd */
+	odyssey_smallflush(mmio);
+
+	if (lo != ODY_LO_COPY) {
+		ODY_CFIFO_W(mmio) = 0x00010404;
+		ODY_CFIFO_W(mmio) = 0x00000000;
+		ODY_CFIFO_W(mmio) = 0x00010422;	/* glLogicOp */
+		ODY_CFIFO_W(mmio) = ODY_LO_COPY;
+		odyssey_smallflush(mmio);
+	}
+}
+
+static unsigned odyssey_getpalreg(struct fb_info *p, unsigned i)
+{
+	return ((unsigned *)p->pseudo_palette)[i];
+}
+
+
+/* ------------ Accelerated Functions --------------------- */
+static void odyssey_fillrect(struct fb_info *p, const struct fb_fillrect *region)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&PAR.lock, flags);
+
+	if (PAR.open_flag) {
+		spin_unlock_irqrestore(&PAR.lock, flags);
+		return;
+	}
+
+	switch (region->rop) {
+	case ROP_XOR:
+		odyssey_rect(MMIO, region->dx, region->dy, region->width, region->height,
+			     odyssey_getpalreg(p, region->color), ODY_LO_XOR);
+		break;
+	case ROP_COPY:
+	default:
+		odyssey_rect(MMIO, region->dx, region->dy, region->width, region->height,
+			     odyssey_getpalreg(p, region->color), ODY_LO_COPY);
+		break;
+	}
+
+	spin_unlock_irqrestore(&PAR.lock, flags);
+}
+
+static void odyssey_copyarea(struct fb_info *p, const struct fb_copyarea *area) 
+{
+	unsigned long flags;
+	unsigned sx, sy, dx, dy, w, h;
+	w = area->width;
+	h = area->height;
+
+	if (w < 1 || h < 1)
+		return;
+
+	spin_lock_irqsave(&PAR.lock, flags);
+	if (PAR.open_flag) {
+		spin_unlock_irqrestore(&PAR.lock, flags);
+		return;
+	}
+
+	sx = area->sx;
+	sy = area->sy;
+	dx = area->dx;
+	dy = area->dy;
+
+	odyssey_flush(MMIO);
+	ODY_CFIFO_W(MMIO) = 0x00010658;
+	ODY_CFIFO_W(MMIO) = 0x00120000;
+	ODY_CFIFO_W(MMIO) = 0x00002031;
+	ODY_CFIFO_W(MMIO) = 0x00002000;
+	ODY_CFIFO_W(MMIO) = sx | (sy << 16);
+	ODY_CFIFO_W(MMIO) = 0x80502050;
+	ODY_CFIFO_W(MMIO) = w | (h << 16);	/* size */
+	ODY_CFIFO_W(MMIO) = 0x82223042;
+	ODY_CFIFO_W(MMIO) = 0x00002000;
+	ODY_CFIFO_W(MMIO) = dx | (dy << 16);	/* dest */
+	ODY_CFIFO_W(MMIO) = 0x3222204b;
+
+	/* draw stuff */
+	spin_unlock_irqrestore(&PAR.lock, flags);
+}
+
+static void odyssey_imageblit_8bpp(struct fb_info *p, const struct fb_image *image)
+{
+	int i, j, l;
+	const unsigned char *dp;
+	unsigned pal[256];
+
+	dp = image->data;
+	for (i = 0; i < 256; i++)
+		pal[i] = odyssey_getpalreg(p, i);
+
+	/* perform a PIO blit to card */
+	odyssey_smallflush(MMIO);
+	ODY_CFIFO_W(MMIO) = 0x00010405;
+	ODY_CFIFO_W(MMIO) = 0x00002400;
+	ODY_CFIFO_W(MMIO) = 0xc580cc08;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00011453;
+	ODY_CFIFO_W(MMIO) = 0x00000002;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	odyssey_flush(MMIO);
+	ODY_CFIFO_W(MMIO) = 0x2900812f;
+	ODY_CFIFO_W(MMIO) = 0x00014400;
+	ODY_CFIFO_W(MMIO) = 0x0000000a;
+	ODY_CFIFO_W(MMIO) = 0xcf80a92f;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = pack_ieee754(image->dx);
+	ODY_CFIFO_W(MMIO) = pack_ieee754(image->dy);
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = pack_ieee754(image->dx + image->width);
+	ODY_CFIFO_W(MMIO) = pack_ieee754(image->dy + image->height);
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x8080c800;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00004570;
+	ODY_CFIFO_W(MMIO) = 0x0f00104c;
+	ODY_CFIFO_W(MMIO) = 0x00000071;
+
+	for (j = 0; j < image->height; j++) {
+		ODY_CFIFO_W(MMIO) = 0x00004570;
+		ODY_CFIFO_W(MMIO) = 0x0fd1104c;
+		ODY_CFIFO_W(MMIO) = 0x00000071;
+		i = image->width;
+		while (i > 0) {
+			l = (i > 14) ? 14 : i;
+			i -= l;
+			ODY_CFIFO_W(MMIO) = 0x00014011 | (l << 10);
+			while (l--)
+				ODY_CFIFO_W(MMIO) = pal[*(dp++)];
+		}
+	}
+
+	ODY_CFIFO_W(MMIO) = 0x00014001;
+	odyssey_smallflush(MMIO);
+	ODY_CFIFO_W(MMIO) = 0x290080d6;
+	ODY_CFIFO_W(MMIO) = 0x00011453;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00010405;
+	ODY_CFIFO_W(MMIO) = 0x00002000;
+	odyssey_flush(MMIO);
+}
+
+static void odyssey_imageblit_1bpp_stp(struct fb_info *p, const struct fb_image *image) 
+{
+	unsigned palf, palb, buf;
+	int i;
+	const unsigned char *pic;
+
+	palf = odyssey_getpalreg(p, image->fg_color);
+	palb = odyssey_getpalreg(p, image->bg_color);
+	odyssey_smallflush(MMIO);
+
+	if ((image->dy & 31) < 16)
+		ODY_CFIFO_W(MMIO) = 0x00013c4e;
+	else
+		ODY_CFIFO_W(MMIO) = 0x00013c4f;
+
+	pic = image->data;
+
+	for (i = 0; i < 16; i++) {
+		buf = *(pic++);
+		buf |= buf << 8;
+		ODY_CFIFO_W(MMIO) = buf | (buf << 16);
+	}
+
+	ODY_CFIFO_W(MMIO) = 0x00010404;
+	ODY_CFIFO_W(MMIO) = 0x00000018;
+	odyssey_smallflush(MMIO);
+	ODY_CFIFO_W(MMIO) = 0x0001043f;
+	ODY_CFIFO_W(MMIO) = 0x00000010;
+	ODY_CFIFO_W(MMIO) = 0x00010c21;
+	ODY_CFIFO_W(MMIO) = 0x00000200;
+	ODY_CFIFO_W(MMIO) = ((palb & 255) << 16) | ((palb >> 4) & 0xff0);
+	ODY_CFIFO_W(MMIO) = (palb & 0xff0000) | 0xfff;
+	ODY_CFIFO_W(MMIO) = 0x00014400;
+	ODY_CFIFO_W(MMIO) = 0x00010407;
+	ODY_CFIFO_W(MMIO) = 0xc580cc08;
+	ODY_CFIFO_W(MMIO) = palf&255;
+	ODY_CFIFO_W(MMIO) = (palf >> 8) & 255;
+	ODY_CFIFO_W(MMIO) = (palf >> 16) & 255;
+	ODY_CFIFO_W(MMIO) = 0x8080c800;
+	ODY_CFIFO_W(MMIO) = image->dx;
+	ODY_CFIFO_W(MMIO) = image->dy;
+	ODY_CFIFO_W(MMIO) = 0x8080c800;
+	ODY_CFIFO_W(MMIO) = (image->dx + 8);
+	ODY_CFIFO_W(MMIO) = image->dy;
+	ODY_CFIFO_W(MMIO) = 0x8080c800;
+	ODY_CFIFO_W(MMIO) = (image->dx + 8);
+	ODY_CFIFO_W(MMIO) = (image->dy + 16);
+	ODY_CFIFO_W(MMIO) = 0x8080c800;
+	ODY_CFIFO_W(MMIO) = image->dx;
+	ODY_CFIFO_W(MMIO) = (image->dy + 16);
+	ODY_CFIFO_W(MMIO) = 0x00014001;
+	odyssey_flush(MMIO);
+	ODY_CFIFO_W(MMIO) = 0x00010404;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+}
+
+/* this is a slow fallback */
+static void odyssey_imageblit_1bpp_pio(struct fb_info *p, const struct fb_image *image) 
+{
+	int i, j, l, c;
+	const unsigned char *dp;
+	unsigned char d = 0;
+	unsigned palf, palb;
+
+	dp = image->data;
+	palf = odyssey_getpalreg(p, image->fg_color);
+	palb = odyssey_getpalreg(p, image->bg_color);
+
+	/* perform a PIO blit to card */
+	odyssey_smallflush(MMIO);
+	ODY_CFIFO_W(MMIO) = 0x00010405;
+	ODY_CFIFO_W(MMIO) = 0x00002400;
+	ODY_CFIFO_W(MMIO) = 0xc580cc08;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00011453;
+	ODY_CFIFO_W(MMIO) = 0x00000002;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	odyssey_flush(MMIO);
+	ODY_CFIFO_W(MMIO) = 0x2900812f;
+	ODY_CFIFO_W(MMIO) = 0x00014400;
+	ODY_CFIFO_W(MMIO) = 0x0000000a;
+	ODY_CFIFO_W(MMIO) = 0xcf80a92f;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = pack_ieee754(image->dx);
+	ODY_CFIFO_W(MMIO) = pack_ieee754(image->dy);
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = pack_ieee754(image->dx + image->width);
+	ODY_CFIFO_W(MMIO) = pack_ieee754(image->dy + image->height);
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x8080c800;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00004570;
+	ODY_CFIFO_W(MMIO) = 0x0f00104c;
+	ODY_CFIFO_W(MMIO) = 0x00000071;
+
+	for (j = 0; j < image->height; j++) {
+		c = 0;
+		ODY_CFIFO_W(MMIO) = 0x00004570;
+		ODY_CFIFO_W(MMIO) = 0x0fd1104c;
+		ODY_CFIFO_W(MMIO) = 0x00000071;
+		i = image->width;
+		while (i > 0) {
+			l = (i > 14) ? 14 : i;
+			i -= l;
+			ODY_CFIFO_W(MMIO) = 0x00014011 | (l << 10);
+			while (l--) {
+				if (!c)
+					d = *(dp++);
+				ODY_CFIFO_W(MMIO) = (d & 0x80) ? palf : palb;
+				d <<= 1;
+				c = (c + 1) & 7;
+			}
+		}
+	}
+
+	ODY_CFIFO_W(MMIO) = 0x00014001;
+	odyssey_smallflush(MMIO);
+	ODY_CFIFO_W(MMIO) = 0x290080d6;
+	ODY_CFIFO_W(MMIO) = 0x00011453;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00000000;
+	ODY_CFIFO_W(MMIO) = 0x00010405;
+	ODY_CFIFO_W(MMIO) = 0x00002000;
+	odyssey_flush(MMIO);
+}
+
+static void odyssey_imageblit(struct fb_info *p, const struct fb_image *image) 
+{
+	unsigned long flags;
+	spin_lock_irqsave(&PAR.lock, flags);
+	if (PAR.open_flag) {
+		spin_unlock_irqrestore(&PAR.lock, flags);
+		return;
+	}
+	switch (image->depth) {
+	case 1:
+		if (image->width != 8 || image->height != 16 ||
+		   image->dx & 7 || image->dy & 15)
+			odyssey_imageblit_1bpp_pio(p, image);
+		else
+			odyssey_imageblit_1bpp_stp(p, image);
+		break;
+	case 8:
+		odyssey_imageblit_8bpp(p, image);
+		break;
+	}
+	spin_unlock_irqrestore(&PAR.lock, flags);
+}
+
+static int odyssey_sync(struct fb_info *info)
+{
+	return 0;
+}
+
+static int odyssey_blank(int blank_mode, struct fb_info *info)
+{
+	/* TODO */
+	return 0;
+}
+
+static int odyssey_setcolreg(unsigned regno, unsigned red, unsigned green,
+			     unsigned blue, unsigned transp, struct fb_info *info)
+{
+	if (regno > 255)
+		return 1;
+	((unsigned *)info->pseudo_palette)[regno] = (red >> 8) |
+						    (green & 0xff00) |
+						    ((blue << 8) & 0xff0000);
+	return 0;
+}
+
+
+/* ------------------- Framebuffer Access -------------------- */
+ssize_t odyssey_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+ssize_t odyssey_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+
+/* --------------------- Userland Access --------------------- */
+int odyssey_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
+{
+	return -EINVAL;
+}
+
+int odyssey_mmap(struct fb_info *p, struct vm_area_struct *vma)
+{
+	unsigned long size = vma->vm_end - vma->vm_start;
+	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+	unsigned long start = vma->vm_start;
+
+	switch (offset) {
+	case 0x0000000:
+		if (size != 0x410000)
+			return -EINVAL;
+
+		if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+			return -EINVAL;
+
+		offset += MMIO;
+		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+		vma->vm_flags |= VM_IO;
+
+		if (remap_pfn_range(vma, start, (offset >> PAGE_SHIFT), size, vma->vm_page_prot))
+			return -EAGAIN;
+
+		PAR.mmap_flag = 1;
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int odyssey_open(struct fb_info *p, int user)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&PAR.lock, flags);
+
+        if (user)
+		PAR.open_flag++;
+
+	spin_unlock_irqrestore(&PAR.lock, flags);
+	return 0;
+}
+
+static int odyssey_release(struct fb_info *p, int user)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&PAR.lock, flags);
+
+        if (user && PAR.open_flag) {
+                PAR.open_flag--;
+		if (PAR.open_flag == 0)
+                        PAR.mmap_flag = 0;
+	}
+
+	spin_unlock_irqrestore(&PAR.lock, flags);
+	return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+    /*
+     *  Frame buffer operations
+     */
+
+static struct fb_ops odyssey_ops = {
+	.owner		= THIS_MODULE,
+	.fb_read	= odyssey_read,
+	.fb_write	= odyssey_write,
+	.fb_blank	= odyssey_blank,
+	.fb_fillrect	= odyssey_fillrect,
+	.fb_copyarea	= odyssey_copyarea,
+	.fb_imageblit	= odyssey_imageblit,
+	.fb_sync	= odyssey_sync,
+	.fb_ioctl	= odyssey_ioctl,
+	.fb_setcolreg	= odyssey_setcolreg,
+	.fb_mmap	= odyssey_mmap,
+	.fb_open	= odyssey_open,
+	.fb_release	= odyssey_release,
+};
+
+/* ------------------------------------------------------------------------- */
+
+    /*
+     *  Private early console
+     */
+
+#define MMIO_FIXED	0x900000001b000000
+
+int odyssey_earlyactive = 1;
+static void odyssey_earlyrect(int x, int y, int w, int h, unsigned c)
+{
+	odyssey_rect(MMIO_FIXED, x, y, w, h, c, ODY_LO_COPY);
+}
+
+static void odyssey_earlypaintchar(int x, int y, unsigned char *b, unsigned c, unsigned a)
+{
+	int i, j;
+
+	odyssey_smallflush(MMIO_FIXED);
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00010405;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00002400;
+	ODY_CFIFO_W(MMIO_FIXED) = 0xc580cc08;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00011453;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000002;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	odyssey_flush(MMIO_FIXED);
+	ODY_CFIFO_W(MMIO_FIXED) = 0x2900812f;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00014400;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x0000000a;
+	ODY_CFIFO_W(MMIO_FIXED) = 0xcf80a92f;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = pack_ieee754(x);
+	ODY_CFIFO_W(MMIO_FIXED) = pack_ieee754(y);
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = pack_ieee754(x + 8);
+	ODY_CFIFO_W(MMIO_FIXED) = pack_ieee754(y + 16);
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x8080c800;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00004570;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x0f00104c;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000071;
+
+	for (j = 0; j < 16; j++) {
+		ODY_CFIFO_W(MMIO_FIXED) = 0x00004570;
+		ODY_CFIFO_W(MMIO_FIXED) = 0x0fd1104c;
+		ODY_CFIFO_W(MMIO_FIXED) = 0x00000071;
+		ODY_CFIFO_W(MMIO_FIXED) = 0x00016011;
+		for (i = 7; i >= 0; i--)
+			if ((b[j] >> i) & 1)
+				ODY_CFIFO_W(MMIO_FIXED) = c;
+			else
+				ODY_CFIFO_W(MMIO_FIXED) = a;
+	}
+
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00014001;
+	odyssey_smallflush(MMIO_FIXED);
+	ODY_CFIFO_W(MMIO_FIXED) = 0x290080d6;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00011453;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00000000;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00010405;
+	ODY_CFIFO_W(MMIO_FIXED) = 0x00002000;
+	odyssey_flush(MMIO_FIXED);
+}
+
+static void odyssey_earlyhwinit(void)
+{
+	odyssey_initbuzzgfe(MMIO_FIXED);
+	odyssey_initbuzzxform(MMIO_FIXED);
+	odyssey_initbuzzrast(MMIO_FIXED);
+	odyssey_initpbjvc(MMIO_FIXED);
+	odyssey_initpbjgamma(MMIO_FIXED);
+}
+
+static int posx = -1, posy;
+static spinlock_t earlylock = SPIN_LOCK_UNLOCKED;
+void odyssey_earlychar(unsigned char c, unsigned f)
+{
+	unsigned long flags;
+
+	if (!odyssey_earlyactive)
+		return;
+
+	spin_lock_irqsave(&earlylock, flags);
+
+	if (posx == -1)
+		goto out;
+
+	if (c == '\n') {
+		posy += 16;
+		if (posy >= 1024)
+			posy = 0;
+		posx = 0;
+		goto out;
+	}
+
+	if (posx == 0) {
+		odyssey_earlyrect(0, posy, 1280, 16, 0x000000);
+		if ((posy + 16) < 1024)
+			odyssey_earlyrect(0, (posy + 16), 1280, 1, 0x0000ff);
+	}
+
+	odyssey_earlypaintchar(posx, posy,
+			       (unsigned char *)font_vga_8x16.data + (c << 4),
+				f, 0);
+	posx += 8;
+	if (posx >= 1280) {
+		posx = 0;
+		posy++;
+		if (posy >= 1024)
+			posy = 0;
+	}
+
+out:
+	spin_unlock_irqrestore(&earlylock, flags);
+}
+
+void odyssey_earlystring(char *s,unsigned f)
+{
+	while (*s)
+		odyssey_earlychar(*(s++), f);
+}
+
+void odyssey_earlyinit(void)
+{
+	odyssey_earlyhwinit();
+	odyssey_earlyrect(0, 0, 1280, 1024, 0);
+	posx = 0;
+	posy = 0;
+	odyssey_earlystring("Odyssey early console ready.\n",0xffffff);
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+    /*
+     *  Initialization
+     */
+
+static void __init odyssey_hwinit(unsigned long mmio)
+{
+	/* initialize hardware */
+	odyssey_initbuzzgfe(mmio);
+	odyssey_initbuzzxform(mmio);
+	odyssey_initbuzzrast(mmio);
+	odyssey_initpbjvc(mmio);
+	odyssey_initpbjgamma(mmio);
+}
+
+static int __init odyssey_devinit(void)
+{
+	int xwid;
+
+	xwid = ip30_xtalk_find(ODY_XTALK_MFGR, ODY_XTALK_PART,
+			       IP30_XTALK_NUM_WID);
+
+	if (xwid == -1)
+		return -ENODEV;
+
+	current_par.open_flag = 0;
+	current_par.mmap_flag = 0;
+	current_par.lock = SPIN_LOCK_UNLOCKED;
+
+	current_par.mmio_base = ip30_xtalk_swin(xwid);
+	current_par.mmio_virt = (unsigned long)ioremap(current_par.mmio_base, 0x410000);
+
+	odyssey_fix.mmio_start = current_par.mmio_base;
+	odyssey_fix.mmio_len = 0x410000;
+
+	info.flags = FBINFO_FLAG_DEFAULT;
+	info.screen_base = NULL;
+	info.fbops = &odyssey_ops;
+	info.fix = odyssey_fix;
+	info.var = odyssey_var;
+	info.par = &current_par;
+	info.pseudo_palette = pseudo_palette;
+
+	odyssey_hwinit(current_par.mmio_virt);
+
+	/* This has to been done !!! */	
+	fb_alloc_cmap(&info.cmap, 256, 0);
+
+	odyssey_earlyactive = 0;
+
+	if (register_framebuffer(&info) < 0)
+		return -EINVAL;
+	printk(KERN_INFO "fb%d: %s frame buffer device\n", info.node,
+			 info.fix.id);
+
+	return 0;
+}
+
+static int __init odyssey_probe(struct device *dev)
+{
+	return odyssey_devinit();
+}
+
+static struct device_driver odyssey_driver = {
+	.name = "odyssey",
+	.bus = &platform_bus_type,
+	.probe = odyssey_probe,
+	/* add remove someday */
+};
+
+static struct platform_device odyssey_device = {
+	.name = "odyssey",
+};
+
+int __init odyssey_init(void)
+{
+	int ret = driver_register(&odyssey_driver);
+	if (!ret) {
+		ret = platform_device_register(&odyssey_device);
+		if (ret)
+			driver_unregister(&odyssey_driver);
+	}
+	return ret;
+}
+
+void __exit odyssey_exit(void)
+{
+	 driver_unregister(&odyssey_driver);
+}
+
+MODULE_AUTHOR("Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("SGI Octane (IP30) Odyssey / Buzz Video Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("R28");
+
+module_init(odyssey_init);
+module_exit(odyssey_exit);
+
+MODULE_LICENSE("GPL");
diff -Naurp linux-2.6.26.orig/include/asm-mips/addrspace.h linux-2.6.26/include/asm-mips/addrspace.h
--- linux-2.6.26.orig/include/asm-mips/addrspace.h	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/include/asm-mips/addrspace.h	2008-07-25 03:14:40.000000000 -0400
@@ -54,6 +54,14 @@
 #define XPHYSADDR(a)            ((_ACAST64_(a)) &			\
 				 _CONST64_(0x000000ffffffffff))
 
+#ifndef __ASSEMBLY__
+#ifdef CONFIG_64BIT
+unsigned long kernel_physaddr(unsigned long);
+#else
+#define kernel_physaddr CPHYSADDR
+#endif
+#endif
+
 #ifdef CONFIG_64BIT
 
 /*
diff -Naurp linux-2.6.26.orig/include/asm-mips/dma.h linux-2.6.26/include/asm-mips/dma.h
--- linux-2.6.26.orig/include/asm-mips/dma.h	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/include/asm-mips/dma.h	2008-07-25 03:14:40.000000000 -0400
@@ -88,10 +88,15 @@
 /* don't care; ISA bus master won't work, ISA slave DMA supports 32bit addr */
 #define MAX_DMA_ADDRESS		PAGE_OFFSET
 #else
+/* Horrible hack for IP30; there has to be a better way */
+#ifdef CONFIG_SGI_IP30
+#define MAX_DMA_ADDRESS		(PAGE_OFFSET + 0xA0000000UL)
+#else
 #define MAX_DMA_ADDRESS		(PAGE_OFFSET + 0x01000000)
 #endif
 #define MAX_DMA_PFN		PFN_DOWN(virt_to_phys((void *)MAX_DMA_ADDRESS))
 #define MAX_DMA32_PFN		(1UL << (32 - PAGE_SHIFT))
+#endif
 
 /* 8237 DMA controllers */
 #define IO_DMA1_BASE	0x00	/* 8 bit slave DMA, channels 0..3 */
diff -Naurp linux-2.6.26.orig/include/asm-mips/mach-ip30/addrs.h linux-2.6.26/include/asm-mips/mach-ip30/addrs.h
--- linux-2.6.26.orig/include/asm-mips/mach-ip30/addrs.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/include/asm-mips/mach-ip30/addrs.h	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,36 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (c) 2004-2007 Stanislaw Skowronek
+ *               2007 Joshua Kinard <kumba@xxxxxxxxxx>
+ */
+#ifndef __ASM_MACH_IP30_ADDRS_H
+#define __ASM_MACH_IP30_ADDRS_H
+
+#include <asm/sn/types.h>
+
+#define IP30_WIDGET_XBOW	0
+#define IP30_WIDGET_HEART	8
+#define IP30_WIDGET_GFX_1	12
+#define IP30_WIDGET_BASEIO	15
+
+#define NODE_SWIN_BASE(nasid, widget) \
+	(0x0000000010000000 | (((unsigned long)(widget)) << 24))
+#define NODE_BWIN_BASE(nasid, widget) \
+	(0x0000001000000000 | (((unsigned long)(widget)) << 36))
+#define RAW_NODE_SWIN_BASE(nasid, widget) \
+	(0x9000000010000000 | (((unsigned long)(widget)) << 24))
+#define RAW_NODE_BWIN_BASE(nasid, widget) \
+	(0x9000001000000000 | (((unsigned long)(widget)) << 36))
+
+#define SWIN_SIZE		0x1000000
+#define BWIN_SIZE		0x1000000000L
+
+#define IP30_IO_PORT_BASE	0x9000000000000000
+
+#define IP30_MAX_PROM_MEM	0x40000000
+#define IP30_MEM_BASE		0x20000000
+
+#endif /* __ASM_MACH_IP30_ADDRS_H */
diff -Naurp linux-2.6.26.orig/include/asm-mips/mach-ip30/cpu-feature-overrides.h linux-2.6.26/include/asm-mips/mach-ip30/cpu-feature-overrides.h
--- linux-2.6.26.orig/include/asm-mips/mach-ip30/cpu-feature-overrides.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/include/asm-mips/mach-ip30/cpu-feature-overrides.h	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,39 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (c) 2004-2007 Stanislaw Skowronek
+ */
+
+#ifndef __ASM_MACH_IP30_CPU_FEATURE_OVERRIDES_H
+#define __ASM_MACH_IP30_CPU_FEATURE_OVERRIDES_H
+
+#define cpu_has_watch		1
+#define cpu_has_mips16		0
+#define cpu_has_divec		0
+#define cpu_has_vce		0
+#define cpu_has_cache_cdex_p	0
+#define cpu_has_cache_cdex_s	0
+#define cpu_has_prefetch	1
+#define cpu_has_mcheck		0
+#define cpu_has_ejtag		0
+
+#define cpu_has_llsc		1
+#define cpu_has_vtag_icache	0
+#define cpu_has_dc_aliases	0
+#define cpu_has_ic_fills_f_dc	0
+
+#define cpu_has_nofpuex		0
+#define cpu_has_64bits		1
+
+#define cpu_has_4kex		1
+#define cpu_has_4k_cache	1
+
+#define cpu_has_subset_pcaches	1
+
+#define cpu_dcache_line_size()	32
+#define cpu_icache_line_size()	64
+#define cpu_scache_line_size()	128
+
+#endif /* __ASM_MACH_IP30_CPU_FEATURE_OVERRIDES_H */
diff -Naurp linux-2.6.26.orig/include/asm-mips/mach-ip30/dma-coherence.h linux-2.6.26/include/asm-mips/mach-ip30/dma-coherence.h
--- linux-2.6.26.orig/include/asm-mips/mach-ip30/dma-coherence.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/include/asm-mips/mach-ip30/dma-coherence.h	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,90 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2006  Ralf Baechle <ralf@xxxxxxxxxxxxxx>
+ *               2007  Joshua Kinard <kumba@xxxxxxxxxx>
+ *
+ * derived from include/asm-mips/mach-ip27/dma-coherence.h
+ * and based on code found in the old dma-ip30.c, which is
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
+ */
+#ifndef __ASM_MACH_IP30_DMA_COHERENCE_H
+#define __ASM_MACH_IP30_DMA_COHERENCE_H
+
+#include <asm/mach-ip30/addrs.h>
+#include <asm/pci/bridge.h>
+
+#include <linux/dma-mapping.h>
+
+static inline dma_addr_t pdev_to_baddr(struct pci_dev *dev, dma_addr_t addr,
+					int virt, int size)
+{
+	if(dev->dma_mask == DMA_64BIT_MASK) {
+		if(virt)
+			return (BRIDGE_CONTROLLER(dev->bus)->baddr + addr) |
+				PCI64_ATTR_VIRTUAL;
+
+		if(size >= 0x200)
+			return (BRIDGE_CONTROLLER(dev->bus)->baddr + addr) |
+				PCI64_ATTR_PREF;
+
+		if(addr >= 0x20000000 || addr < 0xA0000000)
+			return (PCI32_DIRECT_BASE + addr - 0x20000000);
+
+		return (BRIDGE_CONTROLLER(dev->bus)->baddr + addr);
+	}
+
+	if(addr < 0x20000000 || addr >= 0xA0000000) {
+		printk(KERN_ERR
+			"BRIDGE: Mapping can't be realized in direct DMA.\n");
+		return -1;
+	}
+
+	return (PCI32_DIRECT_BASE + addr - 0x20000000);
+}
+
+static inline dma_addr_t dev_to_baddr(struct device *dev, dma_addr_t addr,
+					int virt, int size)
+{
+	if(dev)
+		return pdev_to_baddr(to_pci_dev(dev), addr, virt, size);
+	return addr;
+}
+
+struct device;
+
+static inline dma_addr_t plat_map_dma_mem(struct device *dev, void *addr,
+					size_t size)
+{
+	dma_addr_t pa = dev_to_baddr(dev, virt_to_phys(addr), 1, size);
+
+	return pa;
+}
+
+static dma_addr_t plat_map_dma_mem_page(struct device *dev, struct page *page,
+					size_t size)
+{
+	dma_addr_t pa = dev_to_baddr(dev, page_to_phys(page), 0, size);
+
+	return pa;
+}
+
+static unsigned long plat_dma_addr_to_phys(dma_addr_t dma_addr)
+{
+	return dma_addr & (0xffUL << 56);
+}
+
+static inline void plat_unmap_dma_mem(dma_addr_t dma_addr)
+{
+	/* Empty */
+}
+
+static inline int plat_device_is_coherent(struct device *dev)
+{
+	return 1;		/* IP30 non-cohernet mode is unsupported
+				 * (does it even have one?) */
+}
+
+#endif /* __ASM_MACH_IP30_DMA_COHERENCE_H */
diff -Naurp linux-2.6.26.orig/include/asm-mips/mach-ip30/ds1687.h linux-2.6.26/include/asm-mips/mach-ip30/ds1687.h
--- linux-2.6.26.orig/include/asm-mips/mach-ip30/ds1687.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/include/asm-mips/mach-ip30/ds1687.h	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,56 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (c) 2007 Joshua Kinard <kumba@xxxxxxxxxx>
+ */
+#ifndef __ASM_MACH_IP30_DS1687_H
+#define __ASM_MACH_IP30_DS1687_H
+
+#include <asm/sn/types.h>
+
+#include <linux/ioc3.h>
+
+
+#define RTC_ADDR (*(unsigned char *) ((unsigned long)(ioc3->vma) + IOC3_BYTEBUS_DEV1))
+#define RTC_DATA (*(unsigned char *) ((unsigned long)(ioc3->vma) + IOC3_BYTEBUS_DEV2))
+
+/* Registers */
+#define DS1687_SECNDS		0x00
+#define DS1687_SECNDS_ALRM	0x01
+#define DS1687_MINS		0x02
+#define DS1687_MINS_ALRM	0x03
+#define DS1687_HOUR		0x04
+#define DS1687_HOUR_ALRM	0x05
+#define DS1687_DAY		0x06
+#define DS1687_DATE		0x07
+#define DS1687_MONTH		0x08
+#define DS1687_YEAR		0x09
+#define DS1687_CTRL_A		0x0a
+#define DS1687_CTRL_B		0x0b
+#define DS1687_CTRL_C		0x0c
+#define DS1687_CTRL_D		0x0d
+#define DS1687_CENTURY		0x48
+#define DS1687_DATE_ALRM	0x49
+#define DS1687_EXT_CTRL_4A	0x4a
+#define DS1687_EXT_CTRL_4B	0x4b
+
+
+/* Bitfields of the registers
+ *
+ * Each bitfield can have a different meaning depending
+ * on the register in question.  For more information,
+ * see the DS1687/DS1685 datasheet for the various
+ * meanings and functions of the bitfields with their
+ * respective registers
+ */
+#define DS1687_BIT1		0x02
+#define DS1687_BIT2		0x04
+#define DS1687_BIT3		0x08
+#define DS1687_BIT4		0x10
+#define DS1687_BIT5		0x20
+#define DS1687_BIT6		0x40
+#define DS1687_BIT7		0x80
+
+#endif /* __ASM_MACH_IP30_DS1687_H */
diff -Naurp linux-2.6.26.orig/include/asm-mips/mach-ip30/heart.h linux-2.6.26/include/asm-mips/mach-ip30/heart.h
--- linux-2.6.26.orig/include/asm-mips/mach-ip30/heart.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/include/asm-mips/mach-ip30/heart.h	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,231 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
+ *		 2007 Joshua Kinard <kumba@xxxxxxxxxx>
+ */
+
+#ifndef __ASM_MACH_IP30_HEART_H
+#define __ASM_MACH_IP30_HEART_H
+
+#include <linux/types.h>
+
+/* HEART internal register space */
+#define HEART_PIU_BASE		0x900000000ff00000
+
+/* full addresses */
+#define HEART_MODE		((volatile ulong *)0x900000000ff00000)
+#define HEART_SDRAM_MODE	((volatile ulong *)0x900000000ff00008)
+#define HEART_MEM_REF		((volatile ulong *)0x900000000ff00010)
+#define HEART_MEM_REQ_ARB	((volatile ulong *)0x900000000ff00018)
+#define	HEART_MEMCFG0		((volatile ulong *)0x900000000ff00020)
+#define	HEART_MEMCFG1		((volatile ulong *)0x900000000ff00028)
+#define	HEART_MEMCFG2		((volatile ulong *)0x900000000ff00030)
+#define	HEART_MEMCFG3		((volatile ulong *)0x900000000ff00038)
+#define HEART_FC_MODE		((volatile ulong *)0x900000000ff00040)
+#define HEART_FC_TIMER_LIMIT	((volatile ulong *)0x900000000ff00048)
+#define HEART_FC0_ADDR		((volatile ulong *)0x900000000ff00050)
+#define HEART_FC1_ADDR		((volatile ulong *)0x900000000ff00058)
+#define HEART_FC0_CR_CNT	((volatile ulong *)0x900000000ff00060)
+#define HEART_FC1_CR_CNT	((volatile ulong *)0x900000000ff00068)
+#define HEART_FC0_TIMER		((volatile ulong *)0x900000000ff00070)
+#define HEART_FC1_TIMER		((volatile ulong *)0x900000000ff00078)
+#define HEART_STATUS		((volatile ulong *)0x900000000ff00080)
+#define HEART_BERR_ADDR		((volatile ulong *)0x900000000ff00088)
+#define HEART_BERR_MISC		((volatile ulong *)0x900000000ff00090)
+#define HEART_MEMERR_ADDR	((volatile ulong *)0x900000000ff00098)
+#define HEART_MEMERR_DATA	((volatile ulong *)0x900000000ff000a0)
+#define HEART_PIUR_ACC_ERR	((volatile ulong *)0x900000000ff000a8)
+#define	HEART_MLAN_CLK_DIV	((volatile ulong *)0x900000000ff000b0)
+#define	HEART_MLAN_CTL		((volatile ulong *)0x900000000ff000b8)
+#define HEART_IMR(x)		((volatile ulong *)0x900000000ff10000 + (8 * (x)))
+#define HEART_SET_ISR		((volatile ulong *)0x900000000ff10020)
+#define HEART_CLR_ISR		((volatile ulong *)0x900000000ff10028)
+#define HEART_ISR		((volatile ulong *)0x900000000ff10030)
+#define HEART_IMSR		((volatile ulong *)0x900000000ff10038)
+#define HEART_CAUSE		((volatile ulong *)0x900000000ff10040)
+#define HEART_COUNT		((volatile ulong *)0x900000000ff20000)	/* 52-bit counter */
+#define HEART_COMPARE		((volatile ulong *)0x900000000ff30000)	/* 24-bit compare */
+#define HEART_TRIGGER		((volatile ulong *)0x900000000ff40000)
+#define HEART_PRID		((volatile ulong *)0x900000000ff50000)
+#define HEART_SYNC		((volatile ulong *)0x900000000ff60000)
+
+/* HEART Masks */
+#define HEART_ATK_MASK		0x0007ffffffffffff	/* HEART Attack Mask */
+#define HEART_ACK_ALL_MASK	0xffffffffffffffff	/* Acknowledge everything */
+#define HEART_CLR_ALL_MASK	0x0000000000000000	/* Clear all */
+#define HEART_BR_ERR_MASK	0x7ff8000000000000	/* BRIDGE Error Mask */
+#define HEART_ERR_MASK		0x1ff			/* HEART Error Mask */
+#define HEART_ERR_MASK_START	51			/* HEART Error start */
+#define HEART_ERR_MASK_END	63			/* HEART Error end */
+
+#define NON_HEART_IRQ_ST	0x0004000000000000	/* Where non-HEART IRQs begin */
+
+/* bits in the HEART_MODE registers */
+#define	HM_PROC_DISABLE_SHFT	60
+#define	HM_PROC_DISABLE_MSK	((ulong) 0xf << HM_PROC_DISABLE_SHFT)
+#define	HM_PROC_DISABLE(x)	((ulong) 0x1 << (x) + HM_PROC_DISABLE_SHFT)
+#define	HM_MAX_PSR		((ulong) 0x7 << 57)
+#define	HM_MAX_IOSR		((ulong) 0x7 << 54)
+#define	HM_MAX_PEND_IOSR	((ulong) 0x7 << 51)
+
+#define	HM_TRIG_SRC_SEL_MSK	((ulong) 0x7 << 48)
+#define	HM_TRIG_HEART_EXC	((ulong) 0x0 << 48)	/* power-up default */
+#define	HM_TRIG_REG_BIT		((ulong) 0x1 << 48)
+#define	HM_TRIG_SYSCLK		((ulong) 0x2 << 48)
+#define	HM_TRIG_MEMCLK_2X	((ulong) 0x3 << 48)
+#define	HM_TRIG_MEMCLK		((ulong) 0x4 << 48)
+#define	HM_TRIG_IOCLK		((ulong) 0x5 << 48)
+
+#define	HM_PIU_TEST_MODE	((ulong) 0xf << 40)
+
+#define	HM_GP_FLAG_MSK		((ulong) 0xf << 36)
+#define	HM_GP_FLAG(x)		((ulong) 0x1 << (x) + 36)
+
+#define	HM_MAX_PROC_HYST	((ulong) 0xf << 32)
+#define	HM_LLP_WRST_AFTER_RST	((ulong) 0x1 << 28)
+#define	HM_LLP_LINK_RST		((ulong) 0x1 << 27)
+#define	HM_LLP_WARM_RST		((ulong) 0x1 << 26)
+#define	HM_COR_ECC_LCK		((ulong) 0x1 << 25)
+#define	HM_REDUCED_PWR		((ulong) 0x1 << 24)
+#define HM_COLD_RST		((ulong) 0x1 << 23)
+#define	HM_SW_RST		((ulong) 0x1 << 22)
+#define	HM_MEM_FORCE_WR		((ulong) 0x1 << 21)
+#define	HM_DB_ERR_GEN		((ulong) 0x1 << 20)
+#define	HM_SB_ERR_GEN		((ulong) 0x1 << 19)
+#define	HM_CACHED_PIO_EN	((ulong) 0x1 << 18)
+#define	HM_CACHED_PROM_EN	((ulong) 0x1 << 17)
+#define	HM_PE_SYS_COR_ERE	((ulong) 0x1 << 16)
+#define	HM_GLOBAL_ECC_EN	((ulong) 0x1 << 15)
+#define	HM_IO_COH_EN		((ulong) 0x1 << 14)
+#define	HM_INT_EN		((ulong) 0x1 << 13)
+#define	HM_DATA_CHK_EN		((ulong) 0x1 << 12)
+#define	HM_REF_EN		((ulong) 0x1 << 11)
+#define	HM_BAD_SYSWR_ERE	((ulong) 0x1 << 10)
+#define	HM_BAD_SYSRD_ERE	((ulong) 0x1 << 9)
+#define	HM_SYSSTATE_ERE		((ulong) 0x1 << 8)
+#define	HM_SYSCMD_ERE		((ulong) 0x1 << 7)
+#define	HM_NCOR_SYS_ERE		((ulong) 0x1 << 6)
+#define	HM_COR_SYS_ERE		((ulong) 0x1 << 5)
+#define	HM_DATA_ELMNT_ERE	((ulong) 0x1 << 4)
+#define	HM_MEM_ADDR_PROC_ERE	((ulong) 0x1 << 3)
+#define	HM_MEM_ADDR_IO_ERE	((ulong) 0x1 << 2)
+#define	HM_NCOR_MEM_ERE		((ulong) 0x1 << 1)
+#define	HM_COR_MEM_ERE		((ulong) 0x1 << 0)
+
+/* bits in the memory refresh register */
+#define HEART_MEMREF_REFS(x)	((ulong) (0xf & (x)) << 16)	
+#define HEART_MEMREF_PERIOD(x)	((ulong) (0xffff & (x)))
+#define HEART_MEMREF_REFS_VAL	HEART_MEMREF_REFS(8)
+#define HEART_MEMREF_PERIOD_VAL HEART_MEMREF_PERIOD(0x4000)
+#define HEART_MEMREF_VAL	(HEART_MEMREF_REFS_VAL | HEART_MEMREF_PERIOD_VAL)
+
+/* bits in the memory request arbitrarion register */
+#define	HEART_MEMARB_IODIS	(1  << 20)
+#define	HEART_MEMARB_MAXPMWRQS	(15 << 16)
+#define	HEART_MEMARB_MAXPMRRQS	(15 << 12)
+#define	HEART_MEMARB_MAXPMRQS	(15 << 8)
+#define	HEART_MEMARB_MAXRRRQS	(15 << 4)
+#define	HEART_MEMARB_MAXGBRRQS	(15)
+
+/* bits in the memory configuration registers */
+#define	HEART_MEMCFG_VLD	0x80000000	/* bank valid bit */
+#define	HEART_MEMCFG_RAM_MSK	0x003f0000	/* RAM mask */
+#define	HEART_MEMCFG_DENSITY	0x01c00000	/* RAM density */
+#define	HEART_MEMCFG_RAM_SHFT	16
+#define	HEART_MEMCFG_ADDR_MSK	0x000001ff	/* base address mask */
+#define	HEART_MEMCFG_UNIT_SHFT	25		/* 32 MB is the HEART's basic memory unit */
+
+/* bits in the status register */
+#define HEART_STAT_HSTL_SDRV		((ulong) 0x1 << 14)
+#define	HEART_STAT_FC_CR_OUT(x)		((ulong) 0x1 << (x) + 12)
+#define	HEART_STAT_DIR_CNNCT		((ulong) 0x1 << 11)
+#define	HEART_STAT_TRITON		((ulong) 0x1 << 10)
+#define	HEART_STAT_R4K			((ulong) 0x1 << 9)
+#define	HEART_STAT_BIG_ENDIAN		((ulong) 0x1 << 8)
+#define	HEART_STAT_PROC_ACTIVE_SHFT	4
+#define	HEART_STAT_PROC_ACTIVE_MSK	((ulong) 0xf << HEART_STAT_PROC_ACTIVE_SHFT)
+#define	HEART_STAT_PROC_ACTIVE(x)	((ulong) 0x1 << (x) + HEART_STAT_PROC_ACTIVE_SHFT)
+#define	HEART_STAT_WIDGET_ID	0xf
+
+/* interrupt handling */
+#define HEART_VEC_TO_IBIT(vec)	((ulong) 1 << (vec))
+#define	HEART_INT_VECTORS	64		/* how many vectors we manage */
+
+/* IP7 is the CPU count/compare, IP1 and IP0 are SW2 and SW1 */
+#define HEART_INT_LEVEL4	0xfff8000000000000	/* IP6 */
+#define HEART_INT_LEVEL3	0x0004000000000000	/* IP5 */
+#define HEART_INT_LEVEL2	0x0003ffff00000000	/* IP4 */
+#define HEART_INT_LEVEL1	0x00000000ffff0000	/* IP3 */
+#define HEART_INT_LEVEL0	0x000000000000ffff	/* IP2 */
+#define HEART_INT_L4SHIFT	51
+#define HEART_INT_L4MASK	0x1fff
+#define HEART_INT_L3SHIFT	50
+#define HEART_INT_L3MASK	0x1
+#define HEART_INT_L2SHIFT	32
+#define HEART_INT_L2MASK	0x3ffff
+#define HEART_INT_L1SHIFT	16
+#define HEART_INT_L1MASK	0xffff
+#define HEART_INT_L0SHIFT	0
+#define HEART_INT_L0MASK	0xffff
+
+/* errors */
+#define IRQ_HEART_ERR		63
+#define IRQ_BUS_ERR_P(x)	(59 + (x))
+#define IRQ_XT_ERR(xid)		(51 + ((xid) - 1) & 7)
+#define IRQ_XT_ERR_XBOW		58
+#define IRQ_XT_ERR_F		57
+#define IRQ_XT_ERR_E		56
+#define IRQ_XT_ERR_D		55
+#define IRQ_XT_ERR_C		54
+#define IRQ_XT_ERR_B		53
+#define IRQ_XT_ERR_A		52
+#define IRQ_XT_ERR_9		51
+/* count/compare timer */
+#define IRQ_HEART_CC		50
+/* level 2 interrupts */
+#define IRQ_IPI_P(x)		(46 + (x))
+#define IRQ_TIMER_P(x)		(42 + (x))
+#define IRQ_LOCAL_L2_BASE	32
+#define IRQ_LOCAL_L2_NUM	9
+/* level 1 interrupts */
+#define IRQ_LOCAL_L1_BASE	16
+#define IRQ_LOCAL_L1_NUM	16
+/* level 0 interrupts */
+#define IRQ_LOCAL_L0_BASE	3
+#define IRQ_LOCAL_L0_NUM	13
+#define IRQ_FC_HIGH		2
+#define IRQ_FC_LOW		1
+#define IRQ_GENERIC		0
+
+#define SOFT_IRQ_COUNT		51
+#define TIMER_IRQ		63
+
+/* bits in the HEART_CAUSE register */
+#define	HC_PE_SYS_COR_ERR_MSK		((ulong) 0xf << 60)
+#define	HC_PE_SYS_COR_ERR(x)		((ulong) 0x1 << (x) + 60)
+#define	HC_PIOWDB_OFLOW			((ulong) 0x1 << 44)
+#define	HC_PIORWRB_OFLOW		((ulong) 0x1 << 43)
+#define HC_PIUR_ACC_ERR			((ulong) 0x1 << 42)
+#define HC_BAD_SYSWR_ERR		((ulong) 0x1 << 41)
+#define HC_BAD_SYSRD_ERR		((ulong) 0x1 << 40)
+#define	HC_SYSSTATE_ERR_MSK		((ulong) 0xf << 36)
+#define	HC_SYSSTATE_ERR(x)		((ulong) 0x1 << (x) + 36)
+#define HC_SYSCMD_ERR_MSK		((ulong) 0xf << 32)
+#define HC_SYSCMD_ERR(x)		((ulong) 0x1 << (x) + 32)
+#define HC_NCOR_SYSAD_ERR_MSK		((ulong) 0xf << 28)
+#define HC_NCOR_SYSAD_ERR(x)		((ulong) 0x1 << (x) + 28)
+#define HC_COR_SYSAD_ERR_MSK		((ulong) 0xf << 24)
+#define HC_COR_SYSAD_ERR(x)		((ulong) 0x1 << (x) + 24)
+#define HC_DATA_ELMNT_ERR_MSK		((ulong) 0xf << 20)
+#define HC_DATA_ELMNT_ERR(x)		((ulong) 0x1 << (x) + 20)
+#define	HC_WIDGET_ERR			((ulong) 0x1 << 16)
+#define	HC_MEM_ADDR_ERR_PROC_MSK	((ulong) 0xf << 4)
+#define	HC_MEM_ADDR_ERR_PROC(x)		((ulong) 0x1 << (x) + 4)
+#define HC_MEM_ADDR_ERR_IO		((ulong) 0x1 << 2)
+#define HC_NCOR_MEM_ERR			((ulong) 0x1 << 1)
+#define HC_COR_MEM_ERR			((ulong) 0x1 << 0)
+
+#endif /* __ASM_MACH_IP30_HEART_H */
diff -Naurp linux-2.6.26.orig/include/asm-mips/mach-ip30/leds.h linux-2.6.26/include/asm-mips/mach-ip30/leds.h
--- linux-2.6.26.orig/include/asm-mips/mach-ip30/leds.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/include/asm-mips/mach-ip30/leds.h	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,29 @@
+#ifndef IP30LEDS_H
+#define IP30LEDS_H
+
+/*
+ * The LEDs driver reads in a stream of opcodes, two bytes each. The highest
+ * two bits of first byte define the opcode type. Next six bits are parameter
+ * one, and the last eight bits are parameter two.
+ * If a LEDS_LOOP(0) opcode is encountered, the stream is terminated on this
+ * opcode and no operations are performed until a new stream is loaded.
+ * If a LEDS_LOOP(n>0) opcode is encountered, the whole stream is looped.
+ * If neither of these opcodes appears until the end of the stream, the behavior
+ * is the same as at LEDS_LOOP(0), however a warning will be printed.
+ */
+
+#define LEDS_OP_SET	0
+	/* set LED brightness; low bits select LED, next byte sets brightness */
+#define LEDS_OP_WAIT	1
+	/* wait for n ms, where n=param2 * (1 << param1); if n = 0 then stop */
+#define LEDS_OP_LOOP	2
+	/* restart the LEDs, waiting for n ms; if n = 0 then stop */
+#define LEDS_OP_RSVD	3
+	/* reserved opcode */
+
+/*
+ * Anyone who wonders why you can't loop without a delay should consider the
+ * fact that we are processing this opcode inside the kernel.
+ */
+
+#endif
diff -Naurp linux-2.6.26.orig/include/asm-mips/mach-ip30/mangle-port.h linux-2.6.26/include/asm-mips/mach-ip30/mangle-port.h
--- linux-2.6.26.orig/include/asm-mips/mach-ip30/mangle-port.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/include/asm-mips/mach-ip30/mangle-port.h	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,25 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003, 2004 Ralf Baechle
+ */
+#ifndef __ASM_MACH_IP30_MANGLE_PORT_H
+#define __ASM_MACH_IP30_MANGLE_PORT_H
+
+#define __swizzle_addr_b(port)	((port)^3)
+#define __swizzle_addr_w(port)	((port)^2)
+#define __swizzle_addr_l(port)	(port)
+#define __swizzle_addr_q(port)	(port)
+
+#define ioswabb(a,x)		(x)
+#define __mem_ioswabb(a,x)	(x)
+#define ioswabw(a,x)		(x)
+#define __mem_ioswabw(a,x)	cpu_to_le16(x)
+#define ioswabl(a,x)		(x)
+#define __mem_ioswabl(a,x)	cpu_to_le32(x)
+#define ioswabq(a,x)		(x)
+#define __mem_ioswabq(a,x)	cpu_to_le32(x)
+
+#endif /* __ASM_MACH_IP30_MANGLE_PORT_H */
diff -Naurp linux-2.6.26.orig/include/asm-mips/mach-ip30/pcibr.h linux-2.6.26/include/asm-mips/mach-ip30/pcibr.h
--- linux-2.6.26.orig/include/asm-mips/mach-ip30/pcibr.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/include/asm-mips/mach-ip30/pcibr.h	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,51 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Definitions for the built-in PCI bridge
+ * Copyright (C) 2004-2007 Stanislaw Skowronek
+ */
+#ifndef __ASM_MACH_IP30_PCIBR_H
+#define __ASM_MACH_IP30_PCIBR_H
+
+#include <asm/mach-ip30/addrs.h>
+#include <asm/pci/bridge.h>
+
+/* Xtalk */
+#define PCIBR_XTALK_MFGR	0x036
+#define PCIBR_XTALK_PART	0xc002
+
+#define PCIBR_OFFSET_MEM	0x200000
+#define PCIBR_OFFSET_IO		0xa00000
+#define PCIBR_OFFSET_END	0xc00000
+
+#define PCIBR_DIR_ALLOC_BASE	0x1000000
+
+#define PCIBR_XIO_SEES_HEART	0x00000080	/* This is how XIO sees HEART_ISR */
+
+#define PCIBR_IRQ_BASE		8
+
+#define PCIBR_MAX_PCI_BUSSES	8		/* Max PCI Busses/Bridges */
+#define PCIBR_MAX_DEV_PCIBUS	8		/* Max # of devices per bus */
+
+
+
+/* Occult Numbers of the Black Priesthood of Ancient Mu
+ *
+ * Meticuously derived by studying dissassembly,
+ * patents, and random guesses
+ */
+#define PCIBR_ANCIENT_MU_EVEN_RESP	0xddcc9988
+#define PCIBR_ANCIENT_MU_ODD_RESP	0xddcc9988
+#define PCIBR_ANCIENT_MU_INT_DEVICE	0xff000000
+#define PCIBR_ANCIENT_MU_INT_ENABLE	0x7ffffe00
+#define PCIBR_ANCIENT_MU_INT_MODE	0x00000000
+
+
+/* Used by pci-ip30.c and ip30-irq.c */
+extern unsigned int ip30_irq_in_bridge[PCIBR_MAX_PCI_BUSSES * PCIBR_MAX_DEV_PCIBUS];
+extern bridge_t *ip30_irq_bridge[PCIBR_MAX_PCI_BUSSES * PCIBR_MAX_DEV_PCIBUS];
+
+
+#endif /* __ASM_MACH_IP30_PCIBR_H */
diff -Naurp linux-2.6.26.orig/include/asm-mips/mach-ip30/racermp.h linux-2.6.26/include/asm-mips/mach-ip30/racermp.h
--- linux-2.6.26.orig/include/asm-mips/mach-ip30/racermp.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/include/asm-mips/mach-ip30/racermp.h	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,34 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2005-2007 Stanislaw Skowronek
+ */
+
+#ifndef __ASM_MACH_IP30_RACERMP_H
+#define __ASM_MACH_IP30_RACERMP_H
+
+#include <linux/types.h>
+
+#define MPCONF_MAGIC	0xbaddeed2
+#define	MPCONF_ADDR	0xa800000000000600L
+#define MPCONF_SIZE  	0x80
+#define MPCONF(x)	(MPCONF_ADDR + (x) * MPCONF_SIZE)
+
+#define MP_NCPU		4 /* able to do 4, but only 2 physically possible */
+
+#define MP_MAGIC(x)	(*(volatile uint *)	(MPCONF(x) + 0x00))
+#define MP_PRID(x)	(*(volatile uint *)	(MPCONF(x) + 0x04))
+#define MP_PHYSID(x)	(*(volatile uint *)	(MPCONF(x) + 0x08))
+#define MP_VIRTID(x)	(*(volatile uint *)	(MPCONF(x) + 0x0c))
+#define MP_SCACHESZ(x)	(*(volatile uint *)	(MPCONF(x) + 0x10))
+#define MP_FANLOADS(x)	(*(volatile ushort *)	(MPCONF(x) + 0x14))
+#define MP_LAUNCH(x)	(*(volatile void **)	(MPCONF(x) + 0x18))
+#define MP_RNDVZ(x)	(*(volatile void **)	(MPCONF(x) + 0x20))
+#define MP_STACKADDR(x)	(*(volatile ulong *)	(MPCONF(x) + 0x40))
+#define MP_LPARM(x)	(*(volatile ulong *)	(MPCONF(x) + 0x48))
+#define MP_RPARM(x)	(*(volatile ulong *)	(MPCONF(x) + 0x50))
+#define MP_IDLEFLAG(x)	(*(volatile uint *)	(MPCONF(x) + 0x58))
+
+#endif /* __ASM_MACH_IP30_RACERMP_H */
diff -Naurp linux-2.6.26.orig/include/asm-mips/mach-ip30/war.h linux-2.6.26/include/asm-mips/mach-ip30/war.h
--- linux-2.6.26.orig/include/asm-mips/mach-ip30/war.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/include/asm-mips/mach-ip30/war.h	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,25 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2002, 2004, 2007 by Ralf Baechle <ralf@xxxxxxxxxxxxxx>
+ */
+#ifndef __ASM_MIPS_MACH_IP30_WAR_H
+#define __ASM_MIPS_MACH_IP30_WAR_H
+
+#define R4600_V1_INDEX_ICACHEOP_WAR	0
+#define R4600_V1_HIT_CACHEOP_WAR	0
+#define R4600_V2_HIT_CACHEOP_WAR	0
+#define R5432_CP0_INTERRUPT_WAR		0
+#define BCM1250_M3_WAR			0
+#define SIBYTE_1956_WAR			0
+#define MIPS4K_ICACHE_REFILL_WAR	0
+#define MIPS_CACHE_SYNC_WAR		0
+#define TX49XX_ICACHE_INDEX_INV_WAR	0
+#define RM9000_CDEX_SMP_WAR		0
+#define ICACHE_REFILLS_WORKAROUND_WAR	0
+#define R10000_LLSC_WAR			1
+#define MIPS34K_MISSED_ITLB_WAR		0
+
+#endif /* __ASM_MIPS_MACH_IP30_WAR_H */
diff -Naurp linux-2.6.26.orig/include/asm-mips/mach-ip30/xtalk.h linux-2.6.26/include/asm-mips/mach-ip30/xtalk.h
--- linux-2.6.26.orig/include/asm-mips/mach-ip30/xtalk.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/include/asm-mips/mach-ip30/xtalk.h	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,70 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
+ *		 2007 Joshua Kinard <kumba@xxxxxxxxxx>
+ */
+
+#ifndef __ASM_MACH_IP30_XTALK_H
+#define __ASM_MACH_IP30_XTALK_H
+
+#define IP30_XTALK_NUM_WID	16
+
+/* Xtalk Device Mfgr IDs */
+#define XTALK_XBOW_MFGR_ID	0x0	/* IP30 XBow Chip */
+#define XTALK_XXBOW_MFGR_ID	0x0	/* IP35 Xbow + XBridge chip */
+#define XTALK_ODYS_MFGR_ID	0x023	/* Odyssey / VPro */
+#define XTALK_TPU_MFGR_ID	0x024	/* Tensor Processor Unit */
+#define XTALK_XBRDG_MFGR_ID	0x024
+#define XTALK_HEART_MFGR_ID	0x036	/* IP30 HEART Chip */
+#define XTALK_BRIDG_MFGR_ID	0x036	/* PCI Bridge */
+#define XTALK_HUB_MFGR_ID	0x036
+#define XTALK_BDRCK_MFGR_ID	0x036	/* IP35 Hub Chip */
+#define XTALK_IMPCT_MFGR_ID	0x2aa	/* HQ4 / ImpactSR */
+#define XTALK_KONA_MFGR_ID	0x2aa
+#define XTALK_NULL_MFGR_ID	-1	/* NULL */
+
+/* Xtalk Device Part IDs */
+#define XTALK_XBOW_PART_ID	0x0
+#define XTALK_XXBOW_PART_ID	0xd000
+#define XTALK_ODYS_PART_ID	0xc013
+#define XTALK_TPU_PART_ID	0xc202
+#define XTALK_XBRDG_PART_ID	0xd002
+#define XTALK_HEART_PART_ID	0xc001
+#define XTALK_BRIDG_PART_ID	0xc002
+#define XTALK_HUB_PART_ID	0xc101
+#define XTALK_BDRCK_PART_ID	0xc110
+#define XTALK_IMPCT_PART_ID	0xc003
+#define XTALK_KONA_PART_ID	0xc102
+#define XTALK_NULL_PART_ID	-1
+
+/* Xtalk Misc */
+#define XTALK_NODEV		0xffffffff
+#define XTALK_LOW_DEV		8	/* Lowest dev possible (HEART) */
+#define XTALK_HIGH_DEV		15	/* Highest dev possible (BRIDGE) */
+#define XTALK_XBOW		0	/* XBow is always 0 */
+#define XTALK_HEART		8	/* HEART is always 8 */
+#define XTALK_PCIBR		13	/* PCI Cage is always 13 */
+#define XTALK_BRIDGE		15	/* Main Bridge is always 15 */
+#define XTALK_XIO1		12	/* Main XIO Slot / GFX Card */
+
+/* Xbow macros */
+#define XBOW_REG_LINK_STAT_0		0x114
+#define XBOW_REG_LINK_BLOCK_SIZE	0x40
+#define XBOW_REG_LINK_ALIVE		0x80000000
+
+/* For Xtalk Widget identification */
+struct widget_ident {
+	unsigned mfgr;
+	unsigned part;
+	char *name;
+	char *revs[16];
+};
+
+unsigned ip30_xtalk_get_id(int wid);
+unsigned long ip30_xtalk_swin(int wid);
+int ip30_xtalk_find(unsigned mfgr, unsigned part, int last);
+
+#endif /* __ASM_MACH_IP30_XTALK_H */
diff -Naurp linux-2.6.26.orig/include/asm-mips/pci/bridge.h linux-2.6.26/include/asm-mips/pci/bridge.h
--- linux-2.6.26.orig/include/asm-mips/pci/bridge.h	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/include/asm-mips/pci/bridge.h	2008-07-25 03:14:40.000000000 -0400
@@ -841,6 +841,17 @@ struct bridge_controller {
 	unsigned int 		irq_cpu;
 	dma64_addr_t		baddr;
 	unsigned int		pci_int[8];
+#ifdef CONFIG_SGI_IP30
+	int			bridge_rev;
+	unsigned int		irq_base;
+	int			slot_be[8];
+	int			slot_bs[8];
+	unsigned int		win_p[8];
+	int			win_io[8];
+	int			win_be[8];
+	unsigned int		dio_p;
+	unsigned int		d32_p;
+#endif
 };
 
 #define BRIDGE_CONTROLLER(bus) \
diff -Naurp linux-2.6.26.orig/include/asm-mips/pci.h linux-2.6.26/include/asm-mips/pci.h
--- linux-2.6.26.orig/include/asm-mips/pci.h	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/include/asm-mips/pci.h	2008-07-25 03:14:40.000000000 -0400
@@ -39,6 +39,13 @@ struct pci_controller {
 	   and XFree86. Eventually will be removed. */
 	unsigned int need_domain_info;
 
+	/* called just before pci_scan_bus is executed */
+	int (*pre_scan)(struct pci_controller *);
+	/* called after pci_scan_bus is executed */
+	int (*post_scan)(struct pci_controller *, struct pci_bus *);
+	/* called in pcibios_enable_resources */
+	int (*pre_enable)(struct pci_controller *, struct pci_dev *, int);
+
 	int iommu;
 
 	/* Optional access methods for reading/writing the bus number
diff -Naurp linux-2.6.26.orig/include/linux/fb.h linux-2.6.26/include/linux/fb.h
--- linux-2.6.26.orig/include/linux/fb.h	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/include/linux/fb.h	2008-07-25 03:14:40.000000000 -0400
@@ -129,6 +129,8 @@ struct dentry;
 #define FB_ACCEL_NEOMAGIC_NM2230 96	/* NeoMagic NM2230              */
 #define FB_ACCEL_NEOMAGIC_NM2360 97	/* NeoMagic NM2360              */
 #define FB_ACCEL_NEOMAGIC_NM2380 98	/* NeoMagic NM2380              */
+#define FB_ACCEL_SGI_IMPACTSR   666	/* SGI Octane I/E (IMPACTSR)	*/
+#define FB_ACCEL_SGI_ODYSSEY    668	/* SGI Octane Vx (ODYSSEY)	*/
 
 #define FB_ACCEL_SAVAGE4        0x80	/* S3 Savage4                   */
 #define FB_ACCEL_SAVAGE3D       0x81	/* S3 Savage3D                  */
diff -Naurp linux-2.6.26.orig/include/linux/miscdevice.h linux-2.6.26/include/linux/miscdevice.h
--- linux-2.6.26.orig/include/linux/miscdevice.h	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/include/linux/miscdevice.h	2008-07-25 03:14:40.000000000 -0400
@@ -12,6 +12,7 @@
 #define APOLLO_MOUSE_MINOR 7
 #define PC110PAD_MINOR 9
 /*#define ADB_MOUSE_MINOR 10	FIXME OBSOLETE */
+#define LEDS_MINOR	42
 #define WATCHDOG_MINOR		130	/* Watchdog timer     */
 #define TEMP_MINOR		131	/* Temperature Sensor */
 #define RTC_MINOR 135
diff -Naurp linux-2.6.26.orig/include/linux/pci_ids.h linux-2.6.26/include/linux/pci_ids.h
--- linux-2.6.26.orig/include/linux/pci_ids.h	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/include/linux/pci_ids.h	2008-07-25 03:14:40.000000000 -0400
@@ -946,6 +946,7 @@
 #define PCI_VENDOR_ID_SGI		0x10a9
 #define PCI_DEVICE_ID_SGI_IOC3		0x0003
 #define PCI_DEVICE_ID_SGI_LITHIUM	0x1002
+#define PCI_DEVICE_ID_SGI_RAD1		0x0005
 #define PCI_DEVICE_ID_SGI_IOC4		0x100a
 
 #define PCI_VENDOR_ID_WINBOND		0x10ad
diff -Naurp linux-2.6.26.orig/include/sound/rad1.h linux-2.6.26/include/sound/rad1.h
--- linux-2.6.26.orig/include/sound/rad1.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/include/sound/rad1.h	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,119 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
+ */
+
+#ifndef _SOUND_RAD1_H
+#define _SOUND_RAD1_H
+
+#include <linux/types.h>
+
+#define RAD1_ADATRX	0
+#define RAD1_AESRX	1
+#define RAD1_ATOD	2
+#define RAD1_ADATSUBRX	3
+#define RAD1_AESSUBRX	4
+#define RAD1_ADATTX	5
+#define RAD1_AESTX	6
+#define RAD1_DTOA	7
+#define RAD1_STATUS	8
+
+struct rad1regs {
+	u32 pci_status;			/* 0x00000000 */
+	u32 adat_rx_msc_ust; 		/* 0x00000004 */
+	u32 adat_rx_msc0_submsc; 	/* 0x00000008 */
+	u32 aes_rx_msc_ust;	 	/* 0x0000000c */
+	u32 aes_rx_msc0_submsc; 	/* 0x00000010 */
+	u32 atod_msc_ust;		/* 0x00000014 */
+	u32 atod_msc0_submsc; 		/* 0x00000018 */
+	u32 adat_tx_msc_ust; 		/* 0x0000001c */
+	u32 adat_tx_msc0_submsc; 	/* 0x00000020 */
+	u32 aes_tx_msc_ust; 		/* 0x00000024 */
+	u32 aes_tx_msc0_submsc; 	/* 0x00000028 */
+	u32 dtoa_msc_ust; 		/* 0x0000002c */
+	u32 ust_register; 		/* 0x00000030 */
+	u32 gpio_status;		/* 0x00000034 */
+	u32 chip_status1; 		/* 0x00000038 */
+	u32 chip_status0;		/* 0x0000003c */
+
+	u32 ust_clock_control; 		/* 0x00000040 */
+	u32 adat_rx_control; 		/* 0x00000044 */
+	u32 aes_rx_control;	 	/* 0x00000048 */
+	u32 atod_control; 		/* 0x0000004c */
+	u32 adat_tx_control; 		/* 0x00000050 */
+	u32 aes_tx_control; 		/* 0x00000054 */
+	u32 dtoa_control; 		/* 0x00000058 */
+	u32 status_timer; 		/* 0x0000005c */
+
+	u32 _pad70[4];
+
+	u32 misc_control; 		/* 0x00000070 */
+	u32 pci_holdoff;		/* 0x00000074 */
+	u32 pci_arb_control; 		/* 0x00000078 */
+
+	u32 volume_control; 		/* 0x0000007c */
+
+	u32 reset;			/* 0x00000080 */
+
+	u32 gpio0;			/* 0x00000084 */
+	u32 gpio1;			/* 0x00000088 */
+	u32 gpio2;			/* 0x0000008c */
+	u32 gpio3;			/* 0x00000090 */
+
+	u32 _pada0[3];
+
+	u32 clockgen_ictl; 		/* 0x000000a0 */
+	u32 clockgen_rem;	 	/* 0x000000a4 */
+	u32 freq_synth_mux_sel[4]; 	/* 0x000000a8 */
+	u32 mpll0_lock_control;		/* 0x000000b8 */
+	u32 mpll1_lock_control;	 	/* 0x000000bc */
+
+	u32 _pad400[208];
+
+	/* descriptor RAM */
+	struct {
+		u32 loadr; 		/* 0x00000400 + 12*idx */
+		u32 hiadr; 		/* 0x00000404 + 12*idx */
+		u32 control;		/* 0x00000408 + 12*idx */
+	} pci_descr[16];
+
+	/* running descriptors */
+	struct {
+		u32 loadr;
+		u32 control;
+	} pci_lc[9];
+	u32 pci_hiadr[9];
+
+	u32 _pad1000[693];
+
+	u32 adat_subcode_txa_u[24];	/* 0x00001000 */
+	u32 adat_subcode_txa_unused;	/* 0x00001060 */
+
+	u32 _pad1080[7];
+
+	u32 adat_subcode_txb_u[24];	/* 0x00001080 */
+	u32 adat_subcode_txb_unused;	/* 0x000010e0 */
+
+	u32 _pad1100[7];
+
+	u32 aes_subcode_txa_lu[6];	/* 0x00001100 */
+	u32 aes_subcode_txa_lc[6];	/* 0x00001118 */
+	u32 aes_subcode_txa_lv[6];	/* 0x00001130 */
+	u32 aes_subcode_txa_ru[6];	/* 0x00001148 */
+	u32 aes_subcode_txa_rc[6];	/* 0x00001160 */
+	u32 aes_subcode_txa_rv0[2];	/* 0x00001178 */
+	u32 aes_subcode_txb_lu[6];	/* 0x00001180 */
+	u32 aes_subcode_txb_lc[6];	/* 0x00001198 */
+	u32 aes_subcode_txb_lv[6];	/* 0x000011b0 */
+	u32 aes_subcode_txb_ru[6];	/* 0x000011c8 */
+	u32 aes_subcode_txb_rc[6];	/* 0x000011e0 */
+	u32 aes_subcode_txb_rv0[2];	/* 0x000011f8 */
+	u32 aes_subcode_txa_rv2[4];	/* 0x00001200 */
+	u32 aes_subcode_txb_rv2[4];	/* 0x00001210 */
+	u32 aes_subcode_tx_unused; 	/* 0x00001220 */
+};
+
+#endif /* _SOUND_RAD1_H */
diff -Naurp linux-2.6.26.orig/include/video/impactsr.h linux-2.6.26/include/video/impactsr.h
--- linux-2.6.26.orig/include/video/impactsr.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/include/video/impactsr.h	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,138 @@
+/*
+ *  linux/drivers/video/impactsr.h -- SGI Octane MardiGras (IMPACTSR) graphics
+ *
+ *  Copyright (c) 2004 by Stanislaw Skowronek
+ *  
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#ifndef IMPACTSR_H
+#define IMPACTSR_H
+
+/* Xtalk */
+#define IMPACTSR_XTALK_MFGR		0x2aa
+#define IMPACTSR_XTALK_PART		0xc003
+
+/* Convenient access macros */
+#define IMPACTSR_REG64(vma,off)		(*(volatile unsigned long *)((vma)+(off)))
+#define IMPACTSR_REG32(vma,off)		(*(volatile unsigned int *)((vma)+(off)))
+#define IMPACTSR_REG16(vma,off)		(*(volatile unsigned short *)((vma)+(off)))
+#define IMPACTSR_REG8(vma,off)		(*(volatile unsigned char *)((vma)+(off)))
+
+/* ImpactSR (HQ4) register offsets */
+#define IMPACTSR_CFIFO(vma)		IMPACTSR_REG64(vma,0x20400)
+#define IMPACTSR_CFIFOW(vma)		IMPACTSR_REG32(vma,0x20400)
+#define IMPACTSR_CFIFOP(vma)		IMPACTSR_REG64(vma,0x130400)
+#define IMPACTSR_CFIFOPW(vma)		IMPACTSR_REG32(vma,0x130400)
+
+#define IMPACTSR_STATUS(vma)		IMPACTSR_REG32(vma,0x20000)
+#define IMPACTSR_FIFOSTATUS(vma)	IMPACTSR_REG32(vma,0x20008)
+#define IMPACTSR_GIOSTATUS(vma)		IMPACTSR_REG32(vma,0x20100)
+#define IMPACTSR_DMABUSY(vma)		IMPACTSR_REG32(vma,0x20200)
+
+#define IMPACTSR_RESTATUS(vma)		IMPACTSR_REG32(vma,0x2c578)
+
+#define IMPACTSR_CFIFO_HW(vma)		IMPACTSR_REG32(vma,0x40000)
+#define IMPACTSR_CFIFO_LW(vma)		IMPACTSR_REG32(vma,0x40008)
+#define IMPACTSR_CFIFO_DELAY(vma)	IMPACTSR_REG32(vma,0x40010)
+#define IMPACTSR_DFIFO_HW(vma)		IMPACTSR_REG32(vma,0x40020)
+#define IMPACTSR_DFIFO_LW(vma)		IMPACTSR_REG32(vma,0x40028)
+#define IMPACTSR_DFIFO_DELAY(vma)	IMPACTSR_REG32(vma,0x40030)
+
+#define IMPACTSR_XMAP_PP1SELECT(vma)	IMPACTSR_REG8(vma,0x71c08)
+#define IMPACTSR_XMAP_INDEX(vma)	IMPACTSR_REG8(vma,0x71c88)
+#define IMPACTSR_XMAP_CONFIG(vma)	IMPACTSR_REG32(vma,0x71d00)
+#define IMPACTSR_XMAP_CONFIGB(vma)	IMPACTSR_REG8(vma,0x71d08)
+#define IMPACTSR_XMAP_BUF_SELECT(vma)	IMPACTSR_REG32(vma,0x71d80)
+#define IMPACTSR_XMAP_MAIN_MODE(vma)	IMPACTSR_REG32(vma,0x71e00)
+#define IMPACTSR_XMAP_OVERLAY_MODE(vma)	IMPACTSR_REG32(vma,0x71e80)
+#define IMPACTSR_XMAP_DIB(vma)		IMPACTSR_REG32(vma,0x71f00)
+#define IMPACTSR_XMAP_DIB_DW(vma)	IMPACTSR_REG32(vma,0x71f40)
+#define IMPACTSR_XMAP_RE_RAC(vma)	IMPACTSR_REG32(vma,0x71f80)
+
+#define IMPACTSR_VC3_INDEX(vma)		IMPACTSR_REG8(vma,0x72008)
+#define IMPACTSR_VC3_INDEXDATA(vma)	IMPACTSR_REG32(vma,0x72038)
+#define IMPACTSR_VC3_DATA(vma)		IMPACTSR_REG16(vma,0x720b0)
+#define IMPACTSR_VC3_RAM(vma)		IMPACTSR_REG16(vma,0x72190)
+
+#define IMPACTSR_BDVERS0(vma)		IMPACTSR_REG8(vma,0x72408)
+#define IMPACTSR_BDVERS1(vma)		IMPACTSR_REG8(vma,0x72488)
+
+/* FIFO status */
+#define IMPACTSR_CFIFO_MAX		128
+#define IMPACTSR_BFIFO_MAX		16
+
+/* Commands for CFIFO */
+#define IMPACTSR_CMD_WRITERSS(reg,val)	(((0x00180004L|((reg)<<8))<<32)|((unsigned)(val)&0xffffffff))
+#define IMPACTSR_CMD_EXECRSS(reg,val)	(((0x001c0004L|((reg)<<8))<<32)|((unsigned)(val)&0xffffffff))
+
+#define IMPACTSR_CMD_GLINE_XSTARTF(v)	IMPACTSR_CMD_WRITERSS(0x00c,v)
+#define IMPACTSR_CMD_IR_ALIAS(v)	IMPACTSR_CMD_EXECRSS(0x045,v)
+#define IMPACTSR_CMD_BLOCKXYSTARTI(x,y)	IMPACTSR_CMD_WRITERSS(0x046,((x)<<16)|(y))
+#define IMPACTSR_CMD_BLOCKXYENDI(x,y)	IMPACTSR_CMD_WRITERSS(0x047,((x)<<16)|(y))
+#define IMPACTSR_CMD_PACKEDCOLOR(v)	IMPACTSR_CMD_WRITERSS(0x05b,v)
+#define IMPACTSR_CMD_RED(v)		IMPACTSR_CMD_WRITERSS(0x05c,v)
+#define IMPACTSR_CMD_ALPHA(v)		IMPACTSR_CMD_WRITERSS(0x05f,v)
+#define IMPACTSR_CMD_CHAR(v)		IMPACTSR_CMD_EXECRSS(0x070,v)
+#define IMPACTSR_CMD_CHAR_H(v)		IMPACTSR_CMD_WRITERSS(0x070,v)
+#define IMPACTSR_CMD_CHAR_L(v)		IMPACTSR_CMD_EXECRSS(0x071,v)
+#define IMPACTSR_CMD_XFRCONTROL(v)	IMPACTSR_CMD_WRITERSS(0x102,v)
+#define IMPACTSR_CMD_FILLMODE(v)	IMPACTSR_CMD_WRITERSS(0x110,v)
+#define IMPACTSR_CMD_CONFIG(v)		IMPACTSR_CMD_WRITERSS(0x112,v)
+#define IMPACTSR_CMD_XYWIN(x,y)		IMPACTSR_CMD_WRITERSS(0x115,((y)<<16)|(x))
+#define IMPACTSR_CMD_BKGRD_RG(v)	IMPACTSR_CMD_WRITERSS(0x140,((v)<<8))
+#define IMPACTSR_CMD_BKGRD_BA(v)	IMPACTSR_CMD_WRITERSS(0x141,((v)<<8))
+#define IMPACTSR_CMD_WINMODE(v)		IMPACTSR_CMD_WRITERSS(0x14f,v)
+#define IMPACTSR_CMD_XFRSIZE(x,y)	IMPACTSR_CMD_WRITERSS(0x153,((y)<<16)|(x))
+#define IMPACTSR_CMD_XFRMASKLO(v)	IMPACTSR_CMD_WRITERSS(0x156,v)
+#define IMPACTSR_CMD_XFRMASKHI(v)	IMPACTSR_CMD_WRITERSS(0x157,v)
+#define IMPACTSR_CMD_XFRCOUNTERS(x,y)	IMPACTSR_CMD_WRITERSS(0x158,((y)<<16)|(x))
+#define IMPACTSR_CMD_XFRMODE(v)		IMPACTSR_CMD_WRITERSS(0x159,v)
+#define IMPACTSR_CMD_RE_TOGGLECNTX(v)	IMPACTSR_CMD_WRITERSS(0x15f,v)
+#define IMPACTSR_CMD_PIXCMD(v)		IMPACTSR_CMD_WRITERSS(0x160,v)
+#define IMPACTSR_CMD_PP1FILLMODE(m,o)	IMPACTSR_CMD_WRITERSS(0x161,(m)|(o<<26))
+#define IMPACTSR_CMD_COLORMASKMSBS(v)	IMPACTSR_CMD_WRITERSS(0x162,v)
+#define IMPACTSR_CMD_COLORMASKLSBSA(v)	IMPACTSR_CMD_WRITERSS(0x163,v)
+#define IMPACTSR_CMD_COLORMASKLSBSB(v)	IMPACTSR_CMD_WRITERSS(0x164,v)
+#define IMPACTSR_CMD_BLENDFACTOR(v)	IMPACTSR_CMD_WRITERSS(0x165,v)
+#define IMPACTSR_CMD_DRBPOINTERS(v)	IMPACTSR_CMD_WRITERSS(0x16d,v)
+
+#define	IMPACTSR_CMD_HQ_PIXELFORMAT(v)	(0x000c000400000000L|((unsigned)(v)&0xffffffff))
+#define	IMPACTSR_CMD_HQ_SCANWIDTH(v)	(0x000a020400000000L|((unsigned)(v)&0xffffffff))
+#define	IMPACTSR_CMD_HQ_DMATYPE(v)	(0x000a060400000000L|((unsigned)(v)&0xffffffff))
+#define	IMPACTSR_CMD_HQ_PG_LIST_0(v)	(0x0008000400000000L|((unsigned)(v)&0xffffffff))
+#define	IMPACTSR_CMD_HQ_PG_WIDTH(v)	(0x0008040400000000L|((unsigned)(v)&0xffffffff))
+#define	IMPACTSR_CMD_HQ_PG_OFFSET(v)	(0x0008050400000000L|((unsigned)(v)&0xffffffff))
+#define	IMPACTSR_CMD_HQ_PG_STARTADDR(v)	(0x0008060400000000L|((unsigned)(v)&0xffffffff))
+#define	IMPACTSR_CMD_HQ_PG_LINECNT(v)	(0x0008070400000000L|((unsigned)(v)&0xffffffff))
+#define	IMPACTSR_CMD_HQ_PG_WIDTHA(v)	(0x0008080400000000L|((unsigned)(v)&0xffffffff))
+#define IMPACTSR_CMD_HQ_TXBASE(p)	(0x00482008|((p)<<9))
+#define IMPACTSR_CMD_HQ_TXMAX(p,v)	(0x0048300400000000L|((unsigned)(v)&0xffffffff)|((unsigned long)(p)<<40))
+#define IMPACTSR_CMD_HQ_PGBITS(p,v)	(0x00482b0400000000L|((unsigned)(v)&0xffffffff)|((unsigned long)(p)<<40))
+#define IMPACTSR_CMD_HQ_PGSIZE(v)	(0x00482a0400000000L|((unsigned)(v)&0xffffffff))
+#define IMPACTSR_CMD_HQ_STACKPTR(v)	(0x00483a0400000000L|((unsigned)(v)&0xffffffff))
+
+/* Logic operations for the PP1 (SI=source invert, DI=dest invert, RI=result invert) */
+#define IMPACTSR_LO_CLEAR	0
+#define IMPACTSR_LO_AND		1
+#define IMPACTSR_LO_DIAND	2
+#define IMPACTSR_LO_COPY	3
+#define IMPACTSR_LO_SIAND	4
+#define IMPACTSR_LO_NOP		5
+#define IMPACTSR_LO_XOR		6
+#define IMPACTSR_LO_OR		7
+#define IMPACTSR_LO_RIOR	8
+#define IMPACTSR_LO_RIXOR	9
+#define IMPACTSR_LO_RINOP	10
+#define IMPACTSR_LO_DIOR	11
+#define IMPACTSR_LO_RICOPY	12
+#define IMPACTSR_LO_SIOR	13
+#define IMPACTSR_LO_RIAND	14
+#define IMPACTSR_LO_SET		15
+
+/* Blending factors */
+#define IMPACTSR_BLEND_ALPHA	0x0704c900
+
+#endif /* IMPACTSR_H */
diff -Naurp linux-2.6.26.orig/include/video/odyssey.h linux-2.6.26/include/video/odyssey.h
--- linux-2.6.26.orig/include/video/odyssey.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/include/video/odyssey.h	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,52 @@
+/*
+ *  linux/drivers/video/odyssey.h -- SGI Octane Odyssey graphics
+ *
+ *  Copyright (c) 2005 by Stanislaw Skowronek
+ *  
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#ifndef ODYSSEY_H
+#define ODYSSEY_H
+
+/* Xtalk */
+#define ODY_XTALK_MFGR			0x023
+#define ODY_XTALK_PART			0xc013
+
+/* Convenient access macros */
+#define ODY_REG64(vma,off)		(*(volatile unsigned long *)((vma)+(off)))
+#define ODY_REG32(vma,off)		(*(volatile unsigned int *)((vma)+(off)))
+
+/* ImpactSR registers */
+#define ODY_CFIFO_D(vma)		ODY_REG64(vma,0x110000)
+#define ODY_CFIFO_W(vma)		ODY_REG32(vma,0x110000)
+
+#define ODY_DFIFO_D(vma)		ODY_REG64(vma,0x400000)
+#define ODY_DFIFO_W(vma)		ODY_REG32(vma,0x400000)
+
+#define ODY_STATUS0(vma)		ODY_REG32(vma,0x001064)
+#define ODY_STATUS0_CFIFO_HW		0x00008000
+#define ODY_STATUS0_CFIFO_LW		0x00020000
+#define ODY_DBESTAT(vma)		ODY_REG32(vma,0x00106c)
+
+/* Logic operations (SI=source invert, DI=dest invert, RI=result invert) */
+#define ODY_LO_CLEAR	0
+#define ODY_LO_AND	1
+#define ODY_LO_DIAND	2
+#define ODY_LO_COPY	3
+#define ODY_LO_SIAND	4
+#define ODY_LO_NOP	5
+#define ODY_LO_XOR	6
+#define ODY_LO_OR	7
+#define ODY_LO_RIOR	8
+#define ODY_LO_RIXOR	9
+#define ODY_LO_RINOP	10
+#define ODY_LO_DIOR	11
+#define ODY_LO_RICOPY	12
+#define ODY_LO_SIOR	13
+#define ODY_LO_RIAND	14
+#define ODY_LO_SET	15
+
+#endif /* ODYSSEY_H */
diff -Naurp linux-2.6.26.orig/sound/pci/Kconfig linux-2.6.26/sound/pci/Kconfig
--- linux-2.6.26.orig/sound/pci/Kconfig	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/sound/pci/Kconfig	2008-07-25 03:14:40.000000000 -0400
@@ -975,4 +975,10 @@ config SND_AC97_POWER_SAVE_DEFAULT
 	  The default time-out value in seconds for AC97 automatic
 	  power-save mode.  0 means to disable the power-save mode.
 
+config SND_RAD1
+	tristate "SGI RAD1"
+	depends on SND && SGI_IP30
+	help
+	  Say 'Y' or 'M' to include support for SGI RAD1 Pro Audio in Octane.
+
 endmenu
diff -Naurp linux-2.6.26.orig/sound/pci/Makefile linux-2.6.26/sound/pci/Makefile
--- linux-2.6.26.orig/sound/pci/Makefile	2008-07-13 17:51:29.000000000 -0400
+++ linux-2.6.26/sound/pci/Makefile	2008-07-25 03:14:40.000000000 -0400
@@ -21,6 +21,7 @@ snd-fm801-objs := fm801.o
 snd-intel8x0-objs := intel8x0.o
 snd-intel8x0m-objs := intel8x0m.o
 snd-maestro3-objs := maestro3.o
+snd-rad1-objs := rad1.o
 snd-rme32-objs := rme32.o
 snd-rme96-objs := rme96.o
 snd-sis7019-objs := sis7019.o
@@ -47,6 +48,7 @@ obj-$(CONFIG_SND_FM801) += snd-fm801.o
 obj-$(CONFIG_SND_INTEL8X0) += snd-intel8x0.o
 obj-$(CONFIG_SND_INTEL8X0M) += snd-intel8x0m.o
 obj-$(CONFIG_SND_MAESTRO3) += snd-maestro3.o
+obj-$(CONFIG_SND_RAD1) += snd-rad1.o
 obj-$(CONFIG_SND_RME32) += snd-rme32.o
 obj-$(CONFIG_SND_RME96) += snd-rme96.o
 obj-$(CONFIG_SND_SIS7019) += snd-sis7019.o
diff -Naurp linux-2.6.26.orig/sound/pci/rad1.c linux-2.6.26/sound/pci/rad1.c
--- linux-2.6.26.orig/sound/pci/rad1.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.26/sound/pci/rad1.c	2008-07-25 03:14:40.000000000 -0400
@@ -0,0 +1,1247 @@
+/*
+ * rad1.c - ALSA driver for SGI RAD1 (as found in Octane and Octane2)
+ * Copyright (C) 2004-2007 by Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+
+#include <sound/rad1.h>
+
+typedef struct snd_rad1_pipe {
+	unsigned long pma;		/* physical addr of the ring */
+	int *vma;			/* virtual addr of the ring */
+	struct snd_pcm_substream *subs;	/* ALSA substream */
+	struct snd_pcm *pcm;
+	unsigned int pnum;		/* number of periods */
+	unsigned int plen;		/* length of period (in bytes) */
+	unsigned int hptr;		/* hardware pointer */
+	int adma;			/* DMA active flag */
+	unsigned int qptr;		/* queue pointer */
+} rad1_pipe_t;
+
+typedef struct snd_rad1 {
+	spinlock_t lock;
+	struct snd_card *card;
+	struct pci_dev *pci;
+	unsigned long mmio_phys;
+	volatile struct rad1regs *mmio;
+	int timer_active;
+	struct timer_list timer;
+	rad1_pipe_t pipes[9];
+
+	/* random stuff */
+	int last_aesrx_rate;
+
+	/* card controls */
+	unsigned int attctrl;		/* attenuation control */
+	unsigned int rt_atod;		/* AtoD routing */
+	unsigned int rt_aesr;		/* AES Rx routing */
+	unsigned int rt_adat;		/* ADAT Rx routing */
+	unsigned int rt_opto;		/* Optical Out routing */
+} rad1_t;
+#define chip_t rad1_t
+
+static void snd_rad1_hw_init(rad1_t *chip)
+{
+	/* The hex values listed here are, for the most part, unknown values
+	 * determined by running portions of the IRIX kernel inside of Linux
+	 * as a userland application and then extracting the run-time info.
+	 *
+	 * We could define them via macros if we wanted, but the macro names
+	 * would be no more intelligible as RAD1_ANCIENT_MU_* than they are
+	 * simple hex numbers, so until the purpose of each value is known,
+	 * they shall remain as simple hex numbers.
+	 *
+	 * The same applies for pretty much any other hex number found in
+	 * driver that isn't a bit mask or some sort.  One day, we may figure
+	 * it all out, and create an appropriate header file to define them
+	 * all as intelligible macros.
+	 */
+
+	chip->mmio->reset =			0xffffffff;
+	udelay(1000);
+	chip->mmio->reset =			0xffe3cffe;
+	chip->mmio->pci_holdoff =		0x08000010;
+	chip->mmio->pci_arb_control =		0x00fac688;
+
+	/* I/O routing */
+	chip->mmio->atod_control =		0x03000000;	/* Mike 03000000; LineIn 05000000 */
+	chip->rt_atod =				0x03000000;
+	chip->mmio->dtoa_control =		0x20000000;	/* Default */
+	chip->mmio->aes_rx_control =		0x00000018;	/* Optical In 00000018; AES In 00000010 */
+	chip->rt_aesr =				0x00000018;
+	chip->mmio->aes_tx_control =		0x40000000;	/* Default */
+	chip->mmio->adat_rx_control =		0xa0000000;	/* Disabled A0000000; Optical In A0000018 */
+	chip->rt_adat =				0xa0000000;
+	chip->mmio->adat_tx_control =		0x20000000;	/* Default */
+	chip->mmio->gpio3 =			0x00000002;
+	chip->mmio->misc_control =		0x00001500;
+	chip->mmio->mpll0_lock_control =	0x9fffffff;
+	chip->mmio->mpll1_lock_control =	0x9fffffff;
+	chip->mmio->reset =			0xffe3c0fe;
+	udelay(1000);
+	chip->mmio->clockgen_ictl =		0x02000001;
+	chip->mmio->reset =			0xffe24070;
+	udelay(1000);
+	chip->mmio->reset =			0xffe20000;
+	chip->mmio->gpio2 =			0x00000002;
+	chip->mmio->volume_control =		0xd6d6d6d6;
+	chip->attctrl =				0xd6d6d6d6;
+	udelay(1000);
+	chip->mmio->misc_control =		0x00001040;	/* AES-Optical Out 00001040; AES-AES Out 00001440 */
+	chip->rt_opto =				0x00001040;
+	chip->mmio->reset =			0xffe20100;
+	chip->mmio->freq_synth_mux_sel[3] =	0x00000001;
+	chip->mmio->clockgen_rem =		0x0000ffff;
+	chip->mmio->clockgen_ictl =		0x10000603;
+	chip->mmio->reset =			0xffe20000;
+	chip->mmio->reset =			0xffe20200;
+	chip->mmio->freq_synth_mux_sel[2] =	0x00000001;
+	chip->mmio->clockgen_rem =		0x0000ffff;
+	chip->mmio->clockgen_ictl =		0x20000603;
+	chip->mmio->reset =			0xffe20000;
+	chip->mmio->reset =			0xffe20400;
+	chip->mmio->freq_synth_mux_sel[1] =	0x00000001;
+	chip->mmio->clockgen_rem =		0x0000ffff;
+	chip->mmio->clockgen_ictl =		0x40000603;
+	chip->mmio->reset =			0xffe20000;
+	chip->mmio->reset =			0xffe20800;
+	chip->mmio->freq_synth_mux_sel[0] =	0x00000001;
+	chip->mmio->clockgen_rem =		0x0000ffff;
+	chip->mmio->clockgen_ictl =		0x80000603;
+	chip->mmio->reset =			0xffe20000;
+	chip->mmio->gpio1 =			0x00000003;
+	udelay(10000);
+}
+
+static void snd_rad1_setup_dma_pipe(rad1_t *chip, int pidx)
+{
+	rad1_pipe_t *pipe=chip->pipes+pidx;
+
+	if ((-pipe->pnum * pipe->plen) & 0x7f)
+		printk(KERN_WARNING "rad1: pipe %d has unaligned size %d\n", pidx, (pipe->pnum * pipe->plen));
+
+	chip->mmio->pci_descr[pidx].hiadr = (pipe->pma >> 32);
+	chip->mmio->pci_descr[pidx].loadr = (pipe->pma & 0xffffffff);
+	chip->mmio->pci_descr[pidx].control = ((-pipe->pnum * pipe->plen) & 0xffffff80) | (pidx << 3);
+
+	chip->mmio->pci_hiadr[pidx] = (pipe->pma >> 32);
+	chip->mmio->pci_lc[pidx].loadr = (pipe->pma & 0xffffffff);
+	chip->mmio->pci_lc[pidx].control = ((-pipe->pnum * pipe->plen) & 0xffffff80) | (pidx << 3);
+}
+
+static void snd_rad1_activate_timer(rad1_t *chip)
+{
+	if (!chip->timer_active) {
+		chip->timer.expires = (jiffies + 1);
+		add_timer(&chip->timer);
+		chip->timer_active = 1;
+	}
+}
+
+static void snd_rad1_run_pipe(rad1_t *chip, int pidx, int adma)
+{
+	rad1_pipe_t *pipe = (chip->pipes + pidx);
+
+	if (pipe->adma != adma) {
+		pipe->adma = adma;
+
+		switch (pidx) {
+		case RAD1_ATOD:
+			chip->mmio->atod_control = (chip->rt_atod | adma);
+			break;
+		case RAD1_DTOA:
+			chip->mmio->dtoa_control = (0x20000000 | adma);
+			break;
+		case RAD1_AESRX:
+			chip->mmio->aes_rx_control = (chip->rt_aesr | adma);
+			break;
+		case RAD1_AESTX:
+			chip->mmio->aes_tx_control = (0x40000000 | adma);
+			break;
+		}
+	}
+
+	if (adma)
+		snd_rad1_activate_timer(chip);
+}
+
+static void snd_rad1_poll_pipe(rad1_t *chip, int pidx, int is_tx)
+{
+	rad1_pipe_t *pipe = (chip->pipes + pidx);
+	unsigned int hptr = (pipe->pnum * pipe->plen) + (chip->mmio->pci_lc[pidx].control & 0xffffff80);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	if (pipe->adma && pipe->subs) {
+		/* use hardware pointer to detect period crossing */
+		if ((hptr / pipe->plen) != (pipe->hptr / pipe->plen)) {
+			if (is_tx)
+				pipe->qptr = (hptr / 8);
+			else
+				pipe->qptr = (pipe->hptr / 8);
+			spin_unlock_irqrestore(&chip->lock, flags);
+			snd_pcm_period_elapsed(pipe->subs);
+			spin_lock_irqsave(&chip->lock, flags);
+		}
+		pipe->hptr = hptr;
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static void snd_rad1_poll_timer(unsigned long chip_virt)
+{
+	rad1_t *chip = (rad1_t *)chip_virt;
+	int adma = 0;
+
+	if (chip->pipes[RAD1_ATOD].adma) {
+		snd_rad1_poll_pipe(chip, RAD1_ATOD, 0);
+		adma = 1;
+	}
+	if (chip->pipes[RAD1_DTOA].adma) {
+		snd_rad1_poll_pipe(chip, RAD1_DTOA, 1);
+		adma = 1;
+	}
+	if (chip->pipes[RAD1_AESRX].adma) {
+		snd_rad1_poll_pipe(chip, RAD1_AESRX, 0);
+		adma = 1;
+	}
+	if (chip->pipes[RAD1_AESTX].adma) {
+		snd_rad1_poll_pipe(chip, RAD1_AESTX, 1);
+		adma = 1;
+	}
+
+	if (adma) {
+		chip->timer.expires = (jiffies + 1);
+		add_timer(&chip->timer);
+	} else
+		chip->timer_active = 0;
+}
+
+static int snd_rad1_free_pipe(struct snd_pcm_substream *substream, int pidx)
+{
+	rad1_t *chip = snd_pcm_substream_chip(substream);
+	rad1_pipe_t *pipe = (chip->pipes + pidx);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	snd_rad1_run_pipe(chip, pidx, 0);
+	pipe->subs = NULL;
+	spin_unlock_irqrestore(&chip->lock, flags);
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static long snd_rad1_gcd(long x, long y)
+{
+	long t;
+	if (x < y) {
+		t = x;
+		x = y;
+		y = t;
+	}
+
+	while (y) {
+		y = x % (t = y);
+		x = t;
+	}
+
+	return x;
+}
+
+static void snd_rad1_red_frac(long *n, long *d, long max)
+{
+	long gcd = snd_rad1_gcd(*n, *d);
+	if (!gcd)
+		return;
+
+	*n /= gcd;
+	*d /= gcd;
+
+	/* lose precision */
+	while (*n > max || *d > max) {
+		*n >>= 1;
+		*d >>= 1;
+	}
+}
+
+static void snd_rad1_set_cg(rad1_t *chip, int cg, long rate, long base, unsigned muxsel)
+{
+	long div, rem;
+	unsigned flags;
+
+	snd_rad1_red_frac(&base, &rate, 0xffff);
+	div = (base / rate);
+	rem = (base % rate);
+	snd_rad1_red_frac(&rem, &rate, 0xffff);
+	flags = ((rem * 2) < rate) ? 0x600 : 0x200;
+
+	chip->mmio->reset = 0xffe20000 | (0x100 << cg);
+	chip->mmio->freq_synth_mux_sel[3 - cg] = muxsel;
+	chip->mmio->clockgen_rem = (rem << 16) | (0x10000 - rate);
+	chip->mmio->clockgen_ictl = flags | (0x10000000 << cg) | (div - 1);
+	chip->mmio->reset = 0xffe20000;
+}
+
+/* select best master clock source for low jitter */
+static void snd_rad1_set_cgms(rad1_t *chip, int cg, long rate)
+{
+	if (!(176400 % rate))
+		snd_rad1_set_cg(chip, cg, rate, 176400, 0);
+	else
+		snd_rad1_set_cg(chip, cg, rate, 192000, 1);
+}
+
+static void snd_rad1_set_aestx_subcode(rad1_t *chip, unsigned char *sub_lc, unsigned char *sub_rc)
+{
+	unsigned int i, j, lc[6], rc[6];
+
+	for (i = 0; i < 6; i++) {
+		lc[i] = rc[i] = 0;
+		for (j = 0; j < 4; j++) {
+			lc[i] |= sub_lc[i * 4 + j] << (j << 3);
+			rc[i] |= sub_rc[i * 4 + j] << (j << 3);
+		}
+	}
+
+	for (i = 0; i < 6; i++) {
+		chip->mmio->aes_subcode_txa_lu[i] = 0x00000000;
+		chip->mmio->aes_subcode_txa_lc[i] = lc[i];
+		chip->mmio->aes_subcode_txa_lv[i] = 0x00000000;
+		chip->mmio->aes_subcode_txa_ru[i] = 0x00000000;
+		chip->mmio->aes_subcode_txa_rc[i] = rc[i];
+		chip->mmio->aes_subcode_txb_lu[i] = 0x00000000;
+		chip->mmio->aes_subcode_txb_lc[i] = lc[i];
+		chip->mmio->aes_subcode_txb_lv[i] = 0x00000000;
+		chip->mmio->aes_subcode_txb_ru[i] = 0x00000000;
+		chip->mmio->aes_subcode_txb_rc[i] = rc[i];
+	}
+
+	for (i = 0; i < 2; i++) {
+		chip->mmio->aes_subcode_txa_rv0[i] = 0x00000000;
+		chip->mmio->aes_subcode_txb_rv0[i] = 0x00000000;
+	}
+
+	for (i = 0; i < 4; i++) {
+		chip->mmio->aes_subcode_txa_rv2[i] = 0x00000000;
+		chip->mmio->aes_subcode_txb_rv2[i] = 0x00000000;
+	}
+}
+
+static void snd_rad1_genset_aestx_subcode(rad1_t *chip, int rate)
+{
+	unsigned char lc[24], rc[24];
+	int i;
+	for (i = 0; i < 24; i++)
+		lc[i] = rc[i] = 0x00;
+	lc[0] = rc[0] = 0x04;	/* PRO=0, !AUDIO=0, COPY=1, PRE=000, MODE=00 */
+	lc[1] = rc[1] = 0x01;	/* Laser Optical, CD IEC-908 */
+	lc[2] = 0x10;		/* SOURCE=0000, CHANNEL=0001 */
+	rc[2] = 0x20;		/* SOURCE=0000, CHANNEL=0010 */
+
+	/* RAD1 systems have generally decent clock sources, so we mark them Level I */
+	switch (rate) {
+	case 32000:
+		lc[3] = rc[3] = 0x0C;	/* Level I, 32 kHz */
+		break;
+	case 44100:
+		lc[3] = rc[3] = 0x00;	/* Level I, 44.1 kHz */
+		break;
+	case 48000:
+		lc[3] = rc[3] = 0x04;	/* Level I, 48 kHz */
+		break;
+	default:
+		/* not a valid IEC-958 sample rate */
+		lc[3] = rc[3] = 0x10;	/* Level III, 44.1 kHz */
+	}
+	snd_rad1_set_aestx_subcode(chip, lc, rc);
+}
+
+static void snd_rad1_setrate_pipe(rad1_t *chip, int pidx, int rate)
+{
+	if (pidx == RAD1_ATOD)
+		snd_rad1_set_cgms(chip, 0, rate);
+	if (pidx == RAD1_DTOA)
+		snd_rad1_set_cgms(chip, 1, rate);
+	if (pidx == RAD1_AESTX) {
+		snd_rad1_set_cgms(chip, 2, rate);
+		snd_rad1_genset_aestx_subcode(chip, rate);
+	}
+}
+
+static int snd_rad1_prepare_pipe(struct snd_pcm_substream *substream, int pidx)
+{
+	rad1_t *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	rad1_pipe_t *pipe = (chip->pipes + pidx);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	snd_rad1_run_pipe(chip, pidx, 0);
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	pipe->subs = substream;
+	pipe->vma = (int *)runtime->dma_area;
+	pipe->pma = runtime->dma_addr;
+	pipe->pnum = runtime->periods;
+	pipe->plen = frames_to_bytes(runtime, runtime->period_size);
+
+	snd_rad1_setrate_pipe(chip, pidx, runtime->rate);
+
+	pipe->hptr = 0;
+	pipe->qptr = 0;
+	snd_rad1_setup_dma_pipe(chip, pidx);
+
+	return 0;
+}
+
+static void snd_rad1_detect_aesrx_rate(rad1_t *chip, struct snd_pcm_hardware *hw)
+{
+	int rate;
+	unsigned sc = ((chip->mmio->chip_status0) >> 24) & 7;
+	static int rates[8] = {0, 48000, 44100, 32000, 48000, 44100, 44056, 32000};
+
+	if (!rates[sc]) {
+		printk(KERN_INFO "Warning: Recording from an unlocked IEC958 source.\n");
+		printk(KERN_INFO "         Assuming sample rate: %d.\n", chip->last_aesrx_rate);
+		rate = chip->last_aesrx_rate;
+	} else
+		rate = rates[sc];
+
+	chip->last_aesrx_rate = rate;
+	hw->rate_min = hw->rate_max = rate;
+
+	switch (rate) {
+	case 32000:
+		hw->rates = SNDRV_PCM_RATE_32000;
+		break;
+	case 44056:
+		hw->rates = SNDRV_PCM_RATE_CONTINUOUS;
+		break;
+	case 48000:
+		hw->rates = SNDRV_PCM_RATE_48000;
+	case 44100:
+	default:
+		hw->rates = SNDRV_PCM_RATE_44100;
+		break;
+	}
+}
+
+static int snd_rad1_trigger_pipe(struct snd_pcm_substream *substream, int pidx, int cmd)
+{
+	rad1_t *chip = snd_pcm_substream_chip(substream);
+	int result = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		snd_rad1_run_pipe(chip, pidx, 1);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		snd_rad1_run_pipe(chip, pidx, 0);
+		break;
+	default:
+		result = -EINVAL;
+	}
+	return result;
+}
+
+static snd_pcm_uframes_t snd_rad1_pointer_pipe(struct snd_pcm_substream *substream, int pidx)
+{
+	rad1_t *chip = snd_pcm_substream_chip(substream);
+	return chip->pipes[pidx].qptr;
+}
+
+/* ATOD pipe */
+static struct snd_pcm_hardware snd_rad1_atod_hw = {
+	.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	.formats = SNDRV_PCM_FMTBIT_S24_BE,
+	.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_32000 |
+		 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+	.rate_min = 32000,
+	.rate_max = 48000,
+	.channels_min = 2,
+	.channels_max = 2,
+	.buffer_bytes_max = 1048576,
+	.period_bytes_min = 4096,
+	.period_bytes_max = 4096,
+	.periods_min = 4,
+	.periods_max = 256,
+};
+
+static int snd_rad1_atod_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	runtime->hw = snd_rad1_atod_hw;
+	return 0;
+}
+
+static int snd_rad1_atod_close(struct snd_pcm_substream *substream)
+{
+	rad1_t *chip = snd_pcm_substream_chip(substream);
+	snd_rad1_run_pipe(chip, RAD1_ATOD, 0);
+	return 0;
+}
+
+static int snd_rad1_atod_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_rad1_atod_free(struct snd_pcm_substream *substream)
+{
+	return snd_rad1_free_pipe(substream, RAD1_ATOD);
+}
+
+static int snd_rad1_atod_prepare(struct snd_pcm_substream *substream)
+{
+	return snd_rad1_prepare_pipe(substream, RAD1_ATOD);
+}
+
+static int snd_rad1_atod_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	return snd_rad1_trigger_pipe(substream, RAD1_ATOD, cmd);
+}
+
+static snd_pcm_uframes_t snd_rad1_atod_pointer(struct snd_pcm_substream *substream)
+{
+	return snd_rad1_pointer_pipe(substream, RAD1_ATOD);
+}
+
+static struct snd_pcm_ops snd_rad1_atod_ops = {
+	.open = snd_rad1_atod_open,
+	.close = snd_rad1_atod_close,
+	.hw_params = snd_rad1_atod_params,
+	.hw_free = snd_rad1_atod_free,
+	.prepare = snd_rad1_atod_prepare,
+	.trigger = snd_rad1_atod_trigger,
+	.pointer = snd_rad1_atod_pointer,
+	.ioctl = snd_pcm_lib_ioctl,
+};
+
+static void snd_rad1_atod_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_rad1_new_atod(rad1_t *chip, int dev)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(chip->card, "RAD1 AtoD", dev, 0, 1, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_rad1_atod_pcm_free;
+	strcpy(pcm->name, "RAD1 AtoD");
+	chip->pipes[RAD1_ATOD].pcm = pcm;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rad1_atod_ops);
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci),
+					      65536, 65536);
+
+	return 0;
+}
+
+/* DTOA pipe */
+static struct snd_pcm_hardware snd_rad1_dtoa_hw = {
+	.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	.formats = SNDRV_PCM_FMTBIT_S24_BE,
+	.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_32000 |
+		 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+	.rate_min = 32000,
+	.rate_max = 48000,
+	.channels_min = 2,
+	.channels_max = 2,
+	.buffer_bytes_max = 1048576,
+	.period_bytes_min = 4096,
+	.period_bytes_max = 4096,
+	.periods_min = 4,
+	.periods_max = 256,
+};
+
+static int snd_rad1_dtoa_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	runtime->hw = snd_rad1_dtoa_hw;
+	return 0;
+}
+
+static int snd_rad1_dtoa_close(struct snd_pcm_substream *substream)
+{
+	rad1_t *chip = snd_pcm_substream_chip(substream);
+	snd_rad1_run_pipe(chip, RAD1_DTOA, 0);
+	return 0;
+}
+
+static int snd_rad1_dtoa_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_rad1_dtoa_free(struct snd_pcm_substream *substream)
+{
+	return snd_rad1_free_pipe(substream, RAD1_DTOA);
+}
+
+static int snd_rad1_dtoa_prepare(struct snd_pcm_substream *substream)
+{
+	return snd_rad1_prepare_pipe(substream, RAD1_DTOA);
+}
+
+static int snd_rad1_dtoa_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	return snd_rad1_trigger_pipe(substream, RAD1_DTOA, cmd);
+}
+
+static snd_pcm_uframes_t snd_rad1_dtoa_pointer(struct snd_pcm_substream *substream)
+{
+	return snd_rad1_pointer_pipe(substream, RAD1_DTOA);
+}
+
+static struct snd_pcm_ops snd_rad1_dtoa_ops = {
+	.open = snd_rad1_dtoa_open,
+	.close = snd_rad1_dtoa_close,
+	.hw_params = snd_rad1_dtoa_params,
+	.hw_free = snd_rad1_dtoa_free,
+	.prepare = snd_rad1_dtoa_prepare,
+	.trigger = snd_rad1_dtoa_trigger,
+	.pointer = snd_rad1_dtoa_pointer,
+	.ioctl = snd_pcm_lib_ioctl,
+};
+
+static void snd_rad1_dtoa_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_rad1_new_dtoa(rad1_t *chip, int dev)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(chip->card, "RAD1 DtoA", dev, 1, 0, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_rad1_dtoa_pcm_free;
+	strcpy(pcm->name, "RAD1 DtoA");
+	chip->pipes[RAD1_DTOA].pcm = pcm;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rad1_dtoa_ops);
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci),
+					      65536, 65536);
+
+	return 0;
+}
+
+/* AESRX pipe */
+static struct snd_pcm_hardware snd_rad1_aesrx_hw = {
+	.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	.formats = SNDRV_PCM_FMTBIT_S24_BE,
+	.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_32000,
+	.rate_min = 32000,
+	.rate_max = 48000,
+	.channels_min = 2,
+	.channels_max = 2,
+	.buffer_bytes_max = 1048576,
+	.period_bytes_min = 4096,
+	.period_bytes_max = 4096,
+	.periods_min = 4,
+	.periods_max = 256,
+};
+
+static int snd_rad1_aesrx_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	rad1_t *chip = snd_pcm_substream_chip(substream);
+	runtime->hw = snd_rad1_aesrx_hw;
+	snd_rad1_detect_aesrx_rate(chip, &runtime->hw);
+	return 0;
+}
+
+static int snd_rad1_aesrx_close(struct snd_pcm_substream *substream)
+{
+	rad1_t *chip = snd_pcm_substream_chip(substream);
+	snd_rad1_run_pipe(chip, RAD1_AESRX, 0);
+	return 0;
+}
+
+static int snd_rad1_aesrx_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_rad1_aesrx_free(struct snd_pcm_substream *substream)
+{
+	return snd_rad1_free_pipe(substream, RAD1_AESRX);
+}
+
+static int snd_rad1_aesrx_prepare(struct snd_pcm_substream *substream)
+{
+	return snd_rad1_prepare_pipe(substream, RAD1_AESRX);
+}
+
+static int snd_rad1_aesrx_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	return snd_rad1_trigger_pipe(substream, RAD1_AESRX, cmd);
+}
+
+static snd_pcm_uframes_t snd_rad1_aesrx_pointer(struct snd_pcm_substream *substream)
+{
+	return snd_rad1_pointer_pipe(substream, RAD1_AESRX);
+}
+
+static struct snd_pcm_ops snd_rad1_aesrx_ops = {
+	.open = snd_rad1_aesrx_open,
+	.close = snd_rad1_aesrx_close,
+	.hw_params = snd_rad1_aesrx_params,
+	.hw_free = snd_rad1_aesrx_free,
+	.prepare = snd_rad1_aesrx_prepare,
+	.trigger = snd_rad1_aesrx_trigger,
+	.pointer = snd_rad1_aesrx_pointer,
+	.ioctl = snd_pcm_lib_ioctl,
+};
+
+static void snd_rad1_aesrx_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_rad1_new_aesrx(rad1_t *chip, int dev)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(chip->card, "RAD1 AES Rx", dev, 0, 1, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_rad1_aesrx_pcm_free;
+	strcpy(pcm->name, "RAD1 AES Rx");
+	chip->pipes[RAD1_AESRX].pcm = pcm;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rad1_aesrx_ops);
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci),
+					      65536, 65536);
+	chip->last_aesrx_rate = 44100;
+
+	return 0;
+}
+
+/* AESTX pipe */
+static struct snd_pcm_hardware snd_rad1_aestx_hw = {
+	.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	.formats = SNDRV_PCM_FMTBIT_S24_BE,
+	.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+	.rate_min = 32000,
+	.rate_max = 48000,
+	.channels_min = 2,
+	.channels_max = 2,
+	.buffer_bytes_max = 1048576,
+	.period_bytes_min = 4096,
+	.period_bytes_max = 4096,
+	.periods_min = 4,
+	.periods_max = 256,
+};
+
+static int snd_rad1_aestx_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	runtime->hw = snd_rad1_aestx_hw;
+	return 0;
+}
+
+static int snd_rad1_aestx_close(struct snd_pcm_substream *substream)
+{
+	rad1_t *chip = snd_pcm_substream_chip(substream);
+	snd_rad1_run_pipe(chip, RAD1_AESTX, 0);
+	return 0;
+}
+
+static int snd_rad1_aestx_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_rad1_aestx_free(struct snd_pcm_substream *substream)
+{
+	return snd_rad1_free_pipe(substream, RAD1_AESTX);
+}
+
+static int snd_rad1_aestx_prepare(struct snd_pcm_substream *substream)
+{
+	return snd_rad1_prepare_pipe(substream, RAD1_AESTX);
+}
+
+static int snd_rad1_aestx_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	return snd_rad1_trigger_pipe(substream, RAD1_AESTX, cmd);
+}
+
+static snd_pcm_uframes_t snd_rad1_aestx_pointer(struct snd_pcm_substream *substream)
+{
+	return snd_rad1_pointer_pipe(substream, RAD1_AESTX);
+}
+
+static struct snd_pcm_ops snd_rad1_aestx_ops = {
+	.open = snd_rad1_aestx_open,
+	.close = snd_rad1_aestx_close,
+	.hw_params = snd_rad1_aestx_params,
+	.hw_free = snd_rad1_aestx_free,
+	.prepare = snd_rad1_aestx_prepare,
+	.trigger = snd_rad1_aestx_trigger,
+	.pointer = snd_rad1_aestx_pointer,
+	.ioctl = snd_pcm_lib_ioctl,
+};
+
+static void snd_rad1_aestx_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_rad1_new_aestx(rad1_t *chip, int dev)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(chip->card, "RAD1 AES Tx", dev, 1, 0, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_rad1_aestx_pcm_free;
+	strcpy(pcm->name, "RAD1 AES Tx");
+	chip->pipes[RAD1_AESTX].pcm = pcm;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rad1_aestx_ops);
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(chip->pci),
+					      65536, 65536);
+
+	return 0;
+}
+
+/* Volume control */
+static int snd_rad1_control_pv_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int snd_rad1_control_pv_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u)
+{
+	rad1_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int shift=kcontrol->private_value * 16;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	u->value.integer.value[0] = (chip->attctrl >> shift) & 0xff;
+	u->value.integer.value[1] = (chip->attctrl >> (8 + shift)) & 0xff;
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return 0;
+}
+
+static int snd_rad1_control_pv_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u)
+{
+	rad1_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change = 0, shift = kcontrol->private_value * 16;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	if (u->value.integer.value[0] != ((chip->attctrl >> shift) & 0xff))
+		change = 1;
+
+	if (u->value.integer.value[1] != ((chip->attctrl >> (8 + shift)) & 0xff))
+		change = 1;
+
+	if (change) {
+		chip->attctrl &= 0xffff << (16 - shift);
+		chip->attctrl |= u->value.integer.value[0] << shift;
+		chip->attctrl |= u->value.integer.value[1] << (8 + shift);
+		chip->mmio->volume_control = chip->attctrl;
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return change;
+}
+
+/* AES Tx route control */
+static int snd_rad1_control_tr_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info * uinfo)
+{
+	static char *rts[2] = {"Optical", "Coaxial"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item > 1)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, rts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_rad1_control_tr_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u)
+{
+	rad1_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	if (chip->rt_opto == 0x00001440)
+		u->value.enumerated.item[0] = 1;
+	else
+		u->value.enumerated.item[0] = 0;
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return 0;
+}
+
+static int snd_rad1_control_tr_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u)
+{
+	rad1_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change = 0;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	if (u->value.enumerated.item[0] && chip->rt_opto != 0x00001440)
+		change = 1;
+	if (!u->value.enumerated.item[0] && chip->rt_opto == 0x00001440)
+		change = 1;
+	if (change) {
+		if (u->value.enumerated.item[0])
+			chip->rt_opto = 0x00001440;
+		else
+			chip->rt_opto = 0x00001040;
+		chip->mmio->misc_control = chip->rt_opto;
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return change;
+}
+
+/* AES Rx route control */
+
+static int snd_rad1_control_rr_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u)
+{
+	rad1_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	if (chip->rt_aesr == 0x00000010)
+		u->value.enumerated.item[0] = 1;
+	else
+		u->value.enumerated.item[0] = 0;
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return 0;
+}
+
+static int snd_rad1_control_rr_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u)
+{
+	rad1_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change = 0;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	if (u->value.enumerated.item[0] && chip->rt_aesr != 0x00000010)
+		change = 1;
+	if (!u->value.enumerated.item[0] && chip->rt_aesr == 0x00000010)
+		change = 1;
+	if (change) {
+		if (u->value.enumerated.item[0]) {
+			chip->rt_aesr = 0x00000010;
+			chip->rt_adat = 0xa0000018;
+		} else {
+			chip->rt_aesr = 0x00000018;
+			chip->rt_adat = 0xa0000000;
+		}
+		chip->mmio->aes_rx_control = (chip->rt_aesr | chip->pipes[RAD1_AESRX].adma);
+		chip->mmio->adat_rx_control = (chip->rt_adat | chip->pipes[RAD1_ADATRX].adma);
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return change;
+}
+
+
+/* AtoD route control */
+static int snd_rad1_control_ar_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info * uinfo)
+{
+	static char *rts[2] = {"Mic", "Line"};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item > 1)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, rts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_rad1_control_ar_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u)
+{
+	rad1_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	if (chip->rt_atod == 0x05000000)
+		u->value.enumerated.item[0] = 1;
+	else
+		u->value.enumerated.item[0] = 0;
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return 0;
+}
+
+static int snd_rad1_control_ar_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u)
+{
+	rad1_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change = 0;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	if (u->value.enumerated.item[0] && chip->rt_atod != 0x05000000)
+		change = 1;
+	if (!u->value.enumerated.item[0] && chip->rt_atod == 0x05000000)
+		change = 1;
+	if (change) {
+		if (u->value.enumerated.item[0])
+			chip->rt_atod = 0x05000000;
+		else
+			chip->rt_atod = 0x03000000;
+		chip->mmio->atod_control = (chip->rt_atod | chip->pipes[RAD1_ATOD].adma);
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return change;
+}
+
+static struct snd_kcontrol_new snd_rad1_controls[] = {
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Master Playback Volume",
+	.info =		snd_rad1_control_pv_info,
+	.get =		snd_rad1_control_pv_get,
+	.put =		snd_rad1_control_pv_put,
+	.private_value = 1
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Line Capture Volume",
+	.info =		snd_rad1_control_pv_info,
+	.get =		snd_rad1_control_pv_get,
+	.put =		snd_rad1_control_pv_put,
+	.private_value = 0
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"IEC958 Playback Routing",
+	.info =		snd_rad1_control_tr_info,
+	.get =		snd_rad1_control_tr_get,
+	.put =		snd_rad1_control_tr_put
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"IEC958 Capture Routing",
+	.info =		snd_rad1_control_tr_info, /* clone */
+	.get =		snd_rad1_control_rr_get,
+	.put =		snd_rad1_control_rr_put
+},
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Line Capture Routing",
+	.info =		snd_rad1_control_ar_info,
+	.get =		snd_rad1_control_ar_get,
+	.put =		snd_rad1_control_ar_put
+},
+};
+
+static int snd_rad1_add_controls(rad1_t *chip)
+{
+	int idx, err;
+
+	for (idx = 0; idx < 5; idx++)
+		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_rad1_controls[idx], chip))) < 0)
+			return err;
+	return 0;
+}
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static int ndev;
+
+static int snd_rad1_free(rad1_t *chip)
+{
+	if (chip->mmio) {
+		iounmap((void *)(chip->mmio));
+		chip->mmio = NULL;
+	}
+	pci_release_regions(chip->pci);
+	kfree(chip);
+	return 0;
+}
+
+static int snd_rad1_dev_free(struct snd_device *device)
+{
+	rad1_t *chip = device->device_data;
+	return snd_rad1_free(chip);
+}
+
+static int __devinit snd_rad1_create(struct snd_card *card, struct pci_dev *pci, rad1_t **rchip)
+{
+	rad1_t *chip;
+	int err;
+	static struct snd_device_ops ops = {
+		.dev_free = snd_rad1_dev_free,
+	};
+
+	*rchip = NULL;
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	chip = kcalloc(sizeof(rad1_t), 1, GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	init_timer(&chip->timer);
+	chip->timer.function = snd_rad1_poll_timer;
+	chip->timer.data = (unsigned long)chip;
+
+	chip->card = card;
+	chip->pci = pci;
+
+	spin_lock_init(&chip->lock);
+
+	pci_set_master(pci);
+
+	if ((err = pci_request_regions(pci, "RAD1")) < 0) {
+		kfree(chip);
+		return err;
+	}
+
+	chip->mmio_phys = pci_resource_start(pci, 0);
+	chip->mmio = ioremap_nocache(chip->mmio_phys, pci_resource_len(pci, 0));
+
+	snd_rad1_hw_init(chip);
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_rad1_free(chip);
+		return err;
+	}
+	*rchip = chip;
+	return 0;
+}
+
+static struct pci_device_id snd_rad1_ids[] = {
+	{ PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_RAD1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+	{ 0, },
+};
+
+static int __devinit snd_rad1_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+	struct snd_card *card;
+	rad1_t *chip;
+	int err;
+
+	if (ndev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[ndev]) {
+		ndev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(index[ndev], id[ndev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_rad1_create(card, pci, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "RAD1");
+	strcpy(card->shortname, "RADAudio");
+	sprintf(card->longname, "SGI RAD Audio at 0x%lx", chip->mmio_phys);
+
+	/* create pipes */
+	snd_rad1_new_dtoa(chip, 0);
+	snd_rad1_new_atod(chip, 1);
+	snd_rad1_new_aestx(chip, 2);
+	snd_rad1_new_aesrx(chip, 3);
+	snd_rad1_add_controls(chip);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	pci_set_drvdata(pci, card);
+	ndev++;
+	return 0;
+}
+
+static void __devexit snd_rad1_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+MODULE_DEVICE_TABLE(pci, snd_rad1_ids);
+
+static struct pci_driver driver = {
+	.name = "SGI RAD1",
+	.id_table = snd_rad1_ids,
+	.probe = snd_rad1_probe,
+	.remove = __devexit_p(snd_rad1_remove),
+};
+
+static int __init alsa_card_rad1_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_rad1_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+MODULE_AUTHOR("Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("SGI Octane (IP30) RAD1 Alsa Audio Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("R28");
+
+module_init(alsa_card_rad1_init)
+module_exit(alsa_card_rad1_exit)

[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux